summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
-rw-r--r--CREDITS9
-rw-r--r--Documentation/Configure.help364
-rw-r--r--Documentation/DocBook/Makefile29
-rw-r--r--Documentation/DocBook/mcabook.tmpl105
-rw-r--r--Documentation/DocBook/parport-multi.fig59
-rw-r--r--Documentation/DocBook/parport-share.fig154
-rw-r--r--Documentation/DocBook/parport-structure.fig60
-rw-r--r--Documentation/DocBook/parportbook.sgml1747
-rw-r--r--Documentation/DocBook/videobook.tmpl1663
-rw-r--r--Documentation/DocBook/wanbook.tmpl97
-rw-r--r--Documentation/DocBook/z8530book.tmpl383
-rw-r--r--Documentation/networking/comx.txt248
-rw-r--r--Documentation/networking/dmfe.txt63
-rw-r--r--Documentation/sound/ALS43
-rw-r--r--Documentation/sound/Maestro29
-rw-r--r--Documentation/usb/ov511.txt160
-rw-r--r--Documentation/video4linux/CQcam.txt414
-rw-r--r--Documentation/video4linux/bttv/README2
-rw-r--r--MAINTAINERS56
-rw-r--r--Makefile190
-rw-r--r--arch/alpha/config.in13
-rw-r--r--arch/alpha/defconfig13
-rw-r--r--arch/arm/Makefile15
-rw-r--r--arch/arm/boot/compressed/Makefile5
-rw-r--r--arch/arm/config.in15
-rw-r--r--arch/arm/defconfig90
-rw-r--r--arch/arm/kernel/Makefile78
-rw-r--r--arch/arm/kernel/arch.c322
-rw-r--r--arch/arm/kernel/arch.h15
-rw-r--r--arch/arm/kernel/armksyms.c3
-rw-r--r--arch/arm/kernel/bios32.c79
-rw-r--r--arch/arm/kernel/head-armv.S16
-rw-r--r--arch/arm/kernel/ptrace.c42
-rw-r--r--arch/arm/kernel/setup.c392
-rw-r--r--arch/arm/lib/Makefile36
-rw-r--r--arch/arm/lib/io-shark.c79
-rw-r--r--arch/arm/mm/init.c31
-rw-r--r--arch/arm/mm/proc-arm6,7.S135
-rw-r--r--arch/arm/vmlinux-armo.lds.in127
-rw-r--r--arch/arm/vmlinux-armv.lds.in8
-rw-r--r--arch/i386/boot/tools/build.c6
-rw-r--r--arch/i386/config.in13
-rw-r--r--arch/i386/defconfig105
-rw-r--r--arch/i386/kernel/Makefile2
-rw-r--r--arch/i386/kernel/acpi.c454
-rw-r--r--arch/i386/kernel/entry.S4
-rw-r--r--arch/i386/kernel/head.S283
-rw-r--r--arch/i386/kernel/i386_ksyms.c14
-rw-r--r--arch/i386/kernel/irq.c129
-rw-r--r--arch/i386/kernel/mca.c185
-rw-r--r--arch/i386/kernel/mtrr.c68
-rw-r--r--arch/i386/kernel/setup.c54
-rw-r--r--arch/i386/kernel/time.c7
-rw-r--r--arch/i386/lib/delay.c36
-rw-r--r--arch/ia64/config.in13
-rw-r--r--arch/ia64/defconfig45
-rw-r--r--arch/m68k/config.in13
-rw-r--r--arch/m68k/defconfig12
-rw-r--r--arch/mips/arc/init.c4
-rw-r--r--arch/mips/config.in15
-rw-r--r--arch/mips/defconfig20
-rw-r--r--arch/mips/defconfig-ip2220
-rw-r--r--arch/mips/kernel/irixelf.c31
-rw-r--r--arch/mips/kernel/syscalls.h4
-rw-r--r--arch/mips64/config.in15
-rw-r--r--arch/mips64/defconfig21
-rw-r--r--arch/mips64/defconfig-ip2721
-rw-r--r--arch/mips64/kernel/scall_64.S4
-rw-r--r--arch/mips64/kernel/scall_o32.S4
-rw-r--r--arch/ppc/config.in13
-rw-r--r--arch/ppc/defconfig62
-rw-r--r--arch/sh/config.in13
-rw-r--r--arch/sh/defconfig25
-rw-r--r--arch/sparc/boot/piggyback.c19
-rw-r--r--arch/sparc/config.in23
-rw-r--r--arch/sparc/defconfig6
-rw-r--r--arch/sparc/kernel/sys_solaris.c2
-rw-r--r--arch/sparc/kernel/sys_sunos.c53
-rw-r--r--arch/sparc/kernel/systbls.S6
-rw-r--r--arch/sparc64/config.in41
-rw-r--r--arch/sparc64/defconfig80
-rw-r--r--arch/sparc64/kernel/binfmt_aout32.c48
-rw-r--r--arch/sparc64/kernel/ioctl32.c12
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c13
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c52
-rw-r--r--arch/sparc64/kernel/systbls.S8
-rw-r--r--arch/sparc64/solaris/misc.c7
-rw-r--r--arch/sparc64/solaris/systbl.S4
-rw-r--r--drivers/Makefile17
-rw-r--r--drivers/block/Config.in209
-rw-r--r--drivers/block/DAC960.c10
-rw-r--r--drivers/block/Makefile216
-rw-r--r--drivers/block/blkpg.c7
-rw-r--r--drivers/block/elevator.c150
-rw-r--r--drivers/block/ll_rw_blk.c341
-rw-r--r--drivers/cdrom/aztcd.c7
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/bttv.c8
-rw-r--r--drivers/char/bttv.h2
-rw-r--r--drivers/char/console.c2
-rw-r--r--drivers/char/drm/auth.c7
-rw-r--r--drivers/char/drm/bufs.c8
-rw-r--r--drivers/char/drm/context.c5
-rw-r--r--drivers/char/drm/dma.c5
-rw-r--r--drivers/char/drm/drawable.c5
-rw-r--r--drivers/char/drm/drm.h5
-rw-r--r--drivers/char/drm/drmP.h10
-rw-r--r--drivers/char/drm/fops.c18
-rw-r--r--drivers/char/drm/gamma_dma.c5
-rw-r--r--drivers/char/drm/gamma_drv.c9
-rw-r--r--drivers/char/drm/gamma_drv.h5
-rw-r--r--drivers/char/drm/init.c5
-rw-r--r--drivers/char/drm/ioctl.c5
-rw-r--r--drivers/char/drm/lists.c7
-rw-r--r--drivers/char/drm/lock.c5
-rw-r--r--drivers/char/drm/memory.c5
-rw-r--r--drivers/char/drm/proc.c21
-rw-r--r--drivers/char/drm/tdfx_context.c5
-rw-r--r--drivers/char/drm/tdfx_drv.c25
-rw-r--r--drivers/char/drm/tdfx_drv.h6
-rw-r--r--drivers/char/drm/vm.c5
-rw-r--r--drivers/char/generic_serial.c168
-rw-r--r--drivers/char/mem.c8
-rw-r--r--drivers/char/misc.c26
-rw-r--r--drivers/char/mixcomwd.c58
-rw-r--r--drivers/char/n_tty.c50
-rw-r--r--drivers/char/ppdev.c11
-rw-r--r--drivers/char/pty.c4
-rw-r--r--drivers/char/radio-gemtek.c4
-rw-r--r--drivers/char/serial.c35
-rw-r--r--drivers/char/sx.c437
-rw-r--r--drivers/char/sx.h38
-rw-r--r--drivers/char/sxboards.h1
-rw-r--r--drivers/char/tty_io.c4
-rw-r--r--drivers/char/videodev.c72
-rw-r--r--drivers/ide/Config.in153
-rw-r--r--drivers/ide/Makefile238
-rw-r--r--drivers/ide/aec6210.c (renamed from drivers/block/aec6210.c)0
-rw-r--r--drivers/ide/ali14xx.c (renamed from drivers/block/ali14xx.c)0
-rw-r--r--drivers/ide/alim15x3.c (renamed from drivers/block/alim15x3.c)0
-rw-r--r--drivers/ide/amd7409.c (renamed from drivers/block/amd7409.c)0
-rw-r--r--drivers/ide/buddha.c (renamed from drivers/block/buddha.c)0
-rw-r--r--drivers/ide/cmd640.c (renamed from drivers/block/cmd640.c)0
-rw-r--r--drivers/ide/cmd64x.c (renamed from drivers/block/cmd64x.c)0
-rw-r--r--drivers/ide/cs5530.c (renamed from drivers/block/cs5530.c)0
-rw-r--r--drivers/ide/cy82c693.c (renamed from drivers/block/cy82c693.c)0
-rw-r--r--drivers/ide/dtc2278.c (renamed from drivers/block/dtc2278.c)0
-rw-r--r--drivers/ide/falconide.c (renamed from drivers/block/falconide.c)0
-rw-r--r--drivers/ide/gayle.c (renamed from drivers/block/gayle.c)0
-rw-r--r--drivers/ide/hd.c (renamed from drivers/block/hd.c)0
-rw-r--r--drivers/ide/hpt34x.c (renamed from drivers/block/hpt34x.c)0
-rw-r--r--drivers/ide/hpt366.c (renamed from drivers/block/hpt366.c)0
-rw-r--r--drivers/ide/ht6560b.c (renamed from drivers/block/ht6560b.c)0
-rw-r--r--drivers/ide/icside.c (renamed from drivers/block/icside.c)0
-rw-r--r--drivers/ide/ide-cd.c (renamed from drivers/block/ide-cd.c)0
-rw-r--r--drivers/ide/ide-cd.h (renamed from drivers/block/ide-cd.h)0
-rw-r--r--drivers/ide/ide-cs.c (renamed from drivers/block/ide-cs.c)0
-rw-r--r--drivers/ide/ide-disk.c (renamed from drivers/block/ide-disk.c)0
-rw-r--r--drivers/ide/ide-dma.c (renamed from drivers/block/ide-dma.c)0
-rw-r--r--drivers/ide/ide-features.c (renamed from drivers/block/ide-features.c)0
-rw-r--r--drivers/ide/ide-floppy.c (renamed from drivers/block/ide-floppy.c)8
-rw-r--r--drivers/ide/ide-geometry.c (renamed from drivers/block/ide-geometry.c)0
-rw-r--r--drivers/ide/ide-pci.c (renamed from drivers/block/ide-pci.c)0
-rw-r--r--drivers/ide/ide-pmac.c (renamed from drivers/block/ide-pmac.c)0
-rw-r--r--drivers/ide/ide-pnp.c (renamed from drivers/block/ide-pnp.c)0
-rw-r--r--drivers/ide/ide-probe.c (renamed from drivers/block/ide-probe.c)0
-rw-r--r--drivers/ide/ide-proc.c (renamed from drivers/block/ide-proc.c)0
-rw-r--r--drivers/ide/ide-tape.c (renamed from drivers/block/ide-tape.c)0
-rw-r--r--drivers/ide/ide.c (renamed from drivers/block/ide.c)2
-rw-r--r--drivers/ide/ide_modes.h (renamed from drivers/block/ide_modes.h)0
-rw-r--r--drivers/ide/macide.c (renamed from drivers/block/macide.c)0
-rw-r--r--drivers/ide/ns87415.c (renamed from drivers/block/ns87415.c)0
-rw-r--r--drivers/ide/opti621.c (renamed from drivers/block/opti621.c)0
-rw-r--r--drivers/ide/pdc202xx.c (renamed from drivers/block/pdc202xx.c)0
-rw-r--r--drivers/ide/pdc4030.c (renamed from drivers/block/pdc4030.c)0
-rw-r--r--drivers/ide/pdc4030.h (renamed from drivers/block/pdc4030.h)0
-rw-r--r--drivers/ide/piix.c (renamed from drivers/block/piix.c)0
-rw-r--r--drivers/ide/q40ide.c (renamed from drivers/block/q40ide.c)0
-rw-r--r--drivers/ide/qd6580.c (renamed from drivers/block/qd6580.c)0
-rw-r--r--drivers/ide/rapide.c (renamed from drivers/block/rapide.c)0
-rw-r--r--drivers/ide/rz1000.c (renamed from drivers/block/rz1000.c)0
-rw-r--r--drivers/ide/sis5513.c (renamed from drivers/block/sis5513.c)0
-rw-r--r--drivers/ide/sl82c105.c (renamed from drivers/block/sl82c105.c)0
-rw-r--r--drivers/ide/trm290.c (renamed from drivers/block/trm290.c)0
-rw-r--r--drivers/ide/umc8672.c (renamed from drivers/block/umc8672.c)0
-rw-r--r--drivers/ide/via82cxxx.c (renamed from drivers/block/via82cxxx.c)0
-rw-r--r--drivers/net/3c527.c52
-rw-r--r--drivers/net/3c59x.c2
-rw-r--r--drivers/net/Config.in12
-rw-r--r--drivers/net/aironet4500.h2
-rw-r--r--drivers/net/appletalk/Config.in31
-rw-r--r--drivers/net/bsd_comp.c42
-rw-r--r--drivers/net/dgrs.c5
-rw-r--r--drivers/net/dmfe.c586
-rw-r--r--drivers/net/eexpress.c1
-rw-r--r--drivers/net/epic100.c2
-rw-r--r--drivers/net/eql.c4
-rw-r--r--drivers/net/ewrk3.c4
-rw-r--r--drivers/net/hamradio/baycom_epp.c8
-rw-r--r--drivers/net/hamradio/baycom_par.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c2
-rw-r--r--drivers/net/hamradio/bpqether.c2
-rw-r--r--drivers/net/hamradio/hdlcdrv.c6
-rw-r--r--drivers/net/hamradio/pi2.c4
-rw-r--r--drivers/net/hamradio/pt.c4
-rw-r--r--drivers/net/hamradio/scc.c12
-rw-r--r--drivers/net/hamradio/soundmodem/sm.c2
-rw-r--r--drivers/net/hamradio/soundmodem/sm_sbc.c4
-rw-r--r--drivers/net/hamradio/soundmodem/sm_wss.c2
-rw-r--r--drivers/net/hamradio/yam.c6
-rw-r--r--drivers/net/lance.c46
-rw-r--r--drivers/net/pcmcia/3c574_cs.c12
-rw-r--r--drivers/net/pcmcia/Config.in1
-rw-r--r--drivers/net/pcmcia/Makefile2
-rw-r--r--drivers/net/pcmcia/ray_cs.c2
-rw-r--r--drivers/net/pcmcia/tulip_cb.c3150
-rw-r--r--drivers/net/pcnet32.c2
-rw-r--r--drivers/net/plip.c59
-rw-r--r--drivers/net/ppp_async.c627
-rw-r--r--drivers/net/ppp_deflate.c49
-rw-r--r--drivers/net/ppp_generic.c1510
-rw-r--r--drivers/net/ppp_synctty.c2
-rw-r--r--drivers/net/rcpci45.c15
-rw-r--r--drivers/net/rrunner.c8
-rw-r--r--drivers/net/sb1000.c4
-rw-r--r--drivers/net/setup.c5
-rw-r--r--drivers/net/shaper.c11
-rw-r--r--drivers/net/sis900.c2
-rw-r--r--drivers/net/skfp/drvfbi.c2
-rw-r--r--drivers/net/skfp/skfddi.c2
-rw-r--r--drivers/net/sunhme.c4
-rw-r--r--drivers/net/tokenring/Config.in1
-rw-r--r--drivers/net/tokenring/Makefile1
-rw-r--r--drivers/net/tokenring/lanstreamer.c1776
-rw-r--r--drivers/net/tokenring/lanstreamer.h319
-rw-r--r--drivers/net/via-rhine.c2
-rw-r--r--drivers/net/wan/Config.in19
-rw-r--r--drivers/net/wan/Makefile56
-rw-r--r--drivers/net/wan/comx-hw-comx.c1426
-rw-r--r--drivers/net/wan/comx-hw-locomx.c496
-rw-r--r--drivers/net/wan/comx-hw-mixcom.c948
-rw-r--r--drivers/net/wan/comx-proto-fr.c1006
-rw-r--r--drivers/net/wan/comx-proto-lapb.c548
-rw-r--r--drivers/net/wan/comx-proto-ppp.c269
-rw-r--r--drivers/net/wan/comx.c1238
-rw-r--r--drivers/net/wan/comx.h240
-rw-r--r--drivers/net/wan/comxhw.h113
-rw-r--r--drivers/net/wan/cosa.c10
-rw-r--r--drivers/net/wan/hscx.h103
-rw-r--r--drivers/net/wan/mixcom.h35
-rw-r--r--drivers/net/wan/sbni.c6
-rw-r--r--drivers/net/wan/sdla.c2
-rw-r--r--drivers/net/wan/syncppp.c142
-rw-r--r--drivers/net/wan/syncppp.h4
-rw-r--r--drivers/net/wan/z85230.c94
-rw-r--r--drivers/net/wavelan.c6
-rw-r--r--drivers/net/wavelan.h6
-rw-r--r--drivers/net/yellowfin.c1
-rw-r--r--drivers/parport/ChangeLog21
-rw-r--r--drivers/parport/Config.in3
-rw-r--r--drivers/parport/parport_pc.c119
-rw-r--r--drivers/pcmcia/yenta.c25
-rw-r--r--drivers/sbus/audio/audio.c410
-rw-r--r--drivers/sbus/char/sab82532.c8
-rw-r--r--drivers/sbus/char/su.c8
-rw-r--r--drivers/sbus/char/zs.c8
-rw-r--r--drivers/scsi/ChangeLog.ncr53c8xx17
-rw-r--r--drivers/scsi/ChangeLog.sym53c8xx9
-rw-r--r--drivers/scsi/Config.in4
-rw-r--r--drivers/scsi/README.st8
-rw-r--r--drivers/scsi/atp870u.c5
-rw-r--r--drivers/scsi/constants.c563
-rw-r--r--drivers/scsi/eata_dma_proc.c18
-rw-r--r--drivers/scsi/hosts.h28
-rw-r--r--drivers/scsi/ncr53c8xx.c2600
-rw-r--r--drivers/scsi/scsi.c18
-rw-r--r--drivers/scsi/scsi.h11
-rw-r--r--drivers/scsi/scsi_debug.c12
-rw-r--r--drivers/scsi/scsi_ioctl.c64
-rw-r--r--drivers/scsi/scsi_lib.c9
-rw-r--r--drivers/scsi/scsi_merge.c8
-rw-r--r--drivers/scsi/scsi_scan.c80
-rw-r--r--drivers/scsi/scsi_syms.c1
-rw-r--r--drivers/scsi/sd.c74
-rw-r--r--drivers/scsi/sr.c4
-rw-r--r--drivers/scsi/st.c738
-rw-r--r--drivers/scsi/st.h11
-rw-r--r--drivers/scsi/st_options.h9
-rw-r--r--drivers/scsi/sym53c8xx.c175
-rw-r--r--drivers/scsi/sym53c8xx_comm.h2863
-rw-r--r--drivers/sound/Config.in5
-rw-r--r--drivers/sound/Makefile37
-rw-r--r--drivers/sound/dev_table.c2
-rw-r--r--drivers/sound/dev_table.h36
-rw-r--r--drivers/sound/dmabuf.c8
-rw-r--r--drivers/sound/miroaci.h1
-rw-r--r--drivers/sound/mpu401.c21
-rw-r--r--drivers/sound/sb_card.c2
-rw-r--r--drivers/sound/sound_core.c112
-rw-r--r--drivers/sound/sound_firmware.c18
-rw-r--r--drivers/sound/soundcard.c57
-rw-r--r--drivers/sound/waveartist.c12
-rw-r--r--drivers/telephony/ixj.c29
-rw-r--r--drivers/telephony/phonedev.c5
-rw-r--r--drivers/usb/Config.in4
-rw-r--r--drivers/usb/Makefile18
-rw-r--r--drivers/usb/dsbr100.c353
-rw-r--r--drivers/usb/inode.c1
-rw-r--r--drivers/usb/ov511.c484
-rw-r--r--drivers/usb/ov511.h9
-rw-r--r--drivers/usb/pegasus.c714
-rw-r--r--drivers/usb/serial/Makefile9
-rw-r--r--drivers/usb/uhci.c56
-rw-r--r--drivers/usb/uhci.h6
-rw-r--r--drivers/usb/usb-core.c7
-rw-r--r--drivers/usb/usb-storage.c1124
-rw-r--r--drivers/usb/usb-storage.h24
-rw-r--r--drivers/usb/usb-uhci-debug.h79
-rw-r--r--drivers/usb/usb-uhci.c975
-rw-r--r--drivers/usb/usb-uhci.h18
-rw-r--r--drivers/video/aty128.h2
-rw-r--r--drivers/video/aty128fb.c351
-rw-r--r--drivers/video/atyfb.c2
-rw-r--r--drivers/video/cyber2000fb.c106
-rw-r--r--drivers/video/fbcon.c101
-rw-r--r--drivers/video/fbmem.c84
-rw-r--r--drivers/video/hgafb.c86
-rw-r--r--fs/Config.in5
-rw-r--r--fs/adfs/dir.c15
-rw-r--r--fs/affs/super.c19
-rw-r--r--fs/bfs/inode.c15
-rw-r--r--fs/binfmt_aout.c49
-rw-r--r--fs/binfmt_elf.c41
-rw-r--r--fs/binfmt_em86.c11
-rw-r--r--fs/binfmt_misc.c2
-rw-r--r--fs/binfmt_script.c11
-rw-r--r--fs/coda/psdev.c2
-rw-r--r--fs/efs/super.c23
-rw-r--r--fs/exec.c38
-rw-r--r--fs/ext2/dir.c4
-rw-r--r--fs/fat/fatfs_syms.c5
-rw-r--r--fs/fat/inode.c16
-rw-r--r--fs/filesystems.c113
-rw-r--r--fs/hfs/super.c29
-rw-r--r--fs/hpfs/buffer.c9
-rw-r--r--fs/hpfs/hpfs_fn.h1
-rw-r--r--fs/hpfs/inode.c1
-rw-r--r--fs/hpfs/super.c22
-rw-r--r--fs/isofs/inode.c30
-rw-r--r--fs/minix/inode.c17
-rw-r--r--fs/msdos/msdosfs_syms.c13
-rw-r--r--fs/msdos/namei.c25
-rw-r--r--fs/ncpfs/inode.c19
-rw-r--r--fs/nfs/read.c2
-rw-r--r--fs/nfsd/nfsfh.c1
-rw-r--r--fs/nls/nls_base.c161
-rw-r--r--fs/nls/nls_cp437.c31
-rw-r--r--fs/nls/nls_cp737.c31
-rw-r--r--fs/nls/nls_cp775.c31
-rw-r--r--fs/nls/nls_cp850.c31
-rw-r--r--fs/nls/nls_cp852.c31
-rw-r--r--fs/nls/nls_cp855.c31
-rw-r--r--fs/nls/nls_cp857.c31
-rw-r--r--fs/nls/nls_cp860.c31
-rw-r--r--fs/nls/nls_cp861.c31
-rw-r--r--fs/nls/nls_cp862.c31
-rw-r--r--fs/nls/nls_cp863.c31
-rw-r--r--fs/nls/nls_cp864.c31
-rw-r--r--fs/nls/nls_cp865.c31
-rw-r--r--fs/nls/nls_cp866.c31
-rw-r--r--fs/nls/nls_cp869.c31
-rw-r--r--fs/nls/nls_cp874.c31
-rw-r--r--fs/nls/nls_iso8859-1.c31
-rw-r--r--fs/nls/nls_iso8859-14.c31
-rw-r--r--fs/nls/nls_iso8859-15.c31
-rw-r--r--fs/nls/nls_iso8859-2.c31
-rw-r--r--fs/nls/nls_iso8859-3.c31
-rw-r--r--fs/nls/nls_iso8859-4.c31
-rw-r--r--fs/nls/nls_iso8859-5.c31
-rw-r--r--fs/nls/nls_iso8859-6.c31
-rw-r--r--fs/nls/nls_iso8859-7.c31
-rw-r--r--fs/nls/nls_iso8859-8.c31
-rw-r--r--fs/nls/nls_iso8859-9.c31
-rw-r--r--fs/nls/nls_koi8-r.c31
-rw-r--r--fs/ntfs/fs.c47
-rw-r--r--fs/openpromfs/inode.c30
-rw-r--r--fs/partitions/acorn.c72
-rw-r--r--fs/partitions/acorn.h36
-rw-r--r--fs/partitions/atari.c4
-rw-r--r--fs/partitions/sgi.c4
-rw-r--r--fs/proc/procfs_syms.c12
-rw-r--r--fs/qnx4/inode.c17
-rw-r--r--fs/romfs/inode.c21
-rw-r--r--fs/smbfs/inode.c19
-rw-r--r--fs/super.c8
-rw-r--r--fs/sysv/inode.c17
-rw-r--r--fs/udf/super.c60
-rw-r--r--fs/ufs/dir.c4
-rw-r--r--fs/ufs/super.c17
-rw-r--r--fs/umsdos/inode.c17
-rw-r--r--fs/vfat/namei.c13
-rw-r--r--fs/vfat/vfatfs_syms.c10
-rw-r--r--include/asm-alpha/parport.h51
-rw-r--r--include/asm-arm/arch-cl7500/system.h2
-rw-r--r--include/asm-arm/arch-ebsa285/time.h7
-rw-r--r--include/asm-arm/parport.h58
-rw-r--r--include/asm-arm/procinfo.h6
-rw-r--r--include/asm-arm/ptrace.h5
-rw-r--r--include/asm-arm/system.h100
-rw-r--r--include/asm-i386/mca_dma.h76
-rw-r--r--include/asm-i386/mman.h6
-rw-r--r--include/asm-i386/parport.h41
-rw-r--r--include/asm-i386/unistd.h3
-rw-r--r--include/asm-mips/mman.h6
-rw-r--r--include/asm-mips/parport.h49
-rw-r--r--include/asm-mips/sgialib.h7
-rw-r--r--include/asm-mips/unistd.h6
-rw-r--r--include/asm-mips64/mman.h8
-rw-r--r--include/asm-mips64/parport.h51
-rw-r--r--include/asm-mips64/unistd.h10
-rw-r--r--include/asm-sparc/ide.h2
-rw-r--r--include/asm-sparc/vaddrs.h6
-rw-r--r--include/asm-sparc64/parport.h16
-rw-r--r--include/linux/affs_fs.h1
-rw-r--r--include/linux/bfs_fs.h3
-rw-r--r--include/linux/blk.h14
-rw-r--r--include/linux/blkdev.h14
-rw-r--r--include/linux/compatmac.h163
-rw-r--r--include/linux/efs_fs.h6
-rw-r--r--include/linux/elevator.h124
-rw-r--r--include/linux/fb.h9
-rw-r--r--include/linux/fs.h3
-rw-r--r--include/linux/generic_serial.h (renamed from drivers/char/generic_serial.h)5
-rw-r--r--include/linux/hfs_fs.h1
-rw-r--r--include/linux/hpfs_fs.h5
-rw-r--r--include/linux/if_ppp.h10
-rw-r--r--include/linux/iso_fs.h1
-rw-r--r--include/linux/kmod.h2
-rw-r--r--include/linux/minix_fs.h1
-rw-r--r--include/linux/mm.h13
-rw-r--r--include/linux/mmzone.h31
-rw-r--r--include/linux/msdos_fs.h9
-rw-r--r--include/linux/ncp_fs.h1
-rw-r--r--include/linux/nls.h35
-rw-r--r--include/linux/ntfs_fs.h4
-rw-r--r--include/linux/pci.h3
-rw-r--r--include/linux/personality.h2
-rw-r--r--include/linux/ppp_channel.h39
-rw-r--r--include/linux/ppp_defs.h5
-rw-r--r--include/linux/proc_fs.h1
-rw-r--r--include/linux/qnx4_fs.h1
-rw-r--r--include/linux/romfs_fs.h1
-rw-r--r--include/linux/smb_fs.h1
-rw-r--r--include/linux/swap.h3
-rw-r--r--include/linux/sysv_fs.h1
-rw-r--r--include/linux/telephony.h10
-rw-r--r--include/linux/tty.h1
-rw-r--r--include/linux/udf_fs.h1
-rw-r--r--include/linux/ufs_fs.h1
-rw-r--r--include/linux/umsdos_fs.h1
-rw-r--r--include/linux/usb.h46
-rw-r--r--ipc/shm.c28
-rw-r--r--kernel/exec_domain.c34
-rw-r--r--kernel/exit.c7
-rw-r--r--kernel/fork.c10
-rw-r--r--kernel/module.c5
-rw-r--r--kernel/pm.c83
-rw-r--r--mm/filemap.c577
-rw-r--r--mm/mlock.c5
-rw-r--r--mm/mmap.c4
-rw-r--r--mm/mprotect.c5
-rw-r--r--mm/mremap.c2
-rw-r--r--mm/page_alloc.c46
-rw-r--r--mm/vmscan.c3
-rw-r--r--net/appletalk/ddp.c7
-rw-r--r--net/atm/Makefile2
-rw-r--r--net/ax25/af_ax25.c145
-rw-r--r--net/econet/af_econet.c50
-rw-r--r--net/packet/af_packet.c4
-rw-r--r--net/sched/sch_ingress.c1
-rw-r--r--net/x25/af_x25.c6
-rw-r--r--net/x25/x25_link.c9
-rw-r--r--scripts/Makefile6
-rw-r--r--scripts/docgen3
-rw-r--r--scripts/docproc.c78
-rw-r--r--scripts/gen-all-syms7
-rw-r--r--scripts/kernel-doc865
488 files changed, 30396 insertions, 14666 deletions
diff --git a/CREDITS b/CREDITS
index 4e1d57242..b98bae810 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1035,7 +1035,8 @@ S: CV5 8BZ
S: United Kingdom
N: Ron Holt
-E: ron@sovereign.org
+E: ron@holt.org
+E: rholt@netcom.com
W: http://www.holt.org/
W: http://www.ronholt.com/
D: Kernel development
@@ -2396,12 +2397,8 @@ S: 97078 Wuerzburg
S: Germany
N: Greg Ungerer
-E: gerg@stallion.com
+E: gerg@moreton.com.au
D: Author of Stallion multiport serial drivers
-S: Stallion Technologies
-S: 33 Woodstock Rd
-S: Toowong, QLD. 4066
-S: Australia
N: Jeffrey A. Uphoff
E: juphoff@transmeta.com
diff --git a/Documentation/Configure.help b/Documentation/Configure.help
index cee6bef35..64bdd50f2 100644
--- a/Documentation/Configure.help
+++ b/Documentation/Configure.help
@@ -2109,6 +2109,13 @@ CONFIG_SYSVIPC
section 6.4 of the Linux Programmer's Guide, available from
http://www.linuxdoc.org/docs.html#guide .
+ Shared memory is now implemented using a new (minimal) virtual file
+ system, which you need to mount before programs can use shared memory.
+ To do this automatically at system startup just add the following line
+ to your /etc/fstab:
+
+ none /var/shm shm defaults 0 0
+
Saying Y here enlarges your kernel by about 18 KB. Just say Y.
BSD Process Accounting
@@ -2546,6 +2553,16 @@ CONFIG_FBCON_FONTWIDTH8_ONLY
Answer Y here will make the kernel provide only the 8x8 fonts (these
are the less readable).
+Sparc console 8x16 font
+CONFIG_FONT_SUN8x16
+ This is the high resolution console font for Sun machines. Say Y.
+
+Sparc console 12x22 font (not supported by all drivers)
+CONFIG_FONT_SUN12x22
+ This is the high resolution console font for Sun machines with very big
+ letters (like the letters used in the SPARC PROM). If the standard font
+ is unreadable for you, say Y, otherwise say N.
+
VGA 8x8 font
CONFIG_FONT_8x8
This is the "high resolution" font for the VGA frame buffer (the one
@@ -2877,6 +2894,12 @@ CONFIG_PARPORT_PC_FIFO
FIFO. See Documentation/parport.txt to find out how to specify
which IRQ/DMA to use.
+SuperIO chipset support (EXPERIMENTAL)
+CONFIG_PARPORT_PC_SUPERIO
+ Saying Y here enables some probes for Super-IO chipsets in order to
+ find out things like base addresses, IRQ lines and DMA channels. It
+ is safe to say N.
+
Support for PCMCIA management for PC-style ports
CONFIG_PARPORT_PC_PCMCIA
Say Y here if you need PCMCIA support for your PC-style parallel
@@ -4478,18 +4501,6 @@ CONFIG_CHR_DEV_ST
module, say M here and read Documentation/modules.txt and
Documentation/scsi.txt .
-Extra SCSI Tapes
-CONFIG_ST_EXTRA_DEVS
- This controls the amount of additional space allocated in tables for
- drivers that are loaded as modules after the kernel is booted. In the
- event that the SCSI core itself was loaded as a module, this this value
- is the number of additional tape devices that can be loaded after the
- first host driver is loaded.
-
- Admittedly this isn't pretty, but there are tons of race conditions
- involved with resizing the internal arrays on the fly. Someday this
- flag will go away, and everything will work automatically.
-
SCSI CDROM support
CONFIG_BLK_DEV_SR
If you want to use a SCSI CDROM under Linux, say Y and read the
@@ -5650,21 +5661,55 @@ CONFIG_FC4
the system using Fibre Optic and the "X3.269-199X Fibre Channel
Protocol for SCSI" specification. You'll also need the generic SCSI
support, as well as the drivers for the storage array itself and
- for the interface adapter such as SOC. This subsystem could even
- serve for IP networking, with some code extensions.
-
- If unsure, say N.
+ for the interface adapter such as SOC or SOC+. This subsystem could even
+ serve for IP networking, with some code extensions. If unsure, say N.
Sun SOC
CONFIG_FC4_SOC
Serial Optical Channel is an interface card with one or two Fibre
- Optic ports, each of which can be connected to a disk array. Only
- the SBus incarnation of the adapter is supported at the moment.
+ Optic ports, each of which can be connected to a disk array. Note that
+ if you have older firmware in the card, you'll need the microcode from
+ the Solaris driver to make it work.
+
+ This support is also available as a module called soc.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+Sun SOC+ (aka SOCAL)
+CONFIG_FC4_SOCAL
+ Serial Optical Channel Plus is an interface card with up to two Fibre
+ Optic ports. This card supports FC Arbitrated Loop (usually A5000 or
+ internal FC disks in E[3-6]000 machines through the Interface Board).
+ You'll probably need the microcode from the Solaris driver to make it
+ work.
+
+ This support is also available as a module called socal.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
SparcSTORAGE Array 100 and 200 series
CONFIG_SCSI_PLUTO
If you never bought a disk array made by Sun, go with N.
+ This support is also available as a module called pluto.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+Sun Enterprise Network Array (A5000 and EX500)
+CONFIG_SCSI_FCAL
+ This driver drives FC-AL disks connected through a Fibre Channel card
+ using the drivers/fc4 layer (currently only SOCAL).
+ The most common is either A5000 array or internal disks in E[3-6]000
+ machines.
+
+ This support is also available as a module called fcal.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt. If unsure, say N.
+
AcornSCSI support
CONFIG_SCSI_ACORNSCSI_3
This enables support for the Acorn SCSI card (aka30). If you have an
@@ -6185,16 +6230,6 @@ CONFIG_PCMCIA_3C575
The module will be called 3c575_cb.o. If you want to do that, say M
here and read Documentation/modules.txt. If unsure, say N.
-DEC Tulip CardBus support
-CONFIG_PCMCIA_TULIP
- This driver supports CardBus Fast Ethernet adapters based on DEC
- Tulip and compatible chipsets.
-
- This driver can only be compiled as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want).
- The module will be called tulip_cb.o. If you want to do that, say M
- here and read Documentation/modules.txt. If unsure, say N.
-
SMC EPIC CardBus support
CONFIG_PCMCIA_EPIC100
This driver supports CardBus Fast Ethernet adapters based on the SMC
@@ -6809,6 +6844,77 @@ CONFIG_WANPIPE_PPP
you say N, the PPP support will not be included in the driver (saves
about 16 KB of kernel memory).
+MultiGate/COMX support
+CONFIG_COMX
+ Say Y if you want to use any board from the MultiGate (COMX) family.
+ These boards are synchronous serial adapters for the PC, manufactured
+ by ITConsult-Pro Co, Hungary.
+
+ Read linux/Documentation/networking/comx.txt for help on configuring
+ and using COMX interfaces. Further info on these cards can be found at
+ http://www.itc.hu or <info@itc.hu>.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx.o.
+
+COMX/CMX/HiCOMX board support
+CONFIG_COMX_HW_COMX
+ Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the
+ MultiGate family. Say Y if you have one of these.
+
+ You will need additional firmware to use these cards, which are
+ downloadable from ftp://ftp.itc.hu/.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx-hw-comx.o.
+
+LoCOMX board support
+CONFIG_COMX_HW_LOCOMX
+ Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y
+ if you have a board like this.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx-hw-locomx.o.
+
+MixCOM board support
+CONFIG_COMX_HW_MIXCOM
+ Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y
+ if you have a board like this.
+
+ If you want to use the watchdog device on this card, you should
+ select it in the Watchdog Cards section of the Character Devices
+ configuration. The ISDN interface of this card is Teles 16.3 compatible,
+ you should enable it in the ISDN configuration menu. The driver for the
+ flash ROM of this card is available separately on ftp://ftp.itc.hu/.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx-hw-mixcom.o.
+
+MultiGate Cisco-HDLC and synchronous PPP protocol support
+CONFIG_COMX_PROTO_PPP
+ Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards.
+ Say Y if you want to use either protocol on your MultiGate boards.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called
+ comx-proto-ppp.o.
+
+MultiGate LAPB protocol support
+CONFIG_COMX_PROTO_LAPB
+ LAPB protocol driver for all MultiGate boards. Say Y if you
+ want to use this protocol on your MultiGate boards.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx-proto-lapb.o.
+
+MultiGate Frame Relay protocol support
+CONFIG_COMX_PROTO_FR
+ Frame Relay protocol driver for all MultiGate boards. Say Y if you
+ want to use this protocol on your MultiGate boards.
+
+ If you want to compile this as a module, say M and read
+ Documentation/modules.txt. The module will be called comx-proto-fr.o.
+
Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL)
CONFIG_CYCLADES_SYNC
Cyclom 2X from Cyclades Corporation (http://www.cyclades.com and
@@ -7162,6 +7268,10 @@ CONFIG_SK98LIN
say M here and read Documentation/modules.txt. This is recommended.
The module will be called sk98lin.o.
+MyriCOM Gigabit Ethernet support
+CONFIG_MYRI_SBUS
+ This driver supports MyriCOM Sbus gigabit ethernet cards.
+
AMD LANCE and PCnet (AT1500 and NE2100) support
CONFIG_LANCE
If you have a network (Ethernet) card of this type, say Y and read
@@ -7943,6 +8053,19 @@ CONFIG_IBMOL
Linux Token Ring Project site for the latest information at
http://www.linuxtr.net
+IBM Lanstreamer chipset PCI adapter support
+CONFIG_IBMLS
+ This is support for IBM Lanstreamer PCI Token Ring Cards.
+
+ If you have such an adapter, say Y and read the Token-Ring mini-HOWTO
+ available via FTP (user:anonymous) from
+ ftp://metalab.unc/edu/pub/Linux/docs/HOWTO.
+
+ This driver is also available as a modules ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The modules will be called lanstreamer.o. If you want to compile it as
+ a module, say M here and read Documentation/modules.txt.
+
Generic TMS380 Token Ring ISA/PCI/MCA/EISA adapter support
CONFIG_TMS380TR
This driver provides generic support for token ring adapters
@@ -7989,6 +8112,49 @@ CONFIG_SMCTR
read the Token-Ring mini-HOWTO, available from
http://www.linuxdoc.org/docs.html#howto .
+Sun Happy Meal 10/100baseT support
+CONFIG_HAPPYMEAL
+ This driver supports the "hme" interface present on most Ultra systems
+ and as an option on older Sbus systems. This driver supports both PCI
+ and Sbus devices. This driver also supports the "qfe" quad 100baseT
+ device available in both PCI and Sbus configurations.
+
+ This support is also available as a module called sunhme.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+Sun Lance support
+CONFIG_SUNLANCE
+ This driver supports the "le" interface present on all 32-bit Sparc
+ systems, on some older Ultra systems and as an Sbus option.
+
+ This support is also available as a module called sunlance.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+Sun BigMAC 10/100baseT support (EXPERIMENTAL)
+CONFIG_SUNBMAC
+ This driver supports the "be" interface available as an Sbus option.
+ This is Sun's older 100baseT ethernet device.
+
+ This support is also available as a module called sunbmac.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+Sun QuadEthernet support
+CONFIG_SUNQE
+ This driver supports the "qe" 10baseT ethernet device, available as
+ an Sbus option. Note that this is not the same as Quad FastEthernet
+ "qfe" which is supported by the Happy Meal driver instead.
+
+ This support is also available as a module called sunqe.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
Traffic Shaper (EXPERIMENTAL)
CONFIG_SHAPER
The traffic shaper is a virtual network device that allows you to
@@ -8778,7 +8944,25 @@ CONFIG_USB_RIO500
inserted in and removed from the running kernel whenever you want).
The module will be called rio500.o. If you want to compile it as
a module, say M here and read Documentation/modules.txt.
-
+
+D-Link DSB-R100 FM radio upport
+CONFIG_USB_DSBR
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port. Note that the audio is not digital, and
+ you must connect the line out connector to a sound card or a
+ set of speakers.
+
+ This driver uses the Video For Linux API. You must enable
+ (Y or M in config) Video For Linux (under Character Devices)
+ to use this driver. Information on this API and pointers to
+ "v4l" programs may be found on the WWW at
+ http://roadrunner.swansea.uk.linux.org/v4l.shtml .
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called dsbr100.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt.
+
ACPI support
CONFIG_ACPI
Advanced Configuration and Power Interface (ACPI) is an interface
@@ -12465,7 +12649,24 @@ CONFIG_SUN4
SPARC ESP SCSI support
CONFIG_SCSI_SUNESP
This is the driver for the Sun ESP SCSI host adapter. The ESP
- chipset is present in most SPARC-based computers.
+ chipset is present in most SPARC SBUS-based computers.
+
+ This support is also available as a module called esp.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
+
+PTI Qlogic, ISP Driver
+CONFIG_SCSI_QLOGICPTI
+ This driver supports SBUS SCSI controllers from PTI or QLogic. These
+ controllers are known under Solaris as qpti and in the openprom as
+ PTI,ptisp or QLGC,isp. Note that PCI QLogic SCSI controllers are driven
+ by a different driver.
+
+ This support is also available as a module called qlogicpti.o ( = code
+ which can be inserted in and removed from the running kernel
+ whenever you want). If you want to compile it as a module, say M
+ here and read Documentation/modules.txt.
SPARC /dev/openprom compatibility driver (EXPERIMENTAL)
CONFIG_SUN_OPENPROMIO
@@ -12477,24 +12678,101 @@ CONFIG_SUN_OPENPROMIO
inserted in and removed from the running kernel whenever you want),
say M and read Documentation/modules.txt. If unsure, say Y.
+Openprom tree appears in /proc/openprom
+CONFIG_SUN_OPENPROMFS
+ If you say Y, the OpenPROM device tree will be available as a virtual
+ file system, which you can mount to /proc/openprom by
+ "mount -t openpromfs none /proc/openprom".
+
+ If you want to compile the /proc/openprom support as a module ( = code
+ which can be inserted in and removed from the running kernel whenever
+ you want), say M here and read Documentation/modules.txt. The module
+ will be called openpromfs.o. If unsure, say M.
+
+Kernel support for Linux/Sparc 32bit binary compatibility
+CONFIG_SPARC32_COMPAT
+ This allows you to run 32-bit binaries on your Ultra.
+ Everybody wants this; say Y.
+
+Kernel support for 32-bit ELF binaries
+CONFIG_BINFMT_ELF32
+ This allows you to run 32-bit Linux/ELF binaries on your Ultra.
+ Everybody wants this; say Y.
+
+Kernel support for 32-bit (ie. SunOS) a.out binaries
+CONFIG_BINFMT_AOUT32
+ This allows you to run 32-bit a.out format binaries on your Ultra.
+ If you want to run SunOS binaries (see SunOS binary emulation below)
+ or other a.out binaries, say Y. If unsure, say N.
+
+SunOS binary emulation
+CONFIG_SUNOS_EMUL
+ This allows you to run most SunOS binaries. If you want to do this,
+ say Y here and place appropriate files in /usr/gnemul/sunos. See
+ http://www.ultralinux.org/faq.html for more information. If you want
+ to run SunOS binaries on an Ultra you must also say Y to "Kernel
+ support for 32-bit a.out binaries" above.
+
Mostek real time clock support
CONFIG_SUN_MOSTEK_RTC
- The Mostek RTC chip is used on all knows Sun computers except
+ The Mostek RTC chip is used on all known Sun computers except
some JavaStations. For a JavaStation you need to say Y both here
and to "Enhanced Real Time Clock Support".
Say Y here unless you are building a special purpose kernel.
+OBP Flash Device support
+CONFIG_OBP_FLASH
+ The OpenBoot PROM on Ultra systems is flashable. If you want to be
+ able to upgrade the OBP firmware, say Y here.
+
JavaStation OS Flash SIMM (EXPERIMENTAL)
CONFIG_SUN_JSFLASH
This option enables a driver for JavaStation OS Flash driver.
Say N unless you want to boot from your Flash SIMM.
-#Siemens SAB82532 serial support
-#CONFIG_SAB82532
-###
-### Please someone fill these in.
-###
+Siemens SAB82532 serial support
+CONFIG_SAB82532
+ This driver supports the serial ports on newer (PCI) Ultra systems.
+ Say Y if you want to be able to use your serial ports.
+
+Aurora Multiboard 1600se (EXPERIMENTAL)
+CONFIG_SUN_AURORA
+ The Aurora Multiboard is a multi-port high-speed serial controller.
+ If you have one of these, say Y.
+
+Audio support (EXPERIMENTAL)
+CONFIG_SPARCAUDIO
+ This driver provides support for the build-in sound devices on most
+ Sun machines. If you want to be able to use this, select this option
+ and one or more of the lowlevel drivers below. See
+ http://www.dementia.org/~shadow/sparcaudio.html for more information.
+
+AMD7930 Lowlevel Driver
+CONFIG_SPARCAUDIO_AMD7930
+ This driver supports the AMD 7930 chip found on sun4c, 4/6xx, and
+ SparcClassic systems.
+
+CS4231 Lowlevel Driver
+CONFIG_SPARCAUDIO_CS4231
+ This driver supports the Crystal Semiconductor CS4231 chip found on
+ the SS4, SS5, and Ultras.
+
+DBRI Lowlevel Driver
+CONFIG_SPARCAUDIO_DBRI
+ This driver supports the DBRI audio interface found on the SS10, SS20,
+ Sparcbook 3, and Voyager systems.
+
+Dummy lowlevel Driver
+CONFIG_SPARCAUDIO_DUMMY
+ This is a pseudo-driver used for debugging and testing the sparcaudio
+ subsystem. Say N unless you want to work on this subsystem.
+
+Sparc hardware (EXPERIMENTAL)
+CONFIG_PARPORT_SUNBPP
+ This driver provides support for the bidirectional parallel port found
+ on many Sun machines. Note that many of the newer Ultras actually have
+ pc style hardware instead.
IEEE 1394 (aka FireWire) support
CONFIG_IEEE1394
@@ -13629,6 +13907,20 @@ Include support for the NetWinder
CONFIG_ARCH_NETWINDER
Say Y here if you intend to run this kernel on the NetWinder.
+Include support for the Compaq Personal Server
+CONFIG_PERSONAL_SERVER
+ Say Y here if you intend to run this kernel on the Compaq
+ Personal Server.
+
+ The Compaq Personal Server is not available for purchase.
+ There are no product plans beyond the current research
+ prototypes at this time. Information is available at:
+
+ http://crl.research.compaq.com/projects/personalserver
+
+ If you have any questions or comments about the Compaq Personal
+ Server, send e-mail to skiff@crl.dec.com
+
Virtual/Physical Memory Split
CONFIG_1GB
If you are compiling a kernel which will never run on a machine
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
new file mode 100644
index 000000000..6bc727e43
--- /dev/null
+++ b/Documentation/DocBook/Makefile
@@ -0,0 +1,29 @@
+BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml
+
+books: docproc $(BOOKS)
+
+docproc:
+ $(MAKE) -C $(TOPDIR)/scripts docproc
+
+wanbook.sgml: wanbook.tmpl
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
+ <wanbook.tmpl >wanbook.sgml
+
+z8530book.sgml: z8530book.tmpl
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
+ <z8530book.tmpl >z8530book.sgml
+
+mcabook.sgml: mcabook.tmpl
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/arch/i386/kernel/mca.c \
+ <mcabook.tmpl >mcabook.sgml
+
+videobook.sgml: videobook.tmpl
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
+ <videobook.tmpl >videobook.sgml
+
+clean:
+ rm -f core *~
+ rm -r $(BOOKS)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/Documentation/DocBook/mcabook.tmpl b/Documentation/DocBook/mcabook.tmpl
new file mode 100644
index 000000000..a8902e333
--- /dev/null
+++ b/Documentation/DocBook/mcabook.tmpl
@@ -0,0 +1,105 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="MCAGuide">
+ <bookinfo>
+ <title>MCA Driver Programming Interface</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Cox</surname>
+ <affiliation>
+ <address>
+ <email>alan@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>David</firstname>
+ <surname>Weinehall</surname>
+ </author>
+ <author>
+ <firstname>Chris</firstname>
+ <surname>Beauregard</surname>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Alan Cox</holder>
+ <holder>David Weinehall</holder>
+ <holder>Chris Beauregard</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ The MCA bus functions provide a generalised interface to find MCA
+ bus cards, to claim them for a driver, and to read and manipulate POS
+ registers without being aware of the motherboard internals or
+ certain deep magic specific to onboard devices.
+ </para>
+ <para>
+ The basic interface to the MCA bus devices is the slot. Each slot
+ is numbered and virtual slot numbers are assigned to the internal
+ devices. Using a pci_dev as other busses do does not really make
+ sense in the MCA context as the MCA bus resources require card
+ specific interpretation.
+ </para>
+ <para>
+ Finally the MCA bus functions provide a parallel set of DMA
+ functions mimicing the ISA bus DMA functions as closely as possible,
+ although also supporting the additional DMA functionality on the
+ MCA bus controllers.
+ </para>
+ </chapter>
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ None.
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+!Earch/i386/kernel/mca.c
+ </chapter>
+
+ <chapter id="dmafunctions">
+ <title>DMA Functions Provided</title>
+!Iinclude/asm-i386/mca_dma.h
+ </chapter>
+
+</book>
diff --git a/Documentation/DocBook/parport-multi.fig b/Documentation/DocBook/parport-multi.fig
new file mode 100644
index 000000000..e0517b36f
--- /dev/null
+++ b/Documentation/DocBook/parport-multi.fig
@@ -0,0 +1,59 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1425 4350 5175 5475
+6 3450 5100 4425 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4425 5475 4425 5100 3450 5100 3450 5475 4425 5475
+4 0 0 50 0 0 12 0.0000 4 135 510 3600 5400 Printer\001
+-6
+6 3375 4350 5175 4725
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 5175 4725 5175 4350 3375 4350 3375 4725 5175 4725
+4 0 0 50 0 0 12 0.0000 4 180 870 3825 4650 Multiplexor\001
+-6
+6 1425 4650 2775 5475
+6 1425 4650 2775 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 6 0 0 5
+ 2757 5475 2757 4650 1425 4650 1425 5475 2757 5475
+4 0 0 50 0 0 12 0.0000 4 180 735 1725 5100 Computer\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2775 4875 2700 4875 2700 5025 2775 5025 2775 4875
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2775 5175 2700 5175 2700 5325 2775 5325 2775 5175
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 2775 4950 3600 4725
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 2775 5250 3450 5325
+-6
+6 3150 2625 4125 3525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4125 3075 4125 2625 3150 2625 3150 3075 4125 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3675 3075 3675 3525
+4 0 0 50 0 0 12 0.0000 4 135 510 3300 2925 Printer\001
+-6
+6 4275 3450 5250 4350
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 5250 3900 5250 3450 4275 3450 4275 3900 5250 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 4800 3900 4800 4350
+4 0 0 50 0 0 12 0.0000 4 135 510 4425 3750 Printer\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3900 4050 3900 3525 3375 3525 3375 4050 3900 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+ 3675 4050 3675 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3600 4350 3750 4350 3750 4425 3600 4425 3600 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 4725 4350 4875 4350 4875 4425 4725 4425 4725 4350
+4 0 0 50 0 0 12 0.0000 4 135 285 3450 3900 ZIP\001
diff --git a/Documentation/DocBook/parport-share.fig b/Documentation/DocBook/parport-share.fig
new file mode 100644
index 000000000..fe4f37322
--- /dev/null
+++ b/Documentation/DocBook/parport-share.fig
@@ -0,0 +1,154 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #8e8e8e
+0 33 #8e8e8e
+0 34 #aeaaae
+0 35 #515551
+0 36 #414141
+0 37 #868286
+0 38 #8e8e8e
+0 39 #414141
+0 40 #868286
+0 41 #c7c3c7
+0 42 #e7e3e7
+0 43 #414141
+0 44 #868286
+0 45 #c7c3c7
+0 46 #e7e3e7
+0 47 #868286
+0 48 #c7c3c7
+0 49 #e7e3e7
+6 1200 3000 2250 4950
+6 1275 3150 2175 3675
+6 1312 3487 1837 3637
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 5
+ 1312 3562 1312 3524 1474 3524 1474 3487 1675 3487
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1474 3637 1474 3562 1675 3562
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1675 3524 1837 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1675 3487 1675 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+ 1312 3562 1474 3562
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 5
+ 1474 3637 1675 3637 1675 3562 1837 3562 1837 3524
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1716 3637 1797 3637 1797 3600
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1716 3637 1716 3600 1797 3600
+-6
+6 1413 3345 2070 3397
+6 1994 3352 2070 3390
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+ 1994 3390 1994 3352 2070 3352
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1994 3390 2070 3390 2070 3352
+-6
+6 1531 3353 1643 3389
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1568 3353 1606 3353 1606 3389 1568 3389 1568 3353
+2 2 0 0 40 39 19 0 20 0.000 2 0 -1 0 0 5
+ 1606 3353 1643 3353 1643 3389 1606 3389 1606 3353
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1568 3353 1531 3353 1531 3389 1568 3389 1568 3353
+-6
+6 1413 3345 1465 3397
+1 3 0 0 0 39 18 0 20 0.000 1 0.0000 1439 3371 26 26 1439 3371 1439 3397
+1 3 0 0 40 41 18 0 20 0.000 1 0.0000 1439 3371 15 15 1439 3371 1443 3385
+-6
+2 2 0 0 40 7 19 0 20 0.000 2 0 -1 0 0 3
+ 1950 3371 1875 3371 1950 3371
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+ 1945 3384 1896 3384 1896 3357 1945 3357 1945 3384
+-6
+6 1350 3183 2100 3300
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+ 1350 3300 1350 3183 2100 3183
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+ 1350 3300 2100 3300 2100 3183
+-6
+2 1 0 1 7 7 19 0 -1 0.000 2 0 -1 0 0 5
+ 1275 3675 1875 3675 1875 3450 2175 3450 2175 3150
+2 1 0 1 40 7 19 0 -1 0.000 2 0 -1 0 0 3
+ 1275 3675 1275 3150 2175 3150
+-6
+6 1950 3750 2175 3975
+5 1 0 1 7 7 19 0 -1 0.000 0 0 0 0 2038.000 3900.000 1985 3953 1985 3847 2091 3847
+5 1 0 1 40 7 19 0 -1 0.000 0 1 0 0 2038.000 3900.000 1985 3953 2091 3953 2091 3847
+-6
+6 1200 4050 1800 4800
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4125 1725 4125
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4200 1725 4200
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4275 1725 4275
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4350 1725 4350
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4425 1725 4425
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4500 1725 4500
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4575 1725 4575
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4650 1725 4650
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+ 1275 4725 1725 4725
+-6
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+ 1200 4950 1425 4950 1425 4911 1200 4911 1200 4950
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+ 2025 4950 2250 4950 2250 4911 2025 4911 2025 4950
+2 2 0 1 0 42 20 0 20 0.000 2 0 -1 0 0 5
+ 1200 4907 2250 4907 2250 3000 1200 3000 1200 4907
+-6
+6 2374 3225 3375 4050
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+ 2374 3402 3139 3402 3257 4050
+ 0.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+ 2374 3461 3096 3437 3198 4050
+ 0.000 -1.000 0.000
+-6
+2 2 0 1 0 1 50 0 20 0.000 0 0 -1 0 0 5
+ 2925 4575 4050 4575 4050 4875 2925 4875 2925 4575
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+ 1200 3000 1575 2475 2400 2475 2250 3000 1200 3000
+2 3 0 1 0 8 50 0 20 0.000 0 0 -1 0 0 5
+ 2925 4575 3000 4200 4050 4200 4050 4575 2925 4575
+2 2 0 1 0 0 50 0 20 0.000 0 0 -1 0 0 5
+ 3075 4725 3900 4725 3900 4800 3075 4800 3075 4725
+2 2 0 1 0 46 50 0 20 0.000 0 0 -1 0 0 5
+ 4800 3975 6450 3975 6450 4875 4800 4875 4800 3975
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+ 5025 4575 6225 4575 6225 4725 5025 4725 5025 4575
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+ 5025 3975 6225 3975 6225 3300 5025 3300 5025 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+ 4800 3975 4800 3825 5025 3825 5025 3975 4800 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+ 6225 3825 6375 3825 6450 3975 6225 3975 6225 3825
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+ 2400 2475 2250 3000 2250 4875 2400 4350 2400 2475
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+ 3075 4200 3075 4050 3300 4050 3375 4050 3375 4200 3075 4200
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+ 3900 4200 3900 4050 3675 4050 3600 4050 3600 4200 3900 4200
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+ 3705 4050 3825 3675 4185 3390 4590 3615 4800 4035
+ 0.000 -1.000 -1.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+ 3765 4050 3874 3708 4202 3449 4571 3654 4800 4185
+ 0.000 -1.000 -1.000 -1.000 0.000
+4 0 0 50 0 0 12 0.0000 4 180 735 1350 5400 Computer\001
+4 0 0 50 0 0 12 0.0000 4 180 675 3150 5400 Zip drive\001
+4 0 0 50 0 0 12 0.0000 4 135 510 5325 5400 Printer\001
diff --git a/Documentation/DocBook/parport-structure.fig b/Documentation/DocBook/parport-structure.fig
new file mode 100644
index 000000000..4299ce687
--- /dev/null
+++ b/Documentation/DocBook/parport-structure.fig
@@ -0,0 +1,60 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #414541
+0 33 #8e8e8e
+0 34 #414541
+0 35 #8e8e8e
+0 36 #414541
+0 37 #8e8e8e
+0 38 #414541
+0 39 #8e8e8e
+0 40 #414541
+0 41 #8e8e8e
+0 42 #414541
+0 43 #8e8e8e
+0 44 #414141
+0 45 #868286
+0 46 #c7c3c7
+0 47 #8e8e8e
+0 48 #414141
+0 49 #868286
+0 50 #c7c3c7
+0 51 #e7e3e7
+6 2025 1800 3075 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 3045 2250 3045 1800 2025 1800 2025 2250 3045 2250
+4 0 0 50 0 14 12 0.0000 4 180 210 2400 2100 lp\001
+-6
+6 4125 1800 5175 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 5145 2250 5145 1800 4125 1800 4125 2250 5145 2250
+4 0 0 50 0 14 12 0.0000 4 135 315 4425 2100 ppa\001
+-6
+6 3225 3075 4275 3525
+6 3375 3225 4125 3450
+4 0 0 50 0 14 12 0.0000 4 165 735 3375 3375 parport\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+ 4245 3525 4245 3075 3225 3075 3225 3525 4245 3525
+-6
+6 3000 4350 4500 4800
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4500 4800 4500 4350 3000 4350 3000 4800 4500 4800
+4 0 0 50 0 14 12 0.0000 4 165 1050 3225 4650 parport_pc\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2550 2250 3600 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 4650 2250 3825 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 3750 3525 3750 4350
diff --git a/Documentation/DocBook/parportbook.sgml b/Documentation/DocBook/parportbook.sgml
new file mode 100644
index 000000000..1644748ad
--- /dev/null
+++ b/Documentation/DocBook/parportbook.sgml
@@ -0,0 +1,1747 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="ParportGuide">
+ <bookinfo>
+ <title>The Parallel Port Subsystem</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Tim</firstname>
+ <surname>Waugh</surname>
+ <affiliation>
+ <address>
+ <email>twaugh@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>1999-2000</year>
+ <holder>Tim Waugh</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+<chapter id="design">
+<title>Design goals</title>
+
+<sect1>
+<title>The problems</title>
+
+<!-- Short-comings -->
+<!-- How they are addressed -->
+
+<!-- Short-comings
+ - simplistic lp driver
+ - platform differences
+ - no support for Zip drive pass-through
+ - no support for readback? When did Carsten add it?
+ - more parallel port devices. Figures?
+ - IEEE 1284 transfer modes: no advanced modes
+ -->
+
+<para>The first parallel port support for Linux came with the line
+printer driver, <filename>lp</filename>. The printer driver is a
+character special device, and (in Linux 2.0) had support for writing,
+via <function>write</function>, and configuration and statistics
+reporting via <function>ioctl</function>.</para>
+
+<para>The printer driver could be used on any computer that had an IBM
+PC-compatible parallel port. Because some architectures have parallel
+ports that aren't really the same as PC-style ports, other variants of
+the printer driver were written in order to support Amiga and Atari
+parallel ports.</para>
+
+<para>When the Iomega Zip drive was released, and a driver written for
+it, a problem became apparent. The Zip drive is a parallel port
+device that provides a parallel port of its own---it is designed to
+sit between a computer and an attached printer, with the printer
+plugged into the Zip drive, and the Zip drive plugged into the
+computer.</para>
+
+<para>The problem was that, although printers and Zip drives were both
+supported, for any given port only one could be used at a time. Only
+one of the two drivers could be present in the kernel at once. This
+was because of the fact that both drivers wanted to drive the same
+hardware---the parallel port. When the printer driver initialised, it
+would call the <function>check_region</function> function to make sure
+that the IO region associated with the parallel port was free, and
+then it would call <function>request_region</function> to allocate it.
+The Zip drive used the same mechanism. Whichever driver initialised
+first would gain exclusive control of the parallel port.</para>
+
+<para>The only way around this problem at the time was to make sure
+that both drivers were available as loadable kernel modules. To use
+the printer, load the printer driver module; then for the Zip drive,
+unload the printer driver module and load the Zip driver
+module.</para>
+
+<para>The net effect was that printing a document that was stored on a Zip
+drive was a bit of an ordeal, at least if the Zip drive and printer
+shared a parallel port. A better solution was needed.</para>
+
+<para>Zip drives are not the only devices that presented problems for
+Linux. There are other devices with pass-through ports, for example
+parallel port CD-ROM drives. There are also printers that report
+their status textually rather than using simple error pins: sending a
+command to the printer can cause it to report the number of pages that
+it has ever printed, or how much free memory it has, or whether it is
+running out of toner, and so on. The printer driver didn't originally
+offer any facility for reading back this information (although Carsten
+Gross added nibble mode readback support for kernel 2.2).</para>
+
+<!-- IEEE 1284 transfer modes: no advanced modes -->
+
+<para>The IEEE has issued a standards document called IEEE 1284, which
+documents existing practice for parallel port communications in a
+variety of modes. Those modes are: <quote>compatibility</quote>,
+reverse nibble, reverse byte, ECP and EPP. Newer devices often use
+the more advanced modes of transfer (ECP and EPP). In Linux 2.0, the
+printer driver only supported <quote>compatibility mode</quote>
+(i.e. normal printer protocol) and reverse nibble mode.</para>
+
+</sect1>
+
+<sect1>
+<title>The solutions</title>
+
+<!-- How they are addressed
+ - sharing model
+ - overview of structure (i.e. port drivers) in 2.2 and 2.3.
+ - IEEE 1284 stuff
+ - whether or not 'platform independence' goal was met
+ -->
+
+<para>The <filename>parport</filename> code in Linux 2.2 was designed
+to meet these problems of architectural differences in parallel ports,
+of port-sharing between devices with pass-through ports, and of lack
+of support for IEEE 1284 transfer modes.</para>
+
+<!-- platform differences -->
+
+<para>There are two layers to the
+<filename>parport</filename> subsystem, only one of which deals
+directly with the hardware. The other layer deals with sharing and
+IEEE 1284 transfer modes. In this way, parallel support for a
+particular architecture comes in the form of a module which registers
+itself with the generic sharing layer.</para>
+
+<!-- sharing model -->
+
+<para>The sharing model provided by the <filename>parport</filename>
+subsystem is one of exclusive access. A device driver, such as the
+printer driver, must ask the <filename>parport</filename> layer for
+access to the port, and can only use the port once access has been
+granted. When it has finished a <quote>transaction</quote>, it can
+tell the <filename>parport</filename> layer that it may release the
+port for other device drivers to use.</para>
+
+<!-- talk a bit about how drivers can share devices on the same port -->
+
+<para>Devices with pass-through ports all manage to share a parallel
+port with other devices in generally the same way. The device has a
+latch for each of the pins on its pass-through port. The normal state
+of affairs is pass-through mode, with the device copying the signal
+lines between its host port and its pass-through port. When the
+device sees a special signal from the host port, it latches the
+pass-through port so that devices further downstream don't get
+confused by the pass-through device's conversation with the host
+parallel port: the device connected to the pass-through port (and any
+devices connected in turn to it) are effectively cut off from the
+computer. When the pass-through device has completed its transaction
+with the computer, it enables the pass-through port again.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata Align=center scalefit=1 fileref="parport-share.eps">
+</imageobject>
+</mediaobject>
+
+<para>This technique relies on certain <quote>special signals</quote>
+being invisible to devices that aren't watching for them. This tends
+to mean only changing the data signals and leaving the control signals
+alone. IEEE 1284.3 documents a standard protocol for daisy-chaining
+devices together with parallel ports.</para>
+
+<!-- transfer modes -->
+
+<para>Support for standard transfer modes are provided as operations
+that can be performed on a port, along with operations for setting the
+data lines, or the control lines, or reading the status lines. These
+operations appear to the device driver as function pointers; more
+later.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="transfermodes">
+<title>Standard transfer modes</title>
+
+<!-- Defined by IEEE, but in common use (even though there are widely -->
+<!-- varying implementations). -->
+
+<para>The <quote>standard</quote> transfer modes in use over the
+parallel port are <quote>defined</quote> by a document called IEEE
+1284. It really just codifies existing practice and documents
+protocols (and variations on protocols) that have been in common use
+for quite some time.</para>
+
+<para>The original definitions of which pin did what were set out by
+Centronics Data Computer Corporation, but only the printer-side
+interface signals were specified.</para>
+
+<para>By the early 1980s, IBM's host-side implementation had become
+the most widely used. New printers emerged that claimed Centronics
+compatibility, but although compatible with Centronics they differed
+from one another in a number of ways.</para>
+
+<para>As a result of this, when IEEE 1284 was published in 1994, all
+that it could really do was document the various protocols that are
+used for printers (there are about six variations on a theme).</para>
+
+<para>In addition to the protocol used to talk to
+Centronics-compatible printers, IEEE 1284 defined other protocols that
+are used for unidirectional peripheral-to-host transfers (reverse
+nibble and reverse byte) and for fast bidirectional transfers (ECP and
+EPP).</para>
+
+</chapter>
+
+<chapter id="structure">
+<title>Structure</title>
+
+<!-- Main structure
+ - sharing core
+ - parports and their IEEE 1284 overrides
+ - IEEE 1284 transfer modes for generic ports
+ - maybe mention muxes here
+ - pardevices
+ - IEEE 1284.3 API
+ -->
+
+<!-- Diagram -->
+
+<mediaobject>
+<imageobject>
+<imagedata Align=Center ScaleFit=1 fileref="parport-structure.eps">
+</imageobject>
+</mediaobject>
+
+<sect1>
+<title>Sharing core</title>
+
+<!-- sharing core -->
+
+<para>At the core of the <filename>parport</filename> subsystem is the
+sharing mechanism (see <filename>drivers/parport/share.c</filename>).
+This module, <filename>parport</filename>, is responsible for
+keeping track of which ports there are in the system, which device
+drivers might be interested in new ports, and whether or not each port
+is available for use (or if not, which driver is currently using
+it).</para>
+
+</sect1>
+
+<sect1>
+<title>Parports and their overrides</title>
+<!-- parports and their overrides -->
+
+<para>The generic <filename>parport</filename> sharing code doesn't
+directly handle the parallel port hardware. That is done instead by
+<quote>low-level</quote> <filename>parport</filename> drivers. The
+function of a low-level <filename>parport</filename> driver is to
+detect parallel ports, register them with the sharing code, and
+provide a list of access functions for each port.</para>
+
+<para>The most basic access functions that must be provided are ones
+for examining the status lines, for setting the control lines, and for
+setting the data lines. There are also access functions for setting
+the direction of the data lines; normally they are in the
+<quote>forward</quote> direction (that is, the computer drives them),
+but some ports allow switching to <quote>reverse</quote> mode (driven
+by the peripheral). There is an access function for examining the
+data lines once in reverse mode.</para>
+
+</sect1>
+
+<sect1>
+<title>IEEE 1284 transfer modes</title>
+<!-- IEEE 1284 transfer modes -->
+
+<para>Stacked on top of the sharing mechanism, but still in the
+<filename>parport</filename> module, are functions for transferring
+data. They are provided for the device drivers to use, and are very
+much like library routines. Since these transfer functions are
+provided by the generic <filename>parport</filename> core they must
+use the <quote>lowest common denominator</quote> set of access
+functions: they can set the control lines, examine the status lines,
+and use the data lines. With some parallel ports the data lines can
+only be set and not examined, and with other ports accessing the data
+register causes control line activity; with these types of situations,
+the IEEE 1284 transfer functions make a best effort attempt to do the
+right thing. In some cases, it is not physically possible to use
+particular IEEE 1284 transfer modes.</para>
+
+<para>The low-level <filename>parport</filename> drivers also provide
+IEEE 1284 transfer functions, as names in the access function list.
+The low-level driver can just name the generic IEEE 1284 transfer
+functions for this. Some parallel ports can do IEEE 1284 transfers in
+hardware; for those ports, the low-level driver can provide functions
+to utilise that feature.</para>
+
+</sect1>
+
+<!-- muxes? -->
+
+<!-- pardevices and pardrivers -->
+
+<sect1>
+<title>Pardevices and parport_drivers</title>
+
+<para>When a parallel port device driver (such as
+<filename>lp</filename>) initialises it tells the sharing layer about
+itself using <function>parport_register_driver</function>. The
+information is put into a <structname>struct
+parport_driver</structname>, which is put into a linked list. The
+information in a <structname>struct parport_driver</structname> really
+just amounts to some function pointers to callbacks in the parallel
+port device driver.</para>
+
+<para>During its initialisation, a low-level port driver tells the
+sharing layer about all the ports that it has found (using
+<function>parport_register_port</function>), and the sharing layer
+creates a <structname>struct parport</structname> for each of them.
+Each <structname>struct parport</structname> contains (among other
+things) a pointer to a <structname>struct
+parport_operations</structname>, which is a list of function pointers
+for the various operations that can be performed on a port. You can
+think of a <structname>struct parport</structname> as a parallel port
+<quote>object</quote>, if <quote>object-orientated</quote> programming
+is your thing. The <structname>parport</structname> structures are
+chained in a linked list, whose head is <varname>portlist</varname>
+(in <filename>drivers/parport/share.c</filename>).</para>
+
+<para>Once the port has been registered, the low-level port driver
+announces it. The <function>parport_announce_port</function> function
+walks down the list of parallel port device drivers
+(<structname>struct parport_driver</structname>s) calling the
+<function>attach</function> function of each.</para>
+
+<para>Similarly, a low-level port driver can undo the effect of
+registering a port with the
+<function>parport_unregister_port</function> function, and device
+drivers are notified using the <function>detach</function>
+callback.</para>
+
+<para>Device drivers can undo the effect of registering themselves
+with the <function>parport_unregister_driver</function>
+function.</para>
+
+</sect1>
+
+<!-- IEEE 1284.3 API -->
+
+<sect1>
+<title>The IEEE 1284.3 API</title>
+
+<para>The ability to daisy-chain devices is very useful, but if every
+device does it in a different way it could lead to lots of
+complications for device driver writers. Fortunately, the IEEE are
+standardising it in IEEE 1284.3, which covers daisy-chain devices and
+port multiplexors.</para>
+
+<para>At the time of writing, IEEE 1284.3 has not been published, but
+the draft specifies the on-the-wire protocol for daisy-chaining and
+multiplexing, and also suggests a programming interface for using it.
+That interface (or most of it) has been implemented in the
+<filename>parport</filename> code in Linux.</para>
+
+<para>At initialisation of the parallel port <quote>bus</quote>, daisy-chained
+devices are assigned addresses starting from zero. There can only be
+four devices with daisy-chain addresses, plus one device on the end
+that doesn't know about daisy-chaining and thinks it's connected
+directly to a computer.</para>
+
+<para>Another way of connecting more parallel port devices is to use a
+multiplexor. The idea is to have a device that is connected directly
+to a parallel port on a computer, but has a number of parallel ports
+on the other side for other peripherals to connect to (two or four
+ports are allowed). The multiplexor switches control to different
+ports under software control---it is, in effect, a programmable
+printer switch.</para>
+
+<para>Combining the ability of daisy-chaining five devices together
+with the ability to multiplex one parallel port between four gives the
+potential to have twenty peripherals connected to the same parallel
+port!</para>
+
+<para>In addition, of course, a single computer can have multiple
+parallel ports. So, each parallel port peripheral in the system can
+be identified with three numbers, or co-ordinates: the parallel port,
+the multiplexed port, and the daisy-chain address.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata align=center scalefit=1 fileref="parport-multi.eps">
+</imageobject>
+</mediaobject>
+
+<!-- x parport_open -->
+<!-- x parport_close -->
+<!-- x parport_device_id -->
+<!-- x parport_device_num -->
+<!-- x parport_device_coords -->
+<!-- x parport_find_device -->
+<!-- x parport_find_class -->
+
+<para>Each device in the system is numbered at initialisation (by
+<function>parport_daisy_init</function>). You can convert between
+this device number and its co-ordinates with
+<function>parport_device_num</function> and
+<function>parport_device_coords</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_device_num</function></funcdef>
+ <paramdef>int <parameter>parport</parameter></paramdef>
+ <paramdef>int <parameter>mux</parameter></paramdef>
+ <paramdef>int <parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_device_coords</function></funcdef>
+ <paramdef>int <parameter>devnum</parameter></paramdef>
+ <paramdef>int *<parameter>parport</parameter></paramdef>
+ <paramdef>int *<parameter>mux</parameter></paramdef>
+ <paramdef>int *<parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>Any parallel port peripheral will be connected directly or
+indirectly to a parallel port on the system, but it won't have a
+daisy-chain address if it does not know about daisy-chaining, and it
+won't be connected through a multiplexor port if there is no
+multiplexor. The special co-ordinate value <constant>-1</constant> is
+used to indicate these cases.</para>
+
+<para>Two functions are provided for finding devices based on their
+IEEE 1284 Device ID: <function>parport_find_device</function> and
+<function>parport_find_class</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_find_device</function></funcdef>
+ <paramdef>const char *<parameter>mfg</parameter></paramdef>
+ <paramdef>const char *<parameter>mdl</parameter></paramdef>
+ <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_find_class</function></funcdef>
+ <paramdef>parport_device_class <parameter>cls</parameter></paramdef>
+ <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>These functions take a device number (in addition to some other
+things), and return another device number. They walk through the list
+of detected devices until they find one that matches the requirements,
+and then return that device number (or <constant>-1</constant> if
+there are no more such devices). They start their search at the
+device after the one in the list with the number given (at
+<parameter>from</parameter>+1, in other words).</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="drivers">
+<title>Device driver's view</title>
+
+<!-- Cover:
+ - sharing interface, preemption, interrupts, wakeups...
+ - IEEE 1284.3 interface
+ - port operations
+ - why can read data but ctr is faked, etc.
+ -->
+
+<!-- I should take a look at the kernel hackers' guide bit I wrote, -->
+<!-- as that deals with a lot of this. The main complaint with it -->
+<!-- was that there weren't enough examples, but 'The printer -->
+<!-- driver' should deal with that later; might be worth mentioning -->
+<!-- in the text. -->
+
+<para>This section is written from the point of view of the device
+driver programmer, who might be writing a driver for a printer or a
+scanner or else anything that plugs into the parallel port. It
+explains how to use the <filename>parport</filename> interface to find
+parallel ports, use them, and share them with other device
+drivers.</para>
+
+<para>We'll start out with a description of the various functions that
+can be called, and then look at a reasonably simple example of their
+use: the printer driver.</para>
+
+<para>The interactions between the device driver and the
+<filename>parport</filename> layer are as follows. First, the device
+driver registers its existence with <filename>parport</filename>, in
+order to get told about any parallel ports that have been (or will be)
+detected. When it gets told about a parallel port, it then tells
+<filename>parport</filename> that it wants to drive a device on that
+port. Thereafter it can claim exclusive access to the port in order
+to talk to its device.</para>
+
+<para>So, the first thing for the device driver to do is tell
+<filename>parport</filename> that it wants to know what parallel ports
+are on the system. To do this, it uses the
+<function>parport_register_device</function> function:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_driver {
+ const char *name;
+ void (*attach) (struct parport *);
+ void (*detach) (struct parport *);
+ struct parport_driver *next;
+};
+]]></programlisting>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_register_driver</function></funcdef>
+ <paramdef>struct parport_driver *<parameter>driver</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>In other words, the device driver passes pointers to a couple of
+functions to <filename>parport</filename>, and
+<filename>parport</filename> calls <function>attach</function> for
+each port that's detected (and <function>detach</function> for each
+port that disappears -- yes, this can happen).</para>
+
+<para>The next thing that happens is that the device driver tells
+<filename>parport</filename> that it thinks there's a device on the
+port that it can drive. This typically will happen in the driver's
+<function>attach</function> function, and is done with
+<function>parport_register_device</function>:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>void <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>void <parameter>(*irq_func)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>port</parameter> comes from the parameter supplied
+to the <function>attach</function> function when it is called, or
+alternatively can be found from the list of detected parallel ports
+directly with the (now deprecated)
+<function>parport_enumerate</function> function.</para>
+
+<para>The next three parameters, <parameter>pf</parameter>,
+<parameter>kf</parameter>, and <parameter>irq_func</parameter>, are
+more function pointers. These callback functions get called under
+various circumstances, and are always given the
+<parameter>handle</parameter> as one of their parameters.</para>
+
+<para>The preemption callback, <parameter>pf</parameter>, is called
+when the driver has claimed access to the port but another device
+driver wants access. If the driver is willing to let the port go, it
+should return zero and the port will be released on its behalf. There
+is no need to call <function>parport_release</function>. If
+<parameter>pf</parameter> gets called at a bad time for letting the
+port go, it should return non-zero and no action will be taken. It is
+good manners for the driver to try to release the port at the earliest
+opportunity after its preemption callback is called.</para>
+
+<para>The <quote>kick</quote> callback, <parameter>kf</parameter>, is
+called when the port can be claimed for exclusive access; that is,
+<function>parport_claim</function> is guaranteed to succeed inside the
+<quote>kick</quote> callback. If the driver wants to claim the port
+it should do so; otherwise, it need not take any action.</para>
+
+<para>The <parameter>irq_func</parameter> callback is called,
+predictably, when a parallel port interrupt is generated. But it is
+not the only code that hooks on the interrupt. The sequence is this:
+the lowlevel driver is the one that has done
+<function>request_irq</function>; it then does whatever
+hardware-specific things it needs to do to the parallel port hardware
+(for PC-style ports, there is nothing special to do); it then tells
+the IEEE 1284 code about the interrupt, which may involve reacting to
+an IEEE 1284 event, depending on the current IEEE 1284 phase; and
+finally the <parameter>irq_func</parameter> function is called.</para>
+
+<para>None of the callback functions are allowed to block.</para>
+
+<para>The <parameter>flags</parameter> are for telling
+<filename>parport</filename> any requirements or hints that are
+useful. The only useful value here (other than
+<constant>0</constant>, which is the usual value) is
+<constant>PARPORT_DEV_EXCL</constant>. The point of that flag is to
+request exclusive access at all times---once a driver has successfully
+called <function>parport_register_device</function> with that flag, no
+other device drivers will be able to register devices on that port
+(until the successful driver deregisters its device, of
+course).</para>
+
+<para>The <constant>PARPORT_DEV_EXCL</constant> flag is for preventing
+port sharing, and so should only be used when sharing the port with
+other device drivers is impossible and would lead to incorrect
+behaviour. Use it sparingly!</para>
+
+<para>Devices can also be registered by device drivers based on their
+device numbers (the same device numbers as in the previous
+section).</para>
+
+<para>The <function>parport_open</function> function is similar to
+<function>parport_register_device</function>, and
+<function>parport_close</function> is the equivalent of
+<function>parport_unregister_device</function>. The difference is
+that <function>parport_open</function> takes a device number rather
+than a pointer to a <structname>struct parport</structname>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_open</function></funcdef>
+ <paramdef>int <parameter>devnum</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*irqf)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_close</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>int <parameter>(*pf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*kf)</parameter>
+ <funcparams>void *</funcparams></paramdef>
+ <paramdef>int <parameter>(*irqf)</parameter>
+ <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_unregister_device</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The intended use of these functions is during driver
+initialisation while the driver looks for devices that it supports, as
+demonstrated by the following code fragment:</para>
+
+<programlisting>
+<![CDATA[
+int devnum = -1;
+while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM,
+ devnum)) != -1) {
+ struct pardevice *dev = parport_open (devnum, ...);
+ ...
+}
+]]></programlisting>
+
+<para>Once your device driver has registered its device and been
+handed a pointer to a <structname>struct pardevice</structname>, the
+next thing you are likely to want to do is communicate with the device
+you think is there. To do that you'll need to claim access to the
+port.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_claim</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_claim_or_block</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>void <function>parport_release</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>To claim access to the port, use
+<function>parport_claim</function> or
+<function>parport_claim_or_block</function>. The first of these will
+not block, and so can be used from interrupt context. If
+<function>parport_claim</function> succeeds it will return zero and
+the port is available to use. It may fail (returning non-zero) if the
+port is in use by another driver and that driver is not willing to
+relinquish control of the port.</para>
+
+<para>The other function, <function>parport_claim_or_block</function>,
+will block if necessary to wait for the port to be free. If it slept,
+it returns <constant>1</constant>; if it succeeded without needing to
+sleep it returns <constant>0</constant>. If it fails it will return a
+negative error code.</para>
+
+<para>When you have finished communicating with the device, you can
+give up access to the port so that other drivers can communicate with
+their devices. The <function>parport_release</function> function
+cannot fail, but it should not be called without the port claimed.
+Similarly, you should not try to claim the port if you already have it
+claimed.</para>
+
+<para>You may find that although there are convenient points for your
+driver to relinquish the parallel port and allow other drivers to talk
+to their devices, it would be preferable to keep hold of the port.
+The printer driver only needs the port when there is data to print,
+for example, but a network driver (such as PLIP) could be sent a
+remote packet at any time. With PLIP, it is no huge catastrophe if a
+network packet is dropped, since it will likely be sent again, so it
+is possible for that kind of driver to share the port with other
+(pass-through) devices.</para>
+
+<para>The <function>parport_yield</function> and
+<function>parport_yield_blocking</function> functions are for marking
+points in the driver at which other drivers may claim the port and use
+their devices. Yielding the port is similar to releasing it and
+reclaiming it, but it more efficient because nothing is done if there
+are no other devices needing the port. In fact, nothing is done even
+if there are other devices waiting but the current device is still
+within its <quote>timeslice</quote>. The default timeslice is half a
+second, but it can be adjusted via a <filename>/proc</filename>
+entry.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_yield</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_yield_blocking</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The first of these, <function>parport_yield</function>, will not
+block but as a result may fail. The return value for
+<function>parport_yield</function> is the same as for
+<function>parport_claim</function>. The blocking version,
+<function>parport_yield_blocking</function>, has the same return code
+as <function>parport_claim_or_block</function>.</para>
+
+<para>Once the port has been claimed, the device driver can use the
+functions in the <structname>struct parport_operations</structname>
+pointer in the <structname>struct parport</structname> it has a
+pointer to. For example:</para>
+
+<programlisting>
+<![CDATA[
+port->ops->write_data (port, d);
+]]></programlisting>
+
+<para>Some of these operations have <quote>shortcuts</quote>. For
+instance, <function>parport_write_data</function> is equivalent to the
+above, but may be a little bit faster (it's a macro that in some cases
+can avoid needing to indirect through <varname>port</varname> and
+<varname>ops</varname>).</para>
+
+</chapter>
+
+<chapter id="portdrivers">
+<title>Port drivers</title>
+
+<!-- What port drivers are for (i.e. implementing parport objects). -->
+
+<para>To recap, then:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+The device driver registers itself with <filename>parport</filename>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+A low-level driver finds a parallel port and registers it with
+<filename>parport</filename> (these first two things can happen in
+either order). This registration creates a <structname>struct
+parport</structname> which is linked onto a list of known ports.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>parport</filename> calls the <function>attach</function>
+function of each registered device driver, passing it the pointer to
+the new <structname>struct parport</structname>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver gets a handle from <filename>parport</filename>, for
+use with
+<function>parport_claim</function>/<function>release</function>. This
+handle takes the form of a pointer to a <structname>struct
+pardevice</structname>, representing a particular device on the
+parallel port, and is acquired using
+<function>parport_register_device</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver claims the port using
+<function>parport_claim</function> (or
+<function>function_claim_or_block</function>).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Then it goes ahead and uses the port. When finished it releases the
+port.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>The purpose of the low-level drivers, then, is to detect
+parallel ports and provide methods of accessing them
+(i.e. implementing the operations in <structname>struct
+parport_operations</structname>).</para>
+
+<!-- Interaction with sharing engine; port state -->
+<!-- What did I mean by that? -->
+
+<!-- Talk about parport_pc implementation, and contrast with e.g. amiga -->
+
+<para>A more complete description of which operation is supposed to do
+what is available in
+<filename>Documentation/parport-lowlevel.txt</filename>.</para>
+
+</chapter>
+
+<chapter id="lp">
+<title>The printer driver</title>
+
+<!-- Talk the reader through the printer driver. -->
+<!-- Could even talk about parallel port console here. -->
+
+<para>The printer driver, <filename>lp</filename> is a character
+special device driver and a <filename>parport</filename> client. As a
+character special device driver it registers a <structname>struct
+file_operations</structname> using
+<function>register_chrdev</function>, with pointers filled in for
+<structfield>write</structfield>, <structfield>ioctl</structfield>,
+<structfield>open</structfield> and
+<structfield>release</structfield>. As a client of
+<filename>parport</filename>, it registers a <structname>struct
+parport_driver</structname> using
+<function>parport_register_driver</function>, so that
+<filename>parport</filename> knows to call
+<function>lp_attach</function> when a new parallel port is discovered
+(and <function>lp_detach</function> when it goes away).</para>
+
+<para>The parallel port console functionality is also implemented in
+<filename>lp.c</filename>, but that won't be covered here (it's quite
+simple though).</para>
+
+<para>The initialisation of the driver is quite easy to understand
+(see <function>lp_init</function>). The <varname>lp_table</varname>
+is an array of structures that contain information about a specific
+device (the <structname>struct pardevice</structname> associated with
+it, for example). That array is initialised to sensible values first
+of all.</para>
+
+<para>Next, the printer driver calls
+<function>register_chrdev</function> passing it a pointer to
+<varname>lp_fops</varname>, which contains function pointers for the
+printer driver's implementation of <function>open</function>,
+<function>write</function>, and so on. This part is the same as for
+any character special device driver.</para>
+
+<para>After successfully registering itself as a character special
+device driver, the printer driver registers itself as a
+<filename>parport</filename> client using
+<function>parport_register_driver</function>. It passes a pointer to
+this structure:</para>
+
+<programlisting>
+<![CDATA[
+static struct parport_driver lp_driver = {
+ "lp",
+ lp_attach,
+ lp_detach,
+ NULL
+};
+]]></programlisting>
+
+<para>The <function>lp_detach</function> function is not very
+interesting (it does nothing); the interesting bit is
+<function>lp_attach</function>. What goes on here depends on whether
+the user supplied any parameters. The possibilities are: no
+parameters supplied, in which case the printer driver uses every port
+that is detected; the user supplied the parameter <quote>auto</quote>,
+in which case only ports on which the device ID string indicates a
+printer is present are used; or the user supplied a list of parallel
+port numbers to try, in which case only those are used.</para>
+
+<para>For each port that the printer driver wants to use (see
+<function>lp_register</function>), it calls
+<function>parport_register_device</function> and stores the resulting
+<structname>struct pardevice</structname> pointer in the
+<varname>lp_table</varname>. If the user told it to do so, it then
+resets the printer.</para>
+
+<para>The other interesting piece of the printer driver, from the
+point of view of <filename>parport</filename>, is
+<function>lp_write</function>. In this function, the user space
+process has data that it wants printed, and the printer driver hands
+it off to the <filename>parport</filename> code to deal with.</para>
+
+<para>The <filename>parport</filename> functions it uses that we have
+not seen yet are <function>parport_negotiate</function>,
+<function>parport_set_timeout</function>, and
+<function>parport_write</function>. These functions are part of the
+IEEE 1284 implementation.</para>
+
+<para>The way the IEEE 1284 protocol works is that the host tells the
+peripheral what transfer mode it would like to use, and the peripheral
+either accepts that mode or rejects it; if the mode is rejected, the
+host can try again with a different mode. This is the negotation
+phase. Once the peripheral has accepted a particular transfer mode,
+data transfer can begin that mode.</para>
+
+<para>The particular transfer mode that the printer driver wants to
+use is named in IEEE 1284 as <quote>compatibility</quote> mode, and
+the function to request a particular mode is called
+<function>parport_negotiate</function>.</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>int <function>parport_negotiate</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>int <parameter>mode</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>modes</parameter> parameter is a symbolic
+constant representing an IEEE 1284 mode; in this instance, it is
+<constant>IEEE1284_MODE_COMPAT</constant>. (Compatibility mode is
+slightly different to the other modes---rather than being specifically
+requested, it is the default until another mode is selected.)</para>
+
+<para>Back to <function>lp_write</function> then. First, access to
+the parallel port is secured with
+<function>parport_claim_or_block</function>. At this point the driver
+might sleep, waiting for another driver (perhaps a Zip drive driver,
+for instance) to let the port go. Next, it goes to compatibility mode
+using <function>parport_negotiate</function>.</para>
+
+<para>The main work is done in the write-loop. In particular, the
+line that hands the data over to <filename>parport</filename>
+reads:</para>
+
+<programlisting>
+<![CDATA[
+ written = parport_write (port, kbuf, copy_size);
+]]></programlisting>
+
+<para>The <function>parport_write</function> function writes data to
+the peripheral using the currently selected transfer mode
+(compatibility mode, in this case). It returns the number of bytes
+successfully written:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>ssize_t <function>parport_write</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>const void *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+ <funcdef>ssize_t <function>parport_read</function></funcdef>
+ <paramdef>struct parport *<parameter>port</parameter></paramdef>
+ <paramdef>void *<parameter>buf</parameter></paramdef>
+ <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>(<function>parport_read</function> does what it sounds like, but
+only works for modes in which reverse transfer is possible. Of
+course, <function>parport_write</function> only works in modes in
+which forward transfer is possible, too.)</para>
+
+<para>The <parameter>buf</parameter> pointer should be to kernel space
+memory, and obviously the <parameter>len</parameter> parameter
+specifies the amount of data to transfer.</para>
+
+<para>In fact what <function>parport_write</function> does is call the
+appropriate block transfer function from the <structname>struct
+parport_operations</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_operations {
+ [...]
+
+ /* Block read/write */
+ size_t (*epp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*epp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len,
+ int flags);
+
+ size_t (*ecp_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len,
+ int flags);
+ size_t (*ecp_write_addr) (struct parport *port, const void *buf,
+ size_t len, int flags);
+
+ size_t (*compat_write_data) (struct parport *port, const void *buf,
+ size_t len, int flags);
+ size_t (*nibble_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
+ size_t (*byte_read_data) (struct parport *port, void *buf,
+ size_t len, int flags);
+};
+]]></programlisting>
+
+<para>The transfer code in <filename>parport</filename> will tolerate
+a data transfer stall only for so long, and this timeout can be
+specified with <function>parport_set_timeout</function>, which returns
+the previous timeout:</para>
+
+<funcsynopsis><funcprototype>
+ <funcdef>long <function>parport_set_timeout</function></funcdef>
+ <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+ <paramdef>long <parameter>inactivity</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>This timeout is specific to the device, and is restored on
+<function>parport_claim</function>.</para>
+
+</chapter>
+
+<chapter id="ppdev">
+<title>User-level device drivers</title>
+
+<!-- ppdev -->
+<sect1>
+<title>Introduction to ppdev</title>
+
+<para>The printer is accessible through <filename>/dev/lp0</filename>;
+in the same way, the parallel port itself is accessible through
+<filename>/dev/parport0</filename>. The difference is in the level of
+control that you have over the wires in the parallel port
+cable.</para>
+
+<para>With the printer driver, a user-space program (such as the
+printer spooler) can send bytes in <quote>printer protocol</quote>.
+Briefly, this means that for each byte, the eight data lines are set
+up, then a <quote>strobe</quote> line tells the printer to look at the
+data lines, and the printer sets an <quote>acknowledgement</quote>
+line to say that it got the byte. The printer driver also allows the
+user-space program to read bytes in <quote>nibble mode</quote>, which
+is a way of transferring data from the peripheral to the computer half
+a byte at a time (and so it's quite slow).</para>
+
+<para>In contrast, the <filename>ppdev</filename> driver (accessed via
+<filename>/dev/parport0</filename>) allows you to:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+examine status lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set control lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set/examine data lines (and control the direction of the data lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+wait for an interrupt (triggered by one of the status lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+find out how many new interrupts have occurred,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set up a response to an interrupt,
+</para>
+</listitem>
+
+<listitem>
+<para>
+use IEEE 1284 negotiation (for telling peripheral which transfer mode,
+to use)
+</para>
+</listitem>
+
+<listitem>
+<para>
+transfer data using a specified IEEE 1284 mode.
+</para>
+</listitem>
+
+</itemizedlist>
+
+</sect1>
+
+<sect1>
+<title>User-level or kernel-level driver?</title>
+
+<para>The decision of whether to choose to write a kernel-level device
+driver or a user-level device driver depends on several factors. One
+of the main ones from a practical point of view is speed: kernel-level
+device drivers get to run faster because they are not preemptable,
+unlike user-level applications.</para>
+
+<para>Another factor is ease of development. It is in general easier
+to write a user-level driver because (a) one wrong move does not
+result in a crashed machine, (b) you have access to user libraries
+(such as the C library), and (c) debugging is easier.</para>
+
+</sect1>
+
+<sect1>
+<title>Programming interface</title>
+
+<para>The <filename>ppdev</filename> interface is largely the same as
+that of other character special devices, in that it supports
+<function>open</function>, <function>close</function>,
+<function>read</function>, <function>write</function>, and
+<function>ioctl</function>.</para>
+
+<sect2>
+<title>Starting and stopping: <function>open</function> and
+<function>close</function></title>
+
+<para>The device node <filename>/dev/parport0</filename> represents
+any device that is connected to <filename>parport0</filename>, the
+first parallel port in the system. Each time the device node is
+opened, it represents (to the process doing the opening) a different
+device. It can be opened more than once, but only one instance can
+actually be in control of the parallel port at any time. A process
+that has opened <filename>/dev/parport0</filename> shares the parallel
+port in the same way as any other device driver. A user-land driver
+may be sharing the parallel port with in-kernel device drivers as well
+as other user-land drivers.</para>
+</sect2>
+
+<sect2>
+<title>Control: <function>ioctl</function></title>
+
+<para>Most of the control is done, naturally enough, via the
+<function>ioctl</function> call. Using <function>ioctl</function>,
+the user-land driver can control both the <filename>ppdev</filename>
+driver in the kernel and the physical parallel port itself. The
+<function>ioctl</function> call takes as parameters a file descriptor
+(the one returned from opening the device node), a command, and
+optionally (a pointer to) some data.</para>
+
+<variablelist>
+<varlistentry><term><constant>PPCLAIM</constant></term>
+<listitem>
+
+<para>Claims access to the port. As a user-land device driver writer,
+you will need to do this before you are able to actually change the
+state of the parallel port in any way. Note that some operations only
+affect the <filename>ppdev</filename> driver and not the port, such as
+<constant>PPSETMODE</constant>; they can be performed while access to
+the port is not claimed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPEXCL</constant></term>
+<listitem>
+
+<para>Instructs the kernel driver to forbid any sharing of the port
+with other drivers, i.e. it requests exclusivity. The
+<constant>PPEXCL</constant> command is only valid when the port is not
+already claimed for use, and it may mean that the next
+<constant>PPCLAIM</constant> <function>ioctl</function> will fail:
+some other driver may already have registered itself on that
+port.</para>
+
+<para>Most device drivers don't need exclusive access to the port.
+It's only provided in case it is really needed, for example for
+devices where access to the port is required for extensive periods of
+time (many seconds).</para>
+
+<para>Note that the <constant>PPEXCL</constant>
+<function>ioctl</function> doesn't actually claim the port there and
+then---action is deferred until the <constant>PPCLAIM</constant>
+<function>ioctl</function> is performed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRELEASE</constant></term>
+<listitem>
+
+<para>Releases the port. Releasing the port undoes the effect of
+claiming the port. It allows other device drivers to talk to their
+devices (assuming that there are any).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPYIELD</constant></term>
+<listitem>
+
+<para>Yields the port to another driver. This
+<function>ioctl</function> is a kind of short-hand for releasing the
+port and immediately reclaiming it. It gives other drivers a chance
+to talk to their devices, but afterwards claims the port back. An
+example of using this would be in a user-land printer driver: once a
+few characters have been written we could give the port to another
+device driver for a while, but if we still have characters to send to
+the printer we would want the port back as soon as possible.</para>
+
+<para>It is important not to claim the parallel port for too long, as
+other device drivers will have no time to service their devices. If
+your device does not allow for parallel port sharing at all, it is
+better to claim the parallel port exclusively (see
+<constant>PPEXCL</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPNEGOT</constant></term>
+<listitem>
+
+<para>Performs IEEE 1284 negotiation into a particular mode. Briefly,
+negotiation is the method by which the host and the peripheral decide
+on a protocol to use when transferring data.</para>
+
+<para>An IEEE 1284 compliant device will start out in compatibility
+mode, and then the host can negotiate to another mode (such as
+ECP).</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>; values for this are in
+<filename>parport.h</filename> and include:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>IEEE1284_MODE_COMPAT</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_NIBBLE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_BYTE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_EPP</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_ECP</constant></para></listitem>
+</itemizedlist>
+
+<para>The <constant>PPNEGOT</constant> <function>ioctl</function>
+actually does two things: it performs the on-the-wire negotiation, and
+it sets the behaviour of subsequent
+<function>read</function>/<function>write</function> calls so that
+they use that mode (but see <constant>PPSETMODE</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETMODE</constant></term>
+<listitem>
+
+<para>Sets which IEEE 1284 protocol to use for the
+<function>read</function> and <function>write</function> calls.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPGETTIME</constant></term>
+<listitem>
+
+<para>Retrieves the time-out value. The <function>read</function> and
+<function>write</function> calls will time out if the peripheral
+doesn't respond quickly enough. The <constant>PPGETTIME</constant>
+<function>ioctl</function> retrieves the length of time that the
+peripheral is allowed to have before giving up.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+a <structname>struct timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETTIME</constant></term>
+<listitem>
+
+<para>Sets the time-out. The <function>ioctl</function> parameter
+should be a pointer to a <structname>struct
+timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCONTROL</constant></term>
+<listitem>
+
+<para>Sets the control lines. The <function>ioctl</function>
+parameter is a pointer to an <type>unsigned char</type>, the bitwise
+OR of the control line values in
+<filename>parport.h</filename>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRCONTROL</constant></term>
+<listitem>
+
+<para>Returns the last value written to the control register, in the
+form of an <type>unsigned char</type>: each bit corresponds to a
+control line (although some are unused). The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+<para>This doesn't actually touch the hardware; the last value written
+is remembered in software. This is because some parallel port
+hardware does not offer read access to the control register.</para>
+
+<para>The control lines bits are defined in
+<filename>parport.h</filename>:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>PARPORT_CONTROL_STROBE</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_AUTOFD</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_SELECT</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_INIT</constant></para></listitem>
+</itemizedlist>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPFCONTROL</constant></term>
+<listitem>
+
+<para>Frobs the control lines. Since a common operation is to change
+one of the control signals while leaving the others alone, it would be
+quite inefficient for the user-land driver to have to use
+<constant>PPRCONTROL</constant>, make the change, and then use
+<constant>PPWCONTROL</constant>. Of course, each driver could
+remember what state the control lines are supposed to be in (they are
+never changed by anything else), but in order to provide
+<constant>PPRCONTROL</constant>, <filename>ppdev</filename> must
+remember the state of the control lines anyway.</para>
+
+<para>The <constant>PPFCONTROL</constant> <function>ioctl</function>
+is for <quote>frobbing</quote> control lines, and is like
+<constant>PPWCONTROL</constant> but acts on a restricted set of
+control lines. The <function>ioctl</function> parameter is a pointer
+to a <structname>struct ppdev_frob_struct</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct ppdev_frob_struct {
+ unsigned char mask;
+ unsigned char val;
+};
+]]>
+</programlisting>
+
+<para>The <structfield>mask</structfield> and
+<structfield>val</structfield> fields are bitwise ORs of control line
+names (such as in <constant>PPWCONTROL</constant>). The operation
+performed by <constant>PPFCONTROL</constant> is:</para>
+
+<programlisting>
+<![CDATA[new_ctr = (old_ctr & ~mask) | val;]]>
+</programlisting>
+
+<para>In other words, the signals named in
+<structfield>mask</structfield> are set to the values in
+<structfield>val</structfield>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRSTATUS</constant></term>
+<listitem>
+
+<para>Returns an <type>unsigned char</type> containing bits set for
+each status line that is set (for instance,
+<constant>PARPORT_STATUS_BUSY</constant>). The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPDATADIR</constant></term>
+<listitem>
+
+<para>Controls the data line drivers. Normally the computer's
+parallel port will drive the data lines, but for byte-wide transfers
+from the peripheral to the host it is useful to turn off those drivers
+and let the peripheral drive the signals. (If the drivers on the
+computer's parallel port are left on when this happens, the port might
+be damaged.)</para>
+
+<para>This is only needed in conjunction with
+<constant>PPWDATA</constant> or <constant>PPRDATA</constant>.</para>
+
+<para>The <function>ioctl</function> parameter is a pointer to an
+<type>int</type>. If the <type>int</type> is zero, the drivers are
+turned on (forward direction); if non-zero, the drivers are turned off
+(reverse direction).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWDATA</constant></term>
+<listitem>
+
+<para>Sets the data lines (if in forward mode). The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRDATA</constant></term>
+<listitem>
+
+<para>Reads the data lines (if in reverse mode). The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPCLRIRQ</constant></term>
+<listitem>
+
+<para>Clears the interrupt count. The <filename>ppdev</filename>
+driver keeps a count of interrupts as they are triggered.
+<constant>PPCLRIRQ</constant> stores this count in an
+<type>int</type>, a pointer to which is passed in as the
+<function>ioctl</function> parameter.</para>
+
+<para>In addition, the interrupt count is reset to zero.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCTLONIRQ</constant></term>
+<listitem>
+
+<para>Set a trigger response. Afterwards when an interrupt is
+triggered, the interrupt handler will set the control lines as
+requested. The <function>ioctl</function> parameter is a pointer to
+an <type>unsigned char</type>, which is interpreted in the same way as
+for <constant>PPWCONTROL</constant>.</para>
+
+<para>The reason for this <function>ioctl</function> is simply speed.
+Without this <function>ioctl</function>, responding to an interrupt
+would start in the interrupt handler, switch context to the user-land
+driver via <function>poll</function> or <function>select</function>,
+and then switch context back to the kernel in order to handle
+<constant>PPWCONTROL</constant>. Doing the whole lot in the interrupt
+handler is a lot faster.</para>
+
+</listitem></varlistentry>
+
+<!-- PPSETPHASE? -->
+
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>Transferring data: <function>read</function> and
+<function>write</function></title>
+
+<para>Transferring data using <function>read</function> and
+<function>write</function> is straightforward. The data is
+transferring using the current IEEE 1284 mode (see the
+<constant>PPSETMODE</constant> <function>ioctl</function>). For modes
+which can only transfer data in one direction, only the appropriate
+function will work, of course.</para>
+</sect2>
+
+<sect2>
+<title>Waiting for events: <function>poll</function> and
+<function>select</function></title>
+
+<para>The <filename>ppdev</filename> driver provides user-land device
+drivers with the ability to wait for interrupts, and this is done
+using <function>poll</function> (and <function>select</function>,
+which is implemented in terms of <function>poll</function>).</para>
+
+<para>When a user-land device driver wants to wait for an interrupt,
+it sleeps with <function>poll</function>. When the interrupt arrives,
+<filename>ppdev</filename> wakes it up (with a <quote>read</quote>
+event, although strictly speaking there is nothing to actually
+<function>read</function>).</para>
+
+</sect2>
+
+</sect1>
+
+<sect1>
+<title>Examples</title>
+
+<para>Presented here are two demonstrations of how to write a simple
+printer driver for <filename>ppdev</filename>. Firstly we will use
+the <function>write</function> function, and after that we will drive
+the control and data lines directly.</para>
+
+<para>The first thing to do is to actually open the device.</para>
+
+<programlisting><![CDATA[
+int drive_printer (const char *name)
+{
+ int fd;
+ int mode; /* We'll need this later. */
+
+ fd = open (name, O_RDWR);
+ if (fd == -1) {
+ perror ("open");
+ return 1;
+ }
+]]></programlisting>
+
+<para>Here <varname>name</varname> should be something along the lines
+of <filename>"/dev/parport0"</filename>. (If you don't have any
+<filename>/dev/parport</filename> files, you can make them with
+<command>mknod</command>; they are character special device nodes with
+major 99.)</para>
+
+<para>In order to do anything with the port we need to claim access to
+it.</para>
+
+<programlisting><![CDATA[
+ if (ioctl (fd, PPCLAIM)) {
+ perror ("PPCLAIM");
+ close (fd);
+ return 1;
+ }
+]]></programlisting>
+
+<para>Our printer driver will copy its input (from
+<varname>stdin</varname>) to the printer, and it can do that it one of
+two ways. The first way is to hand it all off to the kernel driver,
+with the knowledge that the protocol that the printer speaks is IEEE
+1284's <quote>compatibility</quote> mode.</para>
+
+<programlisting><![CDATA[
+ /* Switch to compatibility mode. (In fact we don't need
+ * to do this, since we start off in compatibility mode
+ * anyway, but this demonstrates PPNEGOT.)
+ mode = IEEE1284_MODE_COMPAT;
+ if (ioctl (fd, PPNEGOT, &mode)) {
+ perror ("PPNEGOT");
+ close (fd);
+ return 1;
+ }
+
+ for (;;) {
+ char buffer[1000];
+ char *ptr = buffer;
+ size_t got;
+
+ got = read (0 /* stdin */, buffer, 1000);
+ if (got < 0) {
+ perror ("read");
+ close (fd);
+ return 1;
+ }
+
+ if (got == 0)
+ /* End of input */
+ break;
+
+ while (got > 0) {
+ int written = write_printer (fd, ptr, got);
+
+ if (written < 0) {
+ perror ("write");
+ close (fd);
+ return 1;
+ }
+
+ ptr += written;
+ got -= written;
+ }
+ }
+]]></programlisting>
+
+<para>The <function>write_printer</function> function is not pictured
+above. This is because the main loop that is shown can be used for
+both methods of driving the printer. Here is one implementation of
+<function>write_printer</function>:</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+ return write (fd, ptr, count);
+}
+]]></programlisting>
+
+<para>We hand the data to the kernel-level driver (using
+<function>write</function>) and it handles the printer
+protocol.</para>
+
+<para>Now let's do it the hard way! In this particular example there
+is no practical reason to do anything other than just call
+<function>write</function>, because we know that the printer talks an
+IEEE 1284 protocol. On the other hand, this particular example does
+not even need a user-land driver since there is already a kernel-level
+one; for the purpose of this discussion, try to imagine that the
+printer speaks a protocol that is not already implemented under
+Linux.</para>
+
+<para>So, here is the alternative implementation of
+<function>write_printer</function> (for brevity, error checking has
+been omitted):</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+ ssize_t wrote = 0;
+
+ while (wrote < count) {
+ unsigned char status, control, data;
+ unsigned char mask = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ unsigned char val = (PARPORT_STATUS_ERROR
+ | PARPORT_STATUS_BUSY);
+ struct parport_frob_struct frob;
+ struct timespec ts;
+
+ /* Wait for printer to be ready */
+ for (;;) {
+ ioctl (fd, PPRSTATUS, &status);
+
+ if ((status & mask) == val)
+ break;
+
+ ioctl (fd, PPRELEASE);
+ sleep (1);
+ ioctl (fd, PPCLAIM);
+ }
+
+ /* Set the data lines */
+ data = * ((char *) ptr)++;
+ ioctl (fd, PPWDATA, &data);
+
+ /* Delay for a bit */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000;
+ nanosleep (&ts, NULL);
+
+ /* Pulse strobe */
+ frob.mask = PARPORT_CONTROL_STROBE;
+ frob.val = PARPORT_CONTROL_STROBE;
+ ioctl (fd, PPFCONTROL, &frob);
+ nanosleep (&ts, NULL);
+
+ /* End the pulse */
+ frob.val = 0;
+ ioctl (fd, PPFCONTROL, &frob);
+ nanosleep (&ts, NULL);
+
+ wrote++;
+ }
+
+ return wrote;
+}
+]]></programlisting>
+
+<para>To show a bit more of the <filename>ppdev</filename> interface,
+here is a small piece of code that is intended to mimic the printer's
+side of printer protocol.</para>
+
+<programlisting><![CDATA[
+ for (;;)
+ {
+ int irqc;
+ int busy = nAck | nFault;
+ int acking = nFault;
+ int ready = Busy | nAck | nFault;
+ char ch;
+
+ /* Set up the control lines when an interrupt happens. */
+ ioctl (fd, PPWCTLONIRQ, &busy);
+
+ /* Now we're ready. */
+ ioctl (fd, PPWCONTROL, &ready);
+
+ /* Wait for an interrupt. */
+ {
+ fd_set rfds;
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+ if (!select (fd + 1, &rfds, NULL, NULL, NULL))
+ /* Caught a signal? */
+ continue;
+ }
+
+ /* We are now marked as busy. */
+
+ /* Fetch the data. */
+ ioctl (fd, PPRDATA, &ch);
+
+ /* Clear the interrupt. */
+ ioctl (fd, PPCLRIRQ, &irqc);
+ if (irqc > 1)
+ fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
+ irqc - 1, irqc == 2 ? "s" : "");
+
+ /* Ack it. */
+ ioctl (fd, PPWCONTROL, &acking);
+ usleep (2);
+ ioctl (fd, PPWCONTROL, &busy);
+
+ putchar (ch);
+ }
+]]></programlisting>
+
+</sect1>
+
+</chapter>
+</book> \ No newline at end of file
diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl
new file mode 100644
index 000000000..c152abf31
--- /dev/null
+++ b/Documentation/DocBook/videobook.tmpl
@@ -0,0 +1,1663 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="V4LGuide">
+ <bookinfo>
+ <title>Video4Linux Programming</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Cox</surname>
+ <affiliation>
+ <address>
+ <email>alan@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Alan Cox</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ Parts of this document first appeared in Linux Magazine under a
+ ninety day exclusivity.
+ </para>
+ <para>
+ Video4Linux is intended to provide a common programming interface
+ for the many TV and capture cards now on the market, as well as
+ parallel port and USB video cameras. Radio, teletext decoders and
+ vertical blanking data interfaces are also provided.
+ </para>
+ </chapter>
+ <chapter>
+ <title>Radio Devices</title>
+ <para>
+ There are a wide variety of radio interfaces available for PC's, and these
+ are generally very simple to program. The biggest problem with supporting
+ such devices is normally extracting documentation from the vendor.
+ </para>
+ <para>
+ The radio interface supports a simple set of control ioctls standardised
+ across all radio and tv interfaces. It does not support read or write, which
+ are used for video streams. The reason radio cards do not allow you to read
+ the audio stream into an application is that without exception they provide
+ a connection on to a soundcard. Soundcards can be used to read the radio
+ data just fine.
+ </para>
+ <sect1 id="registerradio">
+ <title>Registering Radio Devices</title>
+ <para>
+ The Video4linux core provides an interface for registering devices. The
+ first step in writing our radio card driver is to register it.
+ </para>
+ <programlisting>
+
+
+static struct video_device my_radio
+{
+ "My radio",
+ VID_TYPE_TUNER,
+ VID_HARDWARE_MYRADIO,
+ radio_open.
+ radio_close,
+ NULL, /* no read */
+ NULL, /* no write */
+ NULL, /* no poll */
+ radio_ioctl,
+ NULL, /* no special init function */
+ NULL /* no private data */
+};
+
+
+ </programlisting>
+ <para>
+ This declares our video4linux device driver interface. The VID_TYPE_ value
+ defines what kind of an interface we are, and defines basic capabilities.
+ </para>
+ <para>
+ The only defined value relevant for a radio card is VID_TYPE_TUNER which
+ indicates that the device can be tuned. Clearly our radio is going to have some
+ way to change channel so it is tuneable.
+ </para>
+ <para>
+ The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
+ <email>alan@redhat.com</email> when device drivers are going to be released. Until then you
+ can pull a suitably large number out of your hat and use it. 10000 should be
+ safe for a very long time even allowing for the huge number of vendors
+ making new and different radio cards at the moment.
+ </para>
+ <para>
+ We declare an open and close routine, but we do not need read or write,
+ which are used to read and write video data to or from the card itself. As
+ we have no read or write there is no poll function.
+ </para>
+ <para>
+ The private initialise function is run when the device is registered. In
+ this driver we've already done all the work needed. The final pointer is a
+ private data pointer that can be used by the device driver to attach and
+ retrieve private data structures. We set this field "priv" to NULL for
+ the moment.
+ </para>
+ <para>
+ Having the structure defined is all very well but we now need to register it
+ with the kernel.
+ </para>
+ <programlisting>
+
+
+static int io = 0x320;
+
+int __init myradio_init(struct video_init *v)
+{
+ if(check_region(io, MY_IO_SIZE))
+ {
+ printk(KERN_ERR
+ "myradio: port 0x%03X is in use.\n", io);
+ return -EBUSY;
+ }
+
+ if(video_device_register(&amp;my_radio, VFL_TYPE_RADIO)==-1)
+ return -EINVAL;
+ request_region(io, MY_IO_SIZE, "myradio");
+ return 0;
+}
+
+ </programlisting>
+ <para>
+ The first stage of the initialisation, as is normally the case, is to check
+ that the I/O space we are about to fiddle with doesn't belong to some other
+ driver. If it is we leave well alone. If the user gives the address of the
+ wrong device then we will spot this. These policies will generally avoid
+ crashing the machine.
+ </para>
+ <para>
+ Now we ask the Video4Linux layer to register the device for us. We hand it
+ our carefully designed video_device structure and also tell it which group
+ of devices we want it registered with. In this case VFL_TYPE_RADIO.
+ </para>
+ <para>
+ The types available are
+ </para>
+ <table frame=all><title>Device Types</title>
+ <tgroup cols=3 align=left>
+ <tbody>
+ <row>
+ <entry>VFL_TYPE_RADIO</><>/dev/radio{n}</><>
+
+ Radio devices are assigned in this block. As with all of these
+ selections the actual number assignment is done by the video layer
+ accordijng to what is free.</entry>
+ </row><row>
+ <entry>VFL_TYPE_GRABBER</><>/dev/video{n}</><>
+ Video capture devices and also -- counter-intuitively for the name --
+ hardware video playback devices such as MPEG2 cards.</entry>
+ </row><row>
+ <entry>VFL_TYPE_VBI</><>/dev/vbi{n}</><>
+ The VBI devices capture the hidden lines on a television picture
+ that carry further information like closed caption data, teletext
+ (primarily in Europe) and now Intercast and the ATVEC internet
+ television encodings.</entry>
+ </row><row>
+ <entry>VFL_TYPE_VTX</><>/dev/vtx[n}</><>
+ VTX is 'Videotext' also known as 'Teletext'. This is a system for
+ sending numbered, 40x25, mostly textual page images over the hidden
+ lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder
+ chips. (The use of the word smart here has to be taken in context,
+ the smartest teletext chips are fairly dumb pieces of technology).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ We are most definitely a radio.
+ </para>
+ <para>
+ Finally we allocate our I/O space so that nobody treads on us and return 0
+ to signify general happiness with the state of the universe.
+ </para>
+ </sect1>
+ <sect1 id="openradio">
+ <title>Opening And Closing The Radio</title>
+
+ <para>
+ The functions we declared in our video_device are mostly very simple.
+ Firstly we can drop in what is basically standard code for open and close.
+ </para>
+ <programlisting>
+
+
+static int users = 0;
+
+static int radio_open(stuct video_device *dev, int flags)
+{
+ if(users)
+ return -EBUSY;
+ users++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+ </programlisting>
+ <para>
+ At open time we need to do nothing but check if someone else is also using
+ the radio card. If nobody is using it we make a note that we are using it,
+ then we ensure that nobody unloads our driver on us.
+ </para>
+ <programlisting>
+
+
+static int radio_close(struct video_device *dev)
+{
+ users--;
+ MOD_DEC_USE_COUNT;
+}
+
+ </programlisting>
+ <para>
+ At close time we simply need to reduce the user count and allow the module
+ to become unloadable.
+ </para>
+ <para>
+ If you are sharp you will have noticed neither the open nor the close
+ routines attempt to reset or change the radio settings. This is intentional.
+ It allows an application to set up the radio and exit. It avoids a user
+ having to leave an application running all the time just to listen to the
+ radio.
+ </para>
+ </sect1>
+ <sect1 id="ioctlradio">
+ <title>The Ioctl Interface</title>
+ <para>
+ This leaves the ioctl routine, without which the driver will not be
+ terribly useful to anyone.
+ </para>
+ <programlisting>
+
+
+static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability v;
+ v.type = VID_TYPE_TUNER;
+ v.channels = 1;
+ v.audios = 1;
+ v.maxwidth = 0;
+ v.minwidth = 0;
+ v.maxheight = 0;
+ v.minheight = 0;
+ strcpy(v.name, "My Radio");
+ if(copy_to_user(arg, &amp;v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ VIDIOCGCAP is the first ioctl all video4linux devices must support. It
+ allows the applications to find out what sort of a card they have found and
+ to figure out what they want to do about it. The fields in the structure are
+ </para>
+ <table frame=all><title>struct video_capability fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>name</><>The device text name. This is intended for the user.</>
+ </row><row>
+ <entry>channels</><>The number of different channels you can tune on
+ this card. It could even by zero for a card that has
+ no tuning capability. For our simple FM radio it is 1.
+ An AM/FM radio would report 2.</entry>
+ </row><row>
+ <entry>audios</><>The number of audio inputs on this device. For our
+ radio there is only one audio input.</entry>
+ </row><row>
+ <entry>minwidth,minheight</><>The smallest size the card is capable of capturing
+ images in. We set these to zero. Radios do not
+ capture pictures</entry>
+ </row><row>
+ <entry>maxwidth,maxheight</><>The largest image size the card is capable of
+ capturing. For our radio we report 0.
+ </entry>
+ </row><row>
+ <entry>type</><>This reports the capabilities of the device, and
+ matches the field we filled in in the struct
+ video_device when registering.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Having filled in the fields, we use copy_to_user to copy the structure into
+ the users buffer. If the copy fails we return an EFAULT to the application
+ so that it knows it tried to feed us garbage.
+ </para>
+ <para>
+ The next pair of ioctl operations select which tuner is to be used and let
+ the application find the tuner properties. We have only a single FM band
+ tuner in our example device.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&amp;v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ v.rangelow=(87*16000);
+ v.rangehigh=(108*16000);
+ v.flags = VIDEO_TUNER_LOW;
+ v.mode = VIDEO_MODE_AUTO;
+ v.signal = 0xFFFF;
+ strcpy(v.name, "FM");
+ if(copy_to_user(&amp;v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
+ sets the tuner field to the tuner number it wishes to query. The query does
+ not change the tuner that is being used, it merely enquires about the tuner
+ in question.
+ </para>
+ <para>
+ We have exactly one tuner so after copying the user buffer to our temporary
+ structure we complain if they asked for a tuner other than tuner 0.
+ </para>
+ <para>
+ The video_tuner structure has the following fields
+ </para>
+ <table frame=all><title>struct video_tuner fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>int tuner</><entry>The number of the tuner in question</entry>
+ </row><row>
+ <entry>char name[32]</><entry>A text description of this tuner. "FM" will do fine.
+ This is intended for the application.</entry>
+ </row><row>
+ <entry>u32 flags</>
+ <entry>Tuner capability flags</entry>
+ </row>
+ <row>
+ <entry>u16 mode</><entry>The current reception mode</entry>
+
+ </row><row>
+ <entry>u16 signal</><entry>The signal strength scaled between 0 and 65535. If
+ a device cannot tell the signal strength it should
+ report 65535. Many simple cards contain only a
+ signal/no signal bit. Such cards will report either
+ 0 or 65535.</entry>
+
+ </row><row>
+ <entry>u32 rangelow, rangehigh</><entry>
+ The range of frequencies supported by the radio
+ or TV. It is scaled according to the VIDEO_TUNER_LOW
+ flag.</entry>
+
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_tuner flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_TUNER_PAL</><entry>A PAL TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_NTSC</><entry>An NTSC (US) TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_SECAM</><entry>A SECAM (French) TV tuner</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_LOW</><>
+ The tuner frequency is scaled in 1/16th of a KHz
+ steps. If not it is in 1/16th of a MHz steps
+ </entry>
+ </row><row>
+ <entry>VIDEO_TUNER_NORM</><entry>The tuner can set its format</entry>
+ </row><row>
+ <entry>VIDEO_TUNER_STEREO_ON</><entry>The tuner is currently receiving a stereo signal</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_tuner modes</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_MODE_PAL</><>PAL Format</entry>
+ </row><row>
+ <entry>VIDEO_MODE_NTSC</><>NTSC Format (USA)</entry>
+ </row><row>
+ <entry>VIDEO_MODE_SECAM</><>French Format</entry>
+ </row><row>
+ <entry>VIDEO_MODE_AUTO</><>A device that does not need to do
+ TV format switching</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The settings for the radio card are thus fairly simple. We report that we
+ are a tuner called "FM" for FM radio. In order to get the best tuning
+ resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
+ unlikely our card can do that resolution but it is a fair bet the card can
+ do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
+ radio usage.
+ </para>
+ <para>
+ We report that the tuner automatically handles deciding what format it is
+ receiving - true enough as it only handles FM radio. Our example card is
+ also incapable of detecting stereo or signal strengths so it reports a
+ strength of 0xFFFF (maximum) and no stereo detected.
+ </para>
+ <para>
+ To finish off we set the range that can be tuned to be 87-108Mhz, the normal
+ FM broadcast radio range. It is important to find out what the card is
+ actually capable of tuning. It is easy enough to simply use the FM broadcast
+ range. Unfortunately if you do this you will discover the FM broadcast
+ ranges in the USA, Europe and Japan are all subtly different and some users
+ cannot receive all the stations they wish.
+ </para>
+ <para>
+ The application also needs to be able to set the tuner it wishes to use. In
+ our case, with a single tuner this is rather simple to arrange.
+ </para>
+ <programlisting>
+
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.tuner != 0)
+ return -EINVAL;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ We copy the user supplied structure into kernel memory so we can examine it.
+ If the user has selected a tuner other than zero we reject the request. If
+ they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
+ </para>
+ <para>
+ The next two ioctls we need to provide are to get and set the frequency of
+ the radio. These both use an unsigned long argument which is the frequency.
+ The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
+ mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
+ 1/16ths of a KHz.
+ </para>
+ <programlisting>
+
+static unsigned long current_freq;
+
+
+
+ case VIDIOCGFREQ:
+ if(copy_to_user(arg, &amp;current_freq,
+ sizeof(unsigned long))
+ return -EFAULT;
+ return 0;
+
+ </programlisting>
+ <para>
+ Querying the frequency in our case is relatively simple. Our radio card is
+ too dumb to let us query the signal strength so we remember our setting if
+ we know it. All we have to do is copy it to the user.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCSFREQ:
+ {
+ u32 freq;
+ if(copy_from_user(arg, &amp;freq,
+ sizeof(unsigned long))!=0)
+ return -EFAULT;
+ if(hardware_set_freq(freq)<0)
+ return -EINVAL;
+ current_freq = freq;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ Setting the frequency is a little more complex. We begin by copying the
+ desired frequency into kernel space. Next we call a hardware specific routine
+ to set the radio up. This might be as simple as some scaling and a few
+ writes to an I/O port. For most radio cards it turns out a good deal more
+ complicated and may involve programming things like a phase locked loop on
+ the card. This is what documentation is for.
+ </para>
+ <para>
+ The final set of operations we need to provide for our radio are the
+ volume controls. Not all radio cards can even do volume control. After all
+ there is a perfectly good volume control on the sound card. We will assume
+ our radio card has a simple 4 step volume control.
+ </para>
+ <para>
+ There are two ioctls with audio we need to support
+ </para>
+ <programlisting>
+
+static int current_volume=0;
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio != 0)
+ return -EINVAL;
+ v.volume = 16384*current_volume;
+ v.step = 16384;
+ strcpy(v.name, "Radio");
+ v.mode = VIDEO_SOUND_MONO;
+ v.balance = 0;
+ v.base = 0;
+ v.treble = 0;
+
+ if(copy_to_user(arg. &amp;v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ Much like the tuner we start by copying the user structure into kernel
+ space. Again we check if the user has asked for a valid audio input. We have
+ only input 0 and we punt if they ask for another input.
+ </para>
+ <para>
+ Then we fill in the video_audio structure. This has the following format
+ </para>
+ <table frame=all><title>struct video_audio fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>audio</><>The input the user wishes to query</>
+ </row><row>
+ <entry>volume</><>The volume setting on a scale of 0-65535</>
+ </row><row>
+ <entry>base</><>The base level on a scale of 0-65535</>
+ </row><row>
+ <entry>treble</><>The treble level on a scale of 0-65535</>
+ </row><row>
+ <entry>flags</><>The features this audio device supports
+ </entry>
+ </row><row>
+ <entry>name</><>A text name to display to the user. We picked
+ "Radio" as it explains things quite nicely.</>
+ </row><row>
+ <entry>mode</><>The current reception mode for the audio
+
+ We report MONO because our card is too stupid to know if it is in
+ mono or stereo.
+ </entry>
+ </row><row>
+ <entry>balance</><>The stereo balance on a scale of 0-65535, 32768 is
+ middle.</>
+ </row><row>
+ <entry>step</><>The step by which the volume control jumps. This is
+ used to help make it easy for applications to set
+ slider behaviour.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_audio flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_AUDIO_MUTE</><>The audio is currently muted. We
+ could fake this in our driver but we
+ choose not to bother.</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_MUTABLE</><>The input has a mute option</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_TREBLE</><>The input has a treble control</entry>
+ </row><row>
+ <entry>VIDEO_AUDIO_BASS</><>The input has a base control</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame=all><title>struct video_audio modes</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_SOUND_MONO</><>Mono sound</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_STEREO</><>Stereo sound</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_LANG1</><>Alternative language 1 (TV specific)</entry>
+ </row><row>
+ <entry>VIDEO_SOUND_LANG2</><>Alternative language 2 (TV specific)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Having filled in the structure we copy it back to user space.
+ </para>
+ <para>
+ The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
+ video_audio stucture. The driver does its best to honour the request.
+ </para>
+ <programlisting>
+
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+ current_volume = v/16384;
+ hardware_set_volume(current_volume);
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ In our case there is very little that the user can set. The volume is
+ basically the limit. Note that we could pretend to have a mute feature
+ by rewriting this to
+ </para>
+ <programlisting>
+
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+ current_volume = v/16384;
+ if(v.flags&amp;VIDEO_AUDIO_MUTE)
+ hardware_set_volume(0);
+ else
+ hardware_set_volume(current_volume);
+ current_muted = v.flags &amp;
+ VIDEO_AUDIO_MUTE;
+ return 0;
+ }
+
+ </programlisting>
+ <para>
+ This with the corresponding changes to the VIDIOCGAUDIO code to report the
+ state of the mute flag we save and to report the card has a mute function,
+ will allow applications to use a mute facility with this card. It is
+ questionable whether this is a good idea however. User applications can already
+ fake this themselves and kernel space is precious.
+ </para>
+ <para>
+ We now have a working radio ioctl handler. So we just wrap up the function
+ </para>
+ <programlisting>
+
+
+ }
+ return -ENOIOCTLCMD;
+}
+
+ </programlisting>
+ <para>
+ and pass the Video4Linux layer back an error so that it knows we did not
+ understand the request we got passed.
+ </para>
+ </sect1>
+ <sect1 id="modradio">
+ <title>Module Wrapper</title>
+ <para>
+ Finally we add in the usual module wrapping and the driver is done.
+ </para>
+ <programlisting>
+
+#ifndef MODULE
+
+static int io = 0x300;
+
+#else
+
+static int io = -1;
+
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("A driver for an imaginary radio card.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the card.");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR
+ "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+ return myradio_init(NULL);
+}
+
+void cleanup_module(void)
+{
+ video_unregister_device(&amp;my_radio);
+ release_region(io, MY_IO_SIZE);
+}
+
+#endif
+
+ </programlisting>
+ <para>
+ In this example we set the IO base by default if the driver is compiled into
+ the kernel where you cannot pass a parameter. For the module we require the
+ user sets the parameter. We set io to a nonsense port (-1) so that we can
+ tell if the user supplied an io parameter or not.
+ </para>
+ <para>
+ We use MODULE_ defines to give an author for the card driver and a
+ description. We also use them to declare that io is an integer and it is the
+ address of the card.
+ </para>
+ <para>
+ The clean-up routine unregisters the video_device we registered, and frees
+ up the I/O space. Note that the unregister takes the actual video_device
+ structure as its argument. Unlike the file operations structure which can be
+ shared by all instances of a device a video_device structure as an actual
+ instance of the device. If you are registering multiple radio devices you
+ need to fill in one structure per device (most likely by setting up a
+ template and copying it to each of the actual device structures).
+ </para>
+ </sect1>
+ </chapter>
+ <chapter>
+ <title>Video Capture Devices</title>
+ <sect1 id="introvid">
+ <title>Video Capture Device Types</title>
+ <para>
+ The video capture devices share the same interfaces as radio devices. In
+ order to explain the video capture interface I will use the example of a
+ camera that has no tuners or audio input. This keeps the example relatively
+ clean. To get both combine the two driver examples.
+ </para>
+ <para>
+ Video capture devices divide into four categories. A little technology
+ backgrounder. Full motion video even at television resolution (which is
+ actually fairly low) is pretty resource-intensive. You are continually
+ passing megabytes of data every second from the capture card to the display.
+ several alternative approaches have emerged because copying this through the
+ processor and the user program is a particularly bad idea .
+ </para>
+ <para>
+ The first is to add the television image onto the video output directly.
+ This is also how some 3D cards work. These basic cards can generally drop the
+ video into any chosen rectangle of the display. Cards like this, which
+ include most mpeg1 cards that used the feature connector, aren't very
+ friendly in a windowing environment. They don't understand windows or
+ clipping. The video window is always on the top of the display.
+ </para>
+ <para>
+ Chroma keying is a technique used by cards to get around this. It is an old
+ television mixing trick where you mark all the areas you wish to replace
+ with a single clear colour that isn't used in the image - TV people use an
+ incredibly bright blue while computing people often use a paticularly
+ virulent purple. Bright blue occurs on the desktop. Anyone with virulent
+ purple windows has another problem besides their TV overlay.
+ </para>
+ <para>
+ The third approach is to copy the data from the capture card to the video
+ card, but to do it directly across the PCI bus. This relieves the processor
+ from doing the work but does require some smartness on the part of the video
+ capture chip, as well as a suitable video card. Programming this kind of
+ card and more so debugging it can be extremely tricky. There are some quite
+ complicated interactions with the display and you may also have to cope with
+ various chipset bugs that show up when PCI cards start talking to each
+ other.
+ </para>
+ <para>
+ To keep our example fairly simple we will assume a card that supports
+ overlaying a flat rectangular image onto the frame buffer output, and which
+ can also capture stuff into processor memory.
+ </para>
+ </sect1>
+ <sect1 id="regvid">
+ <title>Registering Video Capture Devices</title>
+ <para>
+ This time we need to add more functions for our camera device.
+ </para>
+ <programlisting>
+static struct video_device my_camera
+{
+ "My Camera",
+ VID_TYPE_OVERLAY|VID_TYPE_SCALES|\
+ VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
+ VID_HARDWARE_MYCAMERA,
+ camera_open.
+ camera_close,
+ camera_read, /* no read */
+ NULL, /* no write */
+ camera_poll, /* no poll */
+ camera_ioctl,
+ NULL, /* no special init function */
+ NULL /* no private data */
+};
+ </programlisting>
+ <para>
+ We need a read() function which is used for capturing data from
+ the card, and we need a poll function so that a driver can wait for the next
+ frame to be captured.
+ </para>
+ <para>
+ We use the extra video capability flags that did not apply to the
+ radio interface. The video related flags are
+ </para>
+ <table frame=all><title>Capture Capabilities</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+<entry>VID_TYPE_CAPTURE</><>We support image capture</>
+</row><row>
+<entry>VID_TYPE_TELETEXT</><>A teletext capture device (vbi{n])</>
+</row><row>
+<entry>VID_TYPE_OVERLAY</><>The image can be directly overlaid onto the
+ frame buffer</>
+</row><row>
+<entry>VID_TYPE_CHROMAKEY</><>Chromakey can be used to select which parts
+ of the image to display</>
+</row><row>
+<entry>VID_TYPE_CLIPPING</><>It is possible to give the board a list of
+ rectangles to draw around. </>
+</row><row>
+<entry>VID_TYPE_FRAMERAM</><>The video capture goes into the video memory
+ and actually changes it. Applications need
+ to know this so they can clean up after the
+ card</>
+</row><row>
+<entry>VID_TYPE_SCALES</><>The image can be scaled to various sizes,
+ rather than being a single fixed size.</>
+</row><row>
+<entry>VID_TYPE_MONOCHROME</><>The capture will be monochrome. This isn't a
+ complete answer to the question since a mono
+ camera on a colour capture card will still
+ produce mono output.</>
+</row><row>
+<entry>VID_TYPE_SUBCAPTURE</><>The card allows only part of its field of
+ view to be captured. This enables
+ applications to avoid copying all of a large
+ image into memory when only some section is
+ relevant.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ We set VID_TYPE_CAPTURE so that we are seen as a capture card,
+ VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
+ purple, and VID_TYPE_SCALES because we can be resized.
+ </para>
+ <para>
+ Our setup is fairly similar. This time we also want an interrupt line
+ for the 'frame captured' signal. Not all cards have this so some of them
+ cannot handle poll().
+ </para>
+ <programlisting>
+
+
+static int io = 0x320;
+static int irq = 11;
+
+int __init mycamera_init(struct video_init *v)
+{
+ if(check_region(io, MY_IO_SIZE))
+ {
+ printk(KERN_ERR
+ "mycamera: port 0x%03X is in use.\n", io);
+ return -EBUSY;
+ }
+
+ if(video_device_register(&amp;my_camera,
+ VFL_TYPE_GRABBER)==-1)
+ return -EINVAL;
+ request_region(io, MY_IO_SIZE, "mycamera");
+ return 0;
+}
+
+ </programlisting>
+ <para>
+ This is little changed from the needs of the radio card. We specify
+ VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
+ </para>
+ </sect1>
+ <sect1 id="opvid">
+ <title>Opening And Closing The Capture Device</title>
+ <programlisting>
+
+
+static int users = 0;
+
+static int camera_open(stuct video_device *dev, int flags)
+{
+ if(users)
+ return -EBUSY;
+ if(request_irq(irq, camera_irq, 0, "camera", dev)&lt;0)
+ return -EBUSY;
+ users++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int camera_close(struct video_device *dev)
+{
+ users--;
+ free_irq(irq, dev);
+ MOD_DEC_USE_COUNT;
+}
+ </programlisting>
+ <para>
+ The open and close routines are also quite similar. The only real change is
+ that we now request an interrupt for the camera device interrupt line. If we
+ cannot get the interrupt we report EBUSY to the application and give up.
+ </para>
+ </sect1>
+ <sect1 id="irqvid">
+ <title>Interrupt Handling</title>
+ <para>
+ Our example handler is for an ISA bus device. If it was PCI you would be
+ able to share the interrupt and would have set SA_SHIRQ to indicate a
+ shared IRQ. We pass the device pointer as the interrupt routine argument. We
+ don't need to since we only support one card but doing this will make it
+ easier to upgrade the driver for multiple devices in the future.
+ </para>
+ <para>
+ Our interrupt routine needs to do little if we assume the card can simply
+ queue one frame to be read after it captures it.
+ </para>
+ <programlisting>
+
+
+static struct wait_queue *capture_wait;
+static int capture_ready = 0;
+
+static void camera_irq(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ capture_ready=1;
+ wake_up_interruptible(&amp;capture_wait);
+}
+ </programlisting>
+ <para>
+ The interrupt handler is nice and simple for this card as we are assuming
+ the card is buffering the frame for us. This means we have little to do but
+ wake up anybody interested. We also set a capture_ready flag, as we may
+ capture a frame before an application needs it. In this case we need to know
+ that a frame is ready. If we had to collect the frame on the interrupt life
+ would be more complex.
+ </para>
+ <para>
+ The two new routines we need to supply are camera_read which returns a
+ frame, and camera_poll which waits for a frame to become ready.
+ </para>
+ <programlisting>
+
+
+static int camera_poll(struct video_device *dev,
+ struct file *file, struct poll_table *wait)
+{
+ poll_wait(file, &amp;capture_wait, wait);
+ if(capture_read)
+ return POLLIN|POLLRDNORM;
+ return 0;
+}
+
+ </programlisting>
+ <para>
+ Our wait queue for polling is the capture_wait queue. This will cause the
+ task to be woken up by our camera_irq routine. We check capture_read to see
+ if there is an image present and if so report that it is readable.
+ </para>
+ </sect1>
+ <sect1 id="rdvid">
+ <title>Reading The Video Image</title>
+ <programlisting>
+
+
+static long camera_read(struct video_device *dev, char *buf,
+ unsigned long count)
+{
+ struct wait_queue wait = { current, NULL };
+ u8 *ptr;
+ int len;
+ int i;
+
+ add_wait_queue(&amp;capture_wait, &amp;wait);
+
+ while(!capture_ready)
+ {
+ if(file->flags&amp;O_NDELAY)
+ {
+ remove_wait_queue(&amp;capture_wait, &amp;wait);
+ current->state = TASK_RUNNING;
+ return -EWOULDBLOCK;
+ }
+ if(signal_pending(current))
+ {
+ remove_wait_queue(&amp;capture_wait, &amp;wait);
+ current->state = TASK_RUNNING;
+ return -ERESTARTSYS;
+ }
+ schedule();
+ current->state = TASK_INTERRUPTIBLE;
+ }
+ remove_wait_queue(&amp;capture_wait, &amp;wait);
+ current->state = TASK_RUNNING;
+
+ </programlisting>
+ <para>
+ The first thing we have to do is to ensure that the application waits until
+ the next frame is ready. The code here is almost identical to the mouse code
+ we used earlier in this chapter. It is one of the common building blocks of
+ Linux device driver code and probably one which you will find occurs in any
+ drivers you write.
+ </para>
+ <para>
+ We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
+ signal occurs we need to return from the system call so that the signal can
+ be sent to the application itself. We also check to see if the user actually
+ wanted to avoid waiting - ie if they are using non-blocking I/O and have other things
+ to get on with.
+ </para>
+ <para>
+ Next we copy the data from the card to the user application. This is rarely
+ as easy as our example makes out. We will add capture_w, and capture_h here
+ to hold the width and height of the captured image. We assume the card only
+ supports 24bit RGB for now.
+ </para>
+ <programlisting>
+
+
+
+ capture_ready = 0;
+
+ ptr=(u8 *)buf;
+ len = capture_w * 3 * capture_h; /* 24bit RGB */
+
+ if(len>count)
+ len=count; /* Doesn't all fit */
+
+ for(i=0; i&lt;len; i++)
+ {
+ put_user(inb(io+IMAGE_DATA), ptr);
+ ptr++;
+ }
+
+ hardware_restart_capture();
+
+ return i;
+}
+
+ </programlisting>
+ <para>
+ For a real hardware device you would try to avoid the loop with put_user().
+ Each call to put_user() has a time overhead checking whether the accesses to user
+ space are allowed. It would be better to read a line into a temporary buffer
+ then copy this to user space in one go.
+ </para>
+ <para>
+ Having captured the image and put it into user space we can kick the card to
+ get the next frame acquired.
+ </para>
+ </sect1>
+ <sect1 id="iocvid">
+ <title>Video Ioctl Handling</title>
+ <para>
+ As with the radio driver the major control interface is via the ioctl()
+ function. Video capture devices support the same tuner calls as a radio
+ device and also support additional calls to control how the video functions
+ are handled. In this simple example the card has no tuners to avoid making
+ the code complex.
+ </para>
+ <programlisting>
+
+
+
+static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability v;
+ v.type = VID_TYPE_CAPTURE|\
+ VID_TYPE_CHROMAKEY|\
+ VID_TYPE_SCALES|\
+ VID_TYPE_OVERLAY;
+ v.channels = 1;
+ v.audios = 0;
+ v.maxwidth = 640;
+ v.minwidth = 16;
+ v.maxheight = 480;
+ v.minheight = 16;
+ strcpy(v.name, "My Camera");
+ if(copy_to_user(arg, &amp;v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ The first ioctl we must support and which all video capture and radio
+ devices are required to support is VIDIOCGCAP. This behaves exactly the same
+ as with a radio device. This time, however, we report the extra capabilities
+ we outlined earlier on when defining our video_dev structure.
+ </para>
+ <para>
+ We now set the video flags saying that we support overlay, capture,
+ scaling and chromakey. We also report size limits - our smallest image is
+ 16x16 pixels, our largest is 640x480.
+ </para>
+ <para>
+ To keep things simple we report no audio and no tuning capabilities at all.
+ </para>
+ <programlisting>
+
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel != 0)
+ return -EINVAL;
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = VIDEO_MODE_AUTO;
+ strcpy(v.name, "Camera Input");break;
+ if(copy_to_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ This follows what is very much the standard way an ioctl handler looks
+ in Linux. We copy the data into a kernel space variable and we check that the
+ request is valid (in this case that the input is 0). Finally we copy the
+ camera info back to the user.
+ </para>
+ <para>
+ The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
+ inputs to the video card). Our example card has a single camera input. The
+ fields in the structure are
+ </para>
+ <table frame=all><title>struct video_channel fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+
+ <entry>channel</><>The channel number we are selecting</entry>
+ </row><row>
+ <entry>name</><>The name for this channel. This is intended
+ to describe the port to the user.
+ Appropriate names are therefore things like
+ "Camera" "SCART input"</entry>
+ </row><row>
+ <entry>flags</><>Channel properties</entry>
+ </row><row>
+ <entry>type</><>Input type</entry>
+ </row><row>
+ <entry>norm</><>The current television encoding being used
+ if relevant for this channel.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel flags</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_VC_TUNER</><>Channel has a tuner.</entry>
+ </row><row>
+ <entry>VIDEO_VC_AUDIO</><>Channel has audio.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel types</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_TYPE_TV</><>Television input.</entry>
+ </row><row>
+ <entry>VIDEO_TYPE_CAMERA</><>Fixed camera input.</entry>
+ </row><row>
+ <entry>0</><>Type is unknown.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <table frame=all><title>struct video_channel norms</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>VIDEO_MODE_PAL</><>PAL encoded Television</entry>
+ </row><row>
+ <entry>VIDEO_MODE_NTSC</><>NTSC (US) encoded Television</entry>
+ </row><row>
+ <entry>VIDEO_MODE_SECAM</><>SECAM (French) Televison </entry>
+ </row><row>
+ <entry>VIDEO_MODE_AUTO</><>Automatic switching, or format does not
+ matter</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
+ request the norm is changed - for exaple to switch between a PAL or an NTSC
+ format camera.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCSCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel != 0)
+ return -EINVAL;
+ if(v.norm != VIDEO_MODE_AUTO)
+ return -EINVAL;
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ The implementation of this call in our driver is remarkably easy. Because we
+ are assuming fixed format hardware we need only check that the user has not
+ tried to change anything.
+ </para>
+ <para>
+ The user also needs to be able to configure and adjust the picture they are
+ seeing. This is much like adjusting a television set. A user application
+ also needs to know the palette being used so that it knows how to display
+ the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
+ calls provide this information.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCGPICT
+ {
+ struct video_picture v;
+ v.brightness = hardware_brightness();
+ v.hue = hardware_hue();
+ v.colour = hardware_saturation();
+ v.contrast = hardware_brightness();
+ /* Not settable */
+ v.whiteness = 32768;
+ v.depth = 24; /* 24bit */
+ v.palette = VIDEO_PALETTE_RGB24;
+ if(copy_to_user(&amp;v, arg,
+ sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ The brightness, hue, color, and contrast provide the picture controls that
+ are akin to a conventional television. Whiteness provides additional
+ control for greyscale images. All of these values are scaled between 0-65535
+ and have 32768 as the mid point setting. The scaling means that applications
+ do not have to worry about the capability range of the hardware but can let
+ it make a best effort attempt.
+ </para>
+ <para>
+ Our depth is 24, as this is in bits. We will be returing RGB24 format. This
+ has one byte of red, then one of green, then one of blue. This then repeats
+ for every other pixel in the image. The other common formats the interface
+ defines are
+ </para>
+ <table frame=all><title>Framebuffer Encodings</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>GREY</><>Linear greyscale. This is for simple cameras and the
+ like</>
+ </row><row>
+ <entry>RGB565</><>The top 5 bits hold 32 red levels, the next six bits
+ hold green and the low 5 bits hold blue. </>
+ </row><row>
+ <entry>RGB555</><>The top bit is clear. The red green and blue levels
+ each occupy five bits.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Additional modes are support for YUV capture formats. These are common for
+ TV and video conferencing applications.
+ </para>
+ <para>
+ The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
+ Exactly which ones are supported depends heavily on the card itself. It is
+ possible to support many modes and effects in software. In general doing
+ this in the kernel is a bad idea. Video capture is a performance-sensitive
+ application and the programs can often do better if they aren't being
+ 'helped' by an overkeen driver writer. Thus for our device we will report
+ RGB24 only and refuse to allow a change.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.depth!=24 ||
+ v.palette != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+ set_hardware_brightness(v.brightness);
+ set_hardware_hue(v.hue);
+ set_hardware_saturation(v.colour);
+ set_hardware_brightness(v.contrast);
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ We check the user has not tried to change the palette or the depth. We do
+ not want to carry out some of the changes and then return an error. This may
+ confuse the application which will be assuming no change occurred.
+ </para>
+ <para>
+ In much the same way as you need to be able to set the picture controls to
+ get the right capture images, many cards need to know what they are
+ displaying onto when generating overlay output. In some cases getting this
+ wrong even makes a nasty mess or may crash the computer. For that reason
+ the VIDIOCSBUF ioctl used to set up the frame buffer information may well
+ only be usable by root.
+ </para>
+ <para>
+ We will assume our card is one of the old ISA devices with feature connector
+ and only supports a couple of standard video modes. Very common for older
+ cards although the PCI devices are way smarter than this.
+ </para>
+ <programlisting>
+
+
+static struct video_buffer capture_fb;
+
+ case VIDIOCGFBUF:
+ {
+ if(copy_to_user(arg, &amp;capture_fb,
+ sizeof(capture_fb)))
+ return -EFAULT;
+ return 0;
+
+ }
+
+
+ </programlisting>
+ <para>
+ We keep the frame buffer information in the format the ioctl uses. This
+ makes it nice and easy to work with in the ioctl calls.
+ </para>
+ <programlisting>
+
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
+
+ if(!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.width!=320 &amp;&amp; v.width!=640)
+ return -EINVAL;
+ if(v.height!=200 &amp;&amp; v.height!=240
+ &amp;&amp; v.height!=400
+ &amp;&amp; v.height !=480)
+ return -EINVAL;
+ memcpy(&amp;capture_fb, &amp;v, sizeof(v));
+ hardware_set_fb(&amp;v);
+ return 0;
+ }
+
+
+
+ </programlisting>
+ <para>
+ The capable() function checks a user has the required capability. The Linux
+ operating system has a set of about 30 capabilities indicating privileged
+ access to services. The default set up gives the superuser (uid 0) all of
+ them and nobody else has any.
+ </para>
+ <para>
+ We check that the user has the SYS_ADMIN capability, that is they are
+ allowed to operate as the machine administrator. We don't want anyone but
+ the administrator making a mess of the display.
+ </para>
+ <para>
+ Next we check for standard PC video modes (320 or 640 wide with either
+ EGA or VGA depths). If the mode is not a standard video mode we reject it as
+ not supported by our card. If the mode is acceptable we save it so that
+ VIDIOCFBUF will give the right answer next time it is called. The
+ hardware_set_fb() function is some undescribed card specific function to
+ program the card for the desired mode.
+ </para>
+ <para>
+ Before the driver can display an overlay window it needs to know where the
+ window should be placed, and also how large it should be. If the card
+ supports clipping it needs to know which rectangles to omit from the
+ display. The video_window structure is used to describe the way the image
+ should be displayed.
+ </para>
+ <table frame=all><title>struct video_window fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>width</><>The width in pixels of the desired image. The card
+ may use a smaller size if this size is not available</>
+ </row><row>
+ <entry>height</><>The height of the image. The card may use a smaller
+ size if this size is not available.</>
+ </row><row>
+ <entry>x</><> The X position of the top left of the window. This
+ is in pixels relative to the left hand edge of the
+ picture. Not all cards can display images aligned on
+ any pixel boundary. If the position is unsuitable
+ the card adjusts the image right and reduces the
+ width.</>
+ </row><row>
+ <entry>y</><> The Y position of the top left of the window. This
+ is counted in pixels relative to the top edge of the
+ picture. As with the width if the card cannot
+ display starting on this line it will adjust the
+ values.</>
+ </row><row>
+ <entry>chromakey</><>The colour (expressed in RGB32 format) for the
+ chromakey colour if chroma keying is being used. </>
+ </row><row>
+ <entry>clips</><>An array of rectangles that must not be drawn
+ over.</>
+ </row><row>
+ <entry>clipcount</><>The number of clips in this array.</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Each clip is a struct video_clip which has the following fields
+ </para>
+ <table frame=all><title>video_clip fields</title>
+ <tgroup cols=2 align=left>
+ <tbody>
+ <row>
+ <entry>x, y</><>Co-ordinates relative to the display</>
+ </row><row>
+ <entry>width, height</><>Width and height in pixels</>
+ </row><row>
+ <entry>next</><>A spare field for the application to use</>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ The driver is required to ensure it always draws in the area requested or a smaller area, and that it never draws in any of the areas that are clipped.
+ This may well mean it has to leave alone. small areas the application wished to be
+ drawn.
+ </para>
+ <para>
+ Our example card uses chromakey so does not have to address most of the
+ clipping. We will add a video_window structure to our global variables to
+ remember our parameters, as we did with the frame buffer.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCGWIN:
+ {
+ if(copy_to_user(arg, &amp;capture_win,
+ sizeof(capture_win)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+ case VIDIOCSWIN:
+ {
+ struct video_window v;
+ if(copy_from_user(&amp;v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.width > 640 || v.height > 480)
+ return -EINVAL;
+ if(v.width < 16 || v.height < 16)
+ return -EINVAL;
+ hardware_set_key(v.chromakey);
+ hardware_set_window(v);
+ memcpy(&amp;capture_win, &amp;v, sizeof(v));
+ capture_w = v.width;
+ capture_h = v.height;
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ Because we are using Chromakey our setup is fairly simple. Mostly we have to
+ check the values are sane and load them into the capture card.
+ </para>
+ <para>
+ With all the setup done we can now turn on the actual capture/overlay. This
+ is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
+ where 0 is on and 1 is off.
+ </para>
+ <programlisting>
+
+
+ case VIDIOCCAPTURE:
+ {
+ int v;
+ if(get_user(v, (int *)arg))
+ return -EFAULT;
+ if(v==0)
+ hardware_capture_off();
+ else
+ {
+ if(capture_fb.width == 0
+ || capture_w == 0)
+ return -EINVAL;
+ hardware_capture_on();
+ }
+ return 0;
+ }
+
+
+ </programlisting>
+ <para>
+ We grab the flag from user space and either enable or disable according to
+ its value. There is one small corner case we have to consider here. Suppose
+ that the capture was requested before the video window or the frame buffer
+ had been set up. In those cases there will be unconfigured fields in our
+ card data, as well as unconfigured hardware settings. We check for this case and
+ return an error if the frame buffer or the capture window width is zero.
+ </para>
+ <programlisting>
+
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+ </programlisting>
+ <para>
+
+ We don't need to support any other ioctls, so if we get this far, it is time
+ to tell the video layer that we don't now what the user is talking about.
+ </para>
+ </sect1>
+ <sect1 id="endvid">
+ <title>Other Functionality</title>
+ <para>
+ The Video4Linux layer supports additional features, including a high
+ performance mmap() based capture mode and capturing part of the image.
+ These features are out of the scope of the book. You should however have enough
+ example code to implement most simple video4linux devices for radio and TV
+ cards.
+ </para>
+ </sect1>
+ </chapter>
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry><term>Multiple Opens</term>
+ <listitem>
+ <para>
+ The driver assumes multiple opens should not be allowed. A driver
+ can work around this but not cleanly.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>API Deficiences</term>
+ <listitem>
+ <para>
+ The existing API poorly reflects compression capable devices. There
+ are plans afoot to merge V4L, V4L2 and some other ideas into a
+ better interface.
+ </para>
+ </listitem></varlistentry>
+ </variablelist>
+
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+!Edrivers/char/videodev.c
+ </chapter>
+
+</book>
diff --git a/Documentation/DocBook/wanbook.tmpl b/Documentation/DocBook/wanbook.tmpl
new file mode 100644
index 000000000..9b18bb2d8
--- /dev/null
+++ b/Documentation/DocBook/wanbook.tmpl
@@ -0,0 +1,97 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="WANGuide">
+ <bookinfo>
+ <title>Synchronous PPP and Cisco HDLC Programming Guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Cox</surname>
+ <affiliation>
+ <address>
+ <email>alan@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Alan Cox</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ The syncppp drivers in Linux provide a fairly complete
+ implementation of Cisco HDLC and a minimal implementation of
+ PPP. The longer term goal is to switch the PPP layer to the
+ generic PPP interface that is new in Linux 2.3.x. The API should
+ remain unchanged when this is done, but support will then be
+ available for IPX, compression and other PPP features
+ </para>
+ </chapter>
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry><term>PPP is minimal</term>
+ <listitem>
+ <para>
+ The current PPP implementation is very basic, although sufficient
+ for most wan usages.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>Cisco HDLC Quirks</term>
+ <listitem>
+ <para>
+ Currently we do not end all packets with the correct Cisco multicast
+ or unicast flags. Nothing appears to mind too much but this should
+ be corrected.
+ </para>
+ </listitem></varlistentry>
+ </variablelist>
+
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+!Edrivers/net/wan/syncppp.c
+ </chapter>
+
+</book>
diff --git a/Documentation/DocBook/z8530book.tmpl b/Documentation/DocBook/z8530book.tmpl
new file mode 100644
index 000000000..364c9126d
--- /dev/null
+++ b/Documentation/DocBook/z8530book.tmpl
@@ -0,0 +1,383 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="Z85230Guide">
+ <bookinfo>
+ <title>Z8530 Programming Guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Cox</surname>
+ <affiliation>
+ <address>
+ <email>alan@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2000</year>
+ <holder>Alan Cox</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ The Z85x30 family synchronous/asynchronous controller chips are
+ used on a larg number of cheap network interface cards. The
+ kernel provides a core interface layer that is designed to make
+ it easy to provide WAN services using this chip.
+ </para>
+ <para>
+ The current driver only support synchronous operation. Merging the
+ asynchronous driver support into this code to allow any Z85x30
+ device to be used as both a tty interface and as a synchronous
+ controller is a project for Linux post the 2.4 release
+ </para>
+ <para>
+ The support code handles most common card configurations and
+ supports running both Cisco HDLC and Synchronous PPP. With extra
+ glue the frame relay and X.25 protocols can also be used with this
+ driver.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Driver Modes</title>
+ <para>
+ The Z85230 driver layer can drive Z8530, Z85C30 and Z85230 devices
+ in three different modes. Each mode can be applied to an individual
+ channel on the chip (each chip has two channels).
+ </para>
+ <para>
+ The PIO synchronous mode supports the most common Z8530 wiring. Here
+ the chip is interface to the I/O and interrupt facilities of the
+ host machine but not to the DMA subsystem. When running PIO the
+ Z8530 has extremely tight timing requirements. Doing high speeds,
+ even with a Z85230 will be tricky. Typically you should expect to
+ achieve at best 9600 baud with a Z8C530 and 64Kbits with a Z85230.
+ </para>
+ <para>
+ The DMA mode supports the chip when it is configured to use dual DMA
+ channels on an ISA bus. The better cards tend to support this mode
+ of operation for a single channel. With DMA running the Z85230 tops
+ out when it starts to hit ISA DMA constraints at about 512Kbits. It
+ is worth noting here that many PC machines hang or crash when the
+ chip is driven fast enough to hold the ISA bus solid.
+ </para>
+ <para>
+ Transmit DMA mode uses a single DMA channel. The DMA channel is used
+ for transmission as the transmit FIFO is smaller than the receive
+ FIFO. it gives better performance than pure PIO mode but is nowhere
+ near as ideal as pure DMA mode.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Using the Z85230 driver</title>
+ <para>
+ The Z85230 driver provides the back end interface to your board. To
+ configure a Z8530 interface you need to detect the board and to
+ identify its ports and interrupt resources. It is also your problem
+ to verify the resources are available.
+ </para>
+ <para>
+ Having identified the chip you need to fill in a struct z8530_dev,
+ which describes each chip. This object must exist until you finally
+ shutdown the board. Firstly zero the active field. This ensures
+ nothing goes off without you intending it. The irq field should
+ be set to the interrupt number of the chip. (Each chip has a single
+ interrupt source rather than each channel). You are responsible
+ for allocating the interrupt line. The interrupt handler should be
+ set to <function>z8530_interrupt</function>. The device id should
+ be set to the z8530_dev structure pointer. Whether the interrupt can
+ be shared or not is board dependant, and up to you to initialise.
+ </para>
+ <para>
+ The structure holds two channel structures.
+ Initialise chanA.ctrlio and chanA.dataio with the address of the
+ control and data ports. You can or this with Z8530_PORT_SLEEP to
+ indicate your interface needs the 5uS delay for chip settling done
+ in software. The PORT_SLEEP option is architecture specific. Other
+ flags may become available on future platforms, eg for MMIO.
+ Initialise the chanA.irqs to &amp;z8530_nop to start the chip up
+ as disabled and discarding interrupt events. This ensures that
+ stray interrupts will be mopped up and not hang the bus. Set
+ chanA.dev to point to the device structure itself. The
+ private and name field you may use as you wish. The private field
+ is unused by the Z85230 layer. The name is used for error reporting
+ and it may thus make sense to make it match the network name.
+ </para>
+ <para>
+ Repeat the same operation with the B channel if your chip has
+ both channels wired to something useful. This isnt always the
+ case. If it is not wired then the I/O values do not matter, but
+ you must initialise chanB.dev.
+ </para>
+ <para>
+ If your board has DMA facilities then initialise the txdma and
+ rxdma fields for the relevant channels. You must also allocate the
+ ISA DMA channels and do any neccessary board level initialisation
+ to configure them. The low level driver will do the Z8530 and
+ DMA controller programming but not board specific magic.
+ </para>
+ <para>
+ Having intialised the device you can then call
+ <function>z8530_init</function>. This will probe the chip and
+ reset it into a known state. An identification sequence is then
+ run to identify the chip type. If the checks fail to pass the
+ function returns a non zero error code. Typically this indicates
+ that the port given is not valid. After this call the
+ type field of the z8530_dev structure is initialised to either
+ Z8530, Z85C30 or Z85230 according to the chip found.
+ </para>
+ <para>
+ Once you have called z8530_init you can also make use of the utility
+ function <function>z8530_describe</function>. This provides a
+ consistant reporting format for the Z8530 devices, and allows all
+ the drivers to provide consistent reporting.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Attaching Network Interfaces</title>
+ <para>
+ If you wish to use the network interface facilities of the driver,
+ then you need to attach a network device to each channel that is
+ present and in use. In addition to use the SyncPPP and Cisco HDLC
+ you need to follow some additional plumbing rules. They may seem
+ complex but a look at the example hostess_sv11 driver should
+ reassure you.
+ </para>
+ <para>
+ The network device used for each channel should be pointed to by
+ the netdevice field of each channel. The dev-&gt; priv field of the
+ network device points to your private data - you will need to be
+ able to find your ppp device from this. In addition to use the
+ sync ppp layer the private data must start with a void * pointer
+ to the syncppp structures.
+ </para>
+ <para>
+ The way most drivers approach this paticular problem is to
+ create a structure holding the Z8530 device definition and
+ put that and the syncppp pointer into the private field of
+ the network device. The network device fields of the channels
+ then point back to the network devices. The ppp_device can also
+ be put in the private structure conveniently.
+ </para>
+ <para>
+ If you wish to use the synchronous ppp then you need to attach
+ the syncppp layer to the network device. You should do this before
+ you register the network device. The
+ <function>sppp_attach</function> requires that the first void *
+ pointer in your private data is pointing to an empty struct
+ ppp_device. The function fills in the initial data for the
+ ppp/hdlc layer.
+ </para>
+ <para>
+ Before you register your network device you will also need to
+ provide suitable handlers for most of the network device callbacks.
+ See the network device documentation for more details on this.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Configuring And Activating The Port</title>
+ <para>
+ The Z85230 driver provides helper functions and tables to load the
+ port registers on the Z8530 chips. When programming the register
+ settings for a channel be aware that the documentation recommends
+ initialisation orders. Strange things happen when these are not
+ followed.
+ </para>
+ <para>
+ <function>z8530_channel_load</function> takes an array of
+ pairs of initialisation values in an array of u8 type. The first
+ value is the Z8530 register number. Add 16 to indicate the alternate
+ register bank on the later chips. The array is terminated by a 255.
+ </para>
+ <para>
+ The driver provides a pair of public tables. The
+ z8530_hdlc_kilostream table is for the UK 'Kilostream' service and
+ also happens to cover most other end host configurations. The
+ z8530_hdlc_kilostream_85230 table is the same configuration using
+ the enhancements of the 85230 chip. The configuration loaded is
+ standard NRZ encoded synchronous data with HDLC bitstuffing. All
+ of the timing is taken from the other end of the link.
+ </para>
+ <para>
+ When writing your own tables be aware that the driver internally
+ tracks register values. It may need to reload values. You should
+ therefore be sure to set registers 1-7, 9-11, 14 and 15 in all
+ configurations. Where the register settings depend on DMA selection
+ the driver will update the bits itself when you open or close.
+ Loading a new table with the interface open is not recommended.
+ </para>
+ <para>
+ There are three standard configurations supported by the core
+ code. In PIO mode the interface is programmed up to use
+ interrupt driven PIO. This places high demands on the host processor
+ to avoid latency. The driver is written to take account of latency
+ issues but it cannot avoid latencies caused by other drivers,
+ notably IDE in PIO mode. Because the drivers allocate buffers you
+ must also prevent MTU changes while the port is open.
+ </para>
+ <para>
+ Once the port is open it will call the rx_function of each channel
+ whenever a completed packet arrived. This is invoked from
+ interrupt context and passes you the channel and a network
+ buffer (struct sk_buff) holding the data. The data includes
+ the CRC bytes so most users will want to trim the last two
+ bytes before processing the data. This function is very timing
+ critical. When you wish to simply discard data the support
+ code provides the function <function>z8530_null_rx</function>
+ to discard the data.
+ </para>
+ <para>
+ To active PIO mode sending and receiving the <function>
+ z8530_sync_open</function> is called. This expects to be passed
+ the network device and the channel. Typically this is called from
+ your network device open callback. On a failure a non zero error
+ status is returned. The <function>z8530_sync_close</function>
+ function shuts down a PIO channel. This must be done before the
+ channel is opened again and before the driver shuts down
+ and unloads.
+ </para>
+ <para>
+ The ideal mode of operation is dual channel DMA mode. Here the
+ kernel driver will configure the board for DMA in both directions.
+ The driver also handles ISA DMA issues such as controller
+ programming and the memory range limit for you. This mode is
+ activated by calling the <function>z8530_sync_dma_open</function>
+ function. On failure a non zero error value is returned.
+ Once this mode is activated it can be shut down by calling the
+ <function>z8530_sync_dma_close</function>. You must call the close
+ function matching the open mode you used.
+ </para>
+ <para>
+ The final supported mode uses a single DMA channel to drive the
+ transmit side. As the Z85C30 has a larger FIFO on the receive
+ channel this tends to increase the maximum speed a little.
+ This is activated by calling the <function>z8530_sync_txdma_open
+ </function>. This returns a non zero error code on failure. The
+ <function>z8530_sync_txdma_close</function> function closes down
+ the Z8530 interface from this mode.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Network Layer Functions</title>
+ <para>
+ The Z8530 layer provides functions to queue packets for
+ transmission. The driver internally buffers the frame currently
+ being transmitted and one further frame (in order to keep back
+ to back transmission running). Any further buffering is up to
+ the caller.
+ </para>
+ <para>
+ The function <function>z8530_queue_xmit</function> takes a network
+ buffer in sk_buff format and queues it for transmission. The
+ caller must provide the entire packet with the exception of the
+ bitstuffing and CRC. This is normally done by the caller via
+ the syncppp interface layer. It returns 0 if the buffer has been
+ queued and non zero values for queue full. If the function accepts
+ the buffer it becomes property of the Z8530 layer and the caller
+ should not free it.
+ </para>
+ <para>
+ The function <function>z8530_get_stats</function> returns a pointer
+ to an internally maintained per interface statistics block. This
+ provides most of the interface code needed to implement the network
+ layer get_stats callback.
+ </para>
+ </chapter>
+
+ <chapter>
+ <title>Porting The Z8530 Driver</title>
+ <para>
+ The Z8530 driver is written to be portable. In DMA mode it makes
+ assumptions about the use of ISA DMA. These are probably warranted
+ in most cases as the Z85230 in paticular was designed to glue to PC
+ type machines. The PIO mode makes no real assumptions.
+ </para>
+ <para>
+ Should you need to retarget the Z8530 driver to another architecture
+ the only code that should need changing are the port I/O functions.
+ At the moment these assume PC I/O port accesses. This may not be
+ appropriate for all platforms. Replacing
+ <function>z8530_read_port</function> and <function>z8530_write_port
+ </function> is intended to be all that is required to port this
+ driver layer.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry><term>Interrupt Locking</term>
+ <listitem>
+ <para>
+ The locking in the driver is done via the global cli/sti lock. This
+ makes for relatively poor SMP performance. Switching this to use a
+ per device spin lock would probably materially improve performance.
+ </para>
+ </listitem></varlistentry>
+
+ <varlistentry><term>Occasional Failures</term>
+ <listitem>
+ <para>
+ We have reports of occasional failures when run for very long
+ periods of time and the driver starts to receive junk frames. At
+ the moment the cause of this is not clear.
+ </para>
+ </listitem></varlistentry>
+ </variablelist>
+
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+!Edrivers/net/wan/z85230.c
+ </chapter>
+
+ <chapter id="intfunctions">
+ <title>Internal Functions</title>
+!Idrivers/net/wan/z85230.c
+ </chapter>
+
+</book>
diff --git a/Documentation/networking/comx.txt b/Documentation/networking/comx.txt
new file mode 100644
index 000000000..a58e78d90
--- /dev/null
+++ b/Documentation/networking/comx.txt
@@ -0,0 +1,248 @@
+
+ COMX drivers for the 2.2 kernel
+
+Originally written by: Tivadar Szemethy, <tiv@itc.hu>
+Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+
+Last change: 21/06/1999.
+
+INTRODUCTION
+
+This document describes the software drivers and their use for the
+COMX line of synchronous serial adapters for Linux version 2.2.0 and
+above.
+The cards are produced and sold by ITC-Pro Ltd. Budapest, Hungary
+For further info contact <info@itc.hu>
+or http://www.itc.hu (mostly in Hungarian).
+The firmware files and software are available from ftp://ftp.itc.hu
+
+Currently, the drivers support the following cards and protocols:
+
+COMX (2x64 kbps intelligent board)
+CMX (1x256 + 1x128 kbps intelligent board)
+HiCOMX (2x2Mbps intelligent board)
+LoCOMX (1x512 kbps passive board)
+MixCOM (1x512 or 2x512kbps passive board with a hardware watchdog an
+ optional BRI interface and optional flashROM (1-32M))
+
+At the moment of writing this document, the (Cisco)-HDLC, LAPB, SyncPPP and
+Frame Relay (DTE, rfc1294 IP encapsulation with partially implemented Q933a
+LMI) protocols are available as link-level protocol.
+X.25 support is being worked on.
+
+USAGE
+
+Load the comx.o module and the hardware-specific and protocol-specific
+modules you'll need into the running kernel using the insmod utility.
+This creates the /proc/comx directory.
+See the example scripts in the 'etc' directory.
+
+/proc INTERFACE INTRO
+
+The COMX driver set has a new type of user interface based on the /proc
+filesystem which eliminates the need for external user-land software doing
+IOCTL calls.
+Each network interface or device (i.e. those ones you configure with 'ifconfig'
+and 'route' etc.) has a corresponding directory under /proc/comx. You can
+dynamically create a new interface by saying 'mkdir /proc/comx/comx0' (or you
+can name it whatever you want up to 8 characters long, comx[n] is just a
+convention).
+Generally the files contained in these directories are text files, which can
+be viewed by 'cat filename' and you can write a string to such a file by
+saying 'echo _string_ >filename'. This is very similar to the sysctl interface.
+Don't use a text editor to edit these files, always use 'echo' (or 'cat'
+where appropriate).
+When you've created the comx[n] directory, two files are created automagically
+in it: 'boardtype' and 'protocol'. You have to fill in these files correctly
+for your board and protocol you intend to use (see the board and protocol
+descriptions in this file below or the example scripts in the 'etc' directory).
+After filling in these files, other files will appear in the directory for
+setting the various hardware- and protocol-related informations (for example
+irq and io addresses, keepalive values etc.) These files are set to default
+values upon creation, so you don't necessarily have to change all of them.
+
+When you're ready with filling in the files in the comx[n] directory, you can
+configure the corresponding network interface with the standard network
+configuration utilites. If you're unble to bring the interfaces up, look up
+the various kernel log files on your system, and consult the messages for
+a probable reason.
+
+EXAMPLE
+
+To create the interface 'comx0' which is the first channel of a COMX card:
+
+insmod comx
+# insmod comx-hw-comx ; insmod comx-proto-hdlc (these are usually
+autoloaded if you use the kernel module loader)
+
+mkdir /proc/comx/comx0
+echo comx >/proc/comx/comx0/boardtype
+echo 0x360 >/proc/comx/comx0/io <- jumper-selectable I/O port
+echo 0x0a >/proc/comx/comx0/irq <- jumper-selectable IRQ line
+echo 0xd000 >/proc/comx/comx0/memaddr <- software-configurable memory
+ address. COMX uses 64 KB, and this
+ can be: 0xa000, 0xb000, 0xc000,
+ 0xd000, 0xe000. Avoid conflicts
+ with other hardware.
+cat </etc/siol1.rom >/proc/comx/comx0/firmware <- the firmware for the card
+echo HDLC >/proc/comx/comx0/protocol <- the data-link protocol
+echo 10 >/proc/comx/comx0/keepalive <- the keepalive for the protocol
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 <-
+ finally configure it with ifconfig
+Check its status:
+cat /proc/comx/comx0/status
+
+If you want to use the second channel of this board:
+
+mkdir /proc/comx/comx1
+echo comx >/proc/comx/comx1/boardtype
+echo 0x360 >/proc/comx/comx1/io
+echo 10 >/proc/comx/comx1/irq
+echo 0xd000 >/proc/comx/comx1/memaddr
+echo 1 >/proc/comx/comx1/channel <- channels are numbered
+ as 0 (default) and 1
+
+Now, check if the driver recognized that you're going to use the other
+channel of the same adapter:
+
+cat /proc/comx/comx0/twin
+comx1
+cat /proc/comx/comx1/twin
+comx0
+
+You don't have to load the firmware twice, if you use both channels of
+an adapter, just write it into the channel 0's /proc firmware file.
+
+Default values: io 0x360 for COMX, 0x320 (HICOMX), irq 10, memaddr 0xd0000
+
+THE LOCOMX HARDWARE DRIVER
+
+The LoCOMX driver doesn't require firmware, and it doesn't use memory either,
+but it uses DMA channels 1 and 3. You can set the clock rate (if enabled by
+jumpers on the board) by writing the kbps value into the file named 'clock'.
+Set it to 'external' (it is the default) if you have external clock source.
+
+(Note: currently the LoCOMX driver does not support the internal clock)
+
+THE COMX, CMX AND HICOMX DRIVERS
+
+On the HICOMX, COMX and CMX, you have to load the firmware (it is different for
+the three cards!). All these adapters can share the same memory
+address (we usually use 0xd0000). On the CMX you can set the internal
+clock rate (if enabled by jumpers on the small adapter boards) by writing
+the kbps value into the 'clock' file. You have to do this before initializing
+the card. If you use both HICOMX and CMX/COMX cards, initialize the HICOMX
+first. The I/O address of the HICOMX board is not configurable by any
+method available to the user: it is hardwired to 0x320, and if you have to
+change it, consult ITC-Pro Ltd.
+
+THE MIXCOM DRIVER
+
+The MixCOM board doesn't require firmware, the driver communicates with
+it through I/O ports. You can have three of these cards in one machine.
+
+THE HDLC LINE PROTOCOL DRIVER
+
+There's only one configurable parameter with this protocol: the 'keepalive'
+value. You can set this in seconds or set to 'off'. Agree with the administrator
+of your peer router on this setting. The default is 10 (seconds).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo hdlc >/proc/comx/comx0/protocol
+echo 10 >/proc/comx/comx0/keepalive <- not necessary, 10 is the default
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE PPP LINE PROTOCOL DRIVER
+
+To use this driver, you have to have ppp-2.3.4, and have a modified version of
+pppd (this pppd will work as async pppd to, the modifiactions merely relax
+some restricions in order to be able to use non-async lines too.
+If configured, this driver can use Van Jacobson TCP header compression (you'll
+need the slhc.o module for this).
+Additionaly to use this protocol, enable async ppp in your kernel config, and
+create the comx device special files in /dev. They're character special files
+with major 88, and their names must be the same as their network interface
+counterparts (i.e /dev/comx0 with minor 0 corresponds interface comx0 and so
+on).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo ppp >/proc/comx/comx0/protocol
+ifconfig comx0 up
+pppd comx0 1.2.3.4:5.6.7.8 persist <- with this option pppd won't exit
+ when the line goes down
+
+THE LAPB LINE PROTOCOL DRIVER
+
+For this, you'll need to configure LAPB support (See 'LAPB Data Link Driver' in
+'Network options' section) into your kernel (thanks to Jonathan Naylor for his
+excellent implementation).
+comxlapb.o provides the following files in the appropriate directory
+(the default values in parens): t1 (5), t2 (1), n2 (20), mode (DTE, STD) and
+window (7). Agree with the administrator of your peer router on these
+settings (most people use defaults, but you have to know if you are DTE or
+DCE).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo lapb >/proc/comx/comx0/protocol
+echo dce >/proc/comx/comx0/mode <- DCE interface in this example
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE FRAME RELAY PROTOCOL DRIVER
+
+You DON'T need any other frame relay related modules from the kernel to use
+COMX-Frame Relay. This protocol is a bit more complicated than the others,
+because it allows to use 'subinterfaces' or DLCIs within one physical device.
+First you have to create the 'master' device (the actual physical interface)
+as you would do for other protocols. Specify 'frad' as protocol type.
+Now you can bring this interface up by saying 'ifconfig comx0 up' (or whatever
+you've named the interface). Do not assign any IP address to this interface
+and do not set any routes through it.
+Then, set up your DLCIs the following way: create a comx interface for each
+DLCI you intend to use (with mkdir), and write 'dlci' to the 'boardtype' file,
+and 'ietf-ip' to the 'protocol' file. Currently, the only supported
+encapsulation type is this (also called as RFC1294/1490 IP encapsulation).
+Write the DLCI number to the 'dlci' file, and write the name of the physical
+COMX device to the file called 'master'.
+Now you can assign an IP address to this interface and set routes using it.
+See the example file for further info and example config script.
+Notes: this driver implements a DTE interface with partially implemented
+Q933a LMI.
+You can find an extensively commented example in the 'etc' directory.
+
+FURTHER /proc FILES
+
+boardtype:
+Type of the hardware. Valid values are:
+ 'comx', 'hicomx', 'locomx', 'cmx'.
+
+protocol:
+Data-link protocol on this channel. Can be: HDLC, LAPB, PPP, FRAD
+
+status:
+You can read the channel's actual status from the 'status' file, for example
+'cat /proc/comx/comx3/status'.
+
+lineup_delay:
+Interpreted in seconds (default is 1). Used to avoid line jitter: the system
+will consider the line status 'UP' only if it is up for at least this number
+of seconds.
+
+debug:
+You can set various debug options through this file. Valid options are:
+'comx_events', 'comx_tx', 'comx_rx', 'hw_events', 'hw_tx', 'hw_rx'.
+You can enable a debug options by writing its name prepended by a '+' into
+the debug file, for example 'echo +comx_rx >comx0/debug'.
+Disabling an option happens similarly, use the '-' prefix
+(e.g. 'echo -hw_rx >debug').
+Debug results can be read from the debug file, for example:
+tail -f /proc/comx/comx2/debug
+
+
diff --git a/Documentation/networking/dmfe.txt b/Documentation/networking/dmfe.txt
new file mode 100644
index 000000000..158e94ba6
--- /dev/null
+++ b/Documentation/networking/dmfe.txt
@@ -0,0 +1,63 @@
+ dmfe.c: Version 1.28 01/18/2000
+
+ A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+ Copyright (C) 1997 Sten Wang
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ A. Compiler command:
+
+ A-1: For normal single processor kernel
+ "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall
+ -Wstrict-prototypes -O6 -c dmfe.c"
+
+ A-2: For single processor and enable kernel module version function
+ "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I/usr/src/linux/net/inet
+ -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+
+ A-3: For multiple processors(SMP) and enable the module version function
+ "gcc -D__SMP__ -DMODULE -DMODVERSIONS -D__KERNEL__ -I/usr/src/linux
+ /net/inet -Wall -Wstrict-prototypes -O6 -c dmfe.c"
+
+
+ B. The following steps teach you how to active DM9102 board:
+
+ 1. Used the upper compiler command to compile dmfe.c
+
+ 2. Insert dmfe module into kernel
+ "insmod dmfe" ;;Auto Detection Mode (Suggest)
+ "insmod dmfe mode=0" ;;Force 10M Half Duplex
+ "insmod dmfe mode=1" ;;Force 100M Half Duplex
+ "insmod dmfe mode=4" ;;Force 10M Full Duplex
+ "insmod dmfe mode=5" ;;Force 100M Full Duplex
+
+ 3. Config a dm9102 network interface
+ "ifconfig eth0 172.22.3.18"
+ ^^^^^^^^^^^ Your IP address
+
+ 4. Active the IP routing table. For some distributions, it is not
+ necessary. You can type "route" to check.
+
+ "route add default eth0"
+
+
+ 5. Well done. Your DM9102 adapter actived now.
+
+
+ C. Object files description:
+ 1. dmfe_rh61.o: For Redhat 6.1
+
+ If you can make sure your kernel version, you can rename
+ to dmfe.o and directly use it without re-compiling.
+
+
+ Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
diff --git a/Documentation/sound/ALS b/Documentation/sound/ALS
new file mode 100644
index 000000000..db98daf30
--- /dev/null
+++ b/Documentation/sound/ALS
@@ -0,0 +1,43 @@
+ALS-007/ALS-100/ALS-200 based sound cards
+=========================================
+
+Support for sound cards based around the Avance Logic
+ALS-007/ALS-100/ALS-200 chip is included. These chips are a single
+chip PnP sound solution which is mostly hardware compatible with the
+Sound Blaster 16 card, with most differences occurring in the use of
+the mixer registers. For this reason the ALS code is integrated
+as part of the Sound Blaster 16 driver (adding only 800 bytes to the
+SB16 driver).
+
+To use an ALS sound card under Linux, enable the following options in the
+sound configuration section of the kernel config:
+ - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
+ - FM synthesizer (YM3812/OPL-3) support
+Since the ALS-007/100/200 is a PnP card, the sound driver probably should be
+compiled as a module, with the isapnptools used to wake up the sound card.
+Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit -
+either 0, 1 or 3) to the values used in your particular installation (they
+should match the values used to configure the card using isapnp). The
+ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA"
+should be set to -1. If you wish to use the external MPU-401 interface on
+the card, "MPU401 I/O base of SB16" and "SB MPU401 IRQ" should be set to
+the appropriate values for your installation. (Note that the ALS-007
+requires a separate IRQ for the MPU-401, so don't specify -1 here). (Note
+that the base port of the internal FM synth is fixed at 0x388 on the ALS007;
+in any case the FM synth location cannot be set in the kernel configuration).
+
+The resulting sound driver will provide the following capabilities:
+ - 8 and 16 bit audio playback
+ - 8 and 16 bit audio recording
+ - Software selection of record source (line in, CD, FM, mic, master)
+ - Record and playback of midi data via the external MPU-401
+ - Playback of midi data using inbuilt FM synthesizer
+ - Control of the ALS-007 mixer via any OSS-compatible mixer programs.
+ Controls available are Master (L&R), Line in (L&R), CD (L&R),
+ DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono).
+
+Jonathan Woithe
+jwoithe@physics.adelaide.edu.au
+30 March 1998
+
+Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200
diff --git a/Documentation/sound/Maestro b/Documentation/sound/Maestro
index 8d0fd215d..b7e1334cd 100644
--- a/Documentation/sound/Maestro
+++ b/Documentation/sound/Maestro
@@ -24,7 +24,7 @@ The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978.
The various families of Maestro are mostly identical as far as this
driver is concerned. It doesn't touch the DSP parts that differ (though
-it could for FM synthesis)
+it could for FM synthesis).
Driver OSS Behavior
--------------------
@@ -90,8 +90,33 @@ to be allocated, as a power of two. Up to 4 devices can be registered
( dsps_order=2 ). These devices act as fully distinct units and use
separate channels in the maestro.
+Power Management
+----------------
+
+As of version 0.14, this driver has a minimal understanding of PCI
+Power Management. If it finds a valid power management capability
+on the PCI device it will attempt to use the power management
+functions of the maestro. It will only do this on Maestro 2Es and
+only on machines that are known to function well. You can
+force the use of power management by setting the 'use_pm' module
+option to 1, or can disable it entirely by setting it to 0.
+
+When using power management, the driver does a few things
+differently. It will keep the chip in a lower power mode
+when the module is inserted but /dev/dsp is not open. This
+allows the mixer to function but turns off the clocks
+on other parts of the chip. When /dev/dsp is opened the chip
+is brought into full power mode, and brought back down
+when it is closed. It also powers down the chip entirely
+when the module is removed or the machine is shutdown. This
+can have nonobvious consequences. CD audio may not work
+after a power managing driver is removed. Also, software that
+doesn't understand power management may not be able to talk
+to the powered down chip until the machine goes through a hard
+reboot to bring it back.
+
.. more details ..
------------------
+------------------
drivers/sound/maestro.c contains comments that hopefully explain
the maestro implementation.
diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt
index c13fc4c52..757f7458f 100644
--- a/Documentation/usb/ov511.txt
+++ b/Documentation/usb/ov511.txt
@@ -3,7 +3,7 @@ Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC
-------------------------------------------------------------------------------
Author: Mark McClelland
-Homepage: http://people.delphi.com/mmcclelland/linux/
+Homepage: http://alpha.dyndns.org/ov511
INTRODUCTION:
@@ -11,25 +11,41 @@ This is a preliminary version of my OV511 Linux device driver. Currently, it can
grab a frame in color (YUV420) at 640x480 or 320x240 using either vidcat or
xawtv. Other utilities may work but have not yet been tested.
+NEW IN THIS VERSION:
+ o Preliminary snapshot support
+ o Experimental red-blue misalignment fixes
+ o Better YUV420 color conversion
+ o Module options
+ o Finer-grained debug message control
+ o Support for new cameras (4, 36)
+ o Uses initcalls
+
SUPPORTED CAMERAS:
-________________________________________________________
-Manufacturer | Model | Custom ID | Status
------------------+----------------+-----------+---------
-MediaForte | MV300 | 0 | Working
-D-Link | DSB-C300 | 3 | Working
-Puretek | PT-6007 | 5 | Untested
-Creative Labs | WebCam 3 | 21 | Working
-Lifeview | RoboCam | 100 | Untested
-AverMedia | InterCam Elite | 102 | Working
-MediaForte | MV300 | 112 | Working
---------------------------------------------------------
+_________________________________________________________
+Manufacturer | Model | Custom ID | Status
+-----------------+-----------------+-----------+---------
+MediaForte | MV300 | 0 | Working
+Aiptek | HyperVCam ? | 0 | Working
+NetView | NV300M | 0 | Working
+D-Link | DSB-C300 | 3 | Working
+Hawking Tech. | ??? | 3 | Working
+??? | Generic | 4 | Untested
+Puretek | PT-6007 | 5 | Working
+Creative Labs | WebCam 3 | 21 | Working
+??? | Koala-Cam | 36 | Untested
+Lifeview | RoboCam | 100 | Untested
+AverMedia | InterCam Elite | 102 | Working
+MediaForte | MV300 | 112 | Working
+Omnivision | OV7110 EV board | 112 | Working*
+---------------------------------------------------------
+(*) uses OV7110 (monochrome)
Any camera using the OV511 and the OV7610 CCD should work with this driver. The
driver only detects known cameras though, based on their custom id number. If
you have a currently unsupported camera, the ID number should be reported to you
-in the kernel logs. If you have an unsupported camera, please send me the model,
-manufacturer and ID number and I will add it to the detection code. In the
-meantime, you can add to the code yourself in the function ov511_probe()
+in the kernel logs. Please send me the model, manufacturer and ID number and I
+will add it to the detection code. In the meantime, you can add to the code
+yourself in the function ov511_probe().
WHAT YOU NEED:
@@ -44,7 +60,7 @@ HOW TO USE IT:
You must have first compiled USB support, support for your specific USB host
controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
-making them modules.)
+making them modules.)
Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
@@ -56,6 +72,14 @@ Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
If it is not already there (it usually is), create the video device:
mknod /dev/video c 81 0
+
+Sometimes /dev/video is a symlink to /dev/video0
+
+You will have to set permissions on this device to allow you to read/write
+from it:
+
+ chmod 666 /dev/video
+ chmod 666 /dev/video0 (if necessary)
Now you are ready to run a video app! Both vidcat and xawtv work well for me
at 640x480.
@@ -83,20 +107,103 @@ Now you should be able to run xawtv. Right click for the options dialog. If
you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
Try setting the size to 320x240 if all else fails.
+FAQ:
+Q: "Why does the picture have noise and look grainy"
+A: This is a problem at low light levels, and may be also due to subtle bugs in
+ the code. The cause is most likely the OV7610 settings we are currently
+ using. I am looking into this problem.
+
+Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?"
+A: The I2C code that allows the OV511 to communicate with the camera chip is a
+ bit flaky right now. This message means that the I2C bus never got
+ initialized properly, and the camera will most likely not work even if you
+ disable this warning. Try unloading/reloading the driver or unplugging/re-
+ plugging the camera if this happens.
+
+Q: "Why do you bother with this phony camera detection crap? It doesn't do
+ anything useful!"
+A: The main purpose of only supporting known camera models is to force people
+ with new camera models to tell me about them, so I can assemble the list
+ above, and so the code can know what CCD chip you have. Right now, nearly all
+ of the cameras use the OV7610 and consequently I have not put support for
+ other ones in, so the value of the detection code is questionable. Eventually
+ though, new CCDs might appear and we will be fortunate to have the detection.
+
+MODULE PARAMETERS:
+
+ You can set these with: insmod ov511 NAME=VALUE
+ There is currently no way to set these on a per-camera basis.
+
+ NAME: autoadjust
+ TYPE: integer (boolean)
+ DEFAULT: 1
+ DESC: The camera normally adjusts exposure, gain, and hue automatically. This
+ can be set to 0 to disable this automatic adjustment. Note that there is
+ currently no way to set these parameters manually once autoadjust is
+ disabled. (This feature is not working yet)
+
+ NAME: debug
+ TYPE: integer (0-6)
+ DEFAULT: 3
+ DESC: Sets the threshold for printing debug messages. The higher the value,
+ the more is printed. The levels are cumulative, and are as follows:
+ 0=no debug messages
+ 1=init/detection/unload and other significant messages
+ 2=some warning messages
+ 3=config/control function calls
+ 4=most function calls and data parsing messages
+ 5=highly repetitive mesgs
+
+ NAME: fix_rgb_offset
+ TYPE: integer (boolean)
+ DEFAULT: 0
+ DESC: Some people have reported that the blue component of the image is one
+ or so lines higher than the red component. This is only apparent in
+ images with white objects on black backgrounds at 640x480. Setting this
+ to 1 will realign the color planes correctly. NOTE: This is still
+ experimental and very buggy.
+
+ NAME: snapshot
+ TYPE: integer (boolean)
+ DEFAULT: 0
+ DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
+ button is pressed. Note that this does not yet work with most apps,
+ including xawtv and vidcat. NOTE: See the section "TODO" for more info.
+
WORKING FEATURES:
o Color streaming/capture at 640x480 and 320x240
o YUV420 color
+ o Monochrome
o Setting/getting of saturation, contrast and brightness (no color yet)
-WHAT NEEDS TO BE DONE:
-
-The rest of the work will involve implementing support for all the different
-resolutions, color depths, etc. Also, while support for the OV511's proprietary
-lossy compression is apparently not necessary (the code currently disables it,)
-it would be a nice addition as it improves performance quite a bit. OmniVision
-wouldn't tell me how the algorithm works, so we can't really work on that yet.
-Please kindly inform OmniVision that you would like them to release their
-specifications to the Linux community.
+EXPERIMENTAL FEATURES:
+ o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
+ corrupted frames.
+ o Snapshot mode (only works with some read() based apps; see below for more)
+
+TODO:
+ o Fix the noise / grainy image problem.
+ o Get compression working. It would be a nice addition as it improves
+ frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
+ so we can't really work on that yet. Please kindly inform OmniVision that you
+ would like them to release their specifications to the Linux community.
+ o Get 160x120 working
+ o YUV422 (and other color modes)
+ o Fix read(). It only works right now if you run an mmap() based app like xawtv
+ or vidcat after loading the module and before using read(). Apparently there
+ are some initialization issues.
+ o Get snapshot mode working with mmap().
+ o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
+ o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture()
+ and ov511_set_picture())
+ o Get autoadjust disable working
+ o Devise some clean way to support different types of CCDs (based on Custom ID)
+ o OV511A support
+ o V4L2 support (Probably not until it goes into the kernel)
+ o Fix I2C initialization. Some people are reporting problems with reading the
+ 7610 registers. This could be due to timing differences, an excessive I2C
+ clock rate, or a problem with ov511_i2c_read().
+ o Get rid of the memory management functions (put them in videodev.c??)
HOW TO CONTACT ME:
@@ -108,4 +215,5 @@ CREDITS:
The code is based in no small part on the CPiA driver by Johannes Erdfelt,
Randy Dunlap, and others. Big thanks to them for their pioneering work on that
and the USB stack. Thanks to Bret Wallach for getting camera reg IO , ISOC, and
-image capture working.
+image capture working. Thanks to Orion Sky Lawlor and Kevin Moore for their
+work as well.
diff --git a/Documentation/video4linux/CQcam.txt b/Documentation/video4linux/CQcam.txt
new file mode 100644
index 000000000..6d54c07c0
--- /dev/null
+++ b/Documentation/video4linux/CQcam.txt
@@ -0,0 +1,414 @@
+c-qcam - Connectix Color QuickCam video4linux kernel driver
+
+Copyright (C) 1999 Dave Forrest <drf5n@virginia.edu>
+ released under GNU GPL.
+
+1999-12-08 Dave Forrest, written with kernel version 2.2.12 in mind
+
+
+Table of Contents
+
+1.0 Introduction
+2.0 Compilation, Installation, and Configuration
+3.0 Troubleshooting
+4.0 Future Work / current work arounds
+9.0 Sample Program, v4lgrab
+10.0 Other Information
+
+
+1.0 Introduction
+
+ The file ../drivers/char/c-qcam.c is a device driver for the
+Logitech (nee Connectix) parallel port interface color CCD camera.
+This is a fairly inexpensive device for capturing images. Logitech
+does not currently provide information for developers, but many people
+have engineered several solutions for non-Microsoft use of the Color
+Quickcam.
+
+1.1 Motivation
+
+ I spent a number of hours trying to get my camera to work, and I
+hope this document saves you some time. My camera will not work with
+the 2.2.13 kernel as distributed, but with a few patches to the
+module, I was able to grab some frames. See 4.0, Future Work.
+
+
+
+2.0 Compilation, Installation, and Configuration
+
+ The c-qcam depends on parallel port support, video4linux, and the
+Color Quickcam. It is also nice to have the parallel port readback
+support enabled. I enabled these as modules during the kernel
+configuration. The appropriate flags are:
+
+ CONFIG_PRINTER M for lp.o, parport.o parport_pc.o modules
+ CONFIG_PNP_PARPORT M for autoprobe.o IEEE1284 readback module
+ CONFIG_PRINTER_READBACK M for parport_probe.o IEEE1284 readback module
+ CONFIG_VIDEO_DEV M for videodev.o video4linux module
+ CONFIG_VIDEO_CQCAM M for c-qcam.o Color Quickcam module
+
+ With these flags, the kernel should compile and install the modules.
+To record and monitor the compilation, I use:
+
+ (make dep; \
+ make zlilo ; \
+ make modules; \
+ make modules_install ;
+ depmod -a ) &>log &
+ less log # then a capital 'F' to watch the progress
+
+But that is my personal preference.
+
+2.2 Configuration
+
+ The configuration requires module configuration and device
+configuration. I like kmod or kerneld process with the
+/etc/modules.conf file so the modules can automatically load/unload as
+they are used. The video devices could already exist, be generated
+using MAKEDEV, or need to be created. The following sections detail
+these procedures.
+
+
+2.1 Module Configuration
+
+ Using modules requires a bit of work to install and pass the
+parameters. Do read ../modules.txt, and understand that entries
+in /etc/modules.conf of:
+
+ alias parport_lowlevel parport_pc
+ options parport_pc io=0x378 irq=none
+ alias char-major-81 videodev
+ alias char-major-81-0 c-qcam
+
+will cause the kmod/kerneld/modprobe to do certain things. If you are
+using kmod or kerneld, then a request for a 'char-major-81-0' will cause
+the 'c-qcam' module to load. If you have other video sources with
+modules, you might want to assign the different minor numbers to
+different modules.
+
+2.2 Device Configuration
+
+ At this point, we need to ensure that the device files exist.
+Video4linux used the /dev/video* files, and we want to attach the
+Quickcam to one of these.
+
+ ls -lad /dev/video* # should produce a list of the video devices
+
+If the video devices do not exist, you can create them with:
+
+ su
+ cd /dev
+ for ii in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
+ mknod video$ii c 81 $ii # char-major-81-[0-16]
+ chown root.root video$ii # owned by root
+ chmod 600 video$ii # read/writable by root only
+ done
+
+ Lots of people connect video0 to video and bttv, but you might want
+your c-qcam to mean something more:
+
+ ln -s video0 c-qcam # make /dev/c-qcam a working file
+ ln -s c-qcam video # make /dev/c-qcam your default video source
+
+ But these are conveniences. The important part is to make the proper
+special character files with the right major and minor numbers. All
+of the special device files are listed in ../devices.txt. If you
+would like the c-qcam readable by non-root users, you will need to
+change the permissions.
+
+3.0 Troubleshooting
+
+ If the sample program below, v4lgrab, gives you output then
+everything is working.
+
+ v4lgrab | wc # should give you a count of characters
+
+ Otherwise, you have some problem.
+
+ The c-qcam is IEEE1284 compatible, so if you are using the proc file
+system (CONFIG_PROC_FS), the parallel printer support
+(CONFIG_PRINTER), the IEEE 1284 sytem,(CONFIG_PRINTER_READBACK), you
+should be able to read some identification from your quickcam with
+
+ modprobe -v parport
+ modprobe -v parport_probe
+ cat /proc/parport/PORTNUMBER/autoprobe
+Returns:
+ CLASS:MEDIA;
+ MODEL:Color QuickCam 2.0;
+ MANUFACTURER:Connectix;
+
+ A good response to this indicates that your color quickcam is alive
+and well. A common problem is that the current driver does not
+reliably detect a c-qcam, even though one is attached. In this case,
+
+ modprobe -v c-qcam
+or
+ insmod -v c-qcam
+
+ Returns a message saying "Device or resource busy" Development is
+currently underway, but a workaround is to patch the module to skip
+the detection code and attach to a defined port. Check the
+video4linux mailing list and archive for more current information.
+
+3.1 Checklist:
+
+ Can you get an image?
+ v4lgrab >qcam.ppm ; wc qcam.ppm ; xv qcam.ppm
+
+ Is a working c-qcam connected to the port?
+ grep ^ /proc/parport/?/autoprobe
+
+ Do the /dev/video* files exist?
+ ls -lad /dev/video
+
+ Is the c-qcam module loaded?
+ modprobe -v c-qcam ; lsmod
+
+ Does the camera work with alternate programs? cqcam, etc?
+
+
+
+
+4.0 Future Work / current workarounds
+
+ It is hoped that this section will soon become obsolete, but if it
+isn't, you might try patching the c-qcam module to add a parport=xxx
+option as in the bw-qcam module so you can specify the parallel port:
+
+ insmod -v c-qcam parport=0
+
+And bypass the detection code, see ../../drivers/char/c-qcam.c and
+look for the 'qc_detect' code and call.
+
+ Note that there is work in progress to change the video4linux API,
+this work is documented at the video4linux2 site listed below.
+
+
+9.0 --- A sample program using v4lgrabber,
+
+This program is a simple image grabber that will copy a frame from the
+first video device, /dev/video0 to standard output in portable pixmap
+format (.ppm) Using this like: 'v4lgrab | convert - c-qcam.jpg'
+produced this picture of me at
+ http://mug.sys.virginia.edu/~drf5n/extras/c-qcam.jpg
+
+-------------------- 8< ---------------- 8< -----------------------------
+
+/* Simple Video4Linux image grabber. */
+/*
+ * Video4Linux Driver Test/Example Framegrabbing Program
+ *
+ * Compile with:
+ * gcc -s -Wall -Wstrict-prototypes v4lgrab.c -o v4lgrab
+ * Use as:
+ * v4lgrab >image.ppm
+ *
+ * Copyright (C) 1998-05-03, Phil Blundell <philb@gnu.org>
+ * Copied from http://www.tazenda.demon.co.uk/phil/vgrabber.c
+ * with minor modifications (Dave Forrest, drf5n@virginia.edu).
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+
+#include <linux/types.h>
+#include <linux/videodev.h>
+
+#define FILE "/dev/video0"
+
+/* Stole this from tvset.c */
+
+#define READ_VIDEO_PIXEL(buf, format, depth, r, g, b) \
+{ \
+ switch (format) \
+ { \
+ case VIDEO_PALETTE_GREY: \
+ switch (depth) \
+ { \
+ case 4: \
+ case 6: \
+ case 8: \
+ (r) = (g) = (b) = (*buf++ << 8);\
+ break; \
+ \
+ case 16: \
+ (r) = (g) = (b) = \
+ *((unsigned short *) buf); \
+ buf += 2; \
+ break; \
+ } \
+ break; \
+ \
+ \
+ case VIDEO_PALETTE_RGB565: \
+ { \
+ unsigned short tmp = *(unsigned short *)buf; \
+ (r) = tmp&0xF800; \
+ (g) = (tmp<<5)&0xFC00; \
+ (b) = (tmp<<11)&0xF800; \
+ buf += 2; \
+ } \
+ break; \
+ \
+ case VIDEO_PALETTE_RGB555: \
+ (r) = (buf[0]&0xF8)<<8; \
+ (g) = ((buf[0] << 5 | buf[1] >> 3)&0xF8)<<8; \
+ (b) = ((buf[1] << 2 ) & 0xF8)<<8; \
+ buf += 2; \
+ break; \
+ \
+ case VIDEO_PALETTE_RGB24: \
+ (r) = buf[0] << 8; (g) = buf[1] << 8; \
+ (b) = buf[2] << 8; \
+ buf += 3; \
+ break; \
+ \
+ default: \
+ fprintf(stderr, \
+ "Format %d not yet supported\n", \
+ format); \
+ } \
+}
+
+int get_brightness_adj(unsigned char *image, long size, int *brightness) {
+ long i, tot = 0;
+ for (i=0;i<size*3;i++)
+ tot += image[i];
+ *brightness = (128 - tot/(size*3))/3;
+ return !((tot/(size*3)) >= 126 && (tot/(size*3)) <= 130);
+}
+
+int main(int argc, char ** argv)
+{
+ int fd = open(FILE, O_RDONLY), f;
+ struct video_capability cap;
+ struct video_window win;
+ struct video_picture vpic;
+
+ unsigned char *buffer, *src;
+ int bpp = 24, r, g, b;
+ unsigned int i, src_depth;
+
+ if (fd < 0) {
+ perror(FILE);
+ exit(1);
+ }
+
+ if (ioctl(fd, VIDIOCGCAP, &cap) < 0) {
+ perror("VIDIOGCAP");
+ fprintf(stderr, "(" FILE " not a video4linux device?)\n");
+ close(fd);
+ exit(1);
+ }
+
+ if (ioctl(fd, VIDIOCGWIN, &win) < 0) {
+ perror("VIDIOCGWIN");
+ close(fd);
+ exit(1);
+ }
+
+ if (ioctl(fd, VIDIOCGPICT, &vpic) < 0) {
+ perror("VIDIOCGPICT");
+ close(fd);
+ exit(1);
+ }
+
+ if (cap.type & VID_TYPE_MONOCHROME) {
+ vpic.depth=8;
+ vpic.palette=VIDEO_PALETTE_GREY; /* 8bit grey */
+ if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+ vpic.depth=6;
+ if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+ vpic.depth=4;
+ if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+ fprintf(stderr, "Unable to find a supported capture format.\n");
+ close(fd);
+ exit(1);
+ }
+ }
+ }
+ } else {
+ vpic.depth=24;
+ vpic.palette=VIDEO_PALETTE_RGB24;
+
+ if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
+ vpic.palette=VIDEO_PALETTE_RGB565;
+ vpic.depth=16;
+
+ if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+ vpic.palette=VIDEO_PALETTE_RGB555;
+ vpic.depth=15;
+
+ if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+ fprintf(stderr, "Unable to find a supported capture format.\n");
+ return -1;
+ }
+ }
+ }
+ }
+
+ buffer = malloc(win.width * win.height * bpp);
+ if (!buffer) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+ }
+
+ do {
+ int newbright;
+ read(fd, buffer, win.width * win.height * bpp);
+ f = get_brightness_adj(buffer, win.width * win.height, &newbright);
+ if (f) {
+ vpic.brightness += (newbright << 8);
+ if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
+ perror("VIDIOSPICT");
+ break;
+ }
+ }
+ } while (f);
+
+ fprintf(stdout, "P6\n%d %d 255\n", win.width, win.height);
+
+ src = buffer;
+
+ for (i = 0; i < win.width * win.height; i++) {
+ READ_VIDEO_PIXEL(src, vpic.palette, src_depth, r, g, b);
+ fputc(r>>8, stdout);
+ fputc(g>>8, stdout);
+ fputc(b>>8, stdout);
+ }
+
+ close(fd);
+ return 0;
+}
+-------------------- 8< ---------------- 8< -----------------------------
+
+
+10.0 --- Other Information
+
+Use the ../../Maintainers file, particularly the VIDEO FOR LINUX and PARALLEL
+PORT SUPPORT sections
+
+The video4linux page:
+ http://roadrunner.swansea.linux.org.uk/v4l.shtml
+
+The video4linux2 page:
+ http://millennium.diads.com/bdirks/v4l2.htm
+
+Some web pages about the quickcams:
+ http://www.dkfz-heidelberg.de/Macromol/wedemann/mini-HOWTO-cqcam.html
+
+ http://www.crynwr.com/qcpc/ QuickCam Third-Party Drivers
+ http://www.crynwr.com/qcpc/re.html Some Reverse Engineering
+ http://cse.unl.edu/~cluening/gqcam/ v4l client
+ http://phobos.illtel.denver.co.us/pub/qcread/ doesn't use v4l
+ ftp://ftp.cs.unm.edu/pub/chris/quickcam/ Has lots of drivers
+ http://www.cs.duke.edu/~reynolds/quickcam/ Has lots of information
+
+
diff --git a/Documentation/video4linux/bttv/README b/Documentation/video4linux/bttv/README
index 9d0709a57..4d8298772 100644
--- a/Documentation/video4linux/bttv/README
+++ b/Documentation/video4linux/bttv/README
@@ -17,7 +17,7 @@ CONFIG_I2C=m
CONFIG_I2C_ALGOBIT=m
The latest bttv version is available here:
- http://www.in-berlin.de/User/kraxel/v4l/
+ http://me.in-berlin.de/~kraxel/bttv.html
You'll find Ralphs original (mostly outdated) documentation in the
ralphs-doc subdirectory.
diff --git a/MAINTAINERS b/MAINTAINERS
index 20c1de3a0..6762ce048 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -212,6 +212,11 @@ W: http://www.wittsend.com/computone.html
L: linux-computone@lazuli.wittsend.com
S: Supported
+COMX/MULTIGATE SYNC SERIAL DRIVERS
+P: Gergely Madarasz
+M: Gergely Madarasz <gorgo@itc.hu>
+S: Supported
+
CONFIGURE, MENUCONFIG, XCONFIG
P: Michael Elizabeth Chastain
M: mec@shout.net
@@ -287,7 +292,7 @@ L: linux-kernel@vger.rutgers.edu
S: Maintained
DIGI INTL. EPCA DRIVER
-P: Chad Schwartz
+P: Chad Schwartz
M: support@dgii.com
M: chads@dgii.com
L: digilnux@dgii.com
@@ -330,6 +335,11 @@ M: mike@i-Connect.Net
L: linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
S: Maintained
+EEPRO100 NETWORK DRIVER
+P: Andrey V. Savochkin
+M: saw@saw.sw.com.sg
+S: Maintained
+
ETHEREXPRESS-16 NETWORK DRIVER
P: Philip Blundell
M: Philip.Blundell@pobox.com
@@ -750,7 +760,7 @@ S: Maintained
OPL3-SA2, SA3, and SAx DRIVER
P: Scott Murray
-M: scottm@interlog.com
+M: scott@spiteful.org
L: linux-sound@vger.rutgers.edu
S: Maintained
@@ -825,12 +835,17 @@ M: promise@pnd-pc.demon.co.uk
W: http://www.pnd-pc.demon.co.uk/promise/
S: Maintained
+QNX4 FILESYSTEM
+P: Anders Larsen
+M: al@alarsen.net
+L: linux-kernel@vger.rutgers.edu
+W: http://www.alarsen.net/linux/qnx4fs/
+S: Maintained
+
RAGE128 FRAMEBUFFER DISPLAY DRIVER
P: Brad Douglas
M: brad@neruo.com
-P: Anthony Tong
-M: atong@uiuc.edu
-L: linux-fbdev@vcuser.vc.union.edu
+L: linux-fbdev@vuser.vc.union.edu
S: Maintained
RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
@@ -839,13 +854,6 @@ M: corey@world.std.com
L: linux-kernel@vger.rutgers.edu
S: Maintained
-QNX4 FILESYSTEM
-P: Anders Larsen
-M: al@alarsen.net
-L: linux-kernel@vger.rutgers.edu
-W: http://www.alarsen.net/linux/qnx4fs/
-S: Maintained
-
REAL TIME CLOCK DRIVER
P: Paul Gortmaker
M: p_gortmaker@yahoo.com
@@ -853,8 +861,8 @@ L: linux-kernel@vger.rutgers.edu
S: Maintained
ROSE NETWORK LAYER
-P: Frederic Rible
-M: frible@teaser.fr
+P: Jean-Paul Roubelat
+M: jpr@f6fbb.org
L: linux-hams@vger.rutgers.edu
S: Maintained
@@ -864,6 +872,13 @@ M: pgmdsg@ibi.com
L: linux-kernel@vger.rutgers.edu
S: Maintained
+RTLINUX REALTIME LINUX
+P: Victor Yodaiken
+M: yodaiken@fsmlabs.com
+L: rtl@rtlinux.org
+W: www.rtlinux.org
+S: Maintained
+
SA1100 SUPPORT
P: Nicolas Pitre
M: nico@cam.org
@@ -1100,6 +1115,12 @@ M: weissg@vienna.at
L: linux-usb@suse.com
S: Maintained
+USB PEGASUS DRIVER
+P: Petko Manolov
+M: petkan@spct.net
+L: linux-usb@suse.com
+S: Maintained
+
USB PRINTER DRIVER
P: Vojtech Pavlik
M: vojtech@suse.cz
@@ -1171,13 +1192,6 @@ M: eis@baty.hanse.de
L: linux-x25@vger.rutgers.edu
S: Maintained
-RTLINUX REALTIME LINUX
-P: Victor Yodaiken
-M: yodaiken@fsmlabs.com
-L: rtl@rtlinux.org
-W: www.rtlinux.org
-S: Maintained
-
Z85230 SYNCHRONOUS DRIVER
P: Alan Cox
M: alan@redhat.com
diff --git a/Makefile b/Makefile
index f7787297d..4f9affb7e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 51
-EXTRAVERSION =
+SUBLEVEL = 99
+EXTRAVERSION = -pre1
ARCH = mips
@@ -116,145 +116,53 @@ DRIVERS =drivers/block/block.a \
LIBS =$(TOPDIR)/lib/lib.a
SUBDIRS =kernel drivers mm fs net ipc lib
-ifdef CONFIG_DRM
-DRIVERS += drivers/char/drm/drm.o
-endif
-
-ifeq ($(CONFIG_AGP),y)
-DRIVERS += drivers/char/agp/agp.o
-endif
-
-ifdef CONFIG_NUBUS
-DRIVERS := $(DRIVERS) drivers/nubus/nubus.a
-endif
-
-ifeq ($(CONFIG_ISDN),y)
-DRIVERS := $(DRIVERS) drivers/isdn/isdn.a
-endif
-
-ifdef CONFIG_NET_FC
-DRIVERS := $(DRIVERS) drivers/net/fc/fc.a
-endif
-
-ifdef CONFIG_ATALK
-DRIVERS := $(DRIVERS) drivers/net/appletalk/appletalk.a
-endif
-
-ifdef CONFIG_TR
-DRIVERS := $(DRIVERS) drivers/net/tokenring/tr.a
-endif
-
-ifdef CONFIG_WAN
-DRIVERS := $(DRIVERS) drivers/net/wan/wan.a
-endif
-
-ifeq ($(CONFIG_ARCNET),y)
-DRIVERS := $(DRIVERS) drivers/net/arcnet/arcnet.a
-endif
-
-ifdef CONFIG_ATM
-DRIVERS := $(DRIVERS) drivers/atm/atm.a
-endif
-
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
-endif
-
-ifeq ($(CONFIG_IEEE1394),y)
-DRIVERS := $(DRIVERS) drivers/ieee1394/ieee1394.a
-endif
+DRIVERS-n :=
+DRIVERS-y :=
+DRIVERS-m :=
+DRIVERS- :=
+
+DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
+DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
+DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
+DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
+DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.a
+DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
+DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.a
+DRIVERS-$(CONFIG_IDE) += drivers/ide/ide.a
+DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsi.a
+DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394.a
ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
-endif
-
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o
-endif
-
-ifdef CONFIG_PCI
-DRIVERS := $(DRIVERS) drivers/pci/pci.a
-endif
-
-ifeq ($(CONFIG_PCMCIA),y)
-DRIVERS := $(DRIVERS) drivers/pcmcia/pcmcia.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_NETCARD),y)
-DRIVERS := $(DRIVERS) drivers/net/pcmcia/pcmcia_net.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_CHRDEV),y)
-DRIVERS := $(DRIVERS) drivers/char/pcmcia/pcmcia_char.o
-endif
-
-ifdef CONFIG_DIO
-DRIVERS := $(DRIVERS) drivers/dio/dio.a
-endif
-
-ifdef CONFIG_SBUS
-DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
-endif
-
-ifdef CONFIG_ZORRO
-DRIVERS := $(DRIVERS) drivers/zorro/zorro.a
-endif
-
-ifeq ($(CONFIG_FC4),y)
-DRIVERS := $(DRIVERS) drivers/fc4/fc4.a
-endif
-
-ifdef CONFIG_PPC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifdef CONFIG_MAC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifeq ($(CONFIG_ISAPNP),y)
-DRIVERS := $(DRIVERS) drivers/pnp/pnp.o
-endif
-
-ifdef CONFIG_SGI_IP22
-DRIVERS := $(DRIVERS) drivers/sgi/sgi.a
-endif
-
-ifdef CONFIG_VT
-DRIVERS := $(DRIVERS) drivers/video/video.o
-endif
-
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
-endif
-
-ifdef CONFIG_HAMRADIO
-DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.o
-endif
-
-ifeq ($(CONFIG_TC),y)
-DRIVERS := $(DRIVERS) drivers/tc/tc.a
-endif
-
-ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o
-endif
-
-ifeq ($(CONFIG_I2O),y)
-DRIVERS := $(DRIVERS) drivers/i2o/i2o.a
-endif
-
-ifeq ($(CONFIG_IRDA),y)
-DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a
-endif
-
-ifeq ($(CONFIG_I2C),y)
-DRIVERS := $(DRIVERS) drivers/i2c/i2c.a
-endif
-
-ifeq ($(CONFIG_PHONE),y)
-DRIVERS := $(DRIVERS) drivers/telephony/telephony.a
-endif
+DRIVERS-y += drivers/cdrom/cdrom.a
+endif
+
+DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+DRIVERS-$(CONFIG_PCI) += drivers/pci/pci.a
+DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
+DRIVERS-$(CONFIG_PCMCIA_NETCARD) += drivers/net/pcmcia/pcmcia_net.o
+DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o
+DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a
+DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus.a
+DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/zorro.a
+DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
+DRIVERS-$(CONFIG_PPC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
+DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
+DRIVERS-$(CONFIG_VT) += drivers/video/video.o
+DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
+DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
+DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
+DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.a
+DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda_drivers.a
+DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.a
+DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.a
+
+DRIVERS += $(DRIVERS-y)
include arch/$(ARCH)/Makefile
@@ -399,6 +307,7 @@ modules_install:
if [ -f IPV4_MODULES ]; then inst_mod IPV4_MODULES ipv4; fi; \
if [ -f IPV6_MODULES ]; then inst_mod IPV6_MODULES ipv6; fi; \
if [ -f ATM_MODULES ]; then inst_mod ATM_MODULES atm; fi; \
+ if [ -f IDE_MODULES ]; then inst_mod IDE_MODULES ide; fi; \
if [ -f SCSI_MODULES ]; then inst_mod SCSI_MODULES scsi; fi; \
if [ -f FS_MODULES ]; then inst_mod FS_MODULES fs; fi; \
if [ -f NLS_MODULES ]; then inst_mod NLS_MODULES fs; fi; \
@@ -485,6 +394,9 @@ backup: mrproper
cd .. && tar cf - linux/ | gzip -9 > backup.gz
sync
+sgmldocs:
+ $(MAKE) -C $(TOPDIR)/Documentation/DocBook books
+
sums:
find . -type f -print | sort | env -i xargs sum > .SUMS
diff --git a/arch/alpha/config.in b/arch/alpha/config.in
index 6e760c917..df7ae01ca 100644
--- a/arch/alpha/config.in
+++ b/arch/alpha/config.in
@@ -223,6 +223,19 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig
index cc244e560..c4752a216 100644
--- a/arch/alpha/defconfig
+++ b/arch/alpha/defconfig
@@ -70,12 +70,6 @@ CONFIG_BINFMT_EM86=y
# Block devices
#
CONFIG_BLK_DEV_FD=y
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
# CONFIG_BLK_CPQ_DA is not set
#
@@ -89,8 +83,6 @@ CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_DEV_DAC960 is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -123,6 +115,11 @@ CONFIG_SKB_LARGE=y
# CONFIG_ATALK is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6b4033705..ad8e9423a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -121,6 +121,11 @@ MACHINE = ebsa110
ARCHDIR = ebsa110
endif
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+MACHINE = clps7500
+ARCHDIR = cl7500
+endif
+
ifeq ($(CONFIG_FOOTBRIDGE),y)
MACHINE = footbridge
ARCHDIR = ebsa285
@@ -135,6 +140,11 @@ MACHINE = nexuspci
ARCHDIR = nexuspci
endif
+ifeq ($(CONFIG_ARCH_SHARK),y)
+MACHINE = shark
+ARCHDIR = shark
+endif
+
ifeq ($(CONFIG_ARCH_SA1100),y)
MACHINE = sa1100
ARCHDIR = sa1100
@@ -160,6 +170,11 @@ DRIVERS += drivers/acorn/net/acorn-net.a
DRIVERS += drivers/acorn/scsi/acorn-scsi.a
endif
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+SUBDIRS += drivers/acorn/char
+DRIVERS += drivers/acorn/char/acorn-char.o
+endif
+
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
# The following is a hack to get 'constants.h' up
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index fca3e8c92..37d79da39 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -25,6 +25,10 @@ ifeq ($(CONFIG_ARCH_RPC),y)
ZTEXTADDR = 0x10008000
endif
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+ZTEXTADDR = 0x10008000
+endif
+
ifeq ($(CONFIG_ARCH_EBSA110),y)
ZTEXTADDR = 0x00008000
endif
@@ -39,7 +43,6 @@ endif
ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
HEAD = head-nexuspci.o
-OBJS += $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o
ZTEXTADDR = 0x40200000
ZRELADDR = 0x40008000
endif
diff --git a/arch/arm/config.in b/arch/arm/config.in
index 4c7a5fe19..ff4f954df 100644
--- a/arch/arm/config.in
+++ b/arch/arm/config.in
@@ -37,6 +37,7 @@ if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
bool ' Include support for EBSA285' CONFIG_ARCH_EBSA285
bool ' Include support for CATS' CONFIG_CATS
bool ' Include support for NetWinder' CONFIG_ARCH_NETWINDER
+ bool ' Include support for Compaq Personal Server' CONFIG_PERSONAL_SERVER
fi
if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
@@ -170,6 +171,7 @@ source drivers/parport/Config.in
if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
"$CONFIG_ARCH_SA1100" = "y" -o \
"$CONFIG_ARCH_NETWINDER" = "y" -o \
+ "$CONFIG_PERSONAL_SERVER" = "y" -o \
"$CONFIG_CATS" = "y" ]; then
string 'Initial kernel command string' CONFIG_CMDLINE
fi
@@ -254,6 +256,19 @@ fi
# endmenu
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support?' CONFIG_SCSI
diff --git a/arch/arm/defconfig b/arch/arm/defconfig
index e43a8d2a9..45c89eeb3 100644
--- a/arch/arm/defconfig
+++ b/arch/arm/defconfig
@@ -87,46 +87,6 @@ CONFIG_ISAPNP=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-
-#
-# IDE chipset support/bugfixes
-#
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-CONFIG_IDEDMA_PCI_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
-CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
-CONFIG_BLK_DEV_OFFBOARD=y
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-# CONFIG_BLK_DEV_CMD646 is not set
-CONFIG_BLK_DEV_CY82C693=y
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_PDC202XX=y
-# CONFIG_PDC202XX_FORCE_BURST_BIT is not set
-# CONFIG_PDC202XX_FORCE_MASTER_MODE is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
# CONFIG_BLK_CPQ_DA is not set
#
@@ -172,8 +132,6 @@ CONFIG_PARIDE_KBIC=m
CONFIG_PARIDE_KTTI=m
CONFIG_PARIDE_ON20=m
CONFIG_PARIDE_ON26=m
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
#
# Character devices
@@ -509,6 +467,54 @@ CONFIG_SLIP_MODE_SLIP6=y
# CONFIG_NET_PCMCIA is not set
#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+CONFIG_BLK_DEV_OFFBOARD=y
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+CONFIG_BLK_DEV_CY82C693=y
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_PDC202XX=y
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
# CONFIG_SCSI is not set
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 3920edade..c454c1350 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -8,71 +8,55 @@
HEAD_OBJ = head-$(PROCESSOR).o
ENTRY_OBJ = entry-$(PROCESSOR).o
-O_TARGET := kernel.o
-O_OBJS := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
- semaphore.o setup.o signal.o sys_arm.o time.o traps.o
-
ifeq ($(CONFIG_ISA_DMA),y)
ISA_DMA_OBJS += dma-isa.o
endif
-O_OBJS_arc = dma-arc.o iic.o fiq.o time-acorn.o oldlatches.o
-O_OBJS_a5k = dma-a5k.o iic.o fiq.o time-acorn.o
-O_OBJS_rpc = dma-rpc.o iic.o fiq.o time-acorn.o
+O_OBJS_arc = dma-arc.o oldlatches.o
+O_OBJS_a5k = dma-a5k.o
+O_OBJS_rpc = dma-rpc.o
O_OBJS_ebsa110 = dma-dummy.o
-O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o
+O_OBJS_footbridge = dma.o dma-footbridge.o $(ISA_DMA_OBJS) hw-footbridge.o isa.o
O_OBJS_nexuspci = dma-dummy.o
O_OBJS_sa1100 = dma-dummy.o fiq.o
-OX_OBJS_arc = dma.o
-OX_OBJS_a5k = dma.o
-OX_OBJS_rpc = dma.o
-OX_OBJS_ebsa110 =
-OX_OBJS_footbridge= dma.o hw-footbridge.o
-OX_OBJS_nexuspci =
-OX_OBJS_sa1100 =
+O_TARGET := kernel.o
-all: kernel.o $(HEAD_OBJ) init_task.o
+# Object file lists.
-O_OBJS += $(O_OBJS_$(MACHINE))
+obj-y := arch.o $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o \
+ semaphore.o setup.o signal.o sys_arm.o time.o traps.o \
+ $(O_OBJS_$(MACHINE))
+obj-m :=
+obj-n :=
+obj- :=
-ifeq ($(CONFIG_DEBUG_LL),y)
- O_OBJS += debug-$(PROCESSOR).o
-endif
+export-objs := armksyms.o dma.o ecard.o hw-footbridge.o leds-$(MACHINE).o
-ifeq ($(CONFIG_MODULES),y)
- OX_OBJS = armksyms.o
-endif
+obj-$(CONFIG_ARCH_ACORN) += dma.o ecard.o iic.o fiq.o time-acorn.o
+obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o
+obj-$(CONFIG_MODULES) += armksyms.o
+obj-$(CONFIG_LEDS) += leds-$(MACHINE).o
+obj-$(CONFIG_ARTHUR) += arthur.o
-ifeq ($(CONFIG_ARCH_ACORN),y)
- OX_OBJS += ecard.o
+ifeq ($(MACHINE),nexuspci)
+ obj-$(CONFIG_PCI) += plx9080.o
+else
+ obj-$(CONFIG_PCI) += bios32.o dec21285.o
endif
-ifeq ($(CONFIG_PCI),y)
- ifeq ($(MACHINE),nexuspci)
- O_OBJS += plx9080.o
- else
- O_OBJS += bios32.o dec21285.o
- endif
-endif
+# Files that are both resident and modular; remove from modular.
-ifeq ($(CONFIG_LEDS),y)
- OX_OBJS += leds-$(MACHINE).o
-endif
+obj-m := $(filter-out $(obj-y), $(obj-m))
-ifeq ($(CONFIG_MODULES),y)
- OX_OBJS += $(OX_OBJS_$(MACHINE))
-else
- O_OBJS += $(OX_OBJS_$(MACHINE))
-endif
+# Translate to Rules.make lists.
-ifeq ($(CONFIG_ARTHUR),y)
- O_OBJS += arthur.o
-else
- ifeq ($(CONFIG_ARTHUR),m)
- M_OBJS += arthur.o
- endif
-endif
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
+all: kernel.o $(HEAD_OBJ) init_task.o
$(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
$(CC) -D__ASSEMBLY__ $(AFLAGS) -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
diff --git a/arch/arm/kernel/arch.c b/arch/arm/kernel/arch.c
new file mode 100644
index 000000000..8645497ae
--- /dev/null
+++ b/arch/arm/kernel/arch.c
@@ -0,0 +1,322 @@
+/*
+ * linux/arch/arm/kernel/arch.c
+ *
+ * Architecture specifics
+ */
+#include <linux/config.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+#include <asm/elf.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+
+#include "arch.h"
+
+extern unsigned int system_rev;
+extern unsigned int system_serial_low;
+extern unsigned int system_serial_high;
+
+unsigned int vram_size;
+#ifdef CONFIG_ARCH_ACORN
+unsigned int memc_ctrl_reg;
+unsigned int number_mfm_drives;
+#endif
+
+/*
+ * Architecture specific fixups. This is where any
+ * parameters in the params struct are fixed up, or
+ * any additional architecture specific information
+ * is pulled from the params struct.
+ */
+static void __init
+fixup_acorn(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_ARCH_ACORN
+ int i;
+
+ if (machine_is_riscpc()) {
+ /*
+ * RiscPC can't handle half-word loads and stores
+ */
+ elf_hwcap &= ~HWCAP_HALF;
+
+ switch (params->u1.s.pages_in_vram) {
+ case 512:
+ vram_size += PAGE_SIZE * 256;
+ case 256:
+ vram_size += PAGE_SIZE * 256;
+ default:
+ break;
+ }
+
+ if (vram_size) {
+ desc->video_start = 0x02000000;
+ desc->video_end = 0x02000000 + vram_size;
+ }
+
+ for (i = 0; i < 4; i++) {
+ mi->bank[i].start = PHYS_OFFSET + (i << 26);
+ mi->bank[i].size =
+ params->u1.s.pages_in_bank[i] *
+ params->u1.s.page_size;
+ }
+ mi->nr_banks = 4;
+ }
+ memc_ctrl_reg = params->u1.s.memc_control_reg;
+ number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+#endif
+}
+
+static void __init
+fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+ ORIG_X = params->u1.s.video_x;
+ ORIG_Y = params->u1.s.video_y;
+ ORIG_VIDEO_COLS = params->u1.s.video_num_cols;
+ ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+}
+
+/*
+ * Older NeTTroms either do not provide a parameters
+ * page, or they don't supply correct information in
+ * the parameter page.
+ */
+static void __init
+fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+ if (params->u1.s.nr_pages != 0x2000 &&
+ params->u1.s.nr_pages != 0x4000) {
+ printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+ "detected, using defaults\n");
+
+ params->u1.s.nr_pages = 0x2000; /* 32MB */
+ params->u1.s.ramdisk_size = 0;
+ params->u1.s.flags = FLAG_READONLY;
+ params->u1.s.initrd_start = 0;
+ params->u1.s.initrd_size = 0;
+ params->u1.s.rd_start = 0;
+ }
+}
+
+/*
+ * CATS uses soft-reboot by default, since
+ * hard reboots fail on early boards.
+ */
+static void __init
+fixup_cats(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+ ORIG_VIDEO_LINES = 25;
+ ORIG_VIDEO_POINTS = 16;
+ ORIG_Y = 24;
+}
+
+static void __init
+fixup_coebsa285(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+#if 0
+ extern unsigned long boot_memory_end;
+ extern char boot_command_line[];
+
+ mi->nr_banks = 1;
+ mi->bank[0].start = PHYS_OFFSET;
+ mi->bank[0].size = boot_memory_end;
+
+ *cmdline = boot_command_line;
+#endif
+}
+
+static void __init
+fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
+ char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_ARCH_SA1100
+ int i;
+ extern struct mem_desc {
+ unsigned long phys_start;
+ unsigned long length;
+ } mem_desc[];
+ extern unsigned int mem_desc_size;
+
+ for( i = 0; i < mem_desc_size; i++ ) {
+ if( i >= NR_BANKS ) {
+ printk( __FUNCTION__
+ ": mem_desc too large for meminfo structure\n");
+ break;
+ }
+ mi->bank[i].start = mem_desc[i].phys_start;
+ mi->bank[i].size = mem_desc[i].length;
+ }
+ mi->nr_banks = i;
+
+#if defined(CONFIG_SA1100_BRUTUS)
+ ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+ setup_ramdisk( 1, 0, 0, 8192 );
+ setup_initrd( __phys_to_virt(0xd8000000), 0x00400000 );
+#elif defined(CONFIG_SA1100_EMPEG)
+ ROOT_DEV = MKDEV( 3, 1 ); /* /dev/hda1 */
+ setup_ramdisk( 1, 0, 0, 4096 );
+ setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) );
+#elif defined(CONFIG_SA1100_TIFON)
+ ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
+ setup_ramdisk(1, 0, 0, 4096);
+ setup_initrd( 0xd0000000 + 0x1100004, 0x140000 );
+#elif defined(CONFIG_SA1100_VICTOR)
+ ROOT_DEV = MKDEV( 60, 2 );
+
+ /* Get command line parameters passed from the loader (if any) */
+ if( *((char*)0xc0000000) )
+ strcpy( default_command_line, ((char *)0xc0000000) );
+
+ /* power off if any problem */
+ strcat( default_command_line, " panic=1" );
+#elif defined(CONFIG_SA1100_LART)
+ ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+ setup_ramdisk(1, 0, 0, 8192);
+ setup_initrd(0xc0400000, 0x00400000);
+#endif
+#endif
+}
+
+#define NO_PARAMS 0
+#define NO_VIDEO 0, 0
+
+/*
+ * This is the list of all architectures supported by
+ * this kernel. This should be integrated with the list
+ * in head-armv.S.
+ */
+static struct machine_desc machine_desc[] __attribute__ ((__section__ (".arch.info"))) = {
+ {
+ MACH_TYPE_EBSA110,
+ "EBSA110", /* RMK */
+ 0x00000400,
+ NO_VIDEO,
+ 1, 0, 1, 1, 1,
+ NULL
+ }, {
+ MACH_TYPE_RISCPC,
+ "Acorn-RiscPC", /* RMK */
+ 0x10000100,
+ NO_VIDEO,
+ 1, 1, 0, 0, 0,
+ fixup_acorn
+ }, {
+ 2,
+ "unknown",
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_NEXUSPCI,
+ "FTV/PCI", /* Philip Blundell */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_EBSA285,
+ "EBSA285", /* RMK */
+ 0x00000100,
+ 0x000a0000, 0x000bffff,
+ 0, 0, 0, 0, 0,
+ fixup_ebsa285
+ }, {
+ MACH_TYPE_NETWINDER,
+ "Rebel-NetWinder", /* RMK */
+ 0x00000100,
+ 0x000a0000, 0x000bffff,
+ 1, 0, 1, 0, 0,
+ fixup_netwinder
+ }, {
+ MACH_TYPE_CATS,
+ "Chalice-CATS", /* Philip Blundell */
+ NO_PARAMS,
+ 0x000a0000, 0x000bffff,
+ 0, 0, 0, 0, 1,
+ fixup_cats
+ }, {
+ MACH_TYPE_TBOX,
+ "unknown-TBOX", /* Philip Blundell */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_CO285,
+ "co-EBSA285", /* Mark van Doesburg */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ fixup_coebsa285
+ }, {
+ MACH_TYPE_CLPS7110,
+ "CL-PS7110", /* Werner Almesberger */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_ARCHIMEDES,
+ "Acorn-Archimedes",/* RMK/DAG */
+ 0x0207c000,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ fixup_acorn
+ }, {
+ MACH_TYPE_A5K,
+ "Acorn-A5000", /* RMK/PB */
+ 0x0207c000,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ fixup_acorn
+ }, {
+ MACH_TYPE_ETOILE,
+ "Etoile", /* Alex de Vries */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_LACIE_NAS,
+ "LaCie_NAS", /* Benjamin Herrenschmidt */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_CLPS7500,
+ "CL-PS7500", /* Philip Blundell */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_SHARK,
+ "Shark", /* Alexander Schulz */
+ NO_PARAMS,
+ 0x06000000, 0x06000000+0x001fffff,
+ 0, 0, 0, 0, 0,
+ NULL
+ }, {
+ MACH_TYPE_SA1100,
+ "SA1100-based", /* Nicolas Pitre */
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ fixup_sa1100
+ }, {
+ MACH_TYPE_PERSONAL_SERVER,
+ "Compaq Personal Server",
+ NO_PARAMS,
+ NO_VIDEO,
+ 0, 0, 0, 0, 0,
+ NULL
+ }
+};
diff --git a/arch/arm/kernel/arch.h b/arch/arm/kernel/arch.h
new file mode 100644
index 000000000..b7635b11e
--- /dev/null
+++ b/arch/arm/kernel/arch.h
@@ -0,0 +1,15 @@
+struct machine_desc {
+ unsigned int nr; /* architecture number */
+ const char *name; /* architecture name */
+ unsigned int param_offset; /* parameter page */
+ unsigned int video_start; /* start of video RAM */
+ unsigned int video_end; /* end of video RAM */
+ unsigned int reserve_lp0 :1; /* never has lp0 */
+ unsigned int reserve_lp1 :1; /* never has lp1 */
+ unsigned int reserve_lp2 :1; /* never has lp2 */
+ unsigned int broken_hlt :1; /* hlt is broken */
+ unsigned int soft_reboot :1; /* soft reboot */
+ void (*fixup)(struct machine_desc *,
+ struct param_struct *, char **,
+ struct meminfo *);
+};
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index c445daeee..5a1b4ed31 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/in6.h>
+#include <linux/vt_kern.h>
#include <asm/byteorder.h>
#include <asm/elf.h>
@@ -83,6 +84,8 @@ EXPORT_SYMBOL(fpundefinstr);
EXPORT_SYMBOL(ret_from_exception);
#endif
+EXPORT_SYMBOL(kd_mksound);
+
/* platform dependent support */
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(dump_fpu);
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 7a1b57c22..1b0a064d0 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -222,8 +222,9 @@ static u8 __init no_swizzle(struct pci_dev *dev, u8 *pin)
return 0;
}
-#ifdef CONFIG_FOOTBRIDGE
/* ebsa285 host-specific stuff */
+
+#ifdef CONFIG_ARCH_EBSA285
static int irqmap_ebsa285[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 };
static u8 __init ebsa285_swizzle(struct pci_dev *dev, u8 *pin)
@@ -251,7 +252,9 @@ static struct hw_pci ebsa285_pci __initdata = {
ebsa285_swizzle,
ebsa285_map_irq
};
+#endif
+#ifdef CONFIG_CATS
/* cats host-specific stuff */
static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
@@ -277,7 +280,9 @@ static struct hw_pci cats_pci __initdata = {
no_swizzle,
cats_map_irq
};
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
/* netwinder host-specific stuff */
static int __init netwinder_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
{
@@ -318,6 +323,41 @@ static struct hw_pci netwinder_pci __initdata = {
};
#endif
+#ifdef CONFIG_PERSONAL_SERVER
+static int irqmap_personal_server[] __initdata = {
+ IRQ_IN0, IRQ_IN1, IRQ_IN2, IRQ_IN3, 0, 0, 0,
+ IRQ_DOORBELLHOST, IRQ_DMA1, IRQ_DMA2, IRQ_PCI
+};
+
+static int __init personal_server_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ unsigned char line;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
+
+ if (line > 0x40 && line <= 0x5f) {
+ /* line corresponds to the bit controlling this interrupt
+ * in the footbridge. Ignore the first 8 interrupt bits,
+ * look up the rest in the map. IN0 is bit number 8
+ */
+ return irqmap_personal_server[(line & 0x1f) - 8];
+ } else if (line == 0) {
+ /* no interrupt */
+ return 0;
+ } else
+ return irqmap_personal_server[(line - 1) & 3];
+}
+
+static struct hw_pci personal_server_pci __initdata = {
+ dc21285_init,
+ 0x9000,
+ 0x00100000,
+ no_swizzle,
+ personal_server_map_irq
+};
+
+#endif
+
#ifdef CONFIG_ARCH_NEXUSPCI
/*
* Owing to a PCB cockup, issue A backplanes are wired thus:
@@ -352,17 +392,38 @@ void __init pcibios_init(void)
{
struct hw_pci *hw_pci = NULL;
-#ifdef CONFIG_FOOTBRIDGE
- if (machine_is_ebsa285())
- hw_pci = &ebsa285_pci;
- else if (machine_is_cats())
- hw_pci = &cats_pci;
- else if (machine_is_netwinder())
- hw_pci = &netwinder_pci;
+ do {
+#ifdef CONFIG_ARCH_EBSA285
+ if (machine_is_ebsa285()) {
+ hw_pci = &ebsa285_pci;
+ break;
+ }
+#endif
+#ifdef CONFIG_CATS
+ if (machine_is_cats()) {
+ hw_pci = &cats_pci;
+ break;
+ }
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+ if (machine_is_netwinder()) {
+ hw_pci = &netwinder_pci;
+ break;
+ }
+#endif
+#ifdef CONFIG_PERSONAL_SERVER
+ if (machine_is_personal_server()) {
+ hw_pci = &personal_server_pci;
+ break;
+ }
#endif
#ifdef CONFIG_ARCH_NEXUSPCI
- hw_pci = &ftv_pci;
+ if (machine_is_nexuspci()) {
+ hw_pci = &ftv_pci;
+ break;
+ }
#endif
+ } while (0);
if (hw_pci == NULL)
return;
diff --git a/arch/arm/kernel/head-armv.S b/arch/arm/kernel/head-armv.S
index 35e71a4a9..27a280b8b 100644
--- a/arch/arm/kernel/head-armv.S
+++ b/arch/arm/kernel/head-armv.S
@@ -22,6 +22,7 @@
.equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x8000 + SWAPPER_PGDIR_OFFSET
.section ".text.init",#alloc,#execinstr
+ .type stext, #function
ENTRY(stext)
ENTRY(_stext)
@@ -311,6 +312,7 @@ __lookup_architecture_type:
* required for debugging information to be shown to the user.
* paging_init() does the real page table initialisation.
*/
+ .type __arch_types_start, #object
@ 0x00 - DEC EBSA110
__arch_types_start:
.long 0
@@ -398,15 +400,15 @@ __arch_types_start:
@ 0x0e - CL-PS7500
.long 0
- .long 0
- .long 0
- .long 0
+ .long 0x10000000
+ .long 0x03000000
+ .long 0xe0000000
- @ 0x0f - Shark
- .long 0
- .long 0
- .long 0
+ @ 0x0f - Digital Shark (DNARD)
.long 0
+ .long 0x08000000
+ .long 0x40000000
+ .long 0xe0000000
@ 0x10 - SA1100
.long 0
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 0692d1a6c..0ace4c898 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -521,6 +521,48 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
/* give it a chance to run. */
ret = 0;
goto out;
+
+ case PTRACE_GETREGS:
+ { /* Get all gp regs from the child. */
+ unsigned char *stack;
+
+ ret = 0;
+ stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+ if (copy_to_user((void *)data, stack,
+ sizeof(struct pt_regs)))
+ ret = -EFAULT;
+
+ goto out;
+ };
+
+ case PTRACE_SETREGS:
+ {
+ /* Set all gp regs in the child. */
+ unsigned char *stack;
+
+ ret = 0;
+ stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
+ if (copy_from_user(stack, (void *)data,
+ sizeof(struct pt_regs)))
+ ret = -EFAULT;
+ goto out;
+ };
+
+ case PTRACE_GETFPREGS:
+ /* Get the child FPU state. */
+ ret = 0;
+ if (copy_to_user((void *)data, &child->thread.fpstate,
+ sizeof(struct user_fp)))
+ ret = -EFAULT;
+ goto out;
+
+ case PTRACE_SETFPREGS:
+ /* Set the child FPU state. */
+ ret = 0;
+ if (copy_from_user(&child->thread.fpstate, (void *)data,
+ sizeof(struct user_fp)))
+ ret = -EFAULT;
+ goto out;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = -EIO;
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 5815529aa..6a3429bdd 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -1,13 +1,12 @@
/*
* linux/arch/arm/kernel/setup.c
*
- * Copyright (C) 1995-1999 Russell King
+ * Copyright (C) 1995-2000 Russell King
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
-#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/utsname.h>
@@ -23,6 +22,8 @@
#include <asm/setup.h>
#include <asm/system.h>
+#include "arch.h"
+
#ifndef MEM_SIZE
#define MEM_SIZE (16*1024*1024)
#endif
@@ -31,6 +32,7 @@
#define CONFIG_CMDLINE ""
#endif
+extern void paging_init(struct meminfo *);
extern void reboot_setup(char *str);
extern void disable_hlt(void);
extern int root_mountflags;
@@ -38,33 +40,11 @@ extern int _stext, _text, _etext, _edata, _end;
unsigned int processor_id;
unsigned int __machine_arch_type;
-unsigned int vram_size;
unsigned int system_rev;
unsigned int system_serial_low;
unsigned int system_serial_high;
unsigned int elf_hwcap;
-#ifdef CONFIG_ARCH_ACORN
-unsigned int memc_ctrl_reg;
-unsigned int number_mfm_drives;
-#endif
-
-struct meminfo meminfo;
-
-struct machine_desc {
- const char *name; /* architecture name */
- unsigned int param_offset; /* parameter page */
- unsigned int video_start; /* start of video RAM */
- unsigned int video_end; /* end of video RAM */
- unsigned int reserve_lp0 :1; /* never has lp0 */
- unsigned int reserve_lp1 :1; /* never has lp1 */
- unsigned int reserve_lp2 :1; /* never has lp2 */
- unsigned int broken_hlt :1; /* hlt is broken */
- unsigned int soft_reboot :1; /* soft reboot */
- void (*fixup)(struct machine_desc *,
- struct param_struct *, char **);
-};
-
#ifdef MULTI_CPU
struct processor processor;
#endif
@@ -156,6 +136,33 @@ static void __init setup_processor(void)
cpu_proc_init();
}
+static struct machine_desc * __init setup_architecture(unsigned int nr)
+{
+ extern struct machine_desc __arch_info_begin, __arch_info_end;
+ struct machine_desc *list;
+
+ /*
+ * locate architecture in the list of supported architectures.
+ */
+ for (list = &__arch_info_begin; list < &__arch_info_end; list++)
+ if (list->nr == nr)
+ break;
+
+ /*
+ * If the architecture type is not recognised, then we
+ * can co nothing...
+ */
+ if (list >= &__arch_info_end) {
+ printk("Architecture configuration botched (nr %d), unable "
+ "to continue.\n", nr);
+ while (1);
+ }
+
+ printk("Architecture: %s\n", list->name);
+
+ return list;
+}
+
static unsigned long __init memparse(char *ptr, char **retptr)
{
unsigned long ret = simple_strtoul(ptr, retptr, 0);
@@ -180,7 +187,7 @@ static unsigned long __init memparse(char *ptr, char **retptr)
* are "size[KkMm]"
*/
static void __init
-parse_cmdline(char **cmdline_p, char *from)
+parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from)
{
char c = ' ', *to = command_line;
int usermem = 0, len = 0;
@@ -198,7 +205,7 @@ parse_cmdline(char **cmdline_p, char *from)
*/
if (usermem == 0) {
usermem = 1;
- meminfo.nr_banks = 0;
+ mi->nr_banks = 0;
}
start = PHYS_OFFSET;
@@ -206,9 +213,9 @@ parse_cmdline(char **cmdline_p, char *from)
if (*from == '@')
start = memparse(from + 1, &from);
- meminfo.bank[meminfo.nr_banks].start = start;
- meminfo.bank[meminfo.nr_banks].size = size;
- meminfo.nr_banks += 1;
+ mi->bank[mi->nr_banks].start = start;
+ mi->bank[mi->nr_banks].size = size;
+ mi->nr_banks += 1;
}
c = *from++;
if (!c)
@@ -265,7 +272,8 @@ static void __init setup_initrd(unsigned int start, unsigned int size)
#define free_bootmem(s,sz) free_bootmem((s)<<PAGE_SHIFT, (sz)<<PAGE_SHIFT)
#define reserve_bootmem(s,sz) reserve_bootmem((s)<<PAGE_SHIFT, (sz)<<PAGE_SHIFT)
-static unsigned int __init find_bootmap_pfn(unsigned int bootmap_pages)
+static unsigned int __init
+find_bootmap_pfn(struct meminfo *mi, unsigned int bootmap_pages)
{
unsigned int start_pfn, bank, bootmap_pfn;
@@ -278,25 +286,25 @@ static unsigned int __init find_bootmap_pfn(unsigned int bootmap_pages)
*/
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start) {
- if (__pa(initrd_end) > meminfo.end) {
+ if (__pa(initrd_end) > mi->end) {
printk ("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx) - disabling initrd\n",
- __pa(initrd_end), meminfo.end);
+ __pa(initrd_end), mi->end);
initrd_start = 0;
initrd_end = 0;
}
}
#endif
- for (bank = 0; bank < meminfo.nr_banks; bank ++) {
+ for (bank = 0; bank < mi->nr_banks; bank ++) {
unsigned int start, end;
- if (meminfo.bank[bank].size == 0)
+ if (mi->bank[bank].size == 0)
continue;
- start = O_PFN_UP(meminfo.bank[bank].start);
- end = O_PFN_DOWN(meminfo.bank[bank].size +
- meminfo.bank[bank].start);
+ start = O_PFN_UP(mi->bank[bank].start);
+ end = O_PFN_DOWN(mi->bank[bank].size +
+ mi->bank[bank].start);
if (end < start_pfn)
continue;
@@ -322,7 +330,7 @@ static unsigned int __init find_bootmap_pfn(unsigned int bootmap_pages)
/*
* Initialise the bootmem allocator.
*/
-static void __init setup_bootmem(void)
+static void __init setup_bootmem(struct meminfo *mi)
{
unsigned int end_pfn, start_pfn, bootmap_pages, bootmap_pfn;
unsigned int i;
@@ -330,21 +338,21 @@ static void __init setup_bootmem(void)
/*
* Calculate the physical address of the top of memory.
*/
- meminfo.end = 0;
- for (i = 0; i < meminfo.nr_banks; i++) {
+ mi->end = 0;
+ for (i = 0; i < mi->nr_banks; i++) {
unsigned long end;
- if (meminfo.bank[i].size != 0) {
- end = meminfo.bank[i].start + meminfo.bank[i].size;
- if (meminfo.end < end)
- meminfo.end = end;
+ if (mi->bank[i].size != 0) {
+ end = mi->bank[i].start + mi->bank[i].size;
+ if (mi->end < end)
+ mi->end = end;
}
}
start_pfn = O_PFN_UP(PHYS_OFFSET);
- end_pfn = O_PFN_DOWN(meminfo.end);
+ end_pfn = O_PFN_DOWN(mi->end);
bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
- bootmap_pfn = find_bootmap_pfn(bootmap_pages);
+ bootmap_pfn = find_bootmap_pfn(mi, bootmap_pages);
/*
* Initialise the boot-time allocator
@@ -354,10 +362,10 @@ static void __init setup_bootmem(void)
/*
* Register all available RAM with the bootmem allocator.
*/
- for (i = 0; i < meminfo.nr_banks; i++)
- if (meminfo.bank[i].size)
- free_bootmem(O_PFN_UP(meminfo.bank[i].start),
- PFN_SIZE(meminfo.bank[i].size));
+ for (i = 0; i < mi->nr_banks; i++)
+ if (mi->bank[i].size)
+ free_bootmem(O_PFN_UP(mi->bank[i].start),
+ PFN_SIZE(mi->bank[i].size));
/*
* Register the reserved regions with bootmem
@@ -379,7 +387,8 @@ static void __init setup_bootmem(void)
#endif
}
-static void __init request_standard_resources(struct machine_desc *mdesc)
+static void __init
+request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
{
struct resource *res;
int i;
@@ -389,14 +398,14 @@ static void __init request_standard_resources(struct machine_desc *mdesc)
kernel_data.start = __virt_to_bus(init_mm.end_code);
kernel_data.end = __virt_to_bus(init_mm.brk - 1);
- for (i = 0; i < meminfo.nr_banks; i++) {
+ for (i = 0; i < mi->nr_banks; i++) {
unsigned long virt_start, virt_end;
- if (meminfo.bank[i].size == 0)
+ if (mi->bank[i].size == 0)
continue;
- virt_start = __phys_to_virt(meminfo.bank[i].start);
- virt_end = virt_start + meminfo.bank[i].size - 1;
+ virt_start = __phys_to_virt(mi->bank[i].start);
+ virt_end = virt_start + mi->bank[i].size - 1;
res = alloc_bootmem_low(sizeof(*res));
res->name = "System RAM";
@@ -432,270 +441,15 @@ static void __init request_standard_resources(struct machine_desc *mdesc)
request_resource(&ioport_resource, &lp2);
}
-/*
- * Architecture specific fixups. This is where any
- * parameters in the params struct are fixed up, or
- * any additional architecture specific information
- * is pulled from the params struct.
- */
-static void __init
-fixup_acorn(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
-#ifdef CONFIG_ARCH_ACORN
- int i;
-
- if (machine_is_riscpc()) {
- /*
- * RiscPC can't handle half-word loads and stores
- */
- elf_hwcap &= ~HWCAP_HALF;
-
- switch (params->u1.s.pages_in_vram) {
- case 512:
- vram_size += PAGE_SIZE * 256;
- case 256:
- vram_size += PAGE_SIZE * 256;
- default:
- break;
- }
-
- if (vram_size) {
- desc->video_start = 0x02000000;
- desc->video_end = 0x02000000 + vram_size;
- }
-
- for (i = 0; i < 4; i++) {
- meminfo.bank[i].start = PHYS_OFFSET + (i << 26);
- meminfo.bank[i].size =
- params->u1.s.pages_in_bank[i] *
- params->u1.s.page_size;
- }
- meminfo.nr_banks = 4;
- }
- memc_ctrl_reg = params->u1.s.memc_control_reg;
- number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
-#endif
-}
-
-static void __init
-fixup_ebsa285(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
- ORIG_X = params->u1.s.video_x;
- ORIG_Y = params->u1.s.video_y;
- ORIG_VIDEO_COLS = params->u1.s.video_num_cols;
- ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
-}
-
-/*
- * Older NeTTroms either do not provide a parameters
- * page, or they don't supply correct information in
- * the parameter page.
- */
-static void __init
-fixup_netwinder(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
- if (params->u1.s.nr_pages != 0x2000 &&
- params->u1.s.nr_pages != 0x4000) {
- printk(KERN_WARNING "Warning: bad NeTTrom parameters "
- "detected, using defaults\n");
-
- params->u1.s.nr_pages = 0x2000; /* 32MB */
- params->u1.s.ramdisk_size = 0;
- params->u1.s.flags = FLAG_READONLY;
- params->u1.s.initrd_start = 0;
- params->u1.s.initrd_size = 0;
- params->u1.s.rd_start = 0;
- }
-}
-
-/*
- * CATS uses soft-reboot by default, since
- * hard reboots fail on early boards.
- */
-static void __init
-fixup_cats(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
- ORIG_VIDEO_LINES = 25;
- ORIG_VIDEO_POINTS = 16;
- ORIG_Y = 24;
-}
-
-static void __init
-fixup_coebsa285(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
-#if 0
- extern unsigned long boot_memory_end;
- extern char boot_command_line[];
-
- meminfo.nr_banks = 1;
- meminfo.bank[0].start = PHYS_OFFSET;
- meminfo.bank[0].size = boot_memory_end;
-
- *cmdline = boot_command_line;
-#endif
-}
-
-static void __init
-fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
- char **cmdline)
-{
-#ifdef CONFIG_ARCH_SA1100
- int i;
- extern struct mem_desc {
- unsigned long phys_start;
- unsigned long length;
- } mem_desc[];
- extern unsigned int mem_desc_size;
-
- for( i = 0; i < mem_desc_size; i++ ) {
- if( i >= NR_BANKS ) {
- printk( __FUNCTION__
- ": mem_desc too large for meminfo structure\n");
- break;
- }
- meminfo.bank[i].start = mem_desc[i].phys_start;
- meminfo.bank[i].size = mem_desc[i].length;
- }
- meminfo.nr_banks = i;
-
-#if defined(CONFIG_SA1100_BRUTUS)
- ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
- setup_ramdisk( 1, 0, 0, 8192 );
- setup_initrd( __phys_to_virt(0xd8000000), 0x00400000 );
-#elif defined(CONFIG_SA1100_EMPEG)
- ROOT_DEV = MKDEV( 3, 1 ); /* /dev/hda1 */
- setup_ramdisk( 1, 0, 0, 4096 );
- setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) );
-#elif defined(CONFIG_SA1100_TIFON)
- ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
- setup_ramdisk(1, 0, 0, 4096);
- setup_initrd( 0xd0000000 + 0x1100004, 0x140000 );
-#elif defined(CONFIG_SA1100_VICTOR)
- ROOT_DEV = MKDEV( 60, 2 );
-
- /* Get command line parameters passed from the loader (if any) */
- if( *((char*)0xc0000000) )
- strcpy( default_command_line, ((char *)0xc0000000) );
-
- /* power off if any problem */
- strcat( default_command_line, " panic=1" );
-#elif defined(CONFIG_SA1100_LART)
- ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
- setup_ramdisk(1, 0, 0, 8192);
- setup_initrd(0xc0400000, 0x00400000);
-#endif
-#endif
-}
-
-#define NO_PARAMS 0
-#define NO_VIDEO 0, 0
-
-/*
- * This is the list of all architectures supported by
- * this kernel. This should be integrated with the list
- * in head-armv.S.
- */
-static struct machine_desc machine_desc[] __initdata = {
- { "EBSA110", /* RMK */
- 0x00000400,
- NO_VIDEO,
- 1, 0, 1, 1, 1,
- NULL
- }, { "Acorn-RiscPC", /* RMK */
- 0x10000100,
- NO_VIDEO,
- 1, 1, 0, 0, 0,
- fixup_acorn
- }, { "unknown",
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "FTV/PCI", /* Philip Blundell */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "EBSA285", /* RMK */
- 0x00000100,
- 0x000a0000, 0x000bffff,
- 0, 0, 0, 0, 0,
- fixup_ebsa285
- }, { "Rebel-NetWinder", /* RMK */
- 0x00000100,
- 0x000a0000, 0x000bffff,
- 1, 0, 1, 0, 0,
- fixup_netwinder
- }, { "Chalice-CATS", /* Philip Blundell */
- NO_PARAMS,
- 0x000a0000, 0x000bffff,
- 0, 0, 0, 0, 1,
- fixup_cats
- }, { "unknown-TBOX", /* Philip Blundell */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "co-EBSA285", /* Mark van Doesburg */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- fixup_coebsa285
- }, { "CL-PS7110", /* Werner Almesberger */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "Acorn-Archimedes",/* RMK/DAG */
- 0x0207c000,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- fixup_acorn
- }, { "Acorn-A5000", /* RMK/PB */
- 0x0207c000,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- fixup_acorn
- }, { "Etoile", /* Alex de Vries */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "LaCie_NAS", /* Benjamin Herrenschmidt */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "CL-PS7500", /* Philip Blundell */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, { "Shark", /* Alexander Schulz */
- NO_PARAMS,
- /* do you really mean 0x200000? */
- 0x06000000, 0x06000000+0x00200000,
- 0, 0, 0, 0, 0,
- NULL
- }, { "SA1100-based", /* Nicolas Pitre */
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- fixup_sa1100
- }
-};
-
void __init setup_arch(char **cmdline_p)
{
struct param_struct *params = NULL;
struct machine_desc *mdesc;
+ struct meminfo meminfo;
char *from = default_command_line;
+ memset(&meminfo, 0, sizeof(meminfo));
+
#if defined(CONFIG_ARCH_ARC)
__machine_arch_type = MACH_TYPE_ARCHIMEDES;
#elif defined(CONFIG_ARCH_A5K)
@@ -706,7 +460,7 @@ void __init setup_arch(char **cmdline_p)
ROOT_DEV = MKDEV(0, 255);
- mdesc = machine_desc + machine_arch_type;
+ mdesc = setup_architecture(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->broken_hlt)
@@ -719,7 +473,7 @@ void __init setup_arch(char **cmdline_p)
params = phys_to_virt(mdesc->param_offset);
if (mdesc->fixup)
- mdesc->fixup(mdesc, params, &from);
+ mdesc->fixup(mdesc, params, &from, &meminfo);
if (params && params->u1.s.page_size != PAGE_SIZE) {
printk(KERN_WARNING "Warning: bad configuration page, "
@@ -763,11 +517,11 @@ void __init setup_arch(char **cmdline_p)
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
- parse_cmdline(cmdline_p, from);
- setup_bootmem();
- request_standard_resources(mdesc);
+ parse_cmdline(&meminfo, cmdline_p, from);
+ setup_bootmem(&meminfo);
+ request_standard_resources(&meminfo, mdesc);
- paging_init();
+ paging_init(&meminfo);
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 5046a3377..510a38451 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -14,37 +14,25 @@ L_OBJS := changebit.o csumipv6.o csumpartial.o csumpartialcopy.o \
O_TARGET := lib.o
O_OBJS := backtrace.o delay.o
+L_OBJS_arc := io-acorn.o
+L_OBJS_a5k := io-acorn.o floppydma.o
+L_OBJS_rpc := io-acorn.o floppydma.o
+L_OBJS_clps7500 := io-acorn.o
+L_OBJS_ebsa110 := io-ebsa110.o
+L_OBJS_footbridge := io-footbridge.o
+L_OBJS_nexuspci := io-footbridge.o
+L_OBJS_sa1100 := io-footbridge.o
+L_OBJS_shark := io-shark.o
+
ifeq ($(PROCESSOR),armo)
L_OBJS += uaccess-armo.o
endif
-ifdef CONFIG_ARCH_ACORN
- L_OBJS += io-acorn.o
- ifdef CONFIG_ARCH_A5K
- L_OBJS += floppydma.o
- endif
- ifdef CONFIG_ARCH_RPC
- L_OBJS += floppydma.o
- endif
-endif
-
-ifeq ($(MACHINE),ebsa110)
- L_OBJS += io-ebsa110.o
-else
+ifneq ($(MACHINE),ebsa110)
OX_OBJS += io.o
endif
-ifeq ($(MACHINE),footbridge)
- L_OBJS += io-footbridge.o
-endif
-
-#
-# SA1100 IO routines happen to be the
-# same as the footbridge routines
-#
-ifeq ($(MACHINE),sa1100)
- L_OBJS += io-footbridge.o
-endif
+L_OBJS += $(L_OBJS_$(MACHINE))
include $(TOPDIR)/Rules.make
diff --git a/arch/arm/lib/io-shark.c b/arch/arm/lib/io-shark.c
new file mode 100644
index 000000000..41595bc6f
--- /dev/null
+++ b/arch/arm/lib/io-shark.c
@@ -0,0 +1,79 @@
+/*
+ * linux/arch/arm/lib/io-shark.c
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/arch/arm/lib/io-ebsa.S
+ * Copyright (C) 1995, 1996 Russell King
+ */
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+
+void print_warning(void)
+{
+ printk(KERN_WARNING "ins?/outs? not implemented on this architecture\n");
+}
+
+void insl(unsigned int port, void *to, int len)
+{
+ print_warning();
+}
+
+void insb(unsigned int port, void *to, int len)
+{
+ print_warning();
+}
+
+void outsl(unsigned int port, const void *from, int len)
+{
+ print_warning();
+}
+
+void outsb(unsigned int port, const void *from, int len)
+{
+ print_warning();
+}
+
+/* these should be in assembler again */
+
+/*
+ * Purpose: read a block of data from a hardware register to memory.
+ * Proto : insw(int from_port, void *to, int len_in_words);
+ * Proto : inswb(int from_port, void *to, int len_in_bytes);
+ * Notes : increment to
+ */
+
+void insw(unsigned int port, void *to, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ ((unsigned short *) to)[i] = inw(port);
+}
+
+void inswb(unsigned int port, void *to, int len)
+{
+ insw(port, to, len >> 2);
+}
+
+/*
+ * Purpose: write a block of data from memory to a hardware register.
+ * Proto : outsw(int to_reg, void *from, int len_in_words);
+ * Proto : outswb(int to_reg, void *from, int len_in_bytes);
+ * Notes : increments from
+ */
+
+void outsw(unsigned int port, const void *from, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ outw(((unsigned short *) from)[i], port);
+}
+
+void outswb(unsigned int port, const void *from, int len)
+{
+ outsw(port, from, len >> 2);
+}
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index c58e66647..e7b8c8bb9 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -33,6 +33,7 @@
#include "map.h"
static unsigned long totalram_pages;
+struct meminfo meminfo;
pgd_t swapper_pg_dir[PTRS_PER_PGD];
/*
@@ -160,12 +161,14 @@ void show_mem(void)
/*
* paging_init() sets up the page tables...
*/
-void __init paging_init(void)
+void __init paging_init(struct meminfo *mi)
{
void *zero_page, *bad_page, *bad_table;
unsigned long zone_size[MAX_NR_ZONES];
int i;
+ memcpy(&meminfo, mi, sizeof(meminfo));
+
#ifdef CONFIG_CPU_32
#define TABLE_OFFSET (PTRS_PER_PTE)
#else
@@ -197,12 +200,12 @@ void __init paging_init(void)
* any problems with DMA or highmem, so all memory is
* allocated to the DMA zone.
*/
- for (i = 0; i < meminfo.nr_banks; i++) {
- if (meminfo.bank[i].size) {
+ for (i = 0; i < mi->nr_banks; i++) {
+ if (mi->bank[i].size) {
unsigned int end;
- end = (meminfo.bank[i].start - PHYS_OFFSET +
- meminfo.bank[i].size) >> PAGE_SHIFT;
+ end = (mi->bank[i].start - PHYS_OFFSET +
+ mi->bank[i].size) >> PAGE_SHIFT;
if (zone_size[0] < end)
zone_size[0] = end;
}
@@ -328,24 +331,6 @@ void free_initmem(void)
(unsigned long)(&__init_end),
"init");
-#ifdef CONFIG_FOOTBRIDGE
- {
- extern int __netwinder_begin, __netwinder_end,
- __ebsa285_begin, __ebsa285_end;
-
- if (!machine_is_netwinder())
- free_area((unsigned long)(&__netwinder_begin),
- (unsigned long)(&__netwinder_end),
- "netwinder");
-
- if (!machine_is_ebsa285() && !machine_is_cats() &&
- !machine_is_co285())
- free_area((unsigned long)(&__ebsa285_begin),
- (unsigned long)(&__ebsa285_end),
- "ebsa285/cats");
- }
-#endif
-
printk("\n");
}
diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S
index c6c776acf..9814d2a9c 100644
--- a/arch/arm/mm/proc-arm6,7.S
+++ b/arch/arm/mm/proc-arm6,7.S
@@ -107,19 +107,45 @@ msg: .ascii "DA*%p=%p\n\0"
ENTRY(cpu_arm6_data_abort)
ldr r4, [r0] @ read instruction causing problem
mov r2, r4, lsr #19 @ r2 b1 = L
-Ldata_simple:
+ and r1, r4, #14 << 24
and r2, r2, #2 @ check read/write bit
- mrc p15, 0, r0, c6, c0, 0 @ get FAR
+ teq r1, #4 << 23
+ bne Ldata_simple
+
+
+Ldata_ldmstm: tst r4, #1 << 21 @ check writeback bit
+ beq Ldata_simple
+ mov r7, #0x11
+ orr r7, r7, r7, lsl #8
+ and r0, r4, r7
+ and r1, r4, r7, lsl #1
+ add r0, r0, r1, lsr #1
+ and r1, r4, r7, lsl #2
+ add r0, r0, r1, lsr #2
+ and r1, r4, r7, lsl #3
+ add r0, r0, r1, lsr #3
+ add r0, r0, r0, lsr #8
+ add r0, r0, r0, lsr #4
+ and r7, r0, #15 @ r7 = no. of registers to transfer.
+ and r5, r4, #15 << 16 @ Get Rn
+ ldr r0, [sp, r5, lsr #14] @ Get register
+ tst r4, #1 << 23 @ U bit
+ subne r7, r0, r7, lsl #2
+ addeq r7, r0, r7, lsl #2 @ Do correction (signed)
+Ldata_saver7: str r7, [sp, r5, lsr #14] @ Put register
+Ldata_simple: mrc p15, 0, r0, c6, c0, 0 @ get FAR
mrc p15, 0, r1, c5, c0, 0 @ get FSR
- and r1, r1, #15
+ and r1, r1, #255
mov pc, lr
ENTRY(cpu_arm7_data_abort)
ldr r4, [r0] @ read instruction causing problem
mov r2, r4, lsr #19 @ r2 b1 = L
and r1, r4, #15 << 24
+ and r2, r2, #2 @ check read/write bit
add pc, pc, r1, lsr #22 @ Now branch to the relevent processing routine
movs pc, lr
+
b Ldata_unknown
b Ldata_unknown
b Ldata_unknown
@@ -141,106 +167,24 @@ Ldata_unknown: @ Part of jumptable
mov r2, r3
b baddataabort
-Ldata_ldmstm: tst r4, #1 << 21 @ check writeback bit
- beq Ldata_simple
-
- mov r7, #0x11
- orr r7, r7, r7, lsl #8
- and r0, r4, r7
- and r1, r4, r7, lsl #1
- add r0, r0, r1, lsr #1
- and r1, r4, r7, lsl #2
- add r0, r0, r1, lsr #2
- and r1, r4, r7, lsl #3
- add r0, r0, r1, lsr #3
- add r0, r0, r0, lsr #8
- add r0, r0, r0, lsr #4
- and r7, r0, #15 @ r7 = no. of registers to transfer.
- and r5, r4, #15 << 16 @ Get Rn
- ldr r0, [sp, r5, lsr #14] @ Get register
- eor r6, r4, r4, lsl #2
- tst r6, #1 << 23 @ Check inc/dec ^ writeback
- rsbeq r7, r7, #0
- add r7, r0, r7, lsl #2 @ Do correction (signed)
- str r7, [sp, r5, lsr #14] @ Put register
-
-Ldata_lateldrpostconst:
- movs r1, r4, lsl #20 @ Get offset
- beq Ldata_simple @ if offset is zero, no effect
- and r5, r4, #15 << 16 @ Get Rn
- ldr r0, [sp, r5, lsr #14]
- tst r4, #1 << 23 @ U bit
- subne r0, r0, r1, lsr #20
- addeq r0, r0, r1, lsr #20
- str r0, [sp, r5, lsr #14] @ Put register
- b Ldata_simple
Ldata_lateldrpreconst:
tst r4, #1 << 21 @ check writeback bit
- movnes r1, r4, lsl #20 @ Get offset
+ beq Ldata_simple
+Ldata_lateldrpostconst:
+ movs r1, r4, lsl #20 @ Get offset
beq Ldata_simple
and r5, r4, #15 << 16 @ Get Rn
ldr r0, [sp, r5, lsr #14]
tst r4, #1 << 23 @ U bit
- subne r0, r0, r1, lsr #20
- addeq r0, r0, r1, lsr #20
- str r0, [sp, r5, lsr #14] @ Put register
- b Ldata_simple
-
-Ldata_lateldrpostreg:
- and r5, r4, #15
- ldr r1, [sp, r5, lsl #2] @ Get Rm
- mov r3, r4, lsr #7
- ands r3, r3, #31
- and r6, r4, #0x70
- orreq r6, r6, #8
- add pc, pc, r6
- mov r0, r0
-
- mov r1, r1, lsl r3 @ 0: LSL #!0
- b 1f
- b 1f @ 1: LSL #0
- mov r0, r0
- b 1f @ 2: MUL?
- mov r0, r0
- b 1f @ 3: MUL?
- mov r0, r0
- mov r1, r1, lsr r3 @ 4: LSR #!0
- b 1f
- mov r1, r1, lsr #32 @ 5: LSR #32
- b 1f
- b 1f @ 6: MUL?
- mov r0, r0
- b 1f @ 7: MUL?
- mov r0, r0
- mov r1, r1, asr r3 @ 8: ASR #!0
- b 1f
- mov r1, r1, asr #32 @ 9: ASR #32
- b 1f
- b 1f @ A: MUL?
- mov r0, r0
- b 1f @ B: MUL?
- mov r0, r0
- mov r1, r1, ror r3 @ C: ROR #!0
- b 1f
- mov r1, r1, rrx @ D: RRX
- b 1f
- mov r0, r0 @ E: MUL?
- mov r0, r0
- mov r0, r0 @ F: MUL?
-
-
-1: and r5, r4, #15 << 16 @ Get Rn
- ldr r0, [sp, r5, lsr #14]
- tst r4, #1 << 23 @ U bit
- subne r0, r0, r1
- addeq r0, r0, r1
- str r0, [sp, r5, lsr #14] @ Put register
- b Ldata_simple
+ subne r7, r0, r1, lsr #20
+ addeq r7, r0, r1, lsr #20
+ b Ldata_saver7
Ldata_lateldrprereg:
tst r4, #1 << 21 @ check writeback bit
beq Ldata_simple
+Ldata_lateldrpostreg:
and r5, r4, #15
ldr r1, [sp, r5, lsl #2] @ Get Rm
mov r3, r4, lsr #7
@@ -286,10 +230,9 @@ Ldata_lateldrprereg:
1: and r5, r4, #15 << 16 @ Get Rn
ldr r0, [sp, r5, lsr #14]
tst r4, #1 << 23 @ U bit
- subne r0, r0, r1
- addeq r0, r0, r1
- str r0, [sp, r5, lsr #14] @ Put register
- b Ldata_simple
+ subne r7, r0, r1
+ addeq r7, r0, r1
+ b Ldata_saver7
/*
* Function: arm6_7_check_bugs (void)
diff --git a/arch/arm/vmlinux-armo.lds.in b/arch/arm/vmlinux-armo.lds.in
index 94c155232..9d5ee058d 100644
--- a/arch/arm/vmlinux-armo.lds.in
+++ b/arch/arm/vmlinux-armo.lds.in
@@ -6,76 +6,89 @@ OUTPUT_ARCH(arm)
ENTRY(stext)
SECTIONS
{
- . = TEXTADDR;
+ . = TEXTADDR;
- __init_begin = .; /* Init code and data */
- .text.init : { *(.text.init) }
- __proc_info_begin = .;
- .proc.info : { *(.proc.info) }
- __proc_info_end = .;
- .data.init : { *(.data.init) }
- . = ALIGN(16);
- __setup_start = .;
- .setup.init : { *(.setup.init) }
- __setup_end = .;
- __initcall_start = .;
- .initcall.init : { *(.initcall.init) }
- __initcall_end = .;
- . = ALIGN(32768);
- __init_end = .;
-
- .init.task : {
- *(.init.task)
- }
+ .init : {
+ __init_begin = .; /* Init code and data */
+ *(.text.init)
+ __proc_info_begin = .;
+ *(.proc.info)
+ __proc_info_end = .;
+ __arch_info_begin = .;
+ *(.arch.info)
+ __arch_info_end = .;
+ *(.data.init)
+ . = ALIGN(16);
+ __setup_start = .;
+ *(.setup.init)
+ __setup_end = .;
+ __initcall_start = .;
+ *(.initcall.init)
+ __initcall_end = .;
+ . = ALIGN(32768);
+ __init_end = .;
+ }
- /DISCARD/ : { /* Exit code and data */
- *(.text.exit)
- *(.data.exit)
- }
+ .init.task : {
+ *(.init.task)
+ }
- _text = .; /* Text and read-only data */
- .text : {
- *(.text)
- *(.fixup)
- *(.gnu.warning)
+ /DISCARD/ : { /* Exit code and data */
+ *(.text.exit)
+ *(.data.exit)
}
- .text.lock : { *(.text.lock) } /* out-of-line lock text */
- .rodata : { *(.rodata) }
- .kstrtab : { *(.kstrtab) }
+ .text : {
+ _text = .; /* Text and read-only data */
+ *(.text)
+ *(.fixup)
+ *(.gnu.warning)
+ *(.text.lock) /* out-of-line lock text */
+ *(.rodata)
+ *(.kstrtab)
+ . = ALIGN(16); /* Exception table */
+ __start___ex_table = .;
+ *(__ex_table)
+ __stop___ex_table = .;
- . = ALIGN(16); /* Exception table */
- __start___ex_table = .;
- __ex_table : { *(__ex_table) }
- __stop___ex_table = .;
+ __start___ksymtab = .; /* Kernel symbol table */
+ *(__ksymtab)
+ __stop___ksymtab = .;
- __start___ksymtab = .; /* Kernel symbol table */
- __ksymtab : { *(__ksymtab) }
- __stop___ksymtab = .;
+ *(.got) /* Global offset table */
+
+ _etext = .; /* End of text section */
+ }
- .got : { *(.got) } /* Global offset table */
+ .data : {
+ /*
+ * The cacheline aligned data
+ */
+ . = ALIGN(32);
+ *(.data.cacheline_aligned)
- _etext = .; /* End of text section */
+ /*
+ * and the usual data section
+ */
+ *(.data)
+ CONSTRUCTORS
- .data : { /* Data */
- *(.data)
- CONSTRUCTORS
+ _edata = .;
}
- _edata = .; /* End of data section */
- __bss_start = .; /* BSS */
- .bss : {
- *(.bss)
+ .bss : {
+ __bss_start = .; /* BSS */
+ *(.bss)
+ _end = . ;
}
- _end = . ;
- /* Stabs debugging sections. */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
}
diff --git a/arch/arm/vmlinux-armv.lds.in b/arch/arm/vmlinux-armv.lds.in
index d2a79ca9d..1476ba939 100644
--- a/arch/arm/vmlinux-armv.lds.in
+++ b/arch/arm/vmlinux-armv.lds.in
@@ -13,6 +13,9 @@ SECTIONS
__proc_info_begin = .;
*(.proc.info)
__proc_info_end = .;
+ __arch_info_begin = .;
+ *(.arch.info)
+ __arch_info_end = .;
*(.data.init)
. = ALIGN(16);
__setup_start = .;
@@ -25,6 +28,11 @@ SECTIONS
__init_end = .;
}
+ /DISCARD/ : { /* Exit code and data */
+ *(.text.exit)
+ *(.data.exit)
+ }
+
.text : { /* Real text segment */
_text = .; /* Text and read-only data */
*(.text)
diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c
index bf16abf24..2149dcf51 100644
--- a/arch/i386/boot/tools/build.c
+++ b/arch/i386/boot/tools/build.c
@@ -150,9 +150,13 @@ int main(int argc, char ** argv)
sz = sb.st_size;
fprintf (stderr, "System is %d kB\n", sz/1024);
sys_size = (sz + 15) / 16;
- if (sys_size > (is_big_kernel ? 0xffff : DEF_SYSSIZE))
+ /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */
+ if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE))
die("System is too big. Try using %smodules.",
is_big_kernel ? "" : "bzImage or ");
+ if (sys_size > 0xffff)
+ fprintf(stderr,"warning: kernel is too big for standalone boot "
+ "from floppy\n");
while (sz > 0) {
int l, n;
diff --git a/arch/i386/config.in b/arch/i386/config.in
index e1ba021e1..2a083ad09 100644
--- a/arch/i386/config.in
+++ b/arch/i386/config.in
@@ -175,6 +175,19 @@ fi
source drivers/telephony/Config.in
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index 813a2ecea..0f3f5cb8a 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -93,36 +93,10 @@ CONFIG_ISAPNP=y
# Block devices
#
CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_BLK_DEV_IDECS is not set
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-
-#
-# IDE chipset support/bugfixes
-#
-CONFIG_BLK_DEV_CMD640=y
-# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
-# CONFIG_BLK_DEV_ISAPNP is not set
-CONFIG_BLK_DEV_RZ1000=y
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_IDEPCI_SHARE_IRQ=y
-# CONFIG_BLK_DEV_IDEDMA_PCI is not set
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
#
# Additional Block Devices
@@ -131,11 +105,6 @@ CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -175,6 +144,73 @@ CONFIG_SKB_LARGE=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_PCI_EXPERIMENTAL is not set
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_PIIX_TUNING is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
# SCSI support
#
CONFIG_SCSI=y
@@ -339,7 +375,6 @@ CONFIG_PCMCIA_PCNET=y
# CONFIG_PCMCIA_XIRC2PS is not set
# CONFIG_ARCNET_COM20020_CS is not set
# CONFIG_PCMCIA_3C575 is not set
-# CONFIG_PCMCIA_TULIP is not set
CONFIG_NET_PCMCIA_RADIO=y
CONFIG_PCMCIA_RAYCS=y
# CONFIG_PCMCIA_NETWAVE is not set
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 6671bb35b..525bb2c07 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -29,7 +29,7 @@ endif
endif
ifdef CONFIG_MCA
-O_OBJS += mca.o
+OX_OBJS += mca.o
endif
ifeq ($(CONFIG_MTRR),y)
diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c
index d95bbe6b1..8efa2832a 100644
--- a/arch/i386/kernel/acpi.c
+++ b/arch/i386/kernel/acpi.c
@@ -1,7 +1,7 @@
/*
* acpi.c - Linux ACPI driver
*
- * Copyright (C) 1999 Andrew Henroid
+ * Copyright (C) 1999-2000 Andrew Henroid
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -55,6 +55,11 @@ static int acpi_do_ulong(ctl_table *ctl,
struct file *file,
void *buffer,
size_t *len);
+static int acpi_do_table(ctl_table *ctl,
+ int write,
+ struct file *file,
+ void *buffer,
+ size_t *len);
static int acpi_do_event_reg(ctl_table *ctl,
int write,
struct file *file,
@@ -73,12 +78,6 @@ static int acpi_do_sleep(ctl_table *ctl,
static struct ctl_table_header *acpi_sysctl = NULL;
-static struct acpi_facp *acpi_facp = NULL;
-static int acpi_fake_facp = 0;
-static struct acpi_facs *acpi_facs = NULL;
-static unsigned long acpi_facp_addr = 0;
-static unsigned long acpi_dsdt_addr = 0;
-
// current system sleep state (S0 - S4)
static acpi_sstate_t acpi_sleep_state = ACPI_S0;
// time sleep began
@@ -123,15 +122,28 @@ static unsigned long acpi_slp_typ[] =
ACPI_SLP_TYP_DISABLED /* S5 */
};
+struct acpi_table_info
+{
+ u32 expected_signature;
+ u32 expected_size;
+
+ struct acpi_table *table;
+ size_t size;
+ int mapped;
+};
+
+static struct acpi_table_info acpi_facp
+ = {ACPI_FACP_SIG, sizeof(struct acpi_facp), NULL, 0, 0};
+static struct acpi_table_info acpi_dsdt = {ACPI_DSDT_SIG, 0, NULL, 0, 0};
+static struct acpi_table_info acpi_facs
+ = {ACPI_FACS_SIG, sizeof(struct acpi_facs), NULL, 0, 0};
+static rwlock_t acpi_do_table_lock = RW_LOCK_UNLOCKED;
+
static struct ctl_table acpi_table[] =
{
- {ACPI_FACP, "facp",
- &acpi_facp_addr, sizeof(acpi_facp_addr),
- 0400, NULL, &acpi_do_ulong},
+ {ACPI_FACP, "facp", &acpi_facp, 0, 0644, NULL, &acpi_do_table},
- {ACPI_DSDT, "dsdt",
- &acpi_dsdt_addr, sizeof(acpi_dsdt_addr),
- 0400, NULL, &acpi_do_ulong},
+ {ACPI_DSDT, "dsdt", &acpi_dsdt, 0, 0644, NULL, &acpi_do_table},
{ACPI_PM1_ENABLE, "pm1_enable",
NULL, 0,
@@ -409,6 +421,33 @@ static void acpi_unmap_table(struct acpi_table *table)
}
/*
+ * Initialize an ACPI table
+ */
+static void acpi_init_table(struct acpi_table_info *info,
+ void *data,
+ int mapped)
+{
+ struct acpi_table *table = (struct acpi_table*) data;
+ info->table = table;
+ info->size = (size_t)(table ? table->length:0);
+ info->mapped = mapped;
+}
+
+/*
+ * Destroy an ACPI table
+ */
+static void acpi_destroy_table(struct acpi_table_info *info)
+{
+ if (info->table) {
+ if (info->mapped)
+ acpi_unmap_table(info->table);
+ else
+ kfree(info->table);
+ info->table = NULL;
+ }
+}
+
+/*
* Locate and map ACPI tables
*/
static int __init acpi_find_tables(void)
@@ -458,37 +497,40 @@ static int __init acpi_find_tables(void)
return -ENODEV;
}
// search RSDT for FACP
- acpi_facp = NULL;
+ acpi_facp.table = NULL;
rsdt_entry = (u32 *) (rsdt + 1);
rsdt_entry_count = (int) ((rsdt->length - sizeof(*rsdt)) >> 2);
while (rsdt_entry_count) {
struct acpi_table *dt = acpi_map_table(*rsdt_entry);
if (dt && dt->signature == ACPI_FACP_SIG) {
- acpi_facp = (struct acpi_facp*) dt;
- acpi_facp_addr = *rsdt_entry;
- acpi_dsdt_addr = acpi_facp->dsdt;
+ struct acpi_facp *facp = (struct acpi_facp*) dt;
+ acpi_init_table(&acpi_facp, dt, 1);
+
+ // map DSDT if it exists
+ dt = acpi_map_table(facp->dsdt);
+ if (dt && dt->signature == ACPI_DSDT_SIG)
+ acpi_init_table(&acpi_dsdt, dt, 1);
+ else
+ acpi_unmap_table(dt);
// map FACS if it exists
- if (acpi_facp->facs) {
- dt = acpi_map_table(acpi_facp->facs);
- if (dt && dt->signature == ACPI_FACS_SIG) {
- acpi_facs = (struct acpi_facs*) dt;
- }
- else {
- acpi_unmap_table(dt);
- }
- }
+ dt = acpi_map_table(facp->facs);
+ if (dt && dt->signature == ACPI_FACS_SIG)
+ acpi_init_table(&acpi_facs, dt, 1);
+ else
+ acpi_unmap_table(dt);
}
else {
acpi_unmap_table(dt);
}
+
rsdt_entry++;
rsdt_entry_count--;
}
acpi_unmap_table(rsdt);
- if (!acpi_facp) {
+ if (!acpi_facp.table) {
printk(KERN_ERR "ACPI: missing FACP\n");
return -ENODEV;
}
@@ -500,11 +542,9 @@ static int __init acpi_find_tables(void)
*/
static void acpi_destroy_tables(void)
{
- if (!acpi_fake_facp)
- acpi_unmap_table((struct acpi_table*) acpi_facp);
- else
- kfree(acpi_facp);
- acpi_unmap_table((struct acpi_table*) acpi_facs);
+ acpi_destroy_table(&acpi_facs);
+ acpi_destroy_table(&acpi_dsdt);
+ acpi_destroy_table(&acpi_facp);
}
/*
@@ -512,6 +552,7 @@ static void acpi_destroy_tables(void)
*/
static int __init acpi_init_piix4(struct pci_dev *dev)
{
+ struct acpi_facp *facp;
u32 base;
u16 cmd;
u8 pmregmisc;
@@ -534,33 +575,34 @@ static int __init acpi_init_piix4(struct pci_dev *dev)
printk(KERN_INFO "ACPI: found PIIX4 at 0x%04x\n", base);
- acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
- if (!acpi_facp)
+ facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
+ if (!facp)
return -ENOMEM;
- acpi_fake_facp = 1;
- memset(acpi_facp, 0, sizeof(struct acpi_facp));
- acpi_facp->int_model = ACPI_PIIX4_INT_MODEL;
- acpi_facp->sci_int = ACPI_PIIX4_SCI_INT;
- acpi_facp->smi_cmd = ACPI_PIIX4_SMI_CMD;
- acpi_facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE;
- acpi_facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE;
- acpi_facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ;
- acpi_facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT;
- acpi_facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT;
- acpi_facp->pm2_cnt = ACPI_PIIX4_PM2_CNT;
- acpi_facp->pm_tmr = base + ACPI_PIIX4_PM_TMR;
- acpi_facp->gpe0 = base + ACPI_PIIX4_GPE0;
- acpi_facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN;
- acpi_facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN;
- acpi_facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN;
- acpi_facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN;
- acpi_facp->gpe0_len = ACPI_PIIX4_GPE0_LEN;
- acpi_facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
- acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
-
- acpi_facp_addr = virt_to_phys(acpi_facp);
- acpi_dsdt_addr = 0;
+ memset(facp, 0, sizeof(struct acpi_facp));
+ facp->hdr.signature = ACPI_FACP_SIG;
+ facp->hdr.length = sizeof(struct acpi_facp);
+ facp->int_model = ACPI_PIIX4_INT_MODEL;
+ facp->sci_int = ACPI_PIIX4_SCI_INT;
+ facp->smi_cmd = ACPI_PIIX4_SMI_CMD;
+ facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE;
+ facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE;
+ facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ;
+ facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT;
+ facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT;
+ facp->pm2_cnt = ACPI_PIIX4_PM2_CNT;
+ facp->pm_tmr = base + ACPI_PIIX4_PM_TMR;
+ facp->gpe0 = base + ACPI_PIIX4_GPE0;
+ facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN;
+ facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN;
+ facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN;
+ facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN;
+ facp->gpe0_len = ACPI_PIIX4_GPE0_LEN;
+ facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
+ facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
+
+ acpi_init_table(&acpi_facp, facp, 0);
+ acpi_init_table(&acpi_dsdt, NULL, 0);
acpi_p_blk = base + ACPI_PIIX4_P_BLK;
@@ -572,6 +614,7 @@ static int __init acpi_init_piix4(struct pci_dev *dev)
*/
static int __init acpi_init_via(struct pci_dev *dev)
{
+ struct acpi_facp *facp;
u32 base;
u8 tmp, irq;
@@ -594,39 +637,39 @@ static int __init acpi_init_via(struct pci_dev *dev)
printk(KERN_INFO "ACPI: found %s at 0x%04x\n", dev->name, base);
- acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
- if (!acpi_facp)
+ facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
+ if (!facp)
return -ENOMEM;
- acpi_fake_facp = 1;
- memset(acpi_facp, 0, sizeof(struct acpi_facp));
-
- acpi_facp->int_model = ACPI_VIA_INT_MODEL;
- acpi_facp->sci_int = irq;
- acpi_facp->smi_cmd = base + ACPI_VIA_SMI_CMD;
- acpi_facp->acpi_enable = ACPI_VIA_ACPI_ENABLE;
- acpi_facp->acpi_disable = ACPI_VIA_ACPI_DISABLE;
- acpi_facp->pm1a_evt = base + ACPI_VIA_PM1_EVT;
- acpi_facp->pm1a_cnt = base + ACPI_VIA_PM1_CNT;
- acpi_facp->pm_tmr = base + ACPI_VIA_PM_TMR;
- acpi_facp->gpe0 = base + ACPI_VIA_GPE0;
-
- acpi_facp->pm1_evt_len = ACPI_VIA_PM1_EVT_LEN;
- acpi_facp->pm1_cnt_len = ACPI_VIA_PM1_CNT_LEN;
- acpi_facp->pm_tm_len = ACPI_VIA_PM_TM_LEN;
- acpi_facp->gpe0_len = ACPI_VIA_GPE0_LEN;
- acpi_facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
- acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
-
- acpi_facp->duty_offset = ACPI_VIA_DUTY_OFFSET;
- acpi_facp->duty_width = ACPI_VIA_DUTY_WIDTH;
-
- acpi_facp->day_alarm = ACPI_VIA_DAY_ALARM;
- acpi_facp->mon_alarm = ACPI_VIA_MON_ALARM;
- acpi_facp->century = ACPI_VIA_CENTURY;
-
- acpi_facp_addr = virt_to_phys(acpi_facp);
- acpi_dsdt_addr = 0;
+ memset(facp, 0, sizeof(struct acpi_facp));
+ facp->hdr.signature = ACPI_FACP_SIG;
+ facp->hdr.length = sizeof(struct acpi_facp);
+ facp->int_model = ACPI_VIA_INT_MODEL;
+ facp->sci_int = irq;
+ facp->smi_cmd = base + ACPI_VIA_SMI_CMD;
+ facp->acpi_enable = ACPI_VIA_ACPI_ENABLE;
+ facp->acpi_disable = ACPI_VIA_ACPI_DISABLE;
+ facp->pm1a_evt = base + ACPI_VIA_PM1_EVT;
+ facp->pm1a_cnt = base + ACPI_VIA_PM1_CNT;
+ facp->pm_tmr = base + ACPI_VIA_PM_TMR;
+ facp->gpe0 = base + ACPI_VIA_GPE0;
+
+ facp->pm1_evt_len = ACPI_VIA_PM1_EVT_LEN;
+ facp->pm1_cnt_len = ACPI_VIA_PM1_CNT_LEN;
+ facp->pm_tm_len = ACPI_VIA_PM_TM_LEN;
+ facp->gpe0_len = ACPI_VIA_GPE0_LEN;
+ facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT;
+ facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT;
+
+ facp->duty_offset = ACPI_VIA_DUTY_OFFSET;
+ facp->duty_width = ACPI_VIA_DUTY_WIDTH;
+
+ facp->day_alarm = ACPI_VIA_DAY_ALARM;
+ facp->mon_alarm = ACPI_VIA_MON_ALARM;
+ facp->century = ACPI_VIA_CENTURY;
+
+ acpi_init_table(&acpi_facp, facp, 0);
+ acpi_init_table(&acpi_dsdt, NULL, 0);
acpi_p_blk = base + ACPI_VIA_P_BLK;
@@ -693,29 +736,27 @@ static int __init acpi_find_chipset(void)
*/
static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
{
+ struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
u32 pm1_status, gpe_status, gpe_level, gpe_edge;
unsigned long flags;
// detect and clear fixed events
- pm1_status = (acpi_read_pm1_status(acpi_facp)
- & acpi_read_pm1_enable(acpi_facp));
- acpi_write_pm1_status(acpi_facp, pm1_status);
+ pm1_status = (acpi_read_pm1_status(facp) & acpi_read_pm1_enable(facp));
+ acpi_write_pm1_status(facp, pm1_status);
// detect and handle general-purpose events
- gpe_status = (acpi_read_gpe_status(acpi_facp)
- & acpi_read_gpe_enable(acpi_facp));
+ gpe_status = (acpi_read_gpe_status(facp) & acpi_read_gpe_enable(facp));
gpe_level = gpe_status & acpi_gpe_level;
if (gpe_level) {
// disable level-triggered events (re-enabled after handling)
- acpi_write_gpe_enable(
- acpi_facp,
- acpi_read_gpe_enable(acpi_facp) & ~gpe_level);
+ acpi_write_gpe_enable(facp,
+ acpi_read_gpe_enable(facp) & ~gpe_level);
}
gpe_edge = gpe_status & ~gpe_level;
if (gpe_edge) {
// clear edge-triggered events
- while (acpi_read_gpe_status(acpi_facp) & gpe_edge)
- acpi_write_gpe_status(acpi_facp, gpe_edge);
+ while (acpi_read_gpe_status(facp) & gpe_edge)
+ acpi_write_gpe_status(facp, gpe_edge);
}
// notify process waiting on /dev/acpi
@@ -804,7 +845,7 @@ static void wake_on_busmaster(struct acpi_facp *facp)
static void acpi_idle(void)
{
static int sleep_level = 1;
- struct acpi_facp *facp = acpi_facp;
+ struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
if (!facp || !facp->pm_tmr || !acpi_p_blk)
goto not_initialized;
@@ -973,6 +1014,7 @@ static void acpi_enter_sx(acpi_sstate_t state)
{
unsigned long slp_typ = acpi_slp_typ[(int) state];
if (slp_typ != ACPI_SLP_TYP_DISABLED) {
+ struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
u16 typa, typb, value;
// bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb
@@ -987,20 +1029,20 @@ static void acpi_enter_sx(acpi_sstate_t state)
acpi_sleep_state = state;
// clear wake status
- acpi_write_pm1_status(acpi_facp, ACPI_WAK);
+ acpi_write_pm1_status(facp, ACPI_WAK);
// set SLP_TYPa/b and SLP_EN
- if (acpi_facp->pm1a_cnt) {
- value = inw(acpi_facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK;
- outw(value | typa | ACPI_SLP_EN, acpi_facp->pm1a_cnt);
+ if (facp->pm1a_cnt) {
+ value = inw(facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK;
+ outw(value | typa | ACPI_SLP_EN, facp->pm1a_cnt);
}
- if (acpi_facp->pm1b_cnt) {
- value = inw(acpi_facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK;
- outw(value | typb | ACPI_SLP_EN, acpi_facp->pm1b_cnt);
+ if (facp->pm1b_cnt) {
+ value = inw(facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK;
+ outw(value | typb | ACPI_SLP_EN, facp->pm1b_cnt);
}
// wait until S1 is entered
- while (!(acpi_read_pm1_status(acpi_facp) & ACPI_WAK)) ;
+ while (!(acpi_read_pm1_status(facp) & ACPI_WAK)) ;
// finished sleeping, update system time
acpi_update_clock();
acpi_enter_dx(ACPI_D0);
@@ -1115,6 +1157,120 @@ static int acpi_do_ulong(ctl_table *ctl,
}
/*
+ * Determine if user buffer contains a valid table
+ */
+static int acpi_verify_table(void *buffer,
+ size_t size,
+ struct acpi_table_info *info)
+{
+ if (size < sizeof(struct acpi_table))
+ return -EINVAL;
+ else if (verify_area(VERIFY_READ, buffer, size))
+ return -EFAULT;
+ else {
+ struct acpi_table hdr;
+ size_t table_size;
+
+ copy_from_user(&hdr, buffer, sizeof(hdr));
+ table_size = (size_t) hdr.length;
+ if (hdr.signature != info->expected_signature
+ || table_size < size
+ || (info->expected_size
+ && table_size != info->expected_size))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Examine/replace an ACPI table
+ */
+static int acpi_do_table(ctl_table *ctl,
+ int write,
+ struct file *file,
+ void *buffer,
+ size_t *len)
+{
+ struct acpi_table_info *info = (struct acpi_table_info *) ctl->data;
+ u8 *data = NULL;
+ size_t size = 0;
+ int error = 0;
+
+ if (!info) {
+ *len = 0;
+ return 0;
+ }
+
+ if (!write) {
+ // table read
+ read_lock(&acpi_do_table_lock);
+ if (info->table && file->f_pos < info->size) {
+ data = (u8*) info->table + file->f_pos;
+ size = info->size - file->f_pos;
+ if (size > *len)
+ size = *len;
+ if (copy_to_user(buffer, data, size))
+ error = -EFAULT;
+ }
+ read_unlock(&acpi_do_table_lock);
+ }
+ else if (file->f_pos) {
+ // table body replacement
+ write_lock(&acpi_do_table_lock);
+ if (info->table && file->f_pos < info->size) {
+ data = (u8*) info->table + file->f_pos;
+ size = info->size - file->f_pos;
+ if (size > *len)
+ size = *len;
+ if (copy_from_user(data, buffer, size))
+ error = -EFAULT;
+ }
+ write_unlock(&acpi_do_table_lock);
+ }
+ else {
+ // table header/body replacement
+ struct acpi_table hdr;
+ size_t table_size;
+
+ // make sure we are being given a valid table
+ error = acpi_verify_table(buffer, *len, info);
+ if (error)
+ return error;
+ copy_from_user(&hdr, buffer, sizeof(hdr));
+ table_size = (size_t) hdr.length;
+
+ write_lock(&acpi_do_table_lock);
+
+ data = (u8*) info->table;
+ size = *len;
+
+ if (!data || info->mapped || table_size != info->size) {
+ // allocate a (different sized) table
+ data = kmalloc(table_size, GFP_KERNEL);
+ if (data) {
+ memset(data, 0, table_size);
+ memcpy(data, &hdr, sizeof(hdr));
+ acpi_destroy_table(info);
+ acpi_init_table(info, data, 0);
+ }
+ else
+ error = -ENOMEM;
+ }
+ if (data)
+ copy_from_user(data, buffer, size);
+
+ write_unlock(&acpi_do_table_lock);
+ }
+
+ if (error)
+ return error;
+
+ *len = size;
+ file->f_pos += size;
+ return 0;
+}
+
+/*
* Examine/modify event register
*/
static int acpi_do_event_reg(ctl_table *ctl,
@@ -1123,6 +1279,7 @@ static int acpi_do_event_reg(ctl_table *ctl,
void *buffer,
size_t *len)
{
+ struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
char str[2 * sizeof(u32) + 4], *strend;
u32 val, enabling;
int size;
@@ -1136,10 +1293,10 @@ static int acpi_do_event_reg(ctl_table *ctl,
val = 0;
switch (ctl->ctl_name) {
case ACPI_PM1_ENABLE:
- val = acpi_read_pm1_enable(acpi_facp);
+ val = acpi_read_pm1_enable(facp);
break;
case ACPI_GPE_ENABLE:
- val = acpi_read_gpe_enable(acpi_facp);
+ val = acpi_read_gpe_enable(facp);
break;
case ACPI_GPE_LEVEL:
val = acpi_gpe_level;
@@ -1170,42 +1327,41 @@ static int acpi_do_event_reg(ctl_table *ctl,
switch (ctl->ctl_name) {
case ACPI_PM1_ENABLE:
// clear previously disabled events
- enabling = (val
- & ~acpi_read_pm1_enable(acpi_facp));
- acpi_write_pm1_status(acpi_facp, enabling);
+ enabling = (val & ~acpi_read_pm1_enable(facp));
+ acpi_write_pm1_status(facp, enabling);
if (val) {
// enable ACPI unless it is already
- if (!acpi_is_enabled(acpi_facp))
- acpi_enable(acpi_facp);
+ if (!acpi_is_enabled(facp))
+ acpi_enable(facp);
}
- else if (!acpi_read_gpe_enable(acpi_facp)) {
+ else if (!acpi_read_gpe_enable(facp)) {
// disable ACPI unless it is already
- if (acpi_is_enabled(acpi_facp))
- acpi_disable(acpi_facp);
+ if (acpi_is_enabled(facp))
+ acpi_disable(facp);
}
- acpi_write_pm1_enable(acpi_facp, val);
+ acpi_write_pm1_enable(facp, val);
break;
case ACPI_GPE_ENABLE:
// clear previously disabled events
enabling = (val
- & ~acpi_read_gpe_enable(acpi_facp));
- while (acpi_read_gpe_status(acpi_facp) & enabling)
- acpi_write_gpe_status(acpi_facp, enabling);
+ & ~acpi_read_gpe_enable(facp));
+ while (acpi_read_gpe_status(facp) & enabling)
+ acpi_write_gpe_status(facp, enabling);
if (val) {
// enable ACPI unless it is already
- if (!acpi_is_enabled(acpi_facp))
- acpi_enable(acpi_facp);
+ if (!acpi_is_enabled(facp))
+ acpi_enable(facp);
}
- else if (!acpi_read_pm1_enable(acpi_facp)) {
+ else if (!acpi_read_pm1_enable(facp)) {
// disable ACPI unless it is already
- if (acpi_is_enabled(acpi_facp))
- acpi_disable(acpi_facp);
+ if (acpi_is_enabled(facp))
+ acpi_disable(facp);
}
- acpi_write_gpe_enable(acpi_facp, val);
+ acpi_write_gpe_enable(facp, val);
break;
case ACPI_GPE_LEVEL:
acpi_gpe_level = val;
@@ -1301,8 +1457,9 @@ static int acpi_do_sleep(ctl_table *ctl,
*/
static int __init acpi_init(void)
{
- switch(acpi_enabled)
- {
+ struct acpi_facp *facp = NULL;
+
+ switch (acpi_enabled) {
case ACPI_ENABLED:
if (acpi_find_tables() && acpi_find_chipset())
return -ENODEV;
@@ -1319,37 +1476,39 @@ static int __init acpi_init(void)
return -ENODEV;
}
+ facp = (struct acpi_facp*) acpi_facp.table;
+
/*
* Internally we always keep latencies in timer
* ticks, which is simpler and more consistent (what is
* an uS to us?). Besides, that gives people more
* control in the /proc interfaces.
*/
- if (acpi_facp->p_lvl2_lat
- && acpi_facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) {
- acpi_p_lvl2_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl2_lat);
+ if (facp->p_lvl2_lat
+ && facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) {
+ acpi_p_lvl2_lat = ACPI_uS_TO_TMR_TICKS(facp->p_lvl2_lat);
acpi_enter_lvl2_lat = ACPI_uS_TO_TMR_TICKS(ACPI_TMR_HZ / 1000);
}
- if (acpi_facp->p_lvl3_lat
- && acpi_facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) {
- acpi_p_lvl3_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat);
+ if (facp->p_lvl3_lat
+ && facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) {
+ acpi_p_lvl3_lat = ACPI_uS_TO_TMR_TICKS(facp->p_lvl3_lat);
acpi_enter_lvl3_lat
- = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat * 5);
+ = ACPI_uS_TO_TMR_TICKS(facp->p_lvl3_lat * 5);
}
- if (acpi_claim_ioports(acpi_facp)) {
+ if (acpi_claim_ioports(facp)) {
printk(KERN_ERR "ACPI: I/O port allocation failed\n");
goto err_out;
}
- if (acpi_facp->sci_int
- && request_irq(acpi_facp->sci_int,
+ if (facp->sci_int
+ && request_irq(facp->sci_int,
acpi_irq,
SA_INTERRUPT | SA_SHIRQ,
"acpi",
- acpi_facp)) {
+ &acpi_facp)) {
printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n",
- acpi_facp->sci_int);
+ facp->sci_int);
goto err_out;
}
@@ -1369,7 +1528,7 @@ static int __init acpi_init(void)
return 0;
#endif
- if (acpi_facp->pm_tmr)
+ if (facp->pm_tmr)
pm_idle = acpi_idle;
return 0;
@@ -1387,16 +1546,18 @@ err_out:
*/
static void __exit acpi_exit(void)
{
+ struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
+
pm_idle = NULL;
pm_active = 0;
pm_power_off = NULL;
unregister_sysctl_table(acpi_sysctl);
- acpi_disable(acpi_facp);
- acpi_release_ioports(acpi_facp);
+ acpi_disable(facp);
+ acpi_release_ioports(facp);
- if (acpi_facp->sci_int)
- free_irq(acpi_facp->sci_int, acpi_facp);
+ if (facp->sci_int)
+ free_irq(facp->sci_int, &acpi_facp);
acpi_destroy_tables();
@@ -1429,3 +1590,4 @@ __setup("acpi=", acpi_setup);
module_init(acpi_init);
module_exit(acpi_exit);
+
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
index 0c3cae5d9..56500e466 100644
--- a/arch/i386/kernel/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -638,6 +638,8 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_setfsuid) /* 215 */
.long SYMBOL_NAME(sys_setfsgid)
.long SYMBOL_NAME(sys_pivot_root)
+ .long SYMBOL_NAME(sys_mincore)
+ .long SYMBOL_NAME(sys_madvise)
/*
@@ -646,6 +648,6 @@ ENTRY(sys_call_table)
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
- .rept NR_syscalls-217
+ .rept NR_syscalls-219
.long SYMBOL_NAME(sys_ni_syscall)
.endr
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index f7138faa3..3340946be 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -67,10 +67,17 @@ startup_32:
movl %eax,%cr4
#endif
/*
- * Setup paging (the tables are already set up, just switch them on)
+ * Setup paging (intialize tables, then switch them on)
*/
1:
- movl $0x101000,%eax
+ movl $pg0-__PAGE_OFFSET,%edi /* initialize page tables */
+ movl $007,%eax /* "007" doesn't mean with right to kill, but
+ PRESENT+RW+USER */
+1: stosl
+ add $0x1000,%eax
+ cmp $empty_zero_page-__PAGE_OFFSET,%edi
+ jne 1b
+ movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
@@ -350,8 +357,8 @@ SYMBOL_NAME(gdt):
.long SYMBOL_NAME(gdt_table)
/*
- * This is initialized to create a identity-mapping at 0-4M (for bootup
- * purposes) and another mapping of the 0-4M area at virtual address
+ * This is initialized to create an identity-mapping at 0-8M (for bootup
+ * purposes) and another mapping of the 0-8M area at virtual address
* PAGE_OFFSET.
*/
.org 0x1000
@@ -366,270 +373,20 @@ ENTRY(swapper_pg_dir)
.fill BOOT_KERNEL_PGD_PTRS-2,4,0
/*
- * The page tables are initialized to only 4MB here - the final page
- * tables are set up later depending on memory size. The "007" at the
- * end doesn't mean with right to kill, but PRESENT+RW+USER
+ * The page tables are initialized to only 8MB here - the final page
+ * tables are set up later depending on memory size.
*/
.org 0x2000
ENTRY(pg0)
- .long 0x000007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
- .long 0x008007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
- .long 0x010007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
- .long 0x018007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
- .long 0x020007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
- .long 0x028007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
- .long 0x030007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
- .long 0x038007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
- .long 0x040007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
- .long 0x048007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
- .long 0x050007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
- .long 0x058007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
- .long 0x060007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
- .long 0x068007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
- .long 0x070007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
- .long 0x078007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
- .long 0x080007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
- .long 0x088007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
- .long 0x090007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
- .long 0x098007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
- .long 0x0a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
- .long 0x0a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
- .long 0x0b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
- .long 0x0b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
- .long 0x0c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
- .long 0x0c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
- .long 0x0d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
- .long 0x0d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
- .long 0x0e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
- .long 0x0e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
- .long 0x0f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
- .long 0x0f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
- .long 0x100007,0x101007,0x102007,0x103007,0x104007,0x105007,0x106007,0x107007
- .long 0x108007,0x109007,0x10a007,0x10b007,0x10c007,0x10d007,0x10e007,0x10f007
- .long 0x110007,0x111007,0x112007,0x113007,0x114007,0x115007,0x116007,0x117007
- .long 0x118007,0x119007,0x11a007,0x11b007,0x11c007,0x11d007,0x11e007,0x11f007
- .long 0x120007,0x121007,0x122007,0x123007,0x124007,0x125007,0x126007,0x127007
- .long 0x128007,0x129007,0x12a007,0x12b007,0x12c007,0x12d007,0x12e007,0x12f007
- .long 0x130007,0x131007,0x132007,0x133007,0x134007,0x135007,0x136007,0x137007
- .long 0x138007,0x139007,0x13a007,0x13b007,0x13c007,0x13d007,0x13e007,0x13f007
- .long 0x140007,0x141007,0x142007,0x143007,0x144007,0x145007,0x146007,0x147007
- .long 0x148007,0x149007,0x14a007,0x14b007,0x14c007,0x14d007,0x14e007,0x14f007
- .long 0x150007,0x151007,0x152007,0x153007,0x154007,0x155007,0x156007,0x157007
- .long 0x158007,0x159007,0x15a007,0x15b007,0x15c007,0x15d007,0x15e007,0x15f007
- .long 0x160007,0x161007,0x162007,0x163007,0x164007,0x165007,0x166007,0x167007
- .long 0x168007,0x169007,0x16a007,0x16b007,0x16c007,0x16d007,0x16e007,0x16f007
- .long 0x170007,0x171007,0x172007,0x173007,0x174007,0x175007,0x176007,0x177007
- .long 0x178007,0x179007,0x17a007,0x17b007,0x17c007,0x17d007,0x17e007,0x17f007
- .long 0x180007,0x181007,0x182007,0x183007,0x184007,0x185007,0x186007,0x187007
- .long 0x188007,0x189007,0x18a007,0x18b007,0x18c007,0x18d007,0x18e007,0x18f007
- .long 0x190007,0x191007,0x192007,0x193007,0x194007,0x195007,0x196007,0x197007
- .long 0x198007,0x199007,0x19a007,0x19b007,0x19c007,0x19d007,0x19e007,0x19f007
- .long 0x1a0007,0x1a1007,0x1a2007,0x1a3007,0x1a4007,0x1a5007,0x1a6007,0x1a7007
- .long 0x1a8007,0x1a9007,0x1aa007,0x1ab007,0x1ac007,0x1ad007,0x1ae007,0x1af007
- .long 0x1b0007,0x1b1007,0x1b2007,0x1b3007,0x1b4007,0x1b5007,0x1b6007,0x1b7007
- .long 0x1b8007,0x1b9007,0x1ba007,0x1bb007,0x1bc007,0x1bd007,0x1be007,0x1bf007
- .long 0x1c0007,0x1c1007,0x1c2007,0x1c3007,0x1c4007,0x1c5007,0x1c6007,0x1c7007
- .long 0x1c8007,0x1c9007,0x1ca007,0x1cb007,0x1cc007,0x1cd007,0x1ce007,0x1cf007
- .long 0x1d0007,0x1d1007,0x1d2007,0x1d3007,0x1d4007,0x1d5007,0x1d6007,0x1d7007
- .long 0x1d8007,0x1d9007,0x1da007,0x1db007,0x1dc007,0x1dd007,0x1de007,0x1df007
- .long 0x1e0007,0x1e1007,0x1e2007,0x1e3007,0x1e4007,0x1e5007,0x1e6007,0x1e7007
- .long 0x1e8007,0x1e9007,0x1ea007,0x1eb007,0x1ec007,0x1ed007,0x1ee007,0x1ef007
- .long 0x1f0007,0x1f1007,0x1f2007,0x1f3007,0x1f4007,0x1f5007,0x1f6007,0x1f7007
- .long 0x1f8007,0x1f9007,0x1fa007,0x1fb007,0x1fc007,0x1fd007,0x1fe007,0x1ff007
- .long 0x200007,0x201007,0x202007,0x203007,0x204007,0x205007,0x206007,0x207007
- .long 0x208007,0x209007,0x20a007,0x20b007,0x20c007,0x20d007,0x20e007,0x20f007
- .long 0x210007,0x211007,0x212007,0x213007,0x214007,0x215007,0x216007,0x217007
- .long 0x218007,0x219007,0x21a007,0x21b007,0x21c007,0x21d007,0x21e007,0x21f007
- .long 0x220007,0x221007,0x222007,0x223007,0x224007,0x225007,0x226007,0x227007
- .long 0x228007,0x229007,0x22a007,0x22b007,0x22c007,0x22d007,0x22e007,0x22f007
- .long 0x230007,0x231007,0x232007,0x233007,0x234007,0x235007,0x236007,0x237007
- .long 0x238007,0x239007,0x23a007,0x23b007,0x23c007,0x23d007,0x23e007,0x23f007
- .long 0x240007,0x241007,0x242007,0x243007,0x244007,0x245007,0x246007,0x247007
- .long 0x248007,0x249007,0x24a007,0x24b007,0x24c007,0x24d007,0x24e007,0x24f007
- .long 0x250007,0x251007,0x252007,0x253007,0x254007,0x255007,0x256007,0x257007
- .long 0x258007,0x259007,0x25a007,0x25b007,0x25c007,0x25d007,0x25e007,0x25f007
- .long 0x260007,0x261007,0x262007,0x263007,0x264007,0x265007,0x266007,0x267007
- .long 0x268007,0x269007,0x26a007,0x26b007,0x26c007,0x26d007,0x26e007,0x26f007
- .long 0x270007,0x271007,0x272007,0x273007,0x274007,0x275007,0x276007,0x277007
- .long 0x278007,0x279007,0x27a007,0x27b007,0x27c007,0x27d007,0x27e007,0x27f007
- .long 0x280007,0x281007,0x282007,0x283007,0x284007,0x285007,0x286007,0x287007
- .long 0x288007,0x289007,0x28a007,0x28b007,0x28c007,0x28d007,0x28e007,0x28f007
- .long 0x290007,0x291007,0x292007,0x293007,0x294007,0x295007,0x296007,0x297007
- .long 0x298007,0x299007,0x29a007,0x29b007,0x29c007,0x29d007,0x29e007,0x29f007
- .long 0x2a0007,0x2a1007,0x2a2007,0x2a3007,0x2a4007,0x2a5007,0x2a6007,0x2a7007
- .long 0x2a8007,0x2a9007,0x2aa007,0x2ab007,0x2ac007,0x2ad007,0x2ae007,0x2af007
- .long 0x2b0007,0x2b1007,0x2b2007,0x2b3007,0x2b4007,0x2b5007,0x2b6007,0x2b7007
- .long 0x2b8007,0x2b9007,0x2ba007,0x2bb007,0x2bc007,0x2bd007,0x2be007,0x2bf007
- .long 0x2c0007,0x2c1007,0x2c2007,0x2c3007,0x2c4007,0x2c5007,0x2c6007,0x2c7007
- .long 0x2c8007,0x2c9007,0x2ca007,0x2cb007,0x2cc007,0x2cd007,0x2ce007,0x2cf007
- .long 0x2d0007,0x2d1007,0x2d2007,0x2d3007,0x2d4007,0x2d5007,0x2d6007,0x2d7007
- .long 0x2d8007,0x2d9007,0x2da007,0x2db007,0x2dc007,0x2dd007,0x2de007,0x2df007
- .long 0x2e0007,0x2e1007,0x2e2007,0x2e3007,0x2e4007,0x2e5007,0x2e6007,0x2e7007
- .long 0x2e8007,0x2e9007,0x2ea007,0x2eb007,0x2ec007,0x2ed007,0x2ee007,0x2ef007
- .long 0x2f0007,0x2f1007,0x2f2007,0x2f3007,0x2f4007,0x2f5007,0x2f6007,0x2f7007
- .long 0x2f8007,0x2f9007,0x2fa007,0x2fb007,0x2fc007,0x2fd007,0x2fe007,0x2ff007
- .long 0x300007,0x301007,0x302007,0x303007,0x304007,0x305007,0x306007,0x307007
- .long 0x308007,0x309007,0x30a007,0x30b007,0x30c007,0x30d007,0x30e007,0x30f007
- .long 0x310007,0x311007,0x312007,0x313007,0x314007,0x315007,0x316007,0x317007
- .long 0x318007,0x319007,0x31a007,0x31b007,0x31c007,0x31d007,0x31e007,0x31f007
- .long 0x320007,0x321007,0x322007,0x323007,0x324007,0x325007,0x326007,0x327007
- .long 0x328007,0x329007,0x32a007,0x32b007,0x32c007,0x32d007,0x32e007,0x32f007
- .long 0x330007,0x331007,0x332007,0x333007,0x334007,0x335007,0x336007,0x337007
- .long 0x338007,0x339007,0x33a007,0x33b007,0x33c007,0x33d007,0x33e007,0x33f007
- .long 0x340007,0x341007,0x342007,0x343007,0x344007,0x345007,0x346007,0x347007
- .long 0x348007,0x349007,0x34a007,0x34b007,0x34c007,0x34d007,0x34e007,0x34f007
- .long 0x350007,0x351007,0x352007,0x353007,0x354007,0x355007,0x356007,0x357007
- .long 0x358007,0x359007,0x35a007,0x35b007,0x35c007,0x35d007,0x35e007,0x35f007
- .long 0x360007,0x361007,0x362007,0x363007,0x364007,0x365007,0x366007,0x367007
- .long 0x368007,0x369007,0x36a007,0x36b007,0x36c007,0x36d007,0x36e007,0x36f007
- .long 0x370007,0x371007,0x372007,0x373007,0x374007,0x375007,0x376007,0x377007
- .long 0x378007,0x379007,0x37a007,0x37b007,0x37c007,0x37d007,0x37e007,0x37f007
- .long 0x380007,0x381007,0x382007,0x383007,0x384007,0x385007,0x386007,0x387007
- .long 0x388007,0x389007,0x38a007,0x38b007,0x38c007,0x38d007,0x38e007,0x38f007
- .long 0x390007,0x391007,0x392007,0x393007,0x394007,0x395007,0x396007,0x397007
- .long 0x398007,0x399007,0x39a007,0x39b007,0x39c007,0x39d007,0x39e007,0x39f007
- .long 0x3a0007,0x3a1007,0x3a2007,0x3a3007,0x3a4007,0x3a5007,0x3a6007,0x3a7007
- .long 0x3a8007,0x3a9007,0x3aa007,0x3ab007,0x3ac007,0x3ad007,0x3ae007,0x3af007
- .long 0x3b0007,0x3b1007,0x3b2007,0x3b3007,0x3b4007,0x3b5007,0x3b6007,0x3b7007
- .long 0x3b8007,0x3b9007,0x3ba007,0x3bb007,0x3bc007,0x3bd007,0x3be007,0x3bf007
- .long 0x3c0007,0x3c1007,0x3c2007,0x3c3007,0x3c4007,0x3c5007,0x3c6007,0x3c7007
- .long 0x3c8007,0x3c9007,0x3ca007,0x3cb007,0x3cc007,0x3cd007,0x3ce007,0x3cf007
- .long 0x3d0007,0x3d1007,0x3d2007,0x3d3007,0x3d4007,0x3d5007,0x3d6007,0x3d7007
- .long 0x3d8007,0x3d9007,0x3da007,0x3db007,0x3dc007,0x3dd007,0x3de007,0x3df007
- .long 0x3e0007,0x3e1007,0x3e2007,0x3e3007,0x3e4007,0x3e5007,0x3e6007,0x3e7007
- .long 0x3e8007,0x3e9007,0x3ea007,0x3eb007,0x3ec007,0x3ed007,0x3ee007,0x3ef007
- .long 0x3f0007,0x3f1007,0x3f2007,0x3f3007,0x3f4007,0x3f5007,0x3f6007,0x3f7007
- .long 0x3f8007,0x3f9007,0x3fa007,0x3fb007,0x3fc007,0x3fd007,0x3fe007,0x3ff007
+.org 0x3000
ENTRY(pg1)
- .long 0x400007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
- .long 0x408007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
- .long 0x410007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
- .long 0x418007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
- .long 0x420007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
- .long 0x428007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
- .long 0x430007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
- .long 0x438007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
- .long 0x440007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
- .long 0x448007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
- .long 0x450007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
- .long 0x458007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
- .long 0x460007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
- .long 0x468007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
- .long 0x470007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
- .long 0x478007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
- .long 0x480007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
- .long 0x488007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
- .long 0x490007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
- .long 0x498007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
- .long 0x4a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
- .long 0x4a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
- .long 0x4b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
- .long 0x4b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
- .long 0x4c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
- .long 0x4c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
- .long 0x4d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
- .long 0x4d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
- .long 0x4e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
- .long 0x4e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
- .long 0x4f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
- .long 0x4f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
- .long 0x500007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
- .long 0x508007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
- .long 0x510007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
- .long 0x518007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
- .long 0x520007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
- .long 0x528007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
- .long 0x530007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
- .long 0x538007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
- .long 0x540007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
- .long 0x548007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
- .long 0x550007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
- .long 0x558007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
- .long 0x560007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
- .long 0x568007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
- .long 0x570007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
- .long 0x578007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
- .long 0x580007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
- .long 0x588007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
- .long 0x590007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
- .long 0x598007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
- .long 0x5a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
- .long 0x5a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
- .long 0x5b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
- .long 0x5b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
- .long 0x5c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
- .long 0x5c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
- .long 0x5d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
- .long 0x5d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
- .long 0x5e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
- .long 0x5e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
- .long 0x5f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
- .long 0x5f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
- .long 0x600007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
- .long 0x608007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
- .long 0x610007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
- .long 0x618007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
- .long 0x620007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
- .long 0x628007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
- .long 0x630007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
- .long 0x638007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
- .long 0x640007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
- .long 0x648007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
- .long 0x650007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
- .long 0x658007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
- .long 0x660007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
- .long 0x668007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
- .long 0x670007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
- .long 0x678007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
- .long 0x680007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
- .long 0x688007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
- .long 0x690007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
- .long 0x698007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
- .long 0x6a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
- .long 0x6a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
- .long 0x6b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
- .long 0x6b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
- .long 0x6c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
- .long 0x6c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
- .long 0x6d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
- .long 0x6d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
- .long 0x6e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
- .long 0x6e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
- .long 0x6f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
- .long 0x6f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
- .long 0x700007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007
- .long 0x708007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007
- .long 0x710007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007
- .long 0x718007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007
- .long 0x720007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007
- .long 0x728007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007
- .long 0x730007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007
- .long 0x738007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007
- .long 0x740007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007
- .long 0x748007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007
- .long 0x750007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007
- .long 0x758007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007
- .long 0x760007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007
- .long 0x768007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007
- .long 0x770007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007
- .long 0x778007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007
- .long 0x780007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007
- .long 0x788007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007
- .long 0x790007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007
- .long 0x798007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007
- .long 0x7a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007
- .long 0x7a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007
- .long 0x7b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007
- .long 0x7b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007
- .long 0x7c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007
- .long 0x7c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007
- .long 0x7d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007
- .long 0x7d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007
- .long 0x7e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007
- .long 0x7e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007
- .long 0x7f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007
- .long 0x7f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007
+
+/*
+ * empty_zero_page must immediately follow the page tables ! (The
+ * initialization loop counts until empty_zero_page)
+ */
+
.org 0x4000
ENTRY(empty_zero_page)
diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c
index a3389c5f0..e21c33b6f 100644
--- a/arch/i386/kernel/i386_ksyms.c
+++ b/arch/i386/kernel/i386_ksyms.c
@@ -121,21 +121,7 @@ EXPORT_SYMBOL(smp_call_function);
#endif
#ifdef CONFIG_MCA
-/* Adapter probing and info methods. */
EXPORT_SYMBOL(machine_id);
-EXPORT_SYMBOL(mca_find_adapter);
-EXPORT_SYMBOL(mca_write_pos);
-EXPORT_SYMBOL(mca_read_pos);
-EXPORT_SYMBOL(mca_read_stored_pos);
-EXPORT_SYMBOL(mca_set_adapter_name);
-EXPORT_SYMBOL(mca_get_adapter_name);
-EXPORT_SYMBOL(mca_set_adapter_procfn);
-EXPORT_SYMBOL(mca_isenabled);
-EXPORT_SYMBOL(mca_isadapter);
-EXPORT_SYMBOL(mca_mark_as_used);
-EXPORT_SYMBOL(mca_mark_as_unused);
-EXPORT_SYMBOL(mca_find_unused_adapter);
-EXPORT_SYMBOL(mca_is_adapter_used);
#endif
#ifdef CONFIG_VT
diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
index 120c861e7..d2ba2fd9c 100644
--- a/arch/i386/kernel/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -456,6 +456,18 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction *
* hardware disable after having gotten the irq
* controller lock.
*/
+
+/**
+ * disable_irq_nosync - disable an irq without waiting
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. Unlike disable_irq, this function does not ensure existing
+ * instances of the irq handler have completed before returning.
+ *
+ * This function may be called from IRQ context.
+ */
+
void inline disable_irq_nosync(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
@@ -469,10 +481,19 @@ void inline disable_irq_nosync(unsigned int irq)
spin_unlock_irqrestore(&desc->lock, flags);
}
-/*
- * Synchronous version of the above, making sure the IRQ is
- * no longer running on any other IRQ..
+/**
+ * disable_irq - disable an irq and wait for completion
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. That is for two disables you need two enables. This
+ * function waits for any pending IRQ handlers for this interrupt
+ * to complete before returning. If you use this function while
+ * holding a resource the IRQ handler may need you will deadlock.
+ *
+ * This function may be called - with care - from IRQ context.
*/
+
void disable_irq(unsigned int irq)
{
disable_irq_nosync(irq);
@@ -484,6 +505,16 @@ void disable_irq(unsigned int irq)
}
}
+/**
+ * enable_irq - enable interrupt handling on an irq
+ * @irq: Interrupt to enable
+ *
+ * Re-enables the processing of interrupts on this IRQ line
+ * providing no disable_irq calls are now in effect.
+ *
+ * This function may be called from IRQ context.
+ */
+
void enable_irq(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
@@ -598,6 +629,38 @@ out:
return 1;
}
+/**
+ * request_irq - allocate an interrupt line
+ * @irq: Interrupt line to allocate
+ * @handler: Function to be called when the IRQ occurs
+ * @irqflags: Interrupt type flags
+ * @devname: An ascii name for the claiming device
+ * @dev_id: A cookie passed back to the handler function
+ *
+ * This call allocates interrupt resources and enables the
+ * interrupt line and IRQ handling. From the point this
+ * call is made your handler function may be invoked. Since
+ * your handler function must clear any interrupt the board
+ * raises, you must take care both to initialise your hardware
+ * and to set up the interrupt handler in the right order.
+ *
+ * Dev_id must be globally unique. Normally the address of the
+ * device data structure is used as the cookie. Since the handler
+ * receives this value it makes sense to use it.
+ *
+ * If your interrupt is shared you must pass a non NULL dev_id
+ * as this is required when freeing the interrupt.
+ *
+ * Flags:
+ *
+ * SA_SHIRQ Interrupt is shared
+ *
+ * SA_INTERRUPT Disable local interrupts while processing
+ *
+ * SA_SAMPLE_RANDOM The interrupt can be used for entropy
+ *
+ */
+
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
@@ -642,7 +705,25 @@ int request_irq(unsigned int irq,
kfree(action);
return retval;
}
-
+
+/**
+ * free_irq - free an interrupt
+ * @irq: Interrupt line to free
+ * @dev_id: Device identity to free
+ *
+ * Remove an interrupt handler. The handler is removed and if the
+ * interrupt line is no longer in use by any driver it is disabled.
+ * On a shared IRQ the caller must ensure the interrupt is disabled
+ * on the card it drives before calling this function. The function
+ * does not return until any executing interrupts for this IRQ
+ * have completed.
+ *
+ * This function may be called from interrupt context.
+ *
+ * Bugs: Attempting to free an irq in a handler for the same irq hangs
+ * the machine.
+ */
+
void free_irq(unsigned int irq, void *dev_id)
{
irq_desc_t *desc;
@@ -693,6 +774,15 @@ void free_irq(unsigned int irq, void *dev_id)
* with "IRQ_WAITING" cleared and the interrupt
* disabled.
*/
+
+/**
+ * probe_irq_on - begin an interrupt autodetect
+ *
+ * Commence probing for an interrupt. The interrupts are scanned
+ * and a mask of potential interrupt lines is returned.
+ *
+ */
+
unsigned long probe_irq_on(void)
{
unsigned int i;
@@ -770,6 +860,16 @@ unsigned long probe_irq_on(void)
* Return a mask of triggered interrupts (this
* can handle only legacy ISA interrupts).
*/
+
+/**
+ * probe_irq_mask
+ * @val: mask of interrupts to consider
+ *
+ * Scan the ISA bus interrupt lines and return a bitmap of
+ * active interrupts. The interrupt probe logic state is then
+ * returned to its previous value.
+ */
+
unsigned int probe_irq_mask(unsigned long val)
{
int i;
@@ -798,8 +898,27 @@ unsigned int probe_irq_mask(unsigned long val)
/*
* Return the one interrupt that triggered (this can
- * handle any interrupt source)
+ * handle any interrupt source).
+ */
+
+/**
+ * probe_irq_off - end an interrupt autodetect
+ * @val: mask of potential interrupts (unused)
+ *
+ * Scans the unused interrupt lines and returns the line which
+ * appears to have triggered the interrupt. If no interrupt was
+ * found then zero is returned. If more than one interrupt is
+ * found then minus the first candidate is returned to indicate
+ * their is doubt.
+ *
+ * The interrupt probe logic state is returned to its previous
+ * value.
+ *
+ * BUGS: When used in a module (which arguably shouldnt happen)
+ * nothing prevents two IRQ probe callers from overlapping. The
+ * results of this are non-optimal.
*/
+
int probe_irq_off(unsigned long val)
{
int i, irq_found, nr_irqs;
diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c
index 7061e55db..f0f0f9f37 100644
--- a/arch/i386/kernel/mca.c
+++ b/arch/i386/kernel/mca.c
@@ -34,6 +34,7 @@
* - switched to regular procfs methods.
*/
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -362,6 +363,19 @@ void mca_handle_nmi(void)
/*--------------------------------------------------------------------*/
+/**
+ * mca_find_adapter - scan for adapters
+ * @id: MCA identification to search for
+ * @start: Starting slot
+ *
+ * Search the MCA configuration for adapters matching the 16bit
+ * ID given. The first time it should be called with start as zero
+ * and then further calls made passing the return value of the
+ * previous call until MCA_NOTFOUND is returned.
+ *
+ * Disabled adapters are not reported.
+ */
+
int mca_find_adapter(int id, int start)
{
if(mca_info == NULL || id == 0xffff) {
@@ -390,8 +404,25 @@ int mca_find_adapter(int id, int start)
return MCA_NOTFOUND;
} /* mca_find_adapter() */
+EXPORT_SYMBOL(mca_find_adapter);
+
/*--------------------------------------------------------------------*/
+/**
+ * mca_find_unused_adapter - scan for unused adapters
+ * @id: MCA identification to search for
+ * @start: Starting slot
+ *
+ * Search the MCA configuration for adapters matching the 16bit
+ * ID given. The first time it should be called with start as zero
+ * and then further calls made passing the return value of the
+ * previous call until MCA_NOTFOUND is returned.
+ *
+ * Adapters that have been claimed by drivers and those that
+ * are disabled are not reported. This function thus allows a driver
+ * to scan for further cards when some may already be driven.
+ */
+
int mca_find_unused_adapter(int id, int start)
{
if(mca_info == NULL || id == 0xffff) {
@@ -421,8 +452,20 @@ int mca_find_unused_adapter(int id, int start)
return MCA_NOTFOUND;
} /* mca_find_unused_adapter() */
+EXPORT_SYMBOL(mca_find_unused_adapter);
+
/*--------------------------------------------------------------------*/
+/**
+ * mca_read_stored_pos - read POS register from boot data
+ * @slot: slot number to read from
+ * @reg: register to read from
+ *
+ * Fetch a POS value that was stored at boot time by the kernel
+ * when it scanned the MCA space. The register value is returned.
+ * Missing or invalid registers report 0.
+ */
+
unsigned char mca_read_stored_pos(int slot, int reg)
{
if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
@@ -430,8 +473,22 @@ unsigned char mca_read_stored_pos(int slot, int reg)
return mca_info->slot[slot].pos[reg];
} /* mca_read_stored_pos() */
+EXPORT_SYMBOL(mca_read_stored_pos);
+
/*--------------------------------------------------------------------*/
+/**
+ * mca_read_pos - read POS register from card
+ * @slot: slot number to read from
+ * @reg: register to read from
+ *
+ * Fetch a POS value directly from the hardware to obtain the
+ * current value. This is much slower than mca_read_stored_pos and
+ * may not be invoked from interrupt context. It handles the
+ * deep magic required for onboard devices transparently.
+ */
+
+
unsigned char mca_read_pos(int slot, int reg)
{
unsigned int byte = 0;
@@ -489,16 +546,32 @@ unsigned char mca_read_pos(int slot, int reg)
return byte;
} /* mca_read_pos() */
+EXPORT_SYMBOL(mca_read_pos);
+
/*--------------------------------------------------------------------*/
-/* Note that this a technically a Bad Thing, as IBM tech stuff says
- * you should only set POS values through their utilities.
- * However, some devices such as the 3c523 recommend that you write
- * back some data to make sure the configuration is consistent.
- * I'd say that IBM is right, but I like my drivers to work.
- * This function can't do checks to see if multiple devices end up
- * with the same resources, so you might see magic smoke if someone
- * screws up.
+/**
+ * mca_write_pos - read POS register from card
+ * @slot: slot number to read from
+ * @reg: register to read from
+ * @byte: byte to write to the POS registers
+ *
+ * Store a POS value directly from the hardware. You should not
+ * normally need to use this function and should have a very good
+ * knowledge of MCA bus before you do so. Doing this wrongly can
+ * damage the hardware.
+ *
+ * This function may not be used from interrupt context.
+ *
+ * Note that this a technically a Bad Thing, as IBM tech stuff says
+ * you should only set POS values through their utilities.
+ * However, some devices such as the 3c523 recommend that you write
+ * back some data to make sure the configuration is consistent.
+ * I'd say that IBM is right, but I like my drivers to work.
+ *
+ * This function can't do checks to see if multiple devices end up
+ * with the same resources, so you might see magic smoke if someone
+ * screws up.
*/
void mca_write_pos(int slot, int reg, unsigned char byte)
@@ -532,8 +605,20 @@ void mca_write_pos(int slot, int reg, unsigned char byte)
mca_info->slot[slot].pos[reg] = byte;
} /* mca_write_pos() */
+EXPORT_SYMBOL(mca_write_pos);
+
/*--------------------------------------------------------------------*/
+/**
+ * mca_set_adapter_name - Set the description of the card
+ * @slot: slot to name
+ * @name: text string for the namen
+ *
+ * This function sets the name reported via /proc for this
+ * adapter slot. This is for user information only. Setting a
+ * name deletes any previous name.
+ */
+
void mca_set_adapter_name(int slot, char* name)
{
if(mca_info == NULL) return;
@@ -550,6 +635,26 @@ void mca_set_adapter_name(int slot, char* name)
}
}
+EXPORT_SYMBOL(mca_set_adapter_name);
+
+/**
+ * mca_set_adapter_procfn - Set the /proc callback
+ * @slot: slot to configure
+ * @procfn: callback function to call for /proc
+ * @dev: device information passed to the callback
+ *
+ * This sets up an information callback for /proc/mca/slot?. The
+ * function is called with the buffer, slot, and device pointer (or
+ * some equally informative context information, or nothing, if you
+ * prefer), and is expected to put useful information into the
+ * buffer. The adapter name, id, and POS registers get printed
+ * before this is called though, so don't do it again.
+ *
+ * This should be called with a NULL procfn when a module
+ * unregisters, thus preventing kernel crashes and other such
+ * nastiness.
+ */
+
void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
{
if(mca_info == NULL) return;
@@ -560,11 +665,33 @@ void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
}
}
+EXPORT_SYMBOL(mca_set_adapter_procfn);
+
+/**
+ * mca_is_adapter_used - check if claimed by driver
+ * @slot: slot to check
+ *
+ * Returns 1 if the slot has been claimed by a driver
+ */
+
int mca_is_adapter_used(int slot)
{
return mca_info->slot[slot].driver_loaded;
}
+EXPORT_SYMBOL(mca_is_adapter_used);
+
+/**
+ * mca_mark_as_used - claim an MCA device
+ * @slot: slot to claim
+ * FIXME: should we make this threadsafe
+ *
+ * Claim an MCA slot for a device driver. If the
+ * slot is already taken the function returns 1,
+ * if it is not taken it is claimed and 0 is
+ * returned.
+ */
+
int mca_mark_as_used(int slot)
{
if(mca_info->slot[slot].driver_loaded) return 1;
@@ -572,10 +699,29 @@ int mca_mark_as_used(int slot)
return 0;
}
+EXPORT_SYMBOL(mca_mark_as_used);
+
+/**
+ * mca_mark_as_unused - release an MCA device
+ * @slot: slot to claim
+ *
+ * Release the slot for other drives to use.
+ */
+
void mca_mark_as_unused(int slot)
{
mca_info->slot[slot].driver_loaded = 0;
}
+
+EXPORT_SYMBOL(mca_mark_as_unused);
+
+/**
+ * mca_get_adapter_name - get the adapter description
+ * @slot: slot to query
+ *
+ * Return the adapter description if set. If it has not been
+ * set or the slot is out range then return NULL.
+ */
char *mca_get_adapter_name(int slot)
{
@@ -588,6 +734,16 @@ char *mca_get_adapter_name(int slot)
return 0;
}
+EXPORT_SYMBOL(mca_get_adapter_name);
+
+/**
+ * mca_isadapter - check if the slot holds an adapter
+ * @slot: slot to query
+ *
+ * Returns zero if the slot does not hold an adapter, non zero if
+ * it does.
+ */
+
int mca_isadapter(int slot)
{
if(mca_info == NULL) return 0;
@@ -600,6 +756,17 @@ int mca_isadapter(int slot)
return 0;
}
+EXPORT_SYMBOL(mca_isadapter);
+
+
+/**
+ * mca_isadapter - check if the slot holds an adapter
+ * @slot: slot to query
+ *
+ * Returns a non zero value if the slot holds an enabled adapter
+ * and zero for any other case.
+ */
+
int mca_isenabled(int slot)
{
if(mca_info == NULL) return 0;
@@ -611,6 +778,8 @@ int mca_isenabled(int slot)
return 0;
}
+EXPORT_SYMBOL(mca_isenabled);
+
/*--------------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c
index cc9c7eafe..1d6203f65 100644
--- a/arch/i386/kernel/mtrr.c
+++ b/arch/i386/kernel/mtrr.c
@@ -1101,8 +1101,44 @@ static int cyrix_get_free_region (unsigned long base, unsigned long size)
static int (*get_free_region) (unsigned long base,
unsigned long size) = generic_get_free_region;
-int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
- char increment)
+/**
+ * mtrr_add - Add a memory type region
+ * @base: Physical base address of region
+ * @size: Physical size of region
+ * @type: Type of MTRR desired
+ * @increment: If this is true do usage counting on the region
+ *
+ * Memory type region registers control the caching on newer Intel and
+ * non Intel processors. This function allows drivers to request an
+ * MTRR is added. The details and hardware specifics of each processors
+ * implementation are hidden from the caller, but nevertheless the
+ * caller should expect to need to provide a power of two size on an
+ * equivalent power of two boundary.
+ *
+ * If the region cannot be added either because all regions are in use
+ * or the CPU cannot support it a negative value is returned. On success
+ * the register number for this entry is returned, but should be treated
+ * as a cookie only.
+ *
+ * On a multiprocessor machine the changes are made to all processors.
+ * This is required on x86 by the Intel processors.
+ *
+ * The available types are
+ *
+ * MTRR_TYPE_UNCACHEABLE - No caching
+ *
+ * MTRR_TYPE_WRITEBACK - Write data back in bursts whenever
+ *
+ * MTRR_TYPE_WRCOMB - Write data back soon but allow bursts
+ *
+ * MTRR_TYPE_WRTHROUGH - Cache reads but not writes
+ *
+ * BUGS: Needs a quiet flag for the cases where drivers do not mind
+ * failures and do not wish system log messages to be sent.
+ */
+
+int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment)
+{
/* [SUMMARY] Add an MTRR entry.
<base> The starting (base) address of the region.
<size> The size (in bytes) of the region.
@@ -1113,7 +1149,6 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
the error code.
[NOTE] This routine uses a spinlock.
*/
-{
int i, max;
mtrr_type ltype;
unsigned long lbase, lsize, last;
@@ -1145,7 +1180,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
(boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
{
- printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+ printk (KERN_WARNING "mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
return -EINVAL;
}
}
@@ -1162,13 +1197,13 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
{
if (type != MTRR_TYPE_WRCOMB)
{
- printk ("mtrr: only write-combining is supported\n");
+ printk (KERN_WARNING "mtrr: only write-combining is supported\n");
return -EINVAL;
}
}
else if (base + size < 0x100000)
{
- printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
+ printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
base, size);
return -EINVAL;
}
@@ -1179,7 +1214,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
lbase = lbase >> 1, last = last >> 1);
if (lbase != last)
{
- printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
+ printk (KERN_WARNING "mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
base, size);
return -EINVAL;
}
@@ -1196,7 +1231,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
/* If the type is WC, check that this processor supports it */
if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
{
- printk ("mtrr: your processor doesn't support write-combining\n");
+ printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n");
return -ENOSYS;
}
increment = increment ? 1 : 0;
@@ -1212,7 +1247,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
if ( (base < lbase) || (base + size > lbase + lsize) )
{
up(&main_lock);
- printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
+ printk (KERN_WARNING "mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
base, size, lbase, lsize);
return -EINVAL;
}
@@ -1245,6 +1280,21 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
return i;
} /* End Function mtrr_add */
+/**
+ * mtrr_del
+ * @reg: Register returned by mtrr_add
+ * @base: Physical base address
+ * @size: Size of region
+ *
+ * If register is supplied then base and size are ignored. This is
+ * how drivers should call it.
+ *
+ * Releases an MTRR region. If the usage count drops to zero the
+ * register is freed and the region returns to default state.
+ * On success the register is returned, on failure a negative error
+ * code.
+ */
+
int mtrr_del (int reg, unsigned long base, unsigned long size)
/* [SUMMARY] Delete MTRR/decrement usage count.
<reg> The register. If this is less than 0 then <<base>> and <<size>> must
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index b5602ebec..a12b6b73d 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -790,14 +790,14 @@ void __init setup_arch(char **cmdline_p)
static int __init get_model_name(struct cpuinfo_x86 *c)
{
- unsigned int n, dummy, *v;
+ unsigned int n, dummy, *v, ecx, edx;
/* Actually we must have cpuid or we could never have
* figured out that this was AMD from the vendor info :-).
*/
cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
- if (n < 4)
+ if (n < 0x80000004)
return 0;
cpuid(0x80000001, &dummy, &dummy, &dummy, &(c->x86_capability));
v = (unsigned int *) c->x86_model_id;
@@ -806,13 +806,24 @@ static int __init get_model_name(struct cpuinfo_x86 *c)
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
c->x86_model_id[48] = 0;
/* Set MTRR capability flag if appropriate */
- if(boot_cpu_data.x86 !=5)
- return 1;
- if((boot_cpu_data.x86_model == 9) ||
- ((boot_cpu_data.x86_model == 8) &&
- (boot_cpu_data.x86_mask >= 8)))
- c->x86_capability |= X86_FEATURE_MTRR;
+ if(boot_cpu_data.x86 == 5) {
+ if((boot_cpu_data.x86_model == 9) ||
+ ((boot_cpu_data.x86_model == 8) &&
+ (boot_cpu_data.x86_mask >= 8)))
+ c->x86_capability |= X86_FEATURE_MTRR;
+ }
+ if (n >= 0x80000005){
+ cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
+ printk("CPU: L1 I Cache: %dK L1 D Cache: %dK\n",
+ ecx>>24, edx>>24);
+ c->x86_cache_size=(ecx>>24)+(edx>>24);
+ }
+ if (n >= 0x80000006){
+ cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
+ printk("CPU: L2 Cache: %dK\n", ecx>>16);
+ c->x86_cache_size=(ecx>>16);
+ }
return 1;
}
@@ -882,18 +893,7 @@ static int __init amd_model(struct cpuinfo_x86 *c)
}
break;
case 6: /* An Athlon. We can trust the BIOS probably */
- {
-
- u32 ecx, edx, dummy;
- cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
- printk("L1 I Cache: %dK L1 D Cache: %dK\n",
- ecx>>24, edx>>24);
- cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
- printk("L2 Cache: %dK\n", ecx>>16);
- c->x86_cache_size = ecx>>16;
- break;
- }
-
+ break;
}
return r;
}
@@ -1021,10 +1021,18 @@ static void __init cyrix_model(struct cpuinfo_x86 *c)
/* It isnt really a PCI quirk directly, but the cure is the
same. The MediaGX has deep magic SMM stuff that handles the
SB emulation. It thows away the fifo on disable_dma() which
- is wrong and ruins the audio. */
+ is wrong and ruins the audio.
+
+ Bug2: VSA1 has a wrap bug so that using maximum sized DMA
+ causes bad things. According to NatSemi VSA2 has another
+ bug to do with 'hlt'. I've not seen any boards using VSA2
+ and X doesn't seem to support it either so who cares 8).
+ VSA1 we work around however.
+
+ */
- printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n");
- isa_dma_bridge_buggy = 1;
+ printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bugs.\n");
+ isa_dma_bridge_buggy = 2;
#endif
/* GXm supports extended cpuid levels 'ala' AMD */
if (c->cpuid_level == 2) {
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index 84e20b225..b4a7753b8 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -615,6 +615,8 @@ bad_ctc:
void __init time_init(void)
{
+ extern int x86_udelay_tsc;
+
xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;
@@ -650,6 +652,11 @@ void __init time_init(void)
if (tsc_quotient) {
fast_gettimeoffset_quotient = tsc_quotient;
use_tsc = 1;
+ /*
+ * We could be more selective here I suspect
+ * and just enable this for the next intel chips ?
+ */
+ x86_udelay_tsc = 1;
#ifndef do_gettimeoffset
do_gettimeoffset = do_fast_gettimeoffset;
#endif
diff --git a/arch/i386/lib/delay.c b/arch/i386/lib/delay.c
index 6918451a6..ca5eeb796 100644
--- a/arch/i386/lib/delay.c
+++ b/arch/i386/lib/delay.c
@@ -12,12 +12,38 @@
#include <linux/sched.h>
#include <linux/delay.h>
+#include <asm/delay.h>
#ifdef __SMP__
#include <asm/smp.h>
#endif
-void __delay(unsigned long loops)
+int x86_udelay_tsc = 0; /* Delay via TSC */
+
+
+/*
+ * Do a udelay using the TSC for any CPU that happens
+ * to have one that we trust. This could be optimised to avoid
+ * the multiply per loop but its a delay loop so who are we kidding...
+ */
+
+static void __rdtsc_delay(unsigned long loops)
+{
+ unsigned long bclock, now;
+
+ rdtscl(bclock);
+ do
+ {
+ rdtscl(now);
+ }
+ while((now-bclock) < loops);
+}
+
+/*
+ * Non TSC based delay loop for 386, 486, MediaGX
+ */
+
+static void __loop_delay(unsigned long loops)
{
int d0;
__asm__ __volatile__(
@@ -30,6 +56,14 @@ void __delay(unsigned long loops)
:"0" (loops));
}
+void __delay(unsigned long loops)
+{
+ if(x86_udelay_tsc)
+ __rdtsc_delay(loops);
+ else
+ __loop_delay(loops);
+}
+
inline void __const_udelay(unsigned long xloops)
{
int d0;
diff --git a/arch/ia64/config.in b/arch/ia64/config.in
index 3d1dd7e02..e22e1dd01 100644
--- a/arch/ia64/config.in
+++ b/arch/ia64/config.in
@@ -80,6 +80,19 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig
index a96599889..f0e36f6b2 100644
--- a/arch/ia64/defconfig
+++ b/arch/ia64/defconfig
@@ -38,12 +38,6 @@ CONFIG_PCI_OLD_PROC=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
#
# Additional Block Devices
@@ -55,6 +49,45 @@ CONFIG_PCI_OLD_PROC=y
# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_PIIX=y
+CONFIG_PIIX_TUNING=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
# CONFIG_BLK_DEV_HD is not set
#
diff --git a/arch/m68k/config.in b/arch/m68k/config.in
index 5b12f470e..52b81739e 100644
--- a/arch/m68k/config.in
+++ b/arch/m68k/config.in
@@ -152,6 +152,19 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig
index cbea6ba63..e11c32b95 100644
--- a/arch/m68k/defconfig
+++ b/arch/m68k/defconfig
@@ -56,13 +56,6 @@ CONFIG_PROC_HARDWARE=y
CONFIG_AMIGA_FLOPPY=y
CONFIG_ATARI_FLOPPY=y
# CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_IDE is not set
-# CONFIG_BLK_DEV_IDEDISK is not set
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_IDEDOUBLER is not set
# CONFIG_AMIGA_Z2RAM is not set
# CONFIG_ATARI_ACSI is not set
# CONFIG_ACSI_MULTI_LUN is not set
@@ -121,6 +114,11 @@ CONFIG_IP_NOSR=y
# CONFIG_NET_SCHED is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/mips/arc/init.c b/arch/mips/arc/init.c
index 81cf6cf74..05200cb42 100644
--- a/arch/mips/arc/init.c
+++ b/arch/mips/arc/init.c
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.4 1999/10/09 00:00:57 ralf Exp $
+/* $Id: init.c,v 1.5 2000/03/07 15:45:27 ralf Exp $
* 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.
@@ -23,7 +23,7 @@ unsigned short prom_vers, prom_rev;
extern void prom_testtree(void);
-int __init prom_init(int argc, char **argv, char **envp)
+int __init prom_init(int argc, char **argv, char **envp, int *prom_vec)
{
struct linux_promblock *pb;
diff --git a/arch/mips/config.in b/arch/mips/config.in
index c26394e33..1f70c46ad 100644
--- a/arch/mips/config.in
+++ b/arch/mips/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.43 2000/03/12 10:07:55 harald Exp $
+# $Id: config.in,v 1.44 2000/03/13 20:55:19 ralf Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -168,6 +168,19 @@ fi
source drivers/telephony/Config.in
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/mips/defconfig b/arch/mips/defconfig
index 1fda5e771..90b6b4882 100644
--- a/arch/mips/defconfig
+++ b/arch/mips/defconfig
@@ -73,12 +73,8 @@ CONFIG_KMOD=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
#
# Additional Block Devices
@@ -87,10 +83,6 @@ CONFIG_KMOD=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -148,6 +140,13 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
@@ -158,7 +157,6 @@ CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
-CONFIG_ST_EXTRA_DEVS=2
CONFIG_BLK_DEV_SR=y
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_SR_EXTRA_DEVS=2
diff --git a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22
index 1fda5e771..90b6b4882 100644
--- a/arch/mips/defconfig-ip22
+++ b/arch/mips/defconfig-ip22
@@ -73,12 +73,8 @@ CONFIG_KMOD=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
#
# Additional Block Devices
@@ -87,10 +83,6 @@ CONFIG_KMOD=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -148,6 +140,13 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
@@ -158,7 +157,6 @@ CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
-CONFIG_ST_EXTRA_DEVS=2
CONFIG_BLK_DEV_SR=y
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_SR_EXTRA_DEVS=2
diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c
index fa9e68e6f..35055ac68 100644
--- a/arch/mips/kernel/irixelf.c
+++ b/arch/mips/kernel/irixelf.c
@@ -1,4 +1,4 @@
-/* $Id: irixelf.c,v 1.25 2000/03/02 02:36:50 ralf Exp $
+/* $Id: irixelf.c,v 1.26 2000/03/07 15:45:28 ralf Exp $
*
* irixelf.c: Code to load IRIX ELF executables which conform to
* the MIPS ABI.
@@ -36,8 +36,6 @@
#include <asm/mipsregs.h>
#include <asm/prctl.h>
-#include <linux/config.h>
-
#define DLINFO_ITEMS 12
#include <linux/elf.h>
@@ -609,8 +607,7 @@ void irix_map_prda_page (void)
/* These are the functions used to load ELF style executables and shared
* libraries. There is no binary dependent code anywhere else.
*/
-static inline int do_load_irix_binary(struct linux_binprm * bprm,
- struct pt_regs * regs)
+static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct elfhdr elf_ex, interp_elf_ex;
struct dentry *interpreter_dentry;
@@ -755,14 +752,11 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
sys_close(elf_exec_fileno);
current->personality = PER_IRIX32;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
if (current->binfmt && current->binfmt->module)
__MOD_DEC_USE_COUNT(current->binfmt->module);
current->exec_domain = lookup_exec_domain(current->personality);
current->binfmt = &irix_format;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
if (current->binfmt && current->binfmt->module)
__MOD_INC_USE_COUNT(current->binfmt->module);
@@ -832,16 +826,6 @@ out_free_ph:
goto out;
}
-static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_irix_binary(bprm, regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/* This is really simpleminded and specialized - we are loading an
* a.out library that is given an ELF header.
*/
@@ -943,13 +927,11 @@ static int load_irix_library(int fd)
int retval = -EACCES;
struct file *file;
- MOD_INC_USE_COUNT;
file = fget(fd);
if (file) {
retval = do_load_irix_library(file);
fput(file);
}
- MOD_DEC_USE_COUNT;
return retval;
}
@@ -1152,10 +1134,6 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file)
elf_fpregset_t fpu; /* NT_PRFPREG */
struct elf_prpsinfo psinfo; /* NT_PRPSINFO */
-#ifndef CONFIG_BINFMT_IRIX
- MOD_INC_USE_COUNT;
-#endif
-
/* Count what's needed to dump, up to the limit of coredump size. */
segs = 0;
size = 0;
@@ -1365,9 +1343,6 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file)
end_coredump:
set_fs(fs);
-#ifndef CONFIG_BINFMT_IRIX
- MOD_DEC_USE_COUNT;
-#endif
return has_dumped;
}
diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h
index 84ab41425..d5edf1660 100644
--- a/arch/mips/kernel/syscalls.h
+++ b/arch/mips/kernel/syscalls.h
@@ -1,4 +1,4 @@
-/* $Id: syscalls.h,v 1.21 2000/02/05 06:47:08 ralf Exp $
+/* $Id: syscalls.h,v 1.22 2000/02/18 00:24:30 ralf Exp $
*
* 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
@@ -232,3 +232,5 @@ SYS(sys_stat64, 3)
SYS(sys_lstat64, 3)
SYS(sys_fstat64, 3) /* 4215 */
SYS(sys_pivot_root, 2)
+SYS(sys_mincore, 3)
+SYS(sys_madvise, 3)
diff --git a/arch/mips64/config.in b/arch/mips64/config.in
index be0501f60..5efcf06e6 100644
--- a/arch/mips64/config.in
+++ b/arch/mips64/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.15 2000/03/09 15:38:28 ralf Exp $
+# $Id: config.in,v 1.16 2000/03/13 20:55:19 ralf Exp $
#
# For a description of the syntax of this configuration file,
# see the Configure script.
@@ -111,6 +111,19 @@ fi
source drivers/telephony/Config.in
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/mips64/defconfig b/arch/mips64/defconfig
index f34f2e63a..8838fed35 100644
--- a/arch/mips64/defconfig
+++ b/arch/mips64/defconfig
@@ -63,13 +63,10 @@ CONFIG_PCI_NAMES=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
#
# Additional Block Devices
@@ -78,11 +75,6 @@ CONFIG_PCI_NAMES=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -123,6 +115,13 @@ CONFIG_SKB_LARGE=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27
index f34f2e63a..8838fed35 100644
--- a/arch/mips64/defconfig-ip27
+++ b/arch/mips64/defconfig-ip27
@@ -63,13 +63,10 @@ CONFIG_PCI_NAMES=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
#
# Additional Block Devices
@@ -78,11 +75,6 @@ CONFIG_PCI_NAMES=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -123,6 +115,13 @@ CONFIG_SKB_LARGE=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/mips64/kernel/scall_64.S b/arch/mips64/kernel/scall_64.S
index d739e50c1..af3dd1d87 100644
--- a/arch/mips64/kernel/scall_64.S
+++ b/arch/mips64/kernel/scall_64.S
@@ -1,4 +1,4 @@
-/* $Id: scall_64.S,v 1.6 2000/02/18 00:24:30 ralf Exp $
+/* $Id: scall_64.S,v 1.7 2000/02/23 00:41:00 ralf Exp $
*
* 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
@@ -345,3 +345,5 @@ sys_call_table:
PTR sys_ni_syscall
PTR sys_ni_syscall
PTR sys_pivot_root /* 4210 */
+ PTR sys_mincore
+ PTR sys_madvise
diff --git a/arch/mips64/kernel/scall_o32.S b/arch/mips64/kernel/scall_o32.S
index 9d09499c6..246fd8346 100644
--- a/arch/mips64/kernel/scall_o32.S
+++ b/arch/mips64/kernel/scall_o32.S
@@ -1,4 +1,4 @@
-/* $Id: scall_o32.S,v 1.12 2000/03/18 09:02:17 ulfc Exp $
+/* $Id: scall_o32.S,v 1.13 2000/03/18 15:08:06 ulfc Exp $
*
* 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
@@ -409,6 +409,8 @@ illegal_syscall:
sys sys_lstat64 3
sys sys_fstat64 3 /* 4210 */
sys sys_pivot_root 2
+ sys sys_mincore 3
+ sys sys_madvise 3
.endm
.macro sys function, nargs
diff --git a/arch/ppc/config.in b/arch/ppc/config.in
index 7d0ab5fa0..38d9dc72e 100644
--- a/arch/ppc/config.in
+++ b/arch/ppc/config.in
@@ -187,6 +187,19 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
if [ "$CONFIG_SCSI" != "n" ]; then
diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig
index bcab91ec6..5b5288b30 100644
--- a/arch/ppc/defconfig
+++ b/arch/ppc/defconfig
@@ -79,32 +79,6 @@ CONFIG_BOOTX_TEXT=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_BLK_DEV_IDECS is not set
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-CONFIG_BLK_DEV_IDEFLOPPY=y
-CONFIG_BLK_DEV_IDESCSI=y
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-# CONFIG_IDEPCI_SHARE_IRQ is not set
-# CONFIG_BLK_DEV_IDEDMA_PCI is not set
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDE_PMAC=y
-CONFIG_BLK_DEV_IDEDMA_PMAC=y
-CONFIG_IDEDMA_PMAC_AUTO=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
# CONFIG_BLK_CPQ_DA is not set
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
@@ -114,8 +88,6 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
# CONFIG_BLK_DEV_DAC960 is not set
# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -160,6 +132,40 @@ CONFIG_ATALK=m
# CONFIG_NET_SCHED is not set
#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=y
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_IDEDMA_PMAC_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/sh/config.in b/arch/sh/config.in
index fd858e00e..c711ee931 100644
--- a/arch/sh/config.in
+++ b/arch/sh/config.in
@@ -95,6 +95,19 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
diff --git a/arch/sh/defconfig b/arch/sh/defconfig
index ea5851d39..a3f901220 100644
--- a/arch/sh/defconfig
+++ b/arch/sh/defconfig
@@ -45,6 +45,21 @@ CONFIG_BINFMT_ELF=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
+
+#
+# Additional Block Devices
+#
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
CONFIG_BLK_DEV_IDE=y
#
@@ -63,16 +78,6 @@ CONFIG_BLK_DEV_IDEDISK=y
#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_IDE_CHIPSETS is not set
-
-#
-# Additional Block Devices
-#
-# CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_MD is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_IDE_MODES is not set
# CONFIG_BLK_DEV_HD is not set
diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c
index b75edb418..04049be7a 100644
--- a/arch/sparc/boot/piggyback.c
+++ b/arch/sparc/boot/piggyback.c
@@ -1,4 +1,4 @@
-/* $Id: piggyback.c,v 1.2 1998/12/15 12:24:43 jj Exp $
+/* $Id: piggyback.c,v 1.3 2000/03/11 00:22:26 zaitcev Exp $
Simple utility to make a single-image install kernel with initial ramdisk
for Sparc tftpbooting without need to set up nfs.
@@ -29,8 +29,18 @@
#include <sys/types.h>
#include <sys/stat.h>
-/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
- usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
+/*
+ * Note: run this on an a.out kernel (use elftoaout for it),
+ * as PROM looks for a.out image only.
+ */
+
+void usage(void)
+{
+ /* fs_img.gz is an image of initial ramdisk. */
+ fprintf(stderr, "Usage: piggyback vmlinux.aout System.map fs_img.gz\n");
+ fprintf(stderr, "\tKernel image will be modified in place.\n");
+ exit(1);
+}
void die(char *str)
{
@@ -45,7 +55,8 @@ int main(int argc,char **argv)
FILE *map;
struct stat s;
int image, tail;
-
+
+ if (argc != 4) usage();
start = end = 0;
if (stat (argv[3], &s) < 0) die (argv[3]);
map = fopen (argv[2], "r");
diff --git a/arch/sparc/config.in b/arch/sparc/config.in
index 756e531c2..adb0ebe4c 100644
--- a/arch/sparc/config.in
+++ b/arch/sparc/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.87 2000/02/27 19:34:12 davem Exp $
+# $Id: config.in,v 1.88 2000/03/13 03:40:27 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -74,10 +74,9 @@ fi
endmenu
mainmenu_option next_comment
-comment 'Floppy, IDE, and other block devices'
+comment 'Floppy and other block devices'
bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD
-define_bool CONFIG_BLK_DEV_IDE n
bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' Linear (append) mode' CONFIG_MD_LINEAR
@@ -109,6 +108,24 @@ if [ "$CONFIG_ISDN" != "n" ]; then
fi
endmenu
+
+define_bool CONFIG_IDE n
+define_bool CONFIG_BLK_DEV_IDE_MODES n
+define_bool CONFIG_BLK_DEV_HD n
+
+# mainmenu_option next_comment
+# comment 'ATA/IDE/MFM/RLL support'
+#
+# tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+#
+# if [ "$CONFIG_IDE" != "n" ]; then
+# source drivers/ide/Config.in
+# else
+# define_bool CONFIG_BLK_DEV_IDE_MODES n
+# define_bool CONFIG_BLK_DEV_HD n
+# fi
+# endmenu
+
mainmenu_option next_comment
comment 'SCSI support'
diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig
index e719d9214..de483af6e 100644
--- a/arch/sparc/defconfig
+++ b/arch/sparc/defconfig
@@ -101,7 +101,6 @@ CONFIG_KMOD=y
# Floppy, IDE, and other block devices
#
CONFIG_BLK_DEV_FD=y
-# CONFIG_BLK_DEV_IDE is not set
CONFIG_BLK_DEV_MD=y
CONFIG_MD_LINEAR=m
CONFIG_MD_STRIPED=m
@@ -170,6 +169,11 @@ CONFIG_DECNET_SIOCGIFCONF=y
# CONFIG_ISDN is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/sparc/kernel/sys_solaris.c b/arch/sparc/kernel/sys_solaris.c
index 6c46c60f0..8a9d54913 100644
--- a/arch/sparc/kernel/sys_solaris.c
+++ b/arch/sparc/kernel/sys_solaris.c
@@ -15,12 +15,14 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
+/* CHECKME: this stuff looks rather bogus */
asmlinkage int
do_solaris_syscall (struct pt_regs *regs)
{
int ret;
lock_kernel();
+ put_exec_domain(current->exec_domain);
current->personality = PER_SVR4;
current->exec_domain = lookup_exec_domain(PER_SVR4);
diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
index 1d6f208f6..262f6afdd 100644
--- a/arch/sparc/kernel/sys_sunos.c
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.114 2000/03/07 22:27:27 davem Exp $
+/* $Id: sys_sunos.c,v 1.115 2000/03/13 21:57:23 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -296,57 +296,6 @@ asmlinkage void sunos_madvise(unsigned long address, unsigned long len,
unlock_kernel();
}
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'. -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(unsigned long addr, unsigned long len, char *array)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- unsigned long limit;
- int num_pages, pnum, retval = -EINVAL;
-
- lock_kernel();
- if(addr & ~(PAGE_MASK))
- goto out;
-
- num_pages = (len / PAGE_SIZE);
- retval = -EFAULT;
- if(verify_area(VERIFY_WRITE, array, num_pages))
- goto out;
- retval = -ENOMEM;
- if((addr >= PAGE_OFFSET) || ((addr + len) > PAGE_OFFSET))
- goto out; /* I'm sure you're curious about kernel mappings.. */
-
- /* Wheee, go through pte's */
- pnum = 0;
- for(limit = addr + len; addr < limit; addr += PAGE_SIZE, pnum++) {
- pgdp = pgd_offset(current->mm, addr);
- if(pgd_none(*pgdp))
- goto out; /* As per SunOS manpage */
- pmdp = pmd_offset(pgdp, addr);
- if(pmd_none(*pmdp))
- goto out; /* As per SunOS manpage */
- ptep = pte_offset(pmdp, addr);
- if(pte_none(*ptep))
- goto out; /* As per SunOS manpage */
- /* Page in core or Swapped page? */
- __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
- }
- retval = 0; /* Success... I think... */
-out:
- unlock_kernel();
- return retval;
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S
index 8746958d7..42c072164 100644
--- a/arch/sparc/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.94 2000/02/16 07:31:30 davem Exp $
+/* $Id: systbls.S,v 1.95 2000/03/13 21:57:23 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -33,7 +33,7 @@ sys_call_table:
/*60*/ .long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
/*65*/ .long sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_geteuid
/*70*/ .long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
-/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_nis_syscall, sys_getgroups16
+/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
/*80*/ .long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
/*85*/ .long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
@@ -104,7 +104,7 @@ sunos_sys_table:
.long sunos_nosys, sunos_sbrk, sunos_sstk
.long sunos_mmap, sunos_vadvise, sys_munmap
.long sys_mprotect, sunos_madvise, sys_vhangup
- .long sunos_nosys, sunos_mincore, sys_getgroups16
+ .long sunos_nosys, sys_mincore, sys_getgroups16
.long sys_setgroups16, sys_getpgrp, sunos_setpgrp
.long sys_setitimer, sunos_nosys, sys_swapon
.long sys_getitimer, sys_gethostname, sys_sethostname
diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in
index 47c6a5afd..a1fb222c1 100644
--- a/arch/sparc64/config.in
+++ b/arch/sparc64/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.99 2000/02/27 19:34:17 davem Exp $
+# $Id: config.in,v 1.101 2000/03/13 05:49:55 jj Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -98,29 +98,22 @@ fi
tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
tristate 'Network block device support' CONFIG_BLK_DEV_NBD
-if [ "$CONFIG_PCI" = "y" ]; then
- tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
- if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
- dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
- dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
- define_bool CONFIG_BLK_DEV_IDEPCI y
- define_bool CONFIG_BLK_DEV_IDEDMA y
- define_bool CONFIG_IDEDMA_AUTO y
- define_bool CONFIG_IDEDMA_NEW_DRIVE_LISTINGS y
- define_bool CONFIG_BLK_DEV_NS87415 y
- define_bool CONFIG_BLK_DEV_CMD64X y
- define_bool CONFIG_BLK_DEV_IDE_MODES y
- fi
+if [ "$CONFIG_NET" = "y" ]; then
+ source net/Config.in
fi
-endmenu
+mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
-if [ "$CONFIG_NET" = "y" ]; then
- source net/Config.in
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
fi
+endmenu
mainmenu_option next_comment
comment 'SCSI support'
@@ -172,12 +165,16 @@ if [ "$CONFIG_SCSI" != "n" ]; then
bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
fi
+ dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI
- if [ "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
+ if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -o "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
int 'default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8
int 'maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32
int 'synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 10
bool ' enable profiling' CONFIG_SCSI_NCR53C8XX_PROFILE
+ if [ "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then
+ bool ' include support for the NCR PQS/PDS SCSI card' CONFIG_SCSI_NCR53C8XX_PQS_PDS
+ fi
if [ "$CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS" = "0" ]; then
bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
fi
@@ -219,7 +216,7 @@ if [ "$CONFIG_NET" = "y" ]; then
mainmenu_option next_comment
comment 'Ethernet (10 or 100Mbit)'
- bool 'Sun LANCE support' CONFIG_SUNLANCE
+ tristate 'Sun LANCE support' CONFIG_SUNLANCE
tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index a61d4d049..db85cfe1e 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -128,19 +128,6 @@ CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_DEV_RAM is not set
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_IDE=y
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_IDETAPE=m
-CONFIG_BLK_DEV_IDEFLOPPY=m
-# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
-CONFIG_BLK_DEV_NS87415=y
-CONFIG_BLK_DEV_CMD64X=y
-CONFIG_BLK_DEV_IDE_MODES=y
#
# Networking options
@@ -195,6 +182,71 @@ CONFIG_DECNET_SIOCGIFCONF=y
# CONFIG_NET_SCHED is not set
#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_AUTO=y
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+CONFIG_BLK_DEV_CMD64X=y
+# CONFIG_CMD64X_RAID is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+CONFIG_BLK_DEV_NS87415=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
# SCSI support
#
CONFIG_SCSI=y
@@ -228,11 +280,13 @@ CONFIG_AIC7XXX_TAGGED_QUEUEING=y
CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
CONFIG_AIC7XXX_PROC_STATS=y
CONFIG_AIC7XXX_RESET_DELAY=5
+CONFIG_SCSI_NCR53C8XX=y
CONFIG_SCSI_SYM53C8XX=y
CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4
CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
CONFIG_SCSI_NCR53C8XX_SYNC=10
# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
CONFIG_SCSI_QLOGIC_ISP=y
diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c
index 009c506a0..3e95ed9cf 100644
--- a/arch/sparc64/kernel/binfmt_aout32.c
+++ b/arch/sparc64/kernel/binfmt_aout32.c
@@ -82,8 +82,7 @@ if (file->f_op->llseek) { \
* dumping of the process results in another error..
*/
-static inline int
-do_aout32_core_dump(long signr, struct pt_regs * regs, struct file *file)
+static int aout32_core_dump(long signr, struct pt_regs *regs, struct file *file)
{
mm_segment_t fs;
int has_dumped = 0;
@@ -143,17 +142,6 @@ end_coredump:
return has_dumped;
}
-static int
-aout32_core_dump(long signr, struct pt_regs * regs, struct file * file)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_aout32_core_dump(signr, regs, file);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/*
* create_aout32_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
@@ -207,8 +195,7 @@ static u32 *create_aout32_tables(char * p, struct linux_binprm * bprm)
* libraries. There is no binary dependent code anywhere else.
*/
-static inline int do_load_aout32_binary(struct linux_binprm * bprm,
- struct pt_regs * regs)
+static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct exec ex;
struct file * file;
@@ -320,14 +307,11 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm,
}
}
beyond_if:
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
if (current->binfmt && current->binfmt->module)
__MOD_DEC_USE_COUNT(current->binfmt->module);
current->exec_domain = lookup_exec_domain(current->personality);
current->binfmt = &aout32_format;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
if (current->binfmt && current->binfmt->module)
__MOD_INC_USE_COUNT(current->binfmt->module);
@@ -358,21 +342,8 @@ beyond_if:
return 0;
}
-
-static int
-load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_aout32_binary(bprm, regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
-static inline int
-do_load_aout32_library(int fd)
+static int load_aout32_library(int fd)
{
struct file * file;
struct inode * inode;
@@ -444,17 +415,6 @@ out:
return retval;
}
-static int
-load_aout32_library(int fd)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_aout32_library(fd);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
static int __init init_aout32_binfmt(void)
{
return register_binfmt(&aout32_format);
diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
index 39a000ef3..d3a3814a8 100644
--- a/arch/sparc64/kernel/ioctl32.c
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.80 2000/02/17 06:45:09 jj Exp $
+/* $Id: ioctl32.c,v 1.82 2000/03/13 21:57:27 davem Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
@@ -34,6 +34,7 @@
#include <linux/cdrom.h>
#include <linux/loop.h>
#include <linux/auto_fs.h>
+#include <linux/devfs_fs.h>
#include <linux/tty.h>
#include <linux/vt_kern.h>
#include <linux/fb.h>
@@ -2334,6 +2335,10 @@ COMPATIBLE_IOCTL(PPPIOCSDEBUG)
COMPATIBLE_IOCTL(PPPIOCNEWUNIT)
COMPATIBLE_IOCTL(PPPIOCATTACH)
COMPATIBLE_IOCTL(PPPIOCDETACH)
+COMPATIBLE_IOCTL(PPPIOCSMRRU)
+COMPATIBLE_IOCTL(PPPIOCCONNECT)
+COMPATIBLE_IOCTL(PPPIOCDISCONN)
+COMPATIBLE_IOCTL(PPPIOCATTCHAN)
/* CDROM stuff */
COMPATIBLE_IOCTL(CDROMPAUSE)
COMPATIBLE_IOCTL(CDROMRESUME)
@@ -2517,6 +2522,11 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL)
COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
+/* DEVFS */
+COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)
+COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
+COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE)
+COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK)
/* Raw devices */
COMPATIBLE_IOCTL(RAW_SETBIND)
COMPATIBLE_IOCTL(RAW_GETBIND)
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index 1fc0b1ba5..d4ecb0f4f 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.134 2000/03/07 22:27:30 davem Exp $
+/* $Id: sys_sparc32.c,v 1.136 2000/03/13 21:57:29 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -3645,7 +3645,7 @@ struct nfsctl_arg32 {
};
union nfsctl_res32 {
- struct knfs_fh cr32_getfh;
+ __u8 cr32_getfh[NFS_FHSIZE];
u32 cr32_debug;
};
@@ -4218,3 +4218,12 @@ out_sem:
out:
return ret;
}
+
+extern asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char *vec);
+
+asmlinkage long sys32_mincore(unsigned long start, u32 __len, unsigned char *vec)
+{
+ size_t len = (size_t) __len;
+
+ return sys_mincore(start, len, vec);
+}
diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c
index 9673cdd36..f7f5964e9 100644
--- a/arch/sparc64/kernel/sys_sunos32.c
+++ b/arch/sparc64/kernel/sys_sunos32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.40 2000/03/07 22:27:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.41 2000/03/13 21:57:31 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -252,56 +252,6 @@ asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy)
unlock_kernel();
}
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'. -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(u32 __addr, u32 len, u32 u_array)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- unsigned long limit, addr = (unsigned long)__addr;
- int num_pages, pnum, retval = -EINVAL;
- char *array = (char *)A(u_array);
-
- lock_kernel();
- if(addr & ~(4096))
- goto out;
- num_pages = (len / 4096);
- retval = -EFAULT;
- if(verify_area(VERIFY_WRITE, array, num_pages))
- goto out;
- retval = -ENOMEM;
- if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000))
- goto out; /* I'm sure you're curious about kernel mappings.. */
- /* Wheee, go through pte's */
- pnum = 0;
- for(limit = addr + len; addr < limit; addr += 4096, pnum++) {
- pgdp = pgd_offset(current->mm, addr);
- if(pgd_none(*pgdp))
- goto out; /* As per SunOS manpage */
- pmdp = pmd_offset(pgdp, addr);
- if(pmd_none(*pmdp))
- goto out; /* As per SunOS manpage */
- ptep = pte_offset(pmdp, addr);
- if(pte_none(*ptep))
- goto out; /* As per SunOS manpage */
- /* Page in core or Swapped page? */
- __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
- }
- retval = 0; /* Success... I think... */
-out:
- unlock_kernel();
- return retval;
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
index 1f7ab3fef..0a0edbf82 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.68 2000/02/16 07:31:38 davem Exp $
+/* $Id: systbls.S,v 1.69 2000/03/13 21:57:28 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -34,7 +34,7 @@ sys_call_table32:
/*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_fstat64, sys_getpagesize
.word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_geteuid
/*70*/ .word sys_getegid, sys32_mmap, sys_setreuid, sys_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys_nis_syscall, sys32_getgroups16
+ .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys32_mincore, sys32_getgroups16
/*80*/ .word sys32_setgroups16, sys_getpgrp, sys_setgroups, sys32_setitimer, sys32_ftruncate64
.word sys_swapon, sys32_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .word sys_dup2, sys_setfsuid, sys32_fcntl, sys32_select, sys_setfsgid
@@ -93,7 +93,7 @@ sys_call_table:
/*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+ .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
/*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
/*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
@@ -164,7 +164,7 @@ sunos_sys_table:
.word sunos_nosys, sunos_sbrk, sunos_sstk
.word sunos_mmap, sunos_vadvise, sys_munmap
.word sys_mprotect, sunos_madvise, sys_vhangup
- .word sunos_nosys, sunos_mincore, sys32_getgroups16
+ .word sunos_nosys, sys32_mincore, sys32_getgroups16
.word sys32_setgroups16, sys_getpgrp, sunos_setpgrp
.word sys32_setitimer, sunos_nosys, sys_swapon
.word sys32_getitimer, sys_gethostname, sys_sethostname
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 9e2bd4118..b77a27236 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.22 2000/02/16 07:31:41 davem Exp $
+/* $Id: misc.c,v 1.23 2000/03/13 21:57:34 davem Exp $
* misc.c: Miscelaneous syscall emulation for Solaris
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -721,11 +721,8 @@ asmlinkage void solaris_register(void)
{
lock_kernel();
current->personality = PER_SVR4;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
current->exec_domain = lookup_exec_domain(current->personality);
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
unlock_kernel();
}
diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S
index 17562bafd..ca78499b1 100644
--- a/arch/sparc64/solaris/systbl.S
+++ b/arch/sparc64/solaris/systbl.S
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.10 2000/01/12 02:59:26 davem Exp $
+/* $Id: systbl.S,v 1.11 2000/03/13 21:57:35 davem Exp $
* systbl.S: System call entry point table for Solaris compatibility.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -142,7 +142,7 @@ solaris_sys_table:
.word solaris_unimplemented /* async 111 */
.word solaris_unimplemented /* priocntlsys 112 */
.word solaris_pathconf /* pathconf sd 113 */
- .word solaris_unimplemented /* mincore xdx 114 */
+ .word CHAIN(mincore) /* mincore d 114 */
.word solaris_mmap /* mmap xxxxdx 115 */
.word CHAIN(mprotect) /* mprotect xdx 116 */
.word CHAIN(munmap) /* munmap xd 117 */
diff --git a/drivers/Makefile b/drivers/Makefile
index 1d4653233..eca864b62 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -9,9 +9,9 @@
SUB_DIRS := block char net parport sound misc
MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o ieee1394 \
- macintosh video dio zorro fc4 usb \
- nubus tc atm pcmcia i2c telephony
+ALL_SUB_DIRS := $(SUB_DIRS) pci sgi ide scsi sbus cdrom isdn pnp i2o \
+ ieee1394 macintosh video dio zorro fc4 \
+ usb nubus tc atm pcmcia i2c telephony
ifdef CONFIG_DIO
SUB_DIRS += dio
@@ -94,6 +94,17 @@ else
endif
endif
+# If CONFIG_IDE is set, the core of ATA support will be added to the kernel,
+# but some of the low-level things may also be modules.
+ifeq ($(CONFIG_IDE),y)
+SUB_DIRS += ide
+MOD_SUB_DIRS += ide
+else
+ ifeq ($(CONFIG_IDE),m)
+ MOD_SUB_DIRS += ide
+ endif
+endif
+
# If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel,
# but some of the low-level things may also be modules.
ifeq ($(CONFIG_SCSI),y)
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index 0e29e3a2e..51a6db89c 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -16,172 +16,6 @@ if [ "$CONFIG_MAC" = "y" ]; then
bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP
fi
fi
-
-tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
-comment 'Please see Documentation/ide.txt for help/info on IDE drives'
-if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
- bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
-else
- bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
- dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
- if [ "$CONFIG_BLK_DEV_IDEDISK" != "n" ]; then
- bool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE
- fi
- dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
- dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
- dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
- comment 'IDE chipset support/bugfixes'
- if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
- bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
- if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
- bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED
- fi
- if [ "$CONFIG_ISAPNP" = "y" ]; then
- bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP
- fi
- if [ "$CONFIG_PCI" = "y" ]; then
- bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
- bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
- if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
- bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
- bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Good-Bad DMA Model-Firmware (EXPERIMENTAL)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
- define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL y
- else
- define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL n
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP
- fi
- bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
- bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AEC6210" = "y" ]; then
- bool ' AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3
- bool ' AMD Viper support' CONFIG_BLK_DEV_AMD7409
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AMD7409" = "y" ]; then
- bool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE
- fi
- fi
- bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_CMD64X" = "y" ]; then
- bool ' CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693
- fi
- bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then
- bool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA
- fi
- bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT366" = "y" ]; then
- bool ' HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP
- bool ' HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3
- fi
- if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
- bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX
- if [ "$CONFIG_BLK_DEV_PIIX" = "y" -a "$CONFIG_IDEDMA_PCI_AUTO" = "y" ]; then
- bool ' PIIXn Tuning support' CONFIG_PIIX_TUNING
- fi
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
- fi
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX
- if [ "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then
- bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" ]; then
- bool ' Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER
- fi
- fi
- if [ "$CONFIG_X86" = "y" ]; then
- bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
- bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX
- fi
- fi
- if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
- bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
- fi
- fi
- if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
- bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
- if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then
- bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC
- if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then
- bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO
- fi
- fi
- fi
- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
- bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
- if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then
- bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS
- if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
- bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO
- fi
- fi
- bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
- "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
- "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
- define_bool CONFIG_BLK_DEV_IDEDMA y
- if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
- "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
- "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
- define_bool CONFIG_IDEDMA_AUTO y
- fi
- fi
- bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
- if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
- comment 'Note: most of these also require special kernel boot parameters'
- bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
- bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
- bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
- bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
- if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
- fi
- bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
- bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
- fi
- if [ "$CONFIG_AMIGA" = "y" ]; then
- bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE
- fi
- fi
- if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA
- fi
- if [ "$CONFIG_ATARI" = "y" ]; then
- bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE
- fi
- if [ "$CONFIG_MAC" = "y" ]; then
- bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE
- fi
- fi
-fi
if [ "$CONFIG_MCA" = "y" ]; then
tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2
fi
@@ -196,8 +30,15 @@ if [ "$CONFIG_ATARI" = "y" ]; then
tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM
fi
fi
+tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+ source drivers/block/paride/Config.in
+fi
+
if [ "$CONFIG_PCI" = "y" ]; then
tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+ tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
fi
comment 'Additional Block Devices'
@@ -220,41 +61,5 @@ tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
fi
-tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
-if [ "$CONFIG_PCI" = "y" ]; then
- tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
-fi
-
-dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
-if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
- source drivers/block/paride/Config.in
-fi
-
-if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
- "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
- "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
- "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
- "$CONFIG_BLK_DEV_CMD640" = "y" -o \
- "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
- "$CONFIG_BLK_DEV_CS5530" = "y" -o \
- "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
- "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
- "$CONFIG_BLK_DEV_HPT366" = "y" -o \
- "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
- "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
- "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
- "$CONFIG_BLK_DEV_PIIX" = "y" -o \
- "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
- "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
- define_bool CONFIG_BLK_DEV_IDE_MODES y
-else
- define_bool CONFIG_BLK_DEV_IDE_MODES n
-fi
-
-if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
- define_bool CONFIG_BLK_DEV_HD y
-else
- define_bool CONFIG_BLK_DEV_HD n
-fi
endmenu
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index e969e2dd7..87c597c31 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -1021,7 +1021,7 @@ static inline int DAC_new_segment(request_queue_t *q, struct request *req,
if (req->nr_segments < max_segments) {
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
return 0;
@@ -1051,23 +1051,23 @@ static int DAC_merge_requests_fn(request_queue_t *q,
int max_segments;
DAC960_Controller_T * Controller = q->queuedata;
int total_segments = req->nr_segments + next->nr_segments;
- int same_segment;
+ int same_segment;
max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
if (__max_segments < max_segments)
max_segments = __max_segments;
- same_segment = 0;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
{
total_segments--;
- same_segment = 1;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
- q->nr_segments -= same_segment;
+ q->elevator.nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 6882d03f3..9f5c813d7 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -20,7 +20,7 @@ ALL_SUB_DIRS := $(SUB_DIRS) paride
L_TARGET := block.a
-L_OBJS := genhd.o ide-geometry.o
+L_OBJS := genhd.o elevator.o
M_OBJS :=
MOD_LIST_NAME := BLOCK_MODULES
LX_OBJS := ll_rw_blk.o blkpg.o
@@ -98,214 +98,6 @@ else
endif
endif
-#
-# IDE-STUFF
-#
-
-ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
-IDE_OBJS += aec6210.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
-IDE_OBJS += ali14xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
-IDE_OBJS += alim15x3.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
-IDE_OBJS += amd7409.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
-IDE_OBJS += buddha.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD640),y)
-IDE_OBJS += cmd640.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
-IDE_OBJS += cmd64x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CS5530),y)
-IDE_OBJS += cs5530.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
-IDE_OBJS += cy82c693.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
-IDE_OBJS += dtc2278.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
-IDE_OBJS += falconide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
-IDE_OBJS += gayle.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
-IDE_OBJS += q40ide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HD),y)
-L_OBJS += hd.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
-IDE_OBJS += hpt34x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT366),y)
-IDE_OBJS += hpt366.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
-IDE_OBJS += ht6560b.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
-IDE_OBJS += icside.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
-IDE_OBJS += ide-dma.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
-IDE_OBJS += ide-pci.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
-IDE_OBJS += ide-pnp.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
-IDE_OBJS += ide-pmac.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
-IDE_OBJS += macide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_NS87415),y)
-IDE_OBJS += ns87415.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
-IDE_OBJS += opti621.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
-IDE_OBJS += pdc202xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
-IDE_OBJS += pdc4030.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PIIX),y)
-IDE_OBJS += piix.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_QD6580),y)
-IDE_OBJS += qd6580.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
-IDE_OBJS += rapide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
-IDE_OBJS += rz1000.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
-IDE_OBJS += sis5513.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
-IDE_OBJS += sl82c105.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_TRM290),y)
-IDE_OBJS += trm290.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
-IDE_OBJS += umc8672.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
-IDE_OBJS += via82cxxx.o
-endif
-
-### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
-
-ifeq ($(CONFIG_PROC_FS),y)
-IDE_OBJS += ide-proc.o
-endif
-
-###Collect
-
-ifeq ($(CONFIG_BLK_DEV_IDE),y)
- LX_OBJS += ide.o ide-features.o
- L_OBJS += ide-probe.o $(IDE_OBJS)
-else
- ifeq ($(CONFIG_BLK_DEV_IDE),m)
- MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
- M_OBJS += ide-mod.o ide-probe-mod.o
- endif
-endif
-
-############
-
-ifeq ($(CONFIG_BLK_DEV_IDECS),y)
-L_OBJS += ide-cs.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDECS),m)
- M_OBJS += ide-cs.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
-L_OBJS += ide-disk.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
- M_OBJS += ide-disk.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDECD),y)
-L_OBJS += ide-cd.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDECD),m)
- M_OBJS += ide-cd.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
-L_OBJS += ide-tape.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
- M_OBJS += ide-tape.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
-L_OBJS += ide-floppy.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
- M_OBJS += ide-floppy.o
- endif
-endif
-
ifeq ($(CONFIG_BLK_DEV_PS2),y)
L_OBJS += ps2esdi.o
else
@@ -418,11 +210,5 @@ endif
include $(TOPDIR)/Rules.make
-ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
-
-ide-probe-mod.o: ide-probe.o ide-geometry.o
- $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
-
lvm-mod.o: lvm.o lvm-snap.o
$(LD) -r -o $@ lvm.o lvm-snap.o
diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c
index 591ff9310..c709ebdc9 100644
--- a/drivers/block/blkpg.c
+++ b/drivers/block/blkpg.c
@@ -268,6 +268,13 @@ int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
case BLKPG:
return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg);
+ case BLKELVGET:
+ return blkelvget_ioctl(&blk_get_queue(dev)->elevator,
+ (blkelv_ioctl_arg_t *) arg);
+ case BLKELVSET:
+ return blkelvset_ioctl(&blk_get_queue(dev)->elevator,
+ (blkelv_ioctl_arg_t *) arg);
+
default:
return -EINVAL;
}
diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c
new file mode 100644
index 000000000..11532f8fd
--- /dev/null
+++ b/drivers/block/elevator.c
@@ -0,0 +1,150 @@
+/*
+ * linux/drivers/block/elevator.c
+ *
+ * Block device elevator/IO-scheduler.
+ *
+ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+#include <linux/blk.h>
+#include <asm/uaccess.h>
+
+static void elevator_default(struct request * req, elevator_t * elevator,
+ struct list_head * real_head,
+ struct list_head * head, int orig_latency)
+{
+ struct list_head * entry = real_head, * point = NULL;
+ struct request * tmp;
+ int sequence = elevator->sequence;
+ int latency = orig_latency -= elevator->nr_segments, pass = 0;
+ int point_latency = 0xbeefbeef;
+
+ while ((entry = entry->prev) != head) {
+ if (!point && latency >= 0) {
+ point = entry;
+ point_latency = latency;
+ }
+ tmp = blkdev_entry_to_request(entry);
+ if (elevator_sequence_before(tmp->elevator_sequence, sequence) || !tmp->q)
+ break;
+ if (latency >= 0) {
+ if (IN_ORDER(tmp, req) ||
+ (pass && !IN_ORDER(tmp, blkdev_next_request(tmp))))
+ goto link;
+ }
+ latency += tmp->nr_segments;
+ pass = 1;
+ }
+
+ if (point) {
+ entry = point;
+ latency = point_latency;
+ }
+
+ link:
+ list_add(&req->queue, entry);
+ req->elevator_sequence = elevator_sequence(elevator, latency);
+}
+
+#ifdef ELEVATOR_DEBUG
+void elevator_debug(request_queue_t * q, kdev_t dev)
+{
+ int read_pendings = 0, nr_segments = 0;
+ elevator_t * elevator = &q->elevator;
+ struct list_head * entry = &q->queue_head;
+ static int counter;
+
+ if (counter++ % 100)
+ return;
+
+ while ((entry = entry->prev) != &q->queue_head)
+ {
+ struct request * req;
+
+ req = blkdev_entry_to_request(entry);
+ if (req->cmd != READ && req->cmd != WRITE && (req->q || req->nr_segments))
+ printk(KERN_WARNING
+ "%s: elevator req->cmd %d req->nr_segments %u req->q %p\n",
+ kdevname(dev), req->cmd, req->nr_segments, req->q);
+ if (!req->q) {
+ if (req->nr_segments)
+ printk(KERN_WARNING
+ "%s: elevator req->q NULL req->nr_segments %u\n",
+ kdevname(dev), req->nr_segments);
+ continue;
+ }
+ if (req->cmd == READ)
+ read_pendings++;
+ nr_segments += req->nr_segments;
+ }
+
+ if (read_pendings != elevator->read_pendings)
+ {
+ printk(KERN_WARNING
+ "%s: elevator read_pendings %d should be %d\n",
+ kdevname(dev), elevator->read_pendings,
+ read_pendings);
+ elevator->read_pendings = read_pendings;
+ }
+ if (nr_segments != elevator->nr_segments)
+ {
+ printk(KERN_WARNING
+ "%s: elevator nr_segments %d should be %d\n",
+ kdevname(dev), elevator->nr_segments,
+ nr_segments);
+ elevator->nr_segments = nr_segments;
+ }
+}
+#endif
+
+int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
+{
+ int ret;
+ blkelv_ioctl_arg_t output;
+
+ output.queue_ID = elevator;
+ output.read_latency = elevator->read_latency;
+ output.write_latency = elevator->write_latency;
+ output.max_bomb_segments = elevator->max_bomb_segments;
+
+ ret = -EFAULT;
+ if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
+ goto out;
+ ret = 0;
+ out:
+ return ret;
+}
+
+int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
+{
+ blkelv_ioctl_arg_t input;
+ int ret;
+
+ ret = -EFAULT;
+ if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
+ goto out;
+
+ ret = -EINVAL;
+ if (input.read_latency < 0)
+ goto out;
+ if (input.write_latency < 0)
+ goto out;
+ if (input.max_bomb_segments <= 0)
+ goto out;
+
+ elevator->read_latency = input.read_latency;
+ elevator->write_latency = input.write_latency;
+ elevator->max_bomb_segments = input.max_bomb_segments;
+
+ ret = 0;
+ out:
+ return ret;
+}
+
+void elevator_init(elevator_t * elevator)
+{
+ *elevator = ELEVATOR_DEFAULTS;
+}
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index c14591b92..63c5e4b85 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -28,8 +28,6 @@
#include <linux/module.h>
-#define DEBUG_ELEVATOR
-
/*
* MAC Floppy IWM hooks
*/
@@ -150,18 +148,6 @@ request_queue_t * blk_get_queue (kdev_t dev)
return ret;
}
-static inline int get_request_latency(elevator_t * elevator, int rw)
-{
- int latency;
-
- if (rw != READ)
- latency = elevator->write_latency;
- else
- latency = elevator->read_latency;
-
- return latency;
-}
-
void blk_cleanup_queue(request_queue_t * q)
{
memset(q, 0, sizeof(*q));
@@ -186,7 +172,7 @@ static inline int ll_new_segment(request_queue_t *q, struct request *req, int ma
{
if (req->nr_segments < max_segments) {
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
return 0;
@@ -212,18 +198,18 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req,
struct request *next, int max_segments)
{
int total_segments = req->nr_segments + next->nr_segments;
- int same_segment;
+ int same_segment;
- same_segment = 0;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
total_segments--;
- same_segment = 1;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
- q->nr_segments -= same_segment;
+ q->elevator.nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
@@ -254,7 +240,7 @@ static void generic_plug_device (request_queue_t *q, kdev_t dev)
void blk_init_queue(request_queue_t * q, request_fn_proc * rfn)
{
INIT_LIST_HEAD(&q->queue_head);
- q->elevator = ELEVATOR_DEFAULTS;
+ elevator_init(&q->elevator);
q->request_fn = rfn;
q->back_merge_fn = ll_back_merge_fn;
q->front_merge_fn = ll_front_merge_fn;
@@ -425,113 +411,6 @@ static inline void drive_stat_acct(struct request *req,
printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
}
-/* elevator */
-
-#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
-#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
-#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
-#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
-
-static inline struct list_head * seek_to_not_starving_chunk(request_queue_t * q,
- int * lat, int * starving)
-{
- int sequence = q->elevator.sequence;
- struct list_head * entry = q->queue_head.prev;
- int pos = 0;
-
- do {
- struct request * req = blkdev_entry_to_request(entry);
- if (elevator_sequence_before(req->elevator_sequence, sequence)) {
- *lat -= q->nr_segments - pos;
- *starving = 1;
- return entry;
- }
- pos += req->nr_segments;
- } while ((entry = entry->prev) != &q->queue_head);
-
- *starving = 0;
-
- return entry->next;
-}
-
-static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
-{
- if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
- req->elevator_sequence = next->elevator_sequence;
- if (req->cmd == READ)
- e->read_pendings--;
-
-}
-
-static inline int elevator_sequence(elevator_t * e, int latency)
-{
- return latency + e->sequence;
-}
-
-#define elevator_merge_before(q, req, lat) __elevator_merge((q), (req), (lat), 0)
-#define elevator_merge_after(q, req, lat) __elevator_merge((q), (req), (lat), 1)
-static inline void __elevator_merge(request_queue_t * q, struct request * req, int latency, int after)
-{
- int sequence = elevator_sequence(&q->elevator, latency);
- if (after)
- sequence -= req->nr_segments;
- if (elevator_sequence_before(sequence, req->elevator_sequence))
- req->elevator_sequence = sequence;
-}
-
-static inline void elevator_queue(request_queue_t * q,
- struct request * req,
- struct list_head * entry,
- int latency, int starving)
-{
- struct request * tmp, * __tmp;
- int __latency = latency;
-
- __tmp = tmp = blkdev_entry_to_request(entry);
-
- for (;; tmp = blkdev_next_request(tmp))
- {
- if ((latency -= tmp->nr_segments) <= 0)
- {
- tmp = __tmp;
- latency = __latency;
-
- if (starving)
- break;
-
- if (q->head_active && !q->plugged)
- {
- latency -= tmp->nr_segments;
- break;
- }
-
- list_add(&req->queue, &q->queue_head);
- goto after_link;
- }
-
- if (tmp->queue.next == &q->queue_head)
- break;
-
- {
- const int after_current = IN_ORDER(tmp,req);
- const int before_next = IN_ORDER(req,blkdev_next_request(tmp));
-
- if (!IN_ORDER(tmp,blkdev_next_request(tmp))) {
- if (after_current || before_next)
- break;
- } else {
- if (after_current && before_next)
- break;
- }
- }
- }
-
- list_add(&req->queue, &tmp->queue);
-
- after_link:
- req->elevator_sequence = elevator_sequence(&q->elevator, latency);
-}
-
/*
* add-request adds a request to the linked list.
* It disables interrupts (aquires the request spinlock) so that it can muck
@@ -542,20 +421,19 @@ static inline void elevator_queue(request_queue_t * q,
* which is important for drive_stat_acct() above.
*/
-static inline void __add_request(request_queue_t * q, struct request * req,
- int empty, struct list_head * entry,
- int latency, int starving)
+static inline void add_request(request_queue_t * q, struct request * req,
+ struct list_head * head, int latency)
{
int major;
drive_stat_acct(req, req->nr_sectors, 1);
- if (empty) {
+ if (list_empty(head)) {
req->elevator_sequence = elevator_sequence(&q->elevator, latency);
list_add(&req->queue, &q->queue_head);
return;
}
- elevator_queue(q, req, entry, latency, starving);
+ q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency);
/*
* FIXME(eric) I don't understand why there is a need for this
@@ -579,19 +457,17 @@ static inline void __add_request(request_queue_t * q, struct request * req,
/*
* Has to be called with the request spinlock aquired
*/
-static inline void attempt_merge (request_queue_t * q,
- struct request *req,
- int max_sectors,
- int max_segments)
+static void attempt_merge(request_queue_t * q,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
struct request *next;
- if (req->queue.next == &q->queue_head)
- return;
next = blkdev_next_request(req);
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)
+ if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem)
return;
/*
* If we are not allowed to merge these requests, then
@@ -611,54 +487,28 @@ static inline void attempt_merge (request_queue_t * q,
wake_up (&wait_for_request);
}
-static inline void elevator_debug(request_queue_t * q, kdev_t dev)
+static inline void attempt_back_merge(request_queue_t * q,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
-#ifdef DEBUG_ELEVATOR
- int read_pendings = 0, nr_segments = 0;
- elevator_t * elevator = &q->elevator;
- struct list_head * entry = &q->queue_head;
- static int counter;
-
- if (counter++ % 100)
+ if (&req->queue == q->queue_head.prev)
return;
-
- while ((entry = entry->next) != &q->queue_head)
- {
- struct request * req;
-
- req = blkdev_entry_to_request(entry);
- if (!req->q)
- continue;
- if (req->cmd == READ)
- read_pendings++;
- nr_segments += req->nr_segments;
- }
-
- if (read_pendings != elevator->read_pendings)
- {
- printk(KERN_WARNING
- "%s: elevator read_pendings %d should be %d\n",
- kdevname(dev), elevator->read_pendings,
- read_pendings);
- elevator->read_pendings = read_pendings;
- }
- if (nr_segments != q->nr_segments)
- {
- printk(KERN_WARNING
- "%s: elevator nr_segments %d should be %d\n",
- kdevname(dev), q->nr_segments,
- nr_segments);
- q->nr_segments = nr_segments;
- }
-#endif
+ attempt_merge(q, req, max_sectors, max_segments);
}
-static inline void elevator_account_request(request_queue_t * q, struct request * req)
+static inline void attempt_front_merge(request_queue_t * q,
+ struct list_head * head,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
- q->elevator.sequence++;
- if (req->cmd == READ)
- q->elevator.read_pendings++;
- q->nr_segments++;
+ struct list_head * prev;
+
+ prev = req->queue.prev;
+ if (head == prev)
+ return;
+ attempt_merge(q, blkdev_entry_to_request(prev), max_sectors, max_segments);
}
static inline void __make_request(request_queue_t * q, int rw,
@@ -667,11 +517,13 @@ static inline void __make_request(request_queue_t * q, int rw,
int major = MAJOR(bh->b_rdev);
unsigned int sector, count;
int max_segments = MAX_SEGMENTS;
- struct request * req, * prev;
+ struct request * req;
int rw_ahead, max_req, max_sectors;
unsigned long flags;
- int orig_latency, latency, __latency, starving, __starving, empty;
- struct list_head * entry, * __entry = NULL;
+
+ int orig_latency, latency, starving, sequence;
+ struct list_head * entry, * head = &q->queue_head;
+ elevator_t * elevator;
count = bh->b_size >> 9;
sector = bh->b_rsector;
@@ -758,7 +610,8 @@ static inline void __make_request(request_queue_t * q, int rw,
*/
max_sectors = get_max_sectors(bh->b_rdev);
- __latency = orig_latency = get_request_latency(&q->elevator, rw);
+ elevator = &q->elevator;
+ orig_latency = elevator_request_latency(elevator, rw);
/*
* Now we acquire the request spinlock, we have to be mega careful
@@ -767,46 +620,42 @@ static inline void __make_request(request_queue_t * q, int rw,
spin_lock_irqsave(&io_request_lock,flags);
elevator_debug(q, bh->b_rdev);
- empty = 0;
- if (list_empty(&q->queue_head)) {
- empty = 1;
+ if (list_empty(head)) {
q->plug_device_fn(q, bh->b_rdev); /* is atomic */
goto get_rq;
}
/* avoid write-bombs to not hurt iteractiveness of reads */
- if (rw != READ && q->elevator.read_pendings)
- max_segments = q->elevator.max_bomb_segments;
-
- entry = seek_to_not_starving_chunk(q, &__latency, &starving);
+ if (rw != READ && elevator->read_pendings)
+ max_segments = elevator->max_bomb_segments;
- __entry = entry;
- __starving = starving;
+ sequence = elevator->sequence;
+ latency = orig_latency - elevator->nr_segments;
+ starving = 0;
+ entry = head;
- latency = __latency;
-
- if (q->head_active && !q->plugged) {
- /*
- * The scsi disk and cdrom drivers completely remove the request
- * from the queue when they start processing an entry. For this
- * reason it is safe to continue to add links to the top entry
- * for those devices.
- *
- * All other drivers need to jump over the first entry, as that
- * entry may be busy being processed and we thus can't change
- * it.
- */
- if (entry == q->queue_head.next) {
- latency -= blkdev_entry_to_request(entry)->nr_segments;
- if ((entry = entry->next) == &q->queue_head)
- goto get_rq;
- starving = 0;
- }
- }
+ /*
+ * The scsi disk and cdrom drivers completely remove the request
+ * from the queue when they start processing an entry. For this
+ * reason it is safe to continue to add links to the top entry
+ * for those devices.
+ *
+ * All other drivers need to jump over the first entry, as that
+ * entry may be busy being processed and we thus can't change
+ * it.
+ */
+ if (q->head_active && !q->plugged)
+ head = head->next;
- prev = NULL;
- do {
+ while ((entry = entry->prev) != head && !starving) {
req = blkdev_entry_to_request(entry);
+ if (!req->q)
+ break;
+ latency += req->nr_segments;
+ if (elevator_sequence_before(req->elevator_sequence, sequence))
+ starving = 1;
+ if (latency < 0)
+ continue;
if (req->sem)
continue;
@@ -833,20 +682,20 @@ static inline void __make_request(request_queue_t * q, int rw,
* this
*/
if(!(q->back_merge_fn)(q, req, bh, max_segments))
- continue;
+ break;
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
req->nr_sectors += count;
drive_stat_acct(req, count, 0);
- elevator_merge_after(q, req, latency);
+ elevator_merge_after(elevator, req, latency);
/* Can we now merge this req with the next? */
- attempt_merge(q, req, max_sectors, max_segments);
+ attempt_back_merge(q, req, max_sectors, max_segments);
/* or to the beginning? */
} else if (req->sector - count == sector) {
- if (!prev && starving)
- continue;
+ if (starving)
+ break;
/*
* The merge_fn is a more advanced way
* of accomplishing the same task. Instead
@@ -860,7 +709,7 @@ static inline void __make_request(request_queue_t * q, int rw,
* this
*/
if(!(q->front_merge_fn)(q, req, bh, max_segments))
- continue;
+ break;
bh->b_reqnext = req->bh;
req->bh = bh;
req->buffer = bh->b_data;
@@ -869,10 +718,9 @@ static inline void __make_request(request_queue_t * q, int rw,
req->nr_sectors += count;
drive_stat_acct(req, count, 0);
- elevator_merge_before(q, req, latency);
+ elevator_merge_before(elevator, req, latency);
- if (prev)
- attempt_merge(q, prev, max_sectors, max_segments);
+ attempt_front_merge(q, head, req, max_sectors, max_segments);
} else
continue;
@@ -880,9 +728,7 @@ static inline void __make_request(request_queue_t * q, int rw,
spin_unlock_irqrestore(&io_request_lock,flags);
return;
- } while (prev = req,
- (latency -= req->nr_segments) >= 0 &&
- (entry = entry->next) != &q->queue_head);
+ }
/* find an unused request. */
get_rq:
@@ -899,31 +745,10 @@ get_rq:
req = __get_request_wait(max_req, bh->b_rdev);
spin_lock_irqsave(&io_request_lock,flags);
- /* lock got dropped so revalidate elevator */
- empty = 1;
- if (!list_empty(&q->queue_head)) {
- empty = 0;
- __latency = orig_latency;
- __entry = seek_to_not_starving_chunk(q, &__latency, &__starving);
- }
- }
- /*
- * Dont start the IO if the buffer has been
- * invalidated meanwhile. (we have to do this
- * within the io request lock and atomically
- * before adding the request, see buffer.c's
- * insert_into_queues_exclusive() function.
- */
- if (!test_bit(BH_Req, &bh->b_state)) {
- req->rq_status = RQ_INACTIVE;
- spin_unlock_irqrestore(&io_request_lock,flags);
- /*
- * A fake 'everything went ok' completion event.
- * The bh doesnt matter anymore, but we should not
- * signal errors to RAID levels.
- */
- bh->b_end_io(bh, 1);
- return;
+ /* revalidate elevator */
+ head = &q->queue_head;
+ if (q->head_active && !q->plugged)
+ head = head->next;
}
/* fill up the request-info, and add it to the queue */
@@ -939,8 +764,8 @@ get_rq:
req->bh = bh;
req->bhtail = bh;
req->q = q;
- __add_request(q, req, empty, __entry, __latency, __starving);
- elevator_account_request(q, req);
+ add_request(q, req, head, orig_latency);
+ elevator_account_request(elevator, req);
spin_unlock_irqrestore(&io_request_lock, flags);
return;
@@ -1166,10 +991,10 @@ int __init blk_dev_init(void)
#ifdef CONFIG_ISP16_CDI
isp16_init();
#endif CONFIG_ISP16_CDI
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_IDE)
ide_init(); /* this MUST precede hd_init */
#endif
-#ifdef CONFIG_BLK_DEV_HD
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_HD)
hd_init();
#endif
#ifdef CONFIG_BLK_DEV_PS2
diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c
index e7df93ac6..fe0d33a77 100644
--- a/drivers/cdrom/aztcd.c
+++ b/drivers/cdrom/aztcd.c
@@ -1726,10 +1726,9 @@ int __init aztcd_init(void)
{ printk("aztcd: no AZTECH CD-ROM drive found\n");
return -EIO;
}
- for (count = 0; count < AZT_TIMEOUT; count++);
- { count=count*2; /* delay a bit */
- count=count/2;
- }
+
+ for (count = 0; count < AZT_TIMEOUT; count++);
+
if ((st=getAztStatus())==-1)
{ printk("aztcd: Drive Status Error Status=%x\n",st);
return -EIO;
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 18f867c04..21720c81a 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -219,7 +219,7 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
fi
dep_tristate ' Zoran ZR36057/36060 Video For Linux' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV $CONFIG_PCI
dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN
- dep_tristate ' Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI
+ dep_tristate ' Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C
fi
endmenu
diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c
index 19e252968..66dfc0fe5 100644
--- a/drivers/char/bttv.c
+++ b/drivers/char/bttv.c
@@ -719,12 +719,14 @@ static void init_PXC200(struct bttv *btv)
/* ----------------------------------------------------------------------- */
+/* for some vendors it is just the PCI ID */
static struct VENDOR {
int id;
char *name;
} vendors[] = {
{ 0x0001, "ATI Technologies Inc" },
{ 0x10b4, "STB Systems Inc" },
+ { 0x1118, "Terratec" },
{ 0x13eb, "Hauppauge Computer Works Inc" },
{ 0x1461, "Avermedia" },
{ 0x1850, "Chronos" },
@@ -743,6 +745,7 @@ static struct CARD {
} cards[] = {
{ 0x0001, 0x1002, BTTV_HAUPPAUGE878, "TV Wonder" },
{ 0x10b4, 0x2636, BTTV_HAUPPAUGE878, "???" },
+ { 0x1118, 0x153b, BTTV_TERRATVALUE, "TV Value" },
{ 0x13eb, 0x0070, BTTV_HAUPPAUGE878, "WinTV" },
{ 0x1461, 0x0002, BTTV_AVERMEDIA98, "TVCapture 98" },
{ 0x1850, 0x1851, BTTV_CHRONOS_VS2, "Video Shuttle II" },
@@ -2018,6 +2021,7 @@ static int bttv_open(struct video_device *dev, int flags)
btv->gbuf[i].stat = GBUFFER_UNUSED;
burst(0);
+ set_pll(btv);
btv->user++;
up(&btv->lock);
MOD_INC_USE_COUNT;
@@ -2235,6 +2239,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
if (btv->win.norm != v.mode) {
btv->win.norm = v.mode;
down(&btv->lock);
+ set_pll(btv);
make_vbitab(btv);
bt848_set_winsize(btv);
up(&btv->lock);
@@ -2866,6 +2871,7 @@ static struct video_device vbi_template=
static int radio_open(struct video_device *dev, int flags)
{
struct bttv *btv = (struct bttv *)(dev-1);
+ unsigned long v;
down(&btv->lock);
if (btv->user)
@@ -2873,6 +2879,8 @@ static int radio_open(struct video_device *dev, int flags)
btv->user++;
btv->radio = 1;
+ v = 400*16;
+ call_i2c_clients(btv,VIDIOCSFREQ,&v);
call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
bt848_muxsel(btv,0);
up(&btv->lock);
diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h
index ab3d88ab5..3a2cd7e21 100644
--- a/drivers/char/bttv.h
+++ b/drivers/char/bttv.h
@@ -21,7 +21,7 @@
#ifndef _BTTV_H_
#define _BTTV_H_
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,21)
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,22)
#include <linux/types.h>
#include <linux/wait.h>
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 9d8bf6cb0..93e710188 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -369,7 +369,7 @@ static u8 build_attr(int currcons, u8 _color, u8 _intensity, u8 _blink, u8 _unde
static void update_attr(int currcons)
{
attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
- video_erase_char = (build_attr(currcons, color, 1, 0, 0, decscnm) << 8) | ' ';
+ video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
}
/* Note: inverting the screen twice should revert to the original state */
diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c
index 865681956..ebf0671f4 100644
--- a/drivers/char/drm/auth.c
+++ b/drivers/char/drm/auth.c
@@ -1,6 +1,5 @@
/* auth.c -- IOCTLs for authentication -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -23,9 +22,9 @@
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c
index a71d6dde9..1bb7bd612 100644
--- a/drivers/char/drm/bufs.c
+++ b/drivers/char/drm/bufs.c
@@ -1,8 +1,7 @@
/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 12:11:11 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,13 +23,12 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
#define __NO_VERSION__
-#include <linux/config.h>
#include "drmP.h"
#include "linux/un.h"
diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c
index d7f8bdf2b..a8919d83d 100644
--- a/drivers/char/drm/context.c
+++ b/drivers/char/drm/context.c
@@ -1,6 +1,5 @@
/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c
index ea08a859e..0ec14ede5 100644
--- a/drivers/char/drm/dma.c
+++ b/drivers/char/drm/dma.c
@@ -1,6 +1,5 @@
/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:39 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.7 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c
index c26953c1d..19e5da3b7 100644
--- a/drivers/char/drm/drawable.c
+++ b/drivers/char/drm/drawable.c
@@ -1,6 +1,5 @@
/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h
index 320db51eb..fe0f8defe 100644
--- a/drivers/char/drm/drm.h
+++ b/drivers/char/drm/drm.h
@@ -1,6 +1,5 @@
/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 17:11:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
* Acknowledgements:
* Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index fce2df7ec..3a371c23d 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -1,6 +1,5 @@
/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:06:49 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -50,6 +49,10 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
#include "drm.h"
#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then
@@ -475,6 +478,7 @@ extern int drm_fasync(int fd, struct file *filp, int on);
extern ssize_t drm_read(struct file *filp, char *buf, size_t count,
loff_t *off);
extern int drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
/* Mapping support (vm.c) */
#if LINUX_VERSION_CODE < 0x020317
diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c
index 24b17356b..a823db356 100644
--- a/drivers/char/drm/fops.c
+++ b/drivers/char/drm/fops.c
@@ -1,8 +1,7 @@
/* fops.c -- File operations for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 10:26:26 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +23,9 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
@@ -222,3 +222,13 @@ int drm_write_string(drm_device_t *dev, const char *s)
wake_up_interruptible(&dev->buf_readers);
return 0;
}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+
+ poll_wait(filp, &dev->buf_readers, wait);
+ if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+ return 0;
+}
diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c
index 3b1592180..1f8c0a7de 100644
--- a/drivers/char/drm/gamma_dma.c
+++ b/drivers/char/drm/gamma_dma.c
@@ -1,6 +1,5 @@
/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:37 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.9 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c
index 028772f26..6df4440f3 100644
--- a/drivers/char/drm/gamma_drv.c
+++ b/drivers/char/drm/gamma_drv.c
@@ -1,8 +1,7 @@
/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,12 +23,11 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "gamma_drv.h"
EXPORT_SYMBOL(gamma_init);
@@ -52,6 +50,7 @@ static struct file_operations gamma_fops = {
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice gamma_misc = {
diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h
index 15e77dca9..a87655cb9 100644
--- a/drivers/char/drm/gamma_drv.h
+++ b/drivers/char/drm/gamma_drv.h
@@ -1,6 +1,5 @@
/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c
index f416a99af..7a0115e86 100644
--- a/drivers/char/drm/init.c
+++ b/drivers/char/drm/init.c
@@ -1,6 +1,5 @@
/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c
index 886ef661c..13bb60659 100644
--- a/drivers/char/drm/ioctl.c
+++ b/drivers/char/drm/ioctl.c
@@ -1,6 +1,5 @@
/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
* Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c
index b84561f2e..212ed18ed 100644
--- a/drivers/char/drm/lists.c
+++ b/drivers/char/drm/lists.c
@@ -1,6 +1,5 @@
/* lists.c -- Buffer list handling routines -*- linux-c -*-
* Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -154,7 +153,7 @@ int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
buf->list = DRM_LIST_FREE;
do {
old = bl->next;
- bl->next = old;
+ buf->next = old;
prev = cmpxchg(&bl->next, old, buf);
if (++count > DRM_LOOPING_LIMIT) {
DRM_ERROR("Looping\n");
diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c
index e8c1eff10..2523eb21a 100644
--- a/drivers/char/drm/lock.c
+++ b/drivers/char/drm/lock.c
@@ -1,6 +1,5 @@
/* lock.c -- IOCTLs for locking -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c
index af8d510b5..a778a1539 100644
--- a/drivers/char/drm/memory.c
+++ b/drivers/char/drm/memory.c
@@ -1,6 +1,5 @@
/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
* Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 10:28:18 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c
index 33a5b20e8..4d5d1a964 100644
--- a/drivers/char/drm/proc.c
+++ b/drivers/char/drm/proc.c
@@ -1,6 +1,5 @@
/* proc.c -- /proc support for DRM -*- linux-c -*-
* Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 09:44:16 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -79,26 +78,26 @@ int drm_proc_init(drm_device_t *dev)
struct proc_dir_entry *ent;
int i, j;
- drm_root = create_proc_entry("graphics", S_IFDIR, NULL);
+ drm_root = create_proc_entry("dri", S_IFDIR, NULL);
if (!drm_root) {
- DRM_ERROR("Cannot create /proc/graphics\n");
+ DRM_ERROR("Cannot create /proc/dri\n");
return -1;
}
/* Instead of doing this search, we should
- add some global support for /proc/graphics. */
+ add some global support for /proc/dri. */
for (i = 0; i < 8; i++) {
- sprintf(drm_slot_name, "graphics/%d", i);
+ sprintf(drm_slot_name, "dri/%d", i);
drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
if (!drm_dev_root) {
DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
}
if (drm_dev_root->nlink == 2) break;
drm_dev_root = NULL;
}
if (!drm_dev_root) {
- DRM_ERROR("Cannot find slot in /proc/graphics\n");
+ DRM_ERROR("Cannot find slot in /proc/dri\n");
return -1;
}
@@ -112,7 +111,7 @@ int drm_proc_init(drm_device_t *dev)
remove_proc_entry(drm_proc_list[i].name,
drm_dev_root);
remove_proc_entry(drm_slot_name, NULL);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
return -1;
}
ent->read_proc = drm_proc_list[i].f;
@@ -135,7 +134,7 @@ int drm_proc_cleanup(void)
}
remove_proc_entry(drm_slot_name, NULL);
}
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
remove_proc_entry(DRM_NAME, NULL);
}
drm_root = drm_dev_root = NULL;
diff --git a/drivers/char/drm/tdfx_context.c b/drivers/char/drm/tdfx_context.c
index 0c3c541d0..22bf59ff3 100644
--- a/drivers/char/drm/tdfx_context.c
+++ b/drivers/char/drm/tdfx_context.c
@@ -1,6 +1,5 @@
/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
* Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:39:56 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c
index f56e2af95..82b2ac9a2 100644
--- a/drivers/char/drm/tdfx_drv.c
+++ b/drivers/char/drm/tdfx_drv.c
@@ -1,8 +1,7 @@
/* tdfx.c -- tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:35 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -23,17 +22,15 @@
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI$
- * $XFree86$
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "tdfx_drv.h"
-EXPORT_SYMBOL(tdfx_init);
-EXPORT_SYMBOL(tdfx_cleanup);
#define TDFX_NAME "tdfx"
#define TDFX_DESC "tdfx"
@@ -53,6 +50,7 @@ static struct file_operations tdfx_fops = {
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice tdfx_misc = {
@@ -542,6 +540,12 @@ int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
#endif
}
}
+
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY/4;
+ }
+
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
#if DRM_DMA_HISTOGRAM
@@ -582,6 +586,11 @@ int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
}
}
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY;
+ }
+
return 0;
}
diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h
index bdff05ee1..4c0c3282b 100644
--- a/drivers/char/drm/tdfx_drv.h
+++ b/drivers/char/drm/tdfx_drv.h
@@ -1,6 +1,5 @@
/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:38:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,9 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c
index d649a6e75..b4c4c5bbf 100644
--- a/drivers/char/drm/vm.c
+++ b/drivers/char/drm/vm.c
@@ -1,6 +1,5 @@
/* vm.c -- Memory mapping for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:54:35 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 95a6c4370..212525df7 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -18,90 +18,12 @@
#include <linux/serial.h>
#include <linux/mm.h>
#include <asm/semaphore.h>
-#include <linux/version.h>
-
-
-#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300 /* less than 2.2.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source
- work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2,
- and provide for compatibility stuff here if possible. */
-
-/* Some 200 days (on intel) */
-#define MAX_SCHEDULE_TIMEOUT ((long)(~0UL>>1))
-
-
-#ifndef MODULE
-
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c)
-{
- memcpy_fromfs(to, from, c);
- return 0;
-}
-
-
-#define capable(x) suser()
-
-#define queue_task queue_task_irq_off
-#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current) (current->signal & ~current->blocked)
-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2) (((long)t1-t2) > 0)
-
-#define test_and_set_bit(nr, addr) set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI -1
-#define ASYNC_SPD_WARP -1
-
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
- to the "name" field that does exist. As long as the assignments are
- done in the right order, there is nothing to worry about. */
-#define driver_name name
-
-
-/* Should be in a header somewhere. */
-#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN 15 /* in hardware - output and input */
-#endif
-
-#endif
-
-#ifndef TWO_ZERO
-/* This include is new with 2.2 (and required!) */
#include <asm/uaccess.h>
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is
- the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#include "generic_serial.h"
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/generic_serial.h>
+#define DEBUG
static char * tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);
@@ -118,8 +40,6 @@ int gs_debug = 0;
#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n")
#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n")
-
-
#if NEW_WRITE_LOCKING
#define DECL /* Nothing */
#define LOCKIT down (& port->port_write_sem);
@@ -130,6 +50,28 @@ int gs_debug = 0;
#define RELEASEIT restore_flags (flags)
#endif
+#define RS_EVENT_WRITE_WAKEUP 1
+
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+ int i, j, ch;
+
+ for (i=0;i<len;i+=16) {
+ printk ("%08x ", (int) addr+i);
+ for (j=0;j<16;j++) {
+ printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+ }
+ for (j=0;j<16;j++) {
+ ch = addr[j+i];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ }
+}
+#else
+#define my_hd(addr,len)
+#endif
void gs_put_char(struct tty_struct * tty, unsigned char ch)
@@ -393,16 +335,19 @@ int gs_real_chars_in_buffer(struct tty_struct *tty)
if (!tty) return 0;
port = tty->driver_data;
+ if (!port->rd) return 0;
+ if (!port->rd->chars_in_buffer) return 0;
+
func_exit ();
return port->xmit_cnt + port->rd->chars_in_buffer (port);
}
-static void gs_wait_tx_flushed (void * ptr, int timeout)
+static int gs_wait_tx_flushed (void * ptr, int timeout)
{
struct gs_port *port = ptr;
long end_jiffies;
- int jiffies_to_transmit, charsleft;
+ int jiffies_to_transmit, charsleft = 0, rv = 0;
int to, rcib;
func_enter();
@@ -416,7 +361,7 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
func_exit();
- return; /* This is an error which we don't know how to handle. */
+ return -EINVAL; /* This is an error which we don't know how to handle. */
}
gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 1\n");
@@ -426,8 +371,8 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
if(rcib <= 0) {
gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
- func_exit();
- return;
+ func_exit();
+ return rv;
}
gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 3\n");
@@ -460,14 +405,18 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(jiffies_to_transmit);
- if (signal_pending (current))
+ if (signal_pending (current)) {
+ gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
+ rv = -EINTR;
break;
+ }
}
gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft);
current->state = TASK_RUNNING;
func_exit();
+ return rv;
}
@@ -541,7 +490,7 @@ void gs_start(struct tty_struct * tty)
void gs_shutdown_port (struct gs_port *port)
{
long flags;
-
+ func_enter();
if (!(port->flags & ASYNC_INITIALIZED))
return;
@@ -560,6 +509,7 @@ void gs_shutdown_port (struct gs_port *port)
port->flags &= ~ASYNC_INITIALIZED;
restore_flags (flags);
+ func_exit();
}
@@ -746,8 +696,10 @@ void gs_close(struct tty_struct * tty, struct file * filp)
func_exit();
return;
}
+
if (!port->tty) {
- printk (KERN_WARNING "gs: Odd: port->tty is NULL\n");
+ /* This seems to happen when this is called from vhangup. */
+ gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n");
port->tty = tty;
}
@@ -771,6 +723,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
port->count = 0;
}
if (port->count) {
+ gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count);
restore_flags(flags);
func_exit ();
return;
@@ -802,6 +755,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
port->rd->disable_rx_interrupts (port);
+ /* close has no way of returning "EINTR", so discard return value */
if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
gs_wait_tx_flushed (port, port->closing_wait);
@@ -812,8 +766,12 @@ void gs_close(struct tty_struct * tty, struct file * filp)
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
+
port->event = 0;
+ port->rd->close (port);
+ port->rd->shutdown_port (port);
port->tty = 0;
+
if (port->blocked_open) {
if (port->close_delay) {
current->state = TASK_INTERRUPTIBLE;
@@ -825,8 +783,6 @@ void gs_close(struct tty_struct * tty, struct file * filp)
ASYNC_CLOSING | ASYNC_INITIALIZED);
wake_up_interruptible(&port->close_wait);
- port->rd->close (port);
- port->rd->shutdown_port (port);
restore_flags(flags);
func_exit ();
}
@@ -842,7 +798,7 @@ void gs_set_termios (struct tty_struct * tty,
struct termios * old_termios)
{
struct gs_port *port = tty->driver_data;
- int baudrate, tmp;
+ int baudrate, tmp, rv;
struct termios *tiosp;
func_enter();
@@ -867,7 +823,7 @@ void gs_set_termios (struct tty_struct * tty,
&& (tiosp->c_line == old_termios->c_line)
&& (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) {
gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n");
- return;
+ return /* 0 */;
}
} else
gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: "
@@ -923,9 +879,11 @@ void gs_set_termios (struct tty_struct * tty,
/* We should really wait for the characters to be all sent before
changing the settings. -- CAL */
- gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ if (rv < 0) return /* rv */;
- port->rd->set_real_termios(port);
+ rv = port->rd->set_real_termios(port);
+ if (rv < 0) return /* rv */;
if ((!old_termios ||
(old_termios->c_cflag & CRTSCTS)) &&
@@ -943,7 +901,7 @@ void gs_set_termios (struct tty_struct * tty,
#endif
func_exit();
- return;
+ return /* 0 */;
}
@@ -1067,3 +1025,15 @@ void gs_getserial(struct gs_port *port, struct serial_struct *sp)
copy_to_user(sp, &sio, sizeof(struct serial_struct));
}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return 0;
+}
+
+int cleanup_module (void)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 3bf3fbf28..e9a34fbf3 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -26,9 +26,6 @@
#include <asm/io.h>
#include <asm/pgalloc.h>
-#ifdef CONFIG_VIDEO_BT848
-extern int i2c_init(void);
-#endif
#ifdef CONFIG_I2C
extern int i2c_init_all(void);
#endif
@@ -52,7 +49,6 @@ extern int videodev_init(void);
#endif
#ifdef CONFIG_FB
extern void fbmem_init(void);
-extern void fbconsole_init(void);
#endif
#ifdef CONFIG_PROM_CONSOLE
extern void prom_con_init(void);
@@ -621,7 +617,6 @@ int __init chr_dev_init(void)
#endif
#if defined (CONFIG_FB)
fbmem_init();
- fbconsole_init();
#endif
#if defined (CONFIG_PROM_CONSOLE)
prom_con_init();
@@ -665,9 +660,6 @@ int __init chr_dev_init(void)
#ifdef CONFIG_FTAPE
ftape_init();
#endif
-#ifdef CONFIG_VIDEO_BT848
- i2c_init();
-#endif
#if defined(CONFIG_ADB)
adbdev_init();
#endif
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index d3687d9f6..53939959f 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -123,6 +123,22 @@ static struct file_operations misc_fops = {
open: misc_open,
};
+/**
+ * misc_register - register a miscellaneous device
+ * @misc: device structure
+ *
+ * Register a miscellaneous device with the kernel. If the minor
+ * number is set to MISC_DYNAMIC_MINOR a minor number is assigned
+ * and placed in the minor field of the structure. For other cases
+ * the minor number requested is used.
+ *
+ * The structure passed is linked into the kernel and may not be
+ * destroyed until it has been unregistered
+ *
+ * A zero is returned on success and a negative errno code for
+ * failure.
+ */
+
int misc_register(struct miscdevice * misc)
{
static devfs_handle_t devfs_handle = NULL;
@@ -158,6 +174,16 @@ int misc_register(struct miscdevice * misc)
return 0;
}
+/**
+ * misc_deregister - unregister a miscellaneous device
+ * @misc: device to unregister
+ *
+ * Unregister a miscellaneous device that was previously
+ * successfully registered with misc_register. Success
+ * is indicated by a zero return, a negative errno code
+ * indicates an error.
+ */
+
int misc_deregister(struct miscdevice * misc)
{
int i = misc->minor;
diff --git a/drivers/char/mixcomwd.c b/drivers/char/mixcomwd.c
index 199818ebf..5894353e3 100644
--- a/drivers/char/mixcomwd.c
+++ b/drivers/char/mixcomwd.c
@@ -24,10 +24,13 @@
* Version 0.3.1 (99/06/22):
* - allow module removal while internal timer is active,
* print warning about probable reset
+ *
+ * Version 0.4 (99/11/15):
+ * - support for one more type board
*
*/
-#define VERSION "0.3.1"
+#define VERSION "0.4"
#include <linux/module.h>
#include <linux/config.h>
@@ -46,11 +49,13 @@
static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
#define MIXCOM_WATCHDOG_OFFSET 0xc10
-#define MIXCOM_ID1 0x11
-#define MIXCOM_ID2 0x13
+#define MIXCOM_ID 0x11
+#define FLASHCOM_WATCHDOG_OFFSET 0x4
+#define FLASHCOM_ID 0x18
static int mixcomwd_opened;
-static int mixcomwd_port;
+
+static int watchdog_port;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
static int mixcomwd_timer_alive;
@@ -59,7 +64,7 @@ static struct timer_list mixcomwd_timer;
static void mixcomwd_ping(void)
{
- outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET);
+ outb_p(55,watchdog_port);
return;
}
@@ -183,40 +188,61 @@ static int __init mixcomwd_checkcard(int port)
{
int id;
- if(check_region(port,1)) {
+ if(check_region(port+MIXCOM_WATCHDOG_OFFSET,1)) {
return 0;
}
id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
- if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) {
+ if(id!=MIXCOM_ID) {
return 0;
}
return 1;
}
-
+static int __init flashcom_checkcard(int port)
+{
+ int id;
+
+ if(check_region(port + FLASHCOM_WATCHDOG_OFFSET,1)) {
+ return 0;
+ }
+
+ id=inb_p(port + FLASHCOM_WATCHDOG_OFFSET);
+ if(id!=FLASHCOM_ID) {
+ return 0;
+ }
+ return 1;
+ }
+
void __init mixcomwd_init(void)
{
int i;
int found=0;
- for (i = 0; mixcomwd_ioports[i] != 0; i++) {
+ for (i = 0; !found && mixcomwd_ioports[i] != 0; i++) {
if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
found = 1;
- mixcomwd_port = mixcomwd_ioports[i];
- break;
+ watchdog_port = mixcomwd_ioports[i] + MIXCOM_WATCHDOG_OFFSET;
}
}
-
+
+ /* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */
+ for (i = 0x300; !found && i < 0x380; i+=0x8) {
+ if (flashcom_checkcard(i)) {
+ found = 1;
+ watchdog_port = i + FLASHCOM_WATCHDOG_OFFSET;
+ }
+ }
+
if (!found) {
printk("mixcomwd: No card detected, or port not available.\n");
return;
}
- request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog");
-
+ request_region(watchdog_port,1,"MixCOM watchdog");
+
misc_register(&mixcomwd_miscdev);
- printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port);
+ printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",VERSION,watchdog_port);
}
#ifdef MODULE
@@ -236,7 +262,7 @@ void cleanup_module(void)
mixcomwd_timer_alive=0;
}
#endif
- release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1);
+ release_region(watchdog_port,1);
misc_deregister(&mixcomwd_miscdev);
}
#endif
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 0da69c55c..f447dbbbc 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -17,6 +17,10 @@
*
* This file may be redistributed under the terms of the GNU Public
* License.
+ *
+ * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of
+ * the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
+ * who actually finally proved there really was a race.
*/
#include <linux/types.h>
@@ -59,11 +63,18 @@
static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
+ unsigned long flags;
+ /*
+ * The problem of stomping on the buffers ends here.
+ * Why didn't anyone see this one comming? --AJK
+ */
+ spin_lock_irqsave(&tty->read_lock, flags);
if (tty->read_cnt < N_TTY_BUF_SIZE) {
tty->read_buf[tty->read_head] = c;
tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
tty->read_cnt++;
}
+ spin_unlock_irqrestore(&tty->read_lock, flags);
}
/*
@@ -86,7 +97,11 @@ static void check_unthrottle(struct tty_struct * tty)
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
check_unthrottle(tty);
@@ -114,14 +129,19 @@ void n_tty_flush_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;
+ unsigned long flags;
+ ssize_t n = 0;
- return (tty->canon_head > tty->read_tail) ?
+ spin_lock_irqsave(&tty->read_lock, flags);
+ if (!tty->icanon) {
+ n = tty->read_cnt;
+ } else if (tty->canon_data) {
+ n = (tty->canon_head > tty->read_tail) ?
tty->canon_head - tty->read_tail :
tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
}
- return tty->read_cnt;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
+ return n;
}
/*
@@ -283,6 +303,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
{
enum { ERASE, WERASE, KILL } kill_type;
int head, seen_alnums;
+ unsigned long flags;
if (tty->read_head == tty->canon_head) {
/* opost('\a', tty); */ /* what do you think? */
@@ -294,15 +315,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
finish_erasing(tty);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -324,8 +349,10 @@ static void eraser(unsigned char c, struct tty_struct *tty)
else if (seen_alnums)
break;
}
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = head;
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!tty->erasing) {
@@ -658,11 +685,13 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
+ unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
+ spin_lock_irqsave(&tty->read_lock, cpuflags);
i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head));
memcpy(tty->read_buf + tty->read_head, cp, i);
@@ -676,6 +705,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
+ spin_unlock_irqrestore(&tty->read_lock, cpuflags);
} else {
for (i=count, p = cp, f = fp; i; i--, p++) {
if (f)
@@ -850,15 +880,20 @@ static inline int copy_from_read_buf(struct tty_struct *tty,
{
int retval;
ssize_t n;
+ unsigned long flags;
retval = 0;
+ spin_lock_irqsave(&tty->read_lock, flags);
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
mb();
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
@@ -875,6 +910,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
ssize_t retval = 0;
ssize_t size;
long timeout;
+ unsigned long flags;
do_it_again:
@@ -993,9 +1029,11 @@ do_it_again:
eol = test_and_clear_bit(tty->read_tail,
&tty->read_flags);
c = tty->read_buf[tty->read_tail];
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (!eol || (c != __DISABLED_CHAR)) {
put_user(c, b++);
@@ -1094,7 +1132,9 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
nr -= num;
if (nr == 0)
break;
+ current->state = TASK_RUNNING;
get_user(c, b);
+ current->state = TASK_INTERRUPTIBLE;
if (opost(c, tty) < 0)
break;
b++; nr--;
@@ -1102,7 +1142,9 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
} else {
+ current->state = TASK_RUNNING;
c = tty->driver.write(tty, 1, b, nr);
+ current->state = TASK_INTERRUPTIBLE;
if (c < 0) {
retval = c;
goto break_out;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 59853da1f..4336009fc 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -537,6 +537,17 @@ static int pp_release (struct inode * inode, struct file * file)
unsigned int minor = MINOR (inode->i_rdev);
struct pp_struct *pp = file->private_data;
+ if (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
+ if (!(pp->flags & PP_CLAIMED)) {
+ parport_claim_or_block (pp->pdev);
+ pp->flags |= PP_CLAIMED;
+ }
+ parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
+ printk (KERN_DEBUG CHRDEV
+ "%x: negotiated back to compatibility mode because "
+ "user-space forgot\n", minor);
+ }
+
if (pp->flags & PP_CLAIMED) {
parport_release (pp->pdev);
printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 8f25926e9..9331852e1 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -174,7 +174,9 @@ static int pty_write(struct tty_struct * tty, int from_user,
}
up(&tty->flip.pty_sem);
} else {
- c = MIN(count, to->ldisc.receive_room(to));
+ c = to->ldisc.receive_room(to);
+ if (c > count)
+ c = count;
to->ldisc.receive_buf(to, buf, 0, c);
}
diff --git a/drivers/char/radio-gemtek.c b/drivers/char/radio-gemtek.c
index 523ac0955..8b53dbd0d 100644
--- a/drivers/char/radio-gemtek.c
+++ b/drivers/char/radio-gemtek.c
@@ -266,7 +266,7 @@ static int __init gemtek_init(void)
{
if(io==-1)
{
- printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n");
+ printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
return -EINVAL;
}
@@ -299,7 +299,7 @@ static int __init gemtek_init(void)
MODULE_AUTHOR("Jonas Munsin");
MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
MODULE_PARM(io, "i");
-MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (or 0x248 for the combined sound/radiocard))");
+MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
EXPORT_NO_SYMBOLS;
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index 9c3f2e2e5..2d160acfd 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -1648,8 +1648,13 @@ static void change_speed(struct async_struct *info,
serial_outp(info, UART_FCR, fcr); /* set fcr */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
info->LCR = cval; /* Save LCR */
- if (info->state->type != PORT_16750)
+ if (info->state->type != PORT_16750) {
+ if (fcr & UART_FCR_ENABLE_FIFO) {
+ /* emulated UARTs (Lucent Venus 167x) need two steps */
+ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
serial_outp(info, UART_FCR, fcr); /* set fcr */
+ }
restore_flags(flags);
}
@@ -4515,9 +4520,24 @@ int __init rs_init(void)
}
/*
- * register_serial and unregister_serial allows for serial ports to be
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
+
+/**
+ * register_serial - configure a 16x50 serial port at runtime
+ * @req: request structure
+ *
+ * Configure the serial port specified by the request. If the
+ * port exists and is in use an error is returned. If the port
+ * is not currently in the table it is added.
+ *
+ * The port is then probed and if neccessary the IRQ is autodetected
+ * If this fails an error is returned.
+ *
+ * On success the port is ready to use and the line number is returned.
+ */
+
int register_serial(struct serial_struct *req)
{
int i;
@@ -4575,7 +4595,7 @@ int register_serial(struct serial_struct *req)
if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
state->irq = detect_uart_irq(state);
- printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+ printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
state->line + SERIAL_DEV_OFFSET,
state->iomem_base ? "iomem" : "port",
state->iomem_base ? (unsigned long)state->iomem_base :
@@ -4588,6 +4608,15 @@ int register_serial(struct serial_struct *req)
return state->line + SERIAL_DEV_OFFSET;
}
+/**
+ * unregister_serial - deconfigure a 16x50 serial port
+ * @line: line to deconfigure
+ *
+ * The port specified is deconfigured and its resources are freed. Any
+ * user of the port is disconnected as if carrier was dropped. Line is
+ * the port number returned by register_serial.
+ */
+
void unregister_serial(int line)
{
unsigned long flags;
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index 7caf1058a..1b8583034 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -4,7 +4,7 @@
* This driver will also support the older SI, and XIO cards.
*
*
- * (C) 1998 R.E.Wolff@BitWizard.nl
+ * (C) 1998 - 2000 R.E.Wolff@BitWizard.nl
*
* Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous
* version of this driver. Some fragments may have been copied. (none
@@ -33,6 +33,16 @@
*
* Revision history:
* $Log: sx.c,v $
+ * Revision 1.32 2000/03/07 90:00:00 wolff,pvdl
+ * - Fixed some sx_dprintk typos
+ * - added detection for an invalid board/module configuration
+ *
+ * Revision 1.31 2000/03/06 12:00:00 wolff,pvdl
+ * - Added support for EISA
+ *
+ * Revision 1.30 2000/01/21 17:43:06 wolff
+ * - Added support for SX+
+ *
* Revision 1.26 1999/08/05 15:22:14 wolff
* - Port to 2.3.x
* - Reformatted to Linus' liking.
@@ -185,8 +195,8 @@
* */
-#define RCS_ID "$Id: sx.c,v 1.26 1999/08/05 15:22:14 wolff Exp $"
-#define RCS_REV "$Revision: 1.26 $"
+#define RCS_ID "$Id: sx.c,v 1.32 2000/03/07 17:01:02 wolff, pvdl Exp $"
+#define RCS_REV "$Revision: 1.32 $"
#include <linux/module.h>
@@ -221,6 +231,9 @@
#include "sxboards.h"
#include "sxwindow.h"
+#include <linux/generic_serial.h>
+#include <linux/compatmac.h>
+#include "sx.h"
/* I don't think that this driver can handle more than 256 ports on
@@ -228,149 +241,16 @@
if you want more than 4 boards. */
-/* ************************************************************** */
-/* * This section can be removed when 2.0 becomes outdated.... * */
-/* ************************************************************** */
-
-
-#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300 /* less than 2.3.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source
- work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2,
- and provide for compatibility stuff here if possible. */
-
-#include <linux/bios32.h>
-
-#define Get_user(a,b) a = get_user(b)
-#define Put_user(a,b) 0,put_user(a,b)
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c)
-{
- memcpy_fromfs(to, from, c);
- return 0;
-}
-
-#define pci_present pcibios_present
-#define pci_read_config_word pcibios_read_config_word
-#define pci_read_config_dword pcibios_read_config_dword
-
-static inline unsigned char get_irq (unsigned char bus, unsigned char fn)
-{
- unsigned char t;
- pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t);
- return t;
-}
-
-static inline void *ioremap(unsigned long base, long length)
-{
- if (base < 0x100000) return (void *)base;
- return vremap (base, length);
-}
-
-#define my_iounmap(x, b) (((long)x<0x100000)?0:vfree ((void*)x))
-
-#define capable(x) suser()
-
-#define queue_task queue_task_irq_off
-#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current) (current->signal & ~current->blocked)
-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2) (((long)t1-t2) > 0)
-
-
-#define test_and_set_bit(nr, addr) set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI -1
-#define ASYNC_SPD_WARP -1
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
- to the "name" field that does exist. As long as the assignments are
- done in the right order, there is nothing to worry about. */
-#define driver_name name
-
-/* Should be in a header somewhere. They are in tty.h on 2.2 */
-#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN 15 /* in hardware - output and input */
-
-/* The return type of a "close" routine. */
-#define INT void
-#define NO_ERROR /* Nothing */
-
-#else
-
-/* The 2.2.x compatibility section. */
-#include <asm/uaccess.h>
-
-
-#define Get_user(a,b) get_user(a,b)
-#define Put_user(a,b) put_user(a,b)
-#define get_irq(pdev) pdev->irq
-
-#define INT int
-#define NO_ERROR 0
-
-#define my_iounmap(x,b) (iounmap((char *)(b)))
-
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is
- the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#undef RS_EVENT_WRITE_WAKEUP
-#define RS_EVENT_WRITE_WAKEUP 0
-
-
-#include "generic_serial.h"
-#include "sx.h"
-
-
-/* ************************************************************** */
-/* * End of compatibility section.. * */
-/* ************************************************************** */
-
-
/* Why the hell am I defining these here? */
#define SX_TYPE_NORMAL 1
#define SX_TYPE_CALLOUT 2
-#ifndef SX_NORMAL_MAJOR
-/* This allows overriding on the compiler commandline, or in a "major.h"
- include or something like that */
-#define SX_NORMAL_MAJOR 32
-#define SX_CALLOUT_MAJOR 33
-#endif
-
#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8
#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
#endif
-
/* Configurable options:
(Don't be too sure that it'll work if you toggle them) */
@@ -410,19 +290,17 @@ static void sx_disable_rx_interrupts (void * ptr);
static void sx_enable_rx_interrupts (void * ptr);
static int sx_get_CD (void * ptr);
static void sx_shutdown_port (void * ptr);
-static void sx_set_real_termios (void *ptr);
+static int sx_set_real_termios (void *ptr);
static void sx_hungup (void *ptr);
static void sx_close (void *ptr);
static int sx_chars_in_buffer (void * ptr);
static int sx_init_board (struct sx_board *board);
static int sx_init_portstructs (int nboards, int nports);
static int sx_fw_ioctl (struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
+ unsigned int cmd, unsigned long arg);
static int sx_fw_open(struct inode *inode, struct file *filp);
static INT sx_fw_release(struct inode *inode, struct file *filp);
static int sx_init_drivers(void);
-void my_hd (unsigned char *addr, int len);
-
static struct tty_driver sx_driver, sx_callout_driver;
@@ -458,11 +336,13 @@ int sx_slowpoll = 0;
int sx_maxints = 100;
/* These are the only open spaces in my computer. Yours may have more
- or less.... */
+ or less.... -- REW
+ duh: Card at 0xa0000 is possible on HP Netserver?? -- pvdl
+*/
int sx_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000,
0xc8000, 0xd8000, 0xe8000};
int si_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000,
- 0xc8000, 0xd8000, 0xe8000};
+ 0xc8000, 0xd8000, 0xe8000, 0xa0000};
#define NR_SX_ADDRS (sizeof(sx_probe_addrs)/sizeof (int))
#define NR_SI_ADDRS (sizeof(si_probe_addrs)/sizeof (int))
@@ -474,6 +354,8 @@ int sx_irqmask = -1;
#ifndef TWO_ZERO
#ifdef MODULE
+MODULE_PARM(sx_probe_addrs, "i");
+MODULE_PARM(si_probe_addrs, "i");
MODULE_PARM(sx_poll, "i");
MODULE_PARM(sx_slowpoll, "i");
MODULE_PARM(sx_maxints, "i");
@@ -580,6 +462,27 @@ static inline int sx_paranoia_check(struct sx_port const * port,
#define TIMEOUT_2 1000000
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+ int i, j, ch;
+
+ for (i=0;i<len;i+=16) {
+ printk ("%08x ", (int) addr+i);
+ for (j=0;j<16;j++) {
+ printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+ }
+ for (j=0;j<16;j++) {
+ ch = addr[j+i];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ }
+}
+#endif
+
+
+
/* This needs redoing for Alpha -- REW -- Done. */
inline void write_sx_byte (struct sx_board *board, int offset, u8 byte)
@@ -675,6 +578,8 @@ int sx_reset (struct sx_board *board)
printk (KERN_INFO "sx: Card doesn't respond to reset....\n");
return 0;
}
+ } else if (IS_EISA_BOARD(board)) {
+ outb(board->irq<<4, board->eisa_base+0xc02);
} else {
/* Gory details of the SI/ISA board */
write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_SET);
@@ -746,9 +651,12 @@ int sx_start_board (struct sx_board *board)
{
if (IS_SX_BOARD (board)) {
write_sx_byte (board, SX_CONFIG, SX_CONF_BUSEN);
+ } else if (IS_EISA_BOARD(board)) {
+ write_sx_byte(board, SI2_EISA_OFF, SI2_EISA_VAL);
+ outb((board->irq<<4)|4, board->eisa_base+0xc02);
} else {
/* Don't bug me about the clear_set.
- I haven't the foggiest idea what it's about -- REW*/
+ I haven't the foggiest idea what it's about -- REW */
write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_CLEAR);
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
}
@@ -769,6 +677,8 @@ int sx_start_interrupts (struct sx_board *board)
write_sx_byte (board, SX_CONFIG, SX_IRQ_REG_VAL (board) |
SX_CONF_BUSEN |
SX_CONF_HOSTIRQ);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base+0xc03);
} else {
switch (board->irq) {
case 11:write_sx_byte (board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET);break;
@@ -834,6 +744,18 @@ int mod_compat_type (int module_type)
return module_type >> 4;
}
+static void sx_reconfigure_port(struct sx_port *port)
+{
+ if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
+ if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
+ printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
+ }
+ } else {
+ sx_dprintk (SX_DEBUG_TERMIOS,
+ "sx: Not sending reconfigure: port isn't open (%02x).\n",
+ sx_read_channel_byte (port, hi_hstat));
+ }
+}
static void sx_setsignals (struct sx_port *port, int dtr, int rts)
{
@@ -954,7 +876,7 @@ static void sx_set_baud (struct sx_port *port)
/* Simon Allen's version of this routine was 225 lines long. 85 is a lot
better. -- REW */
-static void sx_set_real_termios (void *ptr)
+static int sx_set_real_termios (void *ptr)
{
struct sx_port *port = ptr;
@@ -1008,16 +930,7 @@ static void sx_set_real_termios (void *ptr)
sx_write_channel_byte (port, hi_txoff, STOP_CHAR (port->gs.tty));
sx_write_channel_byte (port, hi_rxoff, STOP_CHAR (port->gs.tty));
- if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
- if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
- printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
- }
- } else {
- sx_dprintk (SX_DEBUG_TERMIOS,
- "sx: Not sending reconfigure: port isn't open (%02x).\n",
- sx_read_channel_byte (port, hi_hstat));
- }
-
+ sx_reconfigure_port(port);
/* Tell line discipline whether we will do input cooking */
if(I_OTHER(port->gs.tty)) {
@@ -1045,6 +958,7 @@ static void sx_set_real_termios (void *ptr)
O_OTHER(port->gs.tty));
/* port->c_dcd = sx_get_CD (port); */
func_exit ();
+ return 0;
}
@@ -1102,7 +1016,7 @@ void sx_transmit_chars (struct sx_port *port)
/* Don't copy pas the end of the source buffer */
if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail)
- c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
+ c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) \n",
c, SERIAL_XMIT_SIZE- port->gs.xmit_tail);
@@ -1331,6 +1245,9 @@ static void sx_interrupt (int irq, void *ptr, struct pt_regs *regs)
sx_write_board_word (board, cc_int_pending, 0);
if (IS_SX_BOARD (board)) {
write_sx_byte (board, SX_RESET_IRQ, 1);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base+0xc03);
+ write_sx_word(board, 8, 0);
} else {
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR);
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
@@ -1474,6 +1391,7 @@ static void sx_shutdown_port (void * ptr)
port->gs.flags &= ~ GS_ACTIVE;
if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
sx_setsignals (port, 0, 0);
+ sx_reconfigure_port(port);
}
func_exit();
@@ -1711,7 +1629,7 @@ int do_memtest_w (struct sx_board *board, int min, int max)
static int sx_fw_ioctl (struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
int rc = 0;
int *descr = (int *)arg, i;
@@ -1754,7 +1672,11 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
board = &boards[arg];
break;
case SXIO_GET_TYPE:
- rc = IS_SX_BOARD (board)? SX_TYPE_SX:SX_TYPE_SI;
+ rc = -ENOENT; /* If we manage to miss one, return error. */
+ if (IS_SX_BOARD (board)) rc = SX_TYPE_SX;
+ if (IS_CF_BOARD (board)) rc = SX_TYPE_CF;
+ if (IS_SI_BOARD (board)) rc = SX_TYPE_SI;
+ if (IS_EISA_BOARD (board)) rc = SX_TYPE_SI;
sx_dprintk (SX_DEBUG_FIRMWARE, "returning type= %d\n", rc);
break;
case SXIO_DO_RAMTEST:
@@ -1786,7 +1708,7 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
for (i=0;i<nbytes;i += SX_CHUNK_SIZE) {
copy_from_user (tmp, (char *)data+i,
(i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
- memcpy_toio ((char *) (board->base + offset + i), tmp,
+ memcpy_toio ((char *) (board->base2 + offset + i), tmp,
(i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
}
@@ -1830,6 +1752,9 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
case SXIO_GETGSDEBUG:
rc = gs_debug;
break;
+ case SXIO_GETNPORTS:
+ rc = sx_nports;
+ break;
default:
printk (KERN_WARNING "Unknown ioctl on firmware device (%x).\n", cmd);
break;
@@ -1886,6 +1811,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : -1),
((ival & TIOCM_RTS) ? 1 : -1));
+ sx_reconfigure_port(port);
}
break;
case TIOCMBIC:
@@ -1894,6 +1820,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 0 : -1),
((ival & TIOCM_RTS) ? 0 : -1));
+ sx_reconfigure_port(port);
}
break;
case TIOCMSET:
@@ -1902,6 +1829,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : 0),
((ival & TIOCM_RTS) ? 1 : 0));
+ sx_reconfigure_port(port);
}
break;
@@ -1980,13 +1908,17 @@ static int sx_init_board (struct sx_board *board)
board->flags |= SX_BOARD_INITIALIZED;
+ if (read_sx_byte (board, 0))
+ /* CF boards may need this. */
+ write_sx_byte(board,0, 0);
+
/* This resets the processor again, to make sure it didn't do any
foolish things while we were downloading the image */
if (!sx_reset (board))
return 0;
sx_start_board (board);
-
+ udelay (10);
if (!sx_busy_wait_neq (board, 0, 0xff, 0)) {
printk (KERN_ERR "sx: Ooops. Board won't initialize.\n");
return 0;
@@ -2050,7 +1982,8 @@ static int sx_init_board (struct sx_board *board)
chans=0;
break;
}
- if (IS_SI_BOARD(board) && (mod_compat_type(type) == 4)) {
+ if ((IS_EISA_BOARD(board) ||
+ IS_SI_BOARD(board)) && (mod_compat_type(type) == 4)) {
printk (KERN_ERR "sx: This is an invalid configuration.\n"
"Don't use SXDCs on an SI/XIO adapter.\n");
chans=0;
@@ -2147,52 +2080,56 @@ int probe_sx (struct sx_board *board)
int i;
func_enter();
- sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n",
- board->base + SX_VPD_ROM);
- if (sx_debug & SX_DEBUG_PROBE)
- my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
+ if (!IS_CF_BOARD (board)) {
+ sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n",
+ board->base + SX_VPD_ROM);
- p = (char *) &vpdp;
- for (i=0;i< sizeof (struct vpd_prom);i++)
- *p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
- if (sx_debug & SX_DEBUG_PROBE)
- my_hd ((char *)&vpdp, 0x20);
+ p = (char *) &vpdp;
+ for (i=0;i< sizeof (struct vpd_prom);i++)
+ *p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
- sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd ((char *)&vpdp, 0x20);
- if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
- sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n",
- vpdp.identifier);
- return 0;
+ sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+
+ if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
+ sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n",
+ vpdp.identifier);
+ return 0;
+ }
}
printheader ();
- printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
- printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ",
- vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
- printk ( "Manufactured: %d/%d\n",
- 1970 + vpdp.myear, vpdp.mweek);
+ if (!IS_CF_BOARD (board)) {
+ printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
+ printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ",
+ vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
+ printk ( "Manufactured: %d/%d\n",
+ 1970 + vpdp.myear, vpdp.mweek);
- if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
- (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
- /* This might be a bit harsh. This was the primary reason the
- SX/ISA card didn't work at first... */
- printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
- return (0);
- }
+ if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
+ (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
+ /* This might be a bit harsh. This was the primary reason the
+ SX/ISA card didn't work at first... */
+ printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
+ return (0);
+ }
- if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
- if (board->base & 0x8000) {
- printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
- printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+ if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
+ if (board->base & 0x8000) {
+ printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
+ printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+ }
}
}
-
board->nports = -1;
/* This resets the processor, and keeps it off the bus. */
@@ -2225,9 +2162,11 @@ int probe_si (struct sx_board *board)
if (sx_debug & SX_DEBUG_PROBE)
my_hd ((char *)(board->base + SI2_ISA_ID_BASE), 0x8);
- for (i=0;i<8;i++) {
- if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
- return 0;
+ if (!IS_EISA_BOARD(board)) {
+ for (i=0;i<8;i++) {
+ if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
+ return 0;
+ }
}
}
@@ -2449,7 +2388,7 @@ void fix_sx_pci (PDEV, struct sx_board *board)
unsigned int t;
#define CNTRL_REG_OFFSET 0x50
-#define CNTRL_REG_GOODVALUE 0x00260000
+#define CNTRL_REG_GOODVALUE 0x18260000
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase);
hwbase &= PCI_BASE_ADDRESS_MEM_MASK;
@@ -2472,6 +2411,7 @@ int sx_init(void)
{
int i;
int found = 0;
+ int eisa_slot;
struct sx_board *board;
#ifdef CONFIG_PCI
@@ -2518,22 +2458,35 @@ int sx_init(void)
tshort = (tint >> 16) & 0xffff;
sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x.\n", tint);
/* sx_dprintk (SX_DEBUG_PROBE, "pdev = %d/%d (%x)\n", pdev, tint); */
- if (tshort != 0x0200) {
+ if ((tshort != 0x0200) && (tshort != 0x0300)) {
sx_dprintk (SX_DEBUG_PROBE, "But it's not an SX card (%d)...\n",
tshort);
continue;
}
board = &boards[found];
- pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint);
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= (tshort == 0x200)?SX_PCI_BOARD:
+ SX_CFPCI_BOARD;
+
+ /* CF boards use base address 3.... */
+ if (IS_CF_BOARD (board))
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_3,
+ &tint);
+ else
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2,
+ &tint);
board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK;
- board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
+ board->base2 =
+ board->base = (ulong) ioremap(board->hw_base, WINDOW_LEN (board));
+ /* Most of the stuff on the CF board is offset by
+ 0x18000 .... */
+ if (IS_CF_BOARD (board)) board->base += 0x18000;
+
board->irq = get_irq (pdev);
- board->flags &= ~SX_BOARD_TYPE;
- board->flags |= SX_PCI_BOARD;
- sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d).\n",
- tint, boards[found].base, board->irq);
+ sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d) %x.\n",
+ tint, boards[found].base, board->irq, board->flags);
if (probe_sx (board)) {
found++;
@@ -2547,6 +2500,7 @@ int sx_init(void)
for (i=0;i<NR_SX_ADDRS;i++) {
board = &boards[found];
board->hw_base = sx_probe_addrs[i];
+ board->base2 =
board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
board->flags &= ~SX_BOARD_TYPE;
board->flags |= SX_ISA_BOARD;
@@ -2562,6 +2516,7 @@ int sx_init(void)
for (i=0;i<NR_SI_ADDRS;i++) {
board = &boards[found];
board->hw_base = si_probe_addrs[i];
+ board->base2 =
board->base = (ulong) ioremap(board->hw_base, SI2_ISA_WINDOW_LEN);
board->flags &= ~SX_BOARD_TYPE;
board->flags |= SI_ISA_BOARD;
@@ -2574,6 +2529,34 @@ int sx_init(void)
}
}
+ sx_dprintk(SX_DEBUG_PROBE, "Probing for EISA cards\n");
+ for(eisa_slot=0x1000; eisa_slot<0x10000; eisa_slot+=0x1000)
+ {
+ if((inb(eisa_slot+0xc80)==0x4d) &&
+ (inb(eisa_slot+0xc81)==0x98))
+ {
+ sx_dprintk(SX_DEBUG_PROBE, "%s : Signature found in EISA slot %d, Product %d Rev %d\n",
+ "XIO", (eisa_slot>>12), inb(eisa_slot+0xc82), inb(eisa_slot+0xc83));
+
+ board = &boards[found];
+ board->eisa_base = eisa_slot;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SI_EISA_BOARD;
+
+ board->hw_base = (((inb(0xc01+eisa_slot) << 8) + inb(0xc00+eisa_slot)) << 16);
+ board->base2 =
+ board->base = (ulong) ioremap(board->hw_base, SI2_EISA_WINDOW_LEN);
+
+ sx_dprintk(SX_DEBUG_PROBE, "IO hw_base address: %x\n", board->hw_base);
+ sx_dprintk(SX_DEBUG_PROBE, "base: %x\n", board->base);
+ board->irq = inb(board->eisa_base+0xc02)>>4;
+ sx_dprintk(SX_DEBUG_PROBE, "IRQ: %d\n", board->irq);
+
+ probe_si(board);
+
+ found++;
+ }
+ }
if (found) {
printk (KERN_INFO "sx: total of %d boards detected.\n", found);
@@ -2588,7 +2571,7 @@ int sx_init(void)
}
-
+#ifdef MODULE
void cleanup_module(void)
{
int i;
@@ -2622,52 +2605,4 @@ void cleanup_module(void)
kfree (sx_termios_locked);
func_exit();
}
-
-
-#ifdef DEBUG
-void my_hd (unsigned char *addr, int len)
-{
- int i, j, ch;
-
- for (i=0;i<len;i+=16) {
- printk ("%08x ", (int) addr+i);
- for (j=0;j<16;j++) {
- printk ("%02x %s", addr[j+i], (j==7)?" ":"");
- }
- for (j=0;j<16;j++) {
- ch = addr[j+i];
- printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
- }
- printk ("\n");
- }
-}
#endif
-
-#ifdef MODULE
-#undef func_enter
-#undef func_exit
-
-#include "generic_serial.c"
-#endif
-
-
-/*
- * Anybody who knows why this doesn't work for me, please tell me -- REW.
- * Snatched from scsi.c (fixed one spelling error):
- * Overrides for Emacs so that we follow Linus' tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/char/sx.h b/drivers/char/sx.h
index e046482e4..662b27ca2 100644
--- a/drivers/char/sx.h
+++ b/drivers/char/sx.h
@@ -37,7 +37,9 @@ struct sx_port {
struct sx_board {
int magic;
unsigned int base;
+ unsigned int base2;
unsigned int hw_base;
+ int eisa_base;
int port_base; /* Number of the first port */
struct sx_port *ports;
int nports;
@@ -65,19 +67,27 @@ struct vpd_prom {
#define MOD_RS232DB25MALE 0x0a
#endif
-
-#define SX_BOARD_PRESENT 0x00000001
+#define SI_ISA_BOARD 0x00000001
#define SX_ISA_BOARD 0x00000002
#define SX_PCI_BOARD 0x00000004
-#define SI_ISA_BOARD 0x00000008
-#define SX_BOARD_INITIALIZED 0x00000010
-#define SX_IRQ_ALLOCATED 0x00000020
+#define SX_CFPCI_BOARD 0x00000008
+#define SX_CFISA_BOARD 0x00000010
+#define SI_EISA_BOARD 0x00000020
+
+#define SX_BOARD_PRESENT 0x00001000
+#define SX_BOARD_INITIALIZED 0x00002000
+#define SX_IRQ_ALLOCATED 0x00004000
+
+#define SX_BOARD_TYPE 0x000000ff
-#define SX_BOARD_TYPE (SX_ISA_BOARD|SX_PCI_BOARD|SI_ISA_BOARD)
+#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_CFPCI_BOARD | \
+ SX_ISA_BOARD | SX_CFISA_BOARD))
-#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_ISA_BOARD))
#define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD)
+#define IS_EISA_BOARD(board) (board->flags & SI_EISA_BOARD)
+
+#define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD))
#define SERIAL_TYPE_NORMAL 1
@@ -168,6 +178,7 @@ struct vpd_prom {
#define SXIO_DO_RAMTEST SPXL(0x07)
#define SXIO_SETGSDEBUG SPXL(0x08)
#define SXIO_GETGSDEBUG SPXL(0x09)
+#define SXIO_GETNPORTS SPXL(0x0a)
#ifndef SXCTL_MISC_MINOR
@@ -175,6 +186,19 @@ struct vpd_prom {
#define SXCTL_MISC_MINOR 167
#endif
+#ifndef SX_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define SX_NORMAL_MAJOR 32
+#define SX_CALLOUT_MAJOR 33
+#endif
+
+
#define SX_TYPE_SX 0x01
#define SX_TYPE_SI 0x02
+#define SX_TYPE_CF 0x03
+
+
+#define WINDOW_LEN(board) (IS_CF_BOARD(board)?0x20000:SX_WINDOW_LEN)
+/* Need a #define for ^^^^^^^ !!! */
diff --git a/drivers/char/sxboards.h b/drivers/char/sxboards.h
index 45636c9e9..2f0f5428e 100644
--- a/drivers/char/sxboards.h
+++ b/drivers/char/sxboards.h
@@ -159,6 +159,7 @@
#define SI2_EISA_OFF 0x42
#define SI2_EISA_VAL 0x01
+#define SI2_EISA_WINDOW_LEN 0x10000
/*****************************************************************************
*********************************** **********************************
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index ed504dcfe..e1e504b79 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1979,6 +1979,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->tq_hangup.routine = do_tty_hangup;
tty->tq_hangup.data = tty;
sema_init(&tty->atomic_read, 1);
+ spin_lock_init(&tty->read_lock);
INIT_LIST_HEAD(&tty->tty_files);
}
@@ -2319,6 +2320,9 @@ void __init tty_init(void)
#ifdef CONFIG_SX
sx_init();
#endif
+#ifdef CONFIG_RIO
+ rio_init();
+#endif
#ifdef CONFIG_8xx
rs_8xx_init();
#endif /* CONFIG_8xx */
diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c
index f0b1aaf4d..0a4e65c57 100644
--- a/drivers/char/videodev.c
+++ b/drivers/char/videodev.c
@@ -24,10 +24,9 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/videodev.h>
+#include <linux/init.h>
-#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
-#endif
#include <asm/system.h>
#include <linux/kmod.h>
@@ -174,20 +173,11 @@ static int video_release(struct inode *inode, struct file *file)
* image ?
*/
-#if LINUX_VERSION_CODE >= 0x020100
static long long video_lseek(struct file * file,
long long offset, int origin)
{
return -ESPIPE;
}
-#else
-static long long video_lseek(struct inode *inode, struct file * file,
- long long offset, int origin)
-{
- return -ESPIPE;
-}
-#endif
-
static int video_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
@@ -210,16 +200,9 @@ static int video_ioctl(struct inode *inode, struct file *file,
*/
-#if LINUX_VERSION_CODE >= 0x020100
int video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-#else
-static int video_mmap(struct inode * ino, struct file * file,
- struct vm_area_struct * vma)
-{
- struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
-#endif
if(vfl->mmap)
return vfl->mmap(vfl, (char *)vma->vm_start,
(unsigned long)(vma->vm_end-vma->vm_start));
@@ -228,8 +211,28 @@ static int video_mmap(struct inode * ino, struct file * file,
extern struct file_operations video_fops;
-/*
- * Video For Linux device drivers request registration here.
+/**
+ * video_register_device - register video4linux devices
+ * @vfd: Video device structure we want to register
+ * @type: type of device to register
+ * FIXME: needs a semaphore on 2.3.x
+ *
+ * The registration code assigns minor numbers based on the type
+ * requested. -ENFILE is returned in all the device slots for this
+ * catetory are full. If not then the minor field is set and the
+ * driver initialize function is called (if non NULL).
+ *
+ * Zero is returned on success.
+ *
+ * Valid types are
+ *
+ * VFL_TYPE_GRABBER - A frame grabber
+ *
+ * VFL_TYPE_VTX - A teletext device
+ *
+ * VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ * VFL_TYPE_RADIO - A radio card
*/
int video_register_device(struct video_device *vfd, int type)
@@ -288,10 +291,14 @@ int video_register_device(struct video_device *vfd, int type)
}
}
sprintf (name, "v4l/%s%d", name_base, i - base);
+ /*
+ * Start the device root only. Anything else
+ * has serious privacy issues.
+ */
vfd->devfs_handle =
devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT,
VIDEO_MAJOR, vfd->minor,
- S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
&video_fops, NULL);
return 0;
}
@@ -299,8 +306,12 @@ int video_register_device(struct video_device *vfd, int type)
return -ENFILE;
}
-/*
- * Unregister an unused video for linux device
+/**
+ * video_unregister_device - unregister a video4linux device
+ * @vfd: the device to unregister
+ *
+ * This unregisters the passed device and deassigns the minor
+ * number. Future open calls will be met with errors.
*/
void video_unregister_device(struct video_device *vfd)
@@ -322,16 +333,14 @@ static struct file_operations video_fops=
mmap: video_mmap,
open: video_open,
release: video_release,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
poll: video_poll,
-#endif
};
/*
* Initialise video for linux
*/
-int videodev_init(void)
+int __init videodev_init(void)
{
struct video_init *vfli = video_init_list;
@@ -365,15 +374,10 @@ void cleanup_module(void)
devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
}
-
-
-
-
-
-
#endif
-#if LINUX_VERSION_CODE >= 0x020100
EXPORT_SYMBOL(video_register_device);
EXPORT_SYMBOL(video_unregister_device);
-#endif
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers");
diff --git a/drivers/ide/Config.in b/drivers/ide/Config.in
new file mode 100644
index 000000000..bcd17994b
--- /dev/null
+++ b/drivers/ide/Config.in
@@ -0,0 +1,153 @@
+#
+# IDE ATA ATAPI Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'IDE, ATA and ATAPI Block devices'
+
+dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
+if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE $CONFIG_X86
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_IDE
+
+ dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+ dep_mbool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK
+ dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
+ dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+ dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
+
+ comment 'IDE chipset support/bugfixes'
+ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
+ dep_bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
+ dep_bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
+ if [ "$CONFIG_PCI" = "y" ]; then
+ dep_bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
+ bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+ if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+ bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+ bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+ dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PCI_AUTO
+ define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL $CONFIG_EXPERIMENTAL
+ dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+ dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING $CONFIG_BLK_DEV_AEC6210 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' AMD Viper support' CONFIG_BLK_DEV_AMD7409 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE $CONFIG_BLK_DEV_AMD7409 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID $CONFIG_BLK_DEV_CMD64X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ dep_mbool ' HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3 $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
+ dep_mbool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO
+ fi
+ dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
+ dep_bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
+ dep_mbool ' Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER $CONFIG_BLK_DEV_PDC202XX $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+ dep_bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
+ bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
+ fi
+ fi
+ if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
+ bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PMAC_AUTO
+ fi
+ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN
+ dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE
+ dep_bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_ICS_AUTO
+ dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
+ fi
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA
+ dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_mbool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI
+ fi
+ if [ "$CONFIG_MAC" = "y" ]; then
+ dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC
+ fi
+
+ bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
+ if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+ comment 'Note: most of these also require special kernel boot parameters'
+ bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+ bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+ bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+ bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+ fi
+ bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+ bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
+ fi
+ fi
+else
+ bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_ONLY
+fi
+
+# if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+# define_bool CONFIG_BLK_DEV_IDEDMA y
+# if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
+# define_bool CONFIG_IDEDMA_AUTO y
+# fi
+# else
+# define_bool CONFIG_BLK_DEV_IDEDMA n
+# define_bool CONFIG_IDEDMA_AUTO n
+# fi
+
+if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
+ "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
+ "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
+ "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD640" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
+ "$CONFIG_BLK_DEV_CS5530" = "y" -o \
+ "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT366" = "y" -o \
+ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+ "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
+ "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
+ "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
+ "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
+ define_bool CONFIG_BLK_DEV_IDE_MODES y
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+fi
+
+endmenu
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
new file mode 100644
index 000000000..699c48b82
--- /dev/null
+++ b/drivers/ide/Makefile
@@ -0,0 +1,238 @@
+#
+# Makefile for the kernel ata, atapi, and ide block device 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 definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := ide.a
+L_OBJS := ide-geometry.o
+M_OBJS :=
+MOD_LIST_NAME := IDE_MODULES
+LX_OBJS :=
+MX_OBJS :=
+
+ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
+IDE_OBJS += aec6210.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+IDE_OBJS += ali14xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
+IDE_OBJS += alim15x3.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
+IDE_OBJS += amd7409.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
+IDE_OBJS += buddha.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD640),y)
+IDE_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
+IDE_OBJS += cmd64x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CS5530),y)
+IDE_OBJS += cs5530.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
+IDE_OBJS += cy82c693.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+IDE_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
+IDE_OBJS += falconide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
+IDE_OBJS += gayle.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
+IDE_OBJS += q40ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
+IDE_OBJS += hpt34x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT366),y)
+IDE_OBJS += hpt366.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+IDE_OBJS += ht6560b.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
+IDE_OBJS += icside.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
+IDE_OBJS += ide-dma.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
+IDE_OBJS += ide-pci.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
+IDE_OBJS += ide-pnp.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
+IDE_OBJS += ide-pmac.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
+IDE_OBJS += macide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_NS87415),y)
+IDE_OBJS += ns87415.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
+IDE_OBJS += opti621.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
+IDE_OBJS += pdc202xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
+IDE_OBJS += pdc4030.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PIIX),y)
+IDE_OBJS += piix.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+IDE_OBJS += qd6580.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
+IDE_OBJS += rapide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+IDE_OBJS += rz1000.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
+IDE_OBJS += sis5513.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
+IDE_OBJS += sl82c105.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRM290),y)
+IDE_OBJS += trm290.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+IDE_OBJS += umc8672.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
+IDE_OBJS += via82cxxx.o
+endif
+
+### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
+
+ifeq ($(CONFIG_PROC_FS),y)
+IDE_OBJS += ide-proc.o
+endif
+
+###Collect
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+ LX_OBJS += ide.o ide-features.o
+ L_OBJS += ide-probe.o $(IDE_OBJS)
+else
+ ifeq ($(CONFIG_BLK_DEV_IDE),m)
+ MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
+ M_OBJS += ide-mod.o ide-probe-mod.o
+ endif
+endif
+
+############
+
+ifeq ($(CONFIG_BLK_DEV_IDECS),y)
+L_OBJS += ide-cs.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECS),m)
+ M_OBJS += ide-cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
+L_OBJS += ide-disk.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
+ M_OBJS += ide-disk.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+L_OBJS += ide-cd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+ M_OBJS += ide-cd.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
+ M_OBJS += ide-tape.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
+ M_OBJS += ide-floppy.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
+
+ide-probe-mod.o: ide-probe.o ide-geometry.o
+ $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
diff --git a/drivers/block/aec6210.c b/drivers/ide/aec6210.c
index cc0aca5fd..cc0aca5fd 100644
--- a/drivers/block/aec6210.c
+++ b/drivers/ide/aec6210.c
diff --git a/drivers/block/ali14xx.c b/drivers/ide/ali14xx.c
index b3ffaa529..b3ffaa529 100644
--- a/drivers/block/ali14xx.c
+++ b/drivers/ide/ali14xx.c
diff --git a/drivers/block/alim15x3.c b/drivers/ide/alim15x3.c
index bc3d2b9e3..bc3d2b9e3 100644
--- a/drivers/block/alim15x3.c
+++ b/drivers/ide/alim15x3.c
diff --git a/drivers/block/amd7409.c b/drivers/ide/amd7409.c
index 7d2018029..7d2018029 100644
--- a/drivers/block/amd7409.c
+++ b/drivers/ide/amd7409.c
diff --git a/drivers/block/buddha.c b/drivers/ide/buddha.c
index 3e1cfcd33..3e1cfcd33 100644
--- a/drivers/block/buddha.c
+++ b/drivers/ide/buddha.c
diff --git a/drivers/block/cmd640.c b/drivers/ide/cmd640.c
index b2077df6d..b2077df6d 100644
--- a/drivers/block/cmd640.c
+++ b/drivers/ide/cmd640.c
diff --git a/drivers/block/cmd64x.c b/drivers/ide/cmd64x.c
index 542ad44a1..542ad44a1 100644
--- a/drivers/block/cmd64x.c
+++ b/drivers/ide/cmd64x.c
diff --git a/drivers/block/cs5530.c b/drivers/ide/cs5530.c
index bb68f7b2e..bb68f7b2e 100644
--- a/drivers/block/cs5530.c
+++ b/drivers/ide/cs5530.c
diff --git a/drivers/block/cy82c693.c b/drivers/ide/cy82c693.c
index cfff0381c..cfff0381c 100644
--- a/drivers/block/cy82c693.c
+++ b/drivers/ide/cy82c693.c
diff --git a/drivers/block/dtc2278.c b/drivers/ide/dtc2278.c
index d8838e111..d8838e111 100644
--- a/drivers/block/dtc2278.c
+++ b/drivers/ide/dtc2278.c
diff --git a/drivers/block/falconide.c b/drivers/ide/falconide.c
index 7bce07517..7bce07517 100644
--- a/drivers/block/falconide.c
+++ b/drivers/ide/falconide.c
diff --git a/drivers/block/gayle.c b/drivers/ide/gayle.c
index 29cceb20e..29cceb20e 100644
--- a/drivers/block/gayle.c
+++ b/drivers/ide/gayle.c
diff --git a/drivers/block/hd.c b/drivers/ide/hd.c
index 5520c17b0..5520c17b0 100644
--- a/drivers/block/hd.c
+++ b/drivers/ide/hd.c
diff --git a/drivers/block/hpt34x.c b/drivers/ide/hpt34x.c
index 425ce35a4..425ce35a4 100644
--- a/drivers/block/hpt34x.c
+++ b/drivers/ide/hpt34x.c
diff --git a/drivers/block/hpt366.c b/drivers/ide/hpt366.c
index d2f2fb433..d2f2fb433 100644
--- a/drivers/block/hpt366.c
+++ b/drivers/ide/hpt366.c
diff --git a/drivers/block/ht6560b.c b/drivers/ide/ht6560b.c
index 6b7d5af72..6b7d5af72 100644
--- a/drivers/block/ht6560b.c
+++ b/drivers/ide/ht6560b.c
diff --git a/drivers/block/icside.c b/drivers/ide/icside.c
index d0e8f8328..d0e8f8328 100644
--- a/drivers/block/icside.c
+++ b/drivers/ide/icside.c
diff --git a/drivers/block/ide-cd.c b/drivers/ide/ide-cd.c
index 641783e9c..641783e9c 100644
--- a/drivers/block/ide-cd.c
+++ b/drivers/ide/ide-cd.c
diff --git a/drivers/block/ide-cd.h b/drivers/ide/ide-cd.h
index 1eb48ef6c..1eb48ef6c 100644
--- a/drivers/block/ide-cd.h
+++ b/drivers/ide/ide-cd.h
diff --git a/drivers/block/ide-cs.c b/drivers/ide/ide-cs.c
index 73d285cb1..73d285cb1 100644
--- a/drivers/block/ide-cs.c
+++ b/drivers/ide/ide-cs.c
diff --git a/drivers/block/ide-disk.c b/drivers/ide/ide-disk.c
index 2ef50f285..2ef50f285 100644
--- a/drivers/block/ide-disk.c
+++ b/drivers/ide/ide-disk.c
diff --git a/drivers/block/ide-dma.c b/drivers/ide/ide-dma.c
index 751424831..751424831 100644
--- a/drivers/block/ide-dma.c
+++ b/drivers/ide/ide-dma.c
diff --git a/drivers/block/ide-features.c b/drivers/ide/ide-features.c
index 3f29ed591..3f29ed591 100644
--- a/drivers/block/ide-features.c
+++ b/drivers/ide/ide-features.c
diff --git a/drivers/block/ide-floppy.c b/drivers/ide/ide-floppy.c
index bee6a4c6c..58eb2411d 100644
--- a/drivers/block/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -1226,10 +1226,10 @@ static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
drive->bios_head = page->heads;
drive->bios_sect = page->sectors;
lba_capacity = floppy->blocks * floppy->block_size;
- if (capacity != lba_capacity) {
- printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
- drive->name, capacity, lba_capacity);
- capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+ if (capacity < lba_capacity) {
+ printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+ "but the drive only handles %d\n",
+ drive->name, lba_capacity, capacity);
floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
}
return 0;
diff --git a/drivers/block/ide-geometry.c b/drivers/ide/ide-geometry.c
index 6ebf20fe1..6ebf20fe1 100644
--- a/drivers/block/ide-geometry.c
+++ b/drivers/ide/ide-geometry.c
diff --git a/drivers/block/ide-pci.c b/drivers/ide/ide-pci.c
index f667ee6dd..f667ee6dd 100644
--- a/drivers/block/ide-pci.c
+++ b/drivers/ide/ide-pci.c
diff --git a/drivers/block/ide-pmac.c b/drivers/ide/ide-pmac.c
index e0803e6fa..e0803e6fa 100644
--- a/drivers/block/ide-pmac.c
+++ b/drivers/ide/ide-pmac.c
diff --git a/drivers/block/ide-pnp.c b/drivers/ide/ide-pnp.c
index ffa3ade56..ffa3ade56 100644
--- a/drivers/block/ide-pnp.c
+++ b/drivers/ide/ide-pnp.c
diff --git a/drivers/block/ide-probe.c b/drivers/ide/ide-probe.c
index 311bcfa25..311bcfa25 100644
--- a/drivers/block/ide-probe.c
+++ b/drivers/ide/ide-probe.c
diff --git a/drivers/block/ide-proc.c b/drivers/ide/ide-proc.c
index 753597e9c..753597e9c 100644
--- a/drivers/block/ide-proc.c
+++ b/drivers/ide/ide-proc.c
diff --git a/drivers/block/ide-tape.c b/drivers/ide/ide-tape.c
index 95e780abf..95e780abf 100644
--- a/drivers/block/ide-tape.c
+++ b/drivers/ide/ide-tape.c
diff --git a/drivers/block/ide.c b/drivers/ide/ide.c
index 326533e7f..9c409dfb6 100644
--- a/drivers/block/ide.c
+++ b/drivers/ide/ide.c
@@ -2587,6 +2587,8 @@ static int ide_ioctl (struct inode *inode, struct file *file,
case BLKFLSBUF:
case BLKSSZGET:
case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
return blk_ioctl(inode->i_rdev, cmd, arg);
default:
diff --git a/drivers/block/ide_modes.h b/drivers/ide/ide_modes.h
index f94d91313..f94d91313 100644
--- a/drivers/block/ide_modes.h
+++ b/drivers/ide/ide_modes.h
diff --git a/drivers/block/macide.c b/drivers/ide/macide.c
index 46a14ba19..46a14ba19 100644
--- a/drivers/block/macide.c
+++ b/drivers/ide/macide.c
diff --git a/drivers/block/ns87415.c b/drivers/ide/ns87415.c
index 0e5675fde..0e5675fde 100644
--- a/drivers/block/ns87415.c
+++ b/drivers/ide/ns87415.c
diff --git a/drivers/block/opti621.c b/drivers/ide/opti621.c
index cc2aa567c..cc2aa567c 100644
--- a/drivers/block/opti621.c
+++ b/drivers/ide/opti621.c
diff --git a/drivers/block/pdc202xx.c b/drivers/ide/pdc202xx.c
index 9ec7c8997..9ec7c8997 100644
--- a/drivers/block/pdc202xx.c
+++ b/drivers/ide/pdc202xx.c
diff --git a/drivers/block/pdc4030.c b/drivers/ide/pdc4030.c
index f42c4946f..f42c4946f 100644
--- a/drivers/block/pdc4030.c
+++ b/drivers/ide/pdc4030.c
diff --git a/drivers/block/pdc4030.h b/drivers/ide/pdc4030.h
index 551785c36..551785c36 100644
--- a/drivers/block/pdc4030.h
+++ b/drivers/ide/pdc4030.h
diff --git a/drivers/block/piix.c b/drivers/ide/piix.c
index 97e57fa55..97e57fa55 100644
--- a/drivers/block/piix.c
+++ b/drivers/ide/piix.c
diff --git a/drivers/block/q40ide.c b/drivers/ide/q40ide.c
index 0f2330370..0f2330370 100644
--- a/drivers/block/q40ide.c
+++ b/drivers/ide/q40ide.c
diff --git a/drivers/block/qd6580.c b/drivers/ide/qd6580.c
index 31781a9f0..31781a9f0 100644
--- a/drivers/block/qd6580.c
+++ b/drivers/ide/qd6580.c
diff --git a/drivers/block/rapide.c b/drivers/ide/rapide.c
index 5905aca41..5905aca41 100644
--- a/drivers/block/rapide.c
+++ b/drivers/ide/rapide.c
diff --git a/drivers/block/rz1000.c b/drivers/ide/rz1000.c
index 455641c1d..455641c1d 100644
--- a/drivers/block/rz1000.c
+++ b/drivers/ide/rz1000.c
diff --git a/drivers/block/sis5513.c b/drivers/ide/sis5513.c
index 942187900..942187900 100644
--- a/drivers/block/sis5513.c
+++ b/drivers/ide/sis5513.c
diff --git a/drivers/block/sl82c105.c b/drivers/ide/sl82c105.c
index 483a98fde..483a98fde 100644
--- a/drivers/block/sl82c105.c
+++ b/drivers/ide/sl82c105.c
diff --git a/drivers/block/trm290.c b/drivers/ide/trm290.c
index fb5e8d1af..fb5e8d1af 100644
--- a/drivers/block/trm290.c
+++ b/drivers/ide/trm290.c
diff --git a/drivers/block/umc8672.c b/drivers/ide/umc8672.c
index 02b581a28..02b581a28 100644
--- a/drivers/block/umc8672.c
+++ b/drivers/ide/umc8672.c
diff --git a/drivers/block/via82cxxx.c b/drivers/ide/via82cxxx.c
index c2ec24d39..c2ec24d39 100644
--- a/drivers/block/via82cxxx.c
+++ b/drivers/ide/via82cxxx.c
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index dab3f99b9..a44153e31 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -2,6 +2,7 @@
*
* (c) Copyright 1998 Red Hat Software Inc
* Written by Alan Cox.
+ * Further debugging by Carl Drougge.
*
* Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
* (for the MCA stuff) written by Wim Dumon.
@@ -15,7 +16,7 @@
*/
static const char *version =
- "3c527.c:v0.07 2000/01/18 Alan Cox (alan@redhat.com)\n";
+ "3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
/**
* DOC: Traps for the unwary
@@ -122,6 +123,7 @@ struct mc32_local
u32 base;
u16 rx_halted;
u16 tx_halted;
+ u16 rx_pending;
u16 exec_pending;
u16 mc_reload_wait; /* a multicast load request is pending */
atomic_t tx_count; /* buffers left */
@@ -451,6 +453,9 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
dev->name, lp->rx_len, lp->tx_len, lp->base);
+
+ if(lp->tx_len > TX_RING_MAX)
+ lp->tx_len = TX_RING_MAX;
dev->open = mc32_open;
dev->stop = mc32_close;
@@ -462,6 +467,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
lp->rx_halted = 1;
lp->tx_halted = 1;
+ lp->rx_pending = 0;
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
@@ -652,6 +658,7 @@ static void mc32_rx_begin(struct net_device *dev)
mc32_ring_poll(dev);
lp->rx_halted=0;
+ lp->rx_pending=0;
}
/**
@@ -944,6 +951,7 @@ static void mc32_timeout(struct net_device *dev)
static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
+ int ioaddr = dev->base_addr;
unsigned long flags;
u16 tx_head;
@@ -967,7 +975,16 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
lp->tx_skb[lp->tx_skb_end] = skb;
lp->tx_skb_end++;
lp->tx_skb_end&=(TX_RING_MAX-1);
+
+ /* TX suspend - shouldnt be needed but apparently is.
+ This is a research item ... */
+
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ lp->tx_box->mbox=0;
+ outb(3, ioaddr+HOST_CMD);
+ /* Transmit now stopped */
+
/* P is the last sending/sent buffer as a pointer */
p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
@@ -990,7 +1007,9 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
p->status = 0;
p->control &= ~(1<<6);
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->tx_box->mbox=0;
+ outb(5, ioaddr+HOST_CMD); /* Restart TX */
restore_flags(flags);
netif_wake_queue(dev);
@@ -1096,11 +1115,16 @@ static void mc32_rx_ring(struct net_device *dev)
base = p->next;
}
while(x++<48);
+
+ /*
+ * Restart ring processing
+ */
while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->rx_box->mbox=0;
lp->rx_box->data[0] = top;
outb(1<<3, ioaddr+HOST_CMD);
+ lp->rx_halted=0;
}
@@ -1123,7 +1147,6 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
struct net_device *dev = dev_id;
struct mc32_local *lp;
int ioaddr, status, boguscount = 0;
- int rx_event = 0;
if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
@@ -1182,7 +1205,15 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
case 0:
break;
case 2: /* RX */
- rx_event=1;
+ lp->rx_pending=1;
+ if(!lp->rx_halted)
+ {
+ /*
+ * Halt ring receive
+ */
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ outb(3<<3, ioaddr+HOST_CMD);
+ }
break;
case 3:
case 4:
@@ -1195,9 +1226,10 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
break;
case 6:
/* Out of RX buffers stat */
- /* Must restart */
lp->net_stats.rx_dropped++;
- rx_event = 1; /* To restart */
+ lp->rx_pending=1;
+ /* Must restart */
+ lp->rx_halted=1;
break;
default:
printk("%s: strange rx ack %d\n",
@@ -1231,11 +1263,17 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
/*
- * Process and restart the receive ring.
+ * Process and restart the receive ring. This has some state
+ * as we must halt the ring to process it and halting the ring
+ * might not occur in the same IRQ handling loop as we issue
+ * the halt.
*/
- if(rx_event)
+ if(lp->rx_pending && lp->rx_halted)
+ {
mc32_rx_ring(dev);
+ lp->rx_pending = 0;
+ }
return;
}
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 839c15ebc..31e8c79b8 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1914,7 +1914,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
EL3WINDOW(4);
mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index b264def30..b62986f83 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -199,11 +199,13 @@ if [ "$CONFIG_FDDI" = "y" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
- if [ "$CONFIG_HIPPI" = "y" ]; then
- tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
- if [ "$CONFIG_ROADRUNNER" != "n" ]; then
- bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ if [ "$CONFIG_INET" = "y" ]; then
+ bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+ if [ "$CONFIG_HIPPI" = "y" ]; then
+ tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
+ if [ "$CONFIG_ROADRUNNER" != "n" ]; then
+ bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ fi
fi
fi
fi
diff --git a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h
index 45e3bc044..844f0ba03 100644
--- a/drivers/net/aironet4500.h
+++ b/drivers/net/aironet4500.h
@@ -454,7 +454,7 @@ struct awc_fid_queue {
};
-extern void
+extern __inline__ void
awc_fid_queue_init(struct awc_fid_queue * queue){
unsigned long flags;
diff --git a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in
index 951cf498d..e42231cbf 100644
--- a/drivers/net/appletalk/Config.in
+++ b/drivers/net/appletalk/Config.in
@@ -3,18 +3,21 @@
#
if [ "$CONFIG_ATALK" != "n" ]; then
- mainmenu_option next_comment
- comment 'Appletalk devices'
- dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
- dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
- if [ "$CONFIG_COPS" != "n" ]; then
- bool ' Dayna firmware support' CONFIG_COPS_DAYNA
- bool ' Tangent firmware support' CONFIG_COPS_TANGENT
- fi
- dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
- if [ "$CONFIG_IPDDP" != "n" ]; then
- bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
- bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
- fi
- endmenu
+ mainmenu_option next_comment
+ comment 'Appletalk devices'
+ bool 'Appletalk interfaces support' CONFIG_APPLETALK
+ if [ "$CONFIG_APPLETALK" != "n" ]; then
+ dep_tristate ' Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_APPLETALK
+ dep_tristate ' COPS LocalTalk PC support' CONFIG_COPS $CONFIG_APPLETALK
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool ' Dayna firmware support' CONFIG_COPS_DAYNA
+ bool ' Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
+ dep_tristate ' Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_APPLETALK
+ if [ "$CONFIG_IPDDP" != "n" ]; then
+ bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+ bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
+ fi
+ fi
+ endmenu
fi
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index 073a7392a..493c2f6c5 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -39,7 +39,7 @@
/*
* This version is for use with contiguous buffers on Linux-derived systems.
*
- * ==FILEVERSION 970607==
+ * ==FILEVERSION 20000226==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the number above to the
@@ -58,32 +58,9 @@
#endif
#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/init.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/byteorder.h>
-
-#include <linux/if.h>
-
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
#include <linux/ppp_defs.h>
@@ -1177,17 +1154,18 @@ static struct compressor ppp_bsd_compress = {
* Module support routines
*************************************************************/
-int
-init_module(void)
+int bsdcomp_init(void)
{
- int answer = ppp_register_compressor (&ppp_bsd_compress);
+ int answer = ppp_register_compressor(&ppp_bsd_compress);
if (answer == 0)
- printk (KERN_INFO "PPP BSD Compression module registered\n");
+ printk(KERN_INFO "PPP BSD Compression module registered\n");
return answer;
}
-void
-cleanup_module(void)
+void bsdcomp_cleanup(void)
{
- ppp_unregister_compressor (&ppp_bsd_compress);
+ ppp_unregister_compressor(&ppp_bsd_compress);
}
+
+module_init(bsdcomp_init);
+module_exit(bsdcomp_cleanup);
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index b42b8d390..5a5f3cc62 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -852,6 +852,8 @@ static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd)
return -EFAULT;
return (0);
case DGRS_SETFILTER:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (ioc.port > privN->bcomm->bc_nports)
return -EINVAL;
if (ioc.filter >= NFILTERS)
@@ -1188,8 +1190,11 @@ dgrs_probe1(struct net_device *dev)
priv->intrcnt = 0;
for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
+ {
+ barrier(); /* gcc 2.95 needs this */
if (priv->intrcnt >= 2)
break;
+ }
if (priv->intrcnt < 2)
{
printk("%s: Not interrupting on IRQ %d (%d)\n",
diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c
index 01a32afd5..87e5ccd7f 100644
--- a/drivers/net/dmfe.c
+++ b/drivers/net/dmfe.c
@@ -1,15 +1,26 @@
/*
- dmfe.c: Version 1.26
+ dmfe.c: Version 1.28 01/18/2000
- A Davicom DM9102 fast ethernet driver for Linux.
+ A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+ Copyright (C) 1997 Sten Wang
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 1.
Compiler command:
"gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall
- -Wstrict-prototypes -O6 -c dmfe.c"
+ -Wstrict-prototypes -O6 -c dmfe.c"
+ OR
+ "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall
+ -Wstrict-prototypes -O6 -c dmfe.c"
The following steps teach you how to active DM9102 board:
1. Used the upper compiler command to compile dmfe.c
@@ -25,7 +36,7 @@
"route add -net 172.22.3.0 eth0"
5. Well done. Your DM9102 adapter actived now.
- Author: Sten Wang, E-mail: sten_wang@davicom.com.tw
+ Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
Date: 10/28,1998
@@ -44,6 +55,9 @@
Check and fix on 64bit and big endian boxes.
Sort out the PCI latency.
+ (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+
+ Cleaned up for kernel merge by Alan Cox (alan@redhat.com)
*/
#include <linux/module.h>
@@ -60,38 +74,41 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/version.h>
-
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include <linux/delay.h>
+
#include <asm/processor.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>
/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
#define DMFE_SUCC 0
#define DM9102_IO_SIZE 0x80
-#define TX_FREE_DESC_CNT 0x1 /* Tx packet count */
+#define DM9102A_IO_SIZE 0x100
+#define TX_FREE_DESC_CNT 0xc /* Tx packet count */
+#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
#define DM910X_RESET 1
-#define CR6_DEFAULT 0x002c0000 /* SF, MII, HD */
+#define CR6_DEFAULT 0x00280000 /* SF, HD */
#define CR7_DEFAULT 0x1a2cd
#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
#define MAX_PACKET_SIZE 1514
#define DMFE_MAX_MULTICAST 14
-#define RX_MAX_TRAFFIC 0x5000
+#define RX_MAX_TRAFFIC 0x14000
#define MAX_CHECK_PACKET 0x8000
#define DMFE_10MHF 0
@@ -100,8 +117,8 @@
#define DMFE_100MFD 5
#define DMFE_AUTO 8
-#define DMFE_TIMER_WUT jiffies+HZ*1 /* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT HZ*2 /* tx packet time-out time */
+#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*1.5 /* tx packet time-out time 1.5 s" */
#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
@@ -109,7 +126,7 @@
#define DELAY_1US udelay(1) /* udelay scale 1 usec */
-#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
/* CR9 definition: SROM/MII */
@@ -125,6 +142,9 @@
#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
+#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+
+
/* Structure/enum declaration ------------------------------- */
struct tx_desc {
u32 tdes0, tdes1, tdes2, tdes3;
@@ -149,13 +169,14 @@ struct dmfe_board_info {
struct pci_dev *net_dev; /* PCI device */
- u32 ioaddr; /* I/O base address */
+ unsigned long ioaddr; /* I/O base address */
+ u32 cr0_data;
u32 cr5_data;
u32 cr6_data;
u32 cr7_data;
u32 cr15_data;
-
-/* descriptor pointer */
+
+ /* descriptor pointer */
unsigned char *buf_pool_ptr; /* Tx buffer pool memory */
unsigned char *buf_pool_start; /* Tx buffer pool align dword */
unsigned char *desc_pool_ptr; /* descriptor pool memory */
@@ -166,9 +187,12 @@ struct dmfe_board_info {
struct rx_desc *rx_insert_ptr;
struct rx_desc *rx_ready_ptr; /* packet come pointer */
u32 tx_packet_cnt; /* transmitted packet count */
+ u32 tx_queue_cnt; /* wait to send packet count */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
+ u16 phy_id2; /* Phyxcer ID2 */
+
u8 media_mode; /* user specify media mode */
u8 op_mode; /* real work media mode */
u8 phy_addr;
@@ -190,14 +214,14 @@ enum dmfe_offsets {
enum dmfe_CR6_bits {
CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
- CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000
+ CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000,
+ CR6_NO_PURGE = 0x20000000
};
/* Global variable declaration ----------------------------- */
-
static int dmfe_debug = 0;
static unsigned char dmfe_media_mode = 8;
-static struct net_device *dmfe_root_dev = NULL; /* First device */
+static struct net_device *dmfe_root_dev = NULL; /* First device */
static u32 dmfe_cr6_user_set = 0;
/* For module input parameter */
@@ -206,7 +230,7 @@ static u32 cr6set = 0;
static unsigned char mode = 8;
static u8 chkmode = 1;
-unsigned long CrcTable[256] =
+static unsigned long CrcTable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
@@ -275,7 +299,7 @@ unsigned long CrcTable[256] =
};
/* function declaration ------------------------------------- */
-static int dmfe_reg_board(void);
+static int dmfe_probe(void);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static int dmfe_stop(struct net_device *);
@@ -288,11 +312,11 @@ static void dmfe_descriptor_init(struct dmfe_board_info *, u32);
static void allocated_rx_buffer(struct dmfe_board_info *);
static void update_cr6(u32, u32);
static void send_filter_frame(struct net_device *, int);
-static u16 phy_read(u32, u8, u8);
-static void phy_write(u32, u8, u8, u16);
+static void dm9132_id_table(struct net_device *, int);
+static u16 phy_read(u32, u8, u8, u32);
+static void phy_write(u32, u8, u8, u16, u32);
static void phy_write_1bit(u32, u32);
static u16 phy_read_1bit(u32);
-static void parser_ctrl_info(struct dmfe_board_info *);
static void dmfe_sense_speed(struct dmfe_board_info *);
static void dmfe_process_mode(struct dmfe_board_info *);
static void dmfe_timer(unsigned long);
@@ -301,7 +325,7 @@ static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
static void dmfe_dynamic_reset(struct net_device *);
static void dmfe_free_rxbuffer(struct dmfe_board_info *);
static void dmfe_init_dm910x(struct net_device *);
-static unsigned long cal_CRC(unsigned char *, unsigned int);
+static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
/* DM910X network board routine ---------------------------- */
@@ -309,9 +333,9 @@ static unsigned long cal_CRC(unsigned char *, unsigned int);
* Search DM910X board, allocate space and register it
*/
-static int __init dmfe_reg_board(void)
+static int __init dmfe_probe(void)
{
- u32 pci_iobase;
+ unsigned long pci_iobase;
u16 dm9102_count = 0;
u8 pci_irqline;
static int index = 0; /* For multiple call */
@@ -320,7 +344,7 @@ static int __init dmfe_reg_board(void)
struct pci_dev *net_dev = NULL;
struct net_device *dev;
- DMFE_DBUG(0, "dmfe_reg_board()", 0);
+ DMFE_DBUG(0, "dmfe_probe()", 0);
if (!pci_present())
return -ENODEV;
@@ -329,20 +353,18 @@ static int __init dmfe_reg_board(void)
while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
{
u32 pci_id;
+ u32 dev_rev;
index++;
if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
continue;
- if (pci_id != PCI_DM9102_ID)
+ if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
continue;
pci_iobase = net_dev->resource[0].start;
pci_irqline = net_dev->irq;
- if (check_region(pci_iobase, DM9102_IO_SIZE)) /* IO range check */
- continue;
-
/* Enable Master/IO access, Disable memory access */
pci_enable_device (net_dev);
@@ -355,7 +377,19 @@ static int __init dmfe_reg_board(void)
pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
- /* IO range and interrupt check */
+ /* Read Chip revesion */
+ pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev);
+
+ /* IO range check */
+ if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+ printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
+ continue;
+ }
+ /* Interrupt check */
+ if (pci_irqline == 0) {
+ printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
+ continue;
+ }
/* Found DM9102 card and PCI resource allocated OK */
dm9102_count++; /* Found a DM9102 card */
@@ -373,7 +407,7 @@ static int __init dmfe_reg_board(void)
db->chip_id = pci_id; /* keep Chip vandor/Device ID */
db->ioaddr = pci_iobase;
- pci_read_config_dword(net_dev, 8, &db->chip_revesion);
+ db->chip_revesion = dev_rev;
db->net_dev = net_dev;
@@ -386,7 +420,7 @@ static int __init dmfe_reg_board(void)
dev->set_multicast_list = &dmfe_set_filter_mode;
dev->do_ioctl = &dmfe_do_ioctl;
- request_region(pci_iobase, DM9102_IO_SIZE, dev->name);
+ request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
/* read 64 word srom data */
for (i = 0; i < 64; i++)
@@ -422,20 +456,17 @@ static int dmfe_open(struct net_device *dev)
db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
if (db->desc_pool_ptr == NULL)
return -ENOMEM;
-
if ((u32) db->desc_pool_ptr & 0x1f)
db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
else
db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
/* Allocated Tx buffer memory */
-
db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
if (db->buf_pool_ptr == NULL) {
kfree(db->desc_pool_ptr);
return -ENOMEM;
}
-
if ((u32) db->buf_pool_ptr & 0x3)
db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
else
@@ -444,16 +475,21 @@ static int dmfe_open(struct net_device *dev)
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
db->in_reset_state = 0;
db->rx_error_cnt = 0;
- if (chkmode && (db->chip_revesion < 0x02000030)) {
- db->dm910x_chk_mode = 1; /* Enter the check mode */
- } else {
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
+ //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */
+ //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */
+ db->cr0_data = 0xc00000; /* TX/RX desc burst mode */
db->dm910x_chk_mode = 4; /* Enter the normal mode */
+ } else {
+ db->cr0_data = 0;
+ db->dm910x_chk_mode = 1; /* Enter the check mode */
}
/* Initilize DM910X board */
@@ -474,14 +510,12 @@ static int dmfe_open(struct net_device *dev)
return 0;
}
-/*
- * Initialize DM910X board
- * Reset DM910X board
- * Initialize TX/Rx descriptor chain structure
- * Send the set-up frame
- * Enable Tx/Rx machine
+/* Initilize DM910X board
+ Reset DM910X board
+ Initilize TX/Rx descriptor chain structure
+ Send the set-up frame
+ Enable Tx/Rx machine
*/
-
static void dmfe_init_dm910x(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -490,16 +524,18 @@ static void dmfe_init_dm910x(struct net_device *dev)
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x board : need 32 PCI clock to complete */
- outl(DM910X_RESET, ioaddr + DCR0);
+ outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
DELAY_5US;
- outl(0, ioaddr + DCR0);
+ outl(db->cr0_data, ioaddr + DCR0);
outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
- outl(0x80, ioaddr + DCR12); /* Reset DM9102 phyxcer */
+ outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */
outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
- /* Parser control information: Phy addr */
- parser_ctrl_info(db);
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Media Mode Check */
db->media_mode = dmfe_media_mode;
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
@@ -514,7 +550,10 @@ static void dmfe_init_dm910x(struct net_device *dev)
update_cr6(db->cr6_data, ioaddr);
/* Send setup frame */
- send_filter_frame(dev, 0);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
/* Init CR5/CR7, interrupt active bit */
outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */
@@ -533,10 +572,9 @@ static void dmfe_init_dm910x(struct net_device *dev)
/*
- * Hardware start transmission.
- * Send a packet to media from the upper layer.
+ Hardware start transmission.
+ Send a packet to media from the upper layer.
*/
-
static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -561,25 +599,24 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
txptr->tdes1 = 0xe1000000 | skb->len;
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
/* Point to next transmit free descriptor */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
- /* transmit counter increase 1 */
- db->tx_packet_cnt++;
- db->stats.tx_packets++;
-
- /* issue Tx polling command */
- outl(0x1, dev->base_addr + DCR1);
+ /* Transmit Packet Process */
+ if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ } else {
+ db->tx_queue_cnt++; /* queue the tx packet */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ }
/* Tx resource check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
- /* Set transmit time stamp */
- dev->trans_start = jiffies; /* saved the time stamp */
-
/* free this SKB */
dev_kfree_skb(skb);
return 0;
@@ -622,8 +659,8 @@ static int dmfe_stop(struct net_device *dev)
}
/*
- * DM9102 insterrupt handler
- * receive the packet to upper layer, free the transmitted packet
+ DM9102 insterrupt handler
+ receive the packet to upper layer, free the transmitted packet
*/
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -637,7 +674,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
return;
}
-
/* A real interrupt coming */
db = (struct dmfe_board_info *) dev->priv;
ioaddr = dev->base_addr;
@@ -667,15 +703,35 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* printk("tdes0=%x\n", txptr->tdes0); */
if (txptr->tdes0 & 0x80000000)
break;
+ db->stats.tx_packets++;
+
if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
/* printk("tdes0=%x\n", txptr->tdes0); */
db->stats.tx_errors++;
}
+ /* Transmit statistic counter */
+ if (txptr->tdes0 != 0x7fffffff) {
+ /* printk("tdes0=%x\n", txptr->tdes0); */
+ db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+ db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
+ if (txptr->tdes0 & TDES0_ERR_MASK)
+ db->stats.tx_errors++;
+ }
txptr = (struct tx_desc *) txptr->next_tx_desc;
db->tx_packet_cnt--;
}
+ /* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
+ /* Send the Tx packet in queue */
+ if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
+ dev->trans_start = jiffies; /* saved the time stamp */
+ db->tx_queue_cnt--;
+ }
+ /* Resource available check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
@@ -695,7 +751,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/* Restore CR7 to enable interrupt mask */
-
if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
db->cr7_data = 0x1a28d;
else
@@ -704,9 +759,8 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/*
- * Receive the come packet and pass to upper layer
+ Receive the come packet and pass to upper layer
*/
-
static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
{
struct rx_desc *rxptr;
@@ -727,11 +781,11 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
/* reused this SKB */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
- db->rx_error_cnt++;
+ /* db->rx_error_cnt++; */
} else {
+ /* A packet with First/Last flag */
rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */
- /* A packet with First/Last flag */
if (rxptr->rdes0 & 0x8000) { /* error summary bit check */
/* This is a error packet */
/* printk("rdes0 error : %x \n", rxptr->rdes0); */
@@ -748,7 +802,7 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb = (struct sk_buff *) rxptr->rx_skb_ptr;
/* Received Packet CRC check need or not */
- if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) {
+ if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) {
/* Found a error received packet */
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
db->dm910x_chk_mode = 3;
@@ -758,11 +812,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb_put(skb, rxlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* Send to upper layer */
- /* skb->ip_summed = CHECKSUM_UNNECESSARY; */
dev->last_rx = jiffies;
db->stats.rx_packets++;
+ db->stats.rx_bytes += rxlen;
}
} else {
+ /* Reuse SKB buffer when the packet is error */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
}
@@ -772,12 +827,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
}
db->rx_ready_ptr = rxptr;
+
}
/*
- * Get statistics from driver.
+ Get statistics from driver.
*/
-
static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
{
struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
@@ -787,9 +842,8 @@ static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
}
/*
- * Set DM910X multicast address
+ Set DM910X multicast address
*/
-
static void dmfe_set_filter_mode(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -809,13 +863,15 @@ static void dmfe_set_filter_mode(struct net_device *dev)
return;
}
DMFE_DBUG(0, "Set multicast address", dev->mc_count);
- send_filter_frame(dev, dev->mc_count);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
}
/*
- * Process the upper socket ioctl command
+ Process the upper socket ioctl command
*/
-
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -823,10 +879,9 @@ static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
/*
- * A periodic timer routine
- * Dynamic media sense, allocated Rx buffer...
+ A periodic timer routine
+ Dynamic media sense, allocated Rx buffer...
*/
-
static void dmfe_timer(unsigned long data)
{
u32 tmp_cr8;
@@ -861,18 +916,26 @@ static void dmfe_timer(unsigned long data)
if (db->wait_reset | (db->tx_packet_cnt &&
((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
- /* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */
+ /*
+ printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
+ */
DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
dmfe_dynamic_reset(dev);
db->timer.expires = DMFE_TIMER_WUT;
add_timer(&db->timer);
return;
}
- db->rx_error_cnt = 0; /* Clear previous counter */
+ db->rx_error_cnt = 0; /* Clear previos counter */
/* Link status check, Dynamic media type change */
- tmp_cr12 = inb(db->ioaddr + DCR12);
- if (db->chip_revesion == 0x02000030) {
+ if (db->chip_id == PCI_DM9132_ID)
+ tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */
+ else
+ tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */
+
+ if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
+ ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+ /* DM9102A Chip */
if (tmp_cr12 & 2)
tmp_cr12 = 0x0; /* Link failed */
else
@@ -882,10 +945,26 @@ static void dmfe_timer(unsigned long data)
/* Link Failed */
DMFE_DBUG(0, "Link Failed", tmp_cr12);
db->link_failed = 1;
- phy_write(db->ioaddr, db->phy_addr, 0, 0x8000); /* reset Phy controller */
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */
+
+ /* 10/100M link failed, used 1M Home-Net */
+ db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */
+ db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
+ db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+ if (db->phy_id2 == 0xb900)
+ phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
} else if ((tmp_cr12 & 0x3) && db->link_failed) {
DMFE_DBUG(0, "Link link OK", tmp_cr12);
db->link_failed = 0;
+
+ /* CR6 bit18=0, select 10/100M */
+ db->cr6_data &= ~0x00040000;
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* Auto Sense Speed */
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
dmfe_process_mode(db);
@@ -902,13 +981,12 @@ static void dmfe_timer(unsigned long data)
}
/*
- * Dynamic reset the DM910X board
- * Stop DM910X board
- * Free Tx/Rx allocated memory
- * Reset DM910X board
- * Re-initilize DM910X board
+ Dynamic reset the DM910X board
+ Stop DM910X board
+ Free Tx/Rx allocated memory
+ Reset DM910X board
+ Re-initilize DM910X board
*/
-
static void dmfe_dynamic_reset(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -929,6 +1007,7 @@ static void dmfe_dynamic_reset(struct net_device *dev)
/* system variable init */
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
@@ -945,9 +1024,8 @@ static void dmfe_dynamic_reset(struct net_device *dev)
}
/*
- * Free all allocated rx buffer
+ free all allocated rx buffer
*/
-
static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
{
DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
@@ -961,9 +1039,8 @@ static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
}
/*
- * Reused the SK buffer
+ Reused the SK buffer
*/
-
static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
{
struct rx_desc *rxptr = db->rx_insert_ptr;
@@ -979,10 +1056,9 @@ static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
}
/*
- * Initialize transmit/Receive descriptor
- * Using Chain structure, and allocated Tx/Rx buffer
+ Initialize transmit/Receive descriptor
+ Using Chain structure, and allocated Tx/Rx buffer
*/
-
static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
{
struct tx_desc *tmp_tx;
@@ -1033,10 +1109,9 @@ static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
}
/*
- * Update CR6 vaule
- * Firstly stop DM910X , then written value and start
+ Update CR6 vaule
+ Firstly stop DM910X , then written value and start
*/
-
static void update_cr6(u32 cr6_data, u32 ioaddr)
{
u32 cr6_tmp;
@@ -1049,11 +1124,50 @@ static void update_cr6(u32 cr6_data, u32 ioaddr)
/* printk("CR6 update %x ", cr6_tmp); */
}
-/*
- * Send a setup frame
- * This setup frame initilize DM910X addres filter mode
+/* Send a setup frame for DM9132
+ This setup frame initilize DM910X addres filter mode
+ */
+static void dm9132_id_table(struct net_device *dev, int mc_cnt)
+{
+ struct dev_mc_list *mcptr;
+ u16 *addrptr;
+ u32 ioaddr = dev->base_addr + 0xc0; /* ID Table */
+ u32 hash_val;
+ u16 i, hash_table[4];
+
+ DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+ /* Node address */
+ addrptr = (u16 *) dev->dev_addr;
+ outw(addrptr[0], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[1], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[2], ioaddr);
+ ioaddr += 4;
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ /* the multicast address in Hash Table : 64 bits */
+ for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+ }
+
+ /* Write the hash table to MAC MD table */
+ for (i = 0; i < 4; i++, ioaddr += 4) {
+ outw(hash_table[i], ioaddr);
+ }
+}
+
+/* Send a setup frame for DM9102/DM9102A
+ This setup frame initilize DM910X addres filter mode
*/
-
static void send_filter_frame(struct net_device *dev, int mc_cnt)
{
struct dmfe_board_info *db = dev->priv;
@@ -1068,17 +1182,17 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
txptr = db->tx_insert_ptr;
suptr = (u32 *) txptr->tx_buf_ptr;
- /* broadcast address */
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
-
/* Node address */
addrptr = (u16 *) dev->dev_addr;
*suptr++ = addrptr[0];
*suptr++ = addrptr[1];
*suptr++ = addrptr[2];
+ /* broadcast address */
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+
/* fit the multicast address */
for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
addrptr = (u16 *) mcptr->dmi_addr;
@@ -1092,19 +1206,21 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
*suptr++ = 0xffff;
*suptr++ = 0xffff;
}
-
/* prepare the setup frame */
- db->tx_packet_cnt++;
- netif_stop_queue(dev);
- txptr->tdes1 = 0x890000c0;
- txptr->tdes0 = 0x80000000;
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
-
- update_cr6(db->cr6_data | 0x2000, dev->base_addr);
- outl(0x1, dev->base_addr + DCR1);
- update_cr6(db->cr6_data, dev->base_addr);
- dev->trans_start = jiffies;
-
+ txptr->tdes1 = 0x890000c0;
+ /* Resource Check and Send the setup packet */
+ if (!db->tx_packet_cnt) {
+ /* Resource Empty */
+ db->tx_packet_cnt++;
+ txptr->tdes0 = 0x80000000;
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ update_cr6(db->cr6_data, dev->base_addr);
+ } else {
+ /* Put into TX queue */
+ db->tx_queue_cnt++;
+ }
}
/*
@@ -1132,9 +1248,8 @@ static void allocated_rx_buffer(struct dmfe_board_info *db)
}
/*
- * Read one word data from the serial ROM
+ Read one word data from the serial ROM
*/
-
static u16 read_srom_word(long ioaddr, int offset)
{
int i;
@@ -1170,35 +1285,6 @@ static u16 read_srom_word(long ioaddr, int offset)
}
/*
- * Parser Control media block to get Phy address
- */
-
-static void parser_ctrl_info(struct dmfe_board_info *db)
-{
- int i;
- char *sdata = db->srom;
- unsigned char count;
-
- /* point to info leaf0 */
- count = *(sdata + 33);
-
- /* Point to First media block */
- sdata += 34;
- for (i = 0; i < count; i++) {
- if (*(sdata + 1) == 1) {
- db->phy_addr = *(sdata + 2);
- break;
- }
- sdata += ((unsigned char) *(sdata) & 0x7f) + 1;
- }
-
- if (i >= count) {
- printk("Can't found Control Block\n");
- db->phy_addr = 1;
- }
-}
-
-/*
* Auto sense the media mode
*/
@@ -1209,13 +1295,16 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
for (i = 1000; i; i--) {
DELAY_5US;
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 1);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
if ((phy_mode & 0x24) == 0x24)
break;
}
if (i) {
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000;
+ if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
+ else /* DM9102/DM9102A */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000;
/* printk("Phy_mode %x ",phy_mode); */
switch (phy_mode) {
case 0x1000:
@@ -1231,23 +1320,22 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
db->op_mode = DMFE_100MFD;
break;
default:
- db->op_mode = DMFE_100MHF;
- DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode);
+ db->op_mode = DMFE_10MHF;
+ DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
break;
}
} else {
- db->op_mode = DMFE_100MHF;
+ db->op_mode = DMFE_10MHF;
DMFE_DBUG(0, "Link Failed :", phy_mode);
}
}
/*
- * Process op-mode
- * AUTO mode : PHY controller in Auto-negotiation Mode
- * Force mode: PHY controller in force mode with HUB
- * N-way force capability with SWITCH
+ Process op-mode
+ AUTO mode : PHY controller in Auto-negotiation Mode
+ Force mode: PHY controller in force mode with HUB
+ N-way force capability with SWITCH
*/
-
static void dmfe_process_mode(struct dmfe_board_info *db)
{
u16 phy_reg;
@@ -1259,11 +1347,11 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */
/* User force the media type */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 5);
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
/* printk("Nway phy_reg5 %x ",phy_reg); */
if (phy_reg & 0x1) {
/* parter own the N-Way capability */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0;
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
switch (db->op_mode) {
case DMFE_10MHF:
phy_reg |= 0x20;
@@ -1278,7 +1366,7 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg |= 0x100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 4, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
} else {
/* parter without the N-Way capability */
switch (db->op_mode) {
@@ -1295,95 +1383,109 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg = 0x2100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 0, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
}
}
}
/*
- * Write a word to Phy register
+ Write a word to Phy register
*/
-
-static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data)
+static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
{
u16 i;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ ioaddr = iobase + 0x80 + offset * 4;
+ outw(phy_data, ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send write command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* written trasnition */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Write a word data to PHY controller */
- for (i = 0x8000; i > 0; i >>= 1)
- phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Write a word data to PHY controller */
+ for (i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ }
}
/*
- * Read a word data from phy register
+ Read a word data from phy register
*/
-
-static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset)
+static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset, u32 chip_id)
{
int i;
u16 phy_data;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ /* DM9132 Chip */
+ ioaddr = iobase + 0x80 + offset * 4;
+ phy_data = inw(ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+
+ ioaddr = iobase + DCR9;
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send read command(10) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Skip transition state */
- phy_read_1bit(ioaddr);
+ /* Skip transition state */
+ phy_read_1bit(ioaddr);
- /* read 16bit data */
- for (phy_data = 0, i = 0; i < 16; i++) {
- phy_data <<= 1;
- phy_data |= phy_read_1bit(ioaddr);
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr);
+ }
}
return phy_data;
}
/*
- * Write one bit data to Phy Controller
+ Write one bit data to Phy Controller
*/
-
static void phy_write_1bit(u32 ioaddr, u32 phy_data)
{
outl(phy_data, ioaddr); /* MII Clock Low */
@@ -1395,9 +1497,8 @@ static void phy_write_1bit(u32 ioaddr, u32 phy_data)
}
/*
- * Read one bit phy data from PHY controller
+ Read one bit phy data from PHY controller
*/
-
static u16 phy_read_1bit(u32 ioaddr)
{
u16 phy_data;
@@ -1412,10 +1513,11 @@ static u16 phy_read_1bit(u32 ioaddr)
}
/*
- * Calculate the CRC valude of the Rx packet
+ Calculate the CRC valude of the Rx packet
+ flag = 1 : return the reverse CRC (for the received packet CRC)
+ 0 : return the normal CRC (for Hash Table index)
*/
-
-static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
+unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
{
unsigned long Crc = 0xffffffff;
@@ -1423,8 +1525,10 @@ static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
}
- return ~Crc;
-
+ if (flag)
+ return ~Crc;
+ else
+ return Crc;
}
@@ -1461,7 +1565,7 @@ static int __init dmfe_init_module(void)
break;
}
- return dmfe_reg_board(); /* search board and register */
+ return dmfe_probe(); /* search board and register */
}
/*
@@ -1473,14 +1577,16 @@ static int __init dmfe_init_module(void)
static void __exit dmfe_cleanup_module(void)
{
struct net_device *next_dev;
+ struct dmfe_board_info *db;
DMFE_DBUG(0, "clean_module()", 0);
while (dmfe_root_dev) {
- next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+ db = dmfe_root_dev->priv;
+ next_dev = db->next_dev;
unregister_netdev(dmfe_root_dev);
- release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE);
- kfree(dmfe_root_dev->priv); /* free board information */
+ release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+ kfree(db); /* free board information */
kfree(dmfe_root_dev); /* free device structure */
dmfe_root_dev = next_dev;
}
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 03c69417a..d5230ddda 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -712,6 +712,7 @@ static unsigned short eexp_start_irq(struct net_device *dev,
ack_cmd |= SCB_RUstart;
scb_wrrfa(dev, lp->rx_buf_start);
lp->rx_ptr = lp->rx_buf_start;
+ lp->started |= STARTED_RU;
}
ack_cmd |= SCB_CUstart | 0x2000;
}
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 1c546b4d1..f855d1978 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -1071,7 +1071,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (! netif_running(dev)) {
outl(0x0200, ioaddr + GENCTL);
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
index 820bc5d4f..e9f0254a7 100644
--- a/drivers/net/eql.c
+++ b/drivers/net/eql.c
@@ -42,7 +42,7 @@ static const char *version =
* reformatted.
*
* Revision 3.12 1995/03/22 21:07:51 anarchy
- * Added suser() checks on configuration.
+ * Added capable() checks on configuration.
* Moved header file.
*
* Revision 3.11 1995/01/19 23:14:31 guru
@@ -1030,6 +1030,8 @@ int init_module(void)
void cleanup_module(void)
{
+ kfree(((equalizer_t *)dev_eql.priv)->stats );
+ kfree(dev_eql.priv);
unregister_netdev(&dev_eql);
}
#endif /* MODULE */
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 90c82b341..ae92696c9 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -1836,7 +1836,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
status = -EFAULT;
break;
case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 1;
} else {
status = -EPERM;
@@ -1844,7 +1844,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
break;
case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 0;
} else {
status = -EPERM;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 98d228b91..e770baacf 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -1254,7 +1254,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
bc->ch_params.tx_delay = hi.data.cp.tx_delay;
bc->ch_params.tx_tail = hi.data.cp.tx_tail;
@@ -1275,7 +1275,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = hi.data.mp.iobase;
dev->irq = /*hi.data.mp.irq*/0;
@@ -1299,6 +1299,8 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
return 0;
@@ -1314,7 +1316,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODE:
- if (!suser() || netif_running(dev))
+ if (!capable(CAP_NET_ADMIN) || netif_running(dev))
return -EACCES;
hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
return baycom_setmode(bc, hi.data.modename);
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
index b68063b07..63dc93cfb 100644
--- a/drivers/net/hamradio/baycom_par.c
+++ b/drivers/net/hamradio/baycom_par.c
@@ -445,7 +445,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index cdcab2621..a90eb0f8c 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -555,7 +555,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
index 64b27286c..111373ca9 100644
--- a/drivers/net/hamradio/baycom_ser_hdx.c
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -598,7 +598,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 0cc9aa7c1..a48b3f6c6 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -366,7 +366,7 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct bpqdev *bpq = dev->priv;
struct bpq_req req;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (bpq == NULL) /* woops! */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index a3560c207..eac33513d 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -635,7 +635,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
s->ch_params.tx_delay = bi.data.cp.tx_delay;
s->ch_params.tx_tail = bi.data.cp.tx_tail;
@@ -656,7 +656,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = bi.data.mp.iobase;
dev->irq = bi.data.mp.irq;
@@ -684,6 +684,8 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if(!capable(CAP_SYS_RAWIO))
+ return -EPERM;
s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
return 0;
diff --git a/drivers/net/hamradio/pi2.c b/drivers/net/hamradio/pi2.c
index b52a254c8..220c4d920 100644
--- a/drivers/net/hamradio/pi2.c
+++ b/drivers/net/hamradio/pi2.c
@@ -1580,7 +1580,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1597,7 +1597,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & 2) { /* if A channel */
diff --git a/drivers/net/hamradio/pt.c b/drivers/net/hamradio/pt.c
index 59762edd7..8baff63ff 100644
--- a/drivers/net/hamradio/pt.c
+++ b/drivers/net/hamradio/pt.c
@@ -998,7 +998,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1015,7 +1015,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & CHANA) { /* if A channel */
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index 09bb422f1..c7f27414f 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1791,7 +1791,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int found = 1;
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg) return -EFAULT;
if (Nchips >= SCC_MAXCHIPS)
@@ -1887,7 +1887,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (cmd == SIOCSCCINI)
{
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
if (Nchips == 0)
@@ -1904,7 +1904,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
if (cmd == SIOCSCCCHANINI)
{
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg) return -EINVAL;
scc->stat.bufsize = SCC_BUFSIZE;
@@ -1957,7 +1957,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -ENOIOCTLCMD;
case SIOCSCCSMEM:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
return -EINVAL;
scc->stat.bufsize = memcfg.bufsize;
@@ -1977,13 +1977,13 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return 0;
case SIOCSCCSKISS:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
return -EINVAL;
return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
case SIOCSCCCAL:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
return -EINVAL;
diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c
index 4e5e3e9b9..af04a2c87 100644
--- a/drivers/net/hamradio/soundmodem/sm.c
+++ b/drivers/net/hamradio/soundmodem/sm.c
@@ -509,7 +509,7 @@ static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return sethw(dev, sm, hi->data.modename);
diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c
index efc2a3295..772940049 100644
--- a/drivers/net/hamradio/soundmodem/sm_sbc.c
+++ b/drivers/net/hamradio/soundmodem/sm_sbc.c
@@ -1,4 +1,4 @@
-/*****************************************************************************/
+
/*
* sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver
@@ -576,7 +576,7 @@ static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
switch (SCSTATE->revhi) {
case 2:
diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c
index f1dc3744e..23abd970a 100644
--- a/drivers/net/hamradio/soundmodem/sm_wss.c
+++ b/drivers/net/hamradio/soundmodem/sm_wss.c
@@ -637,7 +637,7 @@ static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
!SCSTATE->crystal) &&
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 400facbf8..27929e78b 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -962,7 +962,7 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (yp == NULL || yp->magic != YAM_MAGIC)
return -EINVAL;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd != SIOCDEVPRIVATE)
@@ -977,6 +977,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (netif_running(dev))
return -EINVAL; /* Cannot change this parameter when up */
ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC);
+ if(ym==NULL)
+ return -ENOBUFS;
ym->bitrate = 9600;
if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs)))
return -EFAULT;
@@ -987,6 +989,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCYAMSCFG:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
return -EFAULT;
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index 689ffbd22..ce94c3139 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -14,22 +14,8 @@
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- Fixing alignment problem with 1.3.* kernel and some minor changes
- by Andrey V. Savochkin, 1996.
-
- Problems or questions may be send to Donald Becker (see above) or to
- Andrey Savochkin -- saw@shade.msu.ru or
- Laboratory of Computation Methods,
- Department of Mathematics and Mechanics,
- Moscow State University,
- Leninskye Gory, Moscow 119899
-
- But I should to inform you that I'm not an expert in the LANCE card
- and it may occurs that you will receive no answer on your mail
- to Donald Becker. I didn't receive any answer on all my letters
- to him. Who knows why... But may be you are more lucky? ;->
- SAW
-
+ Andrey V. Savochkin:
+ - alignment problem with 1.3.* kernel and some minor changes.
Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
- added support for Linux/Alpha, but removed most of it, because
it worked only for the PCI chip.
@@ -785,11 +771,19 @@ lance_open(struct net_device *dev)
*/
static void
-lance_purge_tx_ring(struct net_device *dev)
+lance_purge_ring(struct net_device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = 0;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb(skb);
+ }
for (i = 0; i < TX_RING_SIZE; i++) {
if (lp->tx_skbuff[i]) {
dev_kfree_skb(lp->tx_skbuff[i]);
@@ -850,7 +844,7 @@ lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
- lance_purge_tx_ring(dev);
+ lance_purge_ring(dev);
lance_init_ring(dev, GFP_ATOMIC);
}
outw(0x0000, dev->base_addr + LANCE_ADDR);
@@ -869,7 +863,7 @@ static void lance_tx_timeout (struct net_device *dev)
outw (0x0004, ioaddr + LANCE_DATA);
lp->stats.tx_errors++;
#ifndef final_version
- {
+ if (lance_debug > 3) {
int i;
printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
@@ -1192,19 +1186,7 @@ lance_close(struct net_device *dev)
}
free_irq(dev->irq, dev);
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = lp->rx_skbuff[i];
- lp->rx_skbuff[i] = 0;
- lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
- if (skb)
- dev_kfree_skb(skb);
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i])
- dev_kfree_skb(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = 0;
- }
+ lance_purge_ring(dev);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 2b5624b29..53f841af2 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -920,8 +920,6 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
"status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS));
- netif_stop_queue (dev);
-
outw(skb->len, ioaddr + TX_FIFO);
outw(0, ioaddr + TX_FIFO);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
@@ -929,13 +927,13 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
/* TxFree appears only in Window 1, not offset 0x1c. */
- if (inw(ioaddr + TxFree) > 1536) {
- netif_start_queue (dev);
- } else
+ if (inw(ioaddr + TxFree) <= 1536) {
+ netif_stop_queue (dev);
/* Interrupt us when the FIFO has room for max-sized packet.
The threshold is in units of dwords. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-
+ }
+
dev_kfree_skb (skb);
pop_tx_status(dev);
@@ -976,8 +974,6 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue (dev);
- } else {
- netif_stop_queue (dev);
}
if (status & TxComplete)
diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in
index 606d5a606..534a4bdbd 100644
--- a/drivers/net/pcmcia/Config.in
+++ b/drivers/net/pcmcia/Config.in
@@ -18,7 +18,6 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
if [ "$CONFIG_CARDBUS" = "y" ]; then
dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
- dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m
fi
bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile
index 11c583dac..5d0d36f4a 100644
--- a/drivers/net/pcmcia/Makefile
+++ b/drivers/net/pcmcia/Makefile
@@ -20,7 +20,6 @@ obj- :=
export-objs := ray_cs.o
CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE
-CFLAGS_tulip_cb.o = -DCARDBUS -DMODULE
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
@@ -40,7 +39,6 @@ obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o
# Cardbus client drivers
obj-$(CONFIG_PCMCIA_3C575) += 3c575_cb.o
-obj-$(CONFIG_PCMCIA_TULIP) += tulip_cb.o
O_OBJS := $(filter-out $(export-objs), $(obj-y))
OX_OBJS := $(filter $(export-objs), $(obj-y))
diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c
index 7c763cf8e..3c088df36 100644
--- a/drivers/net/pcmcia/ray_cs.c
+++ b/drivers/net/pcmcia/ray_cs.c
@@ -1417,7 +1417,7 @@ static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
#define SIOCGIPFRAMING SIOCDEVPRIVATE + 1 /* Get framing mode */
#define SIOCGIPCOUNTRY SIOCDEVPRIVATE + 3 /* Get country code */
case SIOCSIPFRAMING:
- if(!suser()) /* For private IOCTLs, we need to check permissions */
+ if(!capable(CAP_NET_ADMIN)) /* For private IOCTLs, we need to check permissions */
{
err = -EPERM;
break;
diff --git a/drivers/net/pcmcia/tulip_cb.c b/drivers/net/pcmcia/tulip_cb.c
deleted file mode 100644
index 38dcdc0c1..000000000
--- a/drivers/net/pcmcia/tulip_cb.c
+++ /dev/null
@@ -1,3150 +0,0 @@
-/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
-/*
- Written/copyright 1994-1999 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU Public License, incorporated herein by reference.
-
- This driver is for the Digital "Tulip" Ethernet adapter interface.
- It should work with most DEC 21*4*-based chips/ethercards, as well as
- with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
-
- The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- Center of Excellence in Space Data and Information Sciences
- Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-
- Support and updates available at
- http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-*/
-
-#define SMP_CHECK
-static const char version[] = "tulip.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
-
-/* A few user-configurable values. */
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 25;
-
-#define MAX_UNITS 8
-/* Used to pass the full-duplex flag, etc. */
-static int full_duplex[MAX_UNITS] = {0, };
-static int options[MAX_UNITS] = {0, };
-static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */
-
-/* The possible media types that can be set in options[] are: */
-static const char * const medianame[] = {
- "10baseT", "10base2", "AUI", "100baseTx",
- "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
- "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
- "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-
-/* Keep the ring sizes a power of two for efficiency.
- Making the Tx ring too large decreases the effectiveness of channel
- bonding and packet priority.
- There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 32
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#ifdef __alpha__
-static int rx_copybreak = 1518;
-#else
-static int rx_copybreak = 100;
-#endif
-
-/*
- Set the bus performance register.
- Typical: Set 16 longword cache alignment, no burst limit.
- Cache alignment bits 15:14 Burst length 13:8
- 0000 No alignment 0x00000000 unlimited 0800 8 longwords
- 4000 8 longwords 0100 1 longword 1000 16 longwords
- 8000 16 longwords 0200 2 longwords 2000 32 longwords
- C000 32 longwords 0400 4 longwords
- Warning: many older 486 systems are broken and require setting 0x00A04800
- 8 longword cache alignment, 8 longword burst.
- ToDo: Non-Intel setting could be better.
-*/
-
-#if defined(__alpha__)
-static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__powerpc__)
-static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
-static int csr0 = 0x01B00080 | 0x8000;
-#elif defined(__i386__)
-static int csr0 = 0x01A00000 | 0x8000;
-#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
-#endif
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (4*HZ)
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
- to support a pre-NWay full-duplex signaling mechanism using short frames.
- No one knows what it should be, but if left at its default value some
- 10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC 0x6969
-
-#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
-#warning You must compile this file with the correct options!
-#warning See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <asm/processor.h> /* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-
-/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
- This is only in the support-all-kernels source code. */
-
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(csr0, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define tulip_debug debug
-#ifdef TULIP_DEBUG
-static int tulip_debug = TULIP_DEBUG;
-#else
-static int tulip_debug = 1;
-#endif
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the DECchip "Tulip", Digital's
-single-chip ethernet controllers for PCI. Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike
-chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
-supported.
-
-These chips are used on at least 140 unique PCI board designs. The great
-number of chips and board designs supported is the reason for the
-driver size and complexity. Almost of the increasing complexity is in the
-board configuration and media selection code. There is very little
-increasing in the operational critical path length.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS preferably should assign the
-PCI INTA signal to an otherwise unused system IRQ line.
-
-Some boards have EEPROMs tables with default media entry. The factory default
-is usually "autoselect". This should only be overridden when using
-transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
-for forcing full-duplex when used with old link partners that do not do
-autonegotiation.
-
-III. Driver operation
-
-IIIa. Ring buffers
-
-The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-This driver uses statically allocated rings of Rx and Tx descriptors, set at
-compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs
-for the Rx ring buffers at open() time and passes the skb->data field to the
-Tulip as receive data buffers. When an incoming frame is less than
-RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
-copied to the new skbuff. When the incoming frame is larger, the skbuff is
-passed directly up the protocol stack and replaced by a newly allocated
-skbuff.
-
-The RX_COPYBREAK value is chosen to trade-off the memory wasted by
-using a full-sized skbuff for small frames vs. the copying costs of larger
-frames. For small frames the copying cost is negligible (esp. considering
-that we are pre-loading the cache with immediately useful header
-information). For large frames the copying cost is non-trivial, and the
-larger copy might flush the cache of useful data. A subtle aspect of this
-choice is that the Tulip only receives into longword aligned buffers, thus
-the IP header at offset 14 isn't longword aligned for further processing.
-Copied frames are put into the new skbuff at an offset of "+2", thus copying
-has the beneficial effect of aligning the IP header and preloading the
-cache.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'tp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
-Greg LaPolla at Linksys provided PNIC and other Linksys boards.
-Znyx provided a four-port card for testing.
-
-IVb. References
-
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
-http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM")
-http://www.national.com/pf/DP/DP83840A.html
-http://www.asix.com.tw/pmac.htm
-http://www.admtek.com.tw/
-
-IVc. Errata
-
-The old DEC databooks were light on details.
-The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written. Hmmm, now how is that possible?
-
-The DEC SROM format is very badly designed not precisely defined, leading to
-part of the media selection junkheap below. Some boards do not have EEPROM
-media tables and need to be patched up. Worse, other boards use the DEC
-design kit media table when it isn't correct for their board.
-
-We cannot use MII interrupts because there is no defined GPIO pin to attach
-them. The MII transceiver status is polled using an kernel timer.
-
-*/
-
-/* This table use during operation for capabilities and media timer. */
-
-static void tulip_timer(unsigned long data);
-static void t21142_timer(unsigned long data);
-static void mxic_timer(unsigned long data);
-static void pnic_timer(unsigned long data);
-static void comet_timer(unsigned long data);
-
-enum tbl_flag {
- HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
- HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
- HAS_NWAY143=0x40, /* Uses 21143-like internal NWay. */
-};
-static struct tulip_chip_table {
- char *chip_name;
- int io_size;
- int valid_intrs; /* CSR7 interrupt enable settings */
- int flags;
- void (*media_timer)(unsigned long data);
-} tulip_tbl[] = {
- { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
- { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
- { "Digital DS21140 Tulip", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
- { "Digital DS21143 Tulip", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer },
- { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
- HAS_MII, pnic_timer },
- { "Macronix 98713 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Macronix 98715 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "Macronix 98725 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "ASIX AX88140", 128, 0x0001fbff,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
- { "Lite-On PNIC-II", 256, 0x0001ebef,
- HAS_MII | HAS_NWAY143, pnic_timer },
- { "ADMtek Comet", 256, 0x0001abef,
- MC_HASH_ONLY, comet_timer },
- { "Compex 9881 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
- HAS_MII | HAS_ACPI, tulip_timer },
- {0},
-};
-/* This matches the table above. Note 21142 == 21143. */
-enum chips {
- DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
- LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
- X3201_3,
-};
-
-/* A full-duplex map for media types. */
-enum MediaIs {
- MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
- MediaIs100=16};
-static const char media_cap[] =
-{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 };
-static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0};
-/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
-static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
-static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
-static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
-static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-/* Offsets to the Command and Status Registers, "CSRs". All accesses
- must be longword instructions and quadword aligned. */
-enum tulip_offsets {
- CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
- CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
- CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
-
-/* The bits in the CSR5 status registers, mostly interrupt sources. */
-enum status_bits {
- TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
- NormalIntr=0x10000, AbnormalIntr=0x8000,
- RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
- TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
-};
-
-/* The Tulip Rx and Tx buffer descriptors. */
-struct tulip_rx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2;
-};
-
-struct tulip_tx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2; /* We use only buffer 1. */
-};
-
-enum desc_status_bits {
- DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-
-/* Ring-wrap flag in length field, use for last ring entry.
- 0x01000000 means chain on buffer2 address,
- 0x02000000 means use the ring start address in CSR2/3.
- Note: Some work-alike chips do not function correctly in chained mode.
- The ASIX chip works only in chained mode.
- Thus we indicates ring mode, but always write the 'next' field for
- chained mode as well.
-*/
-#define DESC_RING_WRAP 0x02000000
-
-#ifdef CARDBUS
-#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6)
-#else
-#define EEPROM_ADDRLEN 6
-#endif
-#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
-
-struct medialeaf {
- u8 type;
- u8 media;
- unsigned char *leafdata;
-};
-
-struct mediatable {
- u16 defaultmedia;
- u8 leafcount, csr12dir; /* General purpose pin directions. */
- unsigned has_mii:1, has_nonmii:1, has_reset:6;
- u32 csr15dir, csr15val; /* 21143 NWay setting. */
- struct medialeaf mleaf[0];
-};
-
-struct mediainfo {
- struct mediainfo *next;
- int info_type;
- int index;
- unsigned char *info;
-};
-
-struct tulip_private {
- char devname[8]; /* Used only for kernel debugging. */
- const char *product_name;
- struct tulip_rx_desc rx_ring[RX_RING_SIZE];
- struct tulip_tx_desc tx_ring[TX_RING_SIZE];
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
- /* The X3201-3 requires double word aligned tx bufs */
- struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
- /* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- char *rx_buffs; /* Address of temporary Rx buffers. */
- u8 setup_buf[96*sizeof(u16) + 7];
- u16 *setup_frame; /* Pseudo-Tx frame to init address table. */
- int chip_id;
- int revision;
- struct net_device_stats stats;
- struct timer_list timer; /* Media selection timer. */
- int interrupt; /* In-interrupt flag. */
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int full_duplex_lock:1;
- unsigned int fake_addr:1; /* Multiport board faked address. */
- unsigned int default_port:4; /* Last dev->if_port value. */
- unsigned int media2:4; /* Secondary monitored media port. */
- unsigned int medialock:1; /* Don't sense media type. */
- unsigned int mediasense:1; /* Media sensing in progress. */
- unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */
- unsigned int open:1;
- unsigned int csr0; /* CSR0 setting. */
- unsigned int csr6; /* Current CSR6 control settings. */
- unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
- u16 to_advertise; /* NWay capabilities advertised. */
- u16 lpar; /* 21143 Link partner ability. */
- u16 advertising[4];
- signed char phys[4], mii_cnt; /* MII device addresses. */
- struct mediatable *mtable;
- int cur_index; /* Current media index. */
- int saved_if_port;
- struct pci_dev *pdev;
- spinlock_t lock;
- int pad0, pad1; /* Used for 8-byte alignment */
-};
-
-static void parse_eeprom(struct net_device *dev);
-static int read_eeprom(long ioaddr, int location, int addr_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static void select_media(struct net_device *dev, int startup);
-static void tulip_up(struct net_device *dev);
-static void tulip_down(struct net_device *dev);
-static int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
-static void t21142_start_nway(struct net_device *dev);
-static void tulip_tx_timeout(struct net_device *dev);
-static void tulip_init_ring(struct net_device *dev);
-static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int tulip_close(struct net_device *dev);
-static struct net_device_stats *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
-static void set_rx_mode(struct net_device *dev);
-
-/* The Xircom cards are picky about when certain bits in CSR6 can be
- manipulated. Keith Owens <kaos@ocs.com.au>. */
-
-static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
-{
- const int strict_bits = 0x0060e202;
- int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
- long flags;
- save_flags(flags);
- cli();
- if (chip_idx != X3201_3) {
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
- return;
- }
- newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
- /* read 0 on the Xircom cards */
- newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
- currcsr6 = inl(ioaddr + CSR6);
- if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
- ((currcsr6 & ~0x2002) == 0)) {
- outl(newcsr6, ioaddr + CSR6); /* safe */
- restore_flags(flags);
- return;
- }
- /* make sure the transmitter and receiver are stopped first */
- currcsr6 &= ~0x2002;
- while (1) {
- csr5 = inl(ioaddr + CSR5);
- if (csr5 == 0xffffffff)
- break; /* cannot read csr5, card removed? */
- csr5_22_20 = csr5 & 0x700000;
- csr5_19_17 = csr5 & 0x0e0000;
- if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
- (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
- break; /* both are stopped or suspended */
- if (!--attempts) {
- printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
- "csr5=0x%08x\n", csr5);
- outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */
- restore_flags(flags);
- return;
- }
- outl(currcsr6, ioaddr + CSR6);
- udelay(1);
- }
- /* now it is safe to change csr6 */
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
-}
-
-static struct net_device *tulip_probe1(struct pci_dev *pdev,
- struct net_device *dev, long ioaddr, int irq,
- int chip_idx, int board_idx)
-{
- static int did_version = 0; /* Already printed version info. */
- struct tulip_private *tp;
- /* See note below on the multiport cards. */
- static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
- static int last_irq = 0;
- static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */
- u8 chip_rev;
- int i;
- unsigned short sum;
-
- if (tulip_debug > 0 && did_version++ == 0)
- printk(KERN_INFO "%s", version);
-
- dev = init_etherdev(dev, 0);
-
- pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
- /* Bring the 21143 out of sleep mode.
- Caution: Snooze mode does not work with some boards! */
- if (tulip_tbl[chip_idx].flags & HAS_ACPI)
- pci_write_config_dword(pdev, 0x40, 0x00000000);
-
- printk(KERN_INFO "%s: %s rev %d at %#3lx,",
- dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
-
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
- /* Clear the missed-packet counter. */
- (volatile int)inl(ioaddr + CSR8);
-
- if (chip_idx == DC21041) {
- if (inl(ioaddr + CSR9) & 0x8000) {
- printk(" 21040 compatible mode,");
- chip_idx = DC21040;
- } else {
- printk(" 21041 mode,");
- }
- }
-
- /* The station address ROM is read byte serially. The register must
- be polled, waiting for the value to be read bit serially from the
- EEPROM.
- */
- sum = 0;
- if (chip_idx == DC21040) {
- outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
- for (i = 0; i < 6; i++) {
- int value, boguscnt = 100000;
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- dev->dev_addr[i] = value;
- sum += value & 0xff;
- }
- } else if (chip_idx == LC82C168) {
- for (i = 0; i < 3; i++) {
- int value, boguscnt = 100000;
- outl(0x600 | i, ioaddr + 0x98);
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
- sum += value & 0xffff;
- }
- } else if (chip_idx == COMET) {
- /* No need to read the EEPROM. */
- put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
- put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
- for (i = 0; i < 6; i ++)
- sum += dev->dev_addr[i];
- } else if (chip_idx == X3201_3) {
- /* Xircom has its address stored in the CIS
- * we access it through the boot rom interface for now
- * this might not work, as the CIS is not parsed but I
- * (danilo) use the offset I found on my card's CIS !!!
- *
- * Doug Ledford: I changed this routine around so that it
- * walks the CIS memory space, parsing the config items, and
- * finds the proper lan_node_id tuple and uses the data
- * stored there.
- */
- unsigned char j, tuple, link, data_id, data_count;
- outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
- for (i = 0x100; i < 0x1f7; i += link+2) {
- outl(i, ioaddr + CSR10);
- tuple = inl(ioaddr + CSR9) & 0xff;
- outl(i + 1, ioaddr + CSR10);
- link = inl(ioaddr + CSR9) & 0xff;
- outl(i + 2, ioaddr + CSR10);
- data_id = inl(ioaddr + CSR9) & 0xff;
- outl(i + 3, ioaddr + CSR10);
- data_count = inl(ioaddr + CSR9) & 0xff;
- if ( (tuple == 0x22) &&
- (data_id == 0x04) && (data_count == 0x06) ) {
- /*
- * This is it. We have the data we want.
- */
- for (j = 0; j < 6; j++) {
- outl(i + j + 4, ioaddr + CSR10);
- dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
- }
- break;
- } else if (link == 0) {
- break;
- }
- }
- sum = 1; // to make check below fail!
- } else { /* Must be a new chip, with a serial EEPROM interface. */
- /* We read the whole EEPROM, and sort it out later. DEC has a
- specification _Digital Semiconductor 21X4 Serial ROM Format_
- but early vendor boards just put the address in the first six
- EEPROM locations. */
- unsigned char ee_data[EEPROM_SIZE];
- int sa_offset = 0;
-
- for (i = 0; i < sizeof(ee_data)/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect the simple EEPROM format by the duplicated station addr. */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- sa_offset = 20;
- if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) {
- sa_offset = 2; /* Grrr, damn Matrox boards. */
- multiport_cnt = 4;
- }
- for (i = 0; i < 6; i ++) {
- dev->dev_addr[i] = ee_data[i + sa_offset];
- sum += ee_data[i + sa_offset];
- }
- }
- /* Lite-On boards have the address byte-swapped. */
- if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0)
- && dev->dev_addr[1] == 0x00)
- for (i = 0; i < 6; i+=2) {
- char tmp = dev->dev_addr[i];
- dev->dev_addr[i] = dev->dev_addr[i+1];
- dev->dev_addr[i+1] = tmp;
- }
- /* On the Zynx 315 Etherarray and other multiport boards only the
- first Tulip has an EEPROM.
- The addresses of the subsequent ports are derived from the first.
- Many PCI BIOSes also incorrectly report the IRQ line, so we correct
- that here as well. */
- if (sum == 0 || sum == 6*0xff) {
- printk(" EEPROM not present,");
- for (i = 0; i < 5; i++)
- dev->dev_addr[i] = last_phys_addr[i];
- dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386__) /* Patch up x86 BIOS bug. */
- if (last_irq)
- irq = last_irq;
-#endif
- }
-
- for (i = 0; i < 6; i++)
- printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
- printk(", IRQ %d.\n", irq);
- last_irq = irq;
-
- /* We do a request_region() only to register /proc/ioports info. */
- /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
- request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
-
- /* Make certain the data structures are quadword aligned. */
- tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
- memset(tp, 0, sizeof(*tp));
- dev->priv = tp;
-
- tp->lock = SPIN_LOCK_UNLOCKED;
- tp->pdev = pdev;
- tp->chip_id = chip_idx;
- tp->revision = chip_rev;
- tp->csr0 = csr0;
- tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
-
- /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
- And the ASIX must have a burst limit or horrible things happen. */
- if ( (chip_idx == DC21143 && chip_rev == 65) ||
- (chip_idx == X3201_3) )
- tp->csr0 &= ~0x01000000;
- else if (chip_idx == AX88140)
- tp->csr0 |= 0x2000;
-
-#ifdef TULIP_FULL_DUPLEX
- tp->full_duplex = 1;
- tp->full_duplex_lock = 1;
-#endif
-#ifdef TULIP_DEFAULT_MEDIA
- tp->default_port = TULIP_DEFAULT_MEDIA;
-#endif
-#ifdef TULIP_NO_MEDIA_SWITCH
- tp->medialock = 1;
-#endif
-
- /* The lower four bits are the media type. */
- if (board_idx >= 0 && board_idx < MAX_UNITS) {
- tp->default_port = options[board_idx] & 15;
- if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
- tp->full_duplex = 1;
- if (mtu[board_idx] > 0)
- dev->mtu = mtu[board_idx];
- }
- if (dev->mem_start)
- tp->default_port = dev->mem_start;
- if (tp->default_port) {
- tp->medialock = 1;
- if (media_cap[tp->default_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- }
- if (tp->full_duplex)
- tp->full_duplex_lock = 1;
-
- /* This is logically part of probe1(), but too complex to write inline. */
- if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
- parse_eeprom(dev);
-
- if (media_cap[tp->default_port] & MediaIsMII) {
- u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
- tp->to_advertise = media2advert[tp->default_port - 9];
- } else
- tp->to_advertise = 0x03e1;
-
- if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
- (tp->mtable && tp->mtable->has_mii) ||
- ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) {
- int phy, phy_idx;
- if (tp->mtable && tp->mtable->has_mii) {
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == 11) {
- tp->cur_index = i;
- tp->saved_if_port = dev->if_port;
- select_media(dev, 1);
- dev->if_port = tp->saved_if_port;
- break;
- }
- }
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later,
- but takes much time. */
- for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
- phy++) {
- int mii_status = mdio_read(dev, phy, 1);
- if ((mii_status & 0x8301) == 0x8001 ||
- ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
- int mii_reg0 = mdio_read(dev, phy, 0);
- int mii_advert = mdio_read(dev, phy, 4);
- int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
- tp->phys[phy_idx] = phy;
- tp->advertising[phy_idx++] = reg4;
- printk(KERN_INFO "%s: MII transceiver #%d "
- "config %4.4x status %4.4x advertising %4.4x.\n",
- dev->name, phy, mii_reg0, mii_status, mii_advert);
- /* Fixup for DLink with miswired PHY. */
- if (mii_advert != reg4) {
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d,"
- " previously advertising %4.4x.\n",
- dev->name, reg4, phy, mii_advert);
- mdio_write(dev, phy, 4, reg4);
- }
- /* Enable autonegotiation: some boards default to off. */
- mdio_write(dev, phy, 0, mii_reg0 |
- (tp->full_duplex ? 0x1100 : 0x1000) |
- (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
- }
- }
- tp->mii_cnt = phy_idx;
- if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
- printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
- dev->name);
- tp->phys[0] = 1;
- }
- }
-
- /* The Tulip-specific entries in the device structure. */
- dev->open = &tulip_open;
- dev->hard_start_xmit = &tulip_start_xmit;
- dev->stop = &tulip_close;
- dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
- dev->do_ioctl = &private_ioctl;
-#endif
-#ifdef HAVE_MULTICAST
- dev->set_multicast_list = &set_rx_mode;
-#endif
- dev->tx_timeout = tulip_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- /* Reset the xcvr interface and turn on heartbeat. */
- switch (chip_idx) {
- case DC21041:
- outl(0x00000000, ioaddr + CSR13);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx);
- outl(0x0000EF05, ioaddr + CSR13);
- break;
- case DC21040:
- outl(0x00000000, ioaddr + CSR13);
- outl(0x00000004, ioaddr + CSR13);
- break;
- case DC21140: default:
- if (tp->mtable)
- outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
- break;
- case DC21142:
- case PNIC2:
- if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) {
- outl_CSR6(0x82020000, ioaddr, chip_idx);
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl_CSR6(0x820E0000, ioaddr, chip_idx);
- } else {
- outl_CSR6(0x82420200, ioaddr, chip_idx);
- outl(0x0001, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- outl(0x0001, ioaddr + CSR13);
- outl(0x1301, ioaddr + CSR12); /* Start NWay. */
- }
- break;
- case X3201_3:
- outl(0x0008, ioaddr + CSR15);
- udelay(5); /* The delays are Xircom recommended to give the
- * chipset time to reset the actual hardware
- * on the PCMCIA card
- */
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- outl_CSR6(0x32000200, ioaddr, chip_idx);
- break;
- case LC82C168:
- if ( ! tp->mii_cnt) {
- outl_CSR6(0x00420000, ioaddr, chip_idx);
- outl(0x30, ioaddr + CSR12);
- outl(0x0001F078, ioaddr + 0xB8);
- outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
- }
- break;
- case MX98713: case COMPEX9881:
- outl_CSR6(0x00000000, ioaddr, chip_idx);
- outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
- outl(0x00000001, ioaddr + CSR13);
- break;
- case MX98715: case MX98725:
- outl_CSR6(0x01a80000, ioaddr, chip_idx);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00001000, ioaddr + CSR12);
- break;
- case COMET:
- /* No initialization necessary. */
- break;
- }
-
- return dev;
-}
-
-/* Serial EEPROM section. */
-/* The main routine to parse the very complicated SROM structure.
- Search www.digital.com for "21X4 SROM" to get details.
- This code is very complex, and will require changes to support
- additional cards, so I'll be verbose about what is going on.
- */
-
-/* Known cards that have old-style EEPROMs. */
-static struct fixups {
- char *name;
- unsigned char addr0, addr1, addr2;
- u16 newtable[32]; /* Max length below. */
-} eeprom_fixups[] = {
- {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
- 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
- {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
- 0x0000, 0x009E, /* 10baseT */
- 0x0903, 0x006D, /* 100baseTx */ }},
- {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
- 0x0107, 0x8021, /* 100baseFx */
- 0x0108, 0x8021, /* 100baseFx-FD */
- 0x0103, 0x006D, /* 100baseTx */ }},
- {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
- 0x1001, 0x009E, /* 10base2, CSR12 0x10*/
- 0x0000, 0x009E, /* 10baseT */
- 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
- {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
- 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
- 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
- 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
- }},
- {0, 0, 0, 0, {}}};
-
-static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
-
-#if defined(__i386__) /* AKA get_unaligned() */
-#define get_u16(ptr) (*(u16 *)(ptr))
-#else
-#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
-#endif
-
-static void parse_eeprom(struct net_device *dev)
-{
- /* The last media info list parsed, for multiport boards. */
- static struct mediatable *last_mediatable = NULL;
- static unsigned char *last_ee_data = NULL;
- static int controller_index = 0;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- unsigned char *ee_data = tp->eeprom;
- int i;
-#ifdef CARDBUS
- int chip_rev = tp->revision;
-#endif
-
- tp->mtable = 0;
- for (i = 0; i < EEPROM_SIZE/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect an old-style (SA only) EEPROM layout:
- memcmp(eedata, eedata+16, 8). */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- break;
- if (i >= 8) {
- if (ee_data[0] == 0xff) {
- if (last_mediatable) {
- controller_index++;
- printk(KERN_INFO "%s: Controller %d of multiport board.\n",
- dev->name, controller_index);
- tp->mtable = last_mediatable;
- ee_data = last_ee_data;
- goto subsequent_board;
- } else
- printk(KERN_INFO "%s: Missing EEPROM, this interface may "
- "not work correctly!\n",
- dev->name);
- return;
- }
- /* Do a fix-up based on the vendor half of the station address prefix. */
- for (i = 0; eeprom_fixups[i].name; i++) {
- if (dev->dev_addr[0] == eeprom_fixups[i].addr0
- && dev->dev_addr[1] == eeprom_fixups[i].addr1
- && dev->dev_addr[2] == eeprom_fixups[i].addr2) {
- if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
- i++; /* An Accton EN1207, not an outlaw Maxtech. */
- memcpy(ee_data + 26, eeprom_fixups[i].newtable,
- sizeof(eeprom_fixups[i].newtable));
- printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using"
- " substitute media control info.\n",
- dev->name, eeprom_fixups[i].name);
- break;
- }
- }
- if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
- printk(KERN_INFO "%s: Old style EEPROM with no media selection "
- "information.\n",
- dev->name);
- return;
- }
- }
-
- controller_index = 0;
- if (ee_data[19] > 1) { /* Multiport board. */
- last_ee_data = ee_data;
- }
-subsequent_board:
-
- if (ee_data[27] == 0) { /* No valid media table. */
- } else if (tp->chip_id == DC21041) {
- unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
- short media;
- int count;
-
- media = get_u16(p);
- p += 2;
- count = *p++;
-
- printk(KERN_INFO "%s:21041 Media information at %d, default media "
- "%4.4x (%s).\n", dev->name, ee_data[27], media,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- unsigned char media_code = *p++;
- u16 csrvals[3];
- int idx;
- for (idx = 0; idx < 3; idx++) {
- csrvals[idx] = get_u16(p);
- p += 2;
- }
- if (media_code & 0x40) {
- printk(KERN_INFO "%s: 21041 media %2.2x (%s),"
- " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
- dev->name, media_code & 15, medianame[media_code & 15],
- csrvals[0], csrvals[1], csrvals[2]);
- } else
- printk(KERN_INFO "%s: 21041 media #%d, %s.\n",
- dev->name, media_code & 15, medianame[media_code & 15]);
- }
- } else {
- unsigned char *p = (void *)ee_data + ee_data[27];
- unsigned char csr12dir = 0;
- int count;
- struct mediatable *mtable;
- u16 media = get_u16(p);
-
- p += 2;
- if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
- csr12dir = *p++;
- count = *p++;
- mtable = (struct mediatable *)
- kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
- GFP_KERNEL);
- if (mtable == NULL)
- return; /* Horrible, impossible failure. */
- last_mediatable = tp->mtable = mtable;
- mtable->defaultmedia = media;
- mtable->leafcount = count;
- mtable->csr12dir = csr12dir;
- mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
- mtable->csr15dir = mtable->csr15val = 0;
-
- printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- struct medialeaf *leaf = &mtable->mleaf[i];
-
- if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
- leaf->type = 0;
- leaf->media = p[0] & 0x3f;
- leaf->leafdata = p;
- if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
- mtable->has_mii = 1;
- p += 4;
- } else {
- leaf->type = p[1];
- if (p[1] == 0x05) {
- mtable->has_reset = i;
- leaf->media = p[2] & 0x0f;
- } else if (p[1] & 1) {
- mtable->has_mii = 1;
- leaf->media = 11;
- } else {
- mtable->has_nonmii = 1;
- leaf->media = p[2] & 0x0f;
- if (p[1] == 2) {
- if (leaf->media == 0) {
- mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
- mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
- } else if (leaf->media == 0x40) {
- u32 base15 = get_unaligned((u16*)&p[7]);
- mtable->csr15dir =
- (get_unaligned((u16*)&p[9])<<16) + base15;
- mtable->csr15val =
- (get_unaligned((u16*)&p[11])<<16) + base15;
- }
- }
- }
- leaf->leafdata = p + 2;
- p += (p[0] & 0x3f) + 1;
- }
- if (tulip_debug > 1 && leaf->media == 11) {
- unsigned char *bp = leaf->leafdata;
- printk(KERN_INFO "%s: MII interface PHY %d, setup/reset "
- "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
- dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
- bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
- }
- printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described "
- "by a %s (%d) block.\n",
- dev->name, i, medianame[leaf->media], leaf->media,
- block_name[leaf->type], leaf->type);
- }
- }
-}
-/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
-#define EE_CS 0x01 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x01
-#define EE_WRITE_1 0x05
-#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
-#define EE_ENB (0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
- We add a bus turn-around to insure that this remains true. */
-#define eeprom_delay() inl(ee_addr)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5 << addr_len)
-#define EE_READ_CMD (6 << addr_len)
-#define EE_ERASE_CMD (7 << addr_len)
-
-static int read_eeprom(long ioaddr, int location, int addr_len)
-{
- int i;
- unsigned short retval = 0;
- long ee_addr = ioaddr + CSR9;
- int read_cmd = location | EE_READ_CMD;
-
- outl(EE_ENB & ~EE_CS, ee_addr);
- outl(EE_ENB, ee_addr);
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- outl(EE_ENB | dataval, ee_addr);
- eeprom_delay();
- outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- }
- outl(EE_ENB, ee_addr);
-
- for (i = 16; i > 0; i--) {
- outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
- outl(EE_ENB, ee_addr);
- eeprom_delay();
- }
-
- /* Terminate the EEPROM access. */
- outl(EE_ENB & ~EE_CS, ee_addr);
- return retval;
-}
-
-/* MII transceiver control section.
- Read and write the MII registers using software-generated serial
- MDIO protocol. See the MII specifications or DP83840A data sheet
- for details. */
-
-/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues or future 66Mhz PCI. */
-#define mdio_delay() inl(mdio_addr)
-
-/* Read and write the MII registers using software-generated serial
- MDIO protocol. It is just different enough from the EEPROM protocol
- to not share code. The maxium data clock rate is 2.5 Mhz. */
-#define MDIO_SHIFT_CLK 0x10000
-#define MDIO_DATA_WRITE0 0x00000
-#define MDIO_DATA_WRITE1 0x20000
-#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
-#define MDIO_ENB_IN 0x40000
-#define MDIO_DATA_READ 0x80000
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int retval = 0;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- while (--i > 0)
- if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
- return retval & 0xffff;
- return 0xffff;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id == 1) {
- if (location < 7)
- return inl(ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- return inl(ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- return inl(ioaddr + 0xD4 + ((location-29)<<2));
- }
- return 0xffff;
- }
-
- /* Establish sync by sending at least 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
-
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(cmd, ioaddr + 0xA0);
- do
- if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
- break;
- while (--i > 0);
- return;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id != 1)
- return;
- if (location < 7)
- outl(value, ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- outl(value, ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- outl(value, ioaddr + 0xD4 + ((location-29)<<2));
- return;
- }
-
- /* Establish sync by sending 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return;
-}
-
-static void
-tulip_up(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int i;
-
- /* On some chip revs we must set the MII/SYM port before the reset!? */
- if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- outl_CSR6(0x00040000, ioaddr, tp->chip_id);
-
- /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
- outl(0x00000001, ioaddr + CSR0);
-
- /* Deassert reset. */
- outl(tp->csr0, ioaddr + CSR0);
- udelay(2);
-
- if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
- pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
-
- /* Clear the tx ring */
- for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- }
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
-
- if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
- u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
- if (tp->chip_id == AX88140) {
- outl(0, ioaddr + CSR13);
- outl(addr_low, ioaddr + CSR14);
- outl(1, ioaddr + CSR13);
- outl(addr_high, ioaddr + CSR14);
- } else if (tp->chip_id == COMET) {
- outl(addr_low, ioaddr + 0xA4);
- outl(addr_high, ioaddr + 0xA8);
- outl(0, ioaddr + 0xAC);
- outl(0, ioaddr + 0xB0);
- }
- } else if (tp->chip_id != X3201_3) {
- /* This is set_rx_mode(), but without starting the transmitter. */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[15*6];
-
- /* 21140 bug: you must add the broadcast address. */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* Fill the final entry of the table with our physical address. */
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = 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].status = DescOwned;
-
- tp->cur_tx++;
- } else { /* X3201_3 */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[0*6];
-
- /* fill the table with the broadcast address */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* re-fill the first 14 table entries with our address */
- for(i=0; i<14; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
-
- /* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- /* Lie about the address of our setup frame to make the */
- /* chip happy */
- tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- tp->tx_ring[0].status = DescOwned;
-
- tp->cur_tx++;
- }
- outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
- outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
-
- tp->saved_if_port = dev->if_port;
- if (dev->if_port == 0)
- dev->if_port = tp->default_port;
- if (tp->chip_id == DC21041 && dev->if_port > 4)
- /* Invalid: Select initial TP, autosense, autonegotiate. */
- dev->if_port = 4;
-
- /* Allow selecting a default media. */
- i = 0;
- if (tp->mtable == NULL)
- goto media_picked;
- if (dev->if_port) {
- int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
- (dev->if_port == 12 ? 0 : dev->if_port);
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using user-specified media %s.\n",
- dev->name, medianame[dev->if_port]);
- goto media_picked;
- }
- }
- if ((tp->mtable->defaultmedia & 0x0800) == 0) {
- int looking_for = tp->mtable->defaultmedia & 15;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
- dev->name, medianame[looking_for]);
- goto media_picked;
- }
- }
- /* Start sensing first non-full-duplex media. */
- for (i = tp->mtable->leafcount - 1;
- (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
- ;
-media_picked:
-
- tp->csr6 = 0;
- tp->cur_index = i;
- if (dev->if_port == 0 && tp->chip_id == DC21142) {
- if (tp->mii_cnt) {
- select_media(dev, 1);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Using MII transceiver %d, status "
- "%4.4x.\n",
- dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
- outl_CSR6(0x82020000, ioaddr, tp->chip_id);
- tp->csr6 = 0x820E0000;
- dev->if_port = 11;
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- } else
- t21142_start_nway(dev);
- } else if ((tp->chip_id == LC82C168 || tp->chip_id == PNIC2)
- && tp->mii_cnt && ! tp->medialock) {
- dev->if_port = 11;
- tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0001, ioaddr + CSR15);
- } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
- && ! tp->medialock) {
- dev->if_port = 0;
- tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
- /* Provided by BOLO, Macronix - 12/10/1998. */
- dev->if_port = 0;
- tp->csr6 = 0x01880200;
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
- } else if (tp->chip_id == DC21143 &&
- media_cap[dev->if_port] & MediaIsMII) {
- /* We must reset the media CSRs when we force-select MII mode. */
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- } else if (tp->chip_id == X3201_3) {
- outl(0x0008, ioaddr + CSR15);
- udelay(5);
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- tp->csr6 = 0x32400000;
- } else if (tp->chip_id == COMET) {
- dev->if_port = 0;
- tp->csr6 = 0x00040000;
- } else
- select_media(dev, 1);
-
- /* Start the chip's Tx to process setup frame. */
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
-
- /* Enable interrupts by setting the interrupt mask. */
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- outl(0, ioaddr + CSR2); /* Rx poll demand */
-
- netif_start_queue (dev);
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
- dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
- inl(ioaddr + CSR6));
- }
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = RUN_AT(5*HZ);
- tp->timer.data = (unsigned long)dev;
- tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
- add_timer(&tp->timer);
-}
-
-static int
-tulip_open(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
- return -EAGAIN;
-
- tulip_init_ring(dev);
-
- tulip_up(dev);
- tp->open = 1;
- MOD_INC_USE_COUNT;
-
- return 0;
-}
-
-/* Set up the transceiver control registers for the selected media type. */
-static void select_media(struct net_device *dev, int startup)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- struct mediatable *mtable = tp->mtable;
- u32 new_csr6;
- int i;
-
- if (mtable) {
- struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
- unsigned char *p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: /* 21140 non-MII xcvr. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
- " with control setting %2.2x.\n",
- dev->name, p[1]);
- dev->if_port = p[0];
- if (startup)
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- outl(p[1], ioaddr + CSR12);
- new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
- break;
- case 2: case 4: {
- u16 setup[5];
- u32 csr13val, csr14val, csr15dir, csr15val;
- for (i = 0; i < 5; i++)
- setup[i] = get_u16(&p[i*2 + 1]);
-
- dev->if_port = p[0] & 15;
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
-
- if (startup && mtable->has_reset) {
- struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
- unsigned char *rst = rleaf->leafdata;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
- dev->name);
- for (i = 0; i < rst[0]; i++)
- outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
- "%4.4x/%4.4x.\n",
- dev->name, medianame[dev->if_port], setup[0], setup[1]);
- if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
- csr13val = setup[0];
- csr14val = setup[1];
- csr15dir = (setup[3]<<16) | setup[2];
- csr15val = (setup[4]<<16) | setup[2];
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- outl(csr13val, ioaddr + CSR13);
- } else {
- csr13val = 1;
- csr14val = 0x0003FF7F;
- csr15dir = (setup[0]<<16) | 0x0008;
- csr15val = (setup[1]<<16) | 0x0008;
- if (dev->if_port <= 4)
- csr14val = t21142_csr14[dev->if_port];
- if (startup) {
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- }
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- if (startup) outl(csr13val, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n",
- dev->name, csr15dir, csr15val);
- if (mleaf->type == 4)
- new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
- else
- new_csr6 = 0x82420000;
- break;
- }
- case 1: case 3: {
- int phy_num = p[0];
- int init_length = p[1];
- u16 *misc_info;
- u16 to_advertise;
-
- dev->if_port = 11;
- new_csr6 = 0x020E0000;
- if (mleaf->type == 3) { /* 21142 */
- u16 *init_sequence = (u16*)(p+2);
- u16 *reset_sequence = &((u16*)(p+3))[init_length];
- int reset_length = p[2 + init_length*2];
- misc_info = reset_sequence + reset_length;
- if (startup)
- for (i = 0; i < reset_length; i++)
- outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
- for (i = 0; i < init_length; i++)
- outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
- } else {
- u8 *init_sequence = p + 2;
- u8 *reset_sequence = p + 3 + init_length;
- int reset_length = p[2 + init_length];
- misc_info = (u16*)(reset_sequence + reset_length);
- if (startup) {
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- for (i = 0; i < reset_length; i++)
- outl(reset_sequence[i], ioaddr + CSR12);
- }
- for (i = 0; i < init_length; i++)
- outl(init_sequence[i], ioaddr + CSR12);
- }
- to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
- tp->advertising[phy_num] = to_advertise;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n",
- dev->name, to_advertise, phy_num, tp->phys[phy_num]);
- /* Bogus: put in by a committee? */
- mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
- break;
- }
- default:
- printk(KERN_DEBUG "%s: Invalid media table selection %d.\n",
- dev->name, mleaf->type);
- new_csr6 = 0x020E0000;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12) & 0xff);
- } else if (tp->chip_id == DC21041) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
- dev->name, medianame[dev->if_port & 15],
- inl(ioaddr + CSR12) & 0xffff);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- new_csr6 = 0x80020000;
- } else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) {
- if (startup && ! tp->medialock)
- dev->if_port = tp->mii_cnt ? 11 : 0;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
- " media %s.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
- medianame[dev->if_port]);
- if (tp->mii_cnt) {
- new_csr6 = 0x810C0000;
- outl(0x0001, ioaddr + CSR15);
- outl(0x0201B07A, ioaddr + 0xB8);
- } else if (startup) {
- /* Start with 10mbps to do autonegotiation. */
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x0001B078, ioaddr + 0xB8);
- outl(0x0201B078, ioaddr + 0xB8);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- if (startup)
- outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
- else
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- } else if (tp->chip_id == DC21040) { /* 21040 */
- /* Turn on the xcvr interface. */
- int csr12 = inl(ioaddr + CSR12);
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port], csr12);
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- new_csr6 = 0x20000;
- /* Set the full duplux match frame. */
- outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- if (t21040_csr13[dev->if_port] & 8) {
- outl(0x0705, ioaddr + CSR14);
- outl(0x0006, ioaddr + CSR15);
- } else {
- outl(0xffff, ioaddr + CSR14);
- outl(0x0000, ioaddr + CSR15);
- }
- outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
- } else if (tp->chip_id == X3201_3) { /* Xircom */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
-/* Someone is on crack, the Xircom only does MII, no Fx */
-/* if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;*/
- new_csr6 = 0x324c0000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Xircom CardBus Adapter: "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- } else { /* Unknown chip type with no media table. */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
- if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No media description table, assuming "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- }
-
- tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
- return;
-}
-
-/*
- Check the MII negotiated duplex, and change the CSR6 setting if
- required.
- Return 0 if everything is OK.
- Return < 0 if the transceiver is missing or has no link beat.
- */
-static int check_duplex(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int mii_reg1, mii_reg5, negotiated, duplex;
-
- if (tp->full_duplex_lock)
- return 0;
- mii_reg1 = mdio_read(dev, tp->phys[0], 1);
- mii_reg5 = mdio_read(dev, tp->phys[0], 5);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
- "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
- if (mii_reg1 == 0xffff)
- return -2;
- if ((mii_reg1 & 0x0004) == 0) {
- int new_reg1 = mdio_read(dev, tp->phys[0], 1);
- if ((new_reg1 & 0x0004) == 0) {
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: No link beat on the MII interface,"
- " status %4.4x.\n", dev->name, new_reg1);
- return -1;
- }
- }
- negotiated = mii_reg5 & tp->advertising[0];
- duplex = ((negotiated & 0x0300) == 0x0100
- || (negotiated & 0x00C0) == 0x0040);
- /* 100baseTx-FD or 10T-FD, but not 100-HD */
- if (tp->full_duplex != duplex) {
- tp->full_duplex = duplex;
- if (tp->full_duplex) tp->csr6 |= 0x0200;
- else tp->csr6 &= ~0x0200;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- if (tulip_debug > 0)
- printk(KERN_INFO "%s: Setting %s-duplex based on MII"
- "#%d link partner capability of %4.4x.\n",
- dev->name, tp->full_duplex ? "full" : "half",
- tp->phys[0], mii_reg5);
- }
- return 0;
-}
-
-static void tulip_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u32 csr12 = inl(ioaddr + CSR12);
- int next_tick = 2*HZ;
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
- "SIA %8.8x %8.8x %8.8x %8.8x.\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
- csr12, inl(ioaddr + CSR13),
- inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- }
- switch (tp->chip_id) {
- case DC21040:
- if (!tp->medialock && csr12 & 0x0002) { /* Network error */
- printk(KERN_INFO "%s: No link beat found.\n",
- dev->name);
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- select_media(dev, 0);
- dev->trans_start = jiffies;
- }
- break;
- case DC21041:
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n",
- dev->name, csr12);
- switch (dev->if_port) {
- case 0: case 3: case 4:
- if (csr12 & 0x0004) { /*LnkFail */
- /* 10baseT is dead. Check for activity on alternate port. */
- tp->mediasense = 1;
- if (csr12 & 0x0200)
- dev->if_port = 2;
- else
- dev->if_port = 1;
- printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
- dev->name, medianame[dev->if_port]);
- outl(0, ioaddr + CSR13); /* Reset */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- next_tick = 10*HZ; /* 2.4 sec. */
- } else
- next_tick = 30*HZ;
- break;
- case 1: /* 10base2 */
- case 2: /* AUI */
- if (csr12 & 0x0100) {
- next_tick = (30*HZ); /* 30 sec. */
- tp->mediasense = 0;
- } else if ((csr12 & 0x0004) == 0) {
- printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
- dev->name);
- dev->if_port = 0;
- select_media(dev, 0);
- next_tick = (24*HZ)/10; /* 2.4 sec. */
- } else if (tp->mediasense || (csr12 & 0x0002)) {
- dev->if_port = 3 - dev->if_port; /* Swap ports. */
- select_media(dev, 0);
- next_tick = 20*HZ;
- } else {
- next_tick = 20*HZ;
- }
- break;
- }
- break;
- case DC21140: case DC21142: case MX98713: case COMPEX9881: default: {
- struct medialeaf *mleaf;
- unsigned char *p;
- if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */
- /* Not much that can be done.
- Assume this a generic MII or SYM transceiver. */
- next_tick = 60*HZ;
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
- "CSR12 0x%2.2x.\n",
- dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
- break;
- }
- mleaf = &tp->mtable->mleaf[tp->cur_index];
- p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: case 4: {
- /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */
- int offset = mleaf->type == 4 ? 5 : 2;
- s8 bitnum = p[offset];
- if (p[offset+1] & 0x80) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG"%s: Transceiver monitor tick "
- "CSR12=%#2.2x, no media sense.\n",
- dev->name, csr12);
- if (mleaf->type == 4) {
- if (mleaf->media == 3 && (csr12 & 0x02))
- goto select_next_media;
- }
- break;
- }
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
- " bit %d is %d, expecting %d.\n",
- dev->name, csr12, (bitnum >> 1) & 7,
- (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
- (bitnum >= 0));
- /* Check that the specified bit has the proper value. */
- if ((bitnum < 0) !=
- ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
- medianame[mleaf->media]);
- if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */
- goto actually_mii;
- break;
- }
- if (tp->medialock)
- break;
- select_next_media:
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
- if (media_cap[dev->if_port] & MediaIsFD)
- goto select_next_media; /* Skip FD entries. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No link beat on media %s,"
- " trying transceiver type %s.\n",
- dev->name, medianame[mleaf->media & 15],
- medianame[tp->mtable->mleaf[tp->cur_index].media]);
- select_media(dev, 0);
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- next_tick = (24*HZ)/10;
- break;
- }
- case 1: case 3: /* 21140, 21142 MII */
- actually_mii:
- check_duplex(dev);
- next_tick = 60*HZ;
- break;
- case 2: /* 21142 serial block has no link beat. */
- default:
- break;
- }
- }
- break;
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
- of available transceivers. */
-static void t21142_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = 0;
-
- if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII))
- printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
- dev->name, csr12, medianame[dev->if_port]);
- if (media_cap[dev->if_port] & MediaIsMII) {
- check_duplex(dev);
- next_tick = 60*HZ;
- } else if (tp->nwayset) {
- /* Don't screw up a negotiated session! */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
- dev->name, medianame[dev->if_port], csr12);
- } else if (tp->medialock) {
- ;
- } else if (dev->if_port == 3) {
- if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
- "trying NWay.\n", dev->name, csr12);
- t21142_start_nway(dev);
- next_tick = 3*HZ;
- }
- } else if (((csr12 & 0x7000) != 0x5000)
- && tp->chip_id != X3201_3) {
- /* Negotiation failed. Search media types. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
- dev->name, csr12);
- if (!(csr12 & 4)) { /* 10mbps link beat good. */
- new_csr6 = 0x82420000;
- dev->if_port = 0;
- outl(0, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
- } else {
- /* Select 100mbps port to check for link beat. */
- new_csr6 = 0x83860000;
- dev->if_port = 3;
- outl(0, ioaddr + CSR13);
- outl(0x0003FF7F, ioaddr + CSR14);
- outw(8, ioaddr + CSR15);
- outl(1, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
- dev->name, medianame[dev->if_port]);
- if (new_csr6 != (tp->csr6 & ~0x00D5)) {
- tp->csr6 &= 0x00D5;
- tp->csr6 |= new_csr6;
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- next_tick = 3*HZ;
- }
- if (tp->cur_tx - tp->dirty_tx > 0 &&
- jiffies - dev->trans_start > TX_TIMEOUT) {
- printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- tulip_tx_timeout(dev);
- }
-
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void t21142_start_nway(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr14 = ((tp->to_advertise & 0x0180) << 9) |
- ((tp->to_advertise&0x0020)<<1) | 0xffbf;
-
- dev->if_port = 0;
- tp->nway = tp->mediasense = 1;
- tp->nwayset = tp->lpar = 0;
- if (debug > 1)
- printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
- dev->name, csr14);
- outl(0x0001, ioaddr + CSR13);
- outl(csr14, ioaddr + CSR14);
- tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- if (tp->mtable && tp->mtable->csr15dir) {
- outl(tp->mtable->csr15dir, ioaddr + CSR15);
- outl(tp->mtable->csr15val, ioaddr + CSR15);
- } else
- outw(0x0008, ioaddr + CSR15);
- outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */
-}
-
-static void t21142_lnk_change(struct net_device *dev, int csr5)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
-
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
- "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
-
- /* If NWay finished and we have a negotiated partner capability. */
- if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) {
- int setup_done = 0;
- tp->lpar = csr12 >> 16;
- tp->nwayset = 1;
- if (csr12 & 0x01000000) dev->if_port = 5;
- else if (csr12 & 0x00800000) dev->if_port = 3;
- else if (csr12 & 0x00400000) dev->if_port = 4;
- else if (csr12 & 0x00200000) dev->if_port = 0;
- else {
- tp->nwayset = 0;
- if ( ! (csr12 & 2)) dev->if_port = 3;
- else if ( ! (csr12 & 4)) dev->if_port = 0;
- }
- tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0;
-
- if (tulip_debug > 1) {
- if (tp->nwayset)
- printk(KERN_INFO "%s: Switching to %s based on link partner "
- "advertisement %4.4x.\n",
- dev->name, medianame[dev->if_port], tp->lpar);
- else
- printk(KERN_INFO "%s: Switching to %s based on link beat "
- "status of %4.4x.\n",
- dev->name, medianame[dev->if_port], csr12);
- }
-
- if (tp->mtable) {
- int i;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == dev->if_port) {
- tp->cur_index = i;
- select_media(dev, 0);
- setup_done = 1;
- break;
- }
- }
- if ( ! setup_done) {
- tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
- if (tp->full_duplex)
- tp->csr6 |= 0x0200;
- outw(0x0000, ioaddr + CSR13);
- outw(0x0000, ioaddr + CSR14);
- }
- outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id);
- if (debug > 2)
- printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
- dev->name, inl(ioaddr + CSR5));
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- } else if ((tp->nwayset && (csr5 & 0x08000000)
- && (dev->if_port == 3 || dev->if_port == 5)
- && (csr12 & 2) == 2) ||
- (tp->nway && (csr5 & (TPLnkFail)))) {
- /* Link blew? Maybe restart NWay. */
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
- dev->name, medianame[dev->if_port],
- (csr12 & 2) ? "failed" : "good");
- if ((csr12 & 2) && ! tp->medialock) {
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- }
- } else if (dev->if_port == 0 || dev->if_port == 4) {
- if ((csr12 & 4) == 0)
- printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
- dev->name);
- } else if (!(csr12 & 4)) { /* 10mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
- dev->name);
- dev->if_port = 0;
- } else if (tp->nwayset) {
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
- dev->name, medianame[dev->if_port], tp->csr6);
- } else { /* 100mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
- dev->name);
- dev->if_port = 3;
- tp->csr6 = 0x83860000;
- outl(0x0003FF7F, ioaddr + CSR14);
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
-}
-
-static void mxic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 3) {
- printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
- inl(ioaddr + CSR12));
- }
- if (next_tick) {
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
- }
-}
-
-static void pnic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = tp->csr6 & ~0x40C40200;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, "
- "CSR5 %8.8x.\n",
- dev->name, negotiated, inl(ioaddr + CSR5));
-
- if (negotiated & 0x0380) /* 10 vs 100mbps */
- new_csr6 |= 0x810E0000;
- else
- new_csr6 |= 0x814E0000;
- if (((negotiated & 0x0300) == 0x0100) /* Duplex */
- || (negotiated & 0x00C0) == 0x0040
- || tp->full_duplex_lock) {
- tp->full_duplex = 1;
- new_csr6 |= 0x0200;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link "
- "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
- dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
- tp->csr6, inl(ioaddr + CSR6));
- } else {
- int phy_reg = inl(ioaddr + 0xB8);
- int csr5 = inl(ioaddr + CSR5);
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n",
- dev->name, phy_reg, csr5);
-
- if (phy_reg & 0x04000000) { /* Remote link fault */
- /*outl(0x0201F078, ioaddr + 0xB8);*/
- next_tick = 3*HZ;
- }
- if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
- "CSR5 %8.8x, PHY %3.3x.\n",
- dev->name, medianame[dev->if_port], csr12,
- inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
- if (tp->medialock) {
- } else if (dev->if_port == 0) {
- dev->if_port = 3;
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- dev->if_port = 0;
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- new_csr6 |= (tp->csr6 & 0xfdff);
- next_tick = 3*HZ;
- } else
- new_csr6 = tp->csr6;
- if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) {
- tp->full_duplex = 1;
- new_csr6 |= 0x00000200;
- }
- }
- if (tp->csr6 != new_csr6) {
- tp->csr6 = new_csr6;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); /* Restart Tx */
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- dev->trans_start = jiffies;
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
- "CSR6 %8.8x.\n",
- dev->name, tp->full_duplex ? "full" : "half", new_csr6);
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void comet_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
- "%4.4x.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void tulip_tx_timeout(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- /* Do nothing -- the media monitor should handle this. */
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
- dev->name);
- } else if (tp->chip_id == DC21040) {
- if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) {
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- printk(KERN_INFO "%s: transmit timed out, switching to "
- "%s.\n",
- dev->name, medianame[dev->if_port]);
- select_media(dev, 0);
- }
- dev->trans_start = jiffies;
- return;
- } else if (tp->chip_id == DC21041) {
- int csr12 = inl(ioaddr + CSR12);
-
- printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
- "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), csr12,
- inl(ioaddr + CSR13), inl(ioaddr + CSR14));
- tp->mediasense = 1;
- if ( ! tp->medialock) {
- if (dev->if_port == 1 || dev->if_port == 2)
- if (csr12 & 0x0004) {
- dev->if_port = 2 - dev->if_port;
- } else
- dev->if_port = 0;
- else
- dev->if_port = 1;
- select_media(dev, 0);
- }
- } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
- || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
- printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
- "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
- inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- if ( ! tp->medialock && tp->mtable) {
- do
- --tp->cur_index;
- while (tp->cur_index >= 0
- && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
- & MediaIsFD));
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- select_media(dev, 0);
- printk(KERN_WARNING "%s: transmit timed out, switching to %s "
- "media.\n", dev->name, medianame[dev->if_port]);
- }
- } else {
- printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
- "%8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
- dev->if_port = 0;
- }
-
-#if defined(way_too_many_messages)
- if (tulip_debug > 3) {
- int i;
- for (i = 0; i < RX_RING_SIZE; i++) {
- u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
- int j;
- printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x "
- "%2.2x %2.2x %2.2x.\n",
- i, (unsigned int)tp->rx_ring[i].status,
- (unsigned int)tp->rx_ring[i].length,
- (unsigned int)tp->rx_ring[i].buffer1,
- (unsigned int)tp->rx_ring[i].buffer2,
- buf[0], buf[1], buf[2]);
- for (j = 0; buf[j] != 0xee && j < 1600; j++)
- if (j < 100) printk(" %2.2x", buf[j]);
- printk(" j=%d.\n", j);
- }
- printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
- printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
- printk("\n");
- }
-#endif
-
- /* Stop and restart the chip's Tx processes . */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
-
- dev->trans_start = jiffies;
- netif_wake_queue (dev);
- tp->stats.tx_errors++;
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void tulip_init_ring(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- tp->tx_full = 0;
- tp->cur_rx = tp->cur_tx = 0;
- tp->dirty_rx = tp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- tp->rx_ring[i].status = 0x00000000;
- tp->rx_ring[i].length = PKT_BUF_SZ;
- tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
- tp->rx_skbuff[i] = NULL;
- }
- /* Mark the last entry as wrapping the ring. */
- tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
- tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- /* Note the receive buffer must be longword aligned.
- dev_alloc_skb() provides 16 byte alignment. But do *not*
- use skb_reserve() to align the IP header! */
- struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
- tp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */
- tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
- }
- tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-
- /* 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_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3)
- tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif CARDBUS
- }
- tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
-}
-
-static int
-tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry;
- u32 flag;
-
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- /* Calculate the next Tx descriptor entry. */
- entry = tp->cur_tx % TX_RING_SIZE;
-
- tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3) {
- memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
- } else
-#endif
- tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
-
- if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
- flag = 0x60000000; /* No interrupt */
- } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
- flag = 0xe0000000; /* Tx-done intr. */
- } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
- flag = 0x60000000; /* No Tx-done intr. */
- } else {
- /* Leave room for set_rx_mode() to fill entries. */
- flag = 0xe0000000; /* Tx-done intr. */
- tp->tx_full = 1;
- }
- if (entry == TX_RING_SIZE-1)
- flag |= 0xe0000000 | DESC_RING_WRAP;
-
- tp->tx_ring[entry].length = skb->len | flag;
- tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */
- tp->cur_tx++;
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- /* Trigger an immediate transmit demand. */
- outl(0, dev->base_addr + CSR1);
-
- dev->trans_start = jiffies;
-
- return 0;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_instance;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr5, work_budget = max_interrupt_work;
-
- spin_lock (&tp->lock);
-
- do {
- csr5 = inl(ioaddr + CSR5);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outl(csr5 & 0x0001ffff, ioaddr + CSR5);
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
- dev->name, csr5, inl(dev->base_addr + CSR5));
-
- if (csr5 == 0xffffffff)
- break; /* all bits set, assume PCMCIA card removed */
-
- if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
- break;
-
- if (csr5 & (RxIntr | RxNoBuf))
- work_budget -= tulip_rx(dev);
-
- if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
- unsigned int dirty_tx;
-
- for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
- dirty_tx++) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = tp->tx_ring[entry].status;
-
- if (status < 0)
- break; /* It still hasn't been Txed */
- /* Check for Rx filter setup frames. */
- if (tp->tx_skbuff[entry] == NULL)
- continue;
-
- if (status & 0x8000) {
- /* There was an major error, log it. */
-#ifndef final_version
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
- dev->name, status);
-#endif
- tp->stats.tx_errors++;
- if (status & 0x4104) tp->stats.tx_aborted_errors++;
- if (status & 0x0C00) tp->stats.tx_carrier_errors++;
- if (status & 0x0200) tp->stats.tx_window_errors++;
- if (status & 0x0002) tp->stats.tx_fifo_errors++;
- if ((status & 0x0080) && tp->full_duplex == 0)
- tp->stats.tx_heartbeat_errors++;
-#ifdef ETHER_STATS
- if (status & 0x0100) tp->stats.collisions16++;
-#endif
- } else {
-#ifdef ETHER_STATS
- if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
- tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
- tp->stats.collisions += (status >> 3) & 15;
- tp->stats.tx_packets++;
- }
-
- /* Free the original skb. */
- dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
- }
-
-#ifndef final_version
- if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
- dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- if (tp->tx_full &&
- tp->cur_tx - dirty_tx < TX_RING_SIZE - 2)
- /* The ring is no longer full */
- tp->tx_full = 0;
-
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- tp->dirty_tx = dirty_tx;
- if (csr5 & TxDied) {
- if (tulip_debug > 2)
- printk(KERN_WARNING "%s: The transmitter stopped."
- " CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
- dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- }
-
- /* Log errors. */
- if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */
- if (csr5 == 0xffffffff)
- break;
- if (csr5 & TxJabber) tp->stats.tx_errors++;
- if (csr5 & TxFIFOUnderflow) {
- if ((tp->csr6 & 0xC000) != 0xC000)
- tp->csr6 += 0x4000; /* Bump up the Tx threshold */
- else
- tp->csr6 |= 0x00200000; /* Store-n-forward. */
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & RxDied) { /* Missed a Rx frame. */
- tp->stats.rx_errors++;
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & TimerInt) {
- if (tulip_debug > 2)
- printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
- dev->name, csr5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- }
- if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
- if ( tp->chip_id == DC21142)
- t21142_lnk_change(dev, csr5);
- }
- /* Clear all error sources, included undocumented ones! */
- outl(0x0800f7ba, ioaddr + CSR5);
- }
- if (--work_budget < 0) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Too much work during an interrupt, "
- "csr5=0x%8.8x.\n", dev->name, csr5);
- /* Acknowledge all interrupt sources. */
- outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
- /* Clear all but standard interrupt sources. */
- outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
- break;
- }
- } while (1);
-
- if (tulip_debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- spin_unlock (&tp->lock);
-}
-
-static int
-tulip_rx(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry = tp->cur_rx % RX_RING_SIZE;
- int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
- int work_done = 0;
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- /* If we own the next entry, it's a new packet. Send it up. */
- while (tp->rx_ring[entry].status >= 0) {
- s32 status = tp->rx_ring[entry].status;
-
- if (tulip_debug > 5)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- if (--rx_work_limit < 0)
- break;
- if ((status & 0x38008300) != 0x0300) {
- if ((status & 0x38000300) != 0x0300) {
- /* Ingore earlier buffers. */
- if ((status & 0xffff) != 0x7fff) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Oversized Ethernet frame "
- "spanned multiple buffers, status %8.8x!\n",
- dev->name, status);
- tp->stats.rx_length_errors++;
- }
- } else if (status & RxDescFatalErr) {
- /* There was a fatal error. */
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
- dev->name, status);
- tp->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x0890) tp->stats.rx_length_errors++;
- if (status & 0x0004) tp->stats.rx_frame_errors++;
- if (status & 0x0002) tp->stats.rx_crc_errors++;
- if (status & 0x0001) tp->stats.rx_fifo_errors++;
- }
- } else {
- /* Omit the four octet CRC from the length. */
- short pkt_len = ((status >> 16) & 0x7ff) - 4;
- struct sk_buff *skb;
-
-#ifndef final_version
- if (pkt_len > 1518) {
- printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
- dev->name, pkt_len, pkt_len);
- pkt_len = 1518;
- tp->stats.rx_length_errors++;
- }
-#endif
- /* Check if the packet is long enough to accept without copying
- to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
- skb_reserve(skb, 2); /* 16 byte align the IP header */
-#if ! defined(__alpha__)
- eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
- pkt_len, 0);
- skb_put(skb, pkt_len);
-#else
- memcpy(skb_put(skb, pkt_len),
- bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#endif
- work_done++;
- } else { /* Pass up the skb already on the Rx ring. */
- char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
- tp->rx_skbuff[entry] = NULL;
-#ifndef final_version
- if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
- printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
- "do not match in tulip_rx: %p vs. %p / %p.\n",
- dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
- skb->head, temp);
-#endif
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->last_rx = jiffies;
- tp->stats.rx_packets++;
- tp->stats.rx_bytes += pkt_len;
- }
- entry = (++tp->cur_rx) % RX_RING_SIZE;
- }
-
- /* Refill the Rx ring buffers. */
- for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
- entry = tp->dirty_rx % RX_RING_SIZE;
- if (tp->rx_skbuff[entry] == NULL) {
- struct sk_buff *skb;
- skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
- work_done++;
- }
- tp->rx_ring[entry].status = DescOwned;
- }
-
- return work_done;
-}
-
-static void
-tulip_down(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- /* Disable interrupts by clearing the interrupt mask. */
- outl(0x00000000, ioaddr + CSR7);
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
- /* 21040 -- Leave the card in 10baseT state. */
- if (tp->chip_id == DC21040)
- outl(0x00000004, ioaddr + CSR13);
-
- if (inl(ioaddr + CSR6) != 0xffffffff)
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- dev->if_port = tp->saved_if_port;
-}
-
-static int
-tulip_close(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- netif_stop_queue(dev);
-
- if (netif_device_present(dev))
- tulip_down(dev);
-
- del_timer(&tp->timer);
-
- free_irq(dev->irq, dev);
-
- /* Free all the skbuffs in the Rx queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
- tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
- tp->rx_ring[i].length = 0;
- tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
- if (skb) {
- dev_kfree_skb(skb);
- }
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (tp->tx_skbuff[i])
- dev_kfree_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
- }
-
- MOD_DEC_USE_COUNT;
- tp->open = 0;
- return 0;
-}
-
-static struct net_device_stats *tulip_get_stats(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (netif_device_present(dev))
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- return &tp->stats;
-}
-
-#ifdef HAVE_PRIVATE_IOCTL
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u16 *data = (u16 *)&rq->ifr_data;
- int phy = tp->phys[0] & 0x1f;
- long flags;
-
- switch(cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- if (tp->mii_cnt)
- data[0] = phy;
- else if (tp->chip_id == DC21142) /* 21142 pseudo-MII */
- data[0] = 32;
- else if (tp->chip_id == PNIC2)
- data[0] = 32;
- else if (tp->chip_id == COMET)
- data[0] = 1;
- else
- return -ENODEV;
- return 0;
- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- if (data[0] == 32 &&
- (tp->chip_id == DC21142 || tp->chip_id == PNIC2)) {
- int csr12 = inl(ioaddr + CSR12);
- int csr14 = inl(ioaddr + CSR14);
- switch (data[1]) {
- case 0: {
- data[3] = (csr14<<5) & 0x1000;
- break; }
- case 1:
- data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
- + (csr12&0x06 ? 0x04 : 0);
- break;
- case 4: {
- data[3] = ((csr14>>9)&0x0380) +
- ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
- break;
- }
- case 5: data[3] = csr12 >> 16; break;
- default: data[3] = 0; break;
- }
- } else {
- save_flags(flags);
- cli();
- data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
- restore_flags(flags);
- }
- return 0;
- case SIOCDEVPRIVATE+2: /* Write the specified MII register */
-#if defined(CAP_NET_ADMIN)
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-#else
- if (!suser())
- return -EPERM;
-#endif
- if (data[0] == 32 && tp->chip_id == DC21142) {
- if (data[1] == 5)
- tp->to_advertise = data[2];
- } else {
- save_flags(flags);
- cli();
- mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
- restore_flags(flags);
- }
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-
- return -EOPNOTSUPP;
-}
-#endif /* HAVE_PRIVATE_IOCTL */
-
-/* Set or clear the multicast filter for this adaptor.
- Note that we only use exclusion around actually queueing the
- new frame, not around filling tp->setup_frame. This is non-deterministic
- when re-entered but still correct. */
-
-/* The little-endian AUTODIN32 ethernet CRC calculation.
- N.B. Do not use for bulk data, use a table-based routine instead.
- This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline u32 ether_crc_le(int length, unsigned char *data)
-{
- u32 crc = 0xffffffff; /* Initial value. */
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 8; --bit >= 0; current_octet >>= 1) {
- if ((crc ^ current_octet) & 1) {
- crc >>= 1;
- crc ^= ethernet_polynomial_le;
- } else
- crc >>= 1;
- }
- }
- return crc;
-}
-static unsigned const ethernet_polynomial = 0x04c11db7U;
-static inline u32 ether_crc(int length, unsigned char *data)
-{
- int crc = -1;
-
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 0; bit < 8; bit++, current_octet >>= 1)
- crc = (crc << 1) ^
- ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
- }
- return crc;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- tp->csr6 &= ~0x00D5;
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- tp->csr6 |= 0x00C0;
- csr6 |= 0x00C0;
- /* Unconditionally log net taps. */
- printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
- } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter well -- accept all multicasts. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- /* Some work-alikes have only a 64-entry hash filter table. */
- /* Should verify correctness on big-endian/__powerpc__ */
- struct dev_mc_list *mclist;
- int i;
- u32 mc_filter[2]; /* Multicast hash filter */
- if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else {
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
- if (tp->chip_id == AX88140) {
- outl(2, ioaddr + CSR13);
- outl(mc_filter[0], ioaddr + CSR14);
- outl(3, ioaddr + CSR13);
- outl(mc_filter[1], ioaddr + CSR14);
- } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
- outl(mc_filter[0], ioaddr + 0xAC);
- outl(mc_filter[1], ioaddr + 0xB0);
- }
- }
- } else {
- u16 *eaddrs, *setup_frm = tp->setup_frame;
- struct dev_mc_list *mclist;
- u32 tx_flags = 0x08000000 | 192;
- int i;
-
- /* Note that only the low-address shortword of setup_frame is valid!
- The values are doubled for big-endian architectures. */
- if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */
- u16 hash_table[32];
- tx_flags = 0x08400000 | 192; /* Use hash filter. */
- memset(hash_table, 0, sizeof(hash_table));
- set_bit(255, hash_table); /* Broadcast entry */
- /* This should work on big-endian machines as well. */
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
- hash_table);
- for (i = 0; i < 32; i++) {
- *setup_frm++ = hash_table[i];
- *setup_frm++ = hash_table[i];
- }
- setup_frm = &tp->setup_frame[13*6];
- } else if(tp->chip_id != X3201_3) {
- /* We have <= 14 addresses so we can use the wonderful
- 16 address perfect filtering of the Tulip. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- } else {
- /* fill the first two table entries with our address */
- eaddrs = (u16 *)dev->dev_addr;
- for(i=0; i<2; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Double fill each entry to accomodate chips that */
- /* don't like to parse these correctly */
- for (i=0, mclist=dev->mc_list; i<dev->mc_count;
- i++, mclist=mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- i=((i+1)*2);
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- }
-
- /* Fill the final entry with our physical address. */
- eaddrs = (u16 *)dev->dev_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- /* Now add this frame to the Tx list. */
- if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
- /* Same setup recently queued, we need not add it. */
- } else {
- unsigned long flags;
- unsigned int entry, dummy = -1;
-
- save_flags(flags); cli();
- entry = tp->cur_tx++ % TX_RING_SIZE;
-
- if (entry != 0) {
- /* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
- tp->tx_ring[entry].length =
- (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
- tp->tx_ring[entry].buffer1 = 0;
- /* race with chip, set DescOwned later */
- dummy = entry;
- entry = tp->cur_tx++ % TX_RING_SIZE;
- }
-
- tp->tx_skbuff[entry] = 0;
- /* Put the setup frame on the Tx list. */
- if (entry == TX_RING_SIZE-1)
- tx_flags |= DESC_RING_WRAP; /* Wrap ring. */
- tp->tx_ring[entry].length = tx_flags;
- if(tp->chip_id == X3201_3)
- tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- else
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[entry].status = DescOwned;
- if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
- tp->tx_full = 1;
- netif_stop_queue (dev);
- }
- if (dummy >= 0)
- tp->tx_ring[dummy].status = DescOwned;
- restore_flags(flags);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
- }
- }
- outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
-}
-
-static const struct pci_device_id tulip_pci_table[] __devinitdata = {
- { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 },
- { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 },
- { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
- { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 },
- { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
- { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
- { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
- { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
- { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
- { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
- { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
- {0},
-};
-
-MODULE_DEVICE_TABLE(pci, tulip_pci_table);
-
-static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net_device *dev;
- static int board_idx = 0;
-
- printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
-
- pci_enable_device (pdev);
- pci_set_master (pdev);
- dev = tulip_probe1(pdev, NULL,
- pci_resource_start (pdev, 0), pdev->irq,
- id->driver_data, board_idx++);
- if (dev) {
- pdev->driver_data = dev;
- return 0;
- }
- return -ENODEV;
-}
-
-static void tulip_suspend(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
- if (tp->open) tulip_down(dev);
-}
-
-static void tulip_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
- if (tp->open) tulip_up(dev);
-}
-
-static void __devexit tulip_remove(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
- unregister_netdev(dev);
- kfree(dev);
- kfree(tp);
-}
-
-static struct pci_driver tulip_ops = {
- name: "tulip_cb",
- id_table: tulip_pci_table,
- probe: tulip_pci_probe,
- remove: tulip_remove,
- suspend: tulip_suspend,
- resume: tulip_resume
-};
-
-static int __init tulip_init(void)
-{
- pci_register_driver(&tulip_ops);
- return 0;
-}
-
-static __exit void tulip_exit(void)
-{
- pci_unregister_driver(&tulip_ops);
-}
-
-module_init(tulip_init)
-module_exit(tulip_exit)
-
-
-/*
- * Local variables:
- * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 7374eac66..167d3cb12 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1470,7 +1470,7 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
lp->a.write_bcr (ioaddr, 34, data[2]);
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index d86ce1d85..64fafa17f 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -583,6 +583,61 @@ plip_receive(unsigned short nibble_timeout, struct net_device *dev,
return OK;
}
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ *
+ * PLIP is ethernet ish but the daddr might not be valid if unicast.
+ * PLIP fortunately has no bus architecture (its Point-to-point).
+ *
+ * We can't fix the daddr thing as that quirk (more bug) is embedded
+ * in far too many old systems not all even running Linux.
+ */
+
+static unsigned short plip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ */
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+
/* PLIP_RECEIVE_PACKET --- receive a packet */
static int
plip_receive_packet(struct net_device *dev, struct net_local *nl,
@@ -669,7 +724,7 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
case PLIP_PK_DONE:
/* Inform the upper layer for the arrival of a packet. */
- rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+ rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
netif_rx(rcv->skb);
nl->enet_stats.rx_bytes += rcv->length.h;
nl->enet_stats.rx_packets++;
@@ -1227,6 +1282,8 @@ plip_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
pc->nibble = nl->nibble;
break;
case PLIP_SET_TIMEOUT:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
nl->trigger = pc->trigger;
nl->nibble = pc->nibble;
break;
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 43b5508ae..3cbf0e5e4 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -17,11 +17,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990806==
+ * ==FILEVERSION 20000227==
*/
-/* $Id: ppp_async.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
@@ -31,9 +29,18 @@
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
-#define PPP_VERSION "2.4.0"
+#ifndef spin_trylock_bh
+#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \
+ __r = spin_trylock(lock); \
+ if (!__r) local_bh_enable(); \
+ __r; })
+#endif
+
+#define PPP_VERSION "2.4.1"
#define OBUFSIZE 256
@@ -44,7 +51,9 @@ struct asyncppp {
unsigned int state;
unsigned int rbits;
int mru;
- unsigned long busy;
+ spinlock_t xmit_lock;
+ spinlock_t recv_lock;
+ unsigned long xmit_flags;
u32 xaccm[8];
u32 raccm;
unsigned int bytes_sent;
@@ -55,24 +64,18 @@ struct asyncppp {
u16 tfcs;
unsigned char *optr;
unsigned char *olim;
- struct sk_buff_head xq;
unsigned long last_xmit;
struct sk_buff *rpkt;
- struct sk_buff_head rq;
- wait_queue_head_t rwait;
+ int lcp_fcs;
struct ppp_channel chan; /* interface to generic ppp layer */
- int connected;
- int index;
unsigned char obuf[OBUFSIZE];
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
-#define XMIT_FULL 3
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP 0
+#define XMIT_FULL 1
/* State bits */
#define SC_TOSS 0x20000000
@@ -81,8 +84,6 @@ struct asyncppp {
/* Bits in rbits */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-#define PPPASYNC_MAX_RQLEN 32 /* arbitrary */
-
static int flag_time = HZ;
MODULE_PARM(flag_time, "i");
@@ -95,57 +96,17 @@ static int ppp_async_push(struct asyncppp *ap);
static void ppp_async_flush_output(struct asyncppp *ap);
static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
char *flags, int count);
+static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound);
struct ppp_channel_ops async_ops = {
- ppp_async_send
+ ppp_async_send,
+ ppp_async_ioctl
};
/*
- * Routines for locking and unlocking the transmit and receive paths.
- */
-static inline void
-lock_path(struct asyncppp *ap, int bit)
-{
- do {
- while (test_bit(bit, &ap->busy))
- mb();
- } while (test_and_set_bit(bit, &ap->busy));
- mb();
-}
-
-static inline int
-trylock_path(struct asyncppp *ap, int bit)
-{
- if (test_and_set_bit(bit, &ap->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct asyncppp *ap, int bit)
-{
- mb();
- clear_bit(bit, &ap->busy);
-}
-
-#define lock_xmit_path(ap) lock_path(ap, XMIT_BUSY)
-#define trylock_xmit_path(ap) trylock_path(ap, XMIT_BUSY)
-#define unlock_xmit_path(ap) unlock_path(ap, XMIT_BUSY)
-#define lock_recv_path(ap) lock_path(ap, RECV_BUSY)
-#define trylock_recv_path(ap) trylock_path(ap, RECV_BUSY)
-#define unlock_recv_path(ap) unlock_path(ap, RECV_BUSY)
-
-static inline void
-flush_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(q)) != 0)
- kfree_skb(skb);
-}
-
-/*
* Routines implementing the PPP line discipline.
*/
@@ -153,256 +114,113 @@ flush_skb_queue(struct sk_buff_head *q)
* Called when a tty is put into PPP line discipline.
*/
static int
-ppp_async_open(struct tty_struct *tty)
+ppp_asynctty_open(struct tty_struct *tty)
{
struct asyncppp *ap;
+ int err;
ap = kmalloc(sizeof(*ap), GFP_KERNEL);
if (ap == 0)
return -ENOMEM;
- MOD_INC_USE_COUNT;
-
/* initialize the asyncppp structure */
memset(ap, 0, sizeof(*ap));
ap->tty = tty;
ap->mru = PPP_MRU;
+ spin_lock_init(&ap->xmit_lock);
+ spin_lock_init(&ap->recv_lock);
ap->xaccm[0] = ~0U;
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
ap->optr = ap->obuf;
ap->olim = ap->obuf;
- skb_queue_head_init(&ap->xq);
- skb_queue_head_init(&ap->rq);
- init_waitqueue_head(&ap->rwait);
+ ap->lcp_fcs = -1;
+
+ ap->chan.private = ap;
+ ap->chan.ops = &async_ops;
+ ap->chan.mtu = PPP_MRU;
+ err = ppp_register_channel(&ap->chan);
+ if (err) {
+ kfree(ap);
+ return err;
+ }
tty->disc_data = ap;
+ MOD_INC_USE_COUNT;
return 0;
}
/*
* Called when the tty is put into another line discipline
- * (or it hangs up).
+ * or it hangs up.
+ * We assume that while we are in this routine, the tty layer
+ * won't call any of the other line discipline entries for the
+ * same tty.
*/
static void
-ppp_async_close(struct tty_struct *tty)
+ppp_asynctty_close(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
tty->disc_data = 0;
- lock_xmit_path(ap);
- lock_recv_path(ap);
+ ppp_unregister_channel(&ap->chan);
if (ap->rpkt != 0)
kfree_skb(ap->rpkt);
- flush_skb_queue(&ap->rq);
if (ap->tpkt != 0)
kfree_skb(ap->tpkt);
- flush_skb_queue(&ap->xq);
- if (ap->connected)
- ppp_unregister_channel(&ap->chan);
kfree(ap);
MOD_DEC_USE_COUNT;
}
/*
- * Read a PPP frame. pppd can use this to negotiate over the
- * channel before it joins it to a bundle.
+ * Read does nothing.
*/
static ssize_t
-ppp_async_read(struct tty_struct *tty, struct file *file,
- unsigned char *buf, size_t count)
+ppp_asynctty_read(struct tty_struct *tty, struct file *file,
+ unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- struct sk_buff *skb = 0;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- add_wait_queue(&ap->rwait, &wait);
- current->state = TASK_INTERRUPTIBLE;
- for (;;) {
- ret = -EAGAIN;
- skb = skb_dequeue(&ap->rq);
- if (skb)
- break;
- if (file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&ap->rwait, &wait);
-
- if (skb == 0)
- goto out;
-
- ret = -EOVERFLOW;
- if (skb->len > count)
- goto outf;
- ret = -EFAULT;
- if (copy_to_user(buf, skb->data, skb->len))
- goto outf;
- ret = skb->len;
-
- outf:
- kfree_skb(skb);
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_read(&ap->chan, file, buf, count);
}
/*
- * Write a ppp frame. pppd can use this to send frames over
- * this particular channel.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
*/
static ssize_t
-ppp_async_write(struct tty_struct *tty, struct file *file,
- const unsigned char *buf, size_t count)
+ppp_asynctty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- struct sk_buff *skb;
- ssize_t ret;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- ret = -ENOMEM;
- skb = alloc_skb(count + 2, GFP_KERNEL);
- if (skb == 0)
- goto out;
- skb_reserve(skb, 2);
- ret = -EFAULT;
- if (copy_from_user(skb_put(skb, count), buf, count)) {
- kfree_skb(skb);
- goto out;
- }
-
- skb_queue_tail(&ap->xq, skb);
- ppp_async_push(ap);
-
- ret = count;
-
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_write(&ap->chan, buf, count);
}
static int
-ppp_async_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct asyncppp *ap = tty->disc_data;
int err, val;
- u32 accm[8];
- struct sk_buff *skb;
-
- err = -ENXIO;
- if (ap == 0)
- goto out; /* should never happen */
- err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- goto out;
err = -EFAULT;
switch (cmd) {
- case PPPIOCGFLAGS:
- val = ap->flags | ap->rbits;
- if (put_user(val, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSFLAGS:
- if (get_user(val, (int *) arg))
- break;
- ap->flags = val & ~SC_RCV_BITS;
- ap->rbits = val & SC_RCV_BITS;
- err = 0;
- break;
-
- case PPPIOCGASYNCMAP:
- if (put_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSASYNCMAP:
- if (get_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGRASYNCMAP:
- if (put_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSRASYNCMAP:
- if (get_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGXASYNCMAP:
- if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
- break;
- err = 0;
- break;
- case PPPIOCSXASYNCMAP:
- if (copy_from_user(accm, (void *) arg, sizeof(accm)))
- break;
- accm[2] &= ~0x40000000U; /* can't escape 0x5e */
- accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
- memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
- err = 0;
- break;
-
- case PPPIOCGMRU:
- if (put_user(ap->mru, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSMRU:
- if (get_user(val, (int *) arg))
- break;
- if (val < PPP_MRU)
- val = PPP_MRU;
- ap->mru = val;
- err = 0;
- break;
-
- case PPPIOCATTACH:
- if (get_user(val, (int *) arg))
- break;
- err = -EALREADY;
- if (ap->connected)
- break;
- ap->chan.private = ap;
- ap->chan.ops = &async_ops;
- err = ppp_register_channel(&ap->chan, val);
- if (err != 0)
- break;
- ap->connected = 1;
- ap->index = val;
- break;
- case PPPIOCDETACH:
- err = -ENXIO;
- if (!ap->connected)
- break;
- ppp_unregister_channel(&ap->chan);
- ap->connected = 0;
- err = 0;
- break;
case PPPIOCGUNIT:
err = -ENXIO;
- if (!ap->connected)
+ if (ap == 0)
break;
- if (put_user(ap->index, (int *) arg))
+ err = -EFAULT;
+ if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
break;
err = 0;
break;
@@ -414,8 +232,6 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case TCFLSH:
/* flush our buffers and the serial port's buffer */
- if (arg == TCIFLUSH || arg == TCIOFLUSH)
- flush_skb_queue(&ap->rq);
if (arg == TCIOFLUSH || arg == TCOFLUSH)
ppp_async_flush_output(ap);
err = n_tty_ioctl(tty, file, cmd, arg);
@@ -423,68 +239,86 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case FIONREAD:
val = 0;
- if ((skb = skb_peek(&ap->rq)) != 0)
- val = skb->len;
if (put_user(val, (int *) arg))
break;
err = 0;
break;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ case PPPIOCGFLAGS:
+ case PPPIOCSFLAGS:
+ case PPPIOCGASYNCMAP:
+ case PPPIOCSASYNCMAP:
+ case PPPIOCGRASYNCMAP:
+ case PPPIOCSRASYNCMAP:
+ case PPPIOCGXASYNCMAP:
+ case PPPIOCSXASYNCMAP:
+ case PPPIOCGMRU:
+ case PPPIOCSMRU:
+ err = ppp_async_ioctl(&ap->chan, cmd, arg);
+ break;
+
+ case PPPIOCATTACH:
+ err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+ break;
+
default:
err = -ENOIOCTLCMD;
}
- out:
+
return err;
}
static unsigned int
-ppp_async_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
{
- struct asyncppp *ap = tty->disc_data;
unsigned int mask;
+ struct asyncppp *ap = tty->disc_data;
- if (ap == 0)
- return 0; /* should never happen */
- poll_wait(file, &ap->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ap->rq))
- mask |= POLLIN | POLLRDNORM;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ if (ap != 0)
+ mask |= ppp_channel_poll(&ap->chan, file, wait);
if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
mask |= POLLHUP;
return mask;
}
static int
-ppp_async_room(struct tty_struct *tty)
+ppp_asynctty_room(struct tty_struct *tty)
{
return 65535;
}
static void
-ppp_async_receive(struct tty_struct *tty, const unsigned char *buf,
+ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
- trylock_recv_path(ap);
+ spin_lock_bh(&ap->recv_lock);
ppp_async_input(ap, buf, flags, count);
- unlock_recv_path(ap);
+ spin_unlock_bh(&ap->recv_lock);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver.unthrottle)
tty->driver.unthrottle(tty);
}
static void
-ppp_async_wakeup(struct tty_struct *tty)
+ppp_asynctty_wakeup(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0)
return;
- if (ppp_async_push(ap) && ap->connected)
+ if (ppp_async_push(ap))
ppp_output_wakeup(&ap->chan);
}
@@ -492,15 +326,15 @@ ppp_async_wakeup(struct tty_struct *tty)
static struct tty_ldisc ppp_ldisc = {
magic: TTY_LDISC_MAGIC,
name: "ppp",
- open: ppp_async_open,
- close: ppp_async_close,
- read: ppp_async_read,
- write: ppp_async_write,
- ioctl: ppp_async_ioctl,
- poll: ppp_async_poll,
- receive_room: ppp_async_room,
- receive_buf: ppp_async_receive,
- write_wakeup: ppp_async_wakeup,
+ open: ppp_asynctty_open,
+ close: ppp_asynctty_close,
+ read: ppp_asynctty_read,
+ write: ppp_asynctty_write,
+ ioctl: ppp_asynctty_ioctl,
+ poll: ppp_asynctty_poll,
+ receive_room: ppp_asynctty_room,
+ receive_buf: ppp_asynctty_receive,
+ write_wakeup: ppp_asynctty_wakeup,
};
int
@@ -516,6 +350,91 @@ ppp_async_init(void)
}
/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+ struct asyncppp *ap = chan->private;
+ int err, val;
+ u32 accm[8];
+
+ err = -EFAULT;
+ switch (cmd) {
+ case PPPIOCGFLAGS:
+ val = ap->flags | ap->rbits;
+ if (put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if (get_user(val, (int *) arg))
+ break;
+ ap->flags = val & ~SC_RCV_BITS;
+ spin_lock_bh(&ap->recv_lock);
+ ap->rbits = val & SC_RCV_BITS;
+ spin_unlock_bh(&ap->recv_lock);
+ err = 0;
+ break;
+
+ case PPPIOCGASYNCMAP:
+ if (put_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSASYNCMAP:
+ if (get_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGRASYNCMAP:
+ if (put_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSRASYNCMAP:
+ if (get_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGXASYNCMAP:
+ if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSXASYNCMAP:
+ if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+ break;
+ accm[2] &= ~0x40000000U; /* can't escape 0x5e */
+ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
+ memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+ err = 0;
+ break;
+
+ case PPPIOCGMRU:
+ if (put_user(ap->mru, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSMRU:
+ if (get_user(val, (int *) arg))
+ break;
+ if (val < PPP_MRU)
+ val = PPP_MRU;
+ ap->mru = val;
+ err = 0;
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+
+ return err;
+}
+
+/*
* Procedures for encapsulation and framing.
*/
@@ -597,6 +516,9 @@ ppp_async_encode(struct asyncppp *ap)
islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
if (i == 0) {
+ if (islcp)
+ async_lcp_peek(ap, data, count, 0);
+
/*
* Start of a new packet - insert the leading FLAG
* character if necessary.
@@ -675,7 +597,7 @@ ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
ppp_async_push(ap);
- if (test_and_set_bit(XMIT_FULL, &ap->busy))
+ if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
return 0; /* already full */
ap->tpkt = skb;
ap->tpkt_pos = 0;
@@ -694,12 +616,11 @@ ppp_async_push(struct asyncppp *ap)
struct tty_struct *tty = ap->tty;
int tty_stuffed = 0;
- if (!trylock_xmit_path(ap)) {
- set_bit(XMIT_WAKEUP, &ap->busy);
+ set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+ if (!spin_trylock_bh(&ap->xmit_lock))
return 0;
- }
for (;;) {
- if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy))
+ if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
tty_stuffed = 0;
if (!tty_stuffed && ap->optr < ap->olim) {
avail = ap->olim - ap->optr;
@@ -715,22 +636,17 @@ ppp_async_push(struct asyncppp *ap)
if (ap->optr == ap->olim && ap->tpkt != 0) {
if (ppp_async_encode(ap)) {
/* finished processing ap->tpkt */
- struct sk_buff *skb = skb_dequeue(&ap->xq);
- if (skb != 0) {
- ap->tpkt = skb;
- } else {
- clear_bit(XMIT_FULL, &ap->busy);
- done = 1;
- }
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
+ done = 1;
}
continue;
}
/* haven't made any progress */
- unlock_xmit_path(ap);
- if (!(test_bit(XMIT_WAKEUP, &ap->busy)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
|| (!tty_stuffed && ap->tpkt != 0)))
break;
- if (!trylock_xmit_path(ap))
+ if (!spin_trylock_bh(&ap->xmit_lock))
break;
}
return done;
@@ -739,11 +655,11 @@ flush:
if (ap->tpkt != 0) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
ap->optr = ap->olim;
- unlock_xmit_path(ap);
+ spin_unlock_bh(&ap->xmit_lock);
return done;
}
@@ -756,17 +672,16 @@ ppp_async_flush_output(struct asyncppp *ap)
{
int done = 0;
- flush_skb_queue(&ap->xq);
- lock_xmit_path(ap);
+ spin_lock_bh(&ap->xmit_lock);
ap->optr = ap->olim;
if (ap->tpkt != NULL) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
- unlock_xmit_path(ap);
- if (done && ap->connected)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (done)
ppp_output_wakeup(&ap->chan);
}
@@ -795,7 +710,7 @@ process_input_packet(struct asyncppp *ap)
{
struct sk_buff *skb;
unsigned char *p;
- unsigned int len, fcs;
+ unsigned int len, fcs, proto;
int code = 0;
skb = ap->rpkt;
@@ -827,37 +742,32 @@ process_input_packet(struct asyncppp *ap)
goto err;
p = skb_pull(skb, 2);
}
- if (p[0] & 1) {
+ proto = p[0];
+ if (proto & 1) {
/* protocol is compressed */
skb_push(skb, 1)[0] = 0;
- } else if (skb->len < 2)
- goto err;
-
- /* all OK, give it to the generic layer or queue it */
- if (ap->connected) {
- ppp_input(&ap->chan, skb);
} else {
- skb_queue_tail(&ap->rq, skb);
- /* drop old frames if queue too long */
- while (ap->rq.qlen > PPPASYNC_MAX_RQLEN
- && (skb = skb_dequeue(&ap->rq)) != 0)
- kfree(skb);
- wake_up_interruptible(&ap->rwait);
+ if (skb->len < 2)
+ goto err;
+ proto = (proto << 8) + p[1];
+ if (proto == PPP_LCP)
+ async_lcp_peek(ap, p, skb->len, 1);
}
+
+ /* all OK, give it to the generic layer */
+ ppp_input(&ap->chan, skb);
return;
err:
kfree_skb(skb);
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
static inline void
input_error(struct asyncppp *ap, int code)
{
ap->state |= SC_TOSS;
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
/* called when the tty driver has data for us. */
@@ -950,17 +860,96 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
input_error(ap, 0);
}
-#ifdef MODULE
-int
-init_module(void)
+/*
+ * We look at LCP frames going past so that we can notice
+ * and react to the LCP configure-ack from the peer.
+ * In the situation where the peer has been sent a configure-ack
+ * already, LCP is up once it has sent its configure-ack
+ * so the immediately following packet can be sent with the
+ * configured LCP options. This allows us to process the following
+ * packet correctly without pppd needing to respond quickly.
+ *
+ * We only respond to the received configure-ack if we have just
+ * sent a configure-request, and the configure-ack contains the
+ * same data (this is checked using a 16-bit crc of the data).
+ */
+#define CONFREQ 1 /* LCP code field values */
+#define CONFACK 2
+#define LCP_MRU 1 /* LCP option numbers */
+#define LCP_ASYNCMAP 2
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound)
{
- return ppp_async_init();
+ int dlen, fcs, i, code;
+ u32 val;
+
+ data += 2; /* skip protocol bytes */
+ len -= 2;
+ if (len < 4) /* 4 = code, ID, length */
+ return;
+ code = data[0];
+ if (code != CONFACK && code != CONFREQ)
+ return;
+ dlen = (data[2] << 8) + data[3];
+ if (len < dlen)
+ return; /* packet got truncated or length is bogus */
+
+ if (code == (inbound? CONFACK: CONFREQ)) {
+ /*
+ * sent confreq or received confack:
+ * calculate the crc of the data from the ID field on.
+ */
+ fcs = PPP_INITFCS;
+ for (i = 1; i < dlen; ++i)
+ fcs = PPP_FCS(fcs, data[i]);
+
+ if (!inbound) {
+ /* outbound confreq - remember the crc for later */
+ ap->lcp_fcs = fcs;
+ return;
+ }
+
+ /* received confack, check the crc */
+ fcs ^= ap->lcp_fcs;
+ ap->lcp_fcs = -1;
+ if (fcs != 0)
+ return;
+ } else if (inbound)
+ return; /* not interested in received confreq */
+
+ /* process the options in the confack */
+ data += 4;
+ dlen -= 4;
+ /* data[0] is code, data[1] is length */
+ while (dlen >= 2 && dlen >= data[1]) {
+ switch (data[0]) {
+ case LCP_MRU:
+ val = (data[2] << 8) + data[3];
+ if (inbound)
+ ap->mru = val;
+ else
+ ap->chan.mtu = val;
+ break;
+ case LCP_ASYNCMAP:
+ val = (data[2] << 24) + (data[3] << 16)
+ + (data[4] << 8) + data[5];
+ if (inbound)
+ ap->raccm = val;
+ else
+ ap->xaccm[0] = val;
+ break;
+ }
+ dlen -= data[1];
+ data += data[1];
+ }
}
-void
-cleanup_module(void)
+void __exit ppp_async_cleanup(void)
{
if (tty_register_ldisc(N_PPP, NULL) != 0)
printk(KERN_ERR "failed to unregister PPP line discipline\n");
}
-#endif /* MODULE */
+
+module_init(ppp_async_init);
+module_exit(ppp_async_cleanup);
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 3ef379ab2..761d8705c 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -32,26 +32,9 @@
*/
#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/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/init.h>
#include <linux/ppp_defs.h>
#include <linux/ppp-comp.h>
@@ -656,31 +639,21 @@ struct compressor ppp_deflate_draft = {
z_comp_stats, /* decomp_stat */
};
-#ifdef MODULE
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-int
-init_module(void)
+int deflate_init(void)
{
- int answer = ppp_register_compressor (&ppp_deflate);
+ int answer = ppp_register_compressor(&ppp_deflate);
if (answer == 0)
- printk (KERN_INFO
- "PPP Deflate Compression module registered\n");
+ printk(KERN_INFO
+ "PPP Deflate Compression module registered\n");
ppp_register_compressor(&ppp_deflate_draft);
return answer;
}
-void
-cleanup_module(void)
+void deflate_cleanup(void)
{
- if (MOD_IN_USE)
- printk (KERN_INFO
- "Deflate Compression module busy, remove delayed\n");
- else {
- ppp_unregister_compressor (&ppp_deflate);
- ppp_unregister_compressor (&ppp_deflate_draft);
- }
+ ppp_unregister_compressor(&ppp_deflate);
+ ppp_unregister_compressor(&ppp_deflate_draft);
}
-#endif
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 0b3aeff4b..01a4e2f81 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1,7 +1,7 @@
/*
* Generic PPP layer for Linux.
*
- * Copyright 1999 Paul Mackerras.
+ * Copyright 1999-2000 Paul Mackerras.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,11 +19,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990915==
+ * ==FILEVERSION 20000313==
*/
-/* $Id: ppp_generic.c,v 1.5 1999/09/15 11:21:48 paulus Exp $ */
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -44,16 +42,9 @@
#include <linux/tcp.h>
#include <linux/spinlock.h>
#include <net/slhc_vj.h>
+#include <asm/atomic.h>
-#define PPP_VERSION "2.4.0"
-
-EXPORT_SYMBOL(ppp_register_channel);
-EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_input);
-EXPORT_SYMBOL(ppp_input_error);
-EXPORT_SYMBOL(ppp_output_wakeup);
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
+#define PPP_VERSION "2.4.1"
/*
* Network protocols we support.
@@ -64,32 +55,56 @@ EXPORT_SYMBOL(ppp_unregister_compressor);
#define NP_AT 3 /* Appletalk protocol */
#define NUM_NP 4 /* Number of NPs. */
+#define MPHDRLEN 4 /* multilink protocol header length */
+#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */
+#define MIN_FRAG_SIZE 64
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel. In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+ enum {
+ INTERFACE=1, CHANNEL
+ } kind;
+ struct sk_buff_head xq; /* pppd transmit queue */
+ struct sk_buff_head rq; /* receive queue for pppd */
+ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
+ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */
+ int hdrlen; /* space to leave for headers */
+ struct list_head list; /* link in all_* list */
+ int index; /* interface unit / channel number */
+};
+
+#define PF_TO_X(pf, X) ((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file)))
+
+#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
+
+#define ROUNDUP(n, x) (((n) + (x) - 1) / (x))
+
/*
* Data structure describing one ppp unit.
* A ppp unit corresponds to a ppp network interface device
* and represents a multilink bundle.
- * It may have 0 or more ppp channels connected to it.
+ * It can have 0 or more ppp channels connected to it.
*/
struct ppp {
- struct list_head list; /* link in list of ppp units */
- int index; /* interface unit number */
+ struct ppp_file file; /* stuff for read/write/poll */
char name[16]; /* unit name */
- int refcnt; /* # open /dev/ppp attached */
- unsigned long busy; /* lock and other bits */
struct list_head channels; /* list of attached channels */
int n_channels; /* how many channels are attached */
+ spinlock_t rlock; /* lock for receive side */
+ spinlock_t wlock; /* lock for transmit side */
int mru; /* max receive unit */
unsigned int flags; /* control bits */
unsigned int xstate; /* transmit state bits */
unsigned int rstate; /* receive state bits */
int debug; /* debug flags */
struct slcompress *vj; /* state for VJ header compression */
- struct sk_buff_head xq; /* pppd transmit queue */
- struct sk_buff_head rq; /* receive queue for pppd */
- wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
enum NPmode npmode[NUM_NP]; /* what to do with each net proto */
struct sk_buff *xmit_pending; /* a packet ready to go out */
- struct sk_buff_head recv_pending;/* pending input packets */
struct compressor *xcomp; /* transmit packet compressor */
void *xc_state; /* its internal state */
struct compressor *rcomp; /* receive decompressor */
@@ -97,57 +112,121 @@ struct ppp {
unsigned long last_xmit; /* jiffies when last pkt sent */
unsigned long last_recv; /* jiffies when last pkt rcvd */
struct net_device *dev; /* network interface device */
+#ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+ int mrru; /* MP: max reconst. receive unit */
+ u32 nextseq; /* MP: seq no of next packet */
+ u32 minseq; /* MP: min of most recent seqnos */
+ struct sk_buff_head mrq; /* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
struct net_device_stats stats; /* statistics */
};
-static LIST_HEAD(all_ppp_units);
-static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ.
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ)
/*
* Private data structure for each channel.
- * Ultimately this will have multilink stuff etc. in it.
+ * This includes the data structure used for multilink.
*/
struct channel {
- struct list_head list; /* link in list of channels per unit */
+ struct ppp_file file; /* stuff for read/write/poll */
struct ppp_channel *chan; /* public channel data structure */
- int blocked; /* if channel refused last packet */
+ spinlock_t downl; /* protects `chan', file.xq dequeue */
struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' and `ulist' */
+#ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+ u32 lastseq; /* MP: last sequence # received */
+#endif /* CONFIG_PPP_MULTILINK */
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
/*
- * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC.
- * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
- * Bits in xstate: SC_COMP_RUN
+ * all_ppp_lock protects the all_ppp_units.
+ * It also ensures that finding a ppp unit in the all_ppp_units list
+ * and updating its file.refcnt field is atomic.
+ */
+static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_ppp_units);
+
+/*
+ * all_channels_lock protects all_channels and last_channel_index,
+ * and the atomicity of find a channel and updating its file.refcnt
+ * field.
*/
-#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC)
+static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_channels);
+static int last_channel_index;
/* Get the PPP protocol number from a skb */
#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
-/* We limit the length of ppp->rq to this (arbitrary) value */
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
#define PPP_MAX_RQLEN 32
+/* Multilink header bits. */
+#define B 0x80 /* this fragment begins a packet */
+#define E 0x40 /* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b) ((s32)((a) - (b)) < 0)
+#define seq_after(a, b) ((s32)((a) - (b)) > 0)
+
/* Prototypes. */
-static void ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh);
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count);
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count);
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static void ppp_xmit_process(struct ppp *ppp, int wakeup);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
-static void ppp_recv_unlock(struct ppp *ppp);
-static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
static void ppp_ccp_closed(struct ppp *ppp);
static struct compressor *find_compressor(int type);
static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_unit(int unit, int *retp);
-static void ppp_release_unit(struct ppp *ppp);
+static struct ppp *ppp_create_interface(int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_destroy_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(int unit);
+static struct channel *ppp_find_channel(int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
/* Translates a PPP protocol number to a NP index (NP == network protocol) */
static inline int proto_to_npindex(int proto)
@@ -199,69 +278,24 @@ static const int npindex_to_ethertype[NUM_NP] = {
};
/*
- * Routines for locking and unlocking the transmit and receive paths
- * of each unit.
- *
- * On the transmit side, we have threads of control coming into the
- * driver from (at least) three places: the core net code, write calls
- * on /dev/ppp from pppd, and wakeup calls from channels. There is
- * possible concurrency even on UP systems (between mainline and
- * BH processing). The XMIT_BUSY bit in ppp->busy serializes the
- * transmit-side processing for each ppp unit.
+ * Locking shorthand.
*/
-static inline void
-lock_path(struct ppp *ppp, int bit)
-{
- int timeout = 1000000;
-
- do {
- while (test_bit(bit, &ppp->busy)) {
- mb();
- if (--timeout == 0) {
- printk(KERN_ERR "lock_path timeout ppp=%p bit=%x\n", ppp, bit);
- return;
- }
- }
- } while (test_and_set_bit(bit, &ppp->busy));
- mb();
-}
+#define ppp_xmit_lock(ppp) spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp) spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp) spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp) spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp) do { ppp_xmit_lock(ppp); \
+ ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \
+ ppp_xmit_unlock(ppp); } while (0)
-static inline int
-trylock_path(struct ppp *ppp, int bit)
-{
- if (test_and_set_bit(bit, &ppp->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct ppp *ppp, int bit)
-{
- mb();
- clear_bit(bit, &ppp->busy);
-}
-
-#define lock_xmit_path(ppp) lock_path(ppp, XMIT_BUSY)
-#define trylock_xmit_path(ppp) trylock_path(ppp, XMIT_BUSY)
-#define unlock_xmit_path(ppp) unlock_path(ppp, XMIT_BUSY)
-#define lock_recv_path(ppp) lock_path(ppp, RECV_BUSY)
-#define trylock_recv_path(ppp) trylock_path(ppp, RECV_BUSY)
-#define unlock_recv_path(ppp) unlock_path(ppp, RECV_BUSY)
-
-static inline void
-free_skbs(struct sk_buff_head *head)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(head)) != 0)
- kfree_skb(skb);
-}
/*
* /dev/ppp device routines.
* The /dev/ppp device is used by pppd to control the ppp unit.
* It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
*/
static int ppp_open(struct inode *inode, struct file *file)
{
@@ -276,11 +310,20 @@ static int ppp_open(struct inode *inode, struct file *file)
static int ppp_release(struct inode *inode, struct file *file)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
- if (ppp != 0) {
+ if (pf != 0) {
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt)) {
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_destroy_interface(PF_TO_PPP(pf));
+ break;
+ case CHANNEL:
+ ppp_destroy_channel(PF_TO_CHANNEL(pf));
+ break;
+ }
+ }
}
MOD_DEC_USE_COUNT;
return 0;
@@ -289,20 +332,27 @@ static int ppp_release(struct inode *inode, struct file *file)
static ssize_t ppp_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_read(pf, file, buf, count);
+}
+
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count)
+{
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
struct sk_buff *skb = 0;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out; /* not currently attached */
- add_wait_queue(&ppp->rwait, &wait);
+ add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
ret = -EAGAIN;
- skb = skb_dequeue(&ppp->rq);
+ skb = skb_dequeue(&pf->rq);
if (skb)
break;
if (file->f_flags & O_NONBLOCK)
@@ -313,7 +363,7 @@ static ssize_t ppp_read(struct file *file, char *buf,
schedule();
}
current->state = TASK_RUNNING;
- remove_wait_queue(&ppp->rwait, &wait);
+ remove_wait_queue(&pf->rwait, &wait);
if (skb == 0)
goto out;
@@ -335,32 +385,42 @@ static ssize_t ppp_read(struct file *file, char *buf,
static ssize_t ppp_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_write(pf, buf, count);
+}
+
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count)
+{
struct sk_buff *skb;
ssize_t ret;
- int extra;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out;
ret = -ENOMEM;
- extra = PPP_HDRLEN - 2;
- if (ppp->dev && ppp->dev->hard_header_len > PPP_HDRLEN)
- extra = ppp->dev->hard_header_len - 2;
- skb = alloc_skb(count + extra, GFP_KERNEL);
+ skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
if (skb == 0)
goto out;
- skb_reserve(skb, extra);
+ skb_reserve(skb, pf->hdrlen);
ret = -EFAULT;
if (copy_from_user(skb_put(skb, count), buf, count)) {
kfree_skb(skb);
goto out;
}
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ skb_queue_tail(&pf->xq, skb);
+
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_xmit_process(PF_TO_PPP(pf), 0);
+ break;
+ case CHANNEL:
+ ppp_channel_push(PF_TO_CHANNEL(pf));
+ break;
+ }
ret = count;
@@ -370,68 +430,82 @@ static ssize_t ppp_write(struct file *file, const char *buf,
static unsigned int ppp_poll(struct file *file, poll_table *wait)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
unsigned int mask;
- if (ppp == 0)
+ if (pf == 0)
return 0;
- poll_wait(file, &ppp->rwait, wait);
+ poll_wait(file, &pf->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ppp->rq) != 0)
+ if (skb_peek(&pf->rq) != 0)
mask |= POLLIN | POLLRDNORM;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ if (pch->chan == 0)
+ mask |= POLLHUP;
+ }
return mask;
}
static int ppp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
- int err, val, val2, i;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+ struct ppp *ppp;
+ int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
struct npioctl npi;
+ int unit;
+ struct slcompress *vj;
- if (cmd == PPPIOCNEWUNIT) {
- /* Create a new ppp unit */
- int unit, ret;
+ if (pf == 0)
+ return ppp_unattached_ioctl(pf, file, cmd, arg);
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- ppp = ppp_create_unit(unit, &ret);
- if (ppp == 0)
- return ret;
- file->private_data = ppp;
- if (put_user(ppp->index, (int *) arg))
- return -EFAULT;
- return 0;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ struct ppp_channel *chan;
+
+ switch (cmd) {
+ case PPPIOCCONNECT:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+
+ case PPPIOCDISCONN:
+ err = ppp_disconnect_channel(pch);
+ break;
+
+ case PPPIOCDETACH:
+ file->private_data = 0;
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_channel(pch);
+ err = 0;
+ break;
+
+ default:
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ err = -ENOTTY;
+ if (chan->ops->ioctl)
+ err = chan->ops->ioctl(chan, cmd, arg);
+ spin_unlock_bh(&pch->downl);
+ }
+ return err;
}
- if (cmd == PPPIOCATTACH) {
- /* Attach to an existing ppp unit */
- int unit;
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp != 0)
- ++ppp->refcnt;
- spin_unlock(&all_ppp_lock);
- if (ppp == 0)
- return -ENXIO;
- file->private_data = ppp;
- return 0;
+ if (pf->kind != INTERFACE) {
+ /* can't happen */
+ printk(KERN_ERR "PPP: not interface or channel??\n");
+ return -EINVAL;
}
- if (ppp == 0)
- return -ENXIO;
- err = -EFAULT;
+ ppp = PF_TO_PPP(pf);
switch (cmd) {
case PPPIOCDETACH:
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_interface(ppp);
err = 0;
break;
@@ -445,9 +519,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
case PPPIOCSFLAGS:
if (get_user(val, (int *) arg))
break;
+ ppp_lock(ppp);
if (ppp->flags & ~val & SC_CCP_OPEN)
ppp_ccp_closed(ppp);
ppp->flags = val & SC_FLAG_BITS;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -463,7 +539,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
break;
case PPPIOCGUNIT:
- if (put_user(ppp->index, (int *) arg))
+ if (put_user(ppp->file.index, (int *) arg))
break;
err = 0;
break;
@@ -497,18 +573,17 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
val2 = val >> 16;
val &= 0xffff;
}
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
- if (ppp->vj != 0)
- slhc_free(ppp->vj);
- ppp->vj = slhc_init(val2+1, val+1);
- ppp_recv_unlock(ppp);
- ppp_xmit_unlock(ppp, 1);
- err = -ENOMEM;
- if (ppp->vj == 0) {
+ vj = slhc_init(val2+1, val+1);
+ if (vj == 0) {
printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+ err = -ENOMEM;
break;
}
+ ppp_lock(ppp);
+ if (ppp->vj != 0)
+ slhc_free(ppp->vj);
+ ppp->vj = vj;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -533,6 +608,76 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
err = 0;
break;
+#ifdef CONFIG_PPP_MULTILINK
+ case PPPIOCSMRRU:
+ if (get_user(val, (int *) arg))
+ break;
+ ppp_recv_lock(ppp);
+ ppp->mrru = val;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ default:
+ err = -ENOTTY;
+ }
+ return err;
+}
+
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit, err = -EFAULT;
+ struct ppp *ppp;
+ struct channel *chan;
+
+ switch (cmd) {
+ case PPPIOCNEWUNIT:
+ /* Create a new ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ ppp = ppp_create_interface(unit, &err);
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = -EFAULT;
+ if (put_user(ppp->file.index, (int *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCATTACH:
+ /* Attach to an existing ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp != 0)
+ atomic_inc(&ppp->file.refcnt);
+ spin_unlock(&all_ppp_lock);
+ err = -ENXIO;
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = 0;
+ break;
+
+ case PPPIOCATTCHAN:
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock_bh(&all_channels_lock);
+ chan = ppp_find_channel(unit);
+ if (chan != 0)
+ atomic_inc(&chan->file.refcnt);
+ spin_unlock_bh(&all_channels_lock);
+ err = -ENXIO;
+ if (chan == 0)
+ break;
+ file->private_data = &chan->file;
+ err = 0;
+ break;
+
default:
err = -ENOTTY;
}
@@ -557,37 +702,17 @@ static devfs_handle_t devfs_handle = NULL;
int __init ppp_init(void)
{
int err;
-#ifndef MODULE
-#ifdef CONFIG_PPP_DEFLATE
- extern struct compressor ppp_deflate, ppp_deflate_draft;
-#endif
- extern int ppp_async_init(void);
- extern int ppp_sync_init(void);
-#endif
printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
if (err)
printk(KERN_ERR "failed to register PPP device (%d)\n", err);
- devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE,
- PPP_MAJOR, 0,
- S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
- &ppp_device_fops, NULL);
-#ifndef MODULE
-#ifdef CONFIG_PPP_ASYNC
- ppp_async_init();
-#endif
-#ifdef CONFIG_PPP_SYNC_TTY
- ppp_sync_init();
-#endif
-#ifdef CONFIG_PPP_DEFLATE
- if (ppp_register_compressor(&ppp_deflate) == 0)
- printk(KERN_INFO "PPP Deflate compression module registered\n");
- ppp_register_compressor(&ppp_deflate_draft);
-#endif
-#endif /* MODULE */
+ devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE,
+ PPP_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &ppp_device_fops, NULL);
- return -ENODEV;
+ return 0;
}
/*
@@ -636,9 +761,8 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
pp[1] = proto;
netif_stop_queue(dev);
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 0);
+ skb_queue_tail(&ppp->file.xq, skb);
+ ppp_xmit_process(ppp, 0);
return 0;
outf:
@@ -719,38 +843,26 @@ ppp_net_init(struct net_device *dev)
*/
/*
- * Called to unlock the transmit side of the ppp unit,
- * making sure that any work queued up gets done.
+ * Called to do any work queued up on the transmit side
+ * that can now be done.
*/
static void
-ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh)
+ppp_xmit_process(struct ppp *ppp, int wakeup)
{
struct sk_buff *skb;
- for (;;) {
- /* Do whatever work is waiting to be done. */
- if (test_and_clear_bit(XMIT_WAKEUP, &ppp->busy))
- ppp_push(ppp);
- /* If there's no work left to do, tell the core net
- code that we can accept some more. */
- while (ppp->xmit_pending == 0
- && (skb = skb_dequeue(&ppp->xq)) != 0)
- ppp_send_frame(ppp, skb);
- if (ppp->xmit_pending == 0 && skb_peek(&ppp->xq) == 0)
- netif_wake_queue(ppp->dev);
-
- /* Now unlock the transmit path, let others in. */
- unlock_xmit_path(ppp);
- /* Check whether any work was queued up
- between our last check and the unlock. */
- if (!(test_bit(XMIT_WAKEUP, &ppp->busy)
- || (ppp->xmit_pending == 0 && skb_peek(&ppp->xq))))
- break;
- /* If so, lock again and do the work. If we can't get
- the lock, someone else has it and they'll do the work. */
- if (!trylock_xmit_path(ppp))
- break;
- }
+ ppp_xmit_lock(ppp);
+ if (wakeup)
+ ppp_push(ppp);
+ while (ppp->xmit_pending == 0
+ && (skb = skb_dequeue(&ppp->file.xq)) != 0)
+ ppp_send_frame(ppp, skb);
+ /* If there's no work left to do, tell the core net
+ code that we can accept some more. */
+ if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0
+ && ppp->dev != 0)
+ netif_wake_queue(ppp->dev);
+ ppp_xmit_unlock(ppp);
}
/*
@@ -847,10 +959,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
* queue it up for pppd to receive.
*/
if (ppp->flags & SC_LOOP_TRAFFIC) {
- if (ppp->rq.qlen > PPP_MAX_RQLEN)
+ if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
goto drop;
- skb_queue_tail(&ppp->rq, skb);
- wake_up_interruptible(&ppp->rwait);
+ skb_queue_tail(&ppp->file.rq, skb);
+ wake_up_interruptible(&ppp->file.rwait);
return;
}
@@ -871,7 +983,7 @@ static void
ppp_push(struct ppp *ppp)
{
struct list_head *list;
- struct channel *chan;
+ struct channel *pch;
struct sk_buff *skb = ppp->xmit_pending;
if (skb == 0)
@@ -885,40 +997,222 @@ ppp_push(struct ppp *ppp)
return;
}
- /* If we are doing multilink, decide which channel gets the
- packet, and/or fragment the packet over several links. */
- /* XXX for now, just take the first channel */
- list = list->next;
- chan = list_entry(list, struct channel, list);
+ if ((ppp->flags & SC_MULTILINK) == 0) {
+ /* not doing multilink: send it down the first channel */
+ list = list->next;
+ pch = list_entry(list, struct channel, clist);
- if (chan->chan->ops->start_xmit(chan->chan, skb)) {
- ppp->xmit_pending = 0;
- chan->blocked = 0;
- } else
- chan->blocked = 1;
+ spin_lock_bh(&pch->downl);
+ if (skb_queue_len(&pch->file.xq) == 0
+ && pch->chan->ops->start_xmit(pch->chan, skb))
+ ppp->xmit_pending = 0;
+ spin_unlock_bh(&pch->downl);
+ return;
+ }
+
+#ifdef CONFIG_PPP_MULTILINK
+ /* Multilink: fragment the packet over as many links
+ as can take the packet at the moment. */
+ if (!ppp_mp_explode(ppp, skb))
+ return;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ ppp->xmit_pending = 0;
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+ int nch, len, fragsize;
+ int i, bits, hdrlen;
+ unsigned char *p, *q;
+ struct list_head *list;
+ struct channel *pch;
+ struct sk_buff *frag;
+ struct ppp_channel *chan;
+
+ nch = 0;
+ list = &ppp->channels;
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0);
+ /*
+ * If a channel hasn't had a fragment yet, it has to get
+ * one before we send any fragments on later channels.
+ * If it can't take a fragment now, don't give any
+ * to subsequent channels.
+ */
+ if (!pch->had_frag && !pch->avail) {
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ pch->avail = 0;
+ }
+ break;
+ }
+ }
+ if (nch == 0)
+ return 0; /* can't take now, leave it in xmit_pending */
+
+ /* Do protocol field compression (XXX this should be optional) */
+ p = skb->data;
+ len = skb->len;
+ if (*p == 0) {
+ ++p;
+ --len;
+ }
+
+ /* decide on fragment size */
+ fragsize = len;
+ if (nch > 1) {
+ int maxch = ROUNDUP(len, MIN_FRAG_SIZE);
+ if (nch > maxch)
+ nch = maxch;
+ fragsize = ROUNDUP(fragsize, nch);
+ }
+
+ /* skip to the channel after the one we last used
+ and start at that one */
+ for (i = 0; i < ppp->nxchan; ++i) {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ break;
+ }
+ }
+
+ /* create a fragment for each channel */
+ bits = B;
+ hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+ /* XXX gotta do A/C and prot compression here */
+ do {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ continue;
+ }
+ pch = list_entry(list, struct channel, clist);
+ ++i;
+ if (!pch->avail)
+ continue;
+ if (fragsize >= len) {
+ fragsize = len;
+ bits |= E;
+ }
+ frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC);
+ if (frag != 0) {
+ q = skb_put(frag, fragsize + hdrlen);
+ /* make the MP header */
+ if (ppp->flags & SC_MP_XSHORTSEQ) {
+ q[0] = bits + ((ppp->nxseq >> 8) & 0xf);
+ q[1] = ppp->nxseq;
+ } else {
+ q[0] = bits;
+ q[1] = ppp->nxseq >> 16;
+ q[2] = ppp->nxseq >> 8;
+ q[3] = ppp->nxseq;
+ }
+
+ /* copy the data in */
+ memcpy(q + hdrlen, p, fragsize);
+
+ /* try to send it down the channel */
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ if (chan != 0) {
+ if (!chan->ops->start_xmit(chan, frag))
+ skb_queue_tail(&pch->file.xq, frag);
+ } else {
+ /* channel got unregistered, too bad */
+ kfree_skb(skb);
+ }
+ spin_unlock_bh(&pch->downl);
+ }
+ p += fragsize;
+ len -= fragsize;
+ ++ppp->nxseq;
+ bits = 0;
+ } while (len > 0);
+ ppp->nxchan = i;
+
+ return 1;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+ struct sk_buff *skb;
+
+ spin_lock_bh(&pch->downl);
+ if (pch->chan != 0) {
+ while (skb_queue_len(&pch->file.xq) > 0) {
+ skb = skb_dequeue(&pch->file.xq);
+ if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+ /* put the packet back and try again later */
+ skb_queue_head(&pch->file.xq, skb);
+ break;
+ }
+ }
+ } else {
+ /* channel got deregistered */
+ skb_queue_purge(&pch->file.xq);
+ }
+ spin_unlock_bh(&pch->downl);
}
/*
* Receive-side routines.
*/
+
+/* misuse a few fields of the skb for MP reconstruction */
+#define sequence priority
+#define BEbits cb[0]
+
static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb)
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- skb_queue_tail(&ppp->recv_pending, skb);
- if (trylock_recv_path(ppp))
- ppp_recv_unlock(ppp);
+ ppp_recv_lock(ppp);
+ /* ppp->dev == 0 means interface is closing down */
+ if (ppp->dev != 0)
+ ppp_receive_frame(ppp, skb, pch);
+ else
+ kfree_skb(skb);
+ ppp_recv_unlock(ppp);
}
void
ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
{
struct channel *pch = chan->ppp;
+ int proto;
if (pch == 0 || skb->len == 0) {
kfree_skb(skb);
return;
}
- ppp_do_recv(pch->ppp, skb);
+
+ proto = PPP_PROTO(skb);
+ read_lock_bh(&pch->upl);
+ if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+ /* put it on the channel queue */
+ skb_queue_tail(&pch->file.rq, skb);
+ /* drop old frames if queue too long */
+ while (pch->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&pch->file.rq)) != 0)
+ kfree_skb(skb);
+ wake_up_interruptible(&pch->file.rwait);
+ } else {
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ read_unlock_bh(&pch->upl);
}
/* Put a 0-length skb in the receive queue as an error indication */
@@ -930,47 +1224,64 @@ ppp_input_error(struct ppp_channel *chan, int code)
if (pch == 0)
return;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (skb == 0)
- return;
- skb->len = 0; /* probably unnecessary */
- skb->cb[0] = code;
- ppp_do_recv(pch->ppp, skb);
+
+ read_lock_bh(&pch->upl);
+ if (pch->ppp != 0) {
+ skb = alloc_skb(0, GFP_ATOMIC);
+ if (skb != 0) {
+ skb->len = 0; /* probably unnecessary */
+ skb->cb[0] = code;
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ }
+ read_unlock_bh(&pch->upl);
}
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
static void
-ppp_recv_unlock(struct ppp *ppp)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- struct sk_buff *skb;
-
- for (;;) {
- while ((skb = skb_dequeue(&ppp->recv_pending)) != 0)
- ppp_receive_frame(ppp, skb);
- unlock_recv_path(ppp);
- if (skb_peek(&ppp->recv_pending) == 0)
- break;
- if (!trylock_recv_path(ppp))
- break;
+ if (skb->len >= 2) {
+#ifdef CONFIG_PPP_MULTILINK
+ /* XXX do channel-level decompression here */
+ if (PPP_PROTO(skb) == PPP_MP)
+ ppp_receive_mp_frame(ppp, skb, pch);
+ else
+#endif /* CONFIG_PPP_MULTILINK */
+ ppp_receive_nonmp_frame(ppp, skb);
+ return;
}
+
+ if (skb->len > 0)
+ /* note: a 0-length skb is used as an error indication */
+ ++ppp->stats.rx_length_errors;
+
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
+ppp_receive_error(struct ppp *ppp)
+{
+ ++ppp->stats.rx_errors;
+ if (ppp->vj != 0)
+ slhc_toss(ppp->vj);
+}
+
+static void
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
{
struct sk_buff *ns;
int proto, len, npi;
- if (skb->len == 0) {
- /* XXX should do something with code in skb->cb[0] */
- goto err; /* error indication */
- }
-
- if (skb->len < 2) {
- ++ppp->stats.rx_length_errors;
- goto err;
- }
-
- /* Decompress the frame, if compressed. */
+ /*
+ * Decompress the frame, if compressed.
+ * Note that some decompressors need to see uncompressed frames
+ * that come in as well as compressed frames.
+ */
if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN)
&& (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
skb = ppp_decompress_frame(ppp, skb);
@@ -995,7 +1306,12 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
}
len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
if (len <= 0) {
- printk(KERN_ERR "PPP: VJ decompression error\n");
+ int i;
+ printk(KERN_DEBUG "PPP: VJ decompression error\n");
+ printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
+ for (i = 0; i < 16 && i < skb->len; ++i)
+ printk(" %.2x", skb->data[i]);
+ printk("\n");
goto err;
}
len += 2;
@@ -1027,15 +1343,13 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
npi = proto_to_npindex(proto);
if (npi < 0) {
/* control or unknown frame - pass it to pppd */
- skb_queue_tail(&ppp->rq, skb);
+ skb_queue_tail(&ppp->file.rq, skb);
/* limit queue length by dropping old frames */
- while (ppp->rq.qlen > PPP_MAX_RQLEN) {
- skb = skb_dequeue(&ppp->rq);
- if (skb)
- kfree_skb(skb);
- }
+ while (ppp->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&ppp->file.rq)) != 0)
+ kfree_skb(skb);
/* wake up any process polling or blocking on read */
- wake_up_interruptible(&ppp->rwait);
+ wake_up_interruptible(&ppp->file.rwait);
} else {
/* network protocol frame - give it to the kernel */
@@ -1054,10 +1368,8 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
return;
err:
- ++ppp->stats.rx_errors;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static struct sk_buff *
@@ -1070,7 +1382,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
if (proto == PPP_COMP) {
ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);
if (ns == 0) {
- printk(KERN_ERR "ppp_receive: no memory\n");
+ printk(KERN_ERR "ppp_decompress_frame: no memory\n");
goto err;
}
/* the decompressor still expects the A/C bytes in the hdr */
@@ -1101,70 +1413,288 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
err:
ppp->rstate |= SC_DC_ERROR;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
- ++ppp->stats.rx_errors;
+ ppp_receive_error(ppp);
return skb;
}
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ u32 mask, seq, minseq;
+ struct list_head *l;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+
+ if (skb->len < mphdrlen + 3)
+ goto err; /* no good, throw it away */
+
+ /* Decode sequence number and begin/end bits */
+ if (ppp->flags & SC_MP_SHORTSEQ) {
+ seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+ mask = 0xfff;
+ } else {
+ seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+ mask = 0xffffff;
+ }
+ skb->BEbits = skb->data[2];
+ skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/
+
+ /* Expand sequence number to 32 bits */
+ seq |= pch->lastseq & ~mask;
+ if (seq_before(seq, pch->lastseq)) {
+ if (seq_after(seq, pch->lastseq - 100)) {
+ printk(KERN_DEBUG "PPP: MP fragments out of order"
+ " (%u, %u)\n", pch->lastseq, seq);
+ goto err;
+ }
+ seq += mask + 1;
+ }
+ skb->sequence = seq;
+ pch->lastseq = seq;
+
+ /*
+ * Reevaluate minseq, the minimum over all channels of the
+ * last sequence number received on each channel. Because of
+ * the increasing sequence number rule, we know that any fragment
+ * before `minseq' which hasn't arrived is never going to arrive.
+ * The list of channels can't change because we have the receive
+ * side of the ppp unit locked.
+ */
+ minseq = seq;
+ for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {
+ struct channel *ch = list_entry(l, struct channel, clist);
+ if (seq_before(ch->lastseq, seq))
+ seq = ch->lastseq;
+ }
+ ppp->minseq = minseq;
+
+ /* Put the fragment on the reconstruction queue */
+ ppp_mp_insert(ppp, skb);
+
+ /* Pull completed packets off the queue and receive them. */
+ while ((skb = ppp_mp_reconstruct(ppp)) != 0)
+ ppp_receive_nonmp_frame(ppp, skb);
+
+ return;
+
+ err:
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *p;
+ struct sk_buff_head *list = &ppp->mrq;
+ u32 seq = skb->sequence;
+
+ /* N.B. we don't need to lock the list lock because we have the
+ ppp unit receive-side lock. */
+ for (p = list->next; p != (struct sk_buff *)list; p = p->next)
+ if (seq_before(seq, p->sequence))
+ break;
+ __skb_insert(skb, p->prev, p, list);
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+ u32 seq = ppp->nextseq;
+ u32 minseq = ppp->minseq;
+ struct sk_buff_head *list = &ppp->mrq;
+ struct sk_buff *p, *next;
+ struct sk_buff *head, *tail;
+ struct sk_buff *skb = NULL;
+ int lost = 0, len = 0;
+
+ head = list->next;
+ tail = NULL;
+ for (p = head; p != (struct sk_buff *) list; p = next) {
+ next = p->next;
+ if (seq_before(p->sequence, seq)) {
+ /* this can't happen, anyway toss the skb */
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+ p->sequence, seq);
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ continue;
+ }
+ if (p->sequence != seq) {
+ /* Fragment `seq' is missing. If it is after
+ minseq, it might arrive later, so stop here. */
+ if (seq_after(seq, minseq))
+ break;
+ /* Fragment `seq' is lost, keep going. */
+ lost = 1;
+ seq = seq_before(p->sequence, minseq)?
+ p->sequence: minseq;
+ next = p;
+ continue;
+ }
+
+ /*
+ * At this point we know that all the fragments from
+ * ppp->nextseq to seq are either present or lost.
+ * Also, there are no complete packets in the queue
+ * that have no missing fragments and end before this
+ * fragment.
+ */
+
+ /* B bit set indicates this fragment starts a packet */
+ if (p->BEbits & B) {
+ head = p;
+ lost = 0;
+ /* reset len, allow for protocol ID compression */
+ len = p->data[0] & 1;
+ }
+
+ len += p->len;
+
+ /* Got a complete packet yet? */
+ if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+ if (len > ppp->mrru) {
+ ++ppp->stats.rx_length_errors;
+ } else if ((skb = dev_alloc_skb(len)) == NULL) {
+ ++ppp->stats.rx_missed_errors;
+ } else {
+ tail = p;
+ break;
+ }
+ }
+
+ /*
+ * If this is the ending fragment of a packet,
+ * and we haven't found a complete valid packet yet,
+ * we can discard up to and including this fragment.
+ */
+ if (p->BEbits & E)
+ head = next;
+
+ ++seq;
+ }
+
+ /* If we have a complete packet, copy it all into one skb. */
+ if (tail != NULL) {
+ /* If we have discarded any fragments,
+ signal a receive error. */
+ if (head->sequence != ppp->nextseq)
+ ppp_receive_error(ppp);
+
+ /* uncompress protocol ID */
+ if (head->data[0] & 1)
+ *skb_put(skb, 1) = 0;
+ p = head;
+ for (;;) {
+ memcpy(skb_put(skb, p->len), p->data, p->len);
+ if (p == tail)
+ break;
+ p = p->next;
+ }
+ ppp->nextseq = tail->sequence + 1;
+ head = tail->next;
+ }
+
+ /* Discard all the skbuffs that we have copied the data out of
+ or that we can't use. */
+ while ((p = list->next) != head) {
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ }
+
+ return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
/*
* Channel interface.
*/
/*
- * Connect a channel to a given PPP unit.
- * The channel MUST NOT be connected to a PPP unit already.
+ * Create a new, unattached ppp channel.
*/
int
-ppp_register_channel(struct ppp_channel *chan, int unit)
+ppp_register_channel(struct ppp_channel *chan)
{
- struct ppp *ppp;
struct channel *pch;
- int ret = -ENXIO;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp == 0)
- goto out;
pch = kmalloc(sizeof(struct channel), GFP_ATOMIC);
- ret = -ENOMEM;
if (pch == 0)
- goto out;
+ return -ENOMEM;
memset(pch, 0, sizeof(struct channel));
- pch->ppp = ppp;
+ pch->ppp = NULL;
pch->chan = chan;
- list_add(&pch->list, &ppp->channels);
chan->ppp = pch;
- ++ppp->n_channels;
- if (ppp->dev && chan->hdrlen + PPP_HDRLEN > ppp->dev->hard_header_len)
- ppp->dev->hard_header_len = chan->hdrlen + PPP_HDRLEN;
- ret = 0;
- out:
- spin_unlock(&all_ppp_lock);
- return ret;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
+ spin_lock_init(&pch->downl);
+ pch->upl = RW_LOCK_UNLOCKED;
+ spin_lock_bh(&all_channels_lock);
+ pch->file.index = ++last_channel_index;
+ list_add(&pch->file.list, &all_channels);
+ spin_unlock_bh(&all_channels_lock);
+ MOD_INC_USE_COUNT;
+ return 0;
}
/*
- * Disconnect a channel from its PPP unit.
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ return pch->file.index;
+}
+
+/*
+ * Disconnect a channel from the generic layer.
+ * This can be called from mainline or BH/softirq level.
*/
void
ppp_unregister_channel(struct ppp_channel *chan)
{
- struct channel *pch;
+ struct channel *pch = chan->ppp;
- spin_lock(&all_ppp_lock);
- if ((pch = chan->ppp) != 0) {
- chan->ppp = 0;
- list_del(&pch->list);
- --pch->ppp->n_channels;
- kfree(pch);
- }
- spin_unlock(&all_ppp_lock);
+ if (pch == 0)
+ return; /* should never happen */
+ chan->ppp = 0;
+
+ /*
+ * This ensures that we have returned from any calls into the
+ * the channel's start_xmit or ioctl routine before we proceed.
+ */
+ spin_lock_bh(&pch->downl);
+ pch->chan = 0;
+ spin_unlock_bh(&pch->downl);
+ ppp_disconnect_channel(pch);
+ wake_up_interruptible(&pch->file.rwait);
+ spin_lock_bh(&all_channels_lock);
+ list_del(&pch->file.list);
+ spin_unlock_bh(&all_channels_lock);
+ if (atomic_dec_and_test(&pch->file.refcnt))
+ ppp_destroy_channel(pch);
+ MOD_DEC_USE_COUNT;
}
/*
* Callback from a channel when it can accept more to transmit.
- * This should ideally be called at BH level, not interrupt level.
+ * This should be called at BH/softirq level, not interrupt level.
*/
void
ppp_output_wakeup(struct ppp_channel *chan)
@@ -1174,11 +1704,75 @@ ppp_output_wakeup(struct ppp_channel *chan)
if (pch == 0)
return;
- ppp = pch->ppp;
- pch->blocked = 0;
- set_bit(XMIT_WAKEUP, &ppp->busy);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ ppp_channel_push(pch);
+ if (skb_queue_len(&pch->file.xq) == 0) {
+ read_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0)
+ ppp_xmit_process(ppp, 1);
+ read_unlock_bh(&pch->upl);
+ }
+}
+
+/*
+ * This is basically temporary compatibility stuff.
+ */
+ssize_t
+ppp_channel_read(struct ppp_channel *chan, struct file *file,
+ char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_read(&pch->file, file, buf, count);
+}
+
+ssize_t
+ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_write(&pch->file, buf, count);
+}
+
+unsigned int
+ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ struct channel *pch = chan->ppp;
+
+ mask = POLLOUT | POLLWRNORM;
+ if (pch != 0) {
+ poll_wait(file, &pch->file.rwait, wait);
+ if (skb_peek(&pch->file.rq) != 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ return mask;
+}
+
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg)
+{
+ struct channel *pch = chan->ppp;
+ int err = -ENOTTY;
+ int unit;
+
+ if (pch == 0)
+ return -EINVAL;
+ switch (cmd) {
+ case PPPIOCATTACH:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+ case PPPIOCDETACH:
+ err = ppp_disconnect_channel(pch);
+ break;
+ }
+ return err;
}
/*
@@ -1192,6 +1786,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
int err;
struct compressor *cp;
struct ppp_option_data data;
+ void *state;
unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
#ifdef CONFIG_KMOD
char modname[32];
@@ -1220,34 +1815,41 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
err = -ENOBUFS;
if (data.transmit) {
- lock_xmit_path(ppp);
+ ppp_xmit_lock(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state != 0) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
-
- ppp->xcomp = cp;
- ppp->xc_state = cp->comp_alloc(ccp_option, data.length);
- ppp_xmit_unlock(ppp, 1);
- if (ppp->xc_state == 0)
- goto out;
+ ppp_xmit_unlock(ppp);
+
+ state = cp->comp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_xmit_lock(ppp);
+ ppp->xcomp = cp;
+ ppp->xc_state = state;
+ ppp_xmit_unlock(ppp);
+ err = 0;
+ }
} else {
- lock_recv_path(ppp);
+ ppp_recv_lock(ppp);
ppp->rstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state != 0) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
-
- ppp->rcomp = cp;
- ppp->rc_state = cp->decomp_alloc(ccp_option, data.length);
ppp_recv_unlock(ppp);
- if (ppp->rc_state == 0)
- goto out;
+
+ state = cp->decomp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_recv_lock(ppp);
+ ppp->rcomp = cp;
+ ppp->rc_state = state;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ }
}
- err = 0;
out:
return err;
@@ -1292,7 +1894,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->rc_state == 0)
break;
if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
- ppp->index, 0, ppp->mru, ppp->debug)) {
+ ppp->file.index, 0, ppp->mru, ppp->debug)) {
ppp->rstate |= SC_DECOMP_RUN;
ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
}
@@ -1301,7 +1903,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->xc_state == 0)
break;
if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
- ppp->index, 0, ppp->debug))
+ ppp->file.index, 0, ppp->debug))
ppp->xstate |= SC_COMP_RUN;
}
break;
@@ -1329,21 +1931,17 @@ ppp_ccp_closed(struct ppp *ppp)
{
ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
- lock_xmit_path(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
- ppp_xmit_unlock(ppp, 1);
- lock_recv_path(ppp);
ppp->xstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
- ppp_recv_unlock(ppp);
}
/* List of compressors. */
@@ -1451,16 +2049,17 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
}
/*
- * Stuff for handling the list of ppp units and for initialization.
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
*/
/*
- * Create a new ppp unit. Fails if it can't allocate memory or
- * if there is already a unit with the requested number.
+ * Create a new ppp interface unit. Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
* unit == -1 means allocate a new number.
*/
static struct ppp *
-ppp_create_unit(int unit, int *retp)
+ppp_create_interface(int unit, int *retp)
{
struct ppp *ppp;
struct net_device *dev;
@@ -1472,13 +2071,13 @@ ppp_create_unit(int unit, int *retp)
spin_lock(&all_ppp_lock);
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if ((unit < 0 && ppp->index > last_unit + 1)
- || (unit >= 0 && unit < ppp->index))
+ ppp = list_entry(list, struct ppp, file.list);
+ if ((unit < 0 && ppp->file.index > last_unit + 1)
+ || (unit >= 0 && unit < ppp->file.index))
break;
- if (unit == ppp->index)
+ if (unit == ppp->file.index)
goto out; /* unit already exists */
- last_unit = ppp->index;
+ last_unit = ppp->file.index;
}
if (unit < 0)
unit = last_unit + 1;
@@ -1496,17 +2095,15 @@ ppp_create_unit(int unit, int *retp)
}
memset(dev, 0, sizeof(struct net_device));
- ppp->index = unit;
+ ppp->file.index = unit;
sprintf(ppp->name, "ppp%d", unit);
ppp->mru = PPP_MRU;
- skb_queue_head_init(&ppp->xq);
- skb_queue_head_init(&ppp->rq);
- init_waitqueue_head(&ppp->rwait);
- ppp->refcnt = 1;
+ init_ppp_file(&ppp->file, INTERFACE);
for (i = 0; i < NUM_NP; ++i)
ppp->npmode[i] = NPMODE_PASS;
INIT_LIST_HEAD(&ppp->channels);
- skb_queue_head_init(&ppp->recv_pending);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
ppp->dev = dev;
dev->init = ppp_net_init;
@@ -1524,7 +2121,7 @@ ppp_create_unit(int unit, int *retp)
goto out;
}
- list_add(&ppp->list, list->prev);
+ list_add(&ppp->file.list, list->prev);
out:
spin_unlock(&all_ppp_lock);
*retp = ret;
@@ -1534,56 +2131,59 @@ ppp_create_unit(int unit, int *retp)
}
/*
- * Remove a reference to a ppp unit, and destroy it if
- * the reference count goes to 0.
+ * Initialize a ppp_file structure.
*/
-static void ppp_release_unit(struct ppp *ppp)
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
{
- struct list_head *list, *next;
- int ref;
+ pf->kind = kind;
+ skb_queue_head_init(&pf->xq);
+ skb_queue_head_init(&pf->rq);
+ atomic_set(&pf->refcnt, 1);
+ init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Free up all the resources used by a ppp interface unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
+{
+ struct net_device *dev;
spin_lock(&all_ppp_lock);
- ref = --ppp->refcnt;
- if (ref == 0)
- list_del(&ppp->list);
- spin_unlock(&all_ppp_lock);
- if (ref != 0)
- return;
+ list_del(&ppp->file.list);
/* Last fd open to this ppp unit is being closed or detached:
mark the interface down, free the ppp unit */
- if (ppp->dev) {
- rtnl_lock();
- dev_close(ppp->dev);
- rtnl_unlock();
- }
- for (list = ppp->channels.next; list != &ppp->channels; list = next) {
- /* forcibly detach this channel */
- struct channel *chan;
- chan = list_entry(list, struct channel, list);
- chan->chan->ppp = 0;
- next = list->next;
- kfree(chan);
- }
-
- /* Free up resources. */
+ ppp_lock(ppp);
ppp_ccp_closed(ppp);
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
if (ppp->vj) {
slhc_free(ppp->vj);
ppp->vj = 0;
}
- free_skbs(&ppp->xq);
- free_skbs(&ppp->rq);
- free_skbs(&ppp->recv_pending);
- if (ppp->dev) {
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+ dev = ppp->dev;
+ ppp->dev = 0;
+ ppp_unlock(ppp);
+
+ if (dev) {
rtnl_lock();
- unregister_netdevice(ppp->dev);
- ppp->dev = 0;
+ dev_close(dev);
+ unregister_netdevice(dev);
rtnl_unlock();
}
- kfree(ppp);
+
+ /*
+ * We can't acquire any new channels (since we have the
+ * all_ppp_lock) so if n_channels is 0, we can free the
+ * ppp structure. Otherwise we leave it around until the
+ * last channel disconnects from it.
+ */
+ if (ppp->n_channels == 0)
+ kfree(ppp);
+
+ spin_unlock(&all_ppp_lock);
}
/*
@@ -1598,32 +2198,136 @@ ppp_find_unit(int unit)
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if (ppp->index == unit)
+ ppp = list_entry(list, struct ppp, file.list);
+ if (ppp->file.index == unit)
return ppp;
}
return 0;
}
/*
- * Module stuff.
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
*/
-#ifdef MODULE
-int
-init_module(void)
+static struct channel *
+ppp_find_channel(int unit)
{
- ppp_init();
+ struct channel *pch;
+ struct list_head *list;
+
+ list = &all_channels;
+ while ((list = list->next) != &all_channels) {
+ pch = list_entry(list, struct channel, file.list);
+ if (pch->file.index == unit)
+ return pch;
+ }
return 0;
}
-void
-cleanup_module(void)
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+ struct ppp *ppp;
+ int ret = -ENXIO;
+ int hdrlen;
+
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp == 0)
+ goto out;
+ write_lock_bh(&pch->upl);
+ ret = -EINVAL;
+ if (pch->ppp != 0)
+ goto outw;
+ ppp_lock(ppp);
+ spin_lock_bh(&pch->downl);
+ if (pch->chan == 0) /* need to check this?? */
+ goto outr;
+
+ hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
+ if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+ ppp->dev->hard_header_len = hdrlen;
+ list_add(&pch->clist, &ppp->channels);
+ ++ppp->n_channels;
+ pch->ppp = ppp;
+ ret = 0;
+
+ outr:
+ spin_unlock_bh(&pch->downl);
+ ppp_unlock(ppp);
+ outw:
+ write_unlock_bh(&pch->upl);
+ out:
+ spin_unlock(&all_ppp_lock);
+ return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+ struct ppp *ppp;
+ int err = -EINVAL;
+
+ write_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0) {
+ /* remove it from the ppp unit's list */
+ pch->ppp = NULL;
+ ppp_lock(ppp);
+ list_del(&pch->clist);
+ --ppp->n_channels;
+ if (ppp->dev == 0 && ppp->n_channels == 0)
+ /* Last disconnect from a ppp unit
+ that is already dead: free it. */
+ kfree(ppp);
+ else
+ ppp_unlock(ppp);
+ err = 0;
+ }
+ write_unlock_bh(&pch->upl);
+ return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ kfree(pch);
+}
+
+void __exit ppp_cleanup(void)
{
/* should never happen */
- if (!list_empty(&all_ppp_units))
+ if (!list_empty(&all_ppp_units) || !list_empty(&all_channels))
printk(KERN_ERR "PPP: removing module but units remain!\n");
if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
printk(KERN_ERR "PPP: failed to unregister PPP device\n");
- devfs_unregister (devfs_handle);
+ devfs_unregister(devfs_handle);
}
-#endif /* MODULE */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+EXPORT_SYMBOL(ppp_channel_read);
+EXPORT_SYMBOL(ppp_channel_write);
+EXPORT_SYMBOL(ppp_channel_poll);
+EXPORT_SYMBOL(ppp_channel_ioctl);
+EXPORT_SYMBOL(all_ppp_units); /* for debugging */
+EXPORT_SYMBOL(all_channels); /* for debugging */
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index 1a69f6ede..8bae76e1c 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -439,7 +439,7 @@ ppp_sync_ioctl(struct tty_struct *tty, struct file *file,
break;
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
- err = ppp_register_channel(&ap->chan, val);
+ err = ppp_register_channel(&ap->chan);
if (err != 0)
break;
ap->connected = 1;
diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c
index 27b0c6474..e19f1e0fd 100644
--- a/drivers/net/rcpci45.c
+++ b/drivers/net/rcpci45.c
@@ -1034,6 +1034,9 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
printk("RCioctl: cmd = 0x%x\n", cmd);
#endif
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case RCU_PROTOCOL_REV:
@@ -1157,14 +1160,14 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
RCUD_DEFAULT -> rc = 0x11223344;
break;
}
- copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+ if(copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser)))
+ return -EFAULT;
break;
- } /* RCU_COMMAND */
+ } /* RCU_COMMAND */
- default:
- printk("RC default\n");
- rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
- break;
+ default:
+ rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+ return -EINVAL;
}
return 0;
}
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index a3a0018cb..8e8236e92 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -76,8 +76,8 @@ static inline void netif_start_queue(struct net_device *dev)
#else
#define NET_BH 0
#define rr_mark_net_bh(foo) {do{} while(0);}
-#define rr_if_busy(dev) test_bit(LINK_STATE_XOFF, &dev->state)
-#define rr_if_running(dev) test_bit(LINK_STATE_START, &dev->state)
+#define rr_if_busy(dev) netif_queue_stopped(dev)
+#define rr_if_running(dev) netif_running(dev)
#define rr_if_down(dev) {do{} while(0);}
#endif
@@ -1550,7 +1550,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
switch(cmd){
case SIOCRRGFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
@@ -1582,7 +1582,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
kfree(image);
break;
case SIOCRRPFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index 9b17cc79d..bcf6e7e5b 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -1052,7 +1052,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMFREQUENCY: /* set frequency */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(get_user(frequency, (int*) ifr->ifr_data))
return -EFAULT;
@@ -1068,7 +1068,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMPIDS: /* set PIDs */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(copy_from_user(PID, ifr->ifr_data, sizeof(PID)))
return -EFAULT;
diff --git a/drivers/net/setup.c b/drivers/net/setup.c
index d9019b046..f8f59b9fb 100644
--- a/drivers/net/setup.c
+++ b/drivers/net/setup.c
@@ -28,6 +28,7 @@ extern int dlci_setup(void);
extern int lapbeth_init(void);
extern int sdla_setup(void);
extern int sdla_c_setup(void);
+extern int comx_init(void);
extern int abyss_probe(void);
extern int madgemc_probe(void);
@@ -75,7 +76,9 @@ struct net_probe pci_probes[] __initdata = {
#if defined(CONFIG_8xx)
{cpm_enet_init, 0},
#endif
- /*
+#if defined(CONFIG_COMX)
+ {comx_init(), 0},
+#endif /*
* SLHC if present needs attaching so other people see it
* even if not opened.
*/
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index 89cbb0e9e..9190b5a87 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -480,6 +480,8 @@ static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev,
}
#endif
+#ifdef CONFIG_INET
+
static int shaper_neigh_setup(struct neighbour *n)
{
if (n->nud_state == NUD_NONE) {
@@ -499,6 +501,15 @@ static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
return 0;
}
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+ return 0;
+}
+
+#endif
+
static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
{
sh->dev = dev;
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index f8af277ea..2653270c3 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1127,7 +1127,7 @@ static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c
index e1e48bc91..93533c5ef 100644
--- a/drivers/net/skfp/drvfbi.c
+++ b/drivers/net/skfp/drvfbi.c
@@ -129,7 +129,7 @@ extern int AIX_vpdReadByte() ;
/*
* FDDI card reset
*/
-void card_start(smc)
+static void card_start(smc)
struct s_smc *smc ;
{
int i ;
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index 6aceec4d5..b6b112318 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1249,7 +1249,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len);
break;
case SKFP_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
memset(&lp->MacStat, 0, sizeof(lp->MacStat));
} else {
status = -EPERM;
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 344e682f8..059d4a4ea 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.92 2000/02/18 13:49:22 davem Exp $
+/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -254,7 +254,7 @@ do { (__txd)->tx_addr = (__addr); \
#define hme_read_desc32(__hp, __p) (*(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
-#define hme_dma_unmap(__hp, __addr, __size) \
+#define hme_dma_unmap(__hp, __addr, __size, __dir) \
sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
#define hme_dma_sync(__hp, __addr, __size, __dir) \
sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))
diff --git a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in
index 31688f34d..316b638c2 100644
--- a/drivers/net/tokenring/Config.in
+++ b/drivers/net/tokenring/Config.in
@@ -9,6 +9,7 @@ bool 'Token Ring driver support' CONFIG_TR
if [ "$CONFIG_TR" != "n" ]; then
dep_tristate ' IBM Tropic chipset based adapter support' CONFIG_IBMTR $CONFIG_TR
dep_tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR
+ dep_tristate ' IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR
dep_tristate ' Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR $CONFIG_TR
if [ "$CONFIG_TMS380TR" != "n" ]; then
dep_tristate ' Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_TMS380TR
diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile
index f90055b45..1fa4547c4 100644
--- a/drivers/net/tokenring/Makefile
+++ b/drivers/net/tokenring/Makefile
@@ -18,6 +18,7 @@ export-objs := tms380tr.o
obj-$(CONFIG_IBMTR) += ibmtr.o
obj-$(CONFIG_IBMOL) += olympic.o
+obj-$(CONFIG_IBMLS) += lanstreamer.o
obj-$(CONFIG_TMS380TR) += tms380tr.o
obj-$(CONFIG_ABYSS) += abyss.o
obj-$(CONFIG_MADGEMC) += madgemc.o
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
new file mode 100644
index 000000000..e0a339bfa
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -0,0 +1,1776 @@
+/*
+ * lanstreamer.c -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ * 03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing
+ * malloc free checks, reviewed code. <alan@redhat.com>
+ *
+ * To Do:
+ *
+ * 1) Test Network Monitor Mode
+ * 2) Add auto reset logic on adapter errors
+ * 3) Test with varying options
+ *
+ * If Problems do Occur
+ * Most problems can be rectified by either closing and opening the interface
+ * (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult
+ * if compiled into the kernel).
+ */
+
+/* Change STREAMER_DEBUG to 1 to get verbose, and I mean really verbose, messages */
+
+#define STREAMER_DEBUG 0
+#define STREAMER_DEBUG_PACKETS 0
+
+/* Change STREAMER_NETWORK_MONITOR to receive mac frames through the arb channel.
+ * Will also create a /proc/net/streamer_tr entry if proc_fs is compiled into the
+ * kernel.
+ * Intended to be used to create a ring-error reporting network module
+ * i.e. it will give you the source address of beaconers on the ring
+ */
+
+#define STREAMER_NETWORK_MONITOR 0
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <net/checksum.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include "lanstreamer.h"
+
+/* I've got to put some intelligence into the version number so that Peter and I know
+ * which version of the code somebody has got.
+ * Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author.
+ * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike
+ *
+ * Official releases will only have an a.b.c version number format.
+ */
+
+static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan";
+
+static char *open_maj_error[] = {
+ "No error", "Lobe Media Test", "Physical Insertion",
+ "Address Verification", "Neighbor Notification (Ring Poll)",
+ "Request Parameters", "FDX Registration Request",
+ "FDX Lobe Media Test", "FDX Duplicate Address Check",
+ "Unknown stage"
+};
+
+static char *open_min_error[] = {
+ "No error", "Function Failure", "Signal Lost", "Wire Fault",
+ "Ring Speed Mismatch", "Timeout", "Ring Failure", "Ring Beaconing",
+ "Duplicate Node Address", "Request Parameters", "Remove Received",
+ "Reserved", "Reserved", "No Monitor Detected for RPL",
+ "Monitor Contention failer for RPL", "FDX Protocol Error"
+};
+
+/* Module paramters */
+
+/* Ring Speed 0,4,16
+ * 0 = Autosense
+ * 4,16 = Selected speed only, no autosense
+ * This allows the card to be the first on the ring
+ * and become the active monitor.
+ *
+ * WARNING: Some hubs will allow you to insert
+ * at the wrong speed
+ */
+
+static int ringspeed[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(ringspeed, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Packet buffer size */
+
+static int pkt_buf_sz[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Message Level */
+
+static int message_level[STREAMER_MAX_ADAPTERS] = { 1, };
+
+MODULE_PARM(message_level,
+ "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+static int streamer_scan(struct net_device *dev);
+static int streamer_init(struct net_device *dev);
+static int streamer_open(struct net_device *dev);
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev);
+static int streamer_close(struct net_device *dev);
+static void streamer_set_rx_mode(struct net_device *dev);
+static void streamer_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs);
+static struct net_device_stats *streamer_get_stats(struct net_device *dev);
+static int streamer_set_mac_address(struct net_device *dev, void *addr);
+static void streamer_arb_cmd(struct net_device *dev);
+static int streamer_change_mtu(struct net_device *dev, int mtu);
+static void streamer_srb_bh(struct net_device *dev);
+static void streamer_asb_bh(struct net_device *dev);
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int sprintf_info(char *buffer, struct net_device *dev);
+#endif
+#endif
+
+int __init streamer_probe(struct net_device *dev)
+{
+ int cards_found;
+
+ cards_found = streamer_scan(dev);
+ return cards_found ? 0 : -ENODEV;
+}
+
+static int __init streamer_scan(struct net_device *dev)
+{
+ struct pci_dev *pci_device = NULL;
+ struct streamer_private *streamer_priv;
+ int card_no = 0;
+ if (pci_present())
+ {
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+ pci_set_master(pci_device);
+
+ /* Check to see if io has been allocated, if so, we've already done this card,
+ so continue on the card discovery loop */
+
+ if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE))
+ {
+ card_no++;
+ continue;
+ }
+
+ streamer_priv = kmalloc(sizeof(struct streamer_private), GFP_KERNEL);
+ if(streamer_priv==NULL)
+ {
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+ memset(streamer_priv, 0, sizeof(struct streamer_private));
+#ifndef MODULE
+ dev = init_trdev(dev, 0);
+ if(dev==NULL)
+ {
+ kfree(streamer_priv);
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+#endif
+ dev->priv = (void *) streamer_priv;
+#if STREAMER_DEBUG
+ printk("pci_device: %p, dev:%p, dev->priv: %p\n",
+ pci_device, dev, dev->priv);
+#endif
+ dev->irq = pci_device->irq;
+ dev->base_addr = pci_device->resource[0].start;
+ dev->init = &streamer_init;
+ streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
+ init_waitqueue_head(&streamer_priv->srb_wait);
+ init_waitqueue_head(&streamer_priv->trb_wait);
+ if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
+ streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
+ else
+ streamer_priv->pkt_buf_sz = pkt_buf_sz[card_no];
+
+ streamer_priv->streamer_ring_speed = ringspeed[card_no];
+ streamer_priv->streamer_message_level = message_level[card_no];
+ streamer_priv->streamer_multicast_set = 0;
+
+ if (streamer_init(dev) == -1) {
+ unregister_netdevice(dev);
+ kfree(dev->priv);
+ return 0;
+ }
+
+ dev->open = &streamer_open;
+ dev->hard_start_xmit = &streamer_xmit;
+ dev->change_mtu = &streamer_change_mtu;
+
+ dev->stop = &streamer_close;
+ dev->do_ioctl = NULL;
+ dev->set_multicast_list = &streamer_set_rx_mode;
+ dev->get_stats = &streamer_get_stats;
+ dev->set_mac_address = &streamer_set_mac_address;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int __init streamer_init(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ __u8 *streamer_mmio;
+ unsigned long t;
+ unsigned int uaa_addr;
+ struct sk_buff *skb = 0;
+ __u16 misr;
+
+ streamer_priv = (struct streamer_private *) dev->priv;
+ streamer_mmio = streamer_priv->streamer_mmio;
+
+ printk("%s \n", version);
+ printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n",
+ dev->name, (unsigned int) dev->base_addr,
+ streamer_priv->streamer_mmio, dev->irq);
+
+ request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
+ writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL);
+ t = jiffies;
+ /* Hold soft reset bit for a while */
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ);
+
+ writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET,
+ streamer_mmio + BCTL);
+
+#if STREAMER_DEBUG
+ printk("BCTL: %x\n", readw(streamer_mmio + BCTL));
+ printk("GPR: %x\n", readw(streamer_mmio + GPR));
+ printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+ if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */
+ writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE,
+ streamer_mmio + GPR);
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Ringspeed autosense mode on\n",
+ dev->name);
+ } else if (streamer_priv->streamer_ring_speed == 16) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n",
+ dev->name);
+ writew(GPR_16MBPS, streamer_mmio + GPR);
+ } else if (streamer_priv->streamer_ring_speed == 4) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n",
+ dev->name);
+ writew(0, streamer_mmio + GPR);
+ }
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (!skb) {
+ printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n",
+ dev->name);
+ } else {
+ streamer_priv->streamer_rx_ring[0].forward = 0;
+ streamer_priv->streamer_rx_ring[0].status = 0;
+ streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[0].framelen_buflen = 512; /* streamer_priv->pkt_buf_sz; */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ }
+
+#if STREAMER_DEBUG
+ printk("GPR = %x\n", readw(streamer_mmio + GPR));
+#endif
+ /* start solo init */
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+
+ while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+ if (jiffies - t > 40 * HZ) {
+ printk(KERN_ERR
+ "IBM PCI tokenring card not responding\n");
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+ }
+ writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (skb)
+ dev_kfree_skb(skb); /* release skb used for diagnostics */
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x LAPE: %x\n",
+ readw(streamer_mmio + LAPWWO), readw(streamer_mmio + LAPA),
+ readw(streamer_mmio + LAPE));
+#endif
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("initialization response srb dump: ");
+ for (i = 0; i < 10; i++)
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ printk("\n");
+ }
+#endif
+
+ writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA);
+ if (readw(streamer_mmio + LAPD)) {
+ printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",
+ readw(streamer_mmio + LAPD));
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+ uaa_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* skip over Level.Addr field */
+ streamer_priv->streamer_addr_table_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->streamer_parms_addr = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ printk("UAA resides at %x\n", uaa_addr);
+#endif
+
+ /* setup uaa area for access with LAPD */
+ writew(uaa_addr, streamer_mmio + LAPA);
+
+ /* setup uaa area for access with LAPD */
+ {
+ int i;
+ __u16 addr;
+ writew(uaa_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 6; i += 2) {
+ addr = readw(streamer_mmio + LAPDINC);
+ dev->dev_addr[i] = addr & 0xff;
+ dev->dev_addr[i + 1] = (addr >> 8) & 0xff;
+ }
+#if STREAMER_DEBUG
+ printk("Adapter address: ");
+ for (i = 0; i < 6; i++) {
+ printk("%02x:", dev->dev_addr[i]);
+ }
+ printk("\n");
+#endif
+ }
+ return 0;
+}
+
+static int streamer_open(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ char open_error[255];
+ int i, open_finished = 1;
+ __u16 srb_word;
+ __u16 srb_open;
+
+
+ if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) {
+ return -EAGAIN;
+ }
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("pending ints: %x\n", readw(streamer_mmio + SISR));
+#endif
+
+ writew(SISR_MI | SISR_SRB_REPLY, streamer_mmio + SISR_MASK); /* more ints later, doesn't stop arb cmd interrupt */
+ writew(LISR_LIE, streamer_mmio + LISR); /* more ints later */
+
+ /* adapter is closed, so SRB is pointed to by LAPWWO */
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x\n", readw(streamer_mmio + LAPWWO),
+ readw(streamer_mmio + LAPA));
+ printk("LAPE: %x\n", readw(streamer_mmio + LAPE));
+ printk("SISR Mask = %04x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+ do {
+ int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < SRB_COMMAND_SIZE; i += 2) {
+ writew(0, streamer_mmio + LAPDINC);
+ }
+
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC); /* open */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+#if STREAMER_NETWORK_MONITOR
+ /* If Network Monitor, instruct card to copy MAC frames through the ARB */
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC); /* offset 8 word contains open options */
+#else
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC); /* Offset 8 word contains Open.Options */
+#endif
+
+ if (streamer_priv->streamer_laa[0]) {
+ writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA);
+ writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC); /* offset 12 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC); /* offset 14 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC); /* offset 16 word */
+ memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len);
+ }
+
+ /* save off srb open offset */
+ srb_open = readw(streamer_mmio + LAPWWO);
+#if STREAMER_DEBUG
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("srb open request: \n");
+ for (i = 0; i < 16; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+
+ streamer_priv->srb_queued = 1;
+
+ /* signal solo that SRB command has been issued */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued) {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ);
+ if (signal_pending(current)) {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x, LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+ restore_flags(flags);
+
+#if STREAMER_DEBUG
+ printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+ printk("srb open response:\n");
+ writew(srb_open, streamer_mmio + LAPA);
+ for (i = 0; i < 10; i++) {
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+#endif
+
+ /* If we get the same return response as we set, the interrupt wasn't raised and the open
+ * timed out.
+ */
+ writew(srb_open + 2, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word == STREAMER_CLEAR_RET_CODE) {
+ printk(KERN_WARNING "%s: Adapter Open time out or error.\n",
+ dev->name);
+ return -EIO;
+ }
+
+ if (srb_word != 0) {
+ if (srb_word == 0x07) {
+ if (!streamer_priv->streamer_ring_speed && open_finished) { /* Autosense , first time around */
+ printk(KERN_WARNING "%s: Retrying at different ring speed \n",
+ dev->name);
+ open_finished = 0;
+ } else {
+ __u16 error_code;
+
+ writew(srb_open + 6, streamer_mmio + LAPA);
+ error_code = ntohs(readw(streamer_mmio + LAPD));
+ strcpy(open_error, open_maj_error[(error_code & 0xf0) >> 4]);
+ strcat(open_error, " - ");
+ strcat(open_error, open_min_error[(error_code & 0x0f)]);
+
+ if (!streamer_priv->streamer_ring_speed
+ && ((error_code & 0x0f) == 0x0d))
+ {
+ printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name);
+ printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ printk(KERN_WARNING "%s: %s\n",
+ dev->name, open_error);
+ free_irq(dev->irq, dev);
+ return -EIO;
+
+ } /* if autosense && open_finished */
+ } else {
+ printk(KERN_WARNING "%s: Bad OPEN response: %x\n",
+ dev->name, srb_word);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+ } else
+ open_finished = 1;
+ } while (!(open_finished)); /* Will only loop if ring speed mismatch re-open attempted && autosense is on */
+
+ writew(srb_open + 18, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word & (1 << 3))
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name);
+
+ if (srb_word & 1)
+ streamer_priv->streamer_ring_speed = 16;
+ else
+ streamer_priv->streamer_ring_speed = 4;
+
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in %d Mbps mode\n",
+ dev->name,
+ streamer_priv->streamer_ring_speed);
+
+ writew(srb_open + 8, streamer_mmio + LAPA);
+ streamer_priv->asb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->srb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->arb = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* offset 14 word is rsvd */
+ streamer_priv->trb = ntohs(readw(streamer_mmio + LAPDINC));
+
+ streamer_priv->streamer_receive_options = 0x00;
+ streamer_priv->streamer_copy_all_options = 0;
+
+ /* setup rx ring */
+ /* enable rx channel */
+ writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM);
+
+ /* setup rx descriptors */
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (skb == NULL)
+ break;
+
+ skb->dev = dev;
+
+ streamer_priv->streamer_rx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_rx_ring[i + 1]);
+ streamer_priv->streamer_rx_ring[i].status = 0;
+ streamer_priv->streamer_rx_ring[i].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[i].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->rx_ring_skb[i] = skb;
+ }
+ streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_rx_ring[0]);
+
+ if (i == 0) {
+ printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ streamer_priv->rx_ring_last_received = STREAMER_RX_RING_SIZE - 1; /* last processed rx status */
+
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1]), streamer_mmio + RXLBDA);
+
+ /* set bus master interrupt event mask */
+ writew(MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+
+
+ /* setup tx ring */
+ writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM); /* Enables TX channel 2 */
+ for (i = 0; i < STREAMER_TX_RING_SIZE; i++) {
+ streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]);
+ streamer_priv->streamer_tx_ring[i].status = 0;
+ streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[i].buffer = 0;
+ streamer_priv->streamer_tx_ring[i].buflen = 0;
+ }
+ streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_tx_ring[0]);;
+
+ streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE;
+ streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */
+ streamer_priv->tx_ring_last_status = STREAMER_TX_RING_SIZE - 1;
+
+ /* set Busmaster interrupt event mask (handle receives on interrupt only */
+ writew(MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+ /* set system event interrupt mask */
+ writew(SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE, streamer_mmio + SISR_MASK_SUM);
+
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("SISR MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+#if STREAMER_NETWORK_MONITOR
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ printk("%s: Node Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ readw(streamer_mmio + LAPDINC);
+ readw(streamer_mmio + LAPDINC);
+ printk("%s: Functional Address: %04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+
+ writew(streamer_priv->streamer_parms_addr + 4,
+ streamer_mmio + LAPA);
+ printk("%s: NAUN Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+#endif
+
+ netif_start_queue(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * When we enter the rx routine we do not know how many frames have been
+ * queued on the rx channel. Therefore we start at the next rx status
+ * position and travel around the receive ring until we have completed
+ * all the frames.
+ *
+ * This means that we may process the frame before we receive the end
+ * of frame interrupt. This is why we always test the status instead
+ * of blindly processing the next frame.
+ *
+ */
+static void streamer_rx(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_rx_desc *rx_desc;
+ int rx_ring_last_received, length, frame_length, buffer_cnt = 0;
+ struct sk_buff *skb, *skb2;
+
+ /* setup the next rx descriptor to be received */
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ while (rx_desc->status & 0x01000000) { /* While processed descriptors are available */
+ if (rx_ring_last_received != streamer_priv->rx_ring_last_received)
+ {
+ printk(KERN_WARNING "RX Error 1 rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received, streamer_priv->rx_ring_last_received);
+ }
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ frame_length = (rx_desc->framelen_buflen >> 16) & 0xffff;
+
+ if (rx_desc->status & 0x7E830000) { /* errors */
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_WARNING "%s: Rx Error %x \n",
+ dev->name, rx_desc->status);
+ }
+ } else { /* received without errors */
+ if (rx_desc->status & 0x80000000) { /* frame complete */
+ buffer_cnt = 1;
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ } else {
+ skb = dev_alloc_skb(frame_length);
+ }
+
+ if (skb == NULL)
+ {
+ printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name);
+ streamer_priv->streamer_stats.rx_dropped++;
+ } else { /* we allocated an skb OK */
+ skb->dev = dev;
+
+ if (buffer_cnt == 1) {
+ skb2 = streamer_priv->rx_ring_skb[rx_ring_last_received];
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_rx packet print: skb->data2 %p skb->head %p\n", skb2->data, skb2->head);
+ for (i = 0; i < frame_length; i++)
+ {
+ printk("%x:", skb2->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+ skb_put(skb2, length);
+ skb2->protocol = tr_type_trans(skb2, dev);
+ /* recycle this descriptor */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ streamer_priv-> rx_ring_skb[rx_ring_last_received] = skb;
+ /* place recycled descriptor back on the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]),streamer_mmio + RXLBDA);
+ /* pass the received skb up to the protocol */
+ netif_rx(skb2);
+ } else {
+ do { /* Walk the buffers */
+ memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length); /* copy this fragment */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ /* give descriptor back to the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA);
+
+ if (rx_desc->status & 0x80000000)
+ break; /* this descriptor completes the frame */
+
+ /* else get the next pending descriptor */
+ if (rx_ring_last_received!= streamer_priv->rx_ring_last_received)
+ {
+ printk("RX Error rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received,
+ streamer_priv->rx_ring_last_received);
+ }
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE-1)];
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+ } while (1);
+
+ skb->protocol = tr_type_trans(skb, dev);
+ /* send up to the protocol */
+ netif_rx(skb);
+ }
+ streamer_priv->streamer_stats.rx_packets++;
+ streamer_priv->streamer_stats.rx_bytes += length;
+ } /* if skb == null */
+ } /* end received without errors */
+
+ /* try the next one */
+ rx_desc = &streamer_priv->streamer_rx_ring[(rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ } /* end for all completed rx descriptors */
+}
+
+static void streamer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 sisr;
+ __u16 misr;
+ __u16 sisrmask;
+
+ sisrmask = SISR_MI;
+ writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
+ sisr = readw(streamer_mmio + SISR);
+ writew(~sisr, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (!sisr) { /* Interrupt isn't for us */
+ return;
+ }
+
+ if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
+ || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
+ if (sisr & SISR_SRB_REPLY) {
+ if (streamer_priv->srb_queued == 1) {
+ wake_up_interruptible(&streamer_priv->srb_wait);
+ } else if (streamer_priv->srb_queued == 2) {
+ streamer_srb_bh(dev);
+ }
+ streamer_priv->srb_queued = 0;
+ }
+ /* SISR_SRB_REPLY */
+ if (misr & MISR_TX2_EOF) {
+ while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status)
+ {
+ streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1);
+ streamer_priv->free_tx_ring_entries++;
+ streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len;
+ streamer_priv->streamer_stats.tx_packets++;
+ dev_kfree_skb_irq(streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]);
+ streamer_priv-> streamer_tx_ring[streamer_priv->tx_ring_last_status].buffer = 0xdeadbeef;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0;
+ }
+ netif_wake_queue(dev);
+ }
+
+ if (misr & MISR_RX_EOF) {
+ streamer_rx(dev);
+ }
+ /* MISR_RX_EOF */
+ if (sisr & SISR_ADAPTER_CHECK) {
+ printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name);
+ writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
+ dev->name, readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC));
+ free_irq(dev->irq, dev);
+ }
+
+ /* SISR_ADAPTER_CHECK */
+ if (sisr & SISR_ASB_FREE) {
+ /* Wake up anything that is waiting for the asb response */
+ if (streamer_priv->asb_queued) {
+ streamer_asb_bh(dev);
+ }
+ }
+ /* SISR_ASB_FREE */
+ if (sisr & SISR_ARB_CMD) {
+ streamer_arb_cmd(dev);
+ }
+ /* SISR_ARB_CMD */
+ if (sisr & SISR_TRB_REPLY) {
+ /* Wake up anything that is waiting for the trb response */
+ if (streamer_priv->trb_queued) {
+ wake_up_interruptible(&streamer_priv->
+ trb_wait);
+ }
+ streamer_priv->trb_queued = 0;
+ }
+ /* SISR_TRB_REPLY */
+ if (misr & MISR_RX_NOBUF) {
+ /* According to the documentation, we don't have to do anything, but trapping it keeps it out of
+ /var/log/messages. */
+ } /* SISR_RX_NOBUF */
+ } else {
+ printk(KERN_WARNING "%s: Unexpected interrupt: %x\n",
+ dev->name, sisr);
+ printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name,
+ readw(streamer_mmio + SISR_MASK));
+ } /* One if the interrupts we want */
+
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+}
+
+
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ netif_stop_queue(dev);
+
+ if (streamer_priv->free_tx_ring_entries) {
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len;
+ streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb;
+ streamer_priv->free_tx_ring_entries--;
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_xmit packet print:\n");
+ for (i = 0; i < skb->len; i++) {
+ printk("%x:", skb->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+
+ writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
+
+ streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
+ netif_start_queue(dev);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+static int streamer_close(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ int i;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ save_flags(flags);
+ cli();
+
+ streamer_priv->srb_queued = 1;
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued)
+ {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait,
+ jiffies + 60 * HZ);
+ if (signal_pending(current))
+ {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ }
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* TBD. Add graceful way to reset the LLC channel without doing a soft reset.
+ writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL);
+ */
+
+#if STREAMER_DEBUG
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ printk("srb): ");
+ for (i = 0; i < 2; i++) {
+ printk("%x ", htons(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void streamer_set_rx_mode(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 options = 0, set_mc_list = 0;
+ __u16 ata1, ata2;
+ struct dev_mc_list *dmi;
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+ options = streamer_priv->streamer_copy_all_options;
+
+ if (dev->flags & IFF_PROMISC)
+ options |= (3 << 5); /* All LLC and MAC frames, all through the main rx channel */
+ else
+ options &= ~(3 << 5);
+
+ if (dev->mc_count) {
+ set_mc_list = 1;
+ }
+
+ /* Only issue the srb if there is a change in options */
+
+ if ((options ^ streamer_priv->streamer_copy_all_options))
+ {
+ /* Now to issue the srb command to alter the copy.all.options */
+
+ writew(SRB_MODIFY_RECEIVE_OPTIONS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC);
+ writew(0x414a, streamer_mmio + LAPDINC);
+ writew(0x454d, streamer_mmio + LAPDINC);
+ writew(0x2053, streamer_mmio + LAPDINC);
+ writew(0x2020, streamer_mmio + LAPDINC);
+
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_copy_all_options = options;
+ return;
+ }
+
+ if (set_mc_list ^ streamer_priv->streamer_multicast_set)
+ { /* Multicast options have changed */
+ dmi = dev->mc_list;
+
+ writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ ata1 = readw(streamer_mmio + LAPDINC);
+ ata2 = readw(streamer_mmio + LAPD);
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+
+ if (set_mc_list)
+ {
+ /* Turn multicast on */
+
+ /* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00
+ * We do this with a set functional address mask.
+ */
+
+ if (!(ata1 & 0x0400)) { /* need to set functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 | 0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 1;
+ }
+
+ } else { /* Turn multicast off */
+
+ if ((ata1 & 0x0400)) { /* Hmmm, need to reset the functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 & ~0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 0;
+ }
+ }
+
+ }
+}
+
+static void streamer_srb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 srb_word;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+
+ switch (srb_word) {
+
+ /* SRB_MODIFY_RECEIVE_OPTIONS i.e. set_multicast_list options (promiscuous)
+ * At some point we should do something if we get an error, such as
+ * resetting the IFF_PROMISC flag in dev
+ */
+
+ case SRB_MODIFY_RECEIVE_OPTIONS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_WARNING "%s: Receive Options Modified to %x,%x\n",
+ dev->name,
+ streamer_priv->streamer_copy_all_options,
+ streamer_priv->streamer_receive_options);
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_GROUP_ADDRESS - Multicast group setting
+ */
+ case SRB_SET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 1;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x3c:
+ printk(KERN_WARNING "%s: Group/Functional address indicator bits not set correctly\n", dev->name);
+ break;
+ case 0x3e: /* If we ever implement individual multicast addresses, will need to deal with this */
+ printk(KERN_WARNING "%s: Group address registers full\n", dev->name);
+ break;
+ case 0x55:
+ printk(KERN_INFO "%s: Group Address already set.\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list
+ */
+ case SRB_RESET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 0;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x39: /* Must deal with this if individual multicast addresses used */
+ printk(KERN_INFO "%s: Group address not found \n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_FUNC_ADDRESS - Called by the set_rx_mode
+ */
+
+ case SRB_SET_FUNC_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Functional Address Mask Set \n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_LOG - Read and reset the adapter error counters
+ */
+
+ case SRB_READ_LOG:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ {
+ int i;
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Log command complete\n", dev->name);
+ printk("Read Log statistics: ");
+ writew(streamer_priv->srb + 6,
+ streamer_mmio + LAPA);
+ for (i = 0; i < 5; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+ }
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */
+
+ case SRB_READ_SR_COUNTERS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Source Routing Counters issued\n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ default:
+ printk(KERN_WARNING "%s: Unrecognized srb bh return value.\n", dev->name);
+ break;
+ } /* switch srb[0] */
+}
+
+static struct net_device_stats *streamer_get_stats(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ streamer_priv = (struct streamer_private *) dev->priv;
+ return (struct net_device_stats *) &streamer_priv->streamer_stats;
+}
+
+static int streamer_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *saddr = addr;
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+
+ if (netif_running(dev)) {
+ printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name);
+ return -EBUSY;
+ }
+
+ memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len);
+
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_INFO "%s: MAC/LAA Set to = %x.%x.%x.%x.%x.%x\n",
+ dev->name, streamer_priv->streamer_laa[0],
+ streamer_priv->streamer_laa[1],
+ streamer_priv->streamer_laa[2],
+ streamer_priv->streamer_laa[3],
+ streamer_priv->streamer_laa[4],
+ streamer_priv->streamer_laa[5]);
+ }
+ return 0;
+}
+
+static void streamer_arb_cmd(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 header_len;
+ __u16 frame_len, buffer_len;
+ struct sk_buff *mac_frame;
+ __u8 frame_data[256];
+ __u16 buff_off;
+ __u16 lan_status = 0, lan_status_diff; /* Initialize to stop compiler warning */
+ __u8 fdx_prot_error;
+ __u16 next_ptr;
+ __u16 arb_word;
+
+#if STREAMER_NETWORK_MONITOR
+ struct trh_hdr *mac_hdr;
+#endif
+
+ writew(streamer_priv->arb, streamer_mmio + LAPA);
+ arb_word = readw(streamer_mmio + LAPD) & 0xFF;
+
+ if (arb_word == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC));
+ header_len = readw(streamer_mmio + LAPDINC) & 0xff; /* 802.5 Token-Ring Header Length */
+ frame_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ __u16 next;
+ __u8 status;
+ __u16 len;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /*setup window to frame data */
+ next = ntohs(readw(streamer_mmio + LAPDINC));
+ status =
+ ntohs(readw(streamer_mmio + LAPDINC)) & 0xff;
+ len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ /* print out 1st 14 bytes of frame data */
+ for (i = 0; i < 7; i++) {
+ printk("Loc %d = %04x\n", i,
+ ntohs(readw
+ (streamer_mmio + LAPDINC)));
+ }
+
+ printk("next %04x, fs %02x, len %04x \n", next,
+ status, len);
+ }
+#endif
+ mac_frame = dev_alloc_skb(frame_len);
+
+ /* Walk the buffer chain, creating the frame */
+
+ do {
+ int i;
+ __u16 rx_word;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /* setup window to frame data */
+ next_ptr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* read thru status word */
+ buffer_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ if (buffer_len > 256)
+ break;
+
+ i = 0;
+ while (i < buffer_len) {
+ rx_word = readw(streamer_mmio + LAPDINC);
+ frame_data[i] = rx_word & 0xff;
+ frame_data[i + 1] = (rx_word >> 8) & 0xff;
+ i += 2;
+ }
+
+ memcpy_fromio(skb_put(mac_frame, buffer_len),
+ frame_data, buffer_len);
+ } while (next_ptr && (buff_off = next_ptr));
+
+#if STREAMER_NETWORK_MONITOR
+ printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
+ dev->name);
+ mac_hdr = (struct trh_hdr *) mac_frame->data;
+ printk(KERN_WARNING
+ "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
+ mac_hdr->daddr[2], mac_hdr->daddr[3],
+ mac_hdr->daddr[4], mac_hdr->daddr[5]);
+ printk(KERN_WARNING
+ "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->saddr[0], mac_hdr->saddr[1],
+ mac_hdr->saddr[2], mac_hdr->saddr[3],
+ mac_hdr->saddr[4], mac_hdr->saddr[5]);
+#endif
+ mac_frame->dev = dev;
+ mac_frame->protocol = tr_type_trans(mac_frame, dev);
+ netif_rx(mac_frame);
+
+ /* Now tell the card we have dealt with the received frame */
+
+ /* Set LISR Bit 1 */
+ writel(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ /* Is the ASB free ? */
+
+ if (!(readl(streamer_priv->streamer_mmio + SISR) & SISR_ASB_FREE))
+ {
+ streamer_priv->asb_queued = 1;
+ writel(LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ return;
+ /* Drop out and wait for the bottom half to be run */
+ }
+
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+
+ streamer_priv->asb_queued = 2;
+ return;
+
+ } else if (arb_word == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ lan_status = ntohs(readw(streamer_mmio + LAPDINC));
+ fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF;
+
+ /* Issue ARB Free */
+ writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status;
+
+ if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR))
+ {
+ if (lan_status_diff & LSC_LWF)
+ printk(KERN_WARNING "%s: Short circuit detected on the lobe\n", dev->name);
+ if (lan_status_diff & LSC_ARW)
+ printk(KERN_WARNING "%s: Auto removal error\n", dev->name);
+ if (lan_status_diff & LSC_FPE)
+ printk(KERN_WARNING "%s: FDX Protocol Error\n", dev->name);
+ if (lan_status_diff & LSC_RR)
+ printk(KERN_WARNING "%s: Force remove MAC frame received\n", dev->name);
+
+ /* Adapter has been closed by the hardware */
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* @TBD. no llc reset on autostreamer writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); */
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name);
+
+ }
+ /* If serious error */
+ if (streamer_priv->streamer_message_level) {
+ if (lan_status_diff & LSC_SIG_LOSS)
+ printk(KERN_WARNING "%s: No receive signal detected \n", dev->name);
+ if (lan_status_diff & LSC_HARD_ERR)
+ printk(KERN_INFO "%s: Beaconing \n", dev->name);
+ if (lan_status_diff & LSC_SOFT_ERR)
+ printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n", dev->name);
+ if (lan_status_diff & LSC_TRAN_BCN)
+ printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name);
+ if (lan_status_diff & LSC_SS)
+ printk(KERN_INFO "%s: Single Station on the ring \n", dev->name);
+ if (lan_status_diff & LSC_RING_REC)
+ printk(KERN_INFO "%s: Ring recovery ongoing\n", dev->name);
+ if (lan_status_diff & LSC_FDX_MODE)
+ printk(KERN_INFO "%s: Operating in FDX mode\n", dev->name);
+ }
+
+ if (lan_status_diff & LSC_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Counter Overflow \n", dev->name);
+
+ /* Issue READ.LOG command */
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_LOG, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+ }
+
+ if (lan_status_diff & LSC_SR_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name);
+
+ /* Issue a READ.SR.COUNTERS */
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_SR_COUNTERS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE,
+ streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ }
+ streamer_priv->streamer_lan_status = lan_status;
+ } /* Lan.change.status */
+ else
+ printk(KERN_WARNING "%s: Unknown arb command \n", dev->name);
+}
+
+static void streamer_asb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ if (streamer_priv->asb_queued == 1)
+ {
+ /* Dropped through the first time */
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ streamer_priv->asb_queued = 2;
+
+ return;
+ }
+
+ if (streamer_priv->asb_queued == 2) {
+ __u8 rc;
+ writew(streamer_priv->asb + 2, streamer_mmio + LAPA);
+ rc = readw(streamer_mmio + LAPD) & 0xff;
+ switch (rc) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name);
+ break;
+ case 0x26:
+ printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name);
+ break;
+ case 0xFF:
+ /* Valid response, everything should be ok again */
+ break;
+ default:
+ printk(KERN_WARNING "%s: Invalid return code in asb\n", dev->name);
+ break;
+ }
+ }
+ streamer_priv->asb_queued = 0;
+}
+
+static int streamer_change_mtu(struct net_device *dev, int mtu)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u16 max_mtu;
+
+ if (streamer_priv->streamer_ring_speed == 4)
+ max_mtu = 4500;
+ else
+ max_mtu = 18000;
+
+ if (mtu > max_mtu)
+ return -EINVAL;
+ if (mtu < 100)
+ return -EINVAL;
+
+ dev->mtu = mtu;
+ streamer_priv->pkt_buf_sz = mtu + TR_HLEN;
+
+ return 0;
+}
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int streamer_proc_info(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ struct pci_dev *pci_device = NULL;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct net_device *dev;
+
+
+ size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n");
+
+ pos += size;
+ len += size;
+
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->base_addr == (pci_device->base_address[0] & (~3)))
+ { /* Yep, a Streamer device */
+ size = sprintf_info(buffer + len, dev);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ } /* if */
+ } /* for */
+ } /* While */
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+ return len;
+}
+
+static int sprintf_info(char *buffer, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_adapter_addr_table sat;
+ struct streamer_parameters_table spt;
+ int size = 0;
+ int i;
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 14; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & sat;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+ writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 68; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & spt;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+
+
+ size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4],
+ dev->dev_addr[5], sat.node_addr[0], sat.node_addr[1],
+ sat.node_addr[2], sat.node_addr[3], sat.node_addr[4],
+ sat.node_addr[5], sat.func_addr[0], sat.func_addr[1],
+ sat.func_addr[2], sat.func_addr[3]);
+
+ size += sprintf(buffer + size, "\n%6s: Token Ring Parameters Table:\n", dev->name);
+
+ size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address : Poll Address : AccPri : Auth Src : Att Code :\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x :\n",
+ dev->name, spt.phys_addr[0], spt.phys_addr[1],
+ spt.phys_addr[2], spt.phys_addr[3],
+ spt.up_node_addr[0], spt.up_node_addr[1],
+ spt.up_node_addr[2], spt.up_node_addr[3],
+ spt.up_node_addr[4], spt.up_node_addr[4],
+ spt.poll_addr[0], spt.poll_addr[1], spt.poll_addr[2],
+ spt.poll_addr[3], spt.poll_addr[4], spt.poll_addr[5],
+ ntohs(spt.acc_priority), ntohs(spt.auth_source_class),
+ ntohs(spt.att_code));
+
+ size += sprintf(buffer + size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x : %04x : %04x : %04x : \n",
+ dev->name, spt.source_addr[0], spt.source_addr[1],
+ spt.source_addr[2], spt.source_addr[3],
+ spt.source_addr[4], spt.source_addr[5],
+ ntohs(spt.beacon_type), ntohs(spt.major_vector),
+ ntohs(spt.lan_status), ntohs(spt.local_ring),
+ ntohs(spt.mon_error), ntohs(spt.frame_correl));
+
+ size += sprintf(buffer + size, "%6s: Beacon Details : Tx : Rx : NAUN Node Address : NAUN Node Phys : \n",
+ dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: : %02x : %02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x : \n",
+ dev->name, ntohs(spt.beacon_transmit),
+ ntohs(spt.beacon_receive), spt.beacon_naun[0],
+ spt.beacon_naun[1], spt.beacon_naun[2],
+ spt.beacon_naun[3], spt.beacon_naun[4],
+ spt.beacon_naun[5], spt.beacon_phys[0],
+ spt.beacon_phys[1], spt.beacon_phys[2],
+ spt.beacon_phys[3]);
+ return size;
+}
+#endif
+#endif
+
+#ifdef MODULE
+
+static struct net_device *dev_streamer[STREAMER_MAX_ADAPTERS];
+
+int init_module(void)
+{
+ int i;
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+
+ ent = create_proc_entry("net/streamer_tr", 0, 0);
+ ent->read_proc = &streamer_proc_info;
+#endif
+#endif
+ for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++)
+ {
+ dev_streamer[i] = NULL;
+ dev_streamer[i] = init_trdev(dev_streamer[i], 0);
+ if (dev_streamer[i] == NULL)
+ return -ENOMEM;
+
+ dev_streamer[i]->init = &streamer_probe;
+
+ if (register_trdev(dev_streamer[i]) != 0) {
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ if (i == 0)
+ {
+ printk(KERN_INFO "Streamer: No IBM LanStreamer PCI Token Ring cards found in system.\n");
+ return -EIO;
+ } else {
+ printk(KERN_INFO "Streamer: %d IBM LanStreamer PCI Token Ring card(s) found in system.\n", i);
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+ for (i = 0; i < STREAMER_MAX_ADAPTERS; i++)
+ if (dev_streamer[i]) {
+ unregister_trdev(dev_streamer[i]);
+ release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE);
+ kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private));
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ }
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("net/streamer_tr", NULL);
+#endif
+#endif
+}
+#endif /* MODULE */
diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h
new file mode 100644
index 000000000..7ba86dfe5
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.h
@@ -0,0 +1,319 @@
+/*
+ * lanstreamer.h -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ *
+ */
+
+#define BCTL 0x60
+#define BCTL_SOFTRESET (1<<15)
+
+#define GPR 0x4a
+#define GPR_AUTOSENSE (1<<2)
+#define GPR_16MBPS (1<<3)
+
+#define LISR 0x10
+#define LISR_SUM 0x12
+#define LISR_RUM 0x14
+
+#define LISR_LIE (1<<15)
+#define LISR_SLIM (1<<13)
+#define LISR_SLI (1<<12)
+#define LISR_BPEI (1<<9)
+#define LISR_BPE (1<<8)
+#define LISR_SRB_CMD (1<<5)
+#define LISR_ASB_REPLY (1<<4)
+#define LISR_ASB_FREE_REQ (1<<2)
+#define LISR_ARB_FREE (1<<1)
+#define LISR_TRB_FRAME (1<<0)
+
+#define SISR 0x16
+#define SISR_SUM 0x18
+#define SISR_RUM 0x1A
+#define SISR_MASK 0x54
+#define SISR_MASK_SUM 0x56
+#define SISR_MASK_RUM 0x58
+
+#define SISR_MI (1<<15)
+#define SISR_TIMER (1<<11)
+#define SISR_LAP_PAR_ERR (1<<10)
+#define SISR_LAP_ACC_ERR (1<<9)
+#define SISR_PAR_ERR (1<<8)
+#define SISR_ADAPTER_CHECK (1<<6)
+#define SISR_SRB_REPLY (1<<5)
+#define SISR_ASB_FREE (1<<4)
+#define SISR_ARB_CMD (1<<3)
+#define SISR_TRB_REPLY (1<<2)
+
+#define MISR_RUM 0x5A
+#define MISR_MASK 0x5C
+#define MISR_MASK_RUM 0x5E
+
+#define MISR_TX2_IDLE (1<<15)
+#define MISR_TX2_NO_STATUS (1<<14)
+#define MISR_TX2_HALT (1<<13)
+#define MISR_TX2_EOF (1<<12)
+#define MISR_TX1_IDLE (1<<11)
+#define MISR_TX1_NO_STATUS (1<<10)
+#define MISR_TX1_HALT (1<<9)
+#define MISR_TX1_EOF (1<<8)
+#define MISR_RX_NOBUF (1<<5)
+#define MISR_RX_EOB (1<<4)
+#define MISR_RX_NO_STATUS (1<<2)
+#define MISR_RX_HALT (1<<1)
+#define MISR_RX_EOF (1<<0)
+
+#define LAPA 0x62
+#define LAPE 0x64
+#define LAPD 0x66
+#define LAPDINC 0x68
+#define LAPWWO 0x6A
+#define LAPWWC 0x6C
+#define LAPCTL 0x6E
+
+#define TIMER 0x4E4
+
+#define BMCTL_SUM 0x50
+#define BMCTL_RUM 0x52
+#define BMCTL_TX1_DIS (1<<14)
+#define BMCTL_TX2_DIS (1<<10)
+#define BMCTL_RX_DIS (1<<6)
+
+#define RXLBDA 0x90
+#define RXBDA 0x94
+#define RXSTAT 0x98
+#define RXDBA 0x9C
+
+#define TX1LFDA 0xA0
+#define TX1FDA 0xA4
+#define TX1STAT 0xA8
+#define TX1DBA 0xAC
+#define TX2LFDA 0xB0
+#define TX2FDA 0xB4
+#define TX2STAT 0xB8
+#define TX2DBA 0xBC
+
+#define STREAMER_IO_SPACE 256
+
+#define SRB_COMMAND_SIZE 50
+
+#define STREAMER_MAX_ADAPTERS 8 /* 0x08 __MODULE_STRING can't hand 0xnn */
+
+/* Defines for LAN STATUS CHANGE reports */
+#define LSC_SIG_LOSS 0x8000
+#define LSC_HARD_ERR 0x4000
+#define LSC_SOFT_ERR 0x2000
+#define LSC_TRAN_BCN 0x1000
+#define LSC_LWF 0x0800
+#define LSC_ARW 0x0400
+#define LSC_FPE 0x0200
+#define LSC_RR 0x0100
+#define LSC_CO 0x0080
+#define LSC_SS 0x0040
+#define LSC_RING_REC 0x0020
+#define LSC_SR_CO 0x0010
+#define LSC_FDX_MODE 0x0004
+
+/* Defines for OPEN ADAPTER command */
+
+#define OPEN_ADAPTER_EXT_WRAP (1<<15)
+#define OPEN_ADAPTER_DIS_HARDEE (1<<14)
+#define OPEN_ADAPTER_DIS_SOFTERR (1<<13)
+#define OPEN_ADAPTER_PASS_ADC_MAC (1<<12)
+#define OPEN_ADAPTER_PASS_ATT_MAC (1<<11)
+#define OPEN_ADAPTER_ENABLE_EC (1<<10)
+#define OPEN_ADAPTER_CONTENDER (1<<8)
+#define OPEN_ADAPTER_PASS_BEACON (1<<7)
+#define OPEN_ADAPTER_ENABLE_FDX (1<<6)
+#define OPEN_ADAPTER_ENABLE_RPL (1<<5)
+#define OPEN_ADAPTER_INHIBIT_ETR (1<<4)
+#define OPEN_ADAPTER_INTERNAL_WRAP (1<<3)
+
+
+/* Defines for SRB Commands */
+#define SRB_CLOSE_ADAPTER 0x04
+#define SRB_CONFIGURE_BRIDGE 0x0c
+#define SRB_CONFIGURE_HP_CHANNEL 0x13
+#define SRB_MODIFY_BRIDGE_PARMS 0x15
+#define SRB_MODIFY_OPEN_OPTIONS 0x01
+#define SRB_MODIFY_RECEIVE_OPTIONS 0x17
+#define SRB_NO_OPERATION 0x00
+#define SRB_OPEN_ADAPTER 0x03
+#define SRB_READ_LOG 0x08
+#define SRB_READ_SR_COUNTERS 0x16
+#define SRB_RESET_GROUP_ADDRESS 0x02
+#define SRB_RESET_TARGET_SEGMETN 0x14
+#define SRB_SAVE_CONFIGURATION 0x1b
+#define SRB_SET_BRIDGE_PARMS 0x09
+#define SRB_SET_FUNC_ADDRESS 0x07
+#define SRB_SET_GROUP_ADDRESS 0x06
+#define SRB_SET_TARGET_SEGMENT 0x05
+
+/* Clear return code */
+#define STREAMER_CLEAR_RET_CODE 0xfe
+
+/* ARB Commands */
+#define ARB_RECEIVE_DATA 0x81
+#define ARB_LAN_CHANGE_STATUS 0x84
+
+/* ASB Response commands */
+#define ASB_RECEIVE_DATA 0x81
+
+
+/* Streamer defaults for buffers */
+
+#define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */
+#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */
+
+#define PKT_BUF_SZ 4096 /* Default packet size */
+
+/* Streamer data structures */
+
+struct streamer_tx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 bufcnt_framelen;
+ __u32 buffer;
+ __u32 buflen;
+ __u32 rsvd1;
+ __u32 rsvd2;
+ __u32 rsvd3;
+};
+
+struct streamer_rx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 buffer;
+ __u32 framelen_buflen;
+};
+
+struct mac_receive_buffer {
+ __u16 next;
+ __u8 padding;
+ __u8 frame_status;
+ __u16 buffer_length;
+ __u8 frame_data;
+};
+
+struct streamer_private {
+
+ __u16 srb;
+ __u16 trb;
+ __u16 arb;
+ __u16 asb;
+
+ __u8 *streamer_mmio;
+
+ volatile int srb_queued; /* True if an SRB is still posted */
+ wait_queue_head_t srb_wait;
+
+ volatile int asb_queued; /* True if an ASB is posted */
+
+ volatile int trb_queued; /* True if a TRB is posted */
+ wait_queue_head_t trb_wait;
+
+ struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE];
+ struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE];
+ struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE],
+ *rx_ring_skb[STREAMER_RX_RING_SIZE];
+ int tx_ring_free, tx_ring_last_status, rx_ring_last_received,
+ free_tx_ring_entries;
+
+ struct net_device_stats streamer_stats;
+ __u16 streamer_lan_status;
+ __u8 streamer_ring_speed;
+ __u16 pkt_buf_sz;
+ __u8 streamer_receive_options, streamer_copy_all_options,
+ streamer_message_level;
+ __u8 streamer_multicast_set;
+ __u16 streamer_addr_table_addr, streamer_parms_addr;
+ __u16 mac_rx_buffer;
+ __u8 streamer_laa[6];
+};
+
+struct streamer_adapter_addr_table {
+
+ __u8 node_addr[6];
+ __u8 reserved[4];
+ __u8 func_addr[4];
+};
+
+struct streamer_parameters_table {
+
+ __u8 phys_addr[4];
+ __u8 up_node_addr[6];
+ __u8 up_phys_addr[4];
+ __u8 poll_addr[6];
+ __u16 reserved;
+ __u16 acc_priority;
+ __u16 auth_source_class;
+ __u16 att_code;
+ __u8 source_addr[6];
+ __u16 beacon_type;
+ __u16 major_vector;
+ __u16 lan_status;
+ __u16 soft_error_time;
+ __u16 reserved1;
+ __u16 local_ring;
+ __u16 mon_error;
+ __u16 beacon_transmit;
+ __u16 beacon_receive;
+ __u16 frame_correl;
+ __u8 beacon_naun[6];
+ __u32 reserved2;
+ __u8 beacon_phys[4];
+};
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index bbca052e7..19615012f 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1105,7 +1105,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in
index bc4bd5331..e0a6f5e8f 100644
--- a/drivers/net/wan/Config.in
+++ b/drivers/net/wan/Config.in
@@ -16,6 +16,25 @@ if [ "$CONFIG_WAN" = "y" ]; then
dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
+ #
+ # COMX drivers
+ #
+
+ tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+ if [ "$CONFIG_COMX" != "n" ]; then
+ dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+ dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+ dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+ dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+ if [ "$CONFIG_LAPB" = "y" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+ fi
+ if [ "$CONFIG_LAPB" = "m" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+ fi
+ dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+ fi
+
# There is no way to detect a Sealevel board. Force it modular
dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 67f6bb59b..b2cd8aafb 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -49,6 +49,58 @@ else
endif
endif
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else
+ ifeq ($(CONFIG_COMX_HW_COMX),m)
+ M_OBJS += comx-hw-comx.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else
+ ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+ M_OBJS += comx-hw-locomx.o
+ CONFIG_85230_MODULE=y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else
+ ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+ M_OBJS += comx-hw-mixcom.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else
+ ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+ M_OBJS += comx-proto-ppp.o
+ CONFIG_SYNCPPP_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+ M_OBJS += comx-proto-lapb.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_FR),m)
+ M_OBJS += comx-proto-fr.o
+ endif
+endif
+
ifeq ($(CONFIG_COSA),y)
L_OBJS += cosa.o
CONFIG_SYNCPPP_BUILTIN = y
@@ -180,8 +232,8 @@ clean:
rm -f core *.o *.a *.s
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $(WANPIPE_OBJS)
+ $(LD) -r -o $@ $(WANPIPE_OBJS)
cyclomx.o: $(CYCLOMX_OBJS)
- ld -r -o $@ $(CYCLOMX_OBJS)
+ $(LD) -r -o $@ $(CYCLOMX_OBJS)
diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c
new file mode 100644
index 000000000..7381dc8a9
--- /dev/null
+++ b/drivers/net/wan/comx-hw-comx.c
@@ -0,0 +1,1426 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - port back to kernel, add support builtin driver
+ * - cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ * - cleaned up the board load functions, no more long reset
+ * timeouts
+ * - lower modem lines on close
+ * - some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ * - fix multiple board support
+ *
+ * Version 0.83 (99/11/30):
+ * - interrupt handling and locking fixes during initalization
+ * - really fix multiple board support
+ *
+ * Version 0.84 (99/12/02):
+ * - some workarounds for problematic hardware/firmware
+ *
+ * Version 0.85 (00/01/14):
+ * - some additional workarounds :/
+ * - printk cleanups
+ */
+
+#define VERSION "0.85"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
+ (unsigned int)(((struct comx_privdata *)\
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
+ + (unsigned int)(((struct comx_privdata *) \
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+ int len;
+ unsigned char *data;
+};
+
+struct comx_privdata {
+ struct comx_firmware *firmware;
+ u16 clock;
+ char channel; // channel no.
+ int memory_size;
+ short io_extent;
+ u_long histogram[5];
+};
+
+static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct net_device *COMX_access_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct net_device *ret;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ ret = memory_used[mempos];
+
+ if(ret == dev) {
+ goto out;
+ }
+
+ memory_used[mempos] = dev;
+
+ if (!ch->twin || ret != ch->twin) {
+ if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
+ ch->HW_board_on(dev);
+ }
+out:
+ restore_flags(flags);
+ return ret;
+}
+
+static void COMX_release_board(struct net_device *dev, struct net_device *savep)
+{
+ unsigned long flags;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct comx_channel *ch = dev->priv;
+
+ save_flags(flags); cli();
+
+ if (memory_used[mempos] == savep) {
+ goto out;
+ }
+
+ memory_used[mempos] = savep;
+ if (!ch->twin || ch->twin != savep) {
+ ch->HW_board_off(dev);
+ if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
+ }
+out:
+ restore_flags(flags);
+}
+
+static int COMX_txe(struct net_device *dev)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ int rc = 0;
+
+ savep = ch->HW_access_board(dev);
+ if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+ rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+ }
+ ch->HW_release_board(dev,savep);
+ if(rc==0xffff) {
+ printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
+ }
+ return rc;
+}
+
+static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int ret = FRAME_DROPPED;
+ word tmp;
+
+ savep = ch->HW_access_board(dev);
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+ }
+
+ if (skb->len > COMX_MAX_TX_SIZE) {
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if ((ch->line_status & LINE_UP) && tmp==1) {
+ int lensave = skb->len;
+ int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+ word *data = (word *)skb->data;
+
+ if(dest==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ writew((unsigned short)skb->len, dev->mem_start + dest);
+ dest += 2;
+ while (skb->len > 1) {
+ writew(*data++, dev->mem_start + dest);
+ dest += 2; skb->len -= 2;
+ }
+ if (skb->len == 1) {
+ writew(*((byte *)data), dev->mem_start + dest);
+ }
+ writew(0, dev->mem_start + (int)hw->channel *
+ COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += lensave;
+ ret = FRAME_ACCEPTED;
+ } else {
+ ch->stats.tx_dropped++;
+ printk(KERN_INFO "%s: frame dropped\n",dev->name);
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
+ }
+ }
+
+out:
+ ch->HW_release_board(dev, savep);
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static inline int comx_read_buffer(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ word rbuf_offs;
+ struct sk_buff *skb;
+ word len;
+ int i=0;
+ word *writeptr;
+
+ i = 0;
+ rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+ if(rbuf_offs == 0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
+ return 0;
+ }
+ len = readw(dev->mem_start + rbuf_offs);
+ if(len > COMX_MAX_RX_SIZE) {
+ printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
+ return 0;
+ }
+ if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+ ch->stats.rx_dropped++;
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ return 0;
+ }
+ rbuf_offs += 2;
+ skb_reserve(skb, 16);
+ skb_put(skb, len);
+ skb->dev = dev;
+ writeptr = (word *)skb->data;
+ while (i < len) {
+ *writeptr++ = readw(dev->mem_start + rbuf_offs);
+ rbuf_offs += 2;
+ i += 2;
+ }
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += len;
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+ }
+ ch->LINE_rx(dev, skb);
+ return 1;
+}
+
+static inline char comx_line_change(struct net_device *dev, char linestat)
+{
+ struct comx_channel *ch=dev->priv;
+ char idle=1;
+
+
+ if (linestat & LINE_UP) { /* Vonal fol */
+ if (ch->lineup_delay) {
+ if (!test_and_set_bit(0, &ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies +
+ HZ*ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ idle=0;
+ }
+ } else {
+ idle=0;
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+ } else { /* Vonal le */
+ idle=0;
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+ }
+ }
+ return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *interrupted;
+ unsigned long jiffs;
+ char idle = 0;
+ int count = 0;
+ word tmp;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+ return;
+ }
+
+ jiffs = jiffies;
+
+ interrupted = ch->HW_access_board(dev);
+
+ while (!idle && count < 5000) {
+ char channel = 0;
+ idle = 1;
+
+ while (channel < 2) {
+ char linestat = 0;
+ char buffers_emptied = 0;
+
+ if (channel == 1) {
+ if (ch->twin) {
+ dev = ch->twin;
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+ } else {
+ break;
+ }
+ } else {
+ COMX_WRITE(dev, OFF_A_L1_REPENA,
+ COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+ }
+ channel++;
+
+ if ((ch->init_status & (HW_OPEN | LINE_OPEN)) !=
+ (HW_OPEN | LINE_OPEN)) {
+ continue;
+ }
+
+ /* Collect stats */
+ tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+ COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_over_errors += tmp & 0xff;
+ }
+ tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+ COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_missed_errors += tmp & 0xff;
+ }
+
+ if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ while (tmp==1) {
+ idle=0;
+ buffers_emptied+=comx_read_buffer(dev);
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ }
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
+ break;
+ }
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if (tmp==1 && ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
+ break;
+ }
+
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ linestat &= ~LINE_UP;
+ } else {
+ linestat |= LINE_UP;
+ }
+
+ if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+ ch->stats.tx_carrier_errors++;
+ idle &= comx_line_change(dev,linestat);
+ }
+
+ hw->histogram[(int)buffers_emptied]++;
+ }
+ count++;
+ }
+
+ if(count==5000) {
+ printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+ }
+
+ ch->HW_release_board(dev, interrupted);
+}
+
+static int COMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long jiffs;
+ int twin_open=0;
+ int retval;
+ struct net_device *savep;
+
+ if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+ return -ENODEV;
+ }
+
+ if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
+ twin_open=1;
+ }
+
+ if (!twin_open) {
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+ if (request_irq(dev->irq, COMX_interrupt, 0, dev->name,
+ (void *)dev)) {
+ printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status |= IRQ_ALLOCATED;
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+ if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+ ch->init_status &= ~IRQ_ALLOCATED;
+ retval=-ENODEV;
+ goto error;
+ }
+ }
+
+ savep = ch->HW_access_board(dev);
+ COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+ if (ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+
+ COMX_CMD(dev, COMX_CMD_INIT);
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+ udelay(1000);
+
+ COMX_CMD(dev, COMX_CMD_OPEN);
+
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+
+ ch->init_status |= HW_OPEN;
+
+ /* Ez eleg ciki, de ilyen a rendszer */
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ ch->line_status &= ~LINE_UP;
+ } else {
+ ch->line_status |= LINE_UP;
+ }
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+
+ ch->HW_release_board(dev, savep);
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0444;
+
+ }
+ }
+
+ return 0;
+
+error:
+ if(!twin_open) {
+ release_region(dev->base_addr, hw->io_extent);
+ free_irq(dev->irq, (void *)dev);
+ }
+ return retval;
+
+}
+
+static int COMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_channel *twin_ch;
+ struct net_device *savep;
+
+ savep = ch->HW_access_board(dev);
+
+ COMX_CMD(dev, COMX_CMD_CLOSE);
+ udelay(1000);
+ COMX_CMD(dev, COMX_CMD_EXIT);
+
+ ch->HW_release_board(dev, savep);
+
+ if (ch->init_status & IRQ_ALLOCATED) {
+ free_irq(dev->irq, (void *)dev);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+ release_region(dev->base_addr, hw->io_extent);
+
+ if (ch->twin && (twin_ch = ch->twin->priv) &&
+ (twin_ch->init_status & HW_OPEN)) {
+ /* Pass the irq to the twin */
+ if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name,
+ (void *)ch->twin) == 0) {
+ twin_ch->init_status |= IRQ_ALLOCATED;
+ }
+ }
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int COMX_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *savep;
+ int len = 0;
+
+ savep = ch->HW_access_board(dev);
+
+ len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+ "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+ "TxEMPTY: %02x, TxBUFP: %02x\n",
+ (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+ (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+ (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+ (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+ COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+ (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+ COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+ COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+ len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+ "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+ hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+ ch->HW_release_board(dev, savep);
+
+ return len;
+}
+
+static int COMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+ unsigned char id1, id2;
+ struct net_device *saved;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n",
+ dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+ /* 10 usec should be enough here */
+ udelay(100);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount=0;
+ while(loopcount++ < 10000 &&
+ readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ udelay(100);
+ }
+
+ if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+ dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+ retval=-ENODEV;
+ goto out;
+ }
+
+ writeb(0x55, dev->mem_start + 0x18ff);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
+ udelay(100);
+ }
+
+ if(readb(dev->mem_start + 0x18ff) != 0) {
+ printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+ dev->name);
+ retval=-ENODEV;
+ goto out;
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount = 0;
+ while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int CMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ #if 0
+ unsigned char id1, id2;
+ #endif
+ struct net_device *saved;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+ if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+ printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+ inb_p(dev->base_addr));
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET,
+ dev->base_addr);
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ loopcount=0;
+ while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int HICOMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 12;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct net_device *saved;
+ unsigned char id1, id2;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ word *HICOMX_address;
+ char id = 1;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ while (id != 4) {
+ if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+ break;
+ }
+ }
+
+ if (id != 4) {
+ printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+ dev->name, (unsigned int)dev->base_addr, id - 1,
+ inb_p(dev->base_addr + id - 1));
+ return -1;
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ udelay(10);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+ outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], HICOMX_address++);
+ }
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ udelay(100);
+ }
+
+ if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static struct net_device *comx_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct comx_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+
+ if(!S_ISDIR(procfile->mode)) {
+ continue;
+ }
+
+ twin=procfile->data;
+ ch_twin=twin->priv;
+ hw_twin=ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+ dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+ dev->mem_start == twin->mem_start &&
+ hw->channel == (1 - hw_twin->channel) &&
+ ch->hardware == ch_twin->hardware) {
+ return twin;
+ }
+ }
+ return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ char *page;
+
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if(ch->init_status & HW_OPEN) {
+ return -EAGAIN;
+ }
+
+ if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+ } else {
+ byte *tmp;
+
+ if (!hw->firmware) {
+ if ((hw->firmware = kmalloc(sizeof(struct comx_firmware),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ hw->firmware->len = 0;
+ hw->firmware->data = NULL;
+ }
+
+ if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+ if (hw->firmware && hw->firmware->len && file->f_pos
+ && hw->firmware->len < count + file->f_pos) {
+ memcpy(tmp, hw->firmware->data, hw->firmware->len);
+ }
+ if (hw->firmware->data) {
+ kfree(hw->firmware->data);
+ }
+ copy_from_user(tmp + file->f_pos, buffer, count);
+ hw->firmware->len = entry->size = file->f_pos + count;
+ hw->firmware->data = tmp;
+ file->f_pos += count;
+ return count;
+ }
+
+ if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ hw->channel = simple_strtoul(page, NULL, 0);
+ if (hw->channel >= MAX_CHANNELNO) {
+ printk(KERN_ERR "Invalid channel number\n");
+ hw->channel = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ dev->irq = simple_strtoul(page, NULL, 0);
+ if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+ if (dev->irq < 3 || dev->irq > 15) {
+ printk(KERN_ERR "comxhw: Invalid irq number\n");
+ dev->irq = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IO) == 0) {
+ dev->base_addr = simple_strtoul(page, NULL, 0);
+ if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300
+ || dev->base_addr > 0x3fc) {
+ printk(KERN_ERR "Invalid io value\n");
+ dev->base_addr = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+ dev->mem_start = simple_strtoul(page, NULL, 0);
+ if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+ dev->mem_start *= 16;
+ }
+ if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+ || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+ printk(KERN_ERR "Invalid memory page\n");
+ dev->mem_start = 0;
+ }
+ dev->mem_end = dev->mem_start + hw->memory_size;
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+ len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) {
+ len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+ } else {
+ len = sprintf(page, "external\n");
+ }
+ } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+ len = min(FILE_PAGESIZE, min(count,
+ hw->firmware ? (hw->firmware->len - off) : 0));
+ if (len < 0) {
+ len = 0;
+ }
+ *start = hw->firmware ? (hw->firmware->data + off) : NULL;
+ if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+ *eof = 1;
+ }
+ return len;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+ if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+ hw->memory_size = COMX_MEMORY_SIZE;
+ hw->io_extent = COMX_IO_EXTENT;
+ dev->base_addr = COMX_DEFAULT_IO;
+ dev->irq = COMX_DEFAULT_IRQ;
+ dev->mem_start = COMX_DEFAULT_MEMADDR;
+ dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+ } else if (ch->hardware == &hicomx_hw) {
+ hw->memory_size = HICOMX_MEMORY_SIZE;
+ hw->io_extent = HICOMX_IO_EXTENT;
+ dev->base_addr = HICOMX_DEFAULT_IO;
+ dev->irq = HICOMX_DEFAULT_IRQ;
+ dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+ dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 6;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 2; // Ezt tudjuk
+ new_file->nlink = 1;
+
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 9;
+ new_file->nlink = 1;
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 8;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = NULL;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if (ch->hardware == &comx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = COMX_load_board;
+ } else if (ch->hardware == &cmx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = CMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else if (ch->hardware == &hicomx_hw) {
+ ch->HW_board_on = HICOMX_board_on;
+ ch->HW_board_off = HICOMX_board_off;
+ ch->HW_load_board = HICOMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ ch->HW_access_board = COMX_access_board;
+ ch->HW_release_board = COMX_release_board;
+ ch->HW_txe = COMX_txe;
+ ch->HW_open = COMX_open;
+ ch->HW_close = COMX_close;
+ ch->HW_send_packet = COMX_send_packet;
+ ch->HW_statistics = COMX_statistics;
+
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ if (hw->firmware) {
+ if (hw->firmware->data) kfree(hw->firmware->data);
+ kfree(hw->firmware);
+ } if (ch->twin) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+ remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+ }
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int COMX_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+ return 0;
+}
+
+static struct comx_hardware comx_hw = {
+ "comx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware cmx_hw = {
+ "cmx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+ "hicomx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+int __init comx_hw_comx_init(void)
+{
+ comx_register_hardware(&comx_hw);
+ comx_register_hardware(&cmx_hw);
+ comx_register_hardware(&hicomx_hw);
+ memset(memory_used, 0, sizeof(memory_used));
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("comx");
+ comx_unregister_hardware("cmx");
+ comx_unregister_hardware("hicomx");
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c
new file mode 100644
index 000000000..010f02373
--- /dev/null
+++ b/drivers/net/wan/comx-hw-locomx.c
@@ -0,0 +1,496 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu>
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.10 (99/06/17):
+ * - rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ * - some printk's fixed
+ * - get rid of a memory leak (it was impossible though :))
+ *
+ * Version 0.12 (99/07/07):
+ * - check CTS for modem lines, not DCD (which is always high
+ * in case of this board)
+ * Version 0.13 (99/07/08):
+ * - Fix the transmitter status check
+ * - Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+ 11, TCRTxCP,
+ 14, DTRREQ,
+ 255
+};
+
+struct locomx_data {
+ int io_extent;
+ struct z8530_dev board;
+ struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+ struct net_device *dev=c->netdevice;
+ struct comx_channel *ch=dev->priv;
+
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "locomx_rx receiving");
+ }
+ ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+ printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+ return FRAME_DROPPED;
+ }
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+ }
+
+ if(!hw->board.chanA.tx_next_skb) {
+ return FRAME_QUEUED;
+ } else {
+ return FRAME_ACCEPTED;
+ }
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct locomx_data *hw=ch->HW_privdata;
+
+ if(!(ch->line_status & LINE_UP) &&
+ (hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status | LINE_UP);
+ }
+ if((ch->line_status & LINE_UP) &&
+ !(hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+ }
+ mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+ int ret;
+
+ if (!dev->base_addr || !dev->irq) {
+ return -ENODEV;
+ }
+
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+
+ hw->board.chanA.ctrlio=dev->base_addr + 5;
+ hw->board.chanA.dataio=dev->base_addr + 7;
+
+ hw->board.irq=dev->irq;
+ hw->board.chanA.netdevice=dev;
+ hw->board.chanA.dev=&hw->board;
+ hw->board.name=dev->name;
+ hw->board.chanA.txdma=TX_DMA;
+ hw->board.chanA.rxdma=RX_DMA;
+ hw->board.chanA.irqs=&z8530_nop;
+ hw->board.chanB.irqs=&z8530_nop;
+
+ if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT,
+ dev->name, &hw->board)) {
+ printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name,
+ dev->irq);
+ ret=-EAGAIN;
+ goto irq_fail;
+ }
+ if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+ printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n",
+ dev->name, TX_DMA);
+ ret=-EAGAIN;
+ goto dma1_fail;
+ }
+
+ if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+ printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n",
+ dev->name, RX_DMA);
+ ret=-EAGAIN;
+ goto dma2_fail;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if(z8530_init(&hw->board)!=0)
+ {
+ printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+ ret=-ENODEV;
+ goto z8530_fail;
+ }
+
+ hw->board.chanA.dcdcheck=CTS;
+
+ z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+ z8530_channel_load(&hw->board.chanA, z8530_locomx);
+ z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+ z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+ if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+ goto z8530_fail;
+ }
+
+ restore_flags(flags);
+
+
+ hw->board.active=1;
+ hw->board.chanA.rx_function=locomx_rx;
+
+ ch->init_status |= HW_OPEN;
+ if (hw->board.chanA.status & DCD) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ comx_status(dev, ch->line_status);
+
+ init_timer(&hw->status_timer);
+ hw->status_timer.function=locomx_status_timerfun;
+ hw->status_timer.data=(unsigned long)dev;
+ hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+ add_timer(&hw->status_timer);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+ return 0;
+
+z8530_fail:
+ restore_flags(flags);
+ free_dma(RX_DMA);
+dma2_fail:
+ free_dma(TX_DMA);
+dma1_fail:
+ free_irq(dev->irq, &hw->board);
+irq_fail:
+ release_region(dev->base_addr, hw->io_extent);
+ return ret;
+}
+
+static int LOCOMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+ hw->board.chanA.rx_function=z8530_null_rx;
+ netif_stop_queue(dev);
+ z8530_sync_dma_close(dev, &hw->board.chanA);
+
+ z8530_shutdown(&hw->board);
+
+ del_timer(&hw->status_timer);
+ free_dma(RX_DMA);
+ free_dma(TX_DMA);
+ free_irq(dev->irq,&hw->board);
+ release_region(dev->base_addr,8);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int LOCOMX_statistics(struct net_device *dev,char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, "Hello\n");
+
+ return len;
+}
+
+static int LOCOMX_dump(struct net_device *dev) {
+ printk(KERN_INFO "LOCOMX_dump called\n");
+ return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ int val;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 0x360 && val != 0x368 && val != 0x370 &&
+ val != 0x378) {
+ printk(KERN_ERR "LoCOMX: incorrect io address!\n");
+ } else {
+ dev->base_addr = val;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+ printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+ } else {
+ dev->irq = val;
+ }
+ } else {
+ printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n",
+ entry->name);
+ free_page((unsigned long)page);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+
+
+static int LOCOMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw;
+ struct proc_dir_entry *new_file;
+
+ /* Alloc data for private structure */
+ if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+ hw->io_extent = LOCOMX_IO_EXTENT;
+
+ /* Register /proc files */
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+/* No clock yet */
+/*
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+*/
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = LOCOMX_txe;
+ ch->HW_open = LOCOMX_open;
+ ch->HW_close = LOCOMX_close;
+ ch->HW_send_packet = LOCOMX_send_packet;
+ ch->HW_statistics = LOCOMX_statistics;
+ ch->HW_set_clock = NULL;
+
+ ch->current_stats = &hw->board.chanA.stats;
+ memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+ dev->base_addr = LOCOMX_DEFAULT_IO;
+ dev->irq = LOCOMX_DEFAULT_IRQ;
+
+
+ /* O.K. Count one more user on this module */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int LOCOMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_send_packet = NULL;
+ ch->HW_statistics = NULL;
+ ch->HW_set_clock = NULL;
+ memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+ ch->current_stats = &ch->stats;
+
+ kfree(ch->HW_privdata);
+
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+// remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+ "locomx",
+ VERSION,
+ LOCOMX_init,
+ LOCOMX_exit,
+ LOCOMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+int __init comx_hw_locomx_init(void)
+{
+ comx_register_hardware(&locomx_hw);
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("locomx");
+ return;
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c
new file mode 100644
index 000000000..552443f88
--- /dev/null
+++ b/drivers/net/wan/comx-hw-mixcom.c
@@ -0,0 +1,948 @@
+/*
+ * Hardware driver for the MixCom synchronous serial board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.60 (99/06/11):
+ * - ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ * - recognize the one-channel MixCOM card (id byte = 0x13)
+ * - printk fixes
+ *
+ * Version 0.62 (99/07/15):
+ * - fixes according to the new hw docs
+ * - report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ * - line status report fixes
+ *
+ * Version 0.64 (99/12/01):
+ * - some more cosmetical fixes
+ */
+
+#define VERSION "0.64"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+ HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+ (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+ (1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+ u16 clock;
+ char channel;
+ char txbusy;
+ struct sk_buff *sending;
+ unsigned tx_ptr;
+ struct sk_buff *recving;
+ unsigned rx_ptr;
+ unsigned char status;
+ char card_has_status;
+};
+
+static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct net_device *dev, int cmd)
+{
+ unsigned long jiffs = jiffies;
+ unsigned char cec;
+ unsigned delay = 0;
+
+ while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) &&
+ (jiffs + HZ > jiffies)) {
+ udelay(1);
+ if (++delay > (100000 / HZ)) break;
+ }
+ if (cec) {
+ printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+ } else {
+ wr_hscx(dev, HSCX_CMDR, cmd);
+ }
+}
+
+static inline void hscx_fill_fifo(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+ outsb(dev->base_addr + HSCX_FIFO,
+ &(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+ if (to_send <= 32) {
+ hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ } else {
+ hscx_cmd(dev, HSCX_XTF);
+ hw->tx_ptr += 32;
+ }
+}
+
+static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->recving == NULL) {
+ if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+ ch->stats.rx_dropped++;
+ hscx_cmd(dev, HSCX_RHR);
+ } else {
+ skb_reserve(hw->recving, 16);
+ skb_put(hw->recving, HSCX_MTU);
+ }
+ hw->rx_ptr = 0;
+ }
+ if (cnt > 32 || !cnt || hw->recving == NULL) {
+ printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+ cnt, (void *)hw->recving);
+ return;
+ }
+
+ insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+ hw->rx_ptr += cnt;
+ hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct net_device *dev)
+{
+ unsigned long flags;
+ int id, vstr, ret=0;
+
+ save_flags(flags); cli();
+
+ id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
+
+ if (id != MIXCOM_ID ) {
+ ret=-ENODEV;
+ printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+ goto out;
+ }
+
+ vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+ if(vstr>=sizeof(hscx_versions)/sizeof(char*) ||
+ hscx_versions[vstr]==NULL) {
+ printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+ ret = -ENODEV;
+ } else {
+ printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+ ret = 0;
+ }
+
+out:
+
+ restore_flags(flags);
+ return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->clock) {
+ ;
+ } else {
+ ;
+ }
+}
+#endif
+
+static void mixcom_board_on(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+ outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON,
+ MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_board_off(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_off(struct net_device *dev)
+{
+ wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+ wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+ wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+ wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+ wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+ hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+ if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ unsigned long flags;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if (skb->len > HSCX_MTU) {
+ ch->stats.tx_errors++;
+ return FRAME_ERROR;
+ }
+
+ save_flags(flags); cli();
+
+ if (test_and_set_bit(0, &hw->txbusy)) {
+ printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+ restore_flags(flags);
+ return FRAME_DROPPED;
+ }
+
+
+ hw->sending = skb;
+ hw->tx_ptr = 0;
+ hw->txbusy = 1;
+// atomic_inc(&skb->users); // save it
+ hscx_fill_fifo(dev);
+ restore_flags(flags);
+
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte rsta;
+ register word length;
+
+ rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO |
+ HSCX_CRC | HSCX_RAB);
+ length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) |
+ rd_hscx(dev, HSCX_RBCL);
+
+ if ( length > hw->rx_ptr ) {
+ hscx_empty_fifo(dev, length - hw->rx_ptr);
+ }
+
+ if (!(rsta & HSCX_VFR)) {
+ ch->stats.rx_length_errors++;
+ }
+ if (rsta & HSCX_RDO) {
+ ch->stats.rx_over_errors++;
+ }
+ if (!(rsta & HSCX_CRC)) {
+ ch->stats.rx_crc_errors++;
+ }
+ if (rsta & HSCX_RAB) {
+ ch->stats.rx_frame_errors++;
+ }
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += length;
+
+ if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+ skb_trim(hw->recving, hw->rx_ptr - 1);
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, hw->recving,
+ "MIXCOM_interrupt receiving");
+ }
+ hw->recving->dev = dev;
+ if (ch->LINE_rx) {
+ ch->LINE_rx(dev, hw->recving);
+ }
+ }
+ else if(hw->recving) {
+ kfree_skb(hw->recving);
+ }
+ hw->recving = NULL;
+ hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte exir;
+
+ exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+ if (exir & HSCX_RFO) {
+ ch->stats.rx_over_errors++;
+ if (hw->rx_ptr) {
+ kfree_skb(hw->recving);
+ hw->recving = NULL; hw->rx_ptr = 0;
+ }
+ printk(KERN_ERR "MIXCOM: rx overrun\n");
+ hscx_cmd(dev, HSCX_RHR);
+ }
+
+ if (exir & HSCX_XDU) { // xmit underrun
+ ch->stats.tx_errors++;
+ ch->stats.tx_aborted_errors++;
+ if (hw->tx_ptr) {
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ }
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ printk(KERN_ERR "MIXCOM: tx underrun\n");
+ }
+
+ if (exir & HSCX_CSC) {
+ ch->stats.tx_carrier_errors++;
+ if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else if (ch->line_status & LINE_UP) {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev,ch->line_status);
+ }
+ }
+ }
+ if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) &
+ HSCX_CTS)) { // Vonal fol
+ if (!test_and_set_bit(0,&ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies + HZ *
+ ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (hw->sending) {
+ kfree_skb(hw->sending);
+ }
+ hw->sending=NULL;
+ hw->tx_ptr = 0;
+ }
+ }
+ }
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct comx_channel *ch, *twin_ch;
+ struct mixcom_privdata *hw, *twin_hw;
+ register unsigned char ista;
+
+ if (dev==NULL) {
+ printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+ return;
+ }
+
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+
+ save_flags(flags); cli();
+
+ while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF |
+ HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+ register byte ista2 = 0;
+
+ if (ista & HSCX_RME) {
+ mixcom_receive_frame(dev);
+ }
+ if (ista & HSCX_RPF) {
+ hscx_empty_fifo(dev, 32);
+ }
+ if (ista & HSCX_XPR) {
+ if (hw->tx_ptr) {
+ hscx_fill_fifo(dev);
+ } else {
+ clear_bit(0, &hw->txbusy);
+ ch->LINE_tx(dev);
+ }
+ }
+
+ if (ista & HSCX_EXB) {
+ mixcom_extended_interrupt(dev);
+ }
+
+ if ((ista & HSCX_EXA) && ch->twin) {
+ mixcom_extended_interrupt(ch->twin);
+ }
+
+ if ((ista & HSCX_ICA) && ch->twin &&
+ (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+ (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+ if (ista2 & HSCX_RME) {
+ mixcom_receive_frame(ch->twin);
+ }
+ if (ista2 & HSCX_RPF) {
+ hscx_empty_fifo(ch->twin, 32);
+ }
+ if (ista2 & HSCX_XPR) {
+ twin_ch=ch->twin->priv;
+ twin_hw=twin_ch->HW_privdata;
+ if (twin_hw->tx_ptr) {
+ hscx_fill_fifo(ch->twin);
+ } else {
+ clear_bit(0, &twin_hw->txbusy);
+ ch->LINE_tx(ch->twin);
+ }
+ }
+ }
+ }
+
+ restore_flags(flags);
+ return;
+}
+
+static int MIXCOM_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+ if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+ if(hw->channel==1) {
+ if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status &
+ IRQ_ALLOCATED)) {
+ printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+ return -EAGAIN;
+ }
+ }
+
+
+ /* Is our hw present at all ? Not checking for channel 0 if it is already
+ open */
+ if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+ if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+ return -EAGAIN;
+ }
+ if (mixcom_probe(dev)) {
+ return -ENODEV;
+ }
+ }
+
+ save_flags(flags); cli();
+
+ if(hw->channel==1) {
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ }
+
+ if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+ if (request_irq(dev->irq, MIXCOM_interrupt, 0,
+ dev->name, (void *)dev)) {
+ printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status|=IRQ_ALLOCATED;
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ mixcom_board_on(dev);
+ }
+
+ mixcom_on(dev);
+
+
+ hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+ if(hw->status != 0xff) {
+ printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+ hw->card_has_status=1;
+ }
+
+ hw->txbusy = 0;
+ ch->init_status |= HW_OPEN;
+
+ if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ restore_flags(flags);
+
+ ch->LINE_status(dev, ch->line_status);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int MIXCOM_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ mixcom_off(dev);
+
+ /* This is channel 0, twin is not open, we can safely turn off everything */
+ if(hw->channel==0 && (!(TWIN(dev)) ||
+ !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+ mixcom_board_off(dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+
+ /* This is channel 1, channel 0 has already been shutdown, we can release
+ this one too */
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+ mixcom_board_off(TWIN(dev));
+ free_irq(TWIN(dev)->irq, TWIN(dev));
+ release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+ COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+ }
+ }
+
+ /* the ioports for channel 1 can be safely released */
+ if(hw->channel==1) {
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ }
+
+ restore_flags(flags);
+
+ /* If we don't hold any hardware open */
+ if(!(ch->init_status & IRQ_ALLOCATED)) {
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ /* channel 0 was only waiting for us to close channel 1
+ close it completely */
+
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir;
+ procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int MIXCOM_statistics(struct net_device *dev,char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ // struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if(ch->init_status && IRQ_ALLOCATED) {
+ len += sprintf(page + len, "Mixcom board: hardware open\n");
+ }
+
+ return len;
+}
+
+static int MIXCOM_dump(struct net_device *dev) {
+ return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n",
+ (unsigned int)MIXCOM_BOARD_BASE(dev));
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+ else len = sprintf(page, "external\n");
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ if (ch->twin) {
+ len = sprintf(page, "%s\n",ch->twin->name);
+ } else {
+ len = sprintf(page, "none\n");
+ }
+ } else {
+ printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+
+static struct net_device *mixcom_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct mixcom_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if(!S_ISDIR(procfile->mode)) continue;
+
+ twin = procfile->data;
+ ch_twin = twin->priv;
+ hw_twin = ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr &&
+ dev->irq == twin->irq &&
+ ch->hardware == ch_twin->hardware &&
+ dev->base_addr == twin->base_addr +
+ (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+ hw->channel == (1 - hw_twin->channel)) {
+ if (!TWIN(twin) || TWIN(twin)==dev) {
+ return twin;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static void setup_twin(struct net_device* dev)
+{
+
+ if(TWIN(dev) && TWIN(TWIN(dev))) {
+ TWIN(TWIN(dev))=NULL;
+ }
+ if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+ if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+ TWIN(dev)=NULL;
+ } else {
+ TWIN(TWIN(dev))=dev;
+ }
+ }
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ char *page;
+ int value;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value != 0x180 && value != 0x280 && value != 0x380) {
+ printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+ } else {
+ dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+ printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+ } else {
+ dev->irq = value;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ if (!kbps) {
+ hw->clock = 0;
+ } else {
+ hw->clock = kbps;
+ }
+ if (hw->clock < 32 || hw->clock > 2000) {
+ hw->clock = 0;
+ printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+ }
+ }
+ if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+ } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value > 2) {
+ printk(KERN_ERR "Invalid channel number\n");
+ } else {
+ dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+ hw->channel = value;
+ }
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ setup_twin(dev);
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int MIXCOM_init(struct net_device *dev) {
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+#if 0
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+#endif
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ setup_twin(dev);
+
+ /* Fill in ch_struct hw specific pointers */
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = MIXCOM_txe;
+ ch->HW_open = MIXCOM_open;
+ ch->HW_close = MIXCOM_close;
+ ch->HW_send_packet = MIXCOM_send_packet;
+ ch->HW_statistics = MIXCOM_statistics;
+ ch->HW_set_clock = NULL;
+
+ dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+ dev->irq = MIXCOM_DEFAULT_IRQ;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int MIXCOM_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if(hw->channel==0 && TWIN(dev)) {
+ return -EBUSY;
+ }
+
+ if(hw->channel==1 && TWIN(dev)) {
+ TWIN(TWIN(dev))=NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+ "mixcom",
+ VERSION,
+ MIXCOM_init,
+ MIXCOM_exit,
+ MIXCOM_dump,
+ NULL
+};
+
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+int __init comx_hw_mixcom_init(void)
+{
+ return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ comx_unregister_hardware("mixcom");
+}
+#endif
diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c
new file mode 100644
index 000000000..ad62e310c
--- /dev/null
+++ b/drivers/net/wan/comx-proto-fr.c
@@ -0,0 +1,1006 @@
+/*
+ * Frame-relay protocol module for the COMX driver
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.70 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as builtin code
+ *
+ * Version 0.71 (99/06/25):
+ * - use skb priorities and queues for sending keepalive
+ * - use device queues for slave->master data transmit
+ * - set IFF_RUNNING only line protocol up
+ * - fixes on slave device flags
+ *
+ * Version 0.72 (99/07/09):
+ * - handle slave tbusy with master tbusy (should be fixed)
+ * - fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+ "for Linux kernel 2.2.X");
+
+#define FRAD_UI 0x03
+#define NLPID_IP 0xcc
+#define NLPID_Q933_LMI 0x08
+#define NLPID_CISCO_LMI 0x09
+#define Q933_ENQ 0x75
+#define Q933_LINESTAT 0x51
+#define Q933_COUNTERS 0x53
+
+#define MAXALIVECNT 3 /* No. of failures */
+
+struct fr_data {
+ u16 dlci;
+ struct net_device *master;
+ char keepa_pend;
+ char keepa_freq;
+ char keepalivecnt, keeploopcnt;
+ struct timer_list keepa_timer;
+ u8 local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct sk_buff *skb;
+ u8 *fr_packet;
+
+ skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+
+ if(skb==NULL)
+ return;
+
+ skb_reserve(skb, dev->hard_header_len);
+
+ fr_packet=(u8*)skb_put(skb, 13);
+
+ fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+ fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ fr_packet[2] = FRAD_UI;
+ fr_packet[3] = NLPID_Q933_LMI;
+ fr_packet[4] = 0;
+ fr_packet[5] = Q933_ENQ;
+ fr_packet[6] = Q933_LINESTAT;
+ fr_packet[7] = 0x01;
+ fr_packet[8] = 0x01;
+ fr_packet[9] = Q933_COUNTERS;
+ fr_packet[10] = 0x02;
+ fr_packet[11] = ++fr->local_cnt;
+ fr_packet[12] = fr->remote_cnt;
+
+ skb->dev = dev;
+ skb->priority = TC_PRIO_CONTROL;
+ dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (ch->init_status & LINE_OPEN) {
+ if (fr->keepalivecnt == MAXALIVECNT) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ dev->flags &= ~IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags &= ~IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status & ~PROTO_UP);
+ }
+ }
+ }
+ if (fr->keepalivecnt <= MAXALIVECNT) {
+ ++fr->keepalivecnt;
+ }
+ fr_keepalive_send(dev);
+ }
+ mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb,
+ u16 dlci, u8 nlpid)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+ return;
+ }
+
+ fr->remote_cnt = skb->data[7];
+ if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+ fr->keepalivecnt = 0;
+ if ((ch->line_status & LINE_UP) &&
+ !(ch->line_status & PROTO_UP)) {
+ comx_status(dev, ch->line_status |= PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags |= IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status | PROTO_UP);
+ }
+ }
+ }
+ }
+}
+
+static void fr_set_keepalive(struct net_device *dev, int keepa)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!keepa && fr->keepa_freq) { // switch off
+ fr->keepa_freq = 0;
+ if (ch->line_status & LINE_UP) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ del_timer(&fr->keepa_timer);
+ }
+ return;
+ }
+
+ if (keepa) { // bekapcs
+ if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+ del_timer(&fr->keepa_timer);
+ }
+ fr->keepa_freq = keepa;
+ fr->local_cnt = fr->remote_cnt = 0;
+ fr->keepa_timer.expires = jiffies + HZ;
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+ comx_status(dev, ch->line_status);
+ if(ch->line_status & LINE_UP) {
+ add_timer(&fr->keepa_timer);
+ }
+ }
+}
+
+static void fr_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ u16 dlci;
+ u8 nlpid;
+
+ if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+ dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+ if ((nlpid = skb->data[3]) == 0) { // Optional padding
+ nlpid = skb->data[4];
+ skb_pull(skb, 1);
+ }
+ skb_pull(skb, 4); /* DLCI and header throw away */
+
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n",
+ dlci, nlpid);
+ comx_debug_skb(dev, skb, "Contents");
+ }
+
+ /* Megkeressuk, kihez tartozik */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sfr->dlci == dlci)) {
+ skb->dev = sdev;
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Passing it to %s\n",sdev->name);
+ }
+ if (dev != sdev) {
+ sch->stats.rx_packets++;
+ sch->stats.rx_bytes += skb->len;
+ }
+ break;
+ }
+ }
+ switch(nlpid) {
+ case NLPID_IP:
+ skb->protocol = htons(ETH_P_IP);
+ skb->mac.raw = skb->data;
+ comx_rx(sdev, skb);
+ break;
+ case NLPID_Q933_LMI:
+ fr_rx_lmi(dev, skb, dlci, nlpid);
+ default:
+ kfree_skb(skb);
+ break;
+ }
+}
+
+static int fr_tx(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int cnt = 1;
+
+ /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel,
+ es annal a slave-nel aki eppen kuldott.
+ Egy helyen akkor all, ha a master kuldott.
+ Ez megint jo lesz majd, ha utemezni akarunk */
+
+ /* This should be fixed, the slave tbusy should be set when
+ the masters queue is full and reset when not */
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_wake_queue(sdev);
+ cnt++;
+ }
+ }
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static void fr_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (status & LINE_UP) {
+ if (!fr->keepa_freq) {
+ status |= PROTO_UP;
+ }
+ } else {
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ }
+
+ if (dev == fr->master && fr->keepa_freq) {
+ if (status & LINE_UP) {
+ fr->keepa_timer.expires = jiffies + HZ;
+ add_timer(&fr->keepa_timer);
+ fr->keepalivecnt = MAXALIVECNT + 1;
+ fr->keeploopcnt = 0;
+ } else {
+ del_timer(&fr->keepa_timer);
+ }
+ }
+
+ /* Itt a status valtozast vegig kell vinni az osszes slave-n */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ if(status & LINE_UP) {
+ netif_wake_queue(sdev);
+ }
+ comx_status(sdev, status);
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ dev->flags |= IFF_RUNNING;
+ } else {
+ dev->flags &= ~IFF_RUNNING;
+ }
+ }
+ }
+}
+
+static int fr_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+ struct comx_channel *mch;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+ (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+ printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+ return -EINVAL;
+ }
+
+ if (!fr->master) {
+ return -ENODEV;
+ }
+ mch = fr->master->priv;
+ if (fr->master != dev && (!(mch->init_status & LINE_OPEN)
+ || (mch->protocol != &fr_master_protocol))) {
+ printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+ "unable to open %s\n", fr->master->name, dev->name);
+ return -ENODEV;
+ }
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ if (fr->master == dev) {
+ if (fr->keepa_freq) {
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ add_timer(&fr->keepa_timer);
+ } else {
+ if (ch->line_status & LINE_UP) {
+ ch->line_status |= PROTO_UP;
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+ } else {
+ ch->line_status = mch->line_status;
+ if(fr->master->flags & IFF_RUNNING) {
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+// comx_status(dev, ch->line_status);
+ return 0;
+}
+
+static int fr_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+
+ if (fr->master == dev) { // Ha master
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (fr->keepa_freq) {
+ del_timer(&fr->keepa_timer);
+ }
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) &&
+ (sch->init_status & LINE_OPEN)) {
+ dev_close(sdev);
+ }
+ }
+ }
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_channel *sch, *mch;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ if (!fr->master) {
+ printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+ return 0;
+ }
+
+ mch = fr->master->priv;
+
+ /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug_skb(dev, skb, "Sending frame");
+ }
+
+ if (dev != fr->master) {
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ newskb->dev=fr->master;
+ dev_queue_xmit(newskb);
+ dev_kfree_skb(skb);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+ } else {
+ netif_stop_queue(dev);
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_stop_queue(sdev);
+ }
+ }
+
+ switch(mch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ skb_push(skb, dev->hard_header_len);
+ /* Put in DLCI */
+ skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+ skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ skb->data[2] = FRAD_UI;
+ skb->data[3] = NLPID_IP;
+
+ return dev->hard_header_len;
+}
+
+static int fr_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ int len = 0;
+
+ if (fr->master == dev) {
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int slaves = 0;
+
+ len += sprintf(page + len,
+ "This is a Frame Relay master device\nSlaves: ");
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sdev != dev)) {
+ slaves++;
+ len += sprintf(page + len, "%s ", sdev->name);
+ }
+ }
+ len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+ if (fr->keepa_freq) {
+ len += sprintf(page + len, "Line keepalive (value %d) "
+ "status %s [%d]\n", fr->keepa_freq,
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN",
+ fr->keepalivecnt);
+ } else {
+ len += sprintf(page + len, "Line keepalive protocol "
+ "is not set\n");
+ }
+ } else { // if slave
+ len += sprintf(page + len,
+ "This is a Frame Relay slave device, master: %s\n",
+ fr->master ? fr->master->name : "(not set)");
+ }
+ return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ int len = 0;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (strcmp(file->name, FILENAME_DLCI) == 0) {
+ len = sprintf(page, "%04d\n", fr->dlci);
+ } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+ len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+ "(none)");
+ } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+ len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq)
+ : sprintf(page, "off\n");
+ } else {
+ printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ char *page;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+ u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+ if (dlci_new > 1023) {
+ printk(KERN_ERR "Invalid DLCI value\n");
+ }
+ else fr->dlci = dlci_new;
+ } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+ struct net_device *new_master = dev_get_by_name(page);
+
+ if (new_master && new_master->type == ARPHRD_FRAD) {
+ struct comx_channel *sch = new_master->priv;
+ struct fr_data *sfr = sch->LINE_privdata;
+
+ if (sfr && sfr->master == new_master) {
+ if(fr->master)
+ dev_put(fr->master);
+ fr->master = new_master;
+ /* Megorokli a master statuszat */
+ ch->line_status = sch->line_status;
+ }
+ }
+ } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+ int keepa_new = -1;
+
+ if (strcmp(page, KEEPALIVE_OFF) == 0) {
+ keepa_new = 0;
+ } else {
+ keepa_new = simple_strtoul(page, NULL, 10);
+ }
+
+ if (keepa_new < 0 || keepa_new > 100) {
+ printk(KERN_ERR "invalid keepalive\n");
+ } else {
+ if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ if (keepa_new) {
+ fr_set_keepalive(dev, keepa_new);
+ }
+ }
+ } else {
+ printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int fr_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+ if (fr->master && fr->master == dev) {
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ dev_close(sdev);
+ sfr->master = NULL;
+ }
+ }
+ }
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ ch->LINE_status = 0;
+
+ if (fr->master != dev) { // if not master, remove dlci
+ if(fr->master)
+ dev_put(fr->master);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ remove_proc_entry(FILENAME_MASTER, ch->procdir);
+ } else {
+ if (fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ }
+
+ kfree(fr);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int fr_master_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+ fr->master = dev; // this means master
+ fr->dlci = 0; // let's say default
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_FRAD;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 4;
+ new_file->nlink = 1;
+
+ fr_set_keepalive(dev, 0);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int fr_slave_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_DLCI;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 10;
+ new_file->nlink = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status |= HW_OPEN;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status &= ~HW_OPEN;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!fr->master) {
+ return 0;
+ }
+
+ ch = fr->master->priv;
+ fr = ch->LINE_privdata;
+ return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct net_device *dev, char *page)
+{
+ return 0;
+}
+
+static int dlci_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = dlci_open;
+ ch->HW_close = dlci_close;
+ ch->HW_txe = dlci_txe;
+ ch->HW_statistics = dlci_statistics;
+
+ /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_statistics = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+ return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+ "frad",
+ VERSION,
+ ARPHRD_FRAD,
+ fr_master_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_protocol fr_slave_protocol = {
+ "ietf-ip",
+ VERSION,
+ ARPHRD_DLCI,
+ fr_slave_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_hardware fr_dlci = {
+ "dlci",
+ VERSION,
+ dlci_init,
+ dlci_exit,
+ dlci_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+int __init comx_proto_fr_init(void)
+{
+ int ret;
+
+ if ((ret = comx_register_hardware(&fr_dlci))) {
+ return ret;
+ }
+ if ((ret = comx_register_protocol(&fr_master_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware(fr_dlci.name);
+ comx_unregister_protocol(fr_master_protocol.name);
+ comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c
new file mode 100644
index 000000000..c0fc0c6bd
--- /dev/null
+++ b/drivers/net/wan/comx-proto-lapb.c
@@ -0,0 +1,548 @@
+/*
+ * LAPB protocol module for the COMX driver
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as non-module
+ *
+ */
+
+#define VERSION "0.80"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ if (!dev || !dev->priv) {
+ dev_kfree_skb(skb);
+ } else {
+ lapb_data_received(dev->priv, skb);
+ }
+}
+
+static int comxlapb_tx(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ return dev->hard_header_len;
+}
+
+static void comxlapb_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch;
+
+ if (!dev || !(ch = dev->priv)) {
+ return;
+ }
+ if (status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int comxlapb_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int err = 0;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ err = lapb_connect_request(ch);
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb opened, error code: %d\n",
+ dev->name, err);
+ }
+
+ if (!err) {
+ ch->init_status |= LINE_OPEN;
+ MOD_INC_USE_COUNT;
+ }
+ return err;
+}
+
+static int comxlapb_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb closed\n", dev->name);
+ }
+
+ lapb_disconnect_request(ch);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~PROTO_UP;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct sk_buff *skb2;
+
+ if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+ return -ENODEV;
+ }
+
+ if (dev->type == ARPHRD_X25) { // first byte tells what to do
+ switch(skb->data[0]) {
+ case 0x00:
+ break; // transmit
+ case 0x01:
+ lapb_connect_request(ch);
+ kfree_skb(skb);
+ return 0;
+ case 0x02:
+ lapb_disconnect_request(ch);
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ skb_pull(skb,1);
+ }
+
+ netif_stop_queue(dev);
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ lapb_data_request(ch, skb2);
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct net_device *dev, char *page)
+{
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ len += sprintf(page + len, "Line status: ");
+ if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+ len += sprintf(page + len, "not initialized\n");
+ return len;
+ }
+ len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+ "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+ parms.t1timer, parms.t1, parms.t2timer, parms.t2,
+ parms.n2count, parms.n2, parms.window);
+
+ return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (strcmp(file->name, FILENAME_T1) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t1timer, parms.t1);
+ } else if (strcmp(file->name, FILENAME_T2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t2timer, parms.t2);
+ } else if (strcmp(file->name, FILENAME_N2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.n2count, parms.n2);
+ } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+ len += sprintf(page + len, "%u\n", parms.window);
+ } else if (strcmp(file->name, FILENAME_MODE) == 0) {
+ len += sprintf(page + len, "%s, %s\n",
+ parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+ } else {
+ printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct lapb_parms_struct parms;
+ unsigned long parm;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_T1) == 0) {
+ parm=simple_strtoul(page,NULL,10);
+ if (parm > 0 && parm < 100) {
+ parms.t1=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_T2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.t2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_N2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.n2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+ parms.window = simple_strtoul(page, NULL, 10);
+ lapb_setparms(dev->priv, &parms);
+ } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+ if (comx_strcasecmp(page, "dte") == 0) {
+ parms.mode &= ~(LAPB_DCE | LAPB_DTE);
+ parms.mode |= LAPB_DTE;
+ } else if (comx_strcasecmp(page, "dce") == 0) {
+ parms.mode &= ~(LAPB_DTE | LAPB_DCE);
+ parms.mode |= LAPB_DCE;
+ } else if (comx_strcasecmp(page, "std") == 0 ||
+ comx_strcasecmp(page, "standard") == 0) {
+ parms.mode &= ~LAPB_EXTENDED;
+ parms.mode |= LAPB_STANDARD;
+ } else if (comx_strcasecmp(page, "ext") == 0 ||
+ comx_strcasecmp(page, "extended") == 0) {
+ parms.mode &= ~LAPB_STANDARD;
+ parms.mode |= LAPB_EXTENDED;
+ }
+ lapb_setparms(dev->priv, &parms);
+ } else {
+ printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb connected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x01; // link established
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+
+ ch->line_status |= PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x02; // link disconnected
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->line_status &= ~PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->dev->type == ARPHRD_X25) {
+ skb_push(skb, 1);
+ skb->data[0] = 0; // indicate data for X25
+ skb->protocol = htons(ETH_P_X25);
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ }
+
+ skb->dev = ch->dev;
+ skb->mac.raw = skb->data;
+ comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->HW_send_packet) {
+ ch->HW_send_packet(ch->dev, skb);
+ }
+}
+
+static int comxlapb_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+ }
+ lapb_unregister(dev->priv);
+
+ remove_proc_entry(FILENAME_T1, ch->procdir);
+ remove_proc_entry(FILENAME_T2, ch->procdir);
+ remove_proc_entry(FILENAME_N2, ch->procdir);
+ remove_proc_entry(FILENAME_MODE, ch->procdir);
+ remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct lapb_register_struct lapbreg;
+
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = comxlapb_rx;
+ ch->LINE_tx = comxlapb_tx;
+ ch->LINE_status = comxlapb_status;
+ ch->LINE_open = comxlapb_open;
+ ch->LINE_close = comxlapb_close;
+ ch->LINE_xmit = comxlapb_xmit;
+ ch->LINE_header = comxlapb_header;
+ ch->LINE_statistics = comxlapb_statistics;
+
+ lapbreg.connect_confirmation = comxlapb_connected;
+ lapbreg.connect_indication = comxlapb_connected;
+ lapbreg.disconnect_confirmation = comxlapb_disconnected;
+ lapbreg.disconnect_indication = comxlapb_disconnected;
+ lapbreg.data_indication = comxlapb_data_indication;
+ lapbreg.data_transmit = comxlapb_data_transmit;
+ if (lapb_register(dev->priv, &lapbreg)) {
+ return -ENOMEM;
+ }
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb registered\n", dev->name);
+ }
+
+ if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+ return -ENOMEM;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init_lapb(struct net_device *dev)
+{
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_LAPB;
+
+ return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct net_device *dev)
+{
+ dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_X25;
+
+ return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxlapb_read_proc;
+ new_file->write_proc = &comxlapb_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+ "lapb",
+ VERSION,
+ ARPHRD_LAPB,
+ comxlapb_init_lapb,
+ comxlapb_exit,
+ NULL
+};
+
+static struct comx_protocol comx25_protocol = {
+ "x25",
+ VERSION,
+ ARPHRD_X25,
+ comxlapb_init_x25,
+ comxlapb_exit,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+ int ret;
+
+ if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+ return ret;
+ }
+ return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(comxlapb_protocol.name);
+ comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c
new file mode 100644
index 000000000..0b791685d
--- /dev/null
+++ b/drivers/net/wan/comx-proto-ppp.c
@@ -0,0 +1,269 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ * - written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ * - added hdlc protocol
+ * - protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ * - some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ * - don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ * Version 0.23 (99/12/02):
+ * - tbusy fixes
+ *
+ */
+
+#define VERSION "0.23"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include "syncppp.h"
+#include "comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+ struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct syncppp_data *spch=ch->LINE_privdata;
+ struct sppp *sp = (struct sppp *)sppp_of(dev);
+
+ if(!(ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_UP)) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ }
+ if((ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_DOWN)) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ }
+ mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+
+ if(ch->line_status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ return 0;
+}
+
+static void syncppp_status(struct net_device *dev, unsigned short status)
+{
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ if(status & LINE_UP) {
+ netif_wake_queue(dev);
+ sppp_open(dev);
+ } else {
+ /* Line went down */
+ netif_stop_queue(dev);
+ sppp_close(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int syncppp_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ if(ch->line_status & LINE_UP) {
+ sppp_open(dev);
+ }
+
+ init_timer(&spch->status_timer);
+ spch->status_timer.function=syncppp_status_timerfun;
+ spch->status_timer.data=(unsigned long)dev;
+ spch->status_timer.expires=jiffies + HZ*3;
+ add_timer(&spch->status_timer);
+
+ return 0;
+}
+
+static int syncppp_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+ del_timer(&spch->status_timer);
+
+ sppp_close(dev);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ netif_stop_queue(dev);
+ switch(ch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ return 0;
+}
+
+
+static int syncppp_statistics(struct net_device *dev, char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, " ");
+ return len;
+}
+
+
+static int syncppp_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ sppp_detach(dev);
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ kfree(ch->LINE_privdata);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int syncppp_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
+
+ ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+ pppdev->dev = dev;
+ sppp_attach(pppdev);
+
+ if(ch->protocol == &hdlc_protocol) {
+ pppdev->sppp.pp_flags |= PP_CISCO;
+ dev->type = ARPHRD_HDLC;
+ } else {
+ pppdev->sppp.pp_flags &= ~PP_CISCO;
+ dev->type = ARPHRD_PPP;
+ }
+
+ ch->LINE_rx = sppp_input;
+ ch->LINE_tx = syncppp_tx;
+ ch->LINE_status = syncppp_status;
+ ch->LINE_open = syncppp_open;
+ ch->LINE_close = syncppp_close;
+ ch->LINE_xmit = syncppp_xmit;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = syncppp_statistics;
+
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+ "ppp",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+static struct comx_protocol hdlc_protocol = {
+ "hdlc",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+int __init comx_proto_ppp_init(void)
+{
+ int ret;
+
+ if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(syncppp_protocol.name);
+ comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c
new file mode 100644
index 000000000..d3ca69e86
--- /dev/null
+++ b/drivers/net/wan/comx.c
@@ -0,0 +1,1238 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ *
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - clean up source code (playing a bit of indent)
+ * - port back to kernel, add support for non-module versions
+ * - add support for board resets when channel protocol is down
+ * - reset the device structure after protocol exit
+ * the syncppp driver needs it
+ * - add support for /proc/comx/protocols and
+ * /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ * - comment out the board reset support code, the locomx
+ * driver seems not buggy now
+ * - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ * - Handle stats correctly if the lowlevel driver is
+ * is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ * - reset line_status when interface is down
+ *
+ * Version 0.84 (99/12/01):
+ * - comx_status should not check for IFF_UP (to report
+ * line status from dev->open())
+ */
+
+#define VERSION "0.84"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops;
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops; // with open/relase
+static struct file_operations comx_debug_file_ops; // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ &comx_delete_dentry /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+ 0, 4, "comx",
+ S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &comx_root_inode_ops,
+ NULL, comx_fill_inode,
+ NULL, &proc_root, NULL
+};
+
+struct comx_debugflags_struct comx_debugflags[] = {
+ { "comx_rx", DEBUG_COMX_RX },
+ { "comx_tx", DEBUG_COMX_TX },
+ { "hw_tx", DEBUG_HW_TX },
+ { "hw_rx", DEBUG_HW_RX },
+ { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE },
+ { "comxppp", DEBUG_COMX_PPP },
+ { "comxlapb", DEBUG_COMX_LAPB },
+ { "dlci", DEBUG_COMX_DLCI },
+ { NULL, 0 }
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct net_device *dev, char *fmt, ...)
+{
+ struct comx_channel *ch = dev->priv;
+ char *page,*str;
+ va_list args;
+ int len;
+
+ if (!ch->debug_area) return 0;
+
+ if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+ va_start(args, fmt);
+ len = vsprintf(str = page, fmt, args);
+ va_end(args);
+
+ if (len >= PAGE_SIZE) {
+ printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+
+ while (len) {
+ int to_copy;
+ int free = (ch->debug_start - ch->debug_end + ch->debug_size)
+ % ch->debug_size;
+
+ to_copy = min( free ? free : ch->debug_size,
+ min (ch->debug_size - ch->debug_end, len) );
+ memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+ str += to_copy;
+ len -= to_copy;
+ ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+ if (ch->debug_start == ch->debug_end) // Full ? push start away
+ ch->debug_start = (ch->debug_start + len + 1) %
+ ch->debug_size;
+ ch->debug_file->size = (ch->debug_end - ch->debug_start +
+ ch->debug_size) % ch->debug_size;
+ }
+
+ free_page((unsigned long)page);
+ return 0;
+}
+
+int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+ if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+ if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+ return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg)
+{
+ int pos = 0;
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+
+ comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+ while (pos != len) {
+ char line[80];
+ int i = 0;
+
+ memset(line, 0, 80);
+ sprintf(line,"%04d ", pos);
+ do {
+ sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+ sprintf(line + 60 + (pos % 16), "%c",
+ isprint(bytes[pos]) ? bytes[pos] : '.');
+ pos++;
+ } while (pos != len && pos % 16);
+
+ while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+ line[77] = '\n';
+ line[78] = 0;
+
+ comx_debug(dev, "%s", line);
+ }
+ comx_debug(dev, "\n");
+ return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+ ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] =
+ ch->current_stats->tx_bytes;
+
+ ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+ mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+ if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+ ch->HW_reset(dev);
+ }
+ }
+
+ mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif
+
+static int comx_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret=0;
+
+ if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+ if ((ret = ch->HW_open(dev))) return ret;
+ if ((ret = ch->LINE_open(dev))) {
+ ch->HW_close(dev);
+ return ret;
+ };
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0444;
+ }
+
+#if 0
+ ch->reset_pending = 1;
+ ch->reset_timeout = 30;
+ ch->reset_timer.function = comx_reset_timerfun;
+ ch->reset_timer.data = (unsigned long)dev;
+ ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+ add_timer(&ch->reset_timer);
+#endif
+
+ return 0;
+}
+
+static int comx_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret = -ENODEV;
+
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ }
+
+#if 0
+ del_timer(&ch->reset_timer);
+#endif
+
+ if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+ ret = ch->LINE_close(dev);
+ }
+
+ if (ret) return ret;
+
+ if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+ ret = ch->HW_close(dev);
+ }
+
+ ch->line_status=0;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0644;
+ }
+
+ return ret;
+}
+
+void comx_status(struct net_device *dev, int status)
+{
+ struct comx_channel *ch = dev->priv;
+
+#if 0
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ clear_bit(0,&ch->reset_pending);
+ }
+#endif
+
+ printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+ dev->name, status & LINE_UP ? "UP" : "DOWN",
+ status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ?
+ "UP" : "DOWN");
+
+ ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int rc;
+
+ if (skb->len > dev->mtu + dev->hard_header_len) {
+ printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+ (int)skb->len, dev->mtu);
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_TX) {
+ comx_debug_skb(dev, skb, "comx_xmit skb");
+ }
+
+ rc=ch->LINE_xmit(skb, dev);
+// if (!rc) dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_header) {
+ return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+ } else {
+ return 0;
+ }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_rebuild_header) {
+ return(ch->LINE_rebuild_header(skb));
+ } else {
+ return 0;
+ }
+}
+
+int comx_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->debug_flags & DEBUG_COMX_RX) {
+ comx_debug_skb(dev, skb, "comx_rx skb");
+ }
+ if (skb) {
+ netif_rx(skb);
+ }
+ return 0;
+}
+
+static struct net_device_stats *comx_stats(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ del_timer(&ch->lineup_timer);
+ clear_bit(0, &ch->lineup_pending);
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+}
+
+#define LOADAVG(avg, off) (int) \
+ ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \
+ - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ int len = 0;
+ int tmp;
+ int i = 0;
+ char tmpstr[20];
+ int tmpstrlen = 0;
+
+ len += sprintf(page + len, "Interface administrative status is %s, "
+ "modem status is %s, protocol is %s\n",
+ dev->flags & IFF_UP ? "UP" : "DOWN",
+ ch->line_status & LINE_UP ? "UP" : "DOWN",
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN");
+ len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+ "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+ LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+ tmpstr[0] = 0;
+ for (i=0; i != 3; i++) {
+ char tf;
+
+ tf = ch->loadavg[i] % 60 == 0 &&
+ ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+ tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s",
+ ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf,
+ i == 2 ? ")\n" : "/");
+ }
+ len += sprintf(page + len,
+ "%s (output): %d / %d / %d bits/s (%s", tmpstr,
+ LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size),
+ LOADAVG(2, ch->loadavg_size), tmpstr);
+
+ len += sprintf(page + len, "Debug flags: ");
+ tmp = len; i = 0;
+ while (comx_debugflags[i].name) {
+ if (ch->debug_flags & comx_debugflags[i].value)
+ len += sprintf(page + len, "%s ",
+ comx_debugflags[i].name);
+ i++;
+ }
+ len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+ len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+ "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n"
+ "TX errors: underrun: %lu\n",
+ ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors,
+ ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors,
+ ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+ ch->current_stats->tx_fifo_errors);
+
+ if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+ len += ch->LINE_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Line status: driver not initialized\n");
+ }
+ if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+ len += ch->HW_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Board status: driver not initialized\n");
+ }
+
+ return len;
+}
+
+static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_ioctl) {
+ return(ch->LINE_ioctl(dev, ifr, cmd));
+ }
+ return -EINVAL;
+}
+
+static void comx_reset_dev(struct net_device *dev)
+{
+ dev->open = comx_open;
+ dev->stop = comx_close;
+ dev->hard_start_xmit = comx_xmit;
+ dev->hard_header = comx_header;
+ dev->rebuild_header = comx_rebuild_header;
+ dev->get_stats = comx_stats;
+ dev->do_ioctl = comx_ioctl;
+ dev->change_mtu = NULL;
+ dev->tx_queue_len = 20;
+ dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct net_device *dev)
+{
+ struct comx_channel *ch;
+
+ if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch, 0, sizeof(struct comx_channel));
+
+ ch->loadavg[0] = 5;
+ ch->loadavg[1] = 300;
+ ch->loadavg[2] = 900;
+ ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1;
+ if ((ch->avg_bytes = kmalloc(ch->loadavg_size *
+ sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+ ch->loadavg_counter = 0;
+ ch->loadavg_timer.function = comx_loadavg_timerfun;
+ ch->loadavg_timer.data = (unsigned long)dev;
+ ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+ add_timer(&ch->loadavg_timer);
+
+ dev->priv = (void *)ch;
+ ch->dev = dev;
+ ch->line_status &= ~LINE_UP;
+
+ ch->current_stats = &ch->stats;
+
+ comx_reset_dev(dev);
+ return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_STATUS) == 0) {
+ len = comx_statistics(dev, page);
+ } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+ len = sprintf(page, "%s\n", ch->hardware ?
+ ch->hardware->name : HWNAME_NONE);
+ } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+ len = sprintf(page, "%s\n", ch->protocol ?
+ ch->protocol->name : PROTONAME_NONE);
+ } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+ len = sprintf(page, "%01d\n", ch->lineup_delay);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct comx_hardware *hw;
+ struct comx_protocol *line;
+
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+ for(hw=comx_channels;hw;hw=hw->next)
+ len+=sprintf(page+len, "%s\n", hw->name);
+ } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+ for(line=comx_lines;line;line=line->next)
+ len+=sprintf(page+len, "%s\n", line->name);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+ void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ char *page;
+ struct comx_hardware *hw = comx_channels;
+ struct comx_protocol *line = comx_lines;
+ char str[30];
+ int ret=0;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+ copy_from_user(page, buffer, count);
+
+ if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+ if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+ int i;
+ int ret = 0;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (ch->debug_area) kfree(ch->debug_area);
+ if ((ch->debug_area = kmalloc(ch->debug_size = i,
+ GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ }
+ ch->debug_start = ch->debug_end = 0;
+ restore_flags(flags);
+ free_page((unsigned long)page);
+ return count;
+ }
+
+ if (*page != '+' && *page != '-') {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (comx_debugflags[i].value &&
+ strncmp(comx_debugflags[i].name, page + 1,
+ strlen(comx_debugflags[i].name))) {
+ i++;
+ }
+
+ if (comx_debugflags[i].value == 0) {
+ printk(KERN_ERR "Invalid debug option\n");
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ if (*page == '+') {
+ ch->debug_flags |= comx_debugflags[i].value;
+ } else {
+ ch->debug_flags &= ~comx_debugflags[i].value;
+ }
+ } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (hw) {
+ if (strcmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+ sprintf(str,"comx-hw-%s",page);
+ request_module(str);
+ }
+ hw=comx_channels;
+ while (hw) {
+ if (comx_strcasecmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+ if (ch->init_status & HW_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->hardware = hw;
+ entry->size = strlen(page) + 1;
+ if (hw && hw->hw_init) hw->hw_init(dev);
+ } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+ sprintf(str,"comx-proto-%s",page);
+ request_module(str);
+ }
+ line=comx_lines;
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+
+ if (ch->init_status & LINE_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->protocol = line;
+ entry->size = strlen(page) + 1;
+ comx_reset_dev(dev);
+ if (line && line->line_init) line->line_init(dev);
+ } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+ int i;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ if (i >=0 && i < 10) {
+ ch->lineup_delay = i;
+ } else {
+ printk(KERN_ERR "comx: invalid lineup_delay value\n");
+ }
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+ switch(orig) {
+ case 0:
+ file->f_pos = max(0, min(offset,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 1:
+ file->f_pos = max(0, min(offset + file->f_pos,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 2:
+ file->f_pos = max(0,
+ min(offset + file->f_dentry->d_inode->i_size,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ }
+ return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+ if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+ return -EACCES;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+ struct net_device *dev = de->parent->data;
+ struct comx_channel *ch = dev->priv;
+ loff_t copied = 0;
+ unsigned long flags;
+
+ save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+ // from irq inside read. no problem if the buffer is
+ // large enough
+
+ while (count > 0 && ch->debug_start != ch->debug_end) {
+ int len;
+
+ len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+ %ch->debug_size, min (ch->debug_size -
+ ch->debug_start, count));
+
+ if (len) copy_to_user(buffer + copied,
+ ch->debug_area + ch->debug_start, len);
+ ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+ de->size -= len;
+ count -= len;
+ copied += len;
+ }
+
+ restore_flags(flags);
+ return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct proc_dir_entry *new_dir, *debug_file;
+ struct net_device *dev;
+ struct comx_channel *ch;
+
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
+ &comx_root_dir)) == NULL) {
+ return -EIO;
+ }
+
+ new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->nlink = 2;
+ new_dir->data = NULL; // ide jon majd a struct dev
+
+ /* Ezek kellenek */
+ if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644,
+ strlen(HWNAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
+ strlen(PROTONAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+ return -ENOMEM;
+ }
+
+ if ((debug_file = create_proc_entry(FILENAME_DEBUG,
+ S_IFREG | 0644, new_dir)) == NULL) {
+ return -ENOMEM;
+ }
+ debug_file->ops = &comx_debug_inode_ops;
+ debug_file->data = (void *)debug_file;
+ debug_file->read_proc = NULL; // see below
+ debug_file->write_proc = &comx_write_proc;
+ debug_file->nlink = 1;
+
+ if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct net_device));
+ dev->name = (char *)new_dir->name;
+ dev->init = comx_init_dev;
+
+ if (register_netdevice(dev)) {
+ return -EIO;
+ }
+ ch=dev->priv;
+ if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch->if_ptr, 0, sizeof(struct ppp_device));
+ ch->debug_file = debug_file;
+ ch->procdir = new_dir;
+ new_dir->data = dev;
+
+ ch->debug_start = ch->debug_end = 0;
+ if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE,
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+ struct net_device *dev = entry->data;
+ struct comx_channel *ch = dev->priv;
+ int ret;
+
+ /* Egyelore miert ne ? */
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ return ret;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ if(ch->protocol && ch->protocol->line_init) {
+ ch->protocol->line_init(dev);
+ }
+ return ret;
+ }
+ ch->protocol = NULL;
+ ch->hardware = NULL;
+
+ del_timer(&ch->loadavg_timer);
+ kfree(ch->avg_bytes);
+
+ unregister_netdev(dev);
+ if (ch->debug_area) {
+ kfree(ch->debug_area);
+ }
+ if (dev->priv) {
+ kfree(dev->priv);
+ }
+ kfree(dev);
+
+ remove_proc_entry(FILENAME_DEBUG, entry);
+ remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+ remove_proc_entry(FILENAME_STATUS, entry);
+ remove_proc_entry(FILENAME_HARDWARE, entry);
+ remove_proc_entry(FILENAME_PROTOCOL, entry);
+ remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *de;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ return ERR_PTR(-ENOTDIR);
+ }
+
+ if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+ for (de = de->subdir ; de ; de = de->next) {
+ if ((de && de->low_ino) &&
+ (de->namelen == dentry->d_name.len) &&
+ (memcmp(dentry->d_name.name, de->name,
+ de->namelen) == 0)) {
+ if ((inode = proc_get_inode(dir->i_sb,
+ de->low_ino, de)) == NULL) {
+ printk(KERN_ERR "COMX: lookup error\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ }
+ }
+ }
+ dentry->d_op = &comx_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+ d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comx_read_proc;
+ new_file->write_proc = &comx_write_proc;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ comx_channels = comx_hw;
+ } else {
+ while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+ hw = hw->next;
+ }
+ if (strcmp(comx_hw->name, hw->name) == 0) {
+ return -1;
+ }
+ hw->next = comx_hw;
+ }
+
+ printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+ return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ return -1;
+ }
+
+ if (strcmp(hw->name, name) == 0) {
+ comx_channels = comx_channels->next;
+ return 0;
+ }
+
+ while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+ hw = hw->next;
+ }
+
+ if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+ hw->next = hw->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ comx_lines = comx_line;
+ } else {
+ while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+ pr = pr->next;
+ }
+ if (strcmp(comx_line->name, pr->name) == 0) {
+ return -1;
+ }
+ pr->next = comx_line;
+ }
+
+ printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+ return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ return -1;
+ }
+
+ if (strcmp(pr->name, name) == 0) {
+ comx_lines = comx_lines->next;
+ return 0;
+ }
+
+ while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+ pr = pr->next;
+ }
+
+ if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+ pr->next = pr->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+ struct proc_dir_entry *new_file;
+
+ memcpy(&comx_root_inode_ops, &proc_dir_inode_operations,
+ sizeof(struct inode_operations));
+ comx_root_inode_ops.lookup = &comx_lookup;
+ comx_root_inode_ops.mkdir = &comx_mkdir;
+ comx_root_inode_ops.rmdir = &comx_rmdir;
+
+ memcpy(&comx_normal_inode_ops, &proc_net_inode_operations,
+ sizeof(struct inode_operations));
+ comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+ comx_normal_inode_ops.lookup = &comx_lookup;
+
+ memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops,
+ sizeof(struct inode_operations));
+ comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+ memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+ sizeof(struct file_operations));
+ comx_normal_file_ops.open = &comx_file_open;
+ comx_normal_file_ops.release = &comx_file_release;
+
+ memcpy(&comx_debug_file_ops, &comx_normal_file_ops,
+ sizeof(struct file_operations));
+ comx_debug_file_ops.llseek = &comx_debug_lseek;
+ comx_debug_file_ops.read = &comx_debug_read;
+
+ if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+ if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+
+ printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n",
+ VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+ comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+ comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+ comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+ comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+ comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+ comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+ comx_proto_fr_init();
+#endif
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+ remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+ proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h
new file mode 100644
index 000000000..e02849b90
--- /dev/null
+++ b/drivers/net/wan/comx.h
@@ -0,0 +1,240 @@
+/*
+ * General definitions for the COMX driver
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * net_device_stats:
+ * rx_length_errors rec_len < 4 || rec_len > 2000
+ * rx_over_errors receive overrun (OVR)
+ * rx_crc_errors rx crc error
+ * rx_frame_errors aborts rec'd (ABO)
+ * rx_fifo_errors status fifo overrun (PBUFOVR)
+ * rx_missed_errors receive buffer overrun (BUFOVR)
+ * tx_aborted_errors ?
+ * tx_carrier_errors modem line status changes
+ * tx_fifo_errors tx underrun (locomx)
+ */
+#include <linux/config.h>
+
+struct comx_protocol {
+ char *name;
+ char *version;
+ unsigned short encap_type;
+ int (*line_init)(struct net_device *dev);
+ int (*line_exit)(struct net_device *dev);
+ struct comx_protocol *next;
+ };
+
+struct comx_hardware {
+ char *name;
+ char *version;
+ int (*hw_init)(struct net_device *dev);
+ int (*hw_exit)(struct net_device *dev);
+ int (*hw_dump)(struct net_device *dev);
+ struct comx_hardware *next;
+ };
+
+struct comx_channel {
+ void *if_ptr; // General purpose pointer
+ struct net_device *dev; // Where we belong to
+ struct net_device *twin; // On dual-port cards
+ struct proc_dir_entry *procdir; // the directory
+
+ unsigned char init_status;
+ unsigned char line_status;
+
+ struct timer_list lineup_timer; // against line jitter
+ int lineup_pending;
+ unsigned char lineup_delay;
+
+#if 0
+ struct timer_list reset_timer; // for board resetting
+ int reset_pending;
+ int reset_timeout;
+#endif
+
+ struct net_device_stats stats;
+ struct net_device_stats *current_stats;
+#if 0
+ unsigned long board_resets;
+#endif
+ unsigned long *avg_bytes;
+ int loadavg_counter, loadavg_size;
+ int loadavg[3];
+ struct timer_list loadavg_timer;
+ int debug_flags;
+ char *debug_area;
+ int debug_start, debug_end, debug_size;
+ struct proc_dir_entry *debug_file;
+#ifdef CONFIG_COMX_DEBUG_RAW
+ char *raw;
+ int raw_len;
+#endif
+ // LINE specific
+ struct comx_protocol *protocol;
+ void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb);
+ int (*LINE_tx)(struct net_device *dev);
+ void (*LINE_status)(struct net_device *dev, u_short status);
+ int (*LINE_open)(struct net_device *dev);
+ int (*LINE_close)(struct net_device *dev);
+ int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev);
+ int (*LINE_header)(struct sk_buff *skb, struct net_device *dev,
+ u_short type,void *daddr, void *saddr,
+ unsigned len);
+ int (*LINE_rebuild_header)(struct sk_buff *skb);
+ int (*LINE_statistics)(struct net_device *dev, char *page);
+ int (*LINE_parameter_check)(struct net_device *dev);
+ int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr,
+ int cmd);
+ void (*LINE_mod_use)(int);
+ void * LINE_privdata;
+
+ // HW specific
+
+ struct comx_hardware *hardware;
+ void (*HW_board_on)(struct net_device *dev);
+ void (*HW_board_off)(struct net_device *dev);
+ struct net_device *(*HW_access_board)(struct net_device *dev);
+ void (*HW_release_board)(struct net_device *dev, struct net_device *savep);
+ int (*HW_txe)(struct net_device *dev);
+ int (*HW_open)(struct net_device *dev);
+ int (*HW_close)(struct net_device *dev);
+ int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb);
+ int (*HW_statistics)(struct net_device *dev, char *page);
+#if 0
+ int (*HW_reset)(struct net_device *dev, char *page);
+#endif
+ int (*HW_load_board)(struct net_device *dev);
+ void (*HW_set_clock)(struct net_device *dev);
+ void *HW_privdata;
+ };
+
+struct comx_debugflags_struct {
+ char *name;
+ int value;
+ };
+
+#define COMX_ROOT_DIR_NAME "comx"
+
+#define FILENAME_HARDWARE "boardtype"
+#define FILENAME_HARDWARELIST "boardtypes"
+#define FILENAME_PROTOCOL "protocol"
+#define FILENAME_PROTOCOLLIST "protocols"
+#define FILENAME_DEBUG "debug"
+#define FILENAME_CLOCK "clock"
+#define FILENAME_STATUS "status"
+#define FILENAME_IO "io"
+#define FILENAME_IRQ "irq"
+#define FILENAME_KEEPALIVE "keepalive"
+#define FILENAME_LINEUPDELAY "lineup_delay"
+#define FILENAME_CHANNEL "channel"
+#define FILENAME_FIRMWARE "firmware"
+#define FILENAME_MEMADDR "memaddr"
+#define FILENAME_TWIN "twin"
+#define FILENAME_T1 "t1"
+#define FILENAME_T2 "t2"
+#define FILENAME_N2 "n2"
+#define FILENAME_WINDOW "window"
+#define FILENAME_MODE "mode"
+#define FILENAME_DLCI "dlci"
+#define FILENAME_MASTER "master"
+#ifdef CONFIG_COMX_DEBUG_RAW
+#define FILENAME_RAW "raw"
+#endif
+
+#define PROTONAME_NONE "none"
+#define HWNAME_NONE "none"
+#define KEEPALIVE_OFF "off"
+
+#define FRAME_ACCEPTED 0 /* sending and xmitter busy */
+#define FRAME_DROPPED 1
+#define FRAME_ERROR 2 /* xmitter error */
+#define FRAME_QUEUED 3 /* sending but more can come */
+
+#define LINE_UP 1 /* Modem UP */
+#define PROTO_UP 2
+#define PROTO_LOOP 4
+
+#define HW_OPEN 1
+#define LINE_OPEN 2
+#define FW_LOADED 4
+#define IRQ_ALLOCATED 8
+
+#define DEBUG_COMX_RX 2
+#define DEBUG_COMX_TX 4
+#define DEBUG_HW_TX 16
+#define DEBUG_HW_RX 32
+#define DEBUG_HDLC_KEEPALIVE 64
+#define DEBUG_COMX_PPP 128
+#define DEBUG_COMX_LAPB 256
+#define DEBUG_COMX_DLCI 512
+
+#define DEBUG_PAGESIZE 3072
+#define DEFAULT_DEBUG_SIZE 4096
+#define DEFAULT_LINEUP_DELAY 1
+#define FILE_PAGESIZE 3072
+
+#ifndef COMX_PPP_MAJOR
+#define COMX_PPP_MAJOR 88
+#endif
+
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8 byte;
+#endif
+#ifndef word
+typedef u16 word;
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int comx_register_hardware(struct comx_hardware *comx_hw);
+extern int comx_unregister_hardware(char *name);
+extern int comx_register_protocol(struct comx_protocol *comx_line);
+extern int comx_unregister_protocol(char *name);
+
+extern int comx_rx(struct net_device *dev, struct sk_buff *skb);
+extern void comx_status(struct net_device *dev, int status);
+extern void comx_lineup_func(unsigned long d);
+
+extern int comx_debug(struct net_device *dev, char *fmt, ...);
+extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg);
+extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg);
+extern int comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h
new file mode 100644
index 000000000..15230dc1f
--- /dev/null
+++ b/drivers/net/wan/comxhw.h
@@ -0,0 +1,113 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define LOCOMX_IO_EXTENT 8
+#define COMX_IO_EXTENT 4
+#define HICOMX_IO_EXTENT 16
+
+#define COMX_MAX_TX_SIZE 1600
+#define COMX_MAX_RX_SIZE 2048
+
+#define COMX_JAIL_OFFSET 0xffff
+#define COMX_JAIL_VALUE 0xfe
+#define COMX_MEMORY_SIZE 65536
+#define HICOMX_MEMORY_SIZE 16384
+#define COMX_MEM_MIN 0xa0000
+#define COMX_MEM_MAX 0xf0000
+
+#define COMX_DEFAULT_IO 0x360
+#define COMX_DEFAULT_IRQ 10
+#define COMX_DEFAULT_MEMADDR 0xd0000
+#define HICOMX_DEFAULT_IO 0x320
+#define HICOMX_DEFAULT_IRQ 10
+#define HICOMX_DEFAULT_MEMADDR 0xd0000
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+#define MAX_CHANNELNO 2
+
+#define COMX_CHANNEL_OFFSET 0x2000
+
+#define COMX_ENABLE_BOARD_IT 0x40
+#define COMX_BOARD_RESET 0x20
+#define COMX_ENABLE_BOARD_MEM 0x10
+#define COMX_DISABLE_BOARD_MEM 0
+#define COMX_DISABLE_ALL 0x00
+
+#define HICOMX_DISABLE_ALL 0x00
+#define HICOMX_ENABLE_BOARD_MEM 0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET 0x01
+#define HICOMX_PRG_MEM 4
+#define HICOMX_DATA_MEM 0
+#define HICOMX_ID_BYTE 0x55
+
+#define CMX_ID_BYTE 0x31
+#define COMX_CLOCK_CONST 8000
+
+#define LINKUP_READY 3
+
+#define OFF_FW_L1_ID 0x01e /* ID bytes */
+#define OFF_FW_L2_ID 0x1006
+#define FW_L1_ID_1 0xab
+#define FW_L1_ID_2_COMX 0xc0
+#define FW_L1_ID_2_HICOMX 0xc1
+#define FW_L2_ID_1 0xab
+
+#define OFF_A_L2_CMD 0x130 /* command register for L2 */
+#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */
+#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */
+#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */
+#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA 0x12c /* IT rep disable */
+#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */
+#define OFF_A_L1_CLKINI 0x12e /* Timer Const */
+#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */
+#define OFF_A_L2_DAV 0x134 /* Rx DAV */
+#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */
+#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */
+#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */
+
+#define OFF_A_L2_T1 0x174 /* T1 timer */
+#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */
+
+#define COMX_CMD_INIT 1
+#define COMX_CMD_EXIT 2
+#define COMX_CMD_OPEN 16
+#define COMX_CMD_CLOSE 17
+
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 633514166..544fcb8dd 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -1150,19 +1150,19 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
{
switch(cmd) {
case COSAIORSET: /* Reset the device */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
return cosa_reset(cosa);
case COSAIOSTRT: /* Start the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_start(cosa, arg);
case COSAIODOWNLD: /* Download the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_download(cosa, (struct cosa_download *)arg);
case COSAIORMEM:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_readmem(cosa, (struct cosa_download *)arg);
case COSAIORTYPE:
@@ -1189,7 +1189,7 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
case COSAIONRCHANS:
return cosa->nchannels;
case COSAIOBMSET:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if (is_8bit(cosa))
return -EINVAL;
diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h
new file mode 100644
index 000000000..675b7b1f1
--- /dev/null
+++ b/drivers/net/wan/hscx.h
@@ -0,0 +1,103 @@
+#define HSCX_MTU 1600
+
+#define HSCX_ISTA 0x00
+#define HSCX_MASK 0x00
+#define HSCX_STAR 0x01
+#define HSCX_CMDR 0x01
+#define HSCX_MODE 0x02
+#define HSCX_TIMR 0x03
+#define HSCX_EXIR 0x04
+#define HSCX_XAD1 0x04
+#define HSCX_RBCL 0x05
+#define HSCX_SAD2 0x05
+#define HSCX_RAH1 0x06
+#define HSCX_RSTA 0x07
+#define HSCX_RAH2 0x07
+#define HSCX_RAL1 0x08
+#define HSCX_RCHR 0x09
+#define HSCX_RAL2 0x09
+#define HSCX_XBCL 0x0a
+#define HSCX_BGR 0x0b
+#define HSCX_CCR2 0x0c
+#define HSCX_RBCH 0x0d
+#define HSCX_XBCH 0x0d
+#define HSCX_VSTR 0x0e
+#define HSCX_RLCR 0x0e
+#define HSCX_CCR1 0x0f
+#define HSCX_FIFO 0x1e
+
+#define HSCX_HSCX_CHOFFS 0x400
+#define HSCX_SEROFFS 0x1000
+
+#define HSCX_RME 0x80
+#define HSCX_RPF 0x40
+#define HSCX_RSC 0x20
+#define HSCX_XPR 0x10
+#define HSCX_TIN 0x08
+#define HSCX_ICA 0x04
+#define HSCX_EXA 0x02
+#define HSCX_EXB 0x01
+
+#define HSCX_XMR 0x80
+#define HSCX_XDU 0x40
+#define HSCX_EXE 0x40
+#define HSCX_PCE 0x20
+#define HSCX_RFO 0x10
+#define HSCX_CSC 0x08
+#define HSCX_RFS 0x04
+
+#define HSCX_XDOV 0x80
+#define HSCX_XFW 0x40
+#define HSCX_XRNR 0x20
+#define HSCX_RRNR 0x10
+#define HSCX_RLI 0x08
+#define HSCX_CEC 0x04
+#define HSCX_CTS 0x02
+#define HSCX_WFA 0x01
+
+#define HSCX_RMC 0x80
+#define HSCX_RHR 0x40
+#define HSCX_RNR 0x20
+#define HSCX_XREP 0x20
+#define HSCX_STI 0x10
+#define HSCX_XTF 0x08
+#define HSCX_XIF 0x04
+#define HSCX_XME 0x02
+#define HSCX_XRES 0x01
+
+#define HSCX_AUTO 0x00
+#define HSCX_NONAUTO 0x40
+#define HSCX_TRANS 0x80
+#define HSCX_XTRANS 0xc0
+#define HSCX_ADM16 0x20
+#define HSCX_ADM8 0x00
+#define HSCX_TMD_EXT 0x00
+#define HSCX_TMD_INT 0x10
+#define HSCX_RAC 0x08
+#define HSCX_RTS 0x04
+#define HSCX_TLP 0x01
+
+#define HSCX_VFR 0x80
+#define HSCX_RDO 0x40
+#define HSCX_CRC 0x20
+#define HSCX_RAB 0x10
+
+#define HSCX_CIE 0x04
+#define HSCX_RIE 0x02
+
+#define HSCX_DMA 0x80
+#define HSCX_NRM 0x40
+#define HSCX_CAS 0x20
+#define HSCX_XC 0x10
+
+#define HSCX_OV 0x10
+
+#define HSCX_CD 0x80
+
+#define HSCX_RC 0x80
+
+#define HSCX_PU 0x80
+#define HSCX_NRZ 0x00
+#define HSCX_NRZI 0x40
+#define HSCX_ODS 0x10
+#define HSCX_ITF 0x08
diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h
new file mode 100644
index 000000000..1815eef75
--- /dev/null
+++ b/drivers/net/wan/mixcom.h
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define MIXCOM_IO_EXTENT 0x20
+
+#define MIXCOM_DEFAULT_IO 0x180
+#define MIXCOM_DEFAULT_IRQ 5
+
+#define MIXCOM_ID 0x11
+#define MIXCOM_SERIAL_OFFSET 0x1000
+#define MIXCOM_CHANNEL_OFFSET 0x400
+#define MIXCOM_IT_OFFSET 0xc14
+#define MIXCOM_STATUS_OFFSET 0xc14
+#define MIXCOM_ID_OFFSET 0xc10
+#define MIXCOM_ON 0x1
+#define MIXCOM_OFF 0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB 0x1
+#define MIXCOM_CTSA 0x2
+#define MIXCOM_CHANNELNO 0x20
+#define MIXCOM_POWERFAIL 0x40
+#define MIXCOM_BOOT 0x80
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index b161fbabc..be665f0ae 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -101,7 +101,7 @@ static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int sbni_close(struct net_device *dev);
static void sbni_drop_tx_queue(struct net_device *dev);
static struct enet_statistics *sbni_get_stats(struct net_device *dev);
-void card_start(struct net_device *dev);
+static void card_start(struct net_device *dev);
static inline unsigned short sbni_recv(struct net_device *dev);
void change_level(struct net_device *dev);
static inline void sbni_xmit(struct net_device *dev);
@@ -647,7 +647,7 @@ static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-void card_start(struct net_device *dev)
+static void card_start(struct net_device *dev)
{
struct net_local *lp = (struct net_local*)dev->priv;
@@ -1200,6 +1200,8 @@ static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
case SIOCDEVRESINSTATS:
{
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); )
lp->in_stats.all_rx_number = 0;
lp->in_stats.bad_rx_number = 0;
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index e7875649e..0454118a8 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -1247,7 +1247,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct frad_local *flp;
- if(!suser())
+ if(!capable(CAP_NET_ADMIN))
return -EPERM;
flp = dev->priv;
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index e039bbc28..5b8504616 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -147,13 +147,17 @@ static void sppp_print_bytes (u8 *p, u16 len);
static int debug = 0;
+MODULE_PARM(debug,"1i");
+
/*
* Interface down stub
*/
static void if_down(struct net_device *dev)
{
- ;
+ struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+ sp->pp_link_state=SPPP_LINK_DOWN;
}
/*
@@ -182,8 +186,19 @@ static void sppp_clear_timeout(struct sppp *p)
}
}
-/*
- * Process the received packet.
+/**
+ * sppp_input - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ *
+ * This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
+ *
+ * We process the options in the card. If the frame is destined for
+ * the protocol stacks then it requeues the frame for the upper level
+ * protocol. If it is a control from it is processed and discarded
+ * here.
*/
void sppp_input (struct net_device *dev, struct sk_buff *skb)
@@ -194,7 +209,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb)
skb->dev=dev;
skb->mac.raw=skb->data;
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_RUNNING)
{
/* Count received bytes, add FCS and one flag */
sp->ibytes+= skb->len + 3;
@@ -324,7 +339,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 t
h=(struct ppp_header *)skb->data;
if(sp->pp_flags&PP_CISCO)
{
- h->address = CISCO_MULTICAST;
+ h->address = CISCO_UNICAST;
h->control = 0;
}
else
@@ -370,7 +385,7 @@ static void sppp_keepalive (unsigned long dummy)
/* Keepalive mode disabled or channel down? */
if (! (sp->pp_flags & PP_KEEPALIVE) ||
- ! (dev->flags & IFF_RUNNING))
+ ! (dev->flags & IFF_UP))
continue;
/* No keepalive in PPP mode if LCP not opened yet. */
@@ -530,10 +545,10 @@ badreq:
if (h->ident != sp->lcp.confid)
break;
sppp_clear_timeout (sp);
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
+ if ((sp->pp_link_state != SPPP_LINK_UP) &&
+ (dev->flags & IFF_UP)) {
/* Coming out of loopback mode. */
- dev->flags |= IFF_UP;
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
switch (sp->lcp.state) {
@@ -698,9 +713,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
break;
}
sp->pp_loopcnt = 0;
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
- dev->flags |= IFF_UP;
+ if (sp->pp_link_state==SPPP_LINK_DOWN &&
+ (dev->flags & IFF_UP)) {
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
break;
@@ -825,11 +840,19 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
dev_queue_xmit(skb);
}
+/**
+ * sppp_close - close down a synchronous PPP or Cisco HDLC link
+ * @dev: The network device to drop the link of
+ *
+ * This drops the logical interface to the channel. It is not
+ * done politely as we assume we will also be dropping DTR. Any
+ * timeouts are killed.
+ */
int sppp_close (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
- dev->flags &= ~IFF_RUNNING;
+ sp->pp_link_state = SPPP_LINK_DOWN;
sp->lcp.state = LCP_STATE_CLOSED;
sp->ipcp.state = IPCP_STATE_CLOSED;
sppp_clear_timeout (sp);
@@ -838,24 +861,49 @@ int sppp_close (struct net_device *dev)
EXPORT_SYMBOL(sppp_close);
+/**
+ * sppp_open - open a synchronous PPP or Cisco HDLC link
+ * @dev: Network device to activate
+ *
+ * Close down any existing synchronous session and commence
+ * from scratch. In the PPP case this means negotiating LCP/IPCP
+ * and friends, while for Cisco HDLC we simply need to staet sending
+ * keepalives
+ */
int sppp_open (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
- if (!(sp->pp_flags & PP_CISCO))
+ if (!(sp->pp_flags & PP_CISCO)) {
sppp_lcp_open (sp);
+ }
+ sp->pp_link_state = SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_open);
+/**
+ * sppp_reopen - notify of physical link loss
+ * @dev: Device that lost the link
+ *
+ * This function informs the synchronous protocol code that
+ * the underlying link died (for example a carrier drop on X.21)
+ *
+ * We increment the magic numbers to ensure that if the other end
+ * failed to notice we will correctly start a new session. It happens
+ * do to the nature of telco circuits is that you can lose carrier on
+ * one endonly.
+ *
+ * Having done this we go back to negotiating. This function may
+ * be called from an interrupt context.
+ */
+
int sppp_reopen (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
if (!(sp->pp_flags & PP_CISCO))
{
sp->lcp.magic = jiffies;
@@ -864,12 +912,23 @@ int sppp_reopen (struct net_device *dev)
sp->ipcp.state = IPCP_STATE_CLOSED;
/* Give it a moment for the line to settle then go */
sppp_set_timeout (sp, 1);
- }
+ }
+ sp->pp_link_state=SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_reopen);
+/**
+ * sppp_change_mtu - Change the link MTU
+ * @dev: Device to change MTU on
+ * @new_mtu: New MTU
+ *
+ * Change the MTU on the link. This can only be called with
+ * the link down. It returns an error if the link is up or
+ * the mtu is out of range.
+ */
+
int sppp_change_mtu(struct net_device *dev, int new_mtu)
{
if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
@@ -880,6 +939,18 @@ int sppp_change_mtu(struct net_device *dev, int new_mtu)
EXPORT_SYMBOL(sppp_change_mtu);
+/**
+ * sppp_do_ioctl - Ioctl handler for ppp/hdlc
+ * @dev: Device subject to ioctl
+ * @ifr: Interface request block from the user
+ * @cmd: Command that is being issued
+ *
+ * This function handles the ioctls that may be issued by the user
+ * to control the settings of a PPP/HDLC link. It does both busy
+ * and security checks. This function is intended to be wrapped by
+ * callers who wish to add additional ioctl calls of their own.
+ */
+
int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
@@ -913,6 +984,16 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
EXPORT_SYMBOL(sppp_do_ioctl);
+/**
+ * sppp_attach - attach synchronous PPP/HDLC to a device
+ * @pd: PPP device to initialise
+ *
+ * This initialises the PPP/HDLC support on an interface. At the
+ * time of calling the dev element must point to the network device
+ * that this interface is attached to. The interface should not yet
+ * be registered.
+ */
+
void sppp_attach(struct ppp_device *pd)
{
struct net_device *dev = pd->dev;
@@ -973,6 +1054,15 @@ void sppp_attach(struct ppp_device *pd)
EXPORT_SYMBOL(sppp_attach);
+/**
+ * sppp_detach - release PPP resources from a device
+ * @dev: Network device to release
+ *
+ * Stop and free up any PPP/HDLC resources used by this
+ * interface. This must be called before the device is
+ * freed.
+ */
+
void sppp_detach (struct net_device *dev)
{
struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
@@ -1187,7 +1277,7 @@ static void sppp_cp_timeout (unsigned long arg)
cli();
sp->pp_flags &= ~PP_TIMO;
- if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+ if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
restore_flags(flags);
return;
}
@@ -1273,18 +1363,24 @@ static void sppp_print_bytes (u_char *p, u16 len)
printk ("-%x", *p++);
}
-/*
+/**
+ * sppp_rcv - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ * @p: Unused
+ *
* Protocol glue. This drives the deferred processing mode the poorer
- * cards use.
+ * cards use. This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
*/
-int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
{
sppp_input(dev,skb);
return 0;
}
-EXPORT_SYMBOL(sppp_rcv);
struct packet_type sppp_packet_type=
{
@@ -1305,8 +1401,6 @@ void sync_ppp_init(void)
dev_add_pack(&sppp_packet_type);
}
-EXPORT_SYMBOL(sync_ppp_init);
-
#ifdef MODULE
int init_module(void)
diff --git a/drivers/net/wan/syncppp.h b/drivers/net/wan/syncppp.h
index 1e2056869..8f6ed5c1f 100644
--- a/drivers/net/wan/syncppp.h
+++ b/drivers/net/wan/syncppp.h
@@ -47,6 +47,7 @@ struct sppp
u32 ipkts,opkts; /* Packets in/out */
struct timer_list pp_timer;
struct net_device *pp_if;
+ char pp_link_state; /* Link status */
};
struct ppp_device
@@ -75,6 +76,9 @@ struct ppp_device
#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */
#define IPCP_STATE_OPENED 3 /* IPCP state: opened */
+#define SPPP_LINK_DOWN 0 /* link down - no keepalive */
+#define SPPP_LINK_UP 1 /* link is up - keepalive ok */
+
void sppp_attach (struct ppp_device *pd);
void sppp_detach (struct net_device *dev);
void sppp_input (struct net_device *dev, struct sk_buff *m);
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 2ff818b04..f1c618a90 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -55,7 +55,7 @@
static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
/**
- * z8530_read_port:
+ * z8530_read_port - Architecture specific interface function
* @p: port to read
*
* Provided port access methods. The Comtrol SV11 requires no delays
@@ -79,7 +79,7 @@ extern __inline__ int z8530_read_port(unsigned long p)
}
/**
- * z8530_write_port:
+ * z8530_write_port - Architecture specific interface function
* @p: port to write
* @d: value to write
*
@@ -108,7 +108,7 @@ static void z8530_tx_done(struct z8530_channel *c);
/**
- * read_zsreg:
+ * read_zsreg - Read a register from a Z85230
* @c: Z8530 channel to read from (2 per chip)
* @reg: Register to read
* FIXME: Use a spinlock.
@@ -133,7 +133,7 @@ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg)
}
/**
- * read_zsdata:
+ * read_zsdata - Read the data port of a Z8530 channel
* @c: The Z8530 channel to read the data port from
*
* The data port provides fast access to some things. We still
@@ -148,7 +148,7 @@ extern inline u8 read_zsdata(struct z8530_channel *c)
}
/**
- * write_zsreg:
+ * write_zsreg - Write to a Z8530 channel register
* @c: The Z8530 channel
* @reg: Register number
* @val: Value to write
@@ -169,11 +169,28 @@ extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val)
restore_flags(flags);
}
+/**
+ * write_zsctrl - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the control register on the Z8530
+ */
+
extern inline void write_zsctrl(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->ctrlio, val);
}
+/**
+ * write_zsdata - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the data register on the Z8530
+ */
+
+
extern inline void write_zsdata(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->dataio, val);
@@ -249,7 +266,7 @@ u8 z8530_hdlc_kilostream_85230[]=
EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);
/**
- * z8530_flush_fifo:
+ * z8530_flush_fifo - Flush on chip RX FIFO
* @c: Channel to flush
*
* Flush the receive FIFO. There is no specific option for this, we
@@ -276,8 +293,8 @@ static void z8530_flush_fifo(struct z8530_channel *c)
}
/**
- * z8530_rtsdtr:
- * @c: The Z8530 channel to contro;
+ * z8530_rtsdtr - Control the outgoing DTS/RTS line
+ * @c: The Z8530 channel to control;
* @set: 1 to set, 0 to clear
*
* Sets or clears DTR/RTS on the requested line. All locking is handled
@@ -296,7 +313,7 @@ static void z8530_rtsdtr(struct z8530_channel *c, int set)
}
/**
- * z8530_rx:
+ * z8530_rx - Handle a PIO receive event
* @c: Z8530 channel to process
*
* Receive handler for receiving in PIO mode. This is much like the
@@ -378,7 +395,7 @@ static void z8530_rx(struct z8530_channel *c)
/**
- * z8530_tx:
+ * z8530_tx - Handle a PIO transmit event
* @c: Z8530 channel to process
*
* Z8530 transmit interrupt handler for the PIO mode. The basic
@@ -421,7 +438,7 @@ static void z8530_tx(struct z8530_channel *c)
}
/**
- * z8530_status:
+ * z8530_status - Handle a PIO status exception
* @chan: Z8530 channel to process
*
* A status event occured in PIO synchronous mode. There are several
@@ -479,7 +496,7 @@ struct z8530_irqhandler z8530_sync=
EXPORT_SYMBOL(z8530_sync);
/**
- * z8530_dma_rx:
+ * z8530_dma_rx - Handle a DMA RX event
* @chan: Channel to handle
*
* Non bus mastering DMA interfaces for the Z8x30 devices. This
@@ -514,7 +531,7 @@ static void z8530_dma_rx(struct z8530_channel *chan)
}
/**
- * z8530_dma_tx:
+ * z8530_dma_tx - Handle a DMA TX event
* @chan: The Z8530 channel to handle
*
* We have received an interrupt while doing DMA transmissions. It
@@ -525,17 +542,17 @@ static void z8530_dma_tx(struct z8530_channel *chan)
{
if(!chan->dma_tx)
{
- printk("Hey who turned the DMA off?\n");
+ printk(KERN_WARNING "Hey who turned the DMA off?\n");
z8530_tx(chan);
return;
}
/* This shouldnt occur in DMA mode */
- printk(KERN_ERR "DMA tx ??\n");
+ printk(KERN_ERR "DMA tx - bogus event!\n");
z8530_tx(chan);
}
/**
- * z8530_dma_status:
+ * z8530_dma_status - Handle a DMA status exception
* @chan: Z8530 channel to process
*
* A status event occured on the Z8530. We receive these for two reasons
@@ -606,7 +623,7 @@ struct z8530_irqhandler z8530_txdma_sync=
EXPORT_SYMBOL(z8530_txdma_sync);
/**
- * z8530_rx_clear:
+ * z8530_rx_clear - Handle RX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Receive interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -635,7 +652,7 @@ static void z8530_rx_clear(struct z8530_channel *c)
}
/**
- * z8530_tx_clear:
+ * z8530_tx_clear - Handle TX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Transmit interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -650,7 +667,7 @@ static void z8530_tx_clear(struct z8530_channel *c)
}
/**
- * z8530_status_clear:
+ * z8530_status_clear - Handle status events from a stopped chip
* @chan: Z8530 channel to shut up
*
* Status interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -678,7 +695,7 @@ struct z8530_irqhandler z8530_nop=
EXPORT_SYMBOL(z8530_nop);
/**
- * z8530_interrupt:
+ * z8530_interrupt - Handle an interrupt from a Z8530
* @irq: Interrupt number
* @dev_id: The Z8530 device that is interrupting.
* @regs: unused
@@ -758,7 +775,7 @@ static char reg_init[16]=
/**
- * z8530_sync_open:
+ * z8530_sync_open - Open a Z8530 channel for PIO
* @dev: The network interface we are using
* @c: The Z8530 channel to open in synchronous PIO mode
*
@@ -789,7 +806,7 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_open);
/**
- * z8530_sync_close:
+ * z8530_sync_close - Close a PIO Z8530 channel
* @dev: Network device to close
* @c: Z8530 channel to disassociate and move to idle
*
@@ -814,7 +831,7 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_close);
/**
- * z8530_sync_dma_open:
+ * z8530_sync_dma_open - Open a Z8530 for DMA I/O
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -934,7 +951,7 @@ int z8530_sync_dma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_open);
/**
- * z8530_sync_dma_close:
+ * z8530_sync_dma_close - Close down DMA I/O
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -999,7 +1016,7 @@ int z8530_sync_dma_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_close);
/**
- * z8530_sync_txdma_open:
+ * z8530_sync_txdma_open - Open a Z8530 for TX driven DMA
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -1099,7 +1116,7 @@ int z8530_sync_txdma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_txdma_open);
/**
- * z8530_sync_txdma_close:
+ * z8530_sync_txdma_close - Close down a TX driven DMA channel
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -1168,7 +1185,7 @@ static char *z8530_type_name[]={
};
/**
- * z8530_describe:
+ * z8530_describe - Uniformly describe a Z8530 port
* @dev: Z8530 device to describe
* @mapping: string holding mapping type (eg "I/O" or "Mem")
* @io: the port value in question
@@ -1191,7 +1208,7 @@ void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io)
EXPORT_SYMBOL(z8530_describe);
/**
- * z8530_init:
+ * z8530_init - Initialise a Z8530 device
* @dev: Z8530 device to initialise.
*
* Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
@@ -1273,7 +1290,7 @@ int z8530_init(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_init);
/**
- * z8530_shutdown:
+ * z8530_shutdown - Shutdown a Z8530 device
* @dev: The Z8530 chip to shutdown
*
* We set the interrupt handlers to silence any interrupts. We then
@@ -1294,7 +1311,7 @@ int z8530_shutdown(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_shutdown);
/**
- * z8530_channel_load:
+ * z8530_channel_load - Load channel data
* @c: Z8530 channel to configure
* @rtable: Table of register, value pairs
* FIXME: ioctl to allow user uploaded tables
@@ -1333,7 +1350,7 @@ EXPORT_SYMBOL(z8530_channel_load);
/**
- * z8530_tx_begin:
+ * z8530_tx_begin - Begin packet transmission
* @c: The Z8530 channel to kick
*
* This is the speed sensitive side of transmission. If we are called
@@ -1430,7 +1447,7 @@ static void z8530_tx_begin(struct z8530_channel *c)
}
/**
- * z8530_tx_done:
+ * z8530_tx_done - TX complete callback
* @c: The channel that completed a transmit.
*
* This is called when we complete a packet send. We wake the queue,
@@ -1461,7 +1478,7 @@ static void z8530_tx_done(struct z8530_channel *c)
}
/**
- * z8530_null_rx:
+ * z8530_null_rx - Discard a packet
* @c: The channel the packet arrived on
* @skb: The buffer
*
@@ -1477,7 +1494,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_null_rx);
/**
- * z8530_rx_done:
+ * z8530_rx_done - Receive completion callback
* @c: The channel that completed a receive
*
* A new packet is complete. Our goal here is to get back into receive
@@ -1630,7 +1647,7 @@ static void z8530_rx_done(struct z8530_channel *c)
}
/**
- * spans_boundary:
+ * spans_boundary - Check a packet can be ISA DMA'd
* @skb: The buffer to check
*
* Returns true if the buffer cross a DMA boundary on a PC. The poor
@@ -1642,15 +1659,12 @@ extern inline int spans_boundary(struct sk_buff *skb)
unsigned long a=(unsigned long)skb->data;
a^=(a+skb->len);
if(a&0x00010000) /* If the 64K bit is different.. */
- {
- printk("spanner\n");
return 1;
- }
return 0;
}
/**
- * z8530_queue_xmit:
+ * z8530_queue_xmit - Queue a packet
* @c: The channel to use
* @skb: The packet to kick down the channel
*
@@ -1707,7 +1721,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_queue_xmit);
/**
- * z8530_get_stats:
+ * z8530_get_stats - Get network statistics
* @c: The channel to use
*
* Get the statistics block. We keep the statistics in software as
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
index 9fc75e5e6..2162d5ff0 100644
--- a/drivers/net/wavelan.c
+++ b/drivers/net/wavelan.c
@@ -1990,7 +1990,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
}
/* only super-user can see encryption key */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2224,7 +2224,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
/* ------------------ PRIVATE IOCTL ------------------ */
case SIOCSIPQTHR:
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2248,7 +2248,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
#ifdef HISTOGRAM
case SIOCSIPHISTO:
/* Verify that the user is root. */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
diff --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h
index f55bf48e2..38534c934 100644
--- a/drivers/net/wavelan.h
+++ b/drivers/net/wavelan.h
@@ -26,7 +26,7 @@
* product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
* you might need to modify this part to accommodate your hardware.
*/
-const char MAC_ADDRESSES[][3] =
+static const char MAC_ADDRESSES[][3] =
{
{ 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */
{ 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */
@@ -49,14 +49,14 @@ const char MAC_ADDRESSES[][3] =
* (as read in the offset register of the dac area).
* Used to map channel numbers used by `wfreqsel' to frequencies
*/
-const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
0xD0, 0xF0, 0xF8, 0x150 };
/* Frequencies of the 1.0 modem (fixed frequencies).
* Use to map the PSA `subband' to a frequency
* Note : all frequencies apart from the first one need to be multiplied by 10
*/
-const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 85febe47d..f28feea43 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -77,7 +77,6 @@ static int gx_fix = 0;
#include <linux/version.h>
#include <linux/module.h>
-#include <linux/modversions.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog
index 7429ec38b..8d01df21e 100644
--- a/drivers/parport/ChangeLog
+++ b/drivers/parport/ChangeLog
@@ -1,3 +1,24 @@
+2000-03-13 <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_init): Moved from asm/parport.h.
+
+ * Config.in: CONFIG_PARPORT_PC_SUPERIO: new option.
+
+ * parport_pc.c (show_parconfig_smsc37c669): Make __devinit.
+ (show_parconfig_winbond): Likewise.
+ (decode_winbond): Likewise.
+ (decode_smsc): Likewise.
+ (winbond_check): Likewise.
+ (winbond_check2): Likewise.
+ (smsc_check): Likewise.
+ (detect_and_report_winbond): Likewise.
+ (detect_and_report_smsc): Likewise.
+ (get_superio_dma): Likewise.
+ (get_superio_irq): Likewise.
+ (parport_pc_find_isa_ports): New function.
+ (parport_pc_find_ports): New function.
+ (init_module): Make superio a config option, not a parameter.
+
2000-03-10 <twaugh@redhat.com>
* parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
diff --git a/drivers/parport/Config.in b/drivers/parport/Config.in
index d4222f353..1e486c6b3 100644
--- a/drivers/parport/Config.in
+++ b/drivers/parport/Config.in
@@ -13,6 +13,9 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
if [ "$CONFIG_PARPORT_PC" != "n" ]; then
bool ' Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO
+ fi
fi
if [ "$CONFIG_PARPORT_PC" = "y" ]; then
# Don't bother with this if parport_pc is a module; it only affects
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 86fc6f874..7117c847e 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -60,6 +60,8 @@
#include <linux/parport_pc.h>
#include <asm/parport.h>
+#define PARPORT_PC_MAX_PORTS PARPORT_MAX
+
/* ECR modes */
#define ECR_SPP 00
#define ECR_PS2 01
@@ -84,8 +86,10 @@ static struct superio_struct { /* For Super-IO chips autodetection */
int io;
int irq;
int dma;
-} superios[NR_SUPERIOS]= { {0,},};
-
+} superios[NR_SUPERIOS] __devinitdata = { {0,},};
+
+static int user_specified __devinitdata = 0;
+
/* frob_control, but for ECR */
static void frob_econtrol (struct parport *pb, unsigned char m,
unsigned char v)
@@ -1015,9 +1019,9 @@ struct parport_operations parport_pc_ops =
parport_ieee1284_read_byte,
};
+#ifdef CONFIG_PARPORT_PC_SUPERIO
/* Super-IO chipset detection, Winbond, SMSC */
-
-static void show_parconfig_smsc37c669(int io, int key)
+static void __devinit show_parconfig_smsc37c669(int io, int key)
{
int cr1,cr4,cra,cr23,cr26,cr27,i=0;
char *modes[]={ "SPP and Bidirectional (PS/2)",
@@ -1092,7 +1096,7 @@ static void show_parconfig_smsc37c669(int io, int key)
}
-static void show_parconfig_winbond(int io, int key)
+static void __devinit show_parconfig_winbond(int io, int key)
{
int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
@@ -1152,7 +1156,7 @@ static void show_parconfig_winbond(int io, int key)
}
}
-static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
{
char *type=NULL;
int id,progif=2;
@@ -1188,7 +1192,7 @@ static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
return;
}
-static void decode_smsc(int efer, int key, int devid, int devrev)
+static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
{
char *type=NULL;
void (*func)(int io, int key);
@@ -1219,7 +1223,7 @@ static void decode_smsc(int efer, int key, int devid, int devrev)
}
-static void winbond_check(int io, int key)
+static void __devinit winbond_check(int io, int key)
{
int devid,devrev,oldid;
@@ -1237,7 +1241,7 @@ static void winbond_check(int io, int key)
decode_winbond(io,key,devid,devrev,oldid);
}
-static void winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io,int key)
{
int devid,devrev,oldid;
@@ -1254,7 +1258,7 @@ static void winbond_check2(int io,int key)
decode_winbond(io,key,devid,devrev,oldid);
}
-static void smsc_check(int io, int key)
+static void __devinit smsc_check(int io, int key)
{
int devid,devrev;
@@ -1271,7 +1275,7 @@ static void smsc_check(int io, int key)
}
-static void detect_and_report_winbond (void)
+static void __devinit detect_and_report_winbond (void)
{
printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
@@ -1284,7 +1288,7 @@ static void detect_and_report_winbond (void)
winbond_check2(0x250,0x89);
}
-static void detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc (void)
{
printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
smsc_check(0x3f0,0x55);
@@ -1292,8 +1296,9 @@ static void detect_and_report_smsc (void)
smsc_check(0x3f0,0x44);
smsc_check(0x370,0x44);
}
+#endif /* CONFIG_PARPORT_PC_SUPERIO */
-static int get_superio_dma (struct parport *p)
+static int __devinit get_superio_dma (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -1303,7 +1308,7 @@ static int get_superio_dma (struct parport *p)
return PARPORT_DMA_NONE;
}
-static int get_superio_irq (struct parport *p)
+static int __devinit get_superio_irq (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -2330,7 +2335,7 @@ static struct pci_driver parport_pc_pci_driver = {
probe: parport_pc_pci_probe,
};
-static int __devinit parport_pc_init_superio (void)
+static int __init parport_pc_init_superio (void)
{
#ifdef CONFIG_PCI
const struct pci_device_id *id;
@@ -2348,6 +2353,74 @@ static int __devinit parport_pc_init_superio (void)
return 0; /* zero devices found */
}
+/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
+static int __init __attribute__((unused))
+parport_pc_find_isa_ports (int autoirq, int autodma)
+{
+ int count = 0;
+
+ if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL))
+ count++;
+
+ return count;
+}
+
+/* This function is called by parport_pc_init if the user didn't
+ * specify any ports to probe. Its job is to find some ports. Order
+ * is important here -- we want ISA ports to be registered first,
+ * followed by PCI cards (for least surprise), but before that we want
+ * to do chipset-specific tests for some onboard ports that we know
+ * about.
+ *
+ * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
+ * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
+ */
+static int __init parport_pc_find_ports (int autoirq, int autodma)
+{
+ int count = 0, r;
+
+#ifdef CONFIG_PARPORT_PC_SUPERIO
+ detect_and_report_winbond ();
+ detect_and_report_smsc ();
+#endif
+
+ /* Onboard SuperIO chipsets that show themselves on the PCI bus. */
+ count += parport_pc_init_superio ();
+
+ /* ISA ports and whatever (see asm/parport.h). */
+ count += parport_pc_find_nonpci_ports (autoirq, autodma);
+
+ r = pci_register_driver (&parport_pc_pci_driver);
+ if (r > 0)
+ count += r;
+
+ return count;
+}
+
+int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
+{
+ int count = 0, i = 0;
+
+ if (io && *io) {
+ /* Only probe the ports we were given. */
+ user_specified = 1;
+ do {
+ if (!*io_hi) *io_hi = 0x400 + *io;
+ if (parport_pc_probe_port(*(io++), *(io_hi++),
+ *(irq++), *(dma++), NULL))
+ count++;
+ } while (*io && (++i < PARPORT_PC_MAX_PORTS));
+ } else {
+ count += parport_pc_find_ports (irq[0], dma[0]);
+ }
+
+ return count;
+}
+
/* Exported symbols. */
#ifdef CONFIG_PARPORT_PC_PCMCIA
@@ -2367,7 +2440,6 @@ static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PAR
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-static int superio = 0;
MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
MODULE_DESCRIPTION("PC-style parallel port driver");
@@ -2379,18 +2451,12 @@ MODULE_PARM_DESC(irq, "IRQ line");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
MODULE_PARM_DESC(dma, "DMA channel");
MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
-MODULE_PARM(superio, "i");
int init_module(void)
{
/* Work out how many ports we have, then get parport_share to parse
the irq values. */
- unsigned int i, n;
- if (superio) {
- detect_and_report_winbond ();
- detect_and_report_smsc ();
- }
+ unsigned int i;
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
if (i) {
if (parport_parse_irqs(i, irq, irqval)) return 1;
@@ -2415,12 +2481,7 @@ int init_module(void)
}
}
- n = parport_pc_init_superio ();
- n += parport_pc_init (io, io_hi, irqval, dmaval);
- i = pci_register_driver (&parport_pc_pci_driver);
-
- if (i > 0) n += i;
- return !n;
+ return !parport_pc_init (io, io_hi, irqval, dmaval);
}
void cleanup_module(void)
diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c
index e517fbf27..c57fdfcc6 100644
--- a/drivers/pcmcia/yenta.c
+++ b/drivers/pcmcia/yenta.c
@@ -498,7 +498,7 @@ static int yenta_socket_thread(void * data)
return 0;
}
-static unsigned int yenta_probe_irq(pci_socket_t *socket)
+static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask)
{
int i;
unsigned long val;
@@ -518,7 +518,7 @@ static unsigned int yenta_probe_irq(pci_socket_t *socket)
*/
cb_writel(socket, CB_SOCKET_EVENT, -1);
cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
- val = probe_irq_on();
+ val = probe_irq_on() & isa_irq_mask;
for (i = 1; i < 16; i++) {
if (!((val >> i) & 1))
continue;
@@ -647,12 +647,12 @@ static int yenta_suspend(pci_socket_t *socket)
/*
* Set static data that doesn't need re-initializing..
*/
-static void yenta_get_socket_capabilities(pci_socket_t *socket)
+static void yenta_get_socket_capabilities(pci_socket_t *socket, u32 isa_irq_mask)
{
socket->cap.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
socket->cap.map_size = 0x1000;
socket->cap.pci_irq = socket->cb_irq;
- socket->cap.irq_mask = yenta_probe_irq(socket);
+ socket->cap.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
socket->cap.cb_dev = socket->dev;
socket->cap.bus = NULL;
@@ -752,6 +752,17 @@ static struct cardbus_override_struct {
#define NR_OVERRIDES (sizeof(cardbus_override)/sizeof(struct cardbus_override_struct))
/*
+ * Only probe "regular" interrupts, don't
+ * touch dangerous spots like the mouse irq,
+ * because there are mice that apparently
+ * get really confused if they get fondled
+ * too intimately.
+ *
+ * Default to 11, 10, 9, 7, 6, 5, 4, 3.
+ */
+static u32 isa_interrupts = 0x0ef8;
+
+/*
* Initialize a cardbus controller. Make sure we have a usable
* interrupt, and that we can map the cardbus area. Fill in the
* socket information structure..
@@ -790,9 +801,6 @@ static int yenta_open(pci_socket_t *socket)
if (dev->irq && !request_irq(dev->irq, yenta_interrupt, SA_SHIRQ, dev->name, socket))
socket->cb_irq = dev->irq;
- /* And figure out what the dang thing can do for the PCMCIA layer... */
- yenta_get_socket_capabilities(socket);
-
/* Do we have special options for the device? */
for (i = 0; i < NR_OVERRIDES; i++) {
struct cardbus_override_struct *d = cardbus_override+i;
@@ -806,6 +814,9 @@ static int yenta_open(pci_socket_t *socket)
}
}
+ /* Figure out what the dang thing can do for the PCMCIA layer... */
+ yenta_get_socket_capabilities(socket, isa_interrupts);
+
kernel_thread(yenta_socket_thread, socket, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
printk("Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
return 0;
diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c
index 01c76073f..4c7e8003c 100644
--- a/drivers/sbus/audio/audio.c
+++ b/drivers/sbus/audio/audio.c
@@ -1,4 +1,4 @@
-/* $Id: audio.c,v 1.49 2000/02/17 05:52:41 davem Exp $
+/* $Id: audio.c,v 1.50 2000/03/13 03:54:07 davem Exp $
* drivers/sbus/audio/audio.c
*
* Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
@@ -70,7 +70,8 @@ static void lis_free_elist( strevent_t **list);
static void kill_procs( struct strevent *elist, int sig, short e);
static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
-
+static devfs_handle_t devfs_handle = NULL;
+
/* This crap to be pulled off into a local include file */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
@@ -92,185 +93,6 @@ static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
#endif
-int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
- int i, dev;
-
- /* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
- for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
- if (drivers[dev] == NULL)
- break;
- }
-
- if (drivers[dev])
- return -EIO;
-
- /* Ensure that the driver has a proper operations structure. */
- if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
- !drv->ops->start_input || !drv->ops->stop_input)
- return -EINVAL;
-
- /* Setup the circular queues of output and input buffers
- *
- * Each buffer is a single page, but output buffers might
- * be partially filled (by a write with count < output_buffer_size),
- * so each output buffer also has a paired output size.
- *
- * Input buffers, on the other hand, always fill completely,
- * so we don't need input counts - each contains input_buffer_size
- * bytes of audio data.
- *
- * TODO: Make number of input/output buffers tunable parameters
- */
-
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
- init_waitqueue_head(&drv->open_wait);
- init_waitqueue_head(&drv->output_write_wait);
- init_waitqueue_head(&drv->output_drain_wait);
- init_waitqueue_head(&drv->input_read_wait);
-#endif
-
- drv->num_output_buffers = 8;
- drv->output_buffer_size = (4096 * 2);
- drv->playing_count = 0;
- drv->output_offset = 0;
- drv->output_eof = 0;
- drv->output_front = 0;
- drv->output_rear = 0;
- drv->output_count = 0;
- drv->output_active = 0;
- drv->output_buffers = kmalloc(drv->num_output_buffers *
- sizeof(__u8 *), GFP_KERNEL);
- drv->output_sizes = kmalloc(drv->num_output_buffers *
- sizeof(size_t), GFP_KERNEL);
- drv->output_notify = kmalloc(drv->num_output_buffers *
- sizeof(char), GFP_KERNEL);
- if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
- goto kmalloc_failed1;
-
- drv->output_buffer = kmalloc((drv->output_buffer_size *
- drv->num_output_buffers),
- GFP_KERNEL);
- if (!drv->output_buffer)
- goto kmalloc_failed2;
-
- /* Allocate the pages for each output buffer. */
- for (i = 0; i < drv->num_output_buffers; i++) {
- drv->output_buffers[i] = (void *)(drv->output_buffer +
- (i * drv->output_buffer_size));
- drv->output_sizes[i] = 0;
- drv->output_notify[i] = 0;
- }
-
- /* Setup the circular queue of input buffers. */
- drv->num_input_buffers = 8;
- drv->input_buffer_size = (4096 * 2);
- drv->recording_count = 0;
- drv->input_front = 0;
- drv->input_rear = 0;
- drv->input_count = 0;
- drv->input_offset = 0;
- drv->input_size = 0;
- drv->input_active = 0;
- drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
- GFP_KERNEL);
- drv->input_sizes = kmalloc(drv->num_input_buffers *
- sizeof(size_t), GFP_KERNEL);
- if (!drv->input_buffers || !drv->input_sizes)
- goto kmalloc_failed3;
-
- /* Allocate the pages for each input buffer. */
- if (duplex == 1) {
- drv->input_buffer = kmalloc((drv->input_buffer_size *
- drv->num_input_buffers),
- GFP_DMA);
- if (!drv->input_buffer)
- goto kmalloc_failed4;
-
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = (void *)(drv->input_buffer +
- (i * drv->input_buffer_size));
- } else {
- if (duplex == 2) {
- drv->input_buffer = drv->output_buffer;
- drv->input_buffer_size = drv->output_buffer_size;
- drv->num_input_buffers = drv->num_output_buffers;
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = drv->output_buffers[i];
- } else {
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = NULL;
- }
- }
-
- /* Take note of our duplexity */
- drv->duplex = duplex;
-
- /* Ensure that the driver is marked as not being open. */
- drv->flags = 0;
-
- MOD_INC_USE_COUNT;
-
- /* Take driver slot, note which we took */
- drv->index = dev;
- drivers[dev] = drv;
-
- return 0;
-
-kmalloc_failed4:
- kfree(drv->input_buffer);
-
-kmalloc_failed3:
- if (drv->input_sizes)
- kfree(drv->input_sizes);
- if (drv->input_buffers)
- kfree(drv->input_buffers);
- i = drv->num_output_buffers;
-
-kmalloc_failed2:
- kfree(drv->output_buffer);
-
-kmalloc_failed1:
- if (drv->output_buffers)
- kfree(drv->output_buffers);
- if (drv->output_sizes)
- kfree(drv->output_sizes);
- if (drv->output_notify)
- kfree(drv->output_notify);
-
- return -ENOMEM;
-}
-
-int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
- /* Figure out which driver is unregistering */
- if (drivers[drv->index] != drv)
- return -EIO;
-
- /* Deallocate the queue of output buffers. */
- kfree(drv->output_buffer);
- kfree(drv->output_buffers);
- kfree(drv->output_sizes);
- kfree(drv->output_notify);
-
- /* Deallocate the queue of input buffers. */
- if (duplex == 1) {
- kfree(drv->input_buffer);
- kfree(drv->input_sizes);
- }
- kfree(drv->input_buffers);
-
- if (&(drv->sd_siglist) != NULL)
- lis_free_elist( &(drv->sd_siglist) );
-
- MOD_DEC_USE_COUNT;
-
- /* Null the appropriate driver */
- drivers[drv->index] = NULL;
-
- return 0;
-}
-
void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
{
/* If !status, just restart current output.
@@ -2171,6 +1993,229 @@ static struct file_operations sparcaudio_fops = {
release: sparcaudio_release,
};
+static struct {
+ unsigned short minor;
+ char *name;
+ umode_t mode;
+} dev_list[] = {
+ { SPARCAUDIO_MIXER_MINOR, "mixer", S_IWUSR | S_IRUGO },
+ { SPARCAUDIO_DSP_MINOR, "dsp", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_AUDIO_MINOR, "audio", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_DSP16_MINOR, "dspW", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_STATUS_MINOR, "status", S_IRUGO },
+ { SPARCAUDIO_AUDIOCTL_MINOR, "audioctl", S_IRUGO }
+};
+
+static void sparcaudio_mkname (char *buf, char *name, int dev)
+{
+ if (dev)
+ sprintf (buf, "%s%d", name, dev);
+ else
+ sprintf (buf, "%s", name);
+}
+
+int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+ int i, dev;
+ unsigned short minor;
+ char name_buf[32];
+
+ /* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
+ for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
+ if (drivers[dev] == NULL)
+ break;
+ }
+
+ if (drivers[dev])
+ return -EIO;
+
+ /* Ensure that the driver has a proper operations structure. */
+ if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
+ !drv->ops->start_input || !drv->ops->stop_input)
+ return -EINVAL;
+
+ /* Register ourselves with devfs */
+ for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+ sparcaudio_mkname (name_buf, dev_list[i].name, dev);
+ minor = (dev << SPARCAUDIO_DEVICE_SHIFT) | dev_list[i].minor;
+ devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
+ SOUND_MAJOR, minor, S_IFCHR | dev_list[i].mode,
+ 0, 0, &sparcaudio_fops, NULL);
+ }
+
+ /* Setup the circular queues of output and input buffers
+ *
+ * Each buffer is a single page, but output buffers might
+ * be partially filled (by a write with count < output_buffer_size),
+ * so each output buffer also has a paired output size.
+ *
+ * Input buffers, on the other hand, always fill completely,
+ * so we don't need input counts - each contains input_buffer_size
+ * bytes of audio data.
+ *
+ * TODO: Make number of input/output buffers tunable parameters
+ */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
+ init_waitqueue_head(&drv->open_wait);
+ init_waitqueue_head(&drv->output_write_wait);
+ init_waitqueue_head(&drv->output_drain_wait);
+ init_waitqueue_head(&drv->input_read_wait);
+#endif
+
+ drv->num_output_buffers = 8;
+ drv->output_buffer_size = (4096 * 2);
+ drv->playing_count = 0;
+ drv->output_offset = 0;
+ drv->output_eof = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_active = 0;
+ drv->output_buffers = kmalloc(drv->num_output_buffers *
+ sizeof(__u8 *), GFP_KERNEL);
+ drv->output_sizes = kmalloc(drv->num_output_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ drv->output_notify = kmalloc(drv->num_output_buffers *
+ sizeof(char), GFP_KERNEL);
+ if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
+ goto kmalloc_failed1;
+
+ drv->output_buffer = kmalloc((drv->output_buffer_size *
+ drv->num_output_buffers),
+ GFP_KERNEL);
+ if (!drv->output_buffer)
+ goto kmalloc_failed2;
+
+ /* Allocate the pages for each output buffer. */
+ for (i = 0; i < drv->num_output_buffers; i++) {
+ drv->output_buffers[i] = (void *)(drv->output_buffer +
+ (i * drv->output_buffer_size));
+ drv->output_sizes[i] = 0;
+ drv->output_notify[i] = 0;
+ }
+
+ /* Setup the circular queue of input buffers. */
+ drv->num_input_buffers = 8;
+ drv->input_buffer_size = (4096 * 2);
+ drv->recording_count = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_offset = 0;
+ drv->input_size = 0;
+ drv->input_active = 0;
+ drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
+ GFP_KERNEL);
+ drv->input_sizes = kmalloc(drv->num_input_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ if (!drv->input_buffers || !drv->input_sizes)
+ goto kmalloc_failed3;
+
+ /* Allocate the pages for each input buffer. */
+ if (duplex == 1) {
+ drv->input_buffer = kmalloc((drv->input_buffer_size *
+ drv->num_input_buffers),
+ GFP_DMA);
+ if (!drv->input_buffer)
+ goto kmalloc_failed4;
+
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = (void *)(drv->input_buffer +
+ (i * drv->input_buffer_size));
+ } else {
+ if (duplex == 2) {
+ drv->input_buffer = drv->output_buffer;
+ drv->input_buffer_size = drv->output_buffer_size;
+ drv->num_input_buffers = drv->num_output_buffers;
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = drv->output_buffers[i];
+ } else {
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = NULL;
+ }
+ }
+
+ /* Take note of our duplexity */
+ drv->duplex = duplex;
+
+ /* Ensure that the driver is marked as not being open. */
+ drv->flags = 0;
+
+ MOD_INC_USE_COUNT;
+
+ /* Take driver slot, note which we took */
+ drv->index = dev;
+ drivers[dev] = drv;
+
+ return 0;
+
+kmalloc_failed4:
+ kfree(drv->input_buffer);
+
+kmalloc_failed3:
+ if (drv->input_sizes)
+ kfree(drv->input_sizes);
+ if (drv->input_buffers)
+ kfree(drv->input_buffers);
+ i = drv->num_output_buffers;
+
+kmalloc_failed2:
+ kfree(drv->output_buffer);
+
+kmalloc_failed1:
+ if (drv->output_buffers)
+ kfree(drv->output_buffers);
+ if (drv->output_sizes)
+ kfree(drv->output_sizes);
+ if (drv->output_notify)
+ kfree(drv->output_notify);
+
+ return -ENOMEM;
+}
+
+int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+ devfs_handle_t de;
+ int i;
+ char name_buf[32];
+
+ /* Figure out which driver is unregistering */
+ if (drivers[drv->index] != drv)
+ return -EIO;
+
+ /* Deallocate the queue of output buffers. */
+ kfree(drv->output_buffer);
+ kfree(drv->output_buffers);
+ kfree(drv->output_sizes);
+ kfree(drv->output_notify);
+
+ /* Deallocate the queue of input buffers. */
+ if (duplex == 1) {
+ kfree(drv->input_buffer);
+ kfree(drv->input_sizes);
+ }
+ kfree(drv->input_buffers);
+
+ if (&(drv->sd_siglist) != NULL)
+ lis_free_elist( &(drv->sd_siglist) );
+
+ /* Unregister ourselves with devfs */
+ for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+ sparcaudio_mkname (name_buf, dev_list[i].name, drv->index);
+ de = devfs_find_handle (devfs_handle, name_buf, 0, 0, 0,
+ DEVFS_SPECIAL_CHR, 0);
+ devfs_unregister (de);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ /* Null the appropriate driver */
+ drivers[drv->index] = NULL;
+
+ return 0;
+}
+
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static struct symbol_table sparcaudio_syms = {
#include <linux/symtab_begin.h>
@@ -2201,6 +2246,8 @@ int __init sparcaudio_init(void)
/* Register our character device driver with the VFS. */
if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
return -EIO;
+
+ devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL);
#ifdef CONFIG_SPARCAUDIO_AMD7930
amd7930_init();
@@ -2222,6 +2269,7 @@ int __init sparcaudio_init(void)
void cleanup_module(void)
{
devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio");
+ devfs_unregister (devfs_handle);
}
#endif
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
index 220dc62f7..b2fc1ffc3 100644
--- a/drivers/sbus/char/sab82532.c
+++ b/drivers/sbus/char/sab82532.c
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.40 1999/12/19 23:28:08 davem Exp $
+/* $Id: sab82532.c,v 1.41 2000/03/13 03:54:17 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -2163,7 +2163,7 @@ static void __init sab82532_kgdb_hook(int line)
static inline void __init show_serial_version(void)
{
- char *revision = "$Revision: 1.40 $";
+ char *revision = "$Revision: 1.41 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2196,7 +2196,7 @@ int __init sab82532_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
+ serial_driver.name = "tts/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + su_num_ports;
serial_driver.num = NR_PORTS;
@@ -2236,7 +2236,7 @@ int __init sab82532_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c
index 6e30f9b58..a691db430 100644
--- a/drivers/sbus/char/su.c
+++ b/drivers/sbus/char/su.c
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.36 2000/02/09 21:11:22 davem Exp $
+/* $Id: su.c,v 1.37 2000/03/13 03:54:15 davem Exp $
* su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -2223,7 +2223,7 @@ done:
*/
static __inline__ void __init show_su_version(void)
{
- char *revision = "$Revision: 1.36 $";
+ char *revision = "$Revision: 1.37 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2442,7 +2442,7 @@ int __init su_serial_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "su";
- serial_driver.name = "ttyS";
+ serial_driver.name = "ttys/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NR_PORTS;
@@ -2482,7 +2482,7 @@ int __init su_serial_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
index b08b73e18..868193747 100644
--- a/drivers/sbus/char/zs.c
+++ b/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.55 2000/02/09 21:11:24 davem Exp $
+/* $Id: zs.c,v 1.56 2000/03/12 04:02:11 davem Exp $
* zs.c: Zilog serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1928,7 +1928,7 @@ int zs_open(struct tty_struct *tty, struct file * filp)
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.55 $";
+ char *revision = "$Revision: 1.56 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2415,7 +2415,7 @@ int __init zs_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
+ serial_driver.name = "tts/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NUM_CHANNELS;
@@ -2454,7 +2454,7 @@ int __init zs_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx
index f884dfb1e..8f4d4f19c 100644
--- a/drivers/scsi/ChangeLog.ncr53c8xx
+++ b/drivers/scsi/ChangeLog.ncr53c8xx
@@ -1,3 +1,20 @@
+Mon March 6 23:15 2000 Gerard Roudier (groudier@club-internet.fr)
+ * revision 3.2g
+ - Add the file sym53c8xx_comm.h that collects code that should
+ be shared by sym53c8xx and ncr53c8xx drivers. For now, it is
+ a header file that is only included by the ncr53c8xx driver,
+ but things will be cleaned up later. This code addresses
+ notably:
+ * Chip detection and PCI related initialisations
+ * NVRAM detection and reading
+ * DMA mapping
+ * Boot setup command
+ * And some other ...
+ - Add support for the new dynamic dma mapping kernel interface.
+ Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+ - Get data transfer direction from the scsi command structure
+ (Scsi_Cmnd) when this information is available.
+
Sat Jan 8 22:00 2000 Gerard Roudier (groudier@club-internet.fr)
* revision 3.2e
- Add year 2000 copyright.
diff --git a/drivers/scsi/ChangeLog.sym53c8xx b/drivers/scsi/ChangeLog.sym53c8xx
index 4abf98759..47448ec71 100644
--- a/drivers/scsi/ChangeLog.sym53c8xx
+++ b/drivers/scsi/ChangeLog.sym53c8xx
@@ -1,3 +1,12 @@
+Mon Mar 6 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+ * version sym53c8xx-1.5k
+ - Test against expected data transfer direction from SCRIPTS.
+ - Revert the change in 'ncr_flush_done_cmds()' but unmap the
+ scsi dma buffer prior to queueing the command to our done
+ list.
+ - Miscellaneous (minor) fixes in the code added in driver
+ version 1.5j.
+
Sun Feb 20 11:00 2000 Gerard Roudier (groudier@club-internet.fr)
* version sym53c8xx-1.5j
- Add support for the new dynamic dma mapping kernel interface.
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index 4b68450e4..6eee76033 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -8,10 +8,6 @@ fi
dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
-if [ "$CONFIG_CHR_DEV_ST" != "n" ]; then
- int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
-fi
-
dep_tristate ' SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
diff --git a/drivers/scsi/README.st b/drivers/scsi/README.st
index b1b6362f3..77121aa0b 100644
--- a/drivers/scsi/README.st
+++ b/drivers/scsi/README.st
@@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver.
The driver is currently maintained by Kai M{kisara (email
Kai.Makisara@metla.fi)
-Last modified: Sat Aug 7 13:52:16 1999 by makisara@kai.makisara.local
+Last modified: Sat Mar 11 10:34:44 2000 by makisara@kai.makisara.local
BASICS
@@ -134,11 +134,7 @@ A small number of buffers are allocated at driver initialisation. The
maximum number of these buffers is defined by ST_MAX_BUFFERS. The
maximum can be changed with kernel or module startup options. One
buffer is allocated for each drive detected when the driver is
-initialized up to the maximum. The minimum number of allocated buffers
-is ST_EXTRA_DEVS (in hosts.h) (unless this number exceeds the defined
-maximum). This ensures some functionality also for the drives found
-after tape driver initialization (a SCSI adapter driver is loaded as a
-module). The default for ST_EXTRA_DEVS is two.
+initialized up to the maximum.
The driver tries to allocate new buffers at run-time if
necessary. These buffers are freed after use. If the maximum number of
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index c0b3f3a62..6c92531fc 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -105,6 +105,11 @@ irq_numok:
{
tmport += 0x1f;
j = inb(tmport);
+ if((j&0x80)==0)
+ {
+ dev->in_int=0;
+ return;
+ }
tmpcip = dev->pciport;
if ((inb(tmpcip) & 0x08) != 0)
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 849ce76c9..5a74bc2df 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -158,16 +158,20 @@ void print_status (int status) {
}
#if (CONSTANTS & CONST_XSENSE)
-#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
-#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
-#define L 0x004 /* PRINTER DEVICE */
-#define P 0x008 /* PROCESSOR DEVICE */
-#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
-#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
-#define S 0x040 /* SCANNER DEVICE */
-#define O 0x080 /* OPTICAL MEMORY DEVICE */
-#define M 0x100 /* MEDIA CHANGER DEVICE */
-#define C 0x200 /* COMMUNICATION DEVICE */
+#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x0004 /* PRINTER DEVICE */
+#define P 0x0008 /* PROCESSOR DEVICE */
+#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x0020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x0040 /* SCANNER DEVICE */
+#define O 0x0080 /* OPTICAL MEMORY DEVICE */
+#define M 0x0100 /* MEDIA CHANGER DEVICE */
+#define C 0x0200 /* COMMUNICATION DEVICE */
+#define A 0x0400 /* ARRAY STORAGE */
+#define E 0x0800 /* ENCLOSURE SERVICES DEVICE */
+#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE */
+#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE */
struct error_info{
unsigned char code1, code2;
@@ -192,131 +196,213 @@ static struct error_info2 additional2[] =
static struct error_info additional[] =
{
+ {0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
{0x00,0x01,T,"Filemark detected"},
{0x00,0x02,T|S,"End-of-partition/medium detected"},
{0x00,0x03,T,"Setmark detected"},
{0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
- {0x00,0x05,T|S,"End-of-data detected"},
- {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x05,T|L|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
{0x00,0x11,R,"Audio play operation in progress"},
{0x00,0x12,R,"Audio play operation paused"},
{0x00,0x13,R,"Audio play operation successfully completed"},
{0x00,0x14,R,"Audio play operation stopped due to error"},
{0x00,0x15,R,"No current audio status to return"},
- {0x01,0x00,D|W|O,"No index/sector signal"},
- {0x02,0x00,D|W|R|O|M,"No seek complete"},
- {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
+ {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
+ {0x01,0x00,D|W|O|B|K,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
{0x03,0x01,T,"No write current"},
{0x03,0x02,T,"Excessive write errors"},
- {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, cause not reportable"},
- {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
- "Logical unit is in process of becoming ready"},
- {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, initializing command required"},
- {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, manual intervention required"},
- {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
- {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
- {0x06,0x00,D|W|R|O|M,"No reference position found"},
- {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
- {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
- {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
- {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
- {0x09,0x00,D|T|W|R|O,"Track following error"},
- {0x09,0x01,W|R|O,"Tracking servo failure"},
- {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
+ {0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
+ {0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
+ {0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
+ {0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
+ {0x04,0x08,R,"Logical unit not ready,long write in progress"},
+ {0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
+ {0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA/32)"},
+ {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
+ {0x09,0x00,D|T|W|R|O|B,"Track following error"},
+ {0x09,0x01,W|R|O|K,"Tracking servo failure"},
+ {0x09,0x02,W|R|O|K,"Focus servo failure"},
{0x09,0x03,W|R|O,"Spindle servo failure"},
- {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
- {0x0C,0x00,T|S,"Write error"},
- {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
- {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
- {0x10,0x00,D|W|O,"Id crc or ecc error"},
- {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
- {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
- {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
- {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
- {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
- {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
- {0x11,0x06,W|R|O,"Circ unrecovered error"},
- {0x11,0x07,W|O,"Data resynchronization error"},
+ {0x09,0x04,D|T|W|R|O|B,"Head select fault"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
+ {0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
+ {0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
+ {0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
+ {0x0C,0x00,T|R|S,"Write error"},
+ {0x0C,0x01,K,"Write error - recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
+ {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
+ {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
+ {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
+ {0x0C,0x06,D|T|W|O|B,"Block not compressible"},
+ {0x0C,0x07,R,"Write error - recovery needed"},
+ {0x0C,0x08,R,"Write error - recovery failed"},
+ {0x0C,0x09,R,"Write error - loss of streaming"},
+ {0x0C,0x0A,R,"Write error - padding blocks added"},
+ {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
+ {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
+ {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
+ {0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
+ {0x11,0x07,W|O|B,"Data re-synchronization error"},
{0x11,0x08,T,"Incomplete block read"},
{0x11,0x09,T,"No gap found"},
- {0x11,0x0A,D|T|O,"Miscorrected error"},
- {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
- {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
- {0x12,0x00,D|W|O,"Address mark not found for id field"},
- {0x13,0x00,D|W|O,"Address mark not found for data field"},
- {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
- {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
+ {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
+ {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
+ {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
+ {0x11,0x0F,R,"Error reading UPC/EAN number"},
+ {0x11,0x10,R,"Error reading ISRC number"},
+ {0x11,0x11,R,"Read error - loss of streaming"},
+ {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
{0x14,0x02,T,"Filemark or setmark not found"},
{0x14,0x03,T,"End-of-data not found"},
{0x14,0x04,T,"Block sequence error"},
- {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
- {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
- {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
- {0x16,0x00,D|W|O,"Data synchronization mark error"},
- {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
- {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
- {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
- {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
- {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
- {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
- {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
- {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
- {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
- {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
- {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
- {0x18,0x03,R,"Recovered data with circ"},
- {0x18,0x04,R,"Recovered data with lec"},
- {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
- {0x19,0x00,D|O,"Defect list error"},
- {0x19,0x01,D|O,"Defect list not available"},
- {0x19,0x02,D|O,"Defect list error in primary list"},
- {0x19,0x03,D|O,"Defect list error in grown list"},
- {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
- {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
- {0x1C,0x00,D|O,"Defect list not found"},
- {0x1C,0x01,D|O,"Primary defect list not found"},
- {0x1C,0x02,D|O,"Grown defect list not found"},
- {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
- {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
- {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
- {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
- {0x21,0x01,M,"Invalid element address"},
- {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
- {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
- {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
- {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
- {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
- {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
- {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
- {0x27,0x00,D|T|W|O,"Write protected"},
- {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
- {0x28,0x01,M,"Import or export element accessed"},
- {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
- {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
- {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
- {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
- {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
- {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
+ {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
+ {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
+ {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
+ {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
+ {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
+ {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
+ {0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
+ {0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
+ {0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
+ {0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
+ {0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with CIRC"},
+ {0x18,0x04,R,"Recovered data with L-EC"},
+ {0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
+ {0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
+ {0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
+ {0x19,0x00,D|O|K,"Defect list error"},
+ {0x19,0x01,D|O|K,"Defect list not available"},
+ {0x19,0x02,D|O|K,"Defect list error in primary list"},
+ {0x19,0x03,D|O|K,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O|B|K,"Defect list not found"},
+ {0x1C,0x01,D|O|B|K,"Primary defect list not found"},
+ {0x1C,0x02,D|O|B|K,"Grown defect list not found"},
+ {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
+ {0x1F,0x00,D|O|K,"Partial defect list transfer"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
+ {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
+ {0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
+ {0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
+ {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
+ {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
+ {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
+ {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
+ {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
+ {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
+ {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
+ {0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
+ {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
+ {0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
+ {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
+ {0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
+ {0x27,0x03,T|R,"Associated write protect"},
+ {0x27,0x04,T|R,"Persistent write protect"},
+ {0x27,0x05,T|R,"Permanent write protect"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
+ {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
+ {0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
+ {0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
+ {0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
+ {0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
+ {0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
+ {0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
+ {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
+ {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
+ {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
{0x2C,0x01,S,"Too many windows specified"},
{0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2C,0x03,R,"Current program area is not empty"},
+ {0x2C,0x04,R,"Current program area is empty"},
+ {0x2C,0x05,B,"Illegal power condition request"},
{0x2D,0x00,T,"Overwrite error on update in place"},
- {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
- {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
- {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
- {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
- {0x30,0x03,D|T,"Cleaning cartridge installed"},
- {0x31,0x00,D|T|W|O,"Medium format corrupted"},
- {0x31,0x01,D|L|O,"Format command failed"},
- {0x32,0x00,D|W|O,"No defect spare location available"},
- {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
+ {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
+ {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
+ {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
+ {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
+ {0x30,0x08,R,"Cannot write - application code mismatch"},
+ {0x30,0x09,R,"Current session not fixated for append"},
+ {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
+ {0x31,0x01,D|L|R|O|B,"Format command failed"},
+ {0x32,0x00,D|W|O|B|K,"No defect spare location available"},
+ {0x32,0x01,D|W|O|B|K,"Defect list update failure"},
{0x33,0x00,T,"Tape length error"},
- {0x36,0x00,L,"Ribbon, ink, or toner failure"},
- {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
- {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
- {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
+ {0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
+ {0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
+ {0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
+ {0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
+ {0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
+ {0x36,0x00,L,"Ribbon,ink,or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
+ {0x38,0x00,B,"Event status notification"},
+ {0x38,0x02,B,"Esn - power management class event"},
+ {0x38,0x04,B,"Esn - media class event"},
+ {0x38,0x06,B,"Esn - device busy class event"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
+ {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
+ {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
+ {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
+ {0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
{0x3B,0x00,T|L,"Sequential positioning error"},
{0x3B,0x01,T,"Tape position error at beginning-of-medium"},
{0x3B,0x02,T,"Tape position error at end-of-medium"},
@@ -329,57 +415,244 @@ static struct error_info additional[] =
{0x3B,0x09,S,"Read past end of medium"},
{0x3B,0x0A,S,"Read past beginning of medium"},
{0x3B,0x0B,S,"Position past end of medium"},
- {0x3B,0x0C,S,"Position past beginning of medium"},
- {0x3B,0x0D,M,"Medium destination element full"},
- {0x3B,0x0E,M,"Medium source element empty"},
- {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
- {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
- {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
- {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
- {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
- {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
- {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
- {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
- {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
- {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
- {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
- {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
- {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
- {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
- {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
- {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
- {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x3B,0x0C,T|S,"Position past beginning of medium"},
+ {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
+ {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
+ {0x3B,0x0F,R,"End of medium reached"},
+ {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
+ {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
+ {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
+ {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
+ {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
+ {0x3B,0x16,R,"Mechanical positioning or changer error"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
+ {0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
+ {0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
+ {0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
+ {0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
+ {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
+ {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
+ {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
+ {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
+ {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
+ {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
+ {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
+ {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
+ {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
+ {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
+ {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
+ {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
+ {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
+ {0x40,0x00,D,"Ram failure (should use 40 nn)"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
+ {0x41,0x00,D,"Data path failure (should use 40 nn)"},
+ {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
+ {0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
+ {0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
+ {0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
+ {0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
{0x50,0x00,T,"Write append error"},
{0x50,0x01,T,"Write append position error"},
{0x50,0x02,T,"Position error related to timing"},
- {0x51,0x00,T|O,"Erase failure"},
+ {0x51,0x00,T|R|O,"Erase failure"},
{0x52,0x00,T,"Cartridge fault"},
- {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
{0x53,0x01,T,"Unload tape failure"},
- {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
{0x54,0x00,P,"Scsi to host system interface failure"},
{0x55,0x00,P,"System resource failure"},
+ {0x55,0x01,D|O|B|K,"System buffer full"},
+ {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
+ {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
+ {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
{0x57,0x00,R,"Unable to recover table-of-contents"},
{0x58,0x00,O,"Generation does not exist"},
{0x59,0x00,O,"Updated block read"},
- {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
- {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
- {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
- {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
- {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
- {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
- {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
- {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
+ {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
{0x5C,0x00,D|O,"Rpl status change"},
{0x5C,0x01,D|O,"Spindles synchronized"},
{0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
+ {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
+ {0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
+ {0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
+ {0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
+ {0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
+ {0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
+ {0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
+ {0x5D,0x15,D|B,"Hardware impending failure access times too high"},
+ {0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
+ {0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
+ {0x5D,0x18,D|B,"Hardware impending failure controller detected"},
+ {0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
+ {0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
+ {0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
+ {0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
+ {0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
+ {0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
+ {0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
+ {0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
+ {0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
+ {0x5D,0x25,D|B,"Controller impending failure access times too high"},
+ {0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
+ {0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
+ {0x5D,0x28,D|B,"Controller impending failure controller detected"},
+ {0x5D,0x29,D|B,"Controller impending failure throughput performance"},
+ {0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
+ {0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
+ {0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
+ {0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
+ {0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
+ {0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
+ {0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
+ {0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
+ {0x5D,0x35,D|B,"Data channel impending failure access times too high"},
+ {0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
+ {0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
+ {0x5D,0x38,D|B,"Data channel impending failure controller detected"},
+ {0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
+ {0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
+ {0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
+ {0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
+ {0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
+ {0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
+ {0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
+ {0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
+ {0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
+ {0x5D,0x45,D|B,"Servo impending failure access times too high"},
+ {0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
+ {0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
+ {0x5D,0x48,D|B,"Servo impending failure controller detected"},
+ {0x5D,0x49,D|B,"Servo impending failure throughput performance"},
+ {0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
+ {0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
+ {0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
+ {0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
+ {0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
+ {0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
+ {0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
+ {0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
+ {0x5D,0x55,D|B,"Spindle impending failure access times too high"},
+ {0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
+ {0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
+ {0x5D,0x58,D|B,"Spindle impending failure controller detected"},
+ {0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
+ {0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
+ {0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
+ {0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
+ {0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
+ {0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
+ {0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
+ {0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
+ {0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
+ {0x5D,0x65,D|B,"Firmware impending failure access times too high"},
+ {0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
+ {0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
+ {0x5D,0x68,D|B,"Firmware impending failure controller detected"},
+ {0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
+ {0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
+ {0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
+ {0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
+ {0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
+ {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
+ {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
+ {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
+ {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
+ {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
+ {0x5E,0x41,B,"Power state change to active"},
+ {0x5E,0x42,B,"Power state change to idle"},
+ {0x5E,0x43,B,"Power state change to standby"},
+ {0x5E,0x45,B,"Power state change to sleep"},
+ {0x5E,0x47,B|K,"Power state change to device control"},
{0x60,0x00,S,"Lamp failure"},
{0x61,0x00,S,"Video acquisition error"},
{0x61,0x01,S,"Unable to acquire video"},
{0x61,0x02,S,"Out of focus"},
{0x62,0x00,S,"Scan head positioning error"},
{0x63,0x00,R,"End of user area encountered on this track"},
+ {0x63,0x01,R,"Packet does not fit in available space"},
{0x64,0x00,R,"Illegal mode for this track"},
+ {0x64,0x01,R,"Invalid packet size"},
+ {0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
+ {0x66,0x00,S,"Automatic document feeder cover up"},
+ {0x66,0x01,S,"Automatic document feeder lift up"},
+ {0x66,0x02,S,"Document jam in automatic document feeder"},
+ {0x66,0x03,S,"Document miss feed automatic in document feeder"},
+ {0x67,0x00,A,"Configuration failure"},
+ {0x67,0x01,A,"Configuration of incapable logical units failed"},
+ {0x67,0x02,A,"Add logical unit failed"},
+ {0x67,0x03,A,"Modification of logical unit failed"},
+ {0x67,0x04,A,"Exchange of logical unit failed"},
+ {0x67,0x05,A,"Remove of logical unit failed"},
+ {0x67,0x06,A,"Attachment of logical unit failed"},
+ {0x67,0x07,A,"Creation of logical unit failed"},
+ {0x67,0x08,A,"Assign failure occurred"},
+ {0x67,0x09,A,"Multiply assigned logical unit"},
+ {0x68,0x00,A,"Logical unit not configured"},
+ {0x69,0x00,A,"Data loss on logical unit"},
+ {0x69,0x01,A,"Multiple logical unit failures"},
+ {0x69,0x02,A,"Parity/data mismatch"},
+ {0x6A,0x00,A,"Informational,refer to log"},
+ {0x6B,0x00,A,"State change has occurred"},
+ {0x6B,0x01,A,"Redundancy level got better"},
+ {0x6B,0x02,A,"Redundancy level got worse"},
+ {0x6C,0x00,A,"Rebuild failure occurred"},
+ {0x6D,0x00,A,"Recalculate failure occurred"},
+ {0x6E,0x00,A,"Command to logical unit failed"},
+ {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
+ {0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
+ {0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
+ {0x6F,0x03,R,"Read of scrambled sector without authentication"},
+ {0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
+ {0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x70,0x00,T,"Decompression exception short algorithm id of nn"},
+ {0x71,0x00,T,"Decompression exception long algorithm id"},
+ {0x72,0x00,R,"Session fixation error"},
+ {0x72,0x01,R,"Session fixation error writing lead-in"},
+ {0x72,0x02,R,"Session fixation error writing lead-out"},
+ {0x72,0x03,R,"Session fixation error - incomplete track in session"},
+ {0x72,0x04,R,"Empty or partially written reserved track"},
+ {0x72,0x05,R,"No more track reservations allowed"},
+ {0x73,0x00,R,"Cd control error"},
+ {0x73,0x01,R,"Power calibration area almost full"},
+ {0x73,0x02,R,"Power calibration area is full"},
+ {0x73,0x03,R,"Power calibration area error"},
+ {0x73,0x04,R,"Program memory area update failure"},
+ {0x73,0x05,R,"Program memory area is full"},
+ {0x73,0x06,R,"RMA/PMA is full"},
{0, 0, 0, NULL}
};
#endif
diff --git a/drivers/scsi/eata_dma_proc.c b/drivers/scsi/eata_dma_proc.c
index 8768db48c..7961483e1 100644
--- a/drivers/scsi/eata_dma_proc.c
+++ b/drivers/scsi/eata_dma_proc.c
@@ -69,7 +69,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
Scsi_Device *scd, *SDev;
struct Scsi_Host *HBA_ptr;
- Scsi_Cmnd * scmd;
+ Scsi_Request * scmd;
char cmnd[MAX_COMMAND_SIZE];
static u8 buff[512];
static u8 buff2[512];
@@ -153,7 +153,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
} else {
SDev = scsi_get_host_dev(HBA_ptr);
- scmd = scsi_allocate_device(SDev, 1, FALSE);
+ scmd = scsi_allocate_request(SDev);
cmnd[0] = LOG_SENSE;
cmnd[1] = 0;
@@ -166,13 +166,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
cmnd[8] = 0x66;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,
+ scsi_wait_req (scmd, cmnd, buff + 0x144, 0x66,
1 * HZ, 1);
size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
@@ -291,13 +291,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
cmnd[8] = 0x44;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+ scsi_wait_req (scmd, cmnd, buff2, 0x144,
1 * HZ, 1);
swap_statistics(buff2);
@@ -333,7 +333,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
pos = begin + len;
}
- scsi_release_command(scmd);
+ scsi_release_request(scmd);
scsi_free_host_dev(SDev);
}
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index f1b82a5a1..685925e7d 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -469,10 +469,10 @@ extern void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt);
* Prototypes for functions/data in scsi_scan.c
*/
extern void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun);
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun);
extern void scsi_mark_host_reset(struct Scsi_Host *Host);
@@ -485,12 +485,12 @@ struct Scsi_Device_Template
const char * tag;
struct module * module; /* Used for loadable modules */
unsigned char scsi_type;
- unsigned char major;
- unsigned char min_major; /* Minimum major in range. */
- unsigned char max_major; /* Maximum major in range. */
- unsigned char nr_dev; /* Number currently attached */
- unsigned char dev_noticed; /* Number of devices detected. */
- unsigned char dev_max; /* Current size of arrays */
+ unsigned int major;
+ unsigned int min_major; /* Minimum major in range. */
+ unsigned int max_major; /* Maximum major in range. */
+ unsigned int nr_dev; /* Number currently attached */
+ unsigned int dev_noticed; /* Number of devices detected. */
+ unsigned int dev_max; /* Current size of arrays */
unsigned blk:1; /* 0 if character device */
int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
int (*init)(void); /* Sizes arrays based upon number of devices
@@ -534,20 +534,18 @@ extern void scsi_unregister_module(int, void *);
* Note: These things are all evil and all need to go away. My plan is to
* tackle the character devices first, as there aren't any locking implications
* in the block device layer. The block devices will require more work.
+ *
+ * The generics driver has been updated to resize as required. So as the tape
+ * driver. Two down, two more to go.
*/
#ifndef CONFIG_SD_EXTRA_DEVS
#define CONFIG_SD_EXTRA_DEVS 2
#endif
-#ifndef CONFIG_ST_EXTRA_DEVS
-#define CONFIG_ST_EXTRA_DEVS 2
-#endif
#ifndef CONFIG_SR_EXTRA_DEVS
#define CONFIG_SR_EXTRA_DEVS 2
#endif
#define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
-#define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS
#define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
-#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
#endif
/*
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index d2943f103..5fc1bfaab 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -73,7 +73,7 @@
*/
/*
-** January 8 2000, version 3.2e
+** March 6 2000, version 3.2g
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -104,7 +104,7 @@
/*
** Name and version of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2e"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2g"
#define SCSI_NCR_DEBUG_FLAGS (0)
@@ -141,11 +141,6 @@
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/stat.h>
-#ifdef __mips__
-#include <asm/bootinfo.h>
-#include <asm/pgtable.h>
-#include <asm/sni.h>
-#endif /* __mips__ */
#include <linux/version.h>
#include <linux/blk.h>
@@ -189,98 +184,14 @@
*/
typedef u32 u_int32;
typedef u64 u_int64;
-
+typedef u_long vm_offset_t;
#include "ncr53c8xx.h"
-/*==========================================================
-**
-** A la VMS/CAM-3 queue management.
-** Implemented from linux list management.
-**
-**==========================================================
-*/
-
-typedef struct xpt_quehead {
- struct xpt_quehead *flink; /* Forward pointer */
- struct xpt_quehead *blink; /* Backward pointer */
-} XPT_QUEHEAD;
-
-#define xpt_que_init(ptr) do { \
- (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
-} while (0)
-
-static inline void __xpt_que_add(struct xpt_quehead * new,
- struct xpt_quehead * blink,
- struct xpt_quehead * flink)
-{
- flink->blink = new;
- new->flink = flink;
- new->blink = blink;
- blink->flink = new;
-}
-
-static inline void __xpt_que_del(struct xpt_quehead * blink,
- struct xpt_quehead * flink)
-{
- flink->blink = blink;
- blink->flink = flink;
-}
-
-static inline int xpt_que_empty(struct xpt_quehead *head)
-{
- return head->flink == head;
-}
-
-static inline void xpt_que_splice(struct xpt_quehead *list,
- struct xpt_quehead *head)
-{
- struct xpt_quehead *first = list->flink;
-
- if (first != list) {
- struct xpt_quehead *last = list->blink;
- struct xpt_quehead *at = head->flink;
+#define NAME53C "ncr53c"
+#define NAME53C8XX "ncr53c8xx"
+#define DRIVER_SMP_LOCK ncr53c8xx_lock
- first->blink = head;
- head->flink = first;
-
- last->flink = at;
- at->blink = last;
- }
-}
-
-#define xpt_que_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-
-#define xpt_insque(new, pos) __xpt_que_add(new, pos, (pos)->flink)
-
-#define xpt_remque(el) __xpt_que_del((el)->blink, (el)->flink)
-
-#define xpt_insque_head(new, head) __xpt_que_add(new, head, (head)->flink)
-
-static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
-{
- struct xpt_quehead *elem = head->flink;
-
- if (elem != head)
- __xpt_que_del(head, elem->flink);
- else
- elem = 0;
- return elem;
-}
-
-#define xpt_insque_tail(new, head) __xpt_que_add(new, (head)->blink, head)
-
-static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
-{
- struct xpt_quehead *elem = head->blink;
-
- if (elem != head)
- __xpt_que_del(elem->blink, head);
- else
- elem = 0;
- return elem;
-}
+#include "sym53c8xx_comm.h"
/*==========================================================
**
@@ -320,33 +231,6 @@ static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
/*==========================================================
**
-** On x86 architecture, write buffers management does
-** not reorder writes to memory. So, using compiler
-** optimization barriers is enough to guarantee some
-** ordering when the CPU is writing data accessed by
-** the NCR.
-** On Alpha architecture, explicit memory barriers have
-** to be used.
-** Other architectures are defaulted to mb() macro if
-** defined, otherwise use compiler barrier.
-**
-**==========================================================
-*/
-
-#if defined(__i386__)
-#define MEMORY_BARRIER() barrier()
-#elif defined(__alpha__)
-#define MEMORY_BARRIER() mb()
-#else
-# ifdef mb
-# define MEMORY_BARRIER() mb()
-# else
-# define MEMORY_BARRIER() barrier()
-# endif
-#endif
-
-/*==========================================================
-**
** Configuration and Debugging
**
**==========================================================
@@ -469,381 +353,11 @@ typedef u_int32 tagmap_t;
#endif
/*
-** Io mapped or memory mapped.
-*/
-
-#if defined(SCSI_NCR_IOMAPPED)
-#define NCR_IOMAPPED
-#endif
-
-/*
** other
*/
#define NCR_SNOOP_TIMEOUT (1000000)
-/*==========================================================
-**
-** Defines for Linux.
-**
-** Linux and Bsd kernel functions are quite different.
-** These defines allow a minimum change of the original
-** code.
-**
-**==========================================================
-*/
-
- /*
- ** Obvious definitions
- */
-
-#define u_char unsigned char
-#define u_short unsigned short
-#define u_int unsigned int
-#define u_long unsigned long
-
-typedef u_long vm_offset_t;
-typedef int vm_size_t;
-
-#ifndef bcopy
-#define bcopy(s, d, n) memcpy((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(d, n) memset((d), 0, (n))
-#endif
-
-#ifndef offsetof
-#define offsetof(t, m) ((size_t) (&((t *)0)->m))
-#endif
-
-/*
-** Simple Wrapper to kernel PCI bus interface.
-**
-** This wrapper allows to get rid of old kernel PCI interface
-** and still allows to preserve linux-2.0 compatibilty.
-** In fact, it is mostly an incomplete emulation of the new
-** PCI code for pre-2.2 kernels. When kernel-2.0 support
-** will be dropped, we will just have to remove most of this
-** code.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
-
-typedef struct pci_dev *pcidev_t;
-#define PCIDEV_NULL (0)
-#define PciBusNumber(d) (d)->bus->number
-#define PciDeviceFn(d) (d)->devfn
-#define PciVendorId(d) (d)->vendor
-#define PciDeviceId(d) (d)->device
-#define PciIrqLine(d) (d)->irq
-
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
-
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
- *base = pdev->resource[index].start;
- if ((pdev->resource[index].flags & 0x7) == 0x4)
- ++index;
- return ++index;
-}
-#else
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
- *base = pdev->base_address[index++];
- if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
- *base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
- ++index;
- }
- return index;
-}
-#endif
-
-#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */
-
-typedef unsigned int pcidev_t;
-#define PCIDEV_NULL (~0u)
-#define PciBusNumber(d) ((d)>>8)
-#define PciDeviceFn(n) ((d)&0xff)
-#define __PciDev(busn, devfn) (((busn)<<8)+(devfn))
-
-#define pci_present pcibios_present
-
-#define pci_read_config_byte(d, w, v) \
- pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_word(d, w, v) \
- pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_dword(d, w, v) \
- pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-
-#define pci_write_config_byte(d, w, v) \
- pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_word(d, w, v) \
- pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_dword(d, w, v) \
- pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-static pcidev_t __init
-pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
-{
- static unsigned short pci_index;
- int retv;
- unsigned char bus_number, device_fn;
-
- if (prev == PCIDEV_NULL)
- pci_index = 0;
- else
- ++pci_index;
- retv = pcibios_find_device (vendor, device, pci_index,
- &bus_number, &device_fn);
- return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
-}
-
-static u_short __init PciVendorId(pcidev_t dev)
-{
- u_short vendor_id;
- pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
- return vendor_id;
-}
-
-static u_short __init PciDeviceId(pcidev_t dev)
-{
- u_short device_id;
- pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
- return device_id;
-}
-
-static u_int __init PciIrqLine(pcidev_t dev)
-{
- u_short irq;
- pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
- return irq;
-}
-
-static int __init
-pci_get_base_address(pcidev_t dev, int offset, u_long *base)
-{
- u_int32 tmp;
-
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
- *base = tmp;
- offset += sizeof(u_int32);
- if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
- *base |= (((u_long)tmp) << 32);
-#endif
- offset += sizeof(u_int32);
- }
- return offset;
-}
-
-#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
-
-/*
-** SMP threading.
-**
-** Assuming that SMP systems are generally high end systems and may
-** use several SCSI adapters, we are using one lock per controller
-** instead of some global one. For the moment (linux-2.1.95), driver's
-** entry points are called with the 'io_request_lock' lock held, so:
-** - We are uselessly loosing a couple of micro-seconds to lock the
-** controller data structure.
-** - But the driver is not broken by design for SMP and so can be
-** more resistant to bugs or bad changes in the IO sub-system code.
-** - A small advantage could be that the interrupt code is grained as
-** wished (e.g.: threaded by controller).
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
-
-#if 0 /* not yet needed */
-static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
-#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&driver_lock, flags)
-#define NCR_UNLOCK_DRIVER(flags) spin_unlock_irqrestore(&driver_lock, flags)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np) spin_lock_init(&np->smp_lock);
-#define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags)
-#define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags)
-
-#define NCR_LOCK_SCSI_DONE(np, flags) \
- spin_lock_irqsave(&io_request_lock, flags)
-#define NCR_UNLOCK_SCSI_DONE(np, flags) \
- spin_unlock_irqrestore(&io_request_lock, flags)
-
-#else
-
-#if 0 /* not yet needed */
-#define NCR_LOCK_DRIVER(flags) do {;} while (0)
-#define NCR_UNLOCK_DRIVER(flags) do {;} while (0)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np) do { } while (0)
-#define NCR_LOCK_NCB(np, flags) do { save_flags(flags); cli(); } while (0)
-#define NCR_UNLOCK_NCB(np, flags) do { restore_flags(flags); } while (0)
-
-#define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0)
-#define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0)
-
-#endif
-
-/*
-** Address translation
-**
-** The driver has to provide physical memory addresses to
-** the script processor. Because some architectures use
-** different physical addresses from the PCI BUS, we must
-** use virt_to_bus instead of virt_to_phys.
-**
-** FIXME: Bus addresses are _not_ physical addresses.
-*/
-
-#define vtophys(p) virt_to_bus(p)
-
-/*
-** Memory mapped IO
-**
-** Since linux-2.1, we must use ioremap() to map the io memory space.
-** iounmap() to unmap it. That allows portability.
-** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater
-** than the highest physical memory address to kernel virtual pages with
-** vremap() / vfree(). That was not portable but worked with i386
-** architecture.
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
-#define ioremap vremap
-#define iounmap vfree
-#endif
-
-#if defined (__sparc__)
-#include <asm/irq.h>
-#elif defined (__alpha__)
-#define bus_dvma_to_mem(p) ((p) & 0xfffffffful)
-#else
-#define bus_dvma_to_mem(p) (p)
-#endif
-
-#if defined(__i386__) || !defined(NCR_IOMAPPED)
-static vm_offset_t __init remap_pci_mem(u_long base, u_long size)
-{
- u_long page_base = ((u_long) base) & PAGE_MASK;
- u_long page_offs = ((u_long) base) - page_base;
- u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
-
- return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL);
-}
-
-static void __init unmap_pci_mem(vm_offset_t vaddr, u_long size)
-{
- if (vaddr)
- iounmap((void *) (vaddr & PAGE_MASK));
-}
-#endif /* __i386__ || !NCR_IOMAPPED */
-
-/*
-** Insert a delay in micro-seconds and milli-seconds.
-** -------------------------------------------------
-** Under Linux, udelay() is restricted to delay < 1 milli-second.
-** In fact, it generally works for up to 1 second delay.
-** Since 2.1.105, the mdelay() function is provided for delays
-** in milli-seconds.
-** Under 2.0 kernels, udelay() is an inline function that is very
-** inaccurate on Pentium processors.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
-#define UDELAY udelay
-#define MDELAY mdelay
-#else
-static void UDELAY(long us) { udelay(us); }
-static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
-#endif
-
-/*
-** Internal data structure allocation.
-**
-** Linux scsi memory poor pool is adjusted for the need of
-** middle-level scsi driver.
-** We allocate our control blocks in the kernel memory pool
-** to avoid scsi pool shortage.
-**
-** kmalloc() only ensures 8 bytes boundary alignment.
-** The NCR need better alignment for cache line bursting.
-** The global header is moved between the NCB and CCBs and needs
-** origin and destination addresses to have same lower four bits.
-**
-** We use 32 boundary alignment for NCB and CCBs and offset multiple
-** of 32 for global header fields. That's too much but at least enough.
-*/
-
-#define ALIGN_SIZE(shift) (1UL << shift)
-#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1))
-
-#define CACHE_LINE_SHIFT 5
-#define CACHE_LINE_SIZE ALIGN_SIZE(CACHE_LINE_SHIFT)
-#define CACHE_LINE_MASK ALIGN_MASK(CACHE_LINE_SHIFT)
-
-static void *m_alloc(int size, int a_shift)
-{
- u_long addr;
- void *ptr;
- u_long a_size, a_mask;
-
- if (a_shift < 3)
- a_shift = 3;
-
- a_size = ALIGN_SIZE(a_shift);
- a_mask = ALIGN_MASK(a_shift);
-
- ptr = (void *) kmalloc(size + a_size, GFP_UNCACHED | GFP_ATOMIC);
- if (ptr) {
- addr = (((u_long) ptr) + a_size) & a_mask;
- *((void **) (addr - sizeof(void *))) = ptr;
- ptr = (void *) addr;
- }
-
- return ptr;
-}
-
-#ifdef MODULE
-static void m_free(void *ptr, int size)
-{
- u_long addr;
-
- if (ptr) {
- addr = (u_long) ptr;
- ptr = *((void **) (addr - sizeof(void *)));
-
- kfree(ptr);
- }
-}
-#endif
-
-/*
-** Transfer direction
-**
-** Low-level scsi drivers under Linux do not receive the expected
-** data transfer direction from upper scsi drivers.
-** The driver will only check actual data direction for common
-** scsi opcodes. Other ones may cause problem, since they may
-** depend on device type or be vendor specific.
-** I would prefer to never trust the device for data direction,
-** but that is not possible.
-**
-** The original driver requires the expected direction to be known.
-** The Linux version of the driver has been enhanced in order to
-** be able to transfer data in the direction choosen by the target.
-*/
-
-#define XFER_IN (1)
-#define XFER_OUT (2)
-
/*
** Head of list of NCR boards
**
@@ -855,44 +369,8 @@ static void m_free(void *ptr, int size)
static struct Scsi_Host *first_host = NULL;
static Scsi_Host_Template *the_template = NULL;
-
-/*
-** /proc directory entry and proc_info function
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
-static struct proc_dir_entry proc_scsi_ncr53c8xx = {
- PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-#endif
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int func);
-#endif
-
/*
-** Driver setup.
-**
-** This structure is initialized from linux config options.
-** It can be overridden at boot-up by the boot command line.
-*/
-static struct ncr_driver_setup
- driver_setup = SCSI_NCR_DRIVER_SETUP;
-
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-static struct ncr_driver_setup
- driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
-# ifdef MODULE
-char *ncr53c8xx = 0; /* command line passed by insmod */
-# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(ncr53c8xx, "s");
-# endif
-# endif
-#endif
-
-/*
-** Other Linux definitions
+** Other definitions
*/
#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
@@ -903,273 +381,13 @@ static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
static void ncr53c8xx_timeout(unsigned long np);
#define initverbose (driver_setup.verbose)
-#define bootverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
#ifdef SCSI_NCR_NVRAM_SUPPORT
static u_char Tekram_sync[16] __initdata =
{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
#endif /* SCSI_NCR_NVRAM_SUPPORT */
-/*
-** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
-** transmit device configuration to the ncr_attach() function.
-*/
-typedef struct {
- int bus;
- u_char device_fn;
- u_long base;
- u_long base_2;
- u_long io_port;
- int irq;
-/* port and reg fields to use INB, OUTB macros */
- u_long port;
- volatile struct ncr_reg *reg;
-} ncr_slot;
-
-typedef struct {
- int type;
-#define SCSI_NCR_SYMBIOS_NVRAM (1)
-#define SCSI_NCR_TEKRAM_NVRAM (2)
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- union {
- Symbios_nvram Symbios;
- Tekram_nvram Tekram;
- } data;
-#endif
-} ncr_nvram;
-
-/*
-** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
-** to save data on each detected board for ncr_attach().
-*/
-typedef struct {
- ncr_slot slot;
- ncr_chip chip;
- ncr_nvram *nvram;
- u_char host_id;
- int attach_done;
-} ncr_device;
-
-/*==========================================================
-**
-** Debugging tags
-**
-**==========================================================
-*/
-
-#define DEBUG_ALLOC (0x0001)
-#define DEBUG_PHASE (0x0002)
-#define DEBUG_QUEUE (0x0008)
-#define DEBUG_RESULT (0x0010)
-#define DEBUG_SCATTER (0x0020)
-#define DEBUG_SCRIPT (0x0040)
-#define DEBUG_TINY (0x0080)
-#define DEBUG_TIMING (0x0100)
-#define DEBUG_NEGO (0x0200)
-#define DEBUG_TAGS (0x0400)
-
-/*
-** Enable/Disable debug messages.
-** Can be changed at runtime too.
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
- #define DEBUG_FLAGS ncr_debug
-#else
- #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
-#endif
-
-
-
-/*==========================================================
-**
-** assert ()
-**
-**==========================================================
-**
-** modified copy from 386bsd:/usr/include/sys/assert.h
-**
-**----------------------------------------------------------
-*/
-
-#define assert(expression) { \
- if (!(expression)) { \
- (void)printk(KERN_ERR \
- "assertion \"%s\" failed: file \"%s\", line %d\n", \
- #expression, \
- __FILE__, __LINE__); \
- } \
-}
-
-/*==========================================================
-**
-** Big/Little endian support.
-**
-**==========================================================
-*/
-
-/*
-** If the NCR uses big endian addressing mode over the
-** PCI, actual io register addresses for byte and word
-** accesses must be changed according to lane routing.
-** Btw, ncr_offb() and ncr_offw() macros only apply to
-** constants and so donnot generate bloated code.
-*/
-
-#if defined(SCSI_NCR_BIG_ENDIAN)
-
-#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
-#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
-
-#else
-
-#define ncr_offb(o) (o)
-#define ncr_offw(o) (o)
-
-#endif
-
-/*
-** If the CPU and the NCR use same endian-ness addressing,
-** no byte reordering is needed for script patching.
-** Macro cpu_to_scr() is to be used for script patching.
-** Macro scr_to_cpu() is to be used for getting a DWORD
-** from the script.
-*/
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw) cpu_to_le32(dw)
-#define scr_to_cpu(dw) le32_to_cpu(dw)
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw) cpu_to_be32(dw)
-#define scr_to_cpu(dw) be32_to_cpu(dw)
-
-#else
-
-#define cpu_to_scr(dw) (dw)
-#define scr_to_cpu(dw) (dw)
-
-#endif
-
-/*==========================================================
-**
-** Access to the controller chip.
-**
-** If NCR_IOMAPPED is defined, the driver will use
-** normal IOs instead of the MEMORY MAPPED IO method
-** recommended by PCI specifications.
-** If all PCI bridges, host brigdes and architectures
-** would have been correctly designed for PCI, this
-** option would be useless.
-**
-**==========================================================
-*/
-
-/*
-** If the CPU and the NCR use same endian-ness addressing,
-** no byte reordering is needed for accessing chip io
-** registers. Functions suffixed by '_raw' are assumed
-** to access the chip over the PCI without doing byte
-** reordering. Functions suffixed by '_l2b' are
-** assumed to perform little-endian to big-endian byte
-** reordering, those suffixed by '_b2l' blah, blah,
-** blah, ...
-*/
-
-#if defined(NCR_IOMAPPED)
-
-/*
-** IO mapped only input / ouput
-*/
-
-#define INB_OFF(o) inb (np->port + ncr_offb(o))
-#define OUTB_OFF(o, val) outb ((val), np->port + ncr_offb(o))
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) inw_l2b (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_l2b (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_b2l ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_b2l ((val), np->port + (o))
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) inw_b2l (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_b2l (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_l2b ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_l2b ((val), np->port + (o))
-
-#else
-
-#define INW_OFF(o) inw_raw (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_raw (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_raw ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_raw ((val), np->port + (o))
-
-#endif /* ENDIANs */
-
-#else /* defined NCR_IOMAPPED */
-
-/*
-** MEMORY mapped IO input / output
-*/
-
-#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
-#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
-
-#else
-
-#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_raw((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
-
-#endif
-
-#endif /* defined NCR_IOMAPPED */
-
-#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
-#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
-#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
-
-#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
-
-/*
-** Set bit field ON, OFF
-*/
-
-#define OUTONB(r, m) OUTB(r, INB(r) | (m))
-#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
-#define OUTONW(r, m) OUTW(r, INW(r) | (m))
-#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
-#define OUTONL(r, m) OUTL(r, INL(r) | (m))
-#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
-
-
/*==========================================================
**
** Command control block states.
@@ -1765,6 +983,8 @@ struct ccb {
**----------------------------------------------------------------
*/
Scsi_Cmnd *cmd; /* SCSI command */
+ u_char cdb_buf[16]; /* Copy of CDB */
+ u_char sense_buf[64];
int data_len; /* Total data length */
/*----------------------------------------------------------------
@@ -1895,6 +1115,7 @@ struct ncb {
** General controller parameters and configuration.
**----------------------------------------------------------------
*/
+ pcidev_t pdev;
u_short device_id; /* PCI device id */
u_char revision_id; /* PCI device revision id */
u_char bus; /* PCI BUS number */
@@ -1963,6 +1184,7 @@ struct ncb {
u_char order; /* Tag order to use */
u_char verbose; /* Verbosity for this controller*/
int ncr_cache; /* Used for cache test at init. */
+ u_long p_ncb; /* BUS address of this NCB */
/*----------------------------------------------------------------
** Command completion handling.
@@ -1976,6 +1198,9 @@ struct ncb {
** Fields that should be removed or changed.
**----------------------------------------------------------------
*/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ u_long ktime; /* Copy of kernel time */
+#endif
struct ccb *ccb; /* Global CCB */
struct usrcmd user; /* Command from user */
u_char release_stage; /* Synchronisation stage on release */
@@ -2165,7 +1390,7 @@ static void ncb_profile (ncb_p np, ccb_p cp);
static void ncr_script_copy_and_bind
(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
static void ncr_script_fill (struct script * scr, struct scripth * scripth);
-static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
+static int ncr_scatter (ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln);
@@ -2195,25 +1420,6 @@ static void process_waiting_list(ncb_p np, int sts);
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static void ncr_get_nvram (ncr_device *devp, ncr_nvram *nvp);
-static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
-static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
-#endif
-
-/*==========================================================
-**
-**
-** Global static data.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
-#endif
-
static inline char *ncr_name (ncb_p np)
{
return np->inst_name;
@@ -2242,7 +1448,9 @@ static inline char *ncr_name (ncb_p np)
#define RELOC_SOFTC 0x40000000
#define RELOC_LABEL 0x50000000
#define RELOC_REGISTER 0x60000000
+#if 0
#define RELOC_KVAR 0x70000000
+#endif
#define RELOC_LABELH 0x80000000
#define RELOC_MASK 0xf0000000
@@ -2251,19 +1459,21 @@ static inline char *ncr_name (ncb_p np)
#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
#define RADDR(label) (RELOC_REGISTER | REG(label))
#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#if 0
#define KVAR(which) (RELOC_KVAR | (which))
+#endif
+#if 0
#define SCRIPT_KVAR_JIFFIES (0)
-
#define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES
#define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES
-
/*
* Kernel variables referenced in the scripts.
* THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
*/
static void *script_kvars[] __initdata =
{ (void *)&jiffies };
+#endif
static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
@@ -2434,7 +1644,7 @@ static struct script script0 __initdata = {
** ... set a timestamp ...
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.command),
#endif
/*
@@ -2547,7 +1757,7 @@ static struct script script0 __initdata = {
** set the timestamp.
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.status),
#endif
/*
@@ -2783,7 +1993,7 @@ static struct script script0 __initdata = {
** and count the disconnects.
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.disconnect),
SCR_COPY (4),
NADDR (disc_phys),
@@ -2939,7 +2149,7 @@ static struct script script0 __initdata = {
** Set a time stamp for this reselection
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.reselect),
#endif
/*
@@ -3777,7 +2987,6 @@ void __init ncr_script_fill (struct script * scr, struct scripth * scrh)
};
assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
-flush_cache_all();
}
/*==========================================================
@@ -3833,11 +3042,15 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
*/
relocs = 2;
tmp1 = src[0];
+#ifdef RELOC_KVAR
if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
tmp1 = 0;
+#endif
tmp2 = src[1];
+#ifdef RELOC_KVAR
if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
tmp2 = 0;
+#endif
if ((tmp1 ^ tmp2) & 3) {
printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
ncr_name(np), (int) (src-start-1));
@@ -3890,7 +3103,7 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
switch (old & RELOC_MASK) {
case RELOC_REGISTER:
new = (old & ~RELOC_MASK)
- + bus_dvma_to_mem(np->paddr);
+ + pcivtobus(np->paddr);
break;
case RELOC_LABEL:
new = (old & ~RELOC_MASK) + np->p_script;
@@ -3899,8 +3112,9 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
new = (old & ~RELOC_MASK) + np->p_scripth;
break;
case RELOC_SOFTC:
- new = (old & ~RELOC_MASK) + vtophys(np);
+ new = (old & ~RELOC_MASK) + np->p_ncb;
break;
+#ifdef RELOC_KVAR
case RELOC_KVAR:
if (((old & ~RELOC_MASK) <
SCRIPT_KVAR_FIRST) ||
@@ -3910,6 +3124,7 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
new = vtophys(script_kvars[old &
~RELOC_MASK]);
break;
+#endif
case 0:
/* Don't relocate a 0 address. */
if (old == 0) {
@@ -3928,7 +3143,6 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
*dst++ = cpu_to_scr(*src++);
};
-flush_cache_all();
}
/*==========================================================
@@ -3950,17 +3164,6 @@ flush_cache_all();
struct host_data {
struct ncb *ncb;
-
- char ncb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct ncb _ncb_data;
-
- char ccb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct ccb _ccb_data;
-
- char scr_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct script script_data;
-
- struct scripth scripth_data;
};
/*
@@ -4399,88 +3602,6 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
return 0;
}
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-
-void __init ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
-{
- int i;
-
- /* display Symbios nvram host data */
- printk(KERN_DEBUG "%s: HOST ID=%d%s%s%s%s%s\n",
- ncr_name(np), nvram->host_id & 0x0f,
- (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
- (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
- (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"",
- (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"",
- (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
-
- /* display Symbios nvram drive data */
- for (i = 0 ; i < 15 ; i++) {
- struct Symbios_target *tn = &nvram->target[i];
- printk(KERN_DEBUG "%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
- ncr_name(np), i,
- (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
- (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
- (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
- (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
- tn->bus_width,
- tn->sync_period / 4,
- tn->timeout);
- }
-}
-
-static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
-
-void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
-{
- int i, tags, boot_delay;
- char *rem;
-
- /* display Tekram nvram host data */
- tags = 2 << nvram->max_tags_index;
- boot_delay = 0;
- if (nvram->boot_delay_index < 6)
- boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
- switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
- default:
- case 0: rem = ""; break;
- case 1: rem = " REMOVABLE=boot device"; break;
- case 2: rem = " REMOVABLE=all"; break;
- }
-
- printk(KERN_DEBUG
- "%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
- ncr_name(np), nvram->host_id & 0x0f,
- (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
- (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
- (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
- (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
- (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
- (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
- (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
- (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
- rem, boot_delay, tags);
-
- /* display Tekram nvram drive data */
- for (i = 0; i <= 15; i++) {
- int sync, j;
- struct Tekram_target *tn = &nvram->target[i];
- j = tn->sync_index & 0xf;
- sync = Tekram_sync[j];
- printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
- ncr_name(np), i,
- (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
- (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
- (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
- (tn->flags & TEKRAM_START_CMD) ? " START" : "",
- (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
- (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
- sync);
- }
-}
-#endif /* SCSI_NCR_DEBUG_NVRAM */
-
/*
** Host attach and initialisations.
**
@@ -4495,7 +3616,7 @@ static int __init
ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
{
struct host_data *host_data;
- ncb_p np;
+ ncb_p np = 0;
struct Scsi_Host *instance = 0;
u_long flags = 0;
ncr_nvram *nvram = device->nvram;
@@ -4521,21 +3642,25 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
*/
if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
goto attach_error;
-
- /*
- ** Initialize structure.
- */
host_data = (struct host_data *) instance->hostdata;
- bzero (host_data, sizeof(*host_data));
/*
- ** Align np and first ccb to 32 boundary for cache line
- ** bursting when copying the global header.
+ ** Allocate the host control block.
*/
- np = (ncb_p) (((u_long) &host_data->_ncb_data) & CACHE_LINE_MASK);
+ np = __m_calloc_dma(device->pdev, sizeof(struct ncb), "NCB");
+ if (!np)
+ goto attach_error;
NCR_INIT_LOCK_NCB(np);
+ np->pdev = device->pdev;
+ np->p_ncb = vtobus(np);
host_data->ncb = np;
- np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CACHE_LINE_MASK);
+
+ /*
+ ** Allocate the default CCB.
+ */
+ np->ccb = (ccb_p) m_calloc_dma(sizeof(struct ccb), "CCB");
+ if (!np->ccb)
+ goto attach_error;
/*
** Store input informations in the host data structure.
@@ -4554,9 +3679,17 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
np->maxburst = device->chip.burst_max;
np->myaddr = device->host_id;
+ /*
+ ** Allocate SCRIPTS areas.
+ */
np->script0 = (struct script *)
- (((u_long) &host_data->script_data) & CACHE_LINE_MASK);
- np->scripth0 = &host_data->scripth_data;
+ m_calloc_dma(sizeof(struct script), "SCRIPT");
+ if (!np->script0)
+ goto attach_error;
+ np->scripth0 = (struct scripth *)
+ m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
+ if (!np->scripth0)
+ goto attach_error;
/*
** Initialize timer structure
@@ -4592,7 +3725,7 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
** can be used safely.
*/
- np->reg = virt_to_bus((struct ncr_reg*) np->vaddr);
+ np->reg = (struct ncr_reg*) np->vaddr;
#endif /* !defined NCR_IOMAPPED */
@@ -4608,12 +3741,12 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
switch(nvram->type) {
case SCSI_NCR_SYMBIOS_NVRAM:
#ifdef SCSI_NCR_DEBUG_NVRAM
- ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+ ncr_display_Symbios_nvram(&nvram->data.Symbios);
#endif
break;
case SCSI_NCR_TEKRAM_NVRAM:
#ifdef SCSI_NCR_DEBUG_NVRAM
- ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+ ncr_display_Tekram_nvram(&nvram->data.Tekram);
#endif
break;
default:
@@ -4665,13 +3798,14 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
ncr_script_fill (&script0, &scripth0);
np->scripth = np->scripth0;
- np->p_scripth = vtophys(np->scripth);
+ np->p_scripth = vtobus(np->scripth);
- np->p_script = (np->paddr2) ? bus_dvma_to_mem(np->paddr2) : vtophys(np->script0);
+ np->p_script = (np->paddr2) ?
+ pcivtobus(np->paddr2) : vtobus(np->script0);
ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
- np->ccb->p_ccb = vtophys (np->ccb);
+ np->ccb->p_ccb = vtobus (np->ccb);
/*
** Patch the script for LED support.
@@ -4807,6 +3941,8 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
attach_error:
if (!instance) return -1;
printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
+ if (!np)
+ goto unregister;
#ifndef NCR_IOMAPPED
if (np->vaddr) {
#ifdef DEBUG_NCR53C8XX
@@ -4832,6 +3968,15 @@ attach_error:
#endif
free_irq(np->irq, np);
}
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+
+unregister:
scsi_unregister(instance);
return -1;
@@ -4859,6 +4004,7 @@ attach_error:
*/
static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
{
+ unmap_scsi_data(np, cmd);
cmd->host_scribble = (char *) np->done_list;
np->done_list = cmd;
}
@@ -5114,7 +4260,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
**----------------------------------------------------
*/
- segments = ncr_scatter (cp, cp->cmd);
+ segments = ncr_scatter (np, cp, cp->cmd);
if (segments < 0) {
ncr_free_ccb(np, cp);
@@ -5123,47 +4269,24 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
/*----------------------------------------------------
**
- ** Guess xfer direction.
- ** Spare some CPU by testing here frequently opcode.
+ ** Determine xfer direction.
**
**----------------------------------------------------
*/
if (!cp->data_len)
- direction = 0;
- else {
- switch((int) cmd->cmnd[0]) {
- case 0x08: /* READ(6) 08 */
- case 0x28: /* READ(10) 28 */
- case 0xA8: /* READ(12) A8 */
- direction = XFER_IN;
- break;
- case 0x0A: /* WRITE(6) 0A */
- case 0x2A: /* WRITE(10) 2A */
- case 0xAA: /* WRITE(12) AA */
- direction = XFER_OUT;
- break;
- default:
- direction = (XFER_IN|XFER_OUT);
- break;
- }
- }
-
- /*----------------------------------------------------
- **
- ** Set the SAVED_POINTER.
- **
- **----------------------------------------------------
- */
-
- /*
- ** Default to no data transfer.
- */
- lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+ direction = SCSI_DATA_NONE;
+ else
+ direction = scsi_data_direction(cmd);
/*
- ** Compute data out pointers, if needed.
+ ** If data direction is UNKNOWN, speculate DATA_READ
+ ** but prepare alternate pointers for WRITE in case
+ ** of our speculation will be just wrong.
+ ** SCRIPTS will swap values if needed.
*/
- if (direction & XFER_OUT) {
+ switch(direction) {
+ case SCSI_DATA_UNKNOWN:
+ case SCSI_DATA_WRITE:
goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
if (segments <= MAX_SCATTERL)
lastp = goalp - 8 - (segments * 16);
@@ -5171,21 +4294,12 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
lastp -= (segments - MAX_SCATTERL) * 16;
}
- /*
- ** If actual data direction is unknown, save pointers
- ** in header. The SCRIPTS will swap them to current
- ** if target decision will be data out.
- */
- if (direction & XFER_IN) {
- cp->phys.header.wgoalp = cpu_to_scr(goalp);
- cp->phys.header.wlastp = cpu_to_scr(lastp);
- }
- }
-
- /*
- ** Compute data in pointers, if needed.
- */
- if (direction & XFER_IN) {
+ if (direction != SCSI_DATA_UNKNOWN)
+ break;
+ cp->phys.header.wgoalp = cpu_to_scr(goalp);
+ cp->phys.header.wlastp = cpu_to_scr(lastp);
+ /* fall through */
+ case SCSI_DATA_READ:
goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
if (segments <= MAX_SCATTERL)
lastp = goalp - 8 - (segments * 16);
@@ -5193,6 +4307,11 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
lastp -= (segments - MAX_SCATTERL) * 16;
}
+ break;
+ default:
+ case SCSI_DATA_NONE:
+ lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+ break;
}
/*
@@ -5202,7 +4321,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
cp->phys.header.lastp = cpu_to_scr(lastp);
cp->phys.header.goalp = cpu_to_scr(goalp);
- if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+ if (direction == SCSI_DATA_UNKNOWN)
cp->phys.header.savep =
cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
else
@@ -5241,14 +4360,13 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
*/
cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
cp->phys.smsg.size = cpu_to_scr(msglen);
-flush_cache_all();
/*
** command
*/
- cp->phys.cmd.addr = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+ memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
+ cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
- dma_cache_wback_inv((unsigned long)cmd->cmnd, cmd->cmd_len);
/*
** status
@@ -5276,9 +4394,6 @@ flush_cache_all();
** activate this job.
*/
cp->magic = CCB_MAGIC;
-//printk("cp == %08lx\n", cp);
- dma_cache_wback_inv((unsigned long)cp, sizeof(*cp));
-flush_cache_all();
/*
** insert next CCBs into start queue.
@@ -5350,7 +4465,6 @@ static void ncr_put_start_queue(ncb_p np, ccb_p cp)
if (DEBUG_FLAGS & DEBUG_QUEUE)
printk ("%s: queuepos=%d.\n", ncr_name (np), np->squeueput);
- dma_cache_wback_inv((unsigned long)np, sizeof(*np));
/*
** Script processor may be waiting for reselect.
@@ -5699,7 +4813,7 @@ static int ncr_detach(ncb_p np)
#ifdef DEBUG_NCR53C8XX
printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
#endif
- m_free(cp, sizeof(*cp));
+ m_free_dma(cp, sizeof(*cp), "CCB");
}
/*
@@ -5715,12 +4829,20 @@ static int ncr_detach(ncb_p np)
printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
#endif
if (lp->jump_ccb != &lp->jump_ccb_0)
- m_free(lp->jump_ccb, 256);
- m_free(lp, sizeof(*lp));
+ m_free_dma(lp->jump_ccb,256,"JUMP_CCB");
+ m_free_dma(lp, sizeof(*lp), "LCB");
}
}
}
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+
printk("%s: host resources successfully released\n", ncr_name(np));
return 1;
@@ -5874,6 +4996,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
*/
if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
cmd->cmnd[4] >= 7 && !cmd->use_sg) {
+ sync_scsi_data(np, cmd); /* SYNC the data */
ncr_setup_lcb (np, cmd->target, cmd->lun,
(char *) cmd->request_buffer);
}
@@ -5900,6 +5023,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
*/
cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
+ /*
+ ** Copy back sense data to caller's buffer.
+ */
+ memcpy(cmd->sense_buffer, cp->sense_buf,
+ MIN(sizeof(cmd->sense_buffer), sizeof(cp->sense_buf)));
+
if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
u_char * p = (u_char*) & cmd->sense_buffer;
int i;
@@ -6263,11 +5392,9 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code)
if (tp->usrwide > np->maxwide)
tp->usrwide = np->maxwide;
- dma_cache_wback_inv((unsigned long) tp, sizeof(*tp));
ncr_negotiate (np, tp);
}
- dma_cache_wback_inv((unsigned long) np, sizeof(*np));
/*
** Start script processor.
@@ -6277,7 +5404,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code)
if (bootverbose)
printk ("%s: Downloading SCSI SCRIPTS.\n",
ncr_name(np));
- OUTL (nc_scratcha, vtophys(np->script0));
+ OUTL (nc_scratcha, vtobus(np->script0));
OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, start_ram));
}
else
@@ -6661,7 +5788,6 @@ static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln)
lp->jump_tag.l_paddr = lp->usetags?
cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_tag)) :
cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_notag));
-flush_cache_all();
/*
** Announce change to user.
@@ -6792,7 +5918,12 @@ static void ncr_timeout (ncb_p np)
return;
}
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ np->ktime = thistime;
+ np->timer.expires = ktime_get(1);
+#else
np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
+#endif
add_timer(&np->timer);
/*
@@ -6959,7 +6090,6 @@ void ncr_exception (ncb_p np)
u_char istat, dstat;
u_short sist;
int i;
-flush_cache_all();
/*
** interrupt on the fly ?
@@ -7117,7 +6247,6 @@ flush_cache_all();
if (sist & UDC) {
printk ("%s: unexpected disconnect\n", ncr_name(np));
OUTB (HS_PRT, HS_UNEXPECTED);
-//flush_cache_all(); // ???
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
return;
};
@@ -7170,7 +6299,6 @@ void ncr_int_sto (ncb_p np)
** repair start queue and jump to start point.
*/
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sto_restart));
return;
}
@@ -7310,6 +6438,7 @@ static void ncr_int_ma (ncb_p np)
u_int32 dsp;
u_int32 dsa;
u_int32 nxtdsp;
+ u_int32 newtmp;
u_int32 *vdsp;
u_int32 oadr, olen;
u_int32 *tblp;
@@ -7404,11 +6533,11 @@ static void ncr_int_ma (ncb_p np)
nxtdsp = dsp;
}
else if (cp) {
- if (dsp == vtophys (&cp->patch[2])) {
+ if (dsp == CCB_PHYS (cp, patch[2])) {
vdsp = &cp->patch[0];
nxtdsp = scr_to_cpu(vdsp[3]);
}
- else if (dsp == vtophys (&cp->patch[6])) {
+ else if (dsp == CCB_PHYS (cp, patch[6])) {
vdsp = &cp->patch[4];
nxtdsp = scr_to_cpu(vdsp[3]);
}
@@ -7503,7 +6632,11 @@ static void ncr_int_ma (ncb_p np)
*/
newcmd = cp->patch;
- if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+ newtmp = CCB_PHYS (cp, patch);
+ if (newtmp == scr_to_cpu(cp->phys.header.savep)) {
+ newcmd = &cp->patch[4];
+ newtmp = CCB_PHYS (cp, patch[4]);
+ }
/*
** fillin the commands
@@ -7530,7 +6663,7 @@ static void ncr_int_ma (ncb_p np)
#ifdef SCSI_NCR_PROFILE_SUPPORT
np->profile.num_break++;
#endif
- OUTL (nc_temp, vtophys (newcmd));
+ OUTL (nc_temp, newtmp);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
return;
@@ -7699,19 +6832,14 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp)
*/
cp->sensecmd[0] = 0x03;
cp->sensecmd[1] = cmd->lun << 5;
- cp->sensecmd[4] = sizeof(cmd->sense_buffer);
- dma_cache_wback_inv((unsigned long)cmd->sense_buffer,
- sizeof(cmd->sense_buffer));
+ cp->sensecmd[4] = sizeof(cp->sense_buf);
/*
** sense data
*/
- cp->phys.sense.addr =
- cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
- cp->phys.sense.size =
- cpu_to_scr(sizeof(cmd->sense_buffer));
- dma_cache_wback_inv((unsigned long)cmd->sense_buffer,
- sizeof(cmd->sense_buffer));
+ bzero(cp->sense_buf, sizeof(cp->sense_buf));
+ cp->phys.sense.addr = cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cp->sense_buf));
/*
** requeue the command.
@@ -7745,8 +6873,6 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp)
}
out:
-flush_cache_all(); // ???
-//dma_cache_wback_inv((unsigned long)cmd->cmnd, cmd->cmd_len);
OUTONB (nc_dcntl, (STD|NOCOM));
return;
}
@@ -7837,7 +6963,6 @@ void ncr_int_sir (ncb_p np)
}
switch (num) {
-flush_cache_all(); // ???
/*-----------------------------------------------------------------------------
**
** Was Sie schon immer ueber transfermode negotiation wissen wollten ...
@@ -7943,7 +7068,6 @@ flush_cache_all(); // ???
np->msgin [0] = M_NOOP;
np->msgout[0] = M_NOOP;
cp->nego_status = 0;
-flush_cache_all();
break;
case SIR_NEGO_SYNC:
@@ -8024,14 +7148,12 @@ flush_cache_all();
** Answer wasn't acceptable.
*/
ncr_setsync (np, cp, 0, 0xe0);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
@@ -8065,7 +7187,6 @@ flush_cache_all();
}
if (!ofs) {
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
return;
}
@@ -8124,14 +7245,12 @@ flush_cache_all();
** Answer wasn't acceptable.
*/
ncr_setwide (np, cp, 0, 1);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
ncr_setwide (np, cp, wide, 1);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
@@ -8429,7 +7548,7 @@ static void ncr_free_ccb (ncb_p np, ccb_p cp)
#define ncr_reg_bus_addr(r) \
- (bus_dvma_to_mem(np->paddr) + offsetof (struct ncr_reg, r))
+ (pcivtobus(np->paddr) + offsetof (struct ncr_reg, r))
/*------------------------------------------------------------------------
** Initialize the fixed part of a CCB structure.
@@ -8443,7 +7562,7 @@ static void ncr_init_ccb(ncb_p np, ccb_p cp)
/*
** Remember virtual and bus address of this ccb.
*/
- cp->p_ccb = vtophys(cp);
+ cp->p_ccb = vtobus(cp);
cp->phys.header.cp = cp;
/*
@@ -8458,10 +7577,10 @@ static void ncr_init_ccb(ncb_p np, ccb_p cp)
** JUMP @(sched_point)
*/
cp->start.setup_dsa[0] = cpu_to_scr(copy_4);
- cp->start.setup_dsa[1] = cpu_to_scr(vtophys(&cp->start.p_phys));
+ cp->start.setup_dsa[1] = cpu_to_scr(CCB_PHYS(cp, start.p_phys));
cp->start.setup_dsa[2] = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
- cp->start.p_phys = cpu_to_scr(vtophys(&cp->phys));
+ cp->start.p_phys = cpu_to_scr(CCB_PHYS(cp, phys));
bcopy(&cp->start, &cp->restart, sizeof(cp->restart));
@@ -8484,15 +7603,10 @@ static void ncr_alloc_ccb(ncb_p np, u_char tn, u_char ln)
/*
** Allocate memory for this CCB.
*/
- cp = m_alloc(sizeof(struct ccb), 5);
+ cp = m_calloc_dma(sizeof(struct ccb), "CCB");
if (!cp)
return;
- if (DEBUG_FLAGS & DEBUG_ALLOC) {
- PRINT_LUN(np, tn, ln);
- printk ("new ccb @%p.\n", cp);
- }
-
/*
** Count it and initialyze it.
*/
@@ -8510,7 +7624,6 @@ static void ncr_alloc_ccb(ncb_p np, u_char tn, u_char ln)
xpt_insque_head(&cp->link_ccbq, &lp->free_ccbq);
ncr_setup_tags (np, tn, ln);
-flush_cache_all();
}
/*==========================================================
@@ -8551,7 +7664,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
** COPY @(tp->sval), @(sxfer)
*/
tp->getscr[0] = cpu_to_scr(copy_1);
- tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+ tp->getscr[1] = cpu_to_scr(vtobus (&tp->sval));
tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
/*
@@ -8559,7 +7672,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
** COPY @(tp->wval), @(scntl3)
*/
tp->getscr[3] = cpu_to_scr(copy_1);
- tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+ tp->getscr[4] = cpu_to_scr(vtobus (&tp->wval));
tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
/*
@@ -8584,7 +7697,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
/*
** Link this target control block to the JUMP chain.
*/
- np->jump_tcb[th].l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+ np->jump_tcb[th].l_paddr = cpu_to_scr(vtobus (&tp->jump_tcb));
/*
** These assert's should be moved at driver initialisations.
@@ -8619,17 +7732,12 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
/*
** Allocate the lcb.
*/
- lp = m_alloc(sizeof(struct lcb), 3);
+ lp = m_calloc_dma(sizeof(struct lcb), "LCB");
if (!lp)
goto fail;
bzero(lp, sizeof(*lp));
tp->lp[ln] = lp;
- if (DEBUG_FLAGS & DEBUG_ALLOC) {
- PRINT_LUN(np, tn, ln);
- printk ("new lcb @%p.\n", lp);
- }
-
/*
** Initialize the target control block if not yet.
*/
@@ -8650,7 +7758,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
*/
lp->maxnxs = 1;
lp->jump_ccb = &lp->jump_ccb_0;
- lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
/*
** Initilialyze the reselect script:
@@ -8668,7 +7776,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
- lp->load_jump_ccb[1] = cpu_to_scr(vtophys (&lp->p_jump_ccb));
+ lp->load_jump_ccb[1] = cpu_to_scr(vtobus (&lp->p_jump_ccb));
lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
lp->jump_tag.l_cmd = cpu_to_scr(SCR_JUMP);
@@ -8677,7 +7785,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
/*
** Link this lun control block to the JUMP chain.
*/
- tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
+ tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtobus (&lp->jump_lcb));
/*
** Initialize command queuing control.
@@ -8761,12 +7869,12 @@ static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data)
*/
if ((inq_byte7 & INQ7_QUEUE) && lp->jump_ccb == &lp->jump_ccb_0) {
int i;
- lp->jump_ccb = m_alloc(256, 8);
+ lp->jump_ccb = m_calloc_dma(256, "JUMP_CCB");
if (!lp->jump_ccb) {
lp->jump_ccb = &lp->jump_ccb_0;
goto fail;
}
- lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
for (i = 0 ; i < 64 ; i++)
lp->jump_ccb[i] =
cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
@@ -8818,7 +7926,7 @@ fail:
** sizes to the data segment array.
*/
-static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+static int ncr_scatter(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
{
struct scr_tblmove *data;
int segment = 0;
@@ -8829,32 +7937,28 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
if (!use_sg) {
if (cmd->request_bufflen) {
- unsigned long addr, len;
+ u_long baddr = map_scsi_single_data(np, cmd);
- addr = cmd->request_buffer;
- len = cmd->request_bufflen;
data = &data[MAX_SCATTER - 1];
- data[0].addr = cpu_to_scr(vtophys(addr));
- data[0].size = cpu_to_scr(len);
- if (addr)
- dma_cache_wback_inv(addr, len);
- cp->data_len = len;
+ data[0].addr = cpu_to_scr(baddr);
+ data[0].size = cpu_to_scr(cmd->request_bufflen);
+ cp->data_len = cmd->request_bufflen;
segment = 1;
}
}
else if (use_sg <= MAX_SCATTER) {
struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+ use_sg = map_scsi_sg_data(np, cmd);
data = &data[MAX_SCATTER - use_sg];
+
while (segment < use_sg) {
- unsigned long addr, len;
+ u_long baddr = scsi_sg_dma_address(&scatter[segment]);
+ unsigned int len = scsi_sg_dma_len(&scatter[segment]);
- addr = scatter[segment].address;
- len = scatter[segment].length;
- data[segment].addr = cpu_to_scr(vtophys(addr));
+ data[segment].addr = cpu_to_scr(baddr);
data[segment].size = cpu_to_scr(len);
- dma_cache_wback_inv(addr, len);
- cp->data_len += len;
+ cp->data_len += len;
++segment;
}
}
@@ -8862,7 +7966,6 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
return -1;
}
- dma_cache_wback_inv(data, sizeof(*data) * segment);
return segment;
}
@@ -8922,7 +8025,6 @@ static int __init ncr_snooptest (struct ncb* np)
** Set memory and register.
*/
np->ncr_cache = cpu_to_scr(host_wr);
- dma_cache_wback_inv((unsigned long)np, sizeof(*np));
OUTL (nc_temp, ncr_wr);
/*
** Start script (exchange values)
@@ -9277,833 +8379,10 @@ static void __init ncr_getclock (ncb_p np, int mult)
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
-#ifndef uchar
-#define uchar unsigned char
-#endif
-
-#ifndef ushort
-#define ushort unsigned short
-#endif
-
-#ifndef ulong
-#define ulong unsigned long
-#endif
-
-/* ---------------------------------------------------------------------
-**
-** Driver setup from the boot command line
-**
-** ---------------------------------------------------------------------
-*/
-
-#ifdef MODULE
-#define ARG_SEP ' '
-#else
-#define ARG_SEP ','
-#endif
-
-int __init ncr53c8xx_setup(char *str)
-{
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
- char *cur = str;
- char *pc, *pv;
- int val;
- int base;
- int c;
- int xi = 0;
-
- while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
- char *pe;
-
- val = 0;
- pv = pc;
- c = *++pv;
-
- if (c == 'n')
- val = 0;
- else if (c == 'y')
- val = 1;
- else {
- base = 0;
- val = (int) simple_strtoul(pv, &pe, base);
- }
- if (!strncmp(cur, "tags:", 5)) {
- int i;
- driver_setup.default_tags = val;
- if (pe && *pe == '/') {
- i = 0;
- while (*pe && *pe != ARG_SEP &&
- i < sizeof(driver_setup.tag_ctrl)-1) {
- driver_setup.tag_ctrl[i++] = *pe++;
- }
- driver_setup.tag_ctrl[i] = '\0';
- }
- }
- else if (!strncmp(cur, "mpar:", 5))
- driver_setup.master_parity = val;
- else if (!strncmp(cur, "spar:", 5))
- driver_setup.scsi_parity = val;
- else if (!strncmp(cur, "disc:", 5))
- driver_setup.disconnection = val;
- else if (!strncmp(cur, "specf:", 6))
- driver_setup.special_features = val;
- else if (!strncmp(cur, "ultra:", 6))
- driver_setup.ultra_scsi = val;
- else if (!strncmp(cur, "fsn:", 4))
- driver_setup.force_sync_nego = val;
- else if (!strncmp(cur, "revprob:", 8))
- driver_setup.reverse_probe = val;
- else if (!strncmp(cur, "sync:", 5))
- driver_setup.default_sync = val;
- else if (!strncmp(cur, "verb:", 5))
- driver_setup.verbose = val;
- else if (!strncmp(cur, "debug:", 6))
- driver_setup.debug = val;
- else if (!strncmp(cur, "burst:", 6))
- driver_setup.burst_max = val;
- else if (!strncmp(cur, "led:", 4))
- driver_setup.led_pin = val;
- else if (!strncmp(cur, "wide:", 5))
- driver_setup.max_wide = val? 1:0;
- else if (!strncmp(cur, "settle:", 7))
- driver_setup.settle_delay= val;
- else if (!strncmp(cur, "diff:", 5))
- driver_setup.diff_support= val;
- else if (!strncmp(cur, "irqm:", 5))
- driver_setup.irqm = val;
- else if (!strncmp(cur, "pcifix:", 7))
- driver_setup.pci_fix_up = val;
- else if (!strncmp(cur, "buschk:", 7))
- driver_setup.bus_check = val;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- else if (!strncmp(cur, "nvram:", 6))
- driver_setup.use_nvram = val;
-#endif
-
- else if (!strncmp(cur, "safe:", 5) && val)
- memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
- else if (!strncmp(cur, "excl:", 5)) {
- if (xi < SCSI_NCR_MAX_EXCLUDES)
- driver_setup.excludes[xi++] = val;
- }
- else if (!strncmp(cur, "hostid:", 7))
- driver_setup.host_id = val;
- else
- printk("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
-
- if ((cur = strchr(cur, ARG_SEP)) != NULL)
- ++cur;
- }
-#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
- return 0;
-}
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
-#ifndef MODULE
-__setup("ncr53c8xx=", ncr53c8xx_setup);
-#endif
-#endif
-
-static int
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device);
-
-/*
-** Linux entry point for NCR53C8XX devices detection routine.
-**
-** Called by the middle-level scsi drivers at initialization time,
-** or at module installation.
-**
-** Read the PCI configuration and try to attach each
-** detected NCR board.
-**
-** If NVRAM is present, try to attach boards according to
-** the used defined boot order.
-**
-** Returns the number of boards successfully attached.
-*/
-
-static void __init ncr_print_driver_setup(void)
-{
-#define YesNo(y) y ? 'y' : 'n'
- printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
- "burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
- YesNo(driver_setup.disconnection),
- driver_setup.special_features,
- driver_setup.ultra_scsi,
- driver_setup.default_tags,
- driver_setup.default_sync,
- driver_setup.burst_max,
- YesNo(driver_setup.max_wide),
- driver_setup.diff_support,
- YesNo(driver_setup.reverse_probe),
- driver_setup.bus_check);
-
- printk ("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
- "led:%c,settle:%d,irqm:%d,nvram:0x%x,pcifix:0x%x\n",
- YesNo(driver_setup.master_parity),
- YesNo(driver_setup.scsi_parity),
- YesNo(driver_setup.force_sync_nego),
- driver_setup.verbose,
- driver_setup.debug,
- YesNo(driver_setup.led_pin),
- driver_setup.settle_delay,
- driver_setup.irqm,
- driver_setup.use_nvram,
- driver_setup.pci_fix_up);
-#undef YesNo
-}
-
-/*
-** NCR53C8XX devices description table and chip ids list.
-*/
-
-static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
-static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
-
-
-/*===================================================================
-** Detect all 53c8xx hosts and then attach them.
-**
-** If we are using NVRAM, once all hosts are detected, we need to
-** check any NVRAM for boot order in case detect and boot order
-** differ and attach them using the order in the NVRAM.
-**
-** If no NVRAM is found or data appears invalid attach boards in
-** the the order they are detected.
-**===================================================================
-*/
-int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
-{
- pcidev_t pcidev;
- int i, j, chips, hosts, count;
- int attach_count = 0;
- ncr_device *devtbl, *devp;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- ncr_nvram nvram0, nvram, *nvp;
-#endif
-
- /*
- ** PCI is required.
- */
- if (!pci_present())
- return 0;
-
- /*
- ** Initialize driver general stuff.
- */
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
- tpnt->proc_dir = &proc_scsi_ncr53c8xx;
-#else
- tpnt->proc_name = "ncr53c8xx";
-#endif
- tpnt->proc_info = ncr53c8xx_proc_info;
-#endif
-
-#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
-if (ncr53c8xx)
- ncr53c8xx_setup(ncr53c8xx);
-#endif
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
- ncr_debug = driver_setup.debug;
-#endif
-
- if (initverbose >= 2)
- ncr_print_driver_setup();
-
- /*
- ** Allocate the device table since we donnot want to
- ** overflow the kernel stack.
- ** 1 x 4K PAGE is enough for more than 40 devices for i386.
- */
- devtbl = kmalloc(4000, GFP_ATOMIC);
- if (!devtbl)
- return 0;
-
- /*
- ** Detect all 53c8xx hosts.
- ** Save the first Symbios NVRAM content if any
- ** for the boot order.
- */
- chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
- hosts = 4000 / sizeof(*devtbl);
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
-#endif
- j = 0;
- count = 0;
- pcidev = PCIDEV_NULL;
- while (1) {
- char *msg = "";
- if (count >= hosts)
- break;
- if (j >= chips)
- break;
- i = driver_setup.reverse_probe ? chips - 1 - j : j;
- pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
- pcidev);
- if (pcidev == PCIDEV_NULL) {
- ++j;
- continue;
- }
- /* Some HW as the HP LH4 may report twice PCI devices */
- for (i = 0; i < count ; i++) {
- if (devtbl[i].slot.bus == PciBusNumber(pcidev) &&
- devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
- break;
- }
- if (i != count) /* Ignore this device if we already have it */
- continue;
- devp = &devtbl[count];
- devp->host_id = driver_setup.host_id;
- devp->attach_done = 0;
- if (ncr53c8xx_pci_init(tpnt, pcidev, devp)) {
- continue;
- }
- ++count;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- if (nvp) {
- ncr_get_nvram(devp, nvp);
- switch(nvp->type) {
- case SCSI_NCR_SYMBIOS_NVRAM:
- /*
- * Switch to the other nvram buffer, so that
- * nvram0 will contain the first Symbios
- * format NVRAM content with boot order.
- */
- nvp = &nvram;
- msg = "with Symbios NVRAM";
- break;
- case SCSI_NCR_TEKRAM_NVRAM:
- msg = "with Tekram NVRAM";
- break;
- }
- }
-#endif
- printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
- devp->chip.name, msg);
- }
-
- /*
- ** If we have found a SYMBIOS NVRAM, use first the NVRAM boot
- ** sequence as device boot order.
- ** check devices in the boot record against devices detected.
- ** attach devices if we find a match. boot table records that
- ** do not match any detected devices will be ignored.
- ** devices that do not match any boot table will not be attached
- ** here but will attempt to be attached during the device table
- ** rescan.
- */
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
- goto next;
- for (i = 0; i < 4; i++) {
- Symbios_host *h = &nvram0.data.Symbios.host[i];
- for (j = 0 ; j < count ; j++) {
- devp = &devtbl[j];
- if (h->device_fn != devp->slot.device_fn ||
- h->bus_nr != devp->slot.bus ||
- h->device_id != devp->chip.device_id)
- continue;
- if (devp->attach_done)
- continue;
- ncr_get_nvram(devp, nvp);
- if (!ncr_attach (tpnt, attach_count, devp))
- attach_count++;
- devp->attach_done = 1;
- break;
- }
- }
-next:
-#endif
-
- /*
- ** Rescan device list to make sure all boards attached.
- ** Devices without boot records will not be attached yet
- ** so try to attach them here.
- */
- for (i= 0; i < count; i++) {
- devp = &devtbl[i];
- if (!devp->attach_done) {
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- ncr_get_nvram(devp, nvp);
-#endif
- if (!ncr_attach (tpnt, attach_count, devp))
- attach_count++;
- }
- }
-
- kfree(devtbl);
-
- return attach_count;
-}
-
-/*===================================================================
-** Detect and try to read SYMBIOS and TEKRAM NVRAM.
-**
-** Data can be used to order booting of boards.
-**
-** Data is saved in ncr_device structure if NVRAM found. This
-** is then used to find drive boot order for ncr_attach().
-**
-** NVRAM data is passed to Scsi_Host_Template later during
-** ncr_attach() for any device set up.
-*===================================================================
-*/
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
-{
- devp->nvram = nvp;
- if (!nvp)
- return;
- /*
- ** Get access to chip IO registers
- */
-#ifdef NCR_IOMAPPED
- request_region(devp->slot.io_port, 128, "ncr53c8xx");
- devp->slot.port = devp->slot.io_port;
-#else
- devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
- if (!devp->slot.reg)
- return;
-#endif
-
- /*
- ** Try to read SYMBIOS nvram.
- ** Try to read TEKRAM nvram if Symbios nvram not found.
- */
- if (!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
- nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
- else if (!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram))
- nvp->type = SCSI_NCR_TEKRAM_NVRAM;
- else {
- nvp->type = 0;
- devp->nvram = 0;
- }
-
- /*
- ** Release access to chip IO registers
- */
-#ifdef NCR_IOMAPPED
- release_region(devp->slot.port, 128);
-#else
- unmap_pci_mem((u_long) devp->slot.reg, 128ul);
-#endif
-
-}
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
-
-/*
-** Read and check the PCI configuration for any detected NCR
-** boards and save data for attaching after all boards have
-** been detected.
-*/
-
-static int __init
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
-{
- ushort vendor_id, device_id, command;
- uchar cache_line_size, latency_timer;
- uchar revision;
- uint irq;
- ulong base, base_2, io_port;
- int i;
- ncr_chip *chip;
-
- /*
- ** Read info from the PCI config space.
- ** pci_read_config_xxx() functions are assumed to be used for
- ** successfully detected PCI devices.
- */
- vendor_id = PciVendorId(pdev);
- device_id = PciDeviceId(pdev);
- irq = PciIrqLine(pdev);
- i = 0;
- i = pci_get_base_address(pdev, i, &io_port);
- i = pci_get_base_address(pdev, i, &base);
- (void) pci_get_base_address(pdev, i, &base_2);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
-
- /*
- ** If user excludes this chip, donnot initialize it.
- */
- for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
- if (driver_setup.excludes[i] ==
- (io_port & PCI_BASE_ADDRESS_IO_MASK))
- return -1;
- }
- /*
- * Check if the chip is supported
- */
- chip = 0;
- for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
- if (device_id != ncr_chip_table[i].device_id)
- continue;
- if (revision > ncr_chip_table[i].revision_id)
- continue;
- chip = &device->chip;
- memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
- chip->revision_id = revision;
- break;
- }
-
-#if defined(__i386__)
- /*
- * Ignore Symbios chips controlled by SISL RAID controller.
- */
- if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
- unsigned int ScriptsSize, MagicValue;
- vm_offset_t ScriptsRAM;
-
- if (chip->features & FE_RAM8K)
- ScriptsSize = 8192;
- else
- ScriptsSize = 4096;
-
- ScriptsRAM = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
- ScriptsSize);
- if (ScriptsRAM) {
- MagicValue = readl(ScriptsRAM + ScriptsSize - 16);
- unmap_pci_mem(ScriptsRAM, ScriptsSize);
- if (MagicValue == 0x52414944)
- return -1;
- }
- }
-#endif
-
- printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
- PciBusNumber(pdev),
- (int) (PciDeviceFn(pdev) & 0xf8) >> 3,
- (int) (PciDeviceFn(pdev) & 0x7));
-
- if (!chip) {
- printk("ncr53c8xx: not initializing, device not supported\n");
- return -1;
- }
-
-#ifdef __powerpc__
- /*
- * Several fix-up for power/pc.
- * Should not be performed by the driver.
- */
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER...");
- command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
- if (!(command & PCI_COMMAND_IO)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_IO...");
- command |= PCI_COMMAND_IO;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_IO)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
- if (!(command & PCI_COMMAND_MEMORY)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY...");
- command |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_MEMORY)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,140)
- if ( is_prep ) {
- if (io_port >= 0x10000000) {
- printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
- io_port = (io_port & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, io_port);
- }
- if (base >= 0x10000000) {
- printk("ncr53c8xx: reallocating base (Wacky IBM)");
- base = (base & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base);
- }
- if (base_2 >= 0x10000000) {
- printk("ncr53c8xx: reallocating base2 (Wacky IBM)");
- base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_2, base_2);
- }
- }
-#endif
-#endif /* __powerpc__ */
-
-#ifdef __sparc__
- /*
- * Severall fix-ups for sparc.
- *
- * Should not be performed by the driver, but how can OBP know
- * each and every PCI card, if they don't use Fcode?
- */
-
- base = __pa(base);
- base_2 = __pa(base_2);
-
- if (!(command & PCI_COMMAND_MASTER)) {
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
- command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- }
-
- if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
- command |= PCI_COMMAND_INVALIDATE;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- }
-
- if ((chip->features & FE_CLSE) && !cache_line_size) {
- /* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
- cache_line_size = 64 / sizeof(u_int32);
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
- }
-
- if (!latency_timer) {
- latency_timer = 128;
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
- }
-#endif /* __sparc__ */
-
- /*
- * Check availability of IO space, memory space and master capability.
- */
- if (command & PCI_COMMAND_IO)
- io_port &= PCI_BASE_ADDRESS_IO_MASK;
- else
- io_port = 0;
-
- if (command & PCI_COMMAND_MEMORY)
- base &= PCI_BASE_ADDRESS_MEM_MASK;
- else
- base = 0;
-
- if (!io_port && !base) {
- printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
- return -1;
- }
-
- base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
-
- if (io_port && check_region (io_port, 128)) {
-#ifdef __sparc__
- printk("ncr53c8xx: IO region 0x%lx to 0x%lx is in use\n",
- io_port, (io_port + 127));
-#else
- printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
- (int) io_port, (int) (io_port + 127));
-#endif
- return -1;
- }
-
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
- return -1;
- }
-
- /*
- * Fix some features according to driver setup.
- */
- if (!(driver_setup.special_features & 1))
- chip->features &= ~FE_SPECIAL_SET;
- else {
- if (driver_setup.special_features & 2)
- chip->features &= ~FE_WRIE;
- }
- if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
- chip->features |= FE_ULTRA;
- chip->features &= ~FE_ULTRA2;
- }
- if (driver_setup.ultra_scsi < 1)
- chip->features &= ~FE_ULTRA;
- if (!driver_setup.max_wide)
- chip->features &= ~FE_WIDE;
-
-
-#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
-
- /*
- * Try to fix up PCI config according to wished features.
- */
-#if defined(__i386__) && !defined(MODULE)
- if ((driver_setup.pci_fix_up & 1) &&
- (chip->features & FE_CLSE) && cache_line_size == 0) {
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
- extern char x86;
- switch(x86) {
-#else
- switch(boot_cpu_data.x86) {
-#endif
- case 4: cache_line_size = 4; break;
- case 6:
- case 5: cache_line_size = 8; break;
- }
- if (cache_line_size)
- (void) pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
- if (initverbose)
- printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
- }
-
- if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
- (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- command |= PCI_COMMAND_INVALIDATE;
- (void) pci_write_config_word(pdev, PCI_COMMAND, command);
- if (initverbose)
- printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
- }
-#endif
- /*
- * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
- * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
- * and donnot enable READ LINE.
- * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
- */
-
- if (!(chip->features & FE_CLSE)) {
- int burst_max = chip->burst_max;
- if (cache_line_size == 0) {
- chip->features &= ~FE_ERL;
- if (burst_max > 3)
- burst_max = 3;
- }
- else {
- while (cache_line_size < (1 << burst_max))
- --burst_max;
- }
- chip->burst_max = burst_max;
- }
-
- /*
- * Tune PCI LATENCY TIMER according to burst max length transfer.
- * (latency timer >= burst length + 6, we add 10 to be quite sure)
- * If current value is zero, the device has probably been configured
- * for no bursting due to some broken hardware.
- */
-
- if (latency_timer == 0 && chip->burst_max)
- printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
-
- if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
- uchar lt = (1 << chip->burst_max) + 6 + 10;
- if (latency_timer < lt) {
- latency_timer = lt;
- if (initverbose)
- printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
- (void) pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
- }
- }
-
- /*
- * Fix up for recent chips that support CACHE LINE SIZE.
- * If PCI config space is not OK, remove features that shall not be
- * used by the chip. No need to trigger possible chip bugs.
- */
-
- if ((chip->features & FE_CLSE) && cache_line_size == 0) {
- chip->features &= ~FE_CACHE_SET;
- printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
- }
-
- if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- chip->features &= ~FE_WRIE;
- printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
- }
-
-#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
-
- /* initialise ncr_device structure with items required by ncr_attach */
- device->slot.bus = PciBusNumber(pdev);
- device->slot.device_fn = PciDeviceFn(pdev);
- device->slot.base = base;
- device->slot.base_2 = base_2;
- device->slot.io_port = io_port;
- device->slot.irq = irq;
- device->attach_done = 0;
-
- return 0;
-}
-
/*
** Linux select queue depths function
*/
-#define DEF_DEPTH (driver_setup.default_tags)
-#define ALL_TARGETS -2
-#define NO_TARGET -1
-#define ALL_LUNS -2
-#define NO_LUN -1
-
-static int device_queue_depth(ncb_p np, int target, int lun)
-{
- int c, h, t, u, v;
- char *p = driver_setup.tag_ctrl;
- char *ep;
-
- h = -1;
- t = NO_TARGET;
- u = NO_LUN;
- while ((c = *p++) != 0) {
- v = simple_strtoul(p, &ep, 0);
- switch(c) {
- case '/':
- ++h;
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- case 't':
- if (t != target)
- t = (target == v) ? v : NO_TARGET;
- u = ALL_LUNS;
- break;
- case 'u':
- if (u != lun)
- u = (lun == v) ? v : NO_LUN;
- break;
- case 'q':
- if (h == np->unit &&
- (t == ALL_TARGETS || t == target) &&
- (u == ALL_LUNS || u == lun))
- return v;
- break;
- case '-':
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- default:
- break;
- }
- p = ep;
- }
- return DEF_DEPTH;
-}
-
static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
{
struct scsi_device *device;
@@ -10127,7 +8406,7 @@ static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de
** Use at least 2.
** Donnot use more than our maximum.
*/
- numtags = device_queue_depth(np, device->id, device->lun);
+ numtags = device_queue_depth(np->unit, device->id, device->lun);
if (numtags > tp->usrtags)
numtags = tp->usrtags;
if (!device->tagged_supported)
@@ -10157,14 +8436,6 @@ printk("ncr53c8xx_select_queue_depth: host=%d, id=%d, lun=%d, depth=%d\n",
}
/*
-** Linux entry point for info() function
-*/
-const char *ncr53c8xx_info (struct Scsi_Host *host)
-{
- return SCSI_NCR_DRIVER_NAME;
-}
-
-/*
** Linux entry point of queuecommand() function
*/
@@ -10180,6 +8451,10 @@ printk("ncr53c8xx_queue_command\n");
cmd->scsi_done = done;
cmd->host_scribble = NULL;
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ cmd->__data_mapped = 0;
+ cmd->__data_mapping = 0;
+#endif
NCR_LOCK_NCB(np, flags);
@@ -10196,8 +8471,10 @@ printk("ncr53c8xx : command successfully queued\n");
NCR_UNLOCK_NCB(np, flags);
- if (sts != DID_OK)
+ if (sts != DID_OK) {
+ unmap_scsi_data(np, cmd);
done(cmd);
+ }
return sts;
}
@@ -10208,11 +8485,9 @@ printk("ncr53c8xx : command successfully queued\n");
** 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.
-**
-** Exported for certain MIPS machines with a dedicated NCR interrupt.
*/
-void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
{
unsigned long flags;
ncb_p np = (ncb_p) dev_id;
@@ -10458,7 +8733,6 @@ static void process_waiting_list(ncb_p np, int sts)
printk("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
#endif
wcmd->result = ScsiResult(sts, 0);
-//flush_cache_all();
ncr_queue_done_cmd(np, wcmd);
}
}
@@ -10676,50 +8950,8 @@ printk("ncr_user_command: data=%ld\n", uc->data);
#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */
-#ifdef SCSI_NCR_USER_INFO_SUPPORT
-
-struct info_str
-{
- char *buffer;
- int length;
- int offset;
- int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
- if (info->pos + len > info->length)
- len = info->length - info->pos;
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
- if (info->pos < info->offset) {
- data += (info->offset - info->pos);
- len -= (info->offset - info->pos);
- }
-
- if (len > 0) {
- memcpy(info->buffer + info->pos, data, len);
- info->pos += len;
- }
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
- va_list args;
- char buf[81];
- int len;
-
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
- return len;
-}
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
/*
** Copy formatted profile information into the input buffer.
*/
@@ -10827,7 +9059,6 @@ printk("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
return retv;
}
-
/*=========================================================================
** End of proc file system stuff
**=========================================================================
@@ -10835,432 +9066,105 @@ printk("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
#endif
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-
-/* ---------------------------------------------------------------------
+/*==========================================================
**
-** Try reading Symbios format nvram
+** /proc directory entry.
**
-** ---------------------------------------------------------------------
+**==========================================================
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_ncr53c8xx = {
+ PROC_SCSI_NCR53C8XX, 9, NAME53C8XX,
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+/*==========================================================
**
-** GPOI0 - data in/data out
-** GPIO1 - clock
+** Boot command line.
**
-** return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**==========================================================
*/
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(ncr53c8xx, "s");
+# endif
+#endif
-#define SET_BIT 0
-#define CLR_BIT 1
-#define SET_CLK 2
-#define CLR_CLK 3
-
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
-static void nvram_start(ncr_slot *np, u_char *gpreg);
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
-static void nvram_stop(ncr_slot *np, u_char *gpreg);
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
-
-static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
-{
- static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
- u_char gpcntl, gpreg;
- u_char old_gpcntl, old_gpreg;
- u_short csum;
- u_char ack_data;
- int retv = 1;
-
- /* save current state of GPCNTL and GPREG */
- old_gpreg = INB (nc_gpreg);
- old_gpcntl = INB (nc_gpcntl);
- gpcntl = old_gpcntl & 0xfc;
-
- /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
- OUTB (nc_gpreg, old_gpreg);
- OUTB (nc_gpcntl, gpcntl);
-
- /* this is to set NVRAM into a known state with GPIO0/1 both low */
- gpreg = old_gpreg;
- nvram_setBit(np, 0, &gpreg, CLR_CLK);
- nvram_setBit(np, 0, &gpreg, CLR_BIT);
-
- /* now set NVRAM inactive with GPIO0/1 both high */
- nvram_stop(np, &gpreg);
-
- /* activate NVRAM */
- nvram_start(np, &gpreg);
-
- /* write device code and random address MSB */
- nvram_write_byte(np, &ack_data,
- 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* write random address LSB */
- nvram_write_byte(np, &ack_data,
- (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* regenerate START state to set up for reading */
- nvram_start(np, &gpreg);
-
- /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
- nvram_write_byte(np, &ack_data,
- 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* now set up GPIO0 for inputting data */
- gpcntl |= 0x01;
- OUTB (nc_gpcntl, gpcntl);
-
- /* input all active data - only part of total NVRAM */
- csum = nvram_read_data(np,
- (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
-
- /* finally put NVRAM back in inactive mode */
- gpcntl &= 0xfe;
- OUTB (nc_gpcntl, gpcntl);
- nvram_stop(np, &gpreg);
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-printk("ncr53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
- nvram->type,
- nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
- nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
- nvram->byte_count, sizeof(*nvram) - 12,
- nvram->checksum, csum);
-#endif
-
- /* check valid NVRAM signature, verify byte count and checksum */
- if (nvram->type == 0 &&
- !memcmp(nvram->trailer, Symbios_trailer, 6) &&
- nvram->byte_count == sizeof(*nvram) - 12 &&
- csum == nvram->checksum)
- retv = 0;
-out:
- /* return GPIO0/1 to original states after having accessed NVRAM */
- OUTB (nc_gpcntl, old_gpcntl);
- OUTB (nc_gpreg, old_gpreg);
-
- return retv;
-}
-
-/*
- * Read Symbios NvRAM data and compute checksum.
- */
-static u_short __init
-nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
-{
- int x;
- u_short csum;
-
- for (x = 0; x < len; x++)
- nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
-
- for (x = 6, csum = 0; x < len - 6; x++)
- csum += data[x];
-
- return csum;
-}
-
-/*
- * Send START condition to NVRAM to wake it up.
- */
-static void __init nvram_start(ncr_slot *np, u_char *gpreg)
-{
- nvram_setBit(np, 1, gpreg, SET_BIT);
- nvram_setBit(np, 0, gpreg, SET_CLK);
- nvram_setBit(np, 0, gpreg, CLR_BIT);
- nvram_setBit(np, 0, gpreg, CLR_CLK);
-}
-
-/*
- * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
- * GPIO0 must already be set as an output
- */
-static void __init
-nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
-{
- int x;
-
- for (x = 0; x < 8; x++)
- nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
-
- nvram_readAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * READ a byte from the NVRAM and then send an ACK to say we have got it,
- * GPIO0 must already be set as an input
- */
-static void __init
-nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
-{
- int x;
- u_char read_bit;
-
- *read_data = 0;
- for (x = 0; x < 8; x++) {
- nvram_doBit(np, &read_bit, 1, gpreg);
- *read_data |= ((read_bit & 0x01) << (7 - x));
- }
-
- nvram_writeAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
- */
-static void __init
-nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
-{
- OUTB (nc_gpcntl, *gpcntl & 0xfe);
- nvram_doBit(np, 0, write_bit, gpreg);
- OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
- */
-static void __init
-nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
-{
- OUTB (nc_gpcntl, *gpcntl | 0x01);
- nvram_doBit(np, read_bit, 1, gpreg);
- OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
- */
-static void __init nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
-{
- nvram_setBit(np, write_bit, gpreg, SET_BIT);
- nvram_setBit(np, 0, gpreg, SET_CLK);
- if (read_bit)
- *read_bit = INB (nc_gpreg);
- nvram_setBit(np, 0, gpreg, CLR_CLK);
- nvram_setBit(np, 0, gpreg, CLR_BIT);
-}
-
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
- */
-static void __init nvram_stop(ncr_slot *np, u_char *gpreg)
-{
- nvram_setBit(np, 0, gpreg, SET_CLK);
- nvram_setBit(np, 1, gpreg, SET_BIT);
-}
-
-/*
- * Set/clear data/clock bit in GPIO0
- */
-static void __init
-nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+int __init ncr53c8xx_setup(char *str)
{
- UDELAY (5);
- switch (bit_mode){
- case SET_BIT:
- *gpreg |= write_bit;
- break;
- case CLR_BIT:
- *gpreg &= 0xfe;
- break;
- case SET_CLK:
- *gpreg |= 0x02;
- break;
- case CLR_CLK:
- *gpreg &= 0xfd;
- break;
-
- }
- OUTB (nc_gpreg, *gpreg);
- UDELAY (5);
+ return sym53c8xx__setup(str);
}
-#undef SET_BIT 0
-#undef CLR_BIT 1
-#undef SET_CLK 2
-#undef CLR_CLK 3
-
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("ncr53c8xx=", ncr53c8xx_setup);
+#endif
+#endif
-/* ---------------------------------------------------------------------
-**
-** Try reading Tekram format nvram
-**
-** ---------------------------------------------------------------------
+/*===================================================================
**
-** GPOI0 - data in
-** GPIO1 - data out
-** GPIO2 - clock
-** GPIO4 - chip select
+** SYM53C8XX supported device list
**
-** return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**===================================================================
*/
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
-
-static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
-{
- u_char gpcntl, gpreg;
- u_char old_gpcntl, old_gpreg;
- u_short csum;
-
- /* save current state of GPCNTL and GPREG */
- old_gpreg = INB (nc_gpreg);
- old_gpcntl = INB (nc_gpcntl);
-
- /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
- 1/2/4 out */
- gpreg = old_gpreg & 0xe9;
- OUTB (nc_gpreg, gpreg);
- gpcntl = (old_gpcntl & 0xe9) | 0x09;
- OUTB (nc_gpcntl, gpcntl);
-
- /* input all of NVRAM, 64 words */
- csum = Tnvram_read_data(np, (u_short *) nvram,
- sizeof(*nvram) / sizeof(short), &gpreg);
-
- /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
- OUTB (nc_gpcntl, old_gpcntl);
- OUTB (nc_gpreg, old_gpreg);
-
- /* check data valid */
- if (csum != 0x1234)
- return 1;
-
- return 0;
-}
-
-/*
- * Read Tekram NvRAM data and compute checksum.
- */
-static u_short __init
-Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
-{
- u_char read_bit;
- u_short csum;
- int x;
-
- for (x = 0, csum = 0; x < len; x++) {
-
- /* output read command and address */
- Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
- if (read_bit & 0x01)
- return 0; /* Force bad checksum */
-
- Tnvram_Read_Word(np, &data[x], gpreg);
- csum += data[x];
-
- Tnvram_Stop(np, gpreg);
- }
-
- return csum;
-}
-
-/*
- * Send read command and address to NVRAM
- */
-static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
-{
- int x;
-
- /* send 9 bits, start bit (1), command (2), address (6) */
- for (x = 0; x < 9; x++)
- Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
-
- *read_bit = INB (nc_gpreg);
-}
-
-/*
- * READ a byte from the NVRAM
- */
-static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
-{
- int x;
- u_char read_bit;
-
- *nvram_data = 0;
- for (x = 0; x < 16; x++) {
- Tnvram_Read_Bit(np, &read_bit, gpreg);
-
- if (read_bit & 0x01)
- *nvram_data |= (0x01 << (15 - x));
- else
- *nvram_data &= ~(0x01 << (15 - x));
- }
-}
-
-/*
- * Read bit from NVRAM
- */
-static void __init
-Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-{
- UDELAY (2);
- Tnvram_Clk(np, gpreg);
- *read_bit = INB (nc_gpreg);
-}
+static u_short ncr_chip_ids[] __initdata = {
+ PCI_DEVICE_ID_NCR_53C810,
+ PCI_DEVICE_ID_NCR_53C815,
+ PCI_DEVICE_ID_NCR_53C820,
+ PCI_DEVICE_ID_NCR_53C825,
+ PCI_DEVICE_ID_NCR_53C860,
+ PCI_DEVICE_ID_NCR_53C875,
+ PCI_DEVICE_ID_NCR_53C875J,
+ PCI_DEVICE_ID_NCR_53C885,
+ PCI_DEVICE_ID_NCR_53C895,
+ PCI_DEVICE_ID_NCR_53C896,
+ PCI_DEVICE_ID_NCR_53C895A,
+ PCI_DEVICE_ID_NCR_53C1510D
+};
-/*
- * Write bit to GPIO0
- */
-static void __init
-Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+/*==========================================================
+**
+** Chip detection entry point.
+**
+**==========================================================
+*/
+int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
{
- if (write_bit & 0x01)
- *gpreg |= 0x02;
- else
- *gpreg &= 0xfd;
-
- *gpreg |= 0x10;
-
- OUTB (nc_gpreg, *gpreg);
- UDELAY (2);
-
- Tnvram_Clk(np, gpreg);
-}
+ /*
+ ** Initialize driver general stuff.
+ */
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+ tpnt->proc_dir = &proc_scsi_ncr53c8xx;
+#else
+ tpnt->proc_name = NAME53C8XX;
+#endif
+ tpnt->proc_info = ncr53c8xx_proc_info;
+#endif
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
- */
-static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg)
-{
- *gpreg &= 0xef;
- OUTB (nc_gpreg, *gpreg);
- UDELAY (2);
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx);
+#endif
- Tnvram_Clk(np, gpreg);
+ return sym53c8xx__detect(tpnt, ncr_chip_ids,
+ sizeof(ncr_chip_ids)/sizeof(ncr_chip_ids[0]));
}
-/*
- * Pulse clock bit in GPIO0
- */
-static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+/*==========================================================
+**
+** Entry point for info() function
+**
+**==========================================================
+*/
+const char *ncr53c8xx_info (struct Scsi_Host *host)
{
- OUTB (nc_gpreg, *gpreg | 0x04);
- UDELAY (2);
- OUTB (nc_gpreg, *gpreg);
+ return SCSI_NCR_DRIVER_NAME;
}
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
-
/*
** Module stuff
*/
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a6e2c14d9..21cb989ca 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -218,23 +218,6 @@ static void scsi_wait_done(Scsi_Cmnd * SCpnt)
}
}
-void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
- void *buffer, unsigned bufflen,
- int timeout, int retries)
-{
- DECLARE_MUTEX_LOCKED(sem);
-
- if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE)
- BUG();
- SCpnt->request.sem = &sem;
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- scsi_do_cmd (SCpnt, (void *) cmnd,
- buffer, bufflen, scsi_wait_done, timeout, retries);
- down (&sem);
- SCpnt->request.sem = NULL;
-}
-
-
/*
* This lock protects the freelist for all devices on the system.
* We could make this finer grained by having a single lock per
@@ -2499,7 +2482,6 @@ static void scsi_dump_status(int level)
atomic_read(&shpnt->host_active),
shpnt->host_blocked,
shpnt->host_self_blocked);
-
}
printk("\n\n");
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 7a073f86e..677d21410 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -497,9 +497,6 @@ extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
void *buffer, unsigned bufflen,
void (*done) (struct scsi_cmnd *),
int timeout, int retries);
-extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
- void *buffer, unsigned bufflen,
- int timeout, int retries);
extern int scsi_dev_init(void);
/*
@@ -571,7 +568,7 @@ struct scsi_device {
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
/* public: */
- unsigned char id, lun, channel;
+ unsigned int id, lun, channel;
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
@@ -731,9 +728,9 @@ struct scsi_cmnd {
/* public: */
- unsigned char target;
- unsigned char lun;
- unsigned char channel;
+ unsigned int target;
+ unsigned int lun;
+ unsigned int channel;
unsigned char cmd_len;
unsigned char old_cmd_len;
unsigned char sc_data_direction;
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9f36d08c5..4bed377ed 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -549,24 +549,24 @@ static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
static unsigned char cmd[6] =
{TEST_UNIT_READY, 0, 0, 0, 0, 0};
- Scsi_Cmnd * scp;
+ Scsi_Request * scp;
Scsi_Device * sdev;
printk("Allocating host dev\n");
sdev = scsi_get_host_dev(shpnt);
printk("Got %p. Allocating command block\n", sdev);
- scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp = scsi_allocate_request(sdev);
printk("Got %p\n", scp);
- scp->cmd_len = 6;
- scp->use_sg = 0;
+ scp->sr_cmd_len = 6;
+ scp->sr_use_sg = 0;
printk("Sending command\n");
- scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
+ scsi_wait_req (scp, (void *) cmd, (void *) NULL,
0, 100, 3);
printk("Releasing command\n");
- scsi_release_command(scp);
+ scsi_release_request(scp);
printk("Freeing device\n");
scsi_free_host_dev(sdev);
}
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index abdef85ef..a211980a5 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -94,24 +94,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
int timeout, int retries)
{
int result;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0]));
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
- {
- return -EINTR;
- }
+ SRpnt = scsi_allocate_request(dev);
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
+ scsi_wait_req(SRpnt, cmd, NULL, 0, timeout, retries);
- SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result));
+ SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SRpnt->sr_result));
- if (driver_byte(SCpnt->result) != 0)
- switch (SCpnt->sense_buffer[2] & 0xf) {
+ if (driver_byte(SRpnt->sr_result) != 0)
+ switch (SRpnt->sr_sense_buffer[2] & 0xf) {
case ILLEGAL_REQUEST:
if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
dev->lockable = 0;
@@ -126,7 +122,7 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
case UNIT_ATTENTION:
if (dev->removable) {
dev->changed = 1;
- SCpnt->result = 0; /* This is no longer considered an error */
+ SRpnt->sr_result = 0; /* This is no longer considered an error */
/* gag this error, VFS will log it anyway /axboe */
/* printk(KERN_INFO "Disc change detected.\n"); */
break;
@@ -136,20 +132,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
dev->host->host_no,
dev->id,
dev->lun,
- SCpnt->result);
+ SRpnt->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(SCpnt->sense_buffer[0]),
- sense_error(SCpnt->sense_buffer[0]),
- SCpnt->sense_buffer[2] & 0xf);
+ sense_class(SRpnt->sr_sense_buffer[0]),
+ sense_error(SRpnt->sr_sense_buffer[0]),
+ SRpnt->sr_sense_buffer[2] & 0xf);
};
- result = SCpnt->result;
+ result = SRpnt->sr_result;
SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return result;
}
@@ -192,7 +188,7 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
char *buf;
unsigned char cmd[MAX_COMMAND_SIZE];
char *cmd_in;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
unsigned char opcode;
int inlen, outlen, cmdlen;
@@ -235,9 +231,9 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
return -ENOMEM;
memset(buf, 0, buf_needed);
if( inlen == 0 ) {
- data_direction = SCSI_DATA_WRITE;
- } else if (outlen == 0 ) {
data_direction = SCSI_DATA_READ;
+ } else if (outlen == 0 ) {
+ data_direction = SCSI_DATA_WRITE;
} else {
/*
* Can this ever happen?
@@ -297,38 +293,38 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
#ifndef DEBUG_NO_CMD
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
+ SRpnt = scsi_allocate_request(dev);
+ if( SRpnt == NULL )
{
return -EINTR;
}
- SCpnt->sc_data_direction = data_direction;
- scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
+ SRpnt->sr_data_direction = data_direction;
+ scsi_wait_req(SRpnt, cmd, buf, needed, timeout, retries);
/*
* If there was an error condition, pass the info back to the user.
*/
- if (SCpnt->result) {
- int sb_len = sizeof(SCpnt->sense_buffer);
+ if (SRpnt->sr_result) {
+ int sb_len = sizeof(SRpnt->sr_sense_buffer);
sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
result = verify_area(VERIFY_WRITE, cmd_in, sb_len);
if (result)
return result;
- copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len);
+ copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len);
} else {
result = verify_area(VERIFY_WRITE, cmd_in, outlen);
if (result)
return result;
copy_to_user(cmd_in, buf, outlen);
}
- result = SCpnt->result;
+ result = SRpnt->sr_result;
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
if (buf)
scsi_free(buf, buf_needed);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 2438aecc6..74ac6d245 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -87,6 +87,7 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head)
SCpnt->request.cmd = SPECIAL;
SCpnt->request.special = (void *) SCpnt;
SCpnt->request.q = NULL;
+ SCpnt->request.nr_segments = 0;
/*
* We have the option of inserting the head or the tail of the queue.
@@ -155,6 +156,8 @@ int scsi_insert_special_req(Scsi_Request * SRpnt, int at_head)
q = &SRpnt->sr_device->request_queue;
SRpnt->sr_request.cmd = SPECIAL;
SRpnt->sr_request.special = (void *) SRpnt;
+ SRpnt->sr_request.q = NULL;
+ SRpnt->sr_request.nr_segments = 0;
/*
* We have the option of inserting the head or the tail of the queue.
@@ -909,6 +912,9 @@ void scsi_request_fn(request_queue_t * q)
* be in an interrupt handler. Only do this
* from user space, since we do not want to
* sleep from an interrupt.
+ *
+ * FIXME(eric) - have the error handler thread do
+ * this work.
*/
SDpnt->was_reset = 0;
if (SDpnt->removable && !in_interrupt()) {
@@ -950,6 +956,9 @@ void scsi_request_fn(request_queue_t * q)
if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
SCpnt = scsi_allocate_device(SRpnt->sr_device,
FALSE, FALSE);
+ if( !SCpnt ) {
+ break;
+ }
scsi_init_cmd_from_req(SCpnt, SRpnt);
}
diff --git a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c
index d917d9306..84f29ce74 100644
--- a/drivers/scsi/scsi_merge.c
+++ b/drivers/scsi/scsi_merge.c
@@ -324,7 +324,7 @@ static inline int scsi_new_mergeable(request_queue_t * q,
req->nr_segments >= SHpnt->sg_tablesize)
return 0;
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
@@ -346,7 +346,7 @@ static inline int scsi_new_segment(request_queue_t * q,
return 0;
req->nr_hw_segments++;
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
#else
@@ -362,7 +362,7 @@ static inline int scsi_new_segment(request_queue_t * q,
* counter.
*/
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
} else {
return 0;
@@ -665,7 +665,7 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q,
* This one is OK. Let it go.
*/
req->nr_segments += next->nr_segments - 1;
- q->nr_segments--;
+ q->elevator.nr_segments--;
#ifdef DMA_CHUNK_SIZE
req->nr_hw_segments += next->nr_hw_segments - 1;
#endif
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 4060872bf..a43f2988c 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -252,12 +252,12 @@ static int get_device_flags(unsigned char *response_data)
* devices to the disk driver.
*/
void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun)
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun)
{
- int channel;
+ uint channel;
int dev;
int lun;
int max_dev_lun;
@@ -299,8 +299,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
initialize_merge_fn(SDpnt);
/*
@@ -405,7 +403,7 @@ void scan_scsis(struct Scsi_Host *shpnt,
leave:
- { /* Unchain SCpnt from host_queue */
+ { /* Unchain SRpnt from host_queue */
Scsi_Device *prev, *next;
Scsi_Device *dqptr;
@@ -423,8 +421,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
}
}
- scsi_release_commandblocks(SDpnt);
-
/* Last device block does not exist. Free memory. */
if (SDpnt != NULL)
kfree((char *) SDpnt);
@@ -460,7 +456,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
struct Scsi_Device_Template *sdtpnt;
Scsi_Device *SDtail, *SDpnt = *SDpnt2;
- Scsi_Cmnd * SCpnt;
+ Scsi_Request * SRpnt;
int bflags, type = -1;
static int ghost_channel=-1, ghost_dev=-1;
int org_lun = lun;
@@ -472,6 +468,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
SDpnt->channel = channel;
SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
SDpnt->lun = 0;
@@ -496,37 +493,32 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[1] = lun << 5;
scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
- SCpnt = scsi_allocate_device(SDpnt, 0, 0);
+ SRpnt = scsi_allocate_request(SDpnt);
- SCpnt->host = SDpnt->host;
- SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) NULL,
0, SCSI_TIMEOUT + 4 * HZ, 5);
SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
- dev, lun, SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+ dev, lun, SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_driverbyte(SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_hostbyte(SRpnt->sr_result));
SCSI_LOG_SCAN_BUS(3, printk("\n"));
- if (SCpnt->result) {
- if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
- (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
- ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
- if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
- ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
- ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ if (((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) ||
+ (status_byte(SRpnt->sr_result) & CHECK_CONDITION)) &&
+ ((SRpnt->sr_sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SRpnt->sr_sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
+ scsi_release_request(SRpnt);
return 1;
}
} else {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0;
}
}
@@ -540,18 +532,18 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[3] = 0;
scsi_cmd[4] = 255;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result,
256, SCSI_TIMEOUT, 3);
SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
- SCpnt->result ? "failed" : "successful", SCpnt->result));
+ SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
- if (SCpnt->result) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
@@ -560,7 +552,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
* are supported here or not.
*/
if ((scsi_result[0] >> 5) == 3) {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
@@ -705,15 +697,15 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[3] = 0;
scsi_cmd[4] = 0x2a;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result, 0x2a,
SCSI_TIMEOUT, 3);
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_release_commandblocks(SDpnt);
@@ -734,8 +726,6 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
/*
* Register the queue for the device. All I/O requests will come
* in through here. We also need to register a pointer to
diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
index f6e8939a6..94820f5db 100644
--- a/drivers/scsi/scsi_syms.c
+++ b/drivers/scsi/scsi_syms.c
@@ -43,7 +43,6 @@ EXPORT_SYMBOL(scsicam_bios_param);
EXPORT_SYMBOL(scsi_partsize);
EXPORT_SYMBOL(scsi_allocate_device);
EXPORT_SYMBOL(scsi_do_cmd);
-EXPORT_SYMBOL(scsi_wait_cmd);
EXPORT_SYMBOL(scsi_command_size);
EXPORT_SYMBOL(scsi_ioctl);
EXPORT_SYMBOL(print_command);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 43187600b..584a84905 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -176,6 +176,8 @@ static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
case BLKFLSBUF:
case BLKSSZGET:
case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
return blk_ioctl(inode->i_rdev, cmd, arg);
case BLKRRPART: /* Re-read partition tables */
@@ -660,7 +662,7 @@ static int sd_init_onedisk(int i)
unsigned long spintime_value = 0;
int the_result, retries, spintime;
int sector_size;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
/*
* Get the name of the disk, in case we need to log it somewhere.
@@ -679,7 +681,7 @@ static int sd_init_onedisk(int i)
* just after a scsi bus reset.
*/
- SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE);
+ SRpnt = scsi_allocate_request(rscsi_disks[i].device);
buffer = (unsigned char *) scsi_malloc(512);
@@ -694,18 +696,18 @@ static int sd_init_onedisk(int i)
cmd[0] = TEST_UNIT_READY;
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
+ scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries++;
if (the_result == 0
- || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
break;
}
@@ -716,8 +718,8 @@ static int sd_init_onedisk(int i)
*/
if( the_result != 0
&& ((driver_byte(the_result) & DRIVER_SENSE) != 0)
- && SCpnt->sense_buffer[2] == UNIT_ATTENTION
- && SCpnt->sense_buffer[12] == 0x3A ) {
+ && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
+ && SRpnt->sr_sense_buffer[12] == 0x3A ) {
rscsi_disks[i].capacity = 0x1fffff;
sector_size = 512;
rscsi_disks[i].device->changed = 1;
@@ -728,7 +730,7 @@ static int sd_init_onedisk(int i)
/* Look for non-removable devices that return NOT_READY.
* Issue command to spin up drive for these cases. */
if (the_result && !rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY) {
+ SRpnt->sr_sense_buffer[2] == NOT_READY) {
unsigned long time1;
if (!spintime) {
printk("%s: Spinning up disk...", nbuff);
@@ -737,12 +739,12 @@ static int sd_init_onedisk(int i)
cmd[1] |= 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
}
spintime = 1;
@@ -768,15 +770,15 @@ static int sd_init_onedisk(int i)
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
memset((void *) buffer, 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
8, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries--;
} while (the_result && retries);
@@ -806,7 +808,7 @@ static int sd_init_onedisk(int i)
);
if (driver_byte(the_result) & DRIVER_SENSE)
printk("%s : extended sense code = %1x \n",
- nbuff, SCpnt->sense_buffer[2] & 0xf);
+ nbuff, SRpnt->sr_sense_buffer[2] & 0xf);
else
printk("%s : sense not available. \n", nbuff);
@@ -818,7 +820,7 @@ static int sd_init_onedisk(int i)
/* Set dirty bit for removable devices if not ready - sometimes drives
* will not report this properly. */
if (rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY)
+ SRpnt->sr_sense_buffer[2] == NOT_READY)
rscsi_disks[i].device->changed = 1;
} else {
@@ -919,16 +921,16 @@ static int sd_init_onedisk(int i)
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
cmd[2] = 1; /* page code 1 ?? */
cmd[4] = 12;
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
/* same code as READCAPA !! */
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
512, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
if (the_result) {
printk("%s: test WP failed, assume Write Protected\n", nbuff);
@@ -940,12 +942,12 @@ static int sd_init_onedisk(int i)
}
} /* check for write protect */
- SCpnt->device->ten = 1;
- SCpnt->device->remap = 1;
- SCpnt->device->sector_size = sector_size;
+ SRpnt->sr_device->ten = 1;
+ SRpnt->sr_device->remap = 1;
+ SRpnt->sr_device->sector_size = sector_size;
/* Wake up a process waiting for device */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_free(buffer, 512);
return i;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 781cde85a..dc6712247 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -486,7 +486,7 @@ void get_sectorsize(int i)
SRpnt->sr_data_direction = SCSI_DATA_READ;
scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
- 512, SR_TIMEOUT, MAX_RETRIES);
+ 8, SR_TIMEOUT, MAX_RETRIES);
the_result = SRpnt->sr_result;
retries--;
@@ -663,7 +663,7 @@ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command
/* do the locking and issue the command */
SRpnt->sr_request.rq_dev = cdi->dev;
- /* scsi_wait_cmd sets the command length */
+ /* scsi_wait_req sets the command length */
SRpnt->sr_cmd_len = 0;
SRpnt->sr_data_direction = cgc->data_direction;
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 1de7686dc..8df062781 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -6,12 +6,13 @@
Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
Contribution and ideas from several people including (in alphabetical
order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
- Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+ Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and
+ Eric Youngdale.
Copyright 1992 - 2000 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue Feb 29 20:47:03 2000 by makisara@kai.makisara.local
+ Last modified: Mon Mar 13 21:15:29 2000 by makisara@kai.makisara.local
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -125,14 +126,17 @@ DEB( static int debugging = DEBUG; )
24 bits) */
#define SET_DENS_AND_BLK 0x10001
+#define ST_DEV_ARR_LUMP 6
+static rwlock_t st_dev_arr_lock = RW_LOCK_UNLOCKED;
+
static int st_nbr_buffers;
-static ST_buffer **st_buffers;
+static ST_buffer **st_buffers = NULL;
static int st_buffer_size = ST_BUFFER_SIZE;
static int st_write_threshold = ST_WRITE_THRESHOLD;
static int st_max_buffers = ST_MAX_BUFFERS;
static int st_max_sg_segs = ST_MAX_SG;
-static Scsi_Tape *scsi_tapes = NULL;
+static Scsi_Tape **scsi_tapes = NULL;
static int modes_defined = FALSE;
@@ -161,16 +165,14 @@ struct Scsi_Device_Template st_template =
static int st_compression(Scsi_Tape *, int);
-static int find_partition(struct inode *);
-static int update_partition(struct inode *);
+static int find_partition(Scsi_Tape *);
+static int update_partition(Scsi_Tape *);
-static int st_int_ioctl(struct inode *inode, unsigned int cmd_in,
- unsigned long arg);
+static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
-
/* Convert the result to success code */
-static int st_chk_result(Scsi_Request * SRpnt)
+static int st_chk_result(Scsi_Tape *STp, Scsi_Request * SRpnt)
{
int dev;
int result = SRpnt->sr_result;
@@ -184,8 +186,10 @@ static int st_chk_result(Scsi_Request * SRpnt)
if (driver_byte(result) & DRIVER_SENSE)
scode = sense[2] & 0x0f;
- else
+ else {
+ sense[0] = 0;
scode = 0;
+ }
dev = TAPE_NR(SRpnt->sr_request.rq_dev);
DEB(
@@ -224,8 +228,8 @@ static int st_chk_result(Scsi_Request * SRpnt)
&& SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
#endif
) {
- scsi_tapes[dev].recover_count++;
- scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
+ STp->recover_count++;
+ STp->recover_reg++;
DEB(
if (debugging) {
@@ -236,7 +240,7 @@ static int st_chk_result(Scsi_Request * SRpnt)
else
stp = "ioctl";
printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp,
- scsi_tapes[dev].recover_count);
+ STp->recover_count);
} ) /* end DEB */
if ((sense[2] & 0xe0) == 0)
@@ -254,7 +258,9 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt)
Scsi_Tape *STp;
if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < st_template.nr_dev) {
- STp = &(scsi_tapes[st_nbr]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[st_nbr];
+ read_unlock(&st_dev_arr_lock);
if ((STp->buffer)->writing &&
(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40)) {
@@ -330,7 +336,7 @@ static Scsi_Request *
if (do_wait) {
down(SRpnt->sr_request.sem);
SRpnt->sr_request.sem = NULL;
- (STp->buffer)->syscall_result = st_chk_result(SRpnt);
+ (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
}
return SRpnt;
}
@@ -354,7 +360,7 @@ static void write_behind_check(Scsi_Tape * STp)
down(&(STp->sem));
(STp->buffer)->last_SRpnt->sr_request.sem = NULL;
- (STp->buffer)->syscall_result = st_chk_result((STp->buffer)->last_SRpnt);
+ (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt);
scsi_release_request((STp->buffer)->last_SRpnt);
if (STbuffer->writing < STbuffer->buffer_bytes)
@@ -491,15 +497,12 @@ static int flush_write_buffer(Scsi_Tape * STp)
/* Flush the tape buffer. The tape will be positioned correctly unless
seek_next is true. */
-static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
+static int flush_buffer(Scsi_Tape *STp, int seek_next)
{
int backspace, result;
- Scsi_Tape *STp;
ST_buffer *STbuffer;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
/*
@@ -538,7 +541,7 @@ static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
}
}
if (!result && backspace > 0)
- result = st_int_ioctl(inode, MTBSR, backspace);
+ result = st_int_ioctl(STp, MTBSR, backspace);
} else if (STps->eof == ST_FM_HIT) {
if (STps->drv_file >= 0)
STps->drv_file++;
@@ -550,11 +553,11 @@ static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
}
/* Set the mode parameters */
-static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
+static int set_mode_densblk(Scsi_Tape * STp, ST_mode * STm)
{
int set_it = FALSE;
unsigned long arg;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
if (!STp->density_changed &&
STm->default_density >= 0 &&
@@ -572,7 +575,7 @@ static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
} else
arg |= STp->block_size;
if (set_it &&
- st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
+ st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
printk(KERN_WARNING
"st%d: Can't set default block size to %d bytes and density %x.\n",
dev, STm->default_blksize, STm->default_density);
@@ -597,22 +600,26 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
int dev = TAPE_NR(inode->i_rdev);
int mode = TAPE_MODE(inode->i_rdev);
- if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ if (dev >= st_template.dev_max || STp == NULL) {
+ read_unlock(&st_dev_arr_lock);
return (-ENXIO);
+ }
+ read_unlock(&st_dev_arr_lock);
- if (!scsi_block_when_processing_errors(scsi_tapes[dev].device)) {
+ if (!scsi_block_when_processing_errors(STp->device)) {
return -ENXIO;
}
- STp = &(scsi_tapes[dev]);
if (STp->in_use) {
DEB( printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); )
return (-EBUSY);
}
STp->in_use = 1;
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+ STp->rew_at_close = STp->autorew_dev = (MINOR(inode->i_rdev) & 0x80) == 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_INC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_INC_USE_COUNT(st_template.module);
@@ -626,10 +633,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
/* Allocate a buffer for this user */
need_dma_buffer = STp->restr_dma;
+ read_lock(&st_dev_arr_lock);
for (i = 0; i < st_nbr_buffers; i++)
if (!st_buffers[i]->in_use &&
- (!need_dma_buffer || st_buffers[i]->dma))
+ (!need_dma_buffer || st_buffers[i]->dma)) {
+ STp->buffer = st_buffers[i];
break;
+ }
+ read_unlock(&st_dev_arr_lock);
if (i >= st_nbr_buffers) {
STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
if (STp->buffer == NULL) {
@@ -637,8 +648,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
retval = (-EBUSY);
goto err_out;
}
- } else
- STp->buffer = st_buffers[i];
+ }
+
(STp->buffer)->in_use = 1;
(STp->buffer)->writing = 0;
(STp->buffer)->syscall_result = 0;
@@ -824,7 +835,7 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
partition support has been enabled. */
DEBC(printk(ST_DEB_MSG
"st%d: Updating partition number in status.\n", dev));
- if ((STp->partition = find_partition(inode)) < 0) {
+ if ((STp->partition = find_partition(STp)) < 0) {
retval = STp->partition;
goto err_out;
}
@@ -836,11 +847,11 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
STp->density_changed = STp->blksize_changed = FALSE;
STp->compression_changed = FALSE;
if (!(STm->defaults_for_writes) &&
- (retval = set_mode_densblk(inode, STp, STm)) < 0)
+ (retval = set_mode_densblk(STp, STm)) < 0)
goto err_out;
if (STp->default_drvbuffer != 0xff) {
- if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
+ if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
printk(KERN_WARNING
"st%d: Can't set default drive buffering to %d.\n",
dev, STp->default_drvbuffer);
@@ -855,8 +866,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
STp->buffer = NULL;
}
STp->in_use = 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_DEC_USE_COUNT(st_template.module);
return retval;
@@ -882,7 +893,9 @@ static int scsi_tape_flush(struct file *filp)
return 0;
dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
@@ -893,7 +906,7 @@ static int scsi_tape_flush(struct file *filp)
}
if (STp->can_partitions &&
- (result2 = update_partition(inode)) < 0) {
+ (result2 = update_partition(STp)) < 0) {
DEBC(printk(ST_DEB_MSG
"st%d: update_partition at close failed.\n", dev));
if (result == 0)
@@ -950,7 +963,7 @@ static int scsi_tape_flush(struct file *filp)
STps = &(STp->ps[STp->partition]);
if (!STm->sysv || STps->rw != ST_READING) {
if (STp->can_bsr)
- result = flush_buffer(inode, filp, 0);
+ result = flush_buffer(STp, 0);
else if (STps->eof == ST_FM_HIT) {
result = cross_eof(STp, FALSE);
if (result) {
@@ -973,7 +986,7 @@ static int scsi_tape_flush(struct file *filp)
out:
if (STp->rew_at_close) {
- result2 = st_int_ioctl(inode, MTREW, 1);
+ result2 = st_int_ioctl(STp, MTREW, 1);
if (result == 0)
result = result2;
}
@@ -991,10 +1004,12 @@ static int scsi_tape_close(struct inode *inode, struct file *filp)
int dev;
dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
if (STp->door_locked == ST_LOCKED_AUTO)
- st_int_ioctl(inode, MTUNLOCK, 0);
+ st_int_ioctl(STp, MTUNLOCK, 0);
if (STp->buffer != NULL) {
normalize_buffer(STp->buffer);
@@ -1002,8 +1017,8 @@ static int scsi_tape_close(struct inode *inode, struct file *filp)
}
STp->in_use = 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_DEC_USE_COUNT(st_template.module);
@@ -1028,7 +1043,9 @@ static ssize_t
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
/*
* If we are in the middle of error recovery, don't let anyone
@@ -1079,7 +1096,7 @@ static ssize_t
}
if (STp->can_partitions &&
- (retval = update_partition(inode)) < 0)
+ (retval = update_partition(STp)) < 0)
return retval;
STps = &(STp->ps[STp->partition]);
@@ -1092,17 +1109,17 @@ static ssize_t
return (-EOVERFLOW);
if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
+ !st_int_ioctl(STp, MTLOCK, 0))
STp->door_locked = ST_LOCKED_AUTO;
if (STps->rw == ST_READING) {
- retval = flush_buffer(inode, filp, 0);
+ retval = flush_buffer(STp, 0);
if (retval)
return retval;
STps->rw = ST_WRITING;
} else if (STps->rw != ST_WRITING &&
STps->drv_file == 0 && STps->drv_block == 0) {
- if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
+ if ((retval = set_mode_densblk(STp, STm)) < 0)
return retval;
if (STm->default_compression != ST_DONT_TOUCH &&
!(STp->compression_changed)) {
@@ -1328,21 +1345,19 @@ static ssize_t
/* 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 read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
+static long read_tape(Scsi_Tape *STp, long count, Scsi_Request ** aSRpnt)
{
int transfer, blks, bytes;
static unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
- Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
int retval = 0;
if (count == 0)
return 0;
- STp = &(scsi_tapes[dev]);
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
if (STps->eof == ST_FM_HIT)
@@ -1418,7 +1433,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
STps->drv_block += blks - transfer + 1;
- st_int_ioctl(inode, MTBSR, 1);
+ st_int_ioctl(STp, MTBSR, 1);
return (-EIO);
}
/* We have some data, deliver it */
@@ -1429,7 +1444,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
dev, count, (STp->buffer)->buffer_bytes));
if (STps->drv_block >= 0)
STps->drv_block += 1;
- if (st_int_ioctl(inode, MTBSR, 1))
+ if (st_int_ioctl(STp, MTBSR, 1))
return (-EIO);
}
} else if (SRpnt->sr_sense_buffer[2] & 0x80) { /* FM overrides EOM */
@@ -1509,7 +1524,9 @@ static ssize_t
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
/*
* If we are in the middle of error recovery, don't let anyone
@@ -1542,7 +1559,7 @@ static ssize_t
} ) /* end DEB */
if (STp->can_partitions &&
- (total = update_partition(inode)) < 0)
+ (total = update_partition(STp)) < 0)
return total;
if (STp->block_size == 0 &&
@@ -1555,12 +1572,12 @@ static ssize_t
return (-EIO); /* Read must be integral number of blocks */
if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
+ !st_int_ioctl(STp, MTLOCK, 0))
STp->door_locked = ST_LOCKED_AUTO;
STps = &(STp->ps[STp->partition]);
if (STps->rw == ST_WRITING) {
- transfer = flush_buffer(inode, filp, 0);
+ transfer = flush_buffer(STp, 0);
if (transfer)
return transfer;
STps->rw = ST_READING;
@@ -1596,7 +1613,7 @@ static ssize_t
/* Get new data if the buffer is empty */
if ((STp->buffer)->buffer_bytes == 0) {
- special = read_tape(inode, count - total, &SRpnt);
+ special = read_tape(STp, count - total, &SRpnt);
if (special < 0) { /* No need to continue read */
if (SRpnt != NULL) {
scsi_release_request(SRpnt);
@@ -1684,15 +1701,13 @@ static void st_log_options(Scsi_Tape * STp, ST_mode * STm, int dev)
}
-static int st_set_options(struct inode *inode, long options)
+static int st_set_options(Scsi_Tape *STp, long options)
{
int value;
long code;
- Scsi_Tape *STp;
ST_mode *STm;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
- STp = &(scsi_tapes[dev]);
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined) {
memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
@@ -1823,95 +1838,146 @@ static int st_set_options(struct inode *inode, long options)
return 0;
}
+#define MODE_HEADER_LENGTH 4
-#define COMPRESSION_PAGE 0x0f
-#define COMPRESSION_PAGE_LENGTH 16
+/* Mode header and page byte offsets */
+#define MH_OFF_DATA_LENGTH 0
+#define MH_OFF_MEDIUM_TYPE 1
+#define MH_OFF_DEV_SPECIFIC 2
+#define MH_OFF_BDESCS_LENGTH 3
+#define MP_OFF_PAGE_NBR 0
+#define MP_OFF_PAGE_LENGTH 1
-#define MODE_HEADER_LENGTH 4
+/* Mode header and page bit masks */
+#define MH_BIT_WP 0x80
+#define MP_MSK_PAGE_NBR 0x3f
-#define DCE_MASK 0x80
-#define DCC_MASK 0x40
-#define RED_MASK 0x60
+/* Don't return block descriptors */
+#define MODE_SENSE_OMIT_BDESCS 0x08
+#define MODE_SELECT_PAGE_FORMAT 0x10
-/* Control the compression with mode page 15. Algorithm not changed if zero. */
-static int st_compression(Scsi_Tape * STp, int state)
+/* Read a mode page into the tape buffer. The block descriptors are included
+ if incl_block_descs is true. */
+static int read_mode_page(Scsi_Tape *STp, int page, int omit_block_descs)
{
- int dev;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt = NULL;
- if (STp->ready != ST_READY)
- return (-EIO);
-
- /* Read the current page contents */
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = COMPRESSION_PAGE;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+ if (omit_block_descs)
+ cmd[1] = MODE_SENSE_OMIT_BDESCS;
+ cmd[2] = page;
+ cmd[4] = 255;
SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
STp->timeout, 0, TRUE);
if (SRpnt == NULL)
return (STp->buffer)->syscall_result;
- dev = TAPE_NR(SRpnt->sr_request.rq_dev);
+ scsi_release_request(SRpnt);
- if ((STp->buffer)->syscall_result != 0) {
+ return (STp->buffer)->syscall_result;
+}
+
+
+/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
+ in the buffer is correctly formatted. */
+static int write_mode_page(Scsi_Tape *STp, int page)
+{
+ int pgo;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ Scsi_Request *SRpnt = NULL;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = MODE_SELECT_PAGE_FORMAT;
+ pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
+ cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
+
+ /* Clear reserved fields */
+ (STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
+ (STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
+ (STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
+ (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
+ STp->timeout, 0, TRUE);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
+
+ scsi_release_request(SRpnt);
+
+ return (STp->buffer)->syscall_result;
+}
+
+
+#define COMPRESSION_PAGE 0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define CP_OFF_DCE_DCC 2
+
+#define DCE_MASK 0x80
+#define DCC_MASK 0x40
+#define RED_MASK 0x60
+
+
+/* Control the compression with mode page 15. Algorithm not changed if zero.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+ Including block descriptors should not cause any harm to other drives. */
+
+static int st_compression(Scsi_Tape * STp, int state)
+{
+ int retval;
+ int mpoffs; /* Offset to mode page start */
+ unsigned char *b_data = (STp->buffer)->b_data;
+ DEB( int dev = TAPE_NR(STp->devt); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ /* Read the current page contents */
+ retval = read_mode_page(STp, COMPRESSION_PAGE, FALSE);
+ if (retval) {
DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n",
dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
+
+ mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
- ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0)));
+ (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
/* Check if compression can be changed */
- if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+ if ((b_data[mpoffs + 2] & DCC_MASK) == 0) {
DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
/* Do the change */
if (state)
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
+ b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
else
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
+ b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- (STp->buffer)->b_data[0] = 0; /* Reserved data length */
- (STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
- (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
- STp->timeout, 0, TRUE);
-
- if ((STp->buffer)->syscall_result != 0) {
+ retval = write_mode_page(STp, COMPRESSION_PAGE);
+ if (retval) {
DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
dev, state));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
STp->compression_changed = TRUE;
return 0;
}
/* Internal ioctl function */
-static int st_int_ioctl(struct inode *inode,
- unsigned int cmd_in, unsigned long arg)
+static int st_int_ioctl(Scsi_Tape *STp, unsigned int cmd_in, unsigned long arg)
{
int timeout;
long ltmp;
@@ -1919,13 +1985,11 @@ static int st_int_ioctl(struct inode *inode,
int chg_eof = TRUE;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
- Scsi_Tape *STp;
ST_partstat *STps;
int fileno, blkno, at_sm, undone;
int datalen = 0, direction = SCSI_DATA_NONE;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY && cmd_in != MTLOAD) {
if (STp->ready == ST_NO_TAPE)
return (-ENOMEDIUM);
@@ -2120,7 +2184,7 @@ static int st_int_ioctl(struct inode *inode,
case MTEOM:
if (!STp->fast_mteom) {
/* space to the end of tape */
- ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
+ ioctl_result = st_int_ioctl(STp, MTFSF, 0x3fff);
fileno = STps->drv_file;
if (STps->eof >= ST_EOD_1)
return 0;
@@ -2247,9 +2311,9 @@ static int st_int_ioctl(struct inode *inode,
STp->door_locked = ST_UNLOCKED;
if (cmd_in == MTBSFM)
- ioctl_result = st_int_ioctl(inode, MTFSF, 1);
+ ioctl_result = st_int_ioctl(STp, MTFSF, 1);
else if (cmd_in == MTFSFM)
- ioctl_result = st_int_ioctl(inode, MTBSF, 1);
+ ioctl_result = st_int_ioctl(STp, MTBSF, 1);
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
STp->block_size = arg & MT_ST_BLKSIZE_MASK;
@@ -2275,7 +2339,7 @@ static int st_int_ioctl(struct inode *inode,
if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
STp->rew_at_close = 0;
else if (cmd_in == MTLOAD) {
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+ STp->rew_at_close = STp->autorew_dev;
for (i = 0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
STp->ps[i].last_block_valid = FALSE;
@@ -2368,16 +2432,14 @@ static int st_int_ioctl(struct inode *inode,
/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
structure. */
-static int get_location(struct inode *inode, unsigned int *block, int *partition,
+static int get_location(Scsi_Tape *STp, unsigned int *block, int *partition,
int logical)
{
- Scsi_Tape *STp;
- int dev = TAPE_NR(inode->i_rdev);
int result;
unsigned char scmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
+ DEB( int dev = TAPE_NR(STp->devt); )
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
@@ -2430,19 +2492,17 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition
/* Set the tape block and partition. Negative partition means that only the
block should be set in vendor specific way. */
-static int set_location(struct inode *inode, unsigned int block, int partition,
+static int set_location(Scsi_Tape *STp, unsigned int block, int partition,
int logical)
{
- Scsi_Tape *STp;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
int result, p;
unsigned int blk;
int timeout;
unsigned char scmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
+ DEB( int dev = TAPE_NR(STp->devt); )
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
timeout = STp->long_timeout;
@@ -2458,7 +2518,7 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
partition >= ST_NBR_PARTITIONS)
return (-EINVAL);
if (partition != STp->partition) {
- if (get_location(inode, &blk, &p, 1))
+ if (get_location(STp, &blk, &p, 1))
STps->last_block_valid = FALSE;
else {
STps->last_block_valid = TRUE;
@@ -2508,7 +2568,7 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
result = (-EIO);
if (STp->can_partitions &&
(STp->device)->scsi_level >= SCSI_2 &&
- (p = find_partition(inode)) >= 0)
+ (p = find_partition(STp)) >= 0)
STp->partition = p;
} else {
if (STp->can_partitions) {
@@ -2535,12 +2595,12 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
/* Find the current partition number for the drive status. Called from open and
returns either partition number of negative error code. */
-static int find_partition(struct inode *inode)
+static int find_partition(Scsi_Tape *STp)
{
int i, partition;
unsigned int block;
- if ((i = get_location(inode, &block, &partition, 1)) < 0)
+ if ((i = get_location(STp, &block, &partition, 1)) < 0)
return i;
if (partition >= ST_NBR_PARTITIONS)
return (-EIO);
@@ -2549,60 +2609,52 @@ static int find_partition(struct inode *inode)
/* Change the partition if necessary */
-static int update_partition(struct inode *inode)
+static int update_partition(Scsi_Tape *STp)
{
- int dev = TAPE_NR(inode->i_rdev);
- Scsi_Tape *STp;
ST_partstat *STps;
- STp = &(scsi_tapes[dev]);
if (STp->partition == STp->new_partition)
return 0;
STps = &(STp->ps[STp->new_partition]);
if (!STps->last_block_valid)
STps->last_block_visited = 0;
- return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
+ return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
}
/* Functions for reading and writing the medium partition mode page. These
seem to work with Wangtek 6200HS and HP C1533A. */
#define PART_PAGE 0x11
-#define PART_PAGE_LENGTH 10
+#define PART_PAGE_FIXED_LENGTH 8
+
+#define PP_OFF_MAX_ADD_PARTS 2
+#define PP_OFF_NBR_ADD_PARTS 3
+#define PP_OFF_FLAGS 4
+#define PP_OFF_PART_UNITS 6
+#define PP_OFF_RESERVED 7
+
+#define PP_BIT_IDP 0x20
+#define PP_MSK_PSUM_MB 0x10
/* Get the number of partitions on the tape. As a side effect reads the
mode page into the tape buffer. */
-static int nbr_partitions(struct inode *inode)
+static int nbr_partitions(Scsi_Tape *STp)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- Scsi_Tape *STp;
- Scsi_Request *SRpnt = NULL;
- unsigned char cmd[MAX_COMMAND_SIZE];
+ int result;
+ DEB( int dev = TAPE_NR(STp->devt) );
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
- memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8; /* Page format */
- cmd[2] = PART_PAGE;
- cmd[4] = 200;
-
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 200, SCSI_DATA_READ, STp->timeout,
- MAX_READY_RETRIES, TRUE);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ result = read_mode_page(STp, PART_PAGE, TRUE);
- scsi_release_request(SRpnt);
- SRpnt = NULL;
-
- if ((STp->buffer)->syscall_result != 0) {
+ if (result) {
DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n",
dev));
result = (-EIO);
} else {
- result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
+ result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
+ PP_OFF_NBR_ADD_PARTS] + 1;
DEBC(printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result));
}
@@ -2611,62 +2663,69 @@ static int nbr_partitions(struct inode *inode)
/* Partition the tape into two partitions if size > 0 or one partition if
- size == 0 */
-static int partition_tape(struct inode *inode, int size)
+ size == 0.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+
+ My HP C1533A drive returns only one partition size field. This is used to
+ set the size of partition 1. There is no size field for the default partition.
+ Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
+ used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
+ The following algorithm is used to accomodate both drives: if the number of
+ partition size fields is greater than the maximum number of additional partitions
+ in the mode page, the second field is used. Otherwise the first field is used.
+ */
+static int partition_tape(Scsi_Tape *STp, int size)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- int length;
- Scsi_Tape *STp;
- Scsi_Request *SRpnt = NULL;
- unsigned char cmd[MAX_COMMAND_SIZE], *bp;
+ int dev = TAPE_NR(STp->devt), result;
+ int pgo, psd_cnt, psdo;
+ unsigned char *bp;
- if ((result = nbr_partitions(inode)) < 0)
+ result = read_mode_page(STp, PART_PAGE, FALSE);
+ if (result) {
+ DEBC(printk(ST_DEB_MSG "st%d: Can't read partition mode page.\n", dev));
return result;
- STp = &(scsi_tapes[dev]);
-
+ }
/* The mode page is in the buffer. Let's modify it and write it. */
- bp = &((STp->buffer)->b_data[0]);
+ bp = (STp->buffer)->b_data;
+ pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
+ DEBC(printk(ST_DEB_MSG "st%d: Partition page length is %d bytes.\n",
+ dev, bp[pgo + MP_OFF_PAGE_LENGTH] + 2));
+
+ psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
+ psdo = pgo + PART_PAGE_FIXED_LENGTH;
+ if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
+ bp[psdo] = bp[psdo + 1] = 0xff; /* Rest of the tape */
+ psdo += 2;
+ }
+ memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
+
+ DEBC(printk("st%d: psd_cnt %d, max.parts %d, nbr_parts %d\n", dev,
+ psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
+ bp[pgo + PP_OFF_NBR_ADD_PARTS]));
+
if (size <= 0) {
- length = 8;
- bp[MODE_HEADER_LENGTH + 3] = 0;
+ bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
DEBC(printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n",
dev));
} else {
- length = 10;
- bp[MODE_HEADER_LENGTH + 3] = 1;
- bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
- bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
+ bp[psdo] = (size >> 8) & 0xff;
+ bp[psdo + 1] = size & 0xff;
+ bp[pgo + 3] = 1;
DEBC(printk(ST_DEB_MSG
- "st%d: Formatting tape with two partition (1 = %d MB).\n",
+ "st%d: Formatting tape with two partitions (1 = %d MB).\n",
dev, size));
}
- bp[MODE_HEADER_LENGTH + 6] = 0;
- bp[MODE_HEADER_LENGTH + 7] = 0;
- bp[MODE_HEADER_LENGTH + 4] = 0x30; /* IDP | PSUM = MB */
-
- bp[0] = 0;
- bp[1] = 0;
- bp[MODE_HEADER_LENGTH] &= 0x3f;
- bp[MODE_HEADER_LENGTH + 1] = length - 2;
+ bp[pgo + PP_OFF_PART_UNITS] = 0;
+ bp[pgo + PP_OFF_RESERVED] = 0;
+ bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB;
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = length + MODE_HEADER_LENGTH;
-
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
- STp->long_timeout, MAX_READY_RETRIES, TRUE);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
-
- scsi_release_request(SRpnt);
- SRpnt = NULL;
-
- if ((STp->buffer)->syscall_result != 0) {
+ result = write_mode_page(STp, PART_PAGE);
+ if (result) {
printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
result = (-EIO);
- } else
- result = 0;
+ }
return result;
}
@@ -2679,14 +2738,15 @@ static int st_ioctl(struct inode *inode, struct file *file,
{
int i, cmd_nr, cmd_type, bt;
unsigned int blk;
- struct mtop mtc;
- struct mtpos mt_pos;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
+
DEB(
if (debugging && !STp->in_use) {
printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
@@ -2709,6 +2769,8 @@ static int st_ioctl(struct inode *inode, struct file *file,
cmd_nr = _IOC_NR(cmd_in);
if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+ struct mtop mtc;
+
if (_IOC_SIZE(cmd_in) != sizeof(mtc))
return (-EINVAL);
@@ -2751,7 +2813,7 @@ static int st_ioctl(struct inode *inode, struct file *file,
mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
mtc.mt_op == MTCOMPRESSION;
}
- i = flush_buffer(inode, file, i);
+ i = flush_buffer(STp, i);
if (i < 0)
return i;
} else {
@@ -2770,7 +2832,7 @@ static int st_ioctl(struct inode *inode, struct file *file,
STp->device->was_reset = 0;
if (STp->door_locked != ST_UNLOCKED &&
STp->door_locked != ST_LOCK_FAILS) {
- if (st_int_ioctl(inode, MTLOCK, 0)) {
+ if (st_int_ioctl(STp, MTLOCK, 0)) {
printk(KERN_NOTICE
"st%d: Could not relock door after bus reset.\n",
dev);
@@ -2785,18 +2847,18 @@ static int st_ioctl(struct inode *inode, struct file *file,
STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
- st_int_ioctl(inode, MTUNLOCK, 0); /* Ignore result! */
+ st_int_ioctl(STp, MTUNLOCK, 0); /* Ignore result! */
if (mtc.mt_op == MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) != 0)
- return st_set_options(inode, mtc.mt_count);
+ return st_set_options(STp, mtc.mt_count);
if (mtc.mt_op == MTSETPART) {
if (!STp->can_partitions ||
mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
return (-EINVAL);
if (mtc.mt_count >= STp->nbr_partitions &&
- (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+ (STp->nbr_partitions = nbr_partitions(STp)) < 0)
return (-EIO);
if (mtc.mt_count >= STp->nbr_partitions)
return (-EINVAL);
@@ -2807,8 +2869,8 @@ static int st_ioctl(struct inode *inode, struct file *file,
if (mtc.mt_op == MTMKPART) {
if (!STp->can_partitions)
return (-EINVAL);
- if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
- (i = partition_tape(inode, mtc.mt_count)) < 0)
+ if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 ||
+ (i = partition_tape(STp, mtc.mt_count)) < 0)
return i;
for (i = 0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
@@ -2822,93 +2884,97 @@ static int st_ioctl(struct inode *inode, struct file *file,
}
if (mtc.mt_op == MTSEEK) {
- i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
+ i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
if (!STp->can_partitions)
STp->ps[0].rw = ST_IDLE;
return i;
}
if (STp->can_partitions && STp->ready == ST_READY &&
- (i = update_partition(inode)) < 0)
+ (i = update_partition(STp)) < 0)
return i;
if (mtc.mt_op == MTCOMPRESSION)
return st_compression(STp, (mtc.mt_count & 1));
else
- return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
+ return st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
}
if (!STm->defined)
return (-ENXIO);
- if ((i = flush_buffer(inode, file, FALSE)) < 0)
+ if ((i = flush_buffer(STp, FALSE)) < 0)
return i;
if (STp->can_partitions &&
- (i = update_partition(inode)) < 0)
+ (i = update_partition(STp)) < 0)
return i;
if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+ struct mtget mt_status;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
return (-EINVAL);
- (STp->mt_status)->mt_dsreg =
+ mt_status.mt_type = STp->tape_type;
+ mt_status.mt_dsreg =
((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
- (STp->mt_status)->mt_blkno = STps->drv_block;
- (STp->mt_status)->mt_fileno = STps->drv_file;
+ mt_status.mt_blkno = STps->drv_block;
+ mt_status.mt_fileno = STps->drv_file;
if (STp->block_size != 0) {
if (STps->rw == ST_WRITING)
- (STp->mt_status)->mt_blkno +=
+ mt_status.mt_blkno +=
(STp->buffer)->buffer_bytes / STp->block_size;
else if (STps->rw == ST_READING)
- (STp->mt_status)->mt_blkno -=
+ mt_status.mt_blkno -=
((STp->buffer)->buffer_bytes +
STp->block_size - 1) / STp->block_size;
}
- (STp->mt_status)->mt_gstat = 0;
+ mt_status.mt_gstat = 0;
if (STp->drv_write_prot)
- (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
- if ((STp->mt_status)->mt_blkno == 0) {
- if ((STp->mt_status)->mt_fileno == 0)
- (STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
+ mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+ if (mt_status.mt_blkno == 0) {
+ if (mt_status.mt_fileno == 0)
+ mt_status.mt_gstat |= GMT_BOT(0xffffffff);
else
- (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOF(0xffffffff);
}
- (STp->mt_status)->mt_resid = STp->partition;
+ mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
+ mt_status.mt_resid = STp->partition;
if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
- (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOT(0xffffffff);
else if (STps->eof >= ST_EOM_OK)
- (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOD(0xffffffff);
if (STp->density == 1)
- (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_800(0xffffffff);
else if (STp->density == 2)
- (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
else if (STp->density == 3)
- (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
if (STp->ready == ST_READY)
- (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+ mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
if (STp->ready == ST_NO_TAPE)
- (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
if (STps->at_sm)
- (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
+ mt_status.mt_gstat |= GMT_SM(0xffffffff);
if (STm->do_async_writes ||
(STm->do_buffer_writes && STp->block_size != 0) ||
STp->drv_buffer != 0)
- (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+ mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
- i = copy_to_user((char *) arg, (char *) (STp->mt_status),
+ i = copy_to_user((char *) arg, (char *) &(mt_status),
sizeof(struct mtget));
if (i)
return (-EFAULT);
- (STp->mt_status)->mt_erreg = 0; /* Clear after read */
+ STp->recover_reg = 0; /* Clear after read */
return 0;
} /* End of MTIOCGET */
if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+ struct mtpos mt_pos;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
return (-EINVAL);
- if ((i = get_location(inode, &blk, &bt, 0)) < 0)
+ if ((i = get_location(STp, &blk, &bt, 0)) < 0)
return i;
mt_pos.mt_blkno = blk;
i = copy_to_user((char *) arg, (char *) (&mt_pos), sizeof(struct mtpos));
@@ -2920,15 +2986,21 @@ static int st_ioctl(struct inode *inode, struct file *file,
}
-/* Try to allocate a new tape buffer */
+/* Try to allocate a new tape buffer. Calling function must not hold
+ dev_arr_lock. */
static ST_buffer *
new_tape_buffer(int from_initialization, int need_dma)
{
int i, priority, b_size, order, got = 0, segs = 0;
+ unsigned long flags;
ST_buffer *tb;
- if (st_nbr_buffers >= st_template.dev_max)
+ read_lock(&st_dev_arr_lock);
+ if (st_nbr_buffers >= st_template.dev_max) {
+ read_unlock(&st_dev_arr_lock);
return NULL; /* Should never happen */
+ }
+ read_unlock(&st_dev_arr_lock);
if (from_initialization)
priority = GFP_ATOMIC;
@@ -3014,7 +3086,10 @@ static ST_buffer *
tb->dma = need_dma;
tb->buffer_size = got;
tb->writing = 0;
+
+ write_lock_irqsave(&st_dev_arr_lock, flags);
st_buffers[st_nbr_buffers++] = tb;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return tb;
}
@@ -3039,7 +3114,8 @@ static int enlarge_buffer(ST_buffer * STbuffer, int new_size, int need_dma)
priority |= GFP_DMA;
for (b_size = PAGE_SIZE, order=0;
b_size * nbr < new_size - STbuffer->buffer_size;
- order++, b_size *= 2);
+ order++, b_size *= 2)
+ ; /* empty */
for (segs = STbuffer->sg_segs, got = STbuffer->buffer_size;
segs < max_segs && got < new_size;) {
@@ -3080,7 +3156,7 @@ static void normalize_buffer(ST_buffer * STbuffer)
for (i = STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
for (b_size=PAGE_SIZE, order=0; b_size < STbuffer->sg[i].length;
order++, b_size *= 2)
- ;
+ ; /* empty */
free_pages((unsigned long)(STbuffer->sg[i].address), order);
STbuffer->buffer_size -= STbuffer->sg[i].length;
}
@@ -3239,23 +3315,77 @@ static int st_attach(Scsi_Device * SDp)
Scsi_Tape *tpnt;
ST_mode *STm;
ST_partstat *STps;
- int i, mode;
+ int i, mode, target_nbr;
+ unsigned long flags = 0;
if (SDp->type != TYPE_TAPE)
return 1;
+ write_lock_irqsave(&st_dev_arr_lock, flags);
if (st_template.nr_dev >= st_template.dev_max) {
- SDp->attached--;
- return 1;
+ Scsi_Tape **tmp_da;
+ ST_buffer **tmp_ba;
+ int tmp_dev_max;
+
+ tmp_dev_max = st_template.nr_dev + ST_DEV_ARR_LUMP;
+ if (tmp_dev_max > ST_MAX_TAPES)
+ tmp_dev_max = ST_MAX_TAPES;
+ if (tmp_dev_max <= st_template.nr_dev) {
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Too many tape devices (max. %d).\n",
+ ST_MAX_TAPES);
+ return 1;
+ }
+
+ tmp_da = (Scsi_Tape **) kmalloc(tmp_dev_max * sizeof(Scsi_Tape *),
+ GFP_ATOMIC);
+ tmp_ba = (ST_buffer **) kmalloc(tmp_dev_max * sizeof(ST_buffer *),
+ GFP_ATOMIC);
+ if (tmp_da == NULL || tmp_ba == NULL) {
+ if (tmp_da != NULL)
+ kfree(tmp_da);
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Can't extend device array.\n");
+ return 1;
+ }
+
+ memset(tmp_da, 0, tmp_dev_max * sizeof(Scsi_Tape *));
+ if (scsi_tapes != NULL) {
+ memcpy(tmp_da, scsi_tapes,
+ st_template.dev_max * sizeof(Scsi_Tape *));
+ kfree(scsi_tapes);
+ }
+ scsi_tapes = tmp_da;
+
+ memset(tmp_ba, 0, tmp_dev_max * sizeof(ST_buffer *));
+ if (st_buffers != NULL) {
+ memcpy(tmp_ba, st_buffers,
+ st_template.dev_max * sizeof(ST_buffer *));
+ kfree(st_buffers);
+ }
+ st_buffers = tmp_ba;
+
+ st_template.dev_max = tmp_dev_max;
}
- for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
- if (!tpnt->device)
+ for (i = 0; i < st_template.dev_max; i++)
+ if (scsi_tapes[i] == NULL)
break;
-
if (i >= st_template.dev_max)
panic("scsi_devices corrupt (st)");
+ tpnt = (Scsi_Tape *)kmalloc(sizeof(Scsi_Tape), GFP_ATOMIC);
+ if (tpnt == NULL) {
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Can't allocate device descriptor.\n");
+ return 1;
+ }
+ memset(tpnt, 0, sizeof(Scsi_Tape));
+ scsi_tapes[i] = tpnt;
+
for (mode = 0; mode < ST_NBR_MODES; ++mode) {
char name[8];
static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
@@ -3276,11 +3406,11 @@ static int st_attach(Scsi_Device * SDp)
0, 0, &st_fops, NULL);
}
devfs_register_tape (tpnt->de_r[0]);
- scsi_tapes[i].device = SDp;
+ tpnt->device = SDp;
if (SDp->scsi_level <= 2)
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+ tpnt->tape_type = MT_ISSCSI1;
else
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+ tpnt->tape_type = MT_ISSCSI2;
tpnt->inited = 0;
tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
@@ -3333,6 +3463,20 @@ static int st_attach(Scsi_Device * SDp)
tpnt->blksize_changed = FALSE;
st_template.nr_dev++;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+
+ /* See if we need to allocate more static buffers */
+ target_nbr = st_template.nr_dev;
+ if (target_nbr > st_max_buffers)
+ target_nbr = st_max_buffers;
+ for (i=st_nbr_buffers; i < target_nbr; i++)
+ if (!new_tape_buffer(TRUE, TRUE)) {
+ printk(KERN_INFO "st: Unable to allocate new static buffer.\n");
+ break;
+ }
+ /* If the previous allocation fails, we will try again when the buffer is
+ really needed. */
+
return 0;
};
@@ -3354,90 +3498,28 @@ static int st_registered = 0;
/* Driver initialization (not __init because may be called later) */
static int st_init()
{
- int i, j;
- Scsi_Tape *STp;
- int target_nbr;
+ unsigned long flags;
- if (st_template.dev_noticed == 0)
+ if (st_template.dev_noticed == 0 || st_registered)
return 0;
printk(KERN_INFO "st: bufsize %d, wrt %d, max init. buffers %d, s/g segs %d.\n",
st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
+ write_lock_irqsave(&st_dev_arr_lock, flags);
if (!st_registered) {
if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
MAJOR_NR);
return 1;
}
st_registered++;
}
- if (scsi_tapes)
- return 0;
- st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
- if (st_template.dev_max < ST_MAX_TAPES)
- st_template.dev_max = ST_MAX_TAPES;
- if (st_template.dev_max > 128 / ST_NBR_MODES)
- printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
- scsi_tapes =
- (Scsi_Tape *) kmalloc(st_template.dev_max * sizeof(Scsi_Tape),
- GFP_ATOMIC);
- if (scsi_tapes == NULL) {
- printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
-
- DEB(printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n",
- st_buffer_size, st_write_threshold));
- memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
- for (i = 0; i < st_template.dev_max; ++i) {
- STp = &(scsi_tapes[i]);
- STp->capacity = 0xfffff;
- STp->mt_status = (struct mtget *) kmalloc(sizeof(struct mtget),
- GFP_ATOMIC);
- if (STp->mt_status == NULL) {
- for (j=0; j < i; j++)
- kfree(scsi_tapes[j].mt_status);
- kfree(scsi_tapes);
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
- /* Initialize status */
- memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
- }
-
- /* Allocate the buffers */
- st_buffers =
- (ST_buffer **) kmalloc(st_template.dev_max * sizeof(ST_buffer *),
- GFP_ATOMIC);
- if (st_buffers == NULL) {
- printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- for (i=0; i < st_template.dev_max; i++)
- kfree(scsi_tapes[i].mt_status);
- kfree(scsi_tapes);
- unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
- target_nbr = st_template.dev_noticed;
- if (target_nbr < ST_EXTRA_DEVS)
- target_nbr = ST_EXTRA_DEVS;
- if (target_nbr > st_max_buffers)
- target_nbr = st_max_buffers;
-
- for (i = st_nbr_buffers = 0; i < target_nbr; i++) {
- if (!new_tape_buffer(TRUE, TRUE)) {
- if (i == 0) {
- printk(KERN_INFO
- "No tape buffers allocated at initialization.\n");
- break;
- }
- printk(KERN_INFO "Number of tape buffers adjusted.\n");
- break;
- }
- }
+ st_template.dev_max = 0;
+ st_nbr_buffers = 0;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return 0;
}
@@ -3446,9 +3528,12 @@ static void st_detach(Scsi_Device * SDp)
{
Scsi_Tape *tpnt;
int i, mode;
+ unsigned long flags;
- for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
- if (tpnt->device == SDp) {
+ write_lock_irqsave(&st_dev_arr_lock, flags);
+ for (i = 0; i < st_template.dev_max; i++) {
+ tpnt = scsi_tapes[i];
+ if (tpnt != NULL && tpnt->device == SDp) {
tpnt->device = NULL;
for (mode = 0; mode < ST_NBR_MODES; ++mode) {
devfs_unregister (tpnt->de_r[mode]);
@@ -3456,11 +3541,17 @@ static void st_detach(Scsi_Device * SDp)
devfs_unregister (tpnt->de_n[mode]);
tpnt->de_n[mode] = NULL;
}
+ kfree(tpnt);
+ scsi_tapes[i] = 0;
SDp->attached--;
st_template.nr_dev--;
st_template.dev_noticed--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return;
}
+ }
+
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return;
}
@@ -3484,7 +3575,8 @@ void cleanup_module(void)
st_registered--;
if (scsi_tapes != NULL) {
for (i=0; i < st_template.dev_max; ++i)
- kfree(scsi_tapes[i].mt_status);
+ if (scsi_tapes[i])
+ kfree(scsi_tapes[i]);
kfree(scsi_tapes);
if (st_buffers != NULL) {
for (i = 0; i < st_nbr_buffers; i++) {
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index e751efc28..47b3fbff5 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -47,6 +47,7 @@ typedef struct {
#define ST_NBR_MODES (1 << ST_NBR_MODE_BITS)
#define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
#define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
+#define ST_MAX_TAPES (1 << ST_MODE_SHIFT)
/* The status related to each partition */
typedef struct {
@@ -64,7 +65,6 @@ typedef struct {
/* The tape drive descriptor */
typedef struct {
kdev_t devt;
- unsigned capacity;
Scsi_Device *device;
struct semaphore sem;
ST_buffer *buffer;
@@ -79,6 +79,7 @@ typedef struct {
unsigned char restr_dma;
unsigned char scsi2_logical;
unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */
+ int tape_type;
int write_threshold;
int timeout; /* timeout for normal commands */
int long_timeout; /* timeout for commands known to take long time */
@@ -105,13 +106,14 @@ typedef struct {
unsigned char drv_buffer;
unsigned char density;
unsigned char door_locked;
- unsigned char rew_at_close;
+ unsigned char autorew_dev; /* auto-rewind device */
+ unsigned char rew_at_close; /* rewind necessary at close */
unsigned char inited;
int block_size;
int min_block;
int max_block;
- int recover_count;
- struct mtget *mt_status;
+ int recover_count; /* From tape opening */
+ int recover_reg; /* From last status call */
#if DEBUG
unsigned char write_pending;
@@ -122,7 +124,6 @@ typedef struct {
#endif
} Scsi_Tape;
-extern Scsi_Tape *scsi_tapes;
/* Values of eof */
#define ST_NOEOF 0
diff --git a/drivers/scsi/st_options.h b/drivers/scsi/st_options.h
index 8cbc1c69e..fa3926c5d 100644
--- a/drivers/scsi/st_options.h
+++ b/drivers/scsi/st_options.h
@@ -3,17 +3,12 @@
Copyright 1995-2000 Kai Makisara.
- Last modified: Sat Jan 1 18:34:38 2000 by makisara@kai.makisara.local
+ Last modified: Sat Mar 11 10:32:00 2000 by makisara@kai.makisara.local
*/
#ifndef _ST_OPTIONS_H
#define _ST_OPTIONS_H
-/* The minimum limit for the number of SCSI tape devices is determined by
- ST_MAX_TAPES. If the number of tape devices and the "slack" defined by
- ST_EXTRA_DEVS exceeds ST_MAX_TAPES, the large number is used. */
-#define ST_MAX_TAPES 4
-
/* The driver does not wait for some operations to finish before returning
to the user program if ST_NOWAIT is non-zero. This helps if the SCSI
adapter does not support multiple outstanding commands. However, the user
@@ -47,7 +42,7 @@
driver initialisation. The number is also constrained by the number
of drives detected. If more buffers are needed, they are allocated
at run time and freed after use. */
-#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
+#define ST_MAX_BUFFERS 4
/* Maximum number of scatter/gather segments */
#define ST_MAX_SG 16
diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c
index 36eb7b0c1..f9bbce41e 100644
--- a/drivers/scsi/sym53c8xx.c
+++ b/drivers/scsi/sym53c8xx.c
@@ -55,7 +55,7 @@
*/
/*
-** February 20 2000, sym53c8xx 1.5j
+** March 6 2000, sym53c8xx 1.5k
**
** Supported SCSI features:
** Synchronous data transfers
@@ -84,7 +84,7 @@
/*
** Name and version of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5j"
+#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5k"
/* #define DEBUG_896R1 */
#define SCSI_NCR_OPTIMIZE_896
@@ -174,6 +174,9 @@ typedef u64 u_int64;
#include "sym53c8xx.h"
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
/*
** Hmmm... What complex some PCI-HOST bridges actually are,
** despite the fact that the PCI specifications are looking
@@ -1000,8 +1003,9 @@ static m_addr_t ___dma_getp(m_pool_s *mp)
++mp->nump;
return vp;
}
+ else
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
}
- __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
return 0;
}
@@ -1253,7 +1257,7 @@ static void ncr_printl_hex(char *label, u_char *p, int n)
#define SCSI_DATA_READ 2
#define SCSI_DATA_NONE 3
-static __inline__ scsi_data_direction(Scsi_Cmnd *cmd)
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
{
int direction;
@@ -2043,7 +2047,7 @@ struct head {
#define HF_ACT_PM (1u<<2)
#define HF_DP_SAVED (1u<<3)
#define HF_AUTO_SENSE (1u<<4)
-#define HF_DATA_ST (1u<<5)
+#define HF_DATA_IN (1u<<5)
#define HF_PM_TO_C (1u<<6)
#ifdef SCSI_NCR_IARB_SUPPORT
@@ -2051,6 +2055,11 @@ struct head {
#endif
/*
+** This one is stolen from QU_REG.:)
+*/
+#define HF_DATA_ST (1u<<7)
+
+/*
** First four bytes (script)
*/
#define xerr_st header.scr_st[0]
@@ -2568,8 +2577,12 @@ struct script {
ncrcmd data_in2 [ 4];
ncrcmd data_out [MAX_SCATTER * SCR_SG_SIZE];
ncrcmd data_out2 [ 4];
- ncrcmd pm0_data [ 16];
- ncrcmd pm1_data [ 16];
+ ncrcmd pm0_data [ 12];
+ ncrcmd pm0_data_out [ 6];
+ ncrcmd pm0_data_end [ 6];
+ ncrcmd pm1_data [ 12];
+ ncrcmd pm1_data_out [ 6];
+ ncrcmd pm1_data_end [ 6];
};
/*
@@ -2607,7 +2620,7 @@ struct scripth {
ncrcmd sdata_in [ 6];
ncrcmd data_io [ 2];
ncrcmd data_io_com [ 8];
- ncrcmd data_io_out [ 10];
+ ncrcmd data_io_out [ 12];
ncrcmd bad_identify [ 12];
ncrcmd bad_i_t_l [ 4];
ncrcmd bad_i_t_l_q [ 4];
@@ -3146,12 +3159,11 @@ static struct script script0 __initdata = {
}/*-------------------------< DATAPHASE >------------------*/,{
#ifdef SCSI_NCR_PROFILE_SUPPORT
- SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST),
+ SCR_REG_REG (QU_REG, SCR_OR, HF_DATA_ST),
0,
#endif
SCR_RETURN,
- 0,
-
+ 0,
}/*-------------------------< MSG_IN >--------------------*/,{
/*
** Get the first byte of the message.
@@ -3390,7 +3402,7 @@ static struct script script0 __initdata = {
*/
SCR_LOAD_REL (scratcha, 4),
offsetof (struct ccb, phys.num_disc),
- SCR_FROM_REG (HF_REG),
+ SCR_FROM_REG (QU_REG),
0,
SCR_JUMPR ^ IFTRUE (MASK (HF_DATA_ST, HF_DATA_ST)),
8,
@@ -3692,26 +3704,57 @@ static struct script script0 __initdata = {
}/*-------------------------< PM0_DATA >--------------------*/,{
/*
- ** Keep track we are executing the PM0 DATA
- ** mini-script.
+ ** Read our host flags to SFBR, so we will be able
+ ** to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ ** Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR (pm0_data_out),
+ /*
+ ** Actual phase is DATA IN.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM0 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
0,
/*
- ** MOVE the data according to the actual
- ** DATA direction.
+ ** Move the data to memory.
*/
- SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
- 16,
SCR_CHMOV_TBL ^ SCR_DATA_IN,
offsetof (struct ccb, phys.pm0.sg),
- SCR_JUMPR,
- 8,
+ SCR_JUMP,
+ PADDR (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >----------------*/,{
+ /*
+ ** Actual phase is DATA OUT.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ ** Move the data from memory.
+ */
SCR_CHMOV_TBL ^ SCR_DATA_OUT,
offsetof (struct ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >----------------*/,{
/*
- ** Clear the flag that told we were in
- ** the PM0 DATA mini-script.
+ ** Clear the flag that told we were moving
+ ** data from the PM0 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
0,
@@ -3726,26 +3769,57 @@ static struct script script0 __initdata = {
0,
}/*-------------------------< PM1_DATA >--------------------*/,{
/*
- ** Keep track we are executing the PM1 DATA
- ** mini-script.
+ ** Read our host flags to SFBR, so we will be able
+ ** to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ ** Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR (pm1_data_out),
+ /*
+ ** Actual phase is DATA IN.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM1 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
0,
/*
- ** MOVE the data according to the actual
- ** DATA direction.
+ ** Move the data to memory.
*/
- SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
- 16,
SCR_CHMOV_TBL ^ SCR_DATA_IN,
offsetof (struct ccb, phys.pm1.sg),
- SCR_JUMPR,
- 8,
+ SCR_JUMP,
+ PADDR (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >----------------*/,{
+ /*
+ ** Actual phase is DATA OUT.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ ** Move the data from memory.
+ */
SCR_CHMOV_TBL ^ SCR_DATA_OUT,
offsetof (struct ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >----------------*/,{
/*
- ** Clear the flag that told we were in
- ** the PM1 DATA mini-script.
+ ** Clear the flag that told we were moving
+ ** data from the PM1 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
0,
@@ -4193,6 +4267,8 @@ static struct scripth scripth0 __initdata = {
/*
** Direction is DATA OUT.
*/
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+ 0,
SCR_LOAD_REL (scratcha, 4),
offsetof (struct ccb, phys.header.wlastp),
SCR_STORE_REL (scratcha, 4),
@@ -5550,7 +5626,7 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
goto attach_error;
NCR_INIT_LOCK_NCB(np);
np->pdev = device->pdev;
- np->p_ncb = __vtobus(device->pdev, np);
+ np->p_ncb = vtobus(np);
host_data->ncb = np;
/*
@@ -6119,18 +6195,18 @@ static void ncr_free_resources(ncb_p np)
*/
static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
{
+ unmap_scsi_data(np, cmd);
cmd->host_scribble = (char *) np->done_list;
np->done_list = cmd;
}
-static inline void ncr_flush_done_cmds(pcidev_t pdev, Scsi_Cmnd *lcmd)
+static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd)
{
Scsi_Cmnd *cmd;
while (lcmd) {
cmd = lcmd;
lcmd = (Scsi_Cmnd *) cmd->host_scribble;
- __unmap_scsi_data(pdev, cmd);
cmd->scsi_done(cmd);
}
}
@@ -6411,6 +6487,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
cp->phys.header.wlastp = cpu_to_scr(lastp);
/* fall through */
case SCSI_DATA_READ:
+ cp->host_flags |= HF_DATA_IN;
goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
break;
@@ -6488,7 +6565,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
/*
** command
*/
- memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len);
+ memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
@@ -9154,7 +9231,7 @@ next:
cp->scsi_status = S_ILLEGAL;
cp->xerr_status = 0;
cp->phys.extra_bytes = 0;
- cp->host_flags &= HF_PM_TO_C;
+ cp->host_flags &= (HF_PM_TO_C|HF_DATA_IN);
break;
@@ -9221,7 +9298,7 @@ next:
*/
cp->sensecmd[0] = 0x03;
cp->sensecmd[1] = cp->lun << 5;
- cp->sensecmd[4] = sizeof(cmd->sense_buffer);
+ cp->sensecmd[4] = sizeof(cp->sense_buf);
/*
** sense data
@@ -9243,7 +9320,7 @@ next:
cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
cp->scsi_status = S_ILLEGAL;
- cp->host_flags = HF_AUTO_SENSE;
+ cp->host_flags = (HF_AUTO_SENSE|HF_DATA_IN);
cp->phys.header.go.start =
cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
@@ -12616,8 +12693,10 @@ printk("sym53c8xx : command successfully queued\n");
NCR_UNLOCK_NCB(np, flags);
- if (sts != DID_OK)
+ if (sts != DID_OK) {
+ unmap_scsi_data(np, cmd);
done(cmd);
+ }
return sts;
}
@@ -12635,7 +12714,6 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
unsigned long flags;
ncb_p np = (ncb_p) dev_id;
Scsi_Cmnd *done_list;
- pcidev_t pdev;
#ifdef DEBUG_SYM53C8XX
printk("sym53c8xx : interrupt received\n");
@@ -12645,7 +12723,6 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
NCR_LOCK_NCB(np, flags);
ncr_exception(np);
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
@@ -12654,7 +12731,7 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
if (done_list) {
NCR_LOCK_SCSI_DONE(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
NCR_UNLOCK_SCSI_DONE(np, flags);
}
}
@@ -12667,19 +12744,17 @@ static void sym53c8xx_timeout(unsigned long npref)
{
ncb_p np = (ncb_p) npref;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
NCR_LOCK_NCB(np, flags);
ncr_timeout((ncb_p) np);
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
if (done_list) {
NCR_LOCK_SCSI_DONE(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
NCR_UNLOCK_SCSI_DONE(np, flags);
}
}
@@ -12697,7 +12772,6 @@ int sym53c8xx_reset(Scsi_Cmnd *cmd)
ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
int sts;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12742,12 +12816,11 @@ int sym53c8xx_reset(Scsi_Cmnd *cmd)
#endif
out:
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
return sts;
}
@@ -12761,7 +12834,6 @@ int sym53c8xx_abort(Scsi_Cmnd *cmd)
ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
int sts;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12785,12 +12857,11 @@ int sym53c8xx_abort(Scsi_Cmnd *cmd)
sts = ncr_abort_command(np, cmd);
out:
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
return sts;
}
diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h
new file mode 100644
index 000000000..fa02ff585
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_comm.h
@@ -0,0 +1,2863 @@
+/******************************************************************************
+** High Performance device driver for the Symbios 53C896 controller.
+**
+** Copyright (C) 1998-2000 Gerard Roudier <groudier@club-internet.fr>
+**
+** This driver also supports all the Symbios 53C8XX controller family,
+** except 53C810 revisions < 16, 53C825 revisions < 16 and all
+** revisions of 53C815 controllers.
+**
+** This driver is based on the Linux port of the FreeBSD ncr driver.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+**-----------------------------------------------------------------------------
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+** The Linux port of the FreeBSD ncr driver has been achieved in
+** november 1995 by:
+**
+** Gerard Roudier <groudier@club-internet.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+** Major contributions:
+** --------------------
+**
+** NVRAM detection and reading.
+** Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+*******************************************************************************
+*/
+
+/*
+** This file contains definitions and code that the
+** sym53c8xx and ncr53c8xx drivers should share.
+** The sharing will be achieved in a further version
+** of the driver bundle. For now, only the ncr53c8xx
+** driver includes this file.
+*/
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+/*==========================================================
+**
+** Hmmm... What complex some PCI-HOST bridges actually
+** are, despite the fact that the PCI specifications
+** are looking so smart and simple! ;-)
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define SCSI_NCR_DYNAMIC_DMA_MAPPING
+#endif
+
+/*==========================================================
+**
+** Io mapped versus memory mapped.
+**
+**==========================================================
+*/
+
+#if defined(SCSI_NCR_IOMAPPED) || defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+#define NCR_IOMAPPED
+#endif
+
+/*==========================================================
+**
+** Miscallaneous defines.
+**
+**==========================================================
+*/
+
+#define u_char unsigned char
+#define u_short unsigned short
+#define u_int unsigned int
+#define u_long unsigned long
+
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+
+#ifndef bcmp
+#define bcmp(s, d, n) memcmp((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(d, n) memset((d), 0, (n))
+#endif
+
+#ifndef offsetof
+#define offsetof(t, m) ((size_t) (&((t *)0)->m))
+#endif
+
+/*==========================================================
+**
+** assert ()
+**
+**==========================================================
+**
+** modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)panic( \
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+
+/*==========================================================
+**
+** Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_POINTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_NEGO (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_SCATTER (0x0800)
+
+/*
+** Enable/Disable debug messages.
+** Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+ #define DEBUG_FLAGS ncr_debug
+#else
+ #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
+#endif
+
+/*==========================================================
+**
+** A la VMS/CAM-3 queue management.
+** Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+ struct xpt_quehead *flink; /* Forward pointer */
+ struct xpt_quehead *blink; /* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+ (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+ struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
+{
+ flink->blink = new;
+ new->flink = flink;
+ new->blink = blink;
+ blink->flink = new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
+{
+ flink->blink = blink;
+ blink->flink = flink;
+}
+
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+ return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+ struct xpt_quehead *head)
+{
+ struct xpt_quehead *first = list->flink;
+
+ if (first != list) {
+ struct xpt_quehead *last = list->blink;
+ struct xpt_quehead *at = head->flink;
+
+ first->blink = head;
+ head->flink = first;
+
+ last->flink = at;
+ at->blink = last;
+ }
+}
+
+#define xpt_que_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos) __xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el) __xpt_que_del((el)->blink, (el)->flink)
+
+#define xpt_insque_head(new, head) __xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->flink;
+
+ if (elem != head)
+ __xpt_que_del(head, elem->flink);
+ else
+ elem = 0;
+ return elem;
+}
+
+#define xpt_insque_tail(new, head) __xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->blink;
+
+ if (elem != head)
+ __xpt_que_del(elem->blink, head);
+ else
+ elem = 0;
+ return elem;
+}
+
+/*==========================================================
+**
+** On x86 architecture, write buffers management does
+** not reorder writes to memory. So, using compiler
+** optimization barriers is enough to guarantee some
+** ordering when the CPU is writing data accessed by
+** the NCR.
+** On Alpha architecture, explicit memory barriers have
+** to be used.
+** Other architectures are defaulted to mb() macro if
+** defined, otherwise use compiler barrier.
+**
+**==========================================================
+*/
+
+#if defined(__i386__)
+#define MEMORY_BARRIER() barrier()
+#elif defined(__alpha__)
+#define MEMORY_BARRIER() mb()
+#else
+# ifdef mb
+# define MEMORY_BARRIER() mb()
+# else
+# define MEMORY_BARRIER() barrier()
+# endif
+#endif
+
+/*==========================================================
+**
+** Simple Wrapper to kernel PCI bus interface.
+**
+** This wrapper allows to get rid of old kernel PCI
+** interface and still allows to preserve linux-2.0
+** compatibilty. In fact, it is mostly an incomplete
+** emulation of the new PCI code for pre-2.2 kernels.
+** When kernel-2.0 support will be dropped, we will
+** just have to remove most of this code.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL (0)
+#define PciBusNumber(d) (d)->bus->number
+#define PciDeviceFn(d) (d)->devfn
+#define PciVendorId(d) (d)->vendor
+#define PciDeviceId(d) (d)->device
+#define PciIrqLine(d) (d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+ *base = pdev->resource[index].start;
+ if ((pdev->resource[index].flags & 0x7) == 0x4)
+ ++index;
+ return ++index;
+}
+#else
+static int __init
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+ *base = pdev->base_address[index++];
+ if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+ *base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+ ++index;
+ }
+ return index;
+}
+#endif
+
+#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL (~0u)
+#define PciBusNumber(d) ((d)>>8)
+#define PciDeviceFn(d) ((d)&0xff)
+#define __PciDev(busn, devfn) (((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+ pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+ pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+ pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+ pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+ pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+ pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+ static unsigned short pci_index;
+ int retv;
+ unsigned char bus_number, device_fn;
+
+ if (prev == PCIDEV_NULL)
+ pci_index = 0;
+ else
+ ++pci_index;
+ retv = pcibios_find_device (vendor, device, pci_index,
+ &bus_number, &device_fn);
+ return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+ u_short vendor_id;
+ pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+ return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+ u_short device_id;
+ pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+ return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+ u_char irq;
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+ return irq;
+}
+
+static int __init
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+ u_int32 tmp;
+
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+ *base = tmp;
+ offset += sizeof(u_int32);
+ if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+ *base |= (((u_long)tmp) << 32);
+#endif
+ offset += sizeof(u_int32);
+ }
+ return offset;
+}
+
+#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
+/*==========================================================
+**
+** SMP threading.
+**
+** Assuming that SMP systems are generally high end
+** systems and may use several SCSI adapters, we are
+** using one lock per controller instead of some global
+** one. For the moment (linux-2.1.95), driver's entry
+** points are called with the 'io_request_lock' lock
+** held, so:
+** - We are uselessly loosing a couple of micro-seconds
+** to lock the controller data structure.
+** - But the driver is not broken by design for SMP and
+** so can be more resistant to bugs or bad changes in
+** the IO sub-system code.
+** - A small advantage could be that the interrupt code
+** is grained as wished (e.g.: by controller).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
+#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&DRIVER_SMP_LOCK, flags)
+#define NCR_UNLOCK_DRIVER(flags) \
+ spin_unlock_irqrestore(&DRIVER_SMP_LOCK, flags)
+
+#define NCR_INIT_LOCK_NCB(np) spin_lock_init(&np->smp_lock)
+#define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags)
+#define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags)
+
+#define NCR_LOCK_SCSI_DONE(np, flags) \
+ spin_lock_irqsave(&io_request_lock, flags)
+#define NCR_UNLOCK_SCSI_DONE(np, flags) \
+ spin_unlock_irqrestore(&io_request_lock, flags)
+
+#else
+
+#define NCR_LOCK_DRIVER(flags) do { save_flags(flags); cli(); } while (0)
+#define NCR_UNLOCK_DRIVER(flags) do { restore_flags(flags); } while (0)
+
+#define NCR_INIT_LOCK_NCB(np) do { } while (0)
+#define NCR_LOCK_NCB(np, flags) do { save_flags(flags); cli(); } while (0)
+#define NCR_UNLOCK_NCB(np, flags) do { restore_flags(flags); } while (0)
+
+#define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0)
+#define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0)
+
+#endif
+
+/*==========================================================
+**
+** Memory mapped IO
+**
+** Since linux-2.1, we must use ioremap() to map the io
+** memory space and iounmap() to unmap it. This allows
+** portability. Linux 1.3.X and 2.0.X allow to remap
+** physical pages addresses greater than the highest
+** physical memory address to kernel virtual pages with
+** vremap() / vfree(). That was not portable but worked
+** with i386 architecture.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+#ifdef __sparc__
+# include <asm/irq.h>
+# define pcivtobus(p) bus_dvma_to_mem(p)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#elif defined(__alpha__)
+# define pcivtobus(p) ((p) & 0xfffffffful)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#else /* others */
+# define pcivtobus(p) (p)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#endif
+
+#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
+static u_long __init remap_pci_mem(u_long base, u_long size)
+{
+ u_long page_base = ((u_long) base) & PAGE_MASK;
+ u_long page_offs = ((u_long) base) - page_base;
+ u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
+
+ return page_remapped? (page_remapped + page_offs) : 0UL;
+}
+
+static void __init unmap_pci_mem(u_long vaddr, u_long size)
+{
+ if (vaddr)
+ iounmap((void *) (vaddr & PAGE_MASK));
+}
+
+#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */
+
+/*==========================================================
+**
+** Insert a delay in micro-seconds and milli-seconds.
+**
+** Under Linux, udelay() is restricted to delay <
+** 1 milli-second. In fact, it generally works for up
+** to 1 second delay. Since 2.1.105, the mdelay() function
+** is provided for delays in milli-seconds.
+** Under 2.0 kernels, udelay() is an inline function
+** that is very inaccurate on Pentium processors.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
+#define UDELAY udelay
+#define MDELAY mdelay
+#else
+static void UDELAY(long us) { udelay(us); }
+static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
+#endif
+
+/*==========================================================
+**
+** Simple power of two buddy-like allocator.
+**
+** This simple code is not intended to be fast, but to
+** provide power of 2 aligned memory allocations.
+** Since the SCRIPTS processor only supplies 8 bit
+** arithmetic, this allocator allows simple and fast
+** address calculations from the SCRIPTS code.
+** In addition, cache line alignment is guaranteed for
+** power of 2 cache line size.
+** Enhanced in linux-2.3.44 to provide a memory pool
+** per pcidev to support dynamic dma mapping. (I would
+** have preferred a real bus astraction, btw).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
+#else
+#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)
+#endif
+
+#define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */
+#else
+#define MEMO_PAGE_ORDER 1 /* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED /* Free unused pages immediately */
+#define MEMO_WARN 1
+#define MEMO_GFP_FLAGS GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */
+typedef pcidev_t m_bush_t; /* Something that addresses DMAable */
+
+typedef struct m_link { /* Link between free memory chunks */
+ struct m_link *next;
+} m_link_s;
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+typedef struct m_vtob { /* Virtual to Bus address translation */
+ struct m_vtob *next;
+ m_addr_t vaddr;
+ m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT 5
+#define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK (VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m) \
+ ((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif
+
+typedef struct m_pool { /* Memory pool of a given kind */
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ m_bush_t bush;
+ m_addr_t (*getp)(struct m_pool *);
+ void (*freep)(struct m_pool *, m_addr_t);
+#define M_GETP() mp->getp(mp)
+#define M_FREEP(p) mp->freep(mp, p)
+#define GetPages() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define FreePages(p) free_pages(p, MEMO_PAGE_ORDER)
+ int nump;
+ m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+ struct m_pool *next;
+#else
+#define M_GETP() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define M_FREEP(p) free_pages(p, MEMO_PAGE_ORDER)
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+ struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
+
+static void *___m_alloc(m_pool_s *mp, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ int j;
+ m_addr_t a;
+ m_link_s *h = mp->h;
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return 0;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ j = i;
+ while (!h[j].next) {
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ h[j].next = (m_link_s *) M_GETP();
+ if (h[j].next)
+ h[j].next->next = 0;
+ break;
+ }
+ ++j;
+ s <<= 1;
+ }
+ a = (m_addr_t) h[j].next;
+ if (a) {
+ h[j].next = h[j].next->next;
+ while (j > i) {
+ j -= 1;
+ s >>= 1;
+ h[j].next = (m_link_s *) (a+s);
+ h[j].next->next = 0;
+ }
+ }
+#ifdef DEBUG
+ printk("___m_alloc(%d) = %p\n", size, (void *) a);
+#endif
+ return (void *) a;
+}
+
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ m_link_s *q;
+ m_addr_t a, b;
+ m_link_s *h = mp->h;
+
+#ifdef DEBUG
+ printk("___m_free(%p, %d)\n", ptr, size);
+#endif
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ a = (m_addr_t) ptr;
+
+ while (1) {
+#ifdef MEMO_FREE_UNUSED
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ M_FREEP(a);
+ break;
+ }
+#endif
+ b = a ^ s;
+ q = &h[i];
+ while (q->next && q->next != (m_link_s *) b) {
+ q = q->next;
+ }
+ if (!q->next) {
+ ((m_link_s *) a)->next = h[i].next;
+ h[i].next = (m_link_s *) a;
+ break;
+ }
+ q->next = q->next->next;
+ a = a & b;
+ s <<= 1;
+ ++i;
+ }
+}
+
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
+{
+ void *p;
+
+ p = ___m_alloc(mp, size);
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("new %-10s[%4d] @%p.\n", name, size, p);
+
+ if (p)
+ bzero(p, size);
+ else if (uflags & MEMO_WARN)
+ printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
+
+ return p;
+}
+
+#define __m_calloc(mp, s, n) __m_calloc2(mp, s, n, MEMO_WARN)
+
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+ ___m_free(mp, ptr, size);
+
+}
+
+/*
+ * With pci bus iommu support, we use a default pool of unmapped memory
+ * for memory we donnot need to DMA from/to and one pool per pcidev for
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+static m_pool_s mp0;
+
+#else
+
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+ m_addr_t m = GetPages();
+ if (m)
+ ++mp->nump;
+ return m;
+}
+
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+ FreePages(m);
+ --mp->nump;
+}
+
+static m_pool_s mp0 = {0, ___mp0_getp, ___mp0_freep};
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+static void *m_calloc(int size, char *name)
+{
+ u_long flags;
+ void *m;
+ NCR_LOCK_DRIVER(flags);
+ m = __m_calloc(&mp0, size, name);
+ NCR_UNLOCK_DRIVER(flags);
+ return m;
+}
+
+static void m_free(void *ptr, int size, char *name)
+{
+ u_long flags;
+ NCR_LOCK_DRIVER(flags);
+ __m_free(&mp0, ptr, size, name);
+ NCR_UNLOCK_DRIVER(flags);
+}
+
+/*
+ * DMAable pools.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Without pci bus iommu support, all the memory is assumed DMAable */
+
+#define __m_calloc_dma(b, s, n) m_calloc(s, n)
+#define __m_free_dma(b, p, s, n) m_free(p, s, n)
+#define __vtobus(b, p) virt_to_bus(p)
+
+#else
+
+/*
+ * With pci bus iommu support, we maintain one pool per pcidev and a
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+ m_addr_t vp;
+ m_vtob_s *vbp;
+
+ vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+ if (vbp) {
+ dma_addr_t daddr;
+ vp = (m_addr_t) pci_alloc_consistent(mp->bush,
+ PAGE_SIZE<<MEMO_PAGE_ORDER,
+ &daddr);
+ if (vp) {
+ int hc = VTOB_HASH_CODE(vp);
+ vbp->vaddr = vp;
+ vbp->baddr = daddr;
+ vbp->next = mp->vtob[hc];
+ mp->vtob[hc] = vbp;
+ ++mp->nump;
+ return vp;
+ }
+ }
+ if (vbp)
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ return 0;
+}
+
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+ m_vtob_s **vbpp, *vbp;
+ int hc = VTOB_HASH_CODE(m);
+
+ vbpp = &mp->vtob[hc];
+ while (*vbpp && (*vbpp)->vaddr != m)
+ vbpp = &(*vbpp)->next;
+ if (*vbpp) {
+ vbp = *vbpp;
+ *vbpp = (*vbpp)->next;
+ pci_free_consistent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+ (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ --mp->nump;
+ }
+}
+
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+ return mp;
+}
+
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+ if (mp) {
+ bzero(mp, sizeof(*mp));
+ mp->bush = bush;
+ mp->getp = ___dma_getp;
+ mp->freep = ___dma_freep;
+ mp->next = mp0.next;
+ mp0.next = mp;
+ }
+ return mp;
+}
+
+static void ___del_dma_pool(m_pool_s *p)
+{
+ struct m_pool **pp = &mp0.next;
+
+ while (*pp && *pp != p)
+ pp = &(*pp)->next;
+ if (*pp) {
+ *pp = (*pp)->next;
+ __m_free(&mp0, p, sizeof(*p), "MPOOL");
+ }
+}
+
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+ void *m = 0;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (!mp)
+ mp = ___cre_dma_pool(bush);
+ if (mp)
+ m = __m_calloc(mp, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ NCR_UNLOCK_DRIVER(flags);
+
+ return m;
+}
+
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (mp)
+ __m_free(mp, m, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ NCR_UNLOCK_DRIVER(flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+ u_long flags;
+ m_pool_s *mp;
+ int hc = VTOB_HASH_CODE(m);
+ m_vtob_s *vp = 0;
+ m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (mp) {
+ vp = mp->vtob[hc];
+ while (vp && (m_addr_t) vp->vaddr != a)
+ vp = vp->next;
+ }
+ NCR_UNLOCK_DRIVER(flags);
+ return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define _m_calloc_dma(np, s, n) __m_calloc_dma(np->pdev, s, n)
+#define _m_free_dma(np, p, s, n) __m_free_dma(np->pdev, p, s, n)
+#define m_calloc_dma(s, n) _m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n) _m_free_dma(np, p, s, n)
+#define _vtobus(np, p) __vtobus(np->pdev, p)
+#define vtobus(p) _vtobus(np, p)
+
+/*
+ * Deal with DMA mapping/unmapping.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Linux versions prior to pci bus iommu kernel interface */
+
+#define __unmap_scsi_data(pdev, cmd) do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd) ((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd) do {; } while (0)
+
+#define scsi_sg_dma_address(sc) vtobus((sc)->address)
+#define scsi_sg_dma_len(sc) ((sc)->length)
+
+#else
+
+/* Linux version with pci bus iommu kernel interface */
+
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped SCp.phase
+#define __data_mapping SCp.have_data_in
+
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ pci_unmap_single(pdev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+ cmd->__data_mapped = 0;
+}
+
+static u_long __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ dma_addr_t mapping;
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ if (cmd->request_bufflen == 0)
+ return 0;
+
+ mapping = pci_map_single(pdev, cmd->request_buffer,
+ cmd->request_bufflen, dma_dir);
+ cmd->__data_mapped = 1;
+ cmd->__data_mapping = mapping;
+
+ return mapping;
+}
+
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int use_sg;
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ if (cmd->use_sg == 0)
+ return 0;
+
+ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ cmd->__data_mapped = 2;
+ cmd->__data_mapping = use_sg;
+
+ return use_sg;
+}
+
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ pci_dma_sync_single(pdev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+}
+
+#define scsi_sg_dma_address(sc) sg_dma_address(sc)
+#define scsi_sg_dma_len(sc) sg_dma_len(sc)
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->pdev, cmd)
+#define map_scsi_single_data(np, cmd) __map_scsi_single_data(np->pdev, cmd)
+#define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->pdev, cmd)
+#define sync_scsi_data(np, cmd) __sync_scsi_data(np->pdev, cmd)
+
+/*==========================================================
+**
+** SCSI data transfer direction
+**
+** Until some linux kernel version near 2.3.40,
+** low-level scsi drivers were not told about data
+** transfer direction. We check the existence of this
+** feature that has been expected for a _long_ time by
+** all SCSI driver developers by just testing against
+** the definition of SCSI_DATA_UNKNOWN. Indeed this is
+** a hack, but testing against a kernel version would
+** have been a shame. ;-)
+**
+**==========================================================
+*/
+#ifdef SCSI_DATA_UNKNOWN
+
+#define scsi_data_direction(cmd) (cmd->sc_data_direction)
+
+#else
+
+#define SCSI_DATA_UNKNOWN 0
+#define SCSI_DATA_WRITE 1
+#define SCSI_DATA_READ 2
+#define SCSI_DATA_NONE 3
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+ int direction;
+
+ switch((int) cmd->cmnd[0]) {
+ case 0x08: /* READ(6) 08 */
+ case 0x28: /* READ(10) 28 */
+ case 0xA8: /* READ(12) A8 */
+ direction = SCSI_DATA_READ;
+ break;
+ case 0x0A: /* WRITE(6) 0A */
+ case 0x2A: /* WRITE(10) 2A */
+ case 0xAA: /* WRITE(12) AA */
+ direction = SCSI_DATA_WRITE;
+ break;
+ default:
+ direction = SCSI_DATA_UNKNOWN;
+ break;
+ }
+
+ return direction;
+}
+
+#endif /* SCSI_DATA_UNKNOWN */
+
+/*==========================================================
+**
+** Driver setup.
+**
+** This structure is initialized from linux config
+** options. It can be overridden at boot-up by the boot
+** command line.
+**
+**==========================================================
+*/
+static struct ncr_driver_setup
+ driver_setup = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+ driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+/*==========================================================
+**
+** Big/Little endian support.
+**
+** If the NCR uses big endian addressing mode over the
+** PCI, actual io register addresses for byte and word
+** accesses must be changed according to lane routing.
+** Btw, ncr_offb() and ncr_offw() macros only apply to
+** constants and so donnot generate bloated code.
+**
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for script patching.
+** Macro cpu_to_scr() is to be used for script patching.
+** Macro scr_to_cpu() is to be used for getting a DWORD
+** from the script.
+**
+**==========================================================
+*/
+
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
+
+#endif
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*==========================================================
+**
+** Access to the controller chip.
+**
+** If NCR_IOMAPPED is defined, the driver will use
+** normal IOs instead of the MEMORY MAPPED IO method
+** recommended by PCI specifications.
+** If all PCI bridges, host brigdes and architectures
+** would have been correctly designed for PCI, this
+** option would be useless.
+**
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for accessing chip io
+** registers. Functions suffixed by '_raw' are assumed
+** to access the chip over the PCI without doing byte
+** reordering. Functions suffixed by '_l2b' are
+** assumed to perform little-endian to big-endian byte
+** reordering, those suffixed by '_b2l' blah, blah,
+** blah, ...
+**
+**==========================================================
+*/
+
+#if defined(NCR_IOMAPPED)
+
+/*
+** IO mapped only input / ouput
+*/
+
+#define INB_OFF(o) inb (np->base_io + ncr_offb(o))
+#define OUTB_OFF(o, val) outb ((val), np->base_io + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_l2b (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_l2b (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_b2l ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_b2l ((val), np->base_io + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_b2l (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_b2l (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_l2b ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_l2b ((val), np->base_io + (o))
+
+#else
+
+#define INW_OFF(o) inw_raw (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_raw (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_raw ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_raw ((val), np->base_io + (o))
+
+#endif /* ENDIANs */
+
+#else /* defined NCR_IOMAPPED */
+
+/*
+** MEMORY mapped IO input / output
+*/
+
+#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
+
+#else
+
+#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_raw((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
+
+#endif
+
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+** Set bit field ON, OFF
+*/
+
+#define OUTONB(r, m) OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m) OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m) OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
+
+/*==========================================================
+**
+** Structures used by the detection routine to transmit
+** device configuration to the attach function.
+**
+**==========================================================
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_long base;
+ u_long base_2;
+ u_long io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_long base_io;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+/*==========================================================
+**
+** Structure used to store the NVRAM content.
+**
+**==========================================================
+*/
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*==========================================================
+**
+** Structure used by detection routine to save data on
+** each detected board for attach.
+**
+**==========================================================
+*/
+typedef struct {
+ pcidev_t pdev;
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ u_char host_id;
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ u_char pqs_pds;
+#endif
+ int attach_done;
+} ncr_device;
+
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
+
+/*==========================================================
+**
+** NVRAM detection and reading.
+**
+** Currently supported:
+** - 24C16 EEPROM with both Symbios and Tekram layout.
+** - 93C46 EEPROM with Tekram layout.
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+ * 24C16 EEPROM reading.
+ *
+ * GPOI0 - data in/data out
+ * GPIO1 - clock
+ * Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+{
+ UDELAY (5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (5);
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+static void __init
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+{
+ S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ S24C16_do_bit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+static void __init
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ S24C16_do_bit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+static void __init
+S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+static void __init
+S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ S24C16_do_bit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Read 'len' bytes starting at 'offset'.
+ */
+static int __init
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_char ack_data;
+ int retv = 1;
+ int x;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ S24C16_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ S24C16_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ S24C16_write_byte(np, &ack_data,
+ 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ S24C16_write_byte(np, &ack_data,
+ offset & 0xff, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ S24C16_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ S24C16_write_byte(np, &ack_data,
+ 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all requested data - only part of total NVRAM */
+ for (x = 0; x < len; x++)
+ S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ S24C16_stop(np, &gpreg);
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+/*
+ * Try reading Symbios NVRAM.
+ * Return 0 if OK.
+ */
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ /* probe the 24c16 and read the SYMBIOS 24c16 area */
+ if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+ return 1;
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->type != 0 ||
+ memcmp(nvram->trailer, Symbios_trailer, 6) ||
+ nvram->byte_count != len - 12)
+ return 1;
+
+ /* verify checksum */
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+ if (csum != nvram->checksum)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 93C46 EEPROM reading.
+ *
+ * GPOI0 - data in
+ * GPIO1 - data out
+ * GPIO2 - clock
+ * GPIO4 - chip select
+ *
+ * Used by Tekram.
+ */
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ UDELAY (2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+/*
+ * Read bit from NVRAM
+ */
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+{
+ UDELAY (2);
+ T93C46_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+static void __init
+T93C46_Send_Command(ncr_slot *np, u_short write_data,
+ u_char *read_bit, u_char *gpreg)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ 2 bytes from the NVRAM
+ */
+static void __init
+T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ T93C46_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read Tekram NvRAM data.
+ */
+static int __init
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
+{
+ u_char read_bit;
+ int x;
+
+ for (x = 0; x < len; x++) {
+
+ /* output read command and address */
+ T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 1; /* Bad */
+ T93C46_Read_Word(np, &data[x], gpreg);
+ T93C46_Stop(np, gpreg);
+ }
+
+ return 0;
+}
+
+/*
+ * Try reading 93C46 Tekram NVRAM.
+ */
+static int __init
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ retv = T93C46_Read_Data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Try reading Tekram NVRAM.
+ * Return 0 if OK.
+ */
+static int __init
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
+{
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ switch (device_id) {
+ case PCI_DEVICE_ID_NCR_53C885:
+ case PCI_DEVICE_ID_NCR_53C895:
+ case PCI_DEVICE_ID_NCR_53C896:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ break;
+ case PCI_DEVICE_ID_NCR_53C875:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ if (!x)
+ break;
+ default:
+ x = sym_read_T93C46_nvram(np, nvram);
+ break;
+ }
+ if (x)
+ return 1;
+
+ /* verify checksum */
+ for (x = 0, csum = 0; x < len - 1; x += 2)
+ csum += data[x] + (data[x+1] << 8);
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*===================================================================
+**
+** Detect and try to read SYMBIOS and TEKRAM NVRAM.
+**
+** Data can be used to order booting of boards.
+**
+** Data is saved in ncr_device structure if NVRAM found. This
+** is then used to find drive boot order for ncr_attach().
+**
+** NVRAM data is passed to Scsi_Host_Template later during
+** ncr_attach() for any device set up.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
+{
+ devp->nvram = nvp;
+ if (!nvp)
+ return;
+ /*
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(devp->slot.io_port, 128, NAME53C8XX);
+ devp->slot.base_io = devp->slot.io_port;
+#else
+ devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
+ if (!devp->slot.reg)
+ return;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+ if (!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+ nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+ &nvp->data.Tekram))
+ nvp->type = SCSI_NCR_TEKRAM_NVRAM;
+ else {
+ nvp->type = 0;
+ devp->nvram = 0;
+ }
+
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(devp->slot.base_io, 128);
+#else
+ unmap_pci_mem((u_long) devp->slot.reg, 128ul);
+#endif
+
+}
+
+/*===================================================================
+**
+** Display the content of NVRAM for debugging purpose.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_DEBUG_NVRAM
+static void __init ncr_display_Symbios_nvram(Symbios_nvram *nvram)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printk(KERN_DEBUG NAME53C8XX ": HOST ID=%d%s%s%s%s%s\n",
+ nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"",
+ (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printk(KERN_DEBUG NAME53C8XX
+ "-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+static void __init ncr_display_Tekram_nvram(Tekram_nvram *nvram)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printk(KERN_DEBUG NAME53C8XX
+ ": HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = Tekram_sync[j];
+ printk(KERN_DEBUG NAME53C8XX "-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+
+/*===================================================================
+**
+** Utility routines that protperly return data through /proc FS.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+#endif
+
+/*===================================================================
+**
+** Driver setup from the boot command line
+**
+**===================================================================
+*/
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+#define OPT_TAGS 1
+#define OPT_MASTER_PARITY 2
+#define OPT_SCSI_PARITY 3
+#define OPT_DISCONNECTION 4
+#define OPT_SPECIAL_FEATURES 5
+#define OPT_ULTRA_SCSI 6
+#define OPT_FORCE_SYNC_NEGO 7
+#define OPT_REVERSE_PROBE 8
+#define OPT_DEFAULT_SYNC 9
+#define OPT_VERBOSE 10
+#define OPT_DEBUG 11
+#define OPT_BURST_MAX 12
+#define OPT_LED_PIN 13
+#define OPT_MAX_WIDE 14
+#define OPT_SETTLE_DELAY 15
+#define OPT_DIFF_SUPPORT 16
+#define OPT_IRQM 17
+#define OPT_PCI_FIX_UP 18
+#define OPT_BUS_CHECK 19
+#define OPT_OPTIMIZE 20
+#define OPT_RECOVERY 21
+#define OPT_SAFE_SETUP 22
+#define OPT_USE_NVRAM 23
+#define OPT_EXCLUDE 24
+#define OPT_HOST_ID 25
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB 26
+#endif
+
+static char setup_token[] __initdata =
+ "tags:" "mpar:"
+ "spar:" "disc:"
+ "specf:" "ultra:"
+ "fsn:" "revprob:"
+ "sync:" "verb:"
+ "debug:" "burst:"
+ "led:" "wide:"
+ "settle:" "diff:"
+ "irqm:" "pcifix:"
+ "buschk:" "optim:"
+ "recovery:"
+ "safe:" "nvram:"
+ "excl:" "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+ "iarb:"
+#endif
+ ; /* DONNOT REMOVE THIS ';' */
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+ char *cur = setup_token;
+ char *pc;
+ int i = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ ++pc;
+ ++i;
+ if (!strncmp(p, cur, pc - cur))
+ return i;
+ cur = pc;
+ }
+ return 0;
+}
+
+
+static int __init sym53c8xx__setup(char *str)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+ char *cur = str;
+ char *pc, *pv;
+ int i, val, c;
+ int xi = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ char *pe;
+
+ val = 0;
+ pv = pc;
+ c = *++pv;
+
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else
+ val = (int) simple_strtoul(pv, &pe, 0);
+
+ switch (get_setup_token(cur)) {
+ case OPT_TAGS:
+ driver_setup.default_tags = val;
+ if (pe && *pe == '/') {
+ i = 0;
+ while (*pe && *pe != ARG_SEP &&
+ i < sizeof(driver_setup.tag_ctrl)-1) {
+ driver_setup.tag_ctrl[i++] = *pe++;
+ }
+ driver_setup.tag_ctrl[i] = '\0';
+ }
+ break;
+ case OPT_MASTER_PARITY:
+ driver_setup.master_parity = val;
+ break;
+ case OPT_SCSI_PARITY:
+ driver_setup.scsi_parity = val;
+ break;
+ case OPT_DISCONNECTION:
+ driver_setup.disconnection = val;
+ break;
+ case OPT_SPECIAL_FEATURES:
+ driver_setup.special_features = val;
+ break;
+ case OPT_ULTRA_SCSI:
+ driver_setup.ultra_scsi = val;
+ break;
+ case OPT_FORCE_SYNC_NEGO:
+ driver_setup.force_sync_nego = val;
+ break;
+ case OPT_REVERSE_PROBE:
+ driver_setup.reverse_probe = val;
+ break;
+ case OPT_DEFAULT_SYNC:
+ driver_setup.default_sync = val;
+ break;
+ case OPT_VERBOSE:
+ driver_setup.verbose = val;
+ break;
+ case OPT_DEBUG:
+ driver_setup.debug = val;
+ break;
+ case OPT_BURST_MAX:
+ driver_setup.burst_max = val;
+ break;
+ case OPT_LED_PIN:
+ driver_setup.led_pin = val;
+ break;
+ case OPT_MAX_WIDE:
+ driver_setup.max_wide = val? 1:0;
+ break;
+ case OPT_SETTLE_DELAY:
+ driver_setup.settle_delay = val;
+ break;
+ case OPT_DIFF_SUPPORT:
+ driver_setup.diff_support = val;
+ break;
+ case OPT_IRQM:
+ driver_setup.irqm = val;
+ break;
+ case OPT_PCI_FIX_UP:
+ driver_setup.pci_fix_up = val;
+ break;
+ case OPT_BUS_CHECK:
+ driver_setup.bus_check = val;
+ break;
+ case OPT_OPTIMIZE:
+ driver_setup.optimize = val;
+ break;
+ case OPT_RECOVERY:
+ driver_setup.recovery = val;
+ break;
+ case OPT_USE_NVRAM:
+ driver_setup.use_nvram = val;
+ break;
+ case OPT_SAFE_SETUP:
+ memcpy(&driver_setup, &driver_safe_setup,
+ sizeof(driver_setup));
+ break;
+ case OPT_EXCLUDE:
+ if (xi < SCSI_NCR_MAX_EXCLUDES)
+ driver_setup.excludes[xi++] = val;
+ break;
+ case OPT_HOST_ID:
+ driver_setup.host_id = val;
+ break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+ case OPT_IARB:
+ driver_setup.iarb = val;
+ break;
+#endif
+ default:
+ printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+ break;
+ }
+
+ if ((cur = strchr(cur, ARG_SEP)) != NULL)
+ ++cur;
+ }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+ return 0;
+}
+
+/*===================================================================
+**
+** Get device queue depth from boot command line.
+**
+**===================================================================
+*/
+#define DEF_DEPTH (driver_setup.default_tags)
+#define ALL_TARGETS -2
+#define NO_TARGET -1
+#define ALL_LUNS -2
+#define NO_LUN -1
+
+static int device_queue_depth(int unit, int target, int lun)
+{
+ int c, h, t, u, v;
+ char *p = driver_setup.tag_ctrl;
+ char *ep;
+
+ h = -1;
+ t = NO_TARGET;
+ u = NO_LUN;
+ while ((c = *p++) != 0) {
+ v = simple_strtoul(p, &ep, 0);
+ switch(c) {
+ case '/':
+ ++h;
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ case 't':
+ if (t != target)
+ t = (target == v) ? v : NO_TARGET;
+ u = ALL_LUNS;
+ break;
+ case 'u':
+ if (u != lun)
+ u = (lun == v) ? v : NO_LUN;
+ break;
+ case 'q':
+ if (h == unit &&
+ (t == ALL_TARGETS || t == target) &&
+ (u == ALL_LUNS || u == lun))
+ return v;
+ break;
+ case '-':
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ default:
+ break;
+ }
+ p = ep;
+ }
+ return DEF_DEPTH;
+}
+
+/*===================================================================
+**
+** Print out information about driver configuration.
+**
+**===================================================================
+*/
+static void __init ncr_print_driver_setup(void)
+{
+#define YesNo(y) y ? 'y' : 'n'
+ printk (NAME53C8XX ": setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
+ "burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
+ YesNo(driver_setup.disconnection),
+ driver_setup.special_features,
+ driver_setup.ultra_scsi,
+ driver_setup.default_tags,
+ driver_setup.default_sync,
+ driver_setup.burst_max,
+ YesNo(driver_setup.max_wide),
+ driver_setup.diff_support,
+ YesNo(driver_setup.reverse_probe),
+ driver_setup.bus_check);
+
+ printk (NAME53C8XX ": setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
+ "led:%c,settle:%d,irqm:0x%x,nvram:0x%x,pcifix:0x%x\n",
+ YesNo(driver_setup.master_parity),
+ YesNo(driver_setup.scsi_parity),
+ YesNo(driver_setup.force_sync_nego),
+ driver_setup.verbose,
+ driver_setup.debug,
+ YesNo(driver_setup.led_pin),
+ driver_setup.settle_delay,
+ driver_setup.irqm,
+ driver_setup.use_nvram,
+ driver_setup.pci_fix_up);
+#undef YesNo
+}
+
+/*===================================================================
+**
+** SYM53C8XX devices description table.
+**
+**===================================================================
+*/
+
+static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+/*===================================================================
+**
+** Detect all NCR PQS/PDS boards and keep track of their bus nr.
+**
+** The NCR PQS or PDS card is constructed as a DEC bridge
+** behind which sit a proprietary NCR memory controller and
+** four or two 53c875s as separate devices. In its usual mode
+** of operation, the 875s are slaved to the memory controller
+** for all transfers. We can tell if an 875 is part of a
+** PQS/PDS or not since if it is, it will be on the same bus
+** as the memory controller. To operate with the Linux
+** driver, the memory controller is disabled and the 875s
+** freed to function independently. The only wrinkle is that
+** the preset SCSI ID (which may be zero) must be read in from
+** a special configuration space register of the 875.
+**
+**===================================================================
+*/
+#define SCSI_NCR_MAX_PQS_BUS 16
+static int pqs_bus[SCSI_NCR_MAX_PQS_BUS] __initdata = { 0 };
+
+static void __init ncr_detect_pqs_pds(void)
+{
+ short index;
+ pcidev_t dev = PCIDEV_NULL;
+
+ for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+ u_char tmp;
+
+ dev = pci_find_device(0x101a, 0x0009, dev);
+ if (dev == PCIDEV_NULL) {
+ pqs_bus[index] = -1;
+ break;
+ }
+ printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+ pci_read_config_byte(dev, 0x44, &tmp);
+ /* bit 1: allow individual 875 configuration */
+ tmp |= 0x2;
+ pci_write_config_byte(dev, 0x44, tmp);
+ pci_read_config_byte(dev, 0x45, &tmp);
+ /* bit 2: drive individual 875 interrupts to the bus */
+ tmp |= 0x4;
+ pci_write_config_byte(dev, 0x45, tmp);
+
+ pqs_bus[index] = PciBusNumber(dev);
+ }
+}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+/*===================================================================
+**
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
+{
+ u_short vendor_id, device_id, command;
+ u_char cache_line_size, latency_timer;
+ u_char suggested_cache_line_size = 0;
+ u_char pci_fix_up = driver_setup.pci_fix_up;
+ u_char revision;
+ u_int irq;
+ u_long base, base_2, io_port;
+ int i;
+ ncr_chip *chip;
+
+ printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
+ PciBusNumber(pdev),
+ (int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+ (int) (PciDeviceFn(pdev) & 7));
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ if (!pci_dma_supported(pdev, (dma_addr_t) (0xffffffffUL))) {
+ printk(KERN_WARNING NAME53C8XX
+ "32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+ return -1;
+ }
+#endif
+
+ /*
+ ** Read info from the PCI config space.
+ ** pci_read_config_xxx() functions are assumed to be used for
+ ** successfully detected PCI devices.
+ */
+ vendor_id = PciVendorId(pdev);
+ device_id = PciDeviceId(pdev);
+ irq = PciIrqLine(pdev);
+ i = 0;
+ i = pci_get_base_address(pdev, i, &io_port);
+ i = pci_get_base_address(pdev, i, &base);
+ (void) pci_get_base_address(pdev, i, &base_2);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ /*
+ ** Match the BUS number for PQS/PDS devices.
+ ** Read the SCSI ID from a special register mapped
+ ** into the configuration space of the individual
+ ** 875s. This register is set up by the PQS bios
+ */
+ for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
+ u_char tmp;
+ if (pqs_bus[i] == PciBusNumber(pdev)) {
+ pci_read_config_byte(pdev, 0x84, &tmp);
+ device->pqs_pds = 1;
+ device->host_id = tmp;
+ break;
+ }
+ }
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+ /*
+ ** If user excludes this chip, donnot initialize it.
+ */
+ for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
+ if (driver_setup.excludes[i] ==
+ (io_port & PCI_BASE_ADDRESS_IO_MASK))
+ return -1;
+ }
+ /*
+ ** Check if the chip is supported
+ */
+ chip = 0;
+ for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+ if (device_id != ncr_chip_table[i].device_id)
+ continue;
+ if (revision > ncr_chip_table[i].revision_id)
+ continue;
+ chip = &device->chip;
+ memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+ chip->revision_id = revision;
+ break;
+ }
+
+ /*
+ ** Ignore Symbios chips controlled by SISL RAID controller.
+ ** This controller sets value 0x52414944 at RAM end - 16.
+ */
+#if defined(__i386__) && !defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+ if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
+ unsigned int ram_size, ram_val;
+ u_long ram_ptr;
+
+ if (chip->features & FE_RAM8K)
+ ram_size = 8192;
+ else
+ ram_size = 4096;
+
+ ram_ptr = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
+ ram_size);
+ if (ram_ptr) {
+ ram_val = readl_raw(ram_ptr + ram_size - 16);
+ unmap_pci_mem(ram_ptr, ram_size);
+ if (ram_val == 0x52414944) {
+ printk(NAME53C8XX": not initializing, "
+ "driven by SISL RAID controller.\n");
+ return -1;
+ }
+ }
+ }
+#endif /* i386 and PCI MEMORY accessible */
+
+ if (!chip) {
+ printk(NAME53C8XX ": not initializing, device not supported\n");
+ return -1;
+ }
+
+#ifdef __powerpc__
+ /*
+ ** Fix-up for power/pc.
+ ** Should not be performed by the driver.
+ */
+ if ((command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+ != (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+ printk(NAME53C8XX ": setting%s%s...\n",
+ (command & PCI_COMMAND_IO) ? "" : " PCI_COMMAND_IO",
+ (command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
+ command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
+ if ( is_prep ) {
+ if (io_port >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating base (Wacky IBM)");
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_1, base);
+ }
+ if (base_2 >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
+ base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_2, base_2);
+ }
+ }
+#endif
+#endif /* __powerpc__ */
+
+#ifdef __sparc__
+ /*
+ ** Fix-ups for sparc.
+ */
+ if (!cache_line_size)
+ suggested_cache_line_size = 16;
+
+ driver_setup.pci_fix_up |= 0x7;
+#endif /* __sparc__ */
+
+#if defined(__i386__) && !defined(MODULE)
+ if (!cache_line_size) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+ extern char x86;
+ switch(x86) {
+#else
+ switch(boot_cpu_data.x86) {
+#endif
+ case 4: suggested_cache_line_size = 4; break;
+ case 6:
+ case 5: suggested_cache_line_size = 8; break;
+ }
+ }
+#endif /* __i386__ */
+
+ /*
+ ** Check availability of IO space, memory space.
+ ** Enable master capability if not yet.
+ **
+ ** We shouldn't have to care about the IO region when
+ ** we are using MMIO. But calling check_region() from
+ ** both the ncr53c8xx and the sym53c8xx drivers prevents
+ ** from attaching devices from the both drivers.
+ ** If you have a better idea, let me know.
+ */
+/* #ifdef NCR_IOMAPPED */
+#if 1
+ if (!(command & PCI_COMMAND_IO)) {
+ printk(NAME53C8XX ": I/O base address (0x%lx) disabled.\n",
+ (long) io_port);
+ io_port = 0;
+ }
+#endif
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ printk(NAME53C8XX ": PCI_COMMAND_MEMORY not set.\n");
+ base = 0;
+ base_2 = 0;
+ }
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+/* #ifdef NCR_IOMAPPED */
+#if 1
+ if (io_port && check_region (io_port, 128)) {
+ printk(NAME53C8XX ": IO region 0x%lx[0..127] is in use\n",
+ (long) io_port);
+ io_port = 0;
+ }
+ if (!io_port)
+ return -1;
+#endif
+#ifndef NCR_IOMAPPED
+ if (!base) {
+ printk(NAME53C8XX ": MMIO base address disabled.\n");
+ return -1;
+ }
+#endif
+
+/* The ncr53c8xx driver never did set the PCI parity bit. */
+/* Since setting this bit is known to trigger spurious MDPE */
+/* errors on some 895 controllers when noise on power lines is */
+/* too high, I donnot want to change previous ncr53c8xx driver */
+/* behaviour on that point (the sym53c8xx driver set this bit). */
+#if 0
+ /*
+ ** Set MASTER capable and PARITY bit, if not yet.
+ */
+ if ((command & (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY))
+ != (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)) {
+ printk(NAME53C8XX ": setting%s%s...(fix-up)\n",
+ (command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
+ (command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
+ command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+#else
+ /*
+ ** Set MASTER capable if not yet.
+ */
+ if ((command & PCI_COMMAND_MASTER) != PCI_COMMAND_MASTER) {
+ printk(NAME53C8XX ": setting PCI_COMMAND_MASTER...(fix-up)\n");
+ command |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+#endif
+
+ /*
+ ** Fix some features according to driver setup.
+ */
+ if (!(driver_setup.special_features & 1))
+ chip->features &= ~FE_SPECIAL_SET;
+ else {
+ if (driver_setup.special_features & 2)
+ chip->features &= ~FE_WRIE;
+ if (driver_setup.special_features & 4)
+ chip->features &= ~FE_NOPM;
+ }
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
+ if (driver_setup.ultra_scsi < 1)
+ chip->features &= ~FE_ULTRA;
+ if (!driver_setup.max_wide)
+ chip->features &= ~FE_WIDE;
+
+ /*
+ ** Some features are required to be enabled in order to
+ ** work around some chip problems. :) ;)
+ ** (ITEM 12 of a DEL about the 896 I haven't yet).
+ ** We must ensure the chip will use WRITE AND INVALIDATE.
+ ** The revision number limit is for now arbitrary.
+ */
+ if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
+ chip->features |= (FE_WRIE | FE_CLSE);
+ pci_fix_up |= 3; /* Force appropriate PCI fix-up */
+ }
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+ /*
+ ** Try to fix up PCI config according to wished features.
+ */
+ if ((pci_fix_up & 1) && (chip->features & FE_CLSE) &&
+ !cache_line_size && suggested_cache_line_size) {
+ cache_line_size = suggested_cache_line_size;
+ pci_write_config_byte(pdev,
+ PCI_CACHE_LINE_SIZE, cache_line_size);
+ printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
+ cache_line_size);
+ }
+
+ if ((pci_fix_up & 2) && cache_line_size &&
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
+ command |= PCI_COMMAND_INVALIDATE;
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+
+ /*
+ ** Tune PCI LATENCY TIMER according to burst max length transfer.
+ ** (latency timer >= burst length + 6, we add 10 to be quite sure)
+ */
+
+ if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
+ u_char lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ printk(NAME53C8XX
+ ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+ (int) latency_timer, (int) lt);
+ latency_timer = lt;
+ pci_write_config_byte(pdev,
+ PCI_LATENCY_TIMER, latency_timer);
+ }
+ }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /*
+ ** Initialise ncr_device structure with items required by ncr_attach.
+ */
+ device->pdev = pdev;
+ device->slot.bus = PciBusNumber(pdev);
+ device->slot.device_fn = PciDeviceFn(pdev);
+ device->slot.base = base;
+ device->slot.base_2 = base_2;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attach_done = 0;
+
+ return 0;
+}
+
+/*===================================================================
+**
+** Detect all 53c8xx hosts and then attach them.
+**
+** If we are using NVRAM, once all hosts are detected, we need to
+** check any NVRAM for boot order in case detect and boot order
+** differ and attach them using the order in the NVRAM.
+**
+** If no NVRAM is found or data appears invalid attach boards in
+** the the order they are detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx__detect(Scsi_Host_Template *tpnt, u_short ncr_chip_ids[], int chips)
+{
+ pcidev_t pcidev;
+ int i, j, hosts, count;
+ int attach_count = 0;
+ ncr_device *devtbl, *devp;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram0, nvram, *nvp;
+#endif
+
+ /*
+ ** PCI is required.
+ */
+ if (!pci_present())
+ return 0;
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ ncr_debug = driver_setup.debug;
+#endif
+ if (initverbose >= 2)
+ ncr_print_driver_setup();
+
+ /*
+ ** Allocate the device table since we donnot want to
+ ** overflow the kernel stack.
+ ** 1 x 4K PAGE is enough for more than 40 devices for i386.
+ */
+ devtbl = m_calloc(PAGE_SIZE, "devtbl");
+ if (!devtbl)
+ return 0;
+
+ /*
+ ** Detect all NCR PQS/PDS memory controllers.
+ */
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ ncr_detect_pqs_pds();
+#endif
+
+ /*
+ ** Detect all 53c8xx hosts.
+ ** Save the first Symbios NVRAM content if any
+ ** for the boot order.
+ */
+ hosts = PAGE_SIZE / sizeof(*devtbl);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
+#endif
+ j = 0;
+ count = 0;
+ pcidev = PCIDEV_NULL;
+ while (1) {
+ char *msg = "";
+ if (count >= hosts)
+ break;
+ if (j >= chips)
+ break;
+ i = driver_setup.reverse_probe ? chips - 1 - j : j;
+ pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ pcidev);
+ if (pcidev == PCIDEV_NULL) {
+ ++j;
+ continue;
+ }
+ /* Some HW as the HP LH4 may report twice PCI devices */
+ for (i = 0; i < count ; i++) {
+ if (devtbl[i].slot.bus == PciBusNumber(pcidev) &&
+ devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
+ break;
+ }
+ if (i != count) /* Ignore this device if we already have it */
+ continue;
+ devp = &devtbl[count];
+ devp->host_id = driver_setup.host_id;
+ devp->attach_done = 0;
+ if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
+ continue;
+ }
+ ++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvp) {
+ ncr_get_nvram(devp, nvp);
+ switch(nvp->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ /*
+ * Switch to the other nvram buffer, so that
+ * nvram0 will contain the first Symbios
+ * format NVRAM content with boot order.
+ */
+ nvp = &nvram;
+ msg = "with Symbios NVRAM";
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ }
+ }
+#endif
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ if (devp->pqs_pds)
+ msg = "(NCR PQS/PDS)";
+#endif
+ printk(KERN_INFO NAME53C8XX ": 53c%s detected %s\n",
+ devp->chip.name, msg);
+ }
+
+ /*
+ ** If we have found a SYMBIOS NVRAM, use first the NVRAM boot
+ ** sequence as device boot order.
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
+ goto next;
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram0.data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &devtbl[j];
+ if (h->device_fn != devp->slot.device_fn ||
+ h->bus_nr != devp->slot.bus ||
+ h->device_id != devp->chip.device_id)
+ continue;
+ if (devp->attach_done)
+ continue;
+ if (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) {
+ ncr_get_nvram(devp, nvp);
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ }
+ else if (!(driver_setup.use_nvram & 0x80))
+ printk(KERN_INFO NAME53C8XX
+ ": 53c%s state OFF thus not attached\n",
+ devp->chip.name);
+ else
+ continue;
+
+ devp->attach_done = 1;
+ break;
+ }
+ }
+next:
+#endif
+
+ /*
+ ** Rescan device list to make sure all boards attached.
+ ** Devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ devp = &devtbl[i];
+ if (!devp->attach_done) {
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_get_nvram(devp, nvp);
+#endif
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ }
+ }
+
+ m_free(devtbl, PAGE_SIZE, "devtbl");
+
+ return attach_count;
+}
diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in
index 1d74abb50..947e0294e 100644
--- a/drivers/sound/Config.in
+++ b/drivers/sound/Config.in
@@ -81,11 +81,14 @@ fi
dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
+ bool ' Verbose initialisation' CONFIG_SOUND_TRACEINIT
+ bool ' Persistent DMA buffers' CONFIG_SOUND_DMAP
+
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND
fi
-
dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS
+ dep_tristate ' Adlib Cards' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS
dep_tristate ' ACI mixer (miroPCM12)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS
dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS
dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 6a3576df0..5b4d35c8c 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -44,26 +44,27 @@ obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
-obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o
-obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
-obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
+obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o
+obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o uart401.o mpu401.o
-obj-$(CONFIG_SOUND_MSS) += ad1848.o
-obj-$(CONFIG_SOUND_PAS) += pas2.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o
-obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
-obj-$(CONFIG_SOUND_MPU401) += mpu401.o
-obj-$(CONFIG_SOUND_UART6850) += uart6850.o
+obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o uart401.o mpu401.o
+obj-$(CONFIG_SOUND_MSS) += ad1848.o
+obj-$(CONFIG_SOUND_PAS) += pas2.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o
+obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
+obj-$(CONFIG_SOUND_MPU401) += mpu401.o
+obj-$(CONFIG_SOUND_UART6850) += uart6850.o
obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o
-obj-$(CONFIG_SOUND_YM3812) += adlib_card.o opl3.o
-obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
-obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
-obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
-obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
-obj-$(CONFIG_SOUND_AD1816) += ad1816.o
+obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o
+obj-$(CONFIG_SOUND_YM3812) += opl3.o
+obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
+obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
+obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
+obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
+obj-$(CONFIG_SOUND_AD1816) += ad1816.o
obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
index 52d4d79f8..99a4d8f44 100644
--- a/drivers/sound/dev_table.c
+++ b/drivers/sound/dev_table.c
@@ -17,8 +17,6 @@
#include "sound_config.h"
int softoss_dev = 0;
-int sound_started = 0;
-int sndtable_get_cardcount(void);
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
index db2b141d6..a5525a4bf 100644
--- a/drivers/sound/dev_table.h
+++ b/drivers/sound/dev_table.h
@@ -43,8 +43,6 @@
* NOTE! NOTE! NOTE! NOTE!
*/
-extern int sound_started;
-
struct driver_info
{
char *driver_id;
@@ -350,11 +348,14 @@ struct sound_timer_operations
#ifdef _DEV_TABLE_C_
-struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0;
-struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
-struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
-struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
-
+struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL};
+int num_audiodevs = 0;
+struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL};
+int num_mixers = 0;
+struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL};
+int num_synths = 0;
+struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL};
+int num_midis = 0;
#ifndef EXCLUDE_TIMERS
extern struct sound_timer_operations default_sound_timer;
struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
@@ -370,18 +371,17 @@ int num_sound_timers = 0;
#else
-extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
-extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
-extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths;
-extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
-extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; extern int num_sound_timers;
+extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+extern int num_audiodevs;
+extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+extern int num_mixers;
+extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+extern int num_synths;
+extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
+extern int num_midis;
+extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
+extern int num_sound_timers;
#endif /* _DEV_TABLE_C_ */
-void setup_cards(void);
-int sndtable_get_cardcount (void);
-void sound_chconf(int card_type, int ioaddr, int irq, int dma);
-int snd_find_driver(int type);
-void sound_unload_driver(int type);
-int sndtable_identify_card(char *name);
extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
int sndtable_probe (int unit, struct address_info *hw_config);
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
index c0edb593f..ce5e4f732 100644
--- a/drivers/sound/dmabuf.c
+++ b/drivers/sound/dmabuf.c
@@ -66,6 +66,14 @@ static int sound_alloc_dmap(struct dma_buffparms *dmap)
if (dma_buffsize < 4096)
dma_buffsize = 4096;
dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+
+ /*
+ * Now check for the Cyrix problem.
+ */
+
+ if(isa_dma_bridge_buggy==2)
+ dma_pagesize=32768;
+
dmap->raw_buf = NULL;
dmap->buffsize = dma_buffsize;
if (dmap->buffsize > dma_pagesize)
diff --git a/drivers/sound/miroaci.h b/drivers/sound/miroaci.h
index 9fea58a53..ee4e01d1f 100644
--- a/drivers/sound/miroaci.h
+++ b/drivers/sound/miroaci.h
@@ -1,4 +1,3 @@
-#include <linux/config.h>
extern int aci_implied_cmd(unsigned char opcode);
extern int aci_write_cmd(unsigned char opcode, unsigned char parameter);
extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2);
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
index 2757659ac..a329f0e72 100644
--- a/drivers/sound/mpu401.c
+++ b/drivers/sound/mpu401.c
@@ -1726,25 +1726,24 @@ int init_mpu401(void)
{
/* Can be loaded either for module use or to provide functions
to others */
- cfg.irq = irq;
- cfg.io_base = io;
-
- if (cfg.io_base != -1 && cfg.irq != -1) {
- printk(KERN_WARNING "mpu401: need io and irq !");
- return -ENODEV;
+ if (io != -1 && irq != -1) {
+ cfg.irq = irq;
+ cfg.io_base = io;
+ if (probe_mpu401(&cfg) == 0)
+ return -ENODEV;
+ attach_mpu401(&cfg);
}
- if (probe_mpu401(&cfg) == 0)
- return -ENODEV;
- attach_mpu401(&cfg);
-
SOUND_LOCK;
return 0;
}
void cleanup_mpu401(void)
{
- unload_mpu401(&cfg);
+ if (io != -1 && irq != -1) {
+ /* Check for use by, for example, sscape driver */
+ unload_mpu401(&cfg);
+ }
SOUND_LOCK_END;
}
diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c
index 40804417c..c471163ef 100644
--- a/drivers/sound/sb_card.c
+++ b/drivers/sound/sb_card.c
@@ -399,7 +399,6 @@ static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card
/* @X@0001:mpu
*/
-#ifdef CONFIG_MIDI
if((mpu_dev = isapnp_find_dev(bus,
ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL)))
{
@@ -413,7 +412,6 @@ static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card
}
else
printk(KERN_ERR "sb: DT0197H panic: mpu not found\n");
-#endif
/* @P@:Gameport
diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c
index 923b46e90..217bdb605 100644
--- a/drivers/sound/sound_core.c
+++ b/drivers/sound/sound_core.c
@@ -217,6 +217,16 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
static struct sound_unit *chains[16];
+/**
+ * register_sound_special
+ * @fops: File operations for the driver
+ * @unit: Unit number to allocate
+ *
+ * Allocate a special sound device by minor number from the sound
+ * subsystem. The allocated number is returned on succes. On failure
+ * a negative error code is returned.
+ */
+
int register_sound_special(struct file_operations *fops, int unit)
{
char *name;
@@ -240,8 +250,8 @@ int register_sound_special(struct file_operations *fops, int unit)
case 5:
name = "unknown5";
break;
- case 6:
- name = "sndstat";
+ case 6: /* Was once sndstat */
+ name = "unknown6";
break;
case 7:
name = "unknown7";
@@ -272,23 +282,43 @@ int register_sound_special(struct file_operations *fops, int unit)
break;
}
return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1,
- name, S_IRUGO | S_IWUGO);
+ name, S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_special);
+/**
+ * register_sound_mixer
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a mixer device. Unit is the number of the mixer requested.
+ * Pass -1 to request the next free mixer unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_mixer(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[0], fops, dev, 0, 128,
- "mixer", S_IRUGO | S_IWUGO);
+ "mixer", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_mixer);
+/**
+ * register_sound_midi
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a midi device. Unit is the number of the midi device requested.
+ * Pass -1 to request the next free midi unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_midi(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[2], fops, dev, 2, 130,
- "midi", S_IRUGO | S_IWUGO);
+ "midi", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_midi);
@@ -298,22 +328,55 @@ EXPORT_SYMBOL(register_sound_midi);
* in open - see below.
*/
+/**
+ * register_sound_dsp
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a DSP device. Unit is the number of the DSP requested.
+ * Pass -1 to request the next free DSP unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ *
+ * This function allocates both the audio and dsp device entries together
+ * and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
int register_sound_dsp(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[3], fops, dev, 3, 131,
- "dsp", S_IWUGO | S_IRUSR | S_IRGRP);
+ "dsp", S_IWUSR | S_IRUSR);
}
EXPORT_SYMBOL(register_sound_dsp);
+/**
+ * register_sound_synth
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a synth device. Unit is the number of the synth device requested.
+ * Pass -1 to request the next free synth unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
+
int register_sound_synth(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[9], fops, dev, 9, 137,
- "synth", S_IRUGO | S_IWUGO);
+ "synth", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_synth);
+/**
+ * unregister_sound_special
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_special.
+ * The unit passed is the return value from the register function.
+ */
+
+
void unregister_sound_special(int unit)
{
sound_remove_unit(&chains[unit&15], unit);
@@ -321,6 +384,14 @@ void unregister_sound_special(int unit)
EXPORT_SYMBOL(unregister_sound_special);
+/**
+ * unregister_sound_mixer
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_mixer.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_mixer(int unit)
{
sound_remove_unit(&chains[0], unit);
@@ -328,6 +399,14 @@ void unregister_sound_mixer(int unit)
EXPORT_SYMBOL(unregister_sound_mixer);
+/**
+ * unregister_sound_midi
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_midi.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_midi(int unit)
{
return sound_remove_unit(&chains[2], unit);
@@ -335,13 +414,32 @@ void unregister_sound_midi(int unit)
EXPORT_SYMBOL(unregister_sound_midi);
+/**
+ * unregister_sound_dsp
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_dsp.
+ * The unit passed is the return value from the register function.
+ *
+ * Both of the allocated units are released together automatically.
+ */
+
void unregister_sound_dsp(int unit)
{
return sound_remove_unit(&chains[3], unit);
}
+
EXPORT_SYMBOL(unregister_sound_dsp);
+/**
+ * unregister_sound_synth
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_synth.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_synth(int unit)
{
return sound_remove_unit(&chains[9], unit);
diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c
index c446e98e0..393e6a780 100644
--- a/drivers/sound/sound_firmware.c
+++ b/drivers/sound/sound_firmware.c
@@ -47,6 +47,24 @@ static int do_mod_firmware_load(const char *fn, char **fp)
return (int) l;
}
+/**
+ * mod_firmware_load - load sound driver firmware
+ * @fn: filename
+ * @fp: return for the buffer.
+ *
+ * Load the firmware for a sound module (up to 128K) into a buffer.
+ * The buffer is returned in *fp. It is allocated with vmalloc so is
+ * virtually linear and not DMAable. The caller should free it with
+ * vfree when finished.
+ *
+ * The length of the buffer is returned on a successful load, the
+ * value zero on a failure.
+ *
+ * Caution: This API is not recommended. Firmware should be loaded via
+ * an ioctl call and a setup application. This function may disappear
+ * in future.
+ */
+
int mod_firmware_load(const char *fn, char **fp)
{
int r;
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 00f8b6e7b..f3b008f0c 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -74,7 +74,12 @@ caddr_t sound_mem_blocks[1024];
int sound_nblocks = 0;
/* Persistent DMA buffers */
-int sound_dmap_flag = 0;
+#ifdef CONFIG_SOUND_DMAP
+int sound_dmap_flag = 1;
+#else
+int sound_dmap_flag = 0;
+#endif
+
static int soundcard_configured = 0;
static char dma_alloc_map[MAX_DMA_CHANNELS] = {0};
@@ -92,8 +97,6 @@ unsigned long seq_time = 0; /* Time for /dev/sequencer */
static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
static int num_mixer_volumes = 0;
-int traceinit = 0;
-
int *load_mixer_volumes(char *name, int *levels, int present)
{
int i, n;
@@ -637,11 +640,6 @@ soundcard_init(void)
soundcard_configured = 1;
-#if defined(CONFIG_LOWLEVEL_SOUND) && !defined(MODULE)
- sound_preinit_lowlevel_drivers();
- sound_init_lowlevel_drivers();
-#endif
-
audio_init_devices();
soundcard_register_devfs(1); /* register after we know # of devices */
@@ -663,38 +661,15 @@ static int sound[20] = {
static int dmabuf = 0;
static int dmabug = 0;
-MODULE_PARM(traceinit, "i");
MODULE_PARM(dmabuf, "i");
MODULE_PARM(dmabug, "i");
int init_module(void)
{
int err;
-#if FIXED_FOR_2_4_0
- int ints[21];
- int i;
-#endif
-#ifdef HAS_BRIDGE_BUGGY_FUNC
if(dmabug)
isa_dma_bridge_buggy = dmabug;
-#else
- if(dmabug)
- printk(KERN_ERR "sound: rebuild with PCI_QUIRKS enabled to configure this.\n");
-#endif
-
-#if FIXED_FOR_2_4_0
- /*
- * "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);
-#endif
err = create_special_devices();
if (err)
@@ -730,13 +705,6 @@ void cleanup_module(void)
sound_stop_timer();
-#ifdef CONFIG_LOWLEVEL_SOUND
- {
- extern void sound_unload_lowlevel_drivers(void);
-
- sound_unload_lowlevel_drivers();
- }
-#endif
sequencer_unload();
for (i = 0; i < MAX_DMA_CHANNELS; i++)
@@ -855,8 +823,9 @@ void sound_stop_timer(void)
void conf_printf(char *name, struct address_info *hw_config)
{
- if (!traceinit)
- return;
+#ifndef CONFIG_SOUND_TRACEINIT
+ return;
+#else
printk("<%s> at 0x%03x", name, hw_config->io_base);
if (hw_config->irq)
@@ -869,13 +838,14 @@ void conf_printf(char *name, struct address_info *hw_config)
printk(",%d", hw_config->dma2);
}
printk("\n");
+#endif
}
void conf_printf2(char *name, int base, int irq, int dma, int dma2)
{
- if (!traceinit)
- return;
-
+#ifndef CONFIG_SOUND_TRACEINIT
+ return;
+#else
printk("<%s> at 0x%03x", name, base);
if (irq)
@@ -888,6 +858,7 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2)
printk(",%d", dma2);
}
printk("\n");
+#endif
}
/*
diff --git a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c
index d1631defd..75d1a8977 100644
--- a/drivers/sound/waveartist.c
+++ b/drivers/sound/waveartist.c
@@ -1771,6 +1771,18 @@ MODULE_PARM(dma2, "i"); /* DMA2 */
static int __init init_waveartist(void)
{
+ if (!io && machine_is_netwinder()) {
+ /*
+ * The NetWinder WaveArtist is at a fixed address.
+ * If the user does not supply an address, use the
+ * well-known parameters.
+ */
+ io = 0x250;
+ irq = 12;
+ dma = 3;
+ dma2 = 7;
+ }
+
cfg.io_base = io;
cfg.irq = irq;
cfg.dma = dma;
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 9665598b9..6a5af7943 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -508,7 +508,7 @@ static void ixj_timeout(unsigned long ptr)
j->flags.cringing = 0;
ixj_ring_off(board);
} else {
- if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) {
+ if (jiffies - j->ring_cadence_jif >= (hertz/2)) {
j->ring_cadence_t--;
if (j->ring_cadence_t == -1)
j->ring_cadence_t = 15;
@@ -3799,6 +3799,32 @@ int ixj_ioctl(struct inode *inode, struct file *file_p,
case PHONE_CPT_STOP:
ixj_cpt_stop(board);
break;
+ case PHONE_QUERY_CODEC:
+ {
+ struct phone_codec_data pd;
+ int val;
+ int proto_size[] = {
+ -1,
+ 12, 10, 16, 9, 8, 48, 5,
+ 40, 40, 80, 40, 40
+ };
+ if(copy_from_user(&pd, (void *)arg, sizeof(pd)))
+ return -EFAULT;
+ if(pd.type<1 || pd.type>12)
+ return -EPROTONOSUPPORT;
+ if(pd.type<G729)
+ val=proto_size[pd.type];
+ else switch(j->baseframe.low)
+ {
+ case 0xA0:val=2*proto_size[pd.type];break;
+ case 0x50:val=proto_size[pd.type];break;
+ default:val=proto_size[pd.type]*3;break;
+ }
+ pd.buf_min=pd.buf_max=pd.buf_opt=val;
+ if(copy_to_user((void *)arg, &pd, sizeof(pd)))
+ return -EFAULT;
+ return 0;
+ }
case IXJCTL_DSP_IDLE:
idle(board);
break;
@@ -3839,6 +3865,7 @@ int ixj_ioctl(struct inode *inode, struct file *file_p,
ixj_daa_cr4(board, arg | 0x02);
break;
case IXJCTL_PSTN_LINETEST:
+ case PHONE_PSTN_LINETEST:
retval = ixj_linetest(board);
break;
case IXJCTL_CID:
diff --git a/drivers/telephony/phonedev.c b/drivers/telephony/phonedev.c
index 21f850d9b..0f3195163 100644
--- a/drivers/telephony/phonedev.c
+++ b/drivers/telephony/phonedev.c
@@ -10,7 +10,8 @@
*
* Author: Alan Cox, <alan@redhat.com>
*
- * Fixes:
+ * Fixes: Mar 01 2000 Thomas Sparr, <thomas.l.sparr@telia.com>
+ * phone_register_device now works with unit!=PHONE_UNIT_ANY
*/
#include <linux/config.h>
@@ -84,7 +85,7 @@ int phone_register_device(struct phone_device *p, int unit)
if (unit != PHONE_UNIT_ANY) {
base = unit;
- end = unit;
+ end = unit + 1; /* enter the loop at least one time */
}
for (i = base; i < end; i++) {
if (phone_device[i] == NULL) {
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 5f1b61d07..fbb7562dc 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -9,9 +9,6 @@ if [ ! "$CONFIG_USB" = "n" ]; then
comment 'USB Controllers'
dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
- if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
- fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -51,6 +48,7 @@ comment 'USB Devices'
dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB
dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB
dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB
+ dep_tristate ' D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB
comment 'USB HID'
dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7c5b02f21..35bef0e45 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -4,9 +4,10 @@
# Subdirs.
-SUB_DIRS := serial
+SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
+MOD_IN_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) serial
# The target object and module list name.
@@ -37,6 +38,18 @@ obj-m :=
obj-n :=
obj- :=
+# Object files in subdirectories
+
+ifeq ($(CONFIG_USB_SERIAL),y)
+ SUB_DIRS += serial
+ obj-y += serial/serial.o
+else
+ ifeq ($(CONFIG_USB_SERIAL),m)
+ MOD_SUB_DIRS += serial
+ endif
+endif
+
+
# Each configuration option enables a list of files.
obj-$(CONFIG_USB) += usbcore.o
@@ -68,6 +81,7 @@ obj-$(CONFIG_USB_PLUSB) += plusb.o
obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RIO500) += rio500.o
+obj-$(CONFIG_USB_DSBR) += dsbr100.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c
new file mode 100644
index 000000000..8b81e06c0
--- /dev/null
+++ b/drivers/usb/dsbr100.c
@@ -0,0 +1,353 @@
+/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
+ into both the USB and an analog audio input, so this thing
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well. So, scanning will only find
+ stereo stations. Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning. My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ History:
+
+ Version 0.21:
+ Markus Demleitner <msdemlei@tucana.harvard.edu>:
+ Minor cleanup, warnings if something goes wrong, lame attempt
+ to adhere to Documentation/CodingStyle
+
+ Version 0.2:
+ Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+ Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+
+#if CONFIG_MODVERSIONS==1
+#define MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+#define TB_LEN 16
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
+ void *arg);
+static int usb_dsbr100_open(struct video_device *dev, int flags);
+static void usb_dsbr100_close(struct video_device *dev);
+
+
+typedef struct
+{ struct urb readurb,writeurb;
+ struct usb_device *dev;
+ char transfer_buffer[TB_LEN];
+ int curfreq;
+ int stereo;
+ int ifnum;
+} usb_dsbr100;
+
+
+static struct video_device usb_dsbr100_radio=
+{
+ "D-Link DSB R-100 USB radio",
+ VID_TYPE_TUNER,
+ VID_HARDWARE_AZTECH,
+ usb_dsbr100_open,
+ usb_dsbr100_close,
+ NULL, /* Can't read (no capture ability) */
+ NULL, /* Can't write */
+ NULL, /* No poll */
+ usb_dsbr100_ioctl,
+ NULL,
+ NULL
+};
+
+static int users = 0;
+
+static struct usb_driver usb_dsbr100_driver = {
+ name: "dsbr100",
+ probe: usb_dsbr100_probe,
+ disconnect: usb_dsbr100_disconnect,
+ driver_list: {NULL,NULL},
+ fops: NULL,
+ minor: 0
+};
+
+
+static int dsbr100_start(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_stop(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
+{
+ freq = (freq*80)/16+856;
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff,
+ radio->transfer_buffer, 8, 300)<0
+ || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+ radio->stereo = -1;
+ return -1;
+ }
+ radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+ return (radio->transfer_buffer)[0];
+}
+
+static void dsbr100_getstat(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+ radio->stereo = -1;
+ else
+ radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ usb_dsbr100 *radio;
+
+ if (dev->descriptor.idVendor!=DSB100_VENDOR ||
+ dev->descriptor.idProduct!=DSB100_PRODUCT)
+ return NULL;
+ if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL)))
+ return NULL;
+ usb_dsbr100_radio.priv = radio;
+ radio->dev = dev;
+ radio->ifnum = ifnum;
+ radio->curfreq = 1454;
+ return (void*)radio;
+}
+
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr)
+{
+ usb_dsbr100 *radio=ptr;
+
+ if (users)
+ return;
+ kfree(radio);
+ usb_dsbr100_radio.priv = NULL;
+}
+
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
+ void *arg)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (!radio)
+ return -EINVAL;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP: {
+ struct video_capability v;
+ v.type=VID_TYPE_TUNER;
+ v.channels=1;
+ v.audios=1;
+ /* No we don't do pictures */
+ v.maxwidth=0;
+ v.maxheight=0;
+ v.minwidth=0;
+ v.minheight=0;
+ strcpy(v.name, "D-Link R-100 USB Radio");
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGTUNER: {
+ struct video_tuner v;
+ dsbr100_getstat(radio);
+ if(copy_from_user(&v, arg,sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v.rangelow=(87*16000);
+ v.rangehigh=(108*16000);
+ /*v.flags=VIDEO_TUNER_LOW;*/
+ v.mode=VIDEO_MODE_AUTO;
+ v.signal=radio->stereo;
+ v.flags|=VIDEO_TUNER_STEREO_ON;
+ strcpy(v.name, "FM");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER: {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ if (radio->curfreq==-1)
+ return -EINVAL;
+ if(copy_to_user(arg, &(radio->curfreq),
+ sizeof(radio->curfreq)))
+ return -EFAULT;
+ return 0;
+
+ case VIDIOCSFREQ:
+ if(copy_from_user(&(radio->curfreq), arg,
+ sizeof(radio->curfreq)))
+ return -EFAULT;
+ if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+ warn("set frequency failed");
+ return 0;
+
+ case VIDIOCGAUDIO: {
+ struct video_audio v;
+ memset(&v,0, sizeof(v));
+ v.flags|=VIDEO_AUDIO_MUTABLE;
+ v.mode=VIDEO_SOUND_STEREO;
+ v.volume=1;
+ v.step=1;
+ strcpy(v.name, "Radio");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSAUDIO: {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+
+ if(v.flags&VIDEO_AUDIO_MUTE) {
+ if (dsbr100_stop(radio)==-1)
+ warn("radio did not respond properly");
+ }
+ else
+ if (dsbr100_start(radio)==-1)
+ warn("radio did not respond properly");
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+static int usb_dsbr100_open(struct video_device *dev, int flags)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (! radio) {
+ warn("radio not initialised");
+ return -EAGAIN;
+ }
+ if(users)
+ {
+ warn("radio in use");
+ return -EBUSY;
+ }
+ users++;
+ MOD_INC_USE_COUNT;
+ if (dsbr100_start(radio)<0)
+ warn("radio did not start up properly");
+ dsbr100_setfreq(radio,radio->curfreq);
+ return 0;
+}
+
+static void usb_dsbr100_close(struct video_device *dev)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (!radio)
+ return;
+ users--;
+ dsbr100_stop(radio);
+ MOD_DEC_USE_COUNT;
+}
+
+int __init dsbr100_init(void)
+{
+ usb_dsbr100_radio.priv = NULL;
+ usb_register(&usb_dsbr100_driver);
+ if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {
+ warn("couldn't register video device");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int __init init_module(void)
+{
+ return dsbr100_init();
+}
+
+void cleanup_module(void)
+{
+ usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+ if (radio)
+ dsbr100_stop(radio);
+ video_unregister_device(&usb_dsbr100_radio);
+ usb_deregister(&usb_dsbr100_driver);
+}
+
+/*
+vi: ts=8
+Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
diff --git a/drivers/usb/inode.c b/drivers/usb/inode.c
index 92a008d2f..c30f2eaff 100644
--- a/drivers/usb/inode.c
+++ b/drivers/usb/inode.c
@@ -29,6 +29,7 @@
/*****************************************************************************/
#define __NO_VERSION__
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sched.h>
diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c
index 4e9b4f65e..d1f5048e3 100644
--- a/drivers/usb/ov511.c
+++ b/drivers/usb/ov511.c
@@ -2,20 +2,17 @@
* OmniVision OV511 Camera-to-USB Bridge Driver
* Copyright (c) 1999/2000 Mark W. McClelland
* Many improvements by Bret Wallach
- *
+ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Snapshot code by Kevin Moore
+ *
* Based on the Linux CPiA driver.
*
* Released under GPL v.2 license.
*
- * Important keywords in comments:
- * CAMERA SPECIFIC - Camera specific code; may not work with other cameras.
- * DEBUG - Debugging code.
- * FIXME - Something that is broken or needs improvement.
- *
- * Version: 1.07
+ * Version: 1.09
*
* Please see the file: linux/Documentation/usb/ov511.txt
- * and the website at: http://people.delphi.com/mmcclelland/linux/
+ * and the website at: http://alpha.dyndns.org/ov511
* for more info.
*/
@@ -39,15 +36,6 @@
/* Handle mangled (versioned) external symbols */
-#include <linux/config.h> /* retrieve the CONFIG_* macros */
-#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
-# define MODVERSIONS /* force it on */
-#endif
-
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -58,6 +46,7 @@
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/usb.h>
@@ -67,8 +56,6 @@
#define OV511_I2C_RETRIES 3
-#define OV7610_AUTO_ADJUST 1
-
/* Video Size 640 x 480 x 3 bytes for RGB */
#define MAX_FRAME_SIZE (640 * 480 * 3)
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
@@ -77,6 +64,33 @@
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
+// PARAMETER VARIABLES:
+static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
+
+/* 0=no debug messages
+ * 1=init/detection/unload and other significant messages,
+ * 2=some warning messages
+ * 3=config/control function calls
+ * 4=most function calls and data parsing messages
+ * 5=highly repetitive mesgs
+ * NOTE: This should be changed to 0, 1, or 2 for production kernels
+ */
+static int debug = 3;
+
+/* Fix vertical misalignment of red and blue at 640x480 */
+static int fix_rgb_offset = 0;
+
+/* Snapshot mode enabled flag */
+static int snapshot = 0;
+
+MODULE_PARM(autoadjust, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM(snapshot, "i");
+
+MODULE_AUTHOR("Mark McClelland (and others)");
+MODULE_DESCRIPTION("OV511 USB Camera Driver");
+
char kernel_version[] = UTS_RELEASE;
/*******************************/
@@ -206,10 +220,8 @@ int ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char val
USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, &value, 1, HZ);
-#if 0
- PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-#endif
-
+ PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+
return rc;
}
@@ -225,9 +237,7 @@ int ov511_reg_read(struct usb_device *dev, unsigned char reg)
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, buffer, 1, HZ);
-#if 0
- PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]);
-#endif
+ PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
if(rc < 0)
return rc;
@@ -239,9 +249,8 @@ int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char val
{
int rc, retries;
-#if 0
- PDEBUG("i2c write: 0x%02X:0x%02X", reg, value);
-#endif
+ PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+
/* Three byte write cycle */
for(retries = OV511_I2C_RETRIES;;) {
/* Select camera register */
@@ -321,9 +330,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
}
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
-#if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
-#endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
/* This is needed to make ov511_i2c_write() work */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
@@ -355,9 +363,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
if (rc < 0) return rc;
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
- #if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
- #endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
return (value);
}
@@ -391,15 +398,14 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_i2c_read(dev, i);
-#if 0
- PDEBUG("OV7610[0x%X] = 0x%X", i, rc);
-#endif
+
+ PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_i2c_regs( struct usb_device *dev)
{
- PDEBUG("I2C REGS");
+ PDEBUG(3, "I2C REGS");
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
@@ -409,27 +415,27 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn)
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_reg_read(dev, i);
- PDEBUG("OV511[0x%X] = 0x%X", i, rc);
+ PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_regs( struct usb_device *dev)
{
- PDEBUG("CAMERA INTERFACE REGS");
+ PDEBUG(1, "CAMERA INTERFACE REGS");
ov511_dump_reg_range(dev, 0x10, 0x1f);
- PDEBUG("DRAM INTERFACE REGS");
+ PDEBUG(1, "DRAM INTERFACE REGS");
ov511_dump_reg_range(dev, 0x20, 0x23);
- PDEBUG("ISO FIFO REGS");
+ PDEBUG(1, "ISO FIFO REGS");
ov511_dump_reg_range(dev, 0x30, 0x31);
- PDEBUG("PIO REGS");
+ PDEBUG(1, "PIO REGS");
ov511_dump_reg_range(dev, 0x38, 0x39);
ov511_dump_reg_range(dev, 0x3e, 0x3e);
- PDEBUG("I2C REGS");
+ PDEBUG(1, "I2C REGS");
ov511_dump_reg_range(dev, 0x40, 0x49);
- PDEBUG("SYSTEM CONTROL REGS");
+ PDEBUG(1, "SYSTEM CONTROL REGS");
ov511_dump_reg_range(dev, 0x50, 0x53);
ov511_dump_reg_range(dev, 0x5e, 0x5f);
- PDEBUG("OmniCE REGS");
+ PDEBUG(1, "OmniCE REGS");
ov511_dump_reg_range(dev, 0x70, 0x79);
ov511_dump_reg_range(dev, 0x80, 0x9f);
ov511_dump_reg_range(dev, 0xa0, 0xbf);
@@ -441,7 +447,7 @@ int ov511_reset(struct usb_device *dev, unsigned char reset_type)
{
int rc;
- PDEBUG("Reset: type=0x%X", reset_type);
+ PDEBUG(3, "Reset: type=0x%X", reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
if (rc < 0)
err("reset: command failed");
@@ -457,9 +463,7 @@ int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
{
int alt, multiplier, rc;
-#if 0
- PDEBUG("set packet size: %d", size);
-#endif
+ PDEBUG(3, "set packet size: %d", size);
switch (size) {
case 992:
@@ -576,7 +580,7 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511,
p->hue = 0x8000;
p->whiteness = 105 << 8;
- p->depth = 24;
+ p->depth = 3; /* Don't know if this is right */
p->palette = VIDEO_PALETTE_RGB24;
/* Restart the camera */
@@ -594,10 +598,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
int rc = 0;
struct usb_device *dev = ov511->dev;
-#if 0
- PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)",
+ PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
width, height, mode, sub_flag);
-#endif
// ov511_set_packet_size(ov511, 0);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
@@ -606,13 +608,19 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
}
if (mode == VIDEO_PALETTE_GREY) {
- ov511_reg_write(dev, 0x16, 0);
- ov511_i2c_write(dev, 0xe, 0x44);
+ ov511_reg_write(dev, 0x16, 0x00);
+ ov511_i2c_write(dev, 0x0e, 0x44);
ov511_i2c_write(dev, 0x13, 0x21);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x00);
+ ov511_reg_write(dev, 0x1f, 0x01);
} else {
- ov511_reg_write(dev, 0x16, 1);
- ov511_i2c_write(dev, 0xe, 0x4);
- ov511_i2c_write(dev, 0x13, 0x1);
+ ov511_reg_write(dev, 0x16, 0x01);
+ ov511_i2c_write(dev, 0x0e, 0x04);
+ ov511_i2c_write(dev, 0x13, 0x01);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x01);
+ ov511_reg_write(dev, 0x1f, 0x03);
}
if (width == 640 && height == 480) {
@@ -626,13 +634,26 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1);
ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1);
ov511_i2c_write(dev, 0x11, 0x01);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
} else {
ov511_i2c_write(ov511->dev, 0x17, 0x38);
ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2));
ov511_i2c_write(ov511->dev, 0x19, 0x5);
- ov511_i2c_write(ov511->dev, 0x1c, + (480>>1));
+ ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1));
ov511_reg_write(dev, 0x12, 0x4f);
ov511_reg_write(dev, 0x13, 0x3d);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, 0x4f);
+ ov511_reg_write(ov511->dev, 0x1b, 0x3d);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 4); /* check */
} else {
@@ -642,6 +663,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(dev, 0x14, 0x00);
ov511_reg_write(dev, 0x15, 0x00);
+
+ /* FIXME?? Shouldn't below be true only for YUV420? */
ov511_reg_write(dev, 0x18, 0x03);
ov511_i2c_write(dev, 0x12, 0x24);
@@ -654,6 +677,12 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(dev, 0x15, 0x00);
ov511_reg_write(dev, 0x18, 0x03);
+ /* Snapshot additions */
+ ov511_reg_write(dev, 0x1a, 0x27);
+ ov511_reg_write(dev, 0x1b, 0x1f);
+ ov511_reg_write(dev, 0x1c, 0x00);
+ ov511_reg_write(dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 1); /* check */
} else {
@@ -671,7 +700,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
// ov511_set_packet_size(ov511, 993);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
- PDEBUG("reset: command failed");
+ err("reset: command failed");
return -EIO;
}
@@ -683,52 +712,74 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
Turn a YUV4:2:0 block into an RGB block
+Video4Linux seems to use the blue, green, red channel
+order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+
+Color space conversion coefficients taken from the excellent
+http://www.inforamp.net/~poynton/ColorFAQ.html
+In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+Y values are given for all 4 pixels, but the U (Pb)
+and V (Pr) are assumed constant over the 2x2 block.
+
+To avoid floating point arithmetic, the color conversion
+coefficients are scaled into 16.16 fixed-point integers.
+
*************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-static inline void ov511_move_420_block(int y00, int y01, int y10, int y11,
- int u, int v, int w,
- unsigned char * pOut)
+// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+static inline void ov511_move_420_block(
+ int yTL, int yTR, int yBL, int yBR,
+ int u, int v,
+ int rowPixels, unsigned char * rgb)
{
- int r = 68911 * v;
- int g = -16915 * u + -35101 * v;
- int b = 87097 * u;
- y00 *= 49152;
- y01 *= 49152;
- y10 *= 49152;
- y11 *= 49152;
- *(pOut+w*3) = LIMIT(r + y10);
- *pOut++ = LIMIT(r + y00);
- *(pOut+w*3) = LIMIT(g + y10);
- *pOut++ = LIMIT(g + y00);
- *(pOut+w*3) = LIMIT(b + y10);
- *pOut++ = LIMIT(b + y00);
- *(pOut+w*3) = LIMIT(r + y11);
- *pOut++ = LIMIT(r + y01);
- *(pOut+w*3) = LIMIT(g + y11);
- *pOut++ = LIMIT(g + y01);
- *(pOut+w*3) = LIMIT(b + y11);
- *pOut++ = LIMIT(b + y01);
+ const double brightness=1.0;//0->black; 1->full scale
+ const double saturation=1.0;//0->greyscale; 1->full color
+ const double fixScale=brightness*256*256;
+ const int rvScale=(int)(1.402*saturation*fixScale);
+ const int guScale=(int)(-0.344136*saturation*fixScale);
+ const int gvScale=(int)(-0.714136*saturation*fixScale);
+ const int buScale=(int)(1.772*saturation*fixScale);
+ const int yScale=(int)(fixScale);
+
+ int r = rvScale * v;
+ int g = guScale * u + gvScale * v;
+ int b = buScale * u;
+ yTL *= yScale; yTR *= yScale;
+ yBL *= yScale; yBR *= yScale;
+
+ //Write out top two pixels
+ rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL);
+ rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR);
+ rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels
+ rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL);
+ rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR);
}
+
/***************************************************************
For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The
-first 64 bytes of each segment are V, the next 64 are U. The V and
-U are arranged as follows:
+first 64 bytes of each segment are U, the next 64 are V. The U and
+V are arranged as follows:
0 1 ... 7
8 9 ... 15
...
56 57 ... 63
-The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as
-follows:
+U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+
+The next 256 bytes are full resolution Y data and represent 4
+squares of 8x8 pixels as follows:
0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
8 9 ... 15 72 73 ... 79 200 201 ... 207
... ... ...
56 57 ... 63 120 121 127 248 249 ... 255
+Note that the U and V data in one segment represents a 16 x 16 pixel
+area, but the Y data represents a 32 x 8 pixel area.
+
If OV511_DUMPPIX is defined, _parse_data just dumps the
incoming segments, verbatim, in order, into the frame.
When used with vidcat -f ppm -s 640x480 this puts the data
@@ -780,8 +831,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
int y01 = *(pOut+3);
int y10 = *(pOut+iWidth*3);
int y11 = *(pOut+iWidth*3+3);
- int u = *(pIn+64) - 128;
- int v = *pIn++ - 128;
+ int v = *(pIn+64) - 128;
+ int u = *pIn++ - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut);
pOut += 6;
}
@@ -810,8 +861,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
int y00 = *pIn++;
int y11 = *(pIn+8);
int y01 = *pIn++;
- int u = *pOut1 - 128;
- int v = *(pOut1+1) - 128;
+ int v = *pOut1 - 128;
+ int u = *(pOut1+1) - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1);
pOut1 += 6;
}
@@ -868,6 +919,42 @@ static void ov511_parse_data_grey(unsigned char * pIn0,
}
}
+
+/**************************************************************
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ *************************************************************/
+static void fixFrameRGBoffset(struct ov511_frame *frame)
+{
+ int x,y;
+ int rowBytes=frame->width*3,w=frame->width;
+ unsigned char *rgb=frame->data;
+ const int shift=1;//Distance to shift pixels by, vertically
+
+ if (frame->width<400)
+ return;//Don't bother with little images
+
+ //Shift red channel up
+ for (y=shift;y<frame->height;y++)
+ {
+ int lp=(y-shift)*rowBytes;//Previous line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up
+ }
+ //Shift blue channel down
+ for (y=frame->height-shift-1;y>=0;y--)
+ {
+ int ln=(y+shift)*rowBytes;//Next line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down
+ }
+}
+
+
static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
{
unsigned char *cdata;
@@ -887,7 +974,7 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
if (!n || ov511->curframe == -1) continue;
if (st)
- PDEBUG("data error: [%d] len=%d, status=%d", i, n, st);
+ PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
frame = &ov511->frame[ov511->curframe];
@@ -899,14 +986,15 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
struct timeval *ts;
ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
do_gettimeofday(ts);
-#if 0
- PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
+
+ PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
ov511->curframe, (int)(cdata[992]),
(int)(cdata[9]), (int)(cdata[10]));
-#endif
if (frame->scanstate == STATE_LINES) {
int iFrameNext;
+ if (fix_rgb_offset)
+ fixFrameRGBoffset(frame);
frame->grabstate = FRAME_DONE;
if (waitqueue_active(&frame->wq)) {
frame->grabstate = FRAME_DONE;
@@ -919,10 +1007,10 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
ov511->curframe = iFrameNext;
ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
} else {
-#if 0
- PDEBUG("Frame not ready? state = %d",
+
+ PDEBUG(4, "Frame not ready? state = %d",
ov511->frame[iFrameNext].grabstate);
-#endif
+
ov511->curframe = -1;
}
}
@@ -932,11 +1020,18 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
(cdata[8] & 8)) {
-#if 0
- PDEBUG("ov511: Found Frame Start!, framenum = %d",
+
+ PDEBUG(4, "ov511: Found Frame Start!, framenum = %d",
ov511->curframe);
-#endif
- frame->scanstate = STATE_LINES;
+
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (cdata[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "ov511_move_data: snapshot detected");
+ }
+
+ frame->scanstate = STATE_LINES;
frame->segment = 0;
}
@@ -1014,11 +1109,11 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
}
}
-#if 0
- PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n",
+
+ PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n",
aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-#endif
+
return totlen;
}
@@ -1032,7 +1127,7 @@ static void ov511_isoc_irq(struct urb *urb)
return;
if (!ov511->streaming) {
- PDEBUG("hmmm... not streaming, but got interrupt\n");
+ PDEBUG(2, "hmmm... not streaming, but got interrupt");
return;
}
@@ -1166,6 +1261,7 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
frame->grabstate = FRAME_GRABBING;
frame->scanstate = STATE_SCANNING;
frame->scanlength = 0; /* accumulated in ov511_parse_data() */
+ frame->snapshot = 0;
ov511->curframe = framenum;
@@ -1192,7 +1288,7 @@ static int ov511_open(struct video_device *dev, int flags)
int err = -EBUSY;
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_open");
+ PDEBUG(4, "ov511_open");
down(&ov511->lock);
if (ov511->user)
@@ -1212,8 +1308,8 @@ static int ov511_open(struct video_device *dev, int flags)
ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE;
ov511->sub_flag = 0;
- PDEBUG("frame [0] @ %p", ov511->frame[0].data);
- PDEBUG("frame [1] @ %p", ov511->frame[1].data);
+ PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data);
+ PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data);
ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!ov511->sbuf[0].data)
@@ -1222,8 +1318,8 @@ static int ov511_open(struct video_device *dev, int flags)
if (!ov511->sbuf[1].data)
goto open_err_on1;
- PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data);
- PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data);
+ PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data);
+ PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data);
err = ov511_init_isoc(ov511);
if (err)
@@ -1254,7 +1350,7 @@ static void ov511_close(struct video_device *dev)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_close");
+ PDEBUG(4, "ov511_close");
down(&ov511->lock);
ov511->user--;
@@ -1289,9 +1385,8 @@ static long ov511_write(struct video_device *dev, const char *buf, unsigned long
static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
-#if 0
- PDEBUG("IOCtl: 0x%X", cmd);
-#endif
+
+ PDEBUG(4, "IOCtl: 0x%X", cmd);
if (!ov511->dev)
return -EIO;
@@ -1464,11 +1559,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
-#if 0
- PDEBUG("MCAPTURE");
- PDEBUG("frame: %d, size: %dx%d, format: %d",
+ PDEBUG(4, "MCAPTURE");
+ PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
-#endif
if (vm.format != VIDEO_PALETTE_RGB24 &&
vm.format != VIDEO_PALETTE_GREY)
@@ -1516,10 +1609,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
if (copy_from_user((void *)&frame, arg, sizeof(int)))
return -EFAULT;
-#if 0
- PDEBUG("syncing to frame %d, grabstate = %d", frame,
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
ov511->frame[frame].grabstate);
-#endif
+
switch (ov511->frame[frame].grabstate) {
case FRAME_UNUSED:
return -EINVAL;
@@ -1552,7 +1644,17 @@ redo:
}
ov511->frame[frame].grabstate = FRAME_UNUSED;
-
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov511->snap_enabled) &&
+ (ov511->frame[frame].snapshot)) {
+ ov511->frame[frame].snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
return 0;
}
case VIDIOCGFBUF:
@@ -1594,7 +1696,7 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
int frmx = -1;
volatile struct ov511_frame *frame;
- PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock);
+ PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock);
if (!dev || !buf)
return -EFAULT;
@@ -1644,18 +1746,37 @@ restart:
goto restart;
}
- PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+
+ /* Repeat until we get a snapshot frame */
+ if (ov511->snap_enabled && !frame->snapshot) {
+ frame->bytes_read = 0;
+ if (ov511_new_frame(ov511, frmx))
+ err("ov511_read: ov511_new_frame error");
+ goto restart;
+ }
+
+ /* Clear the snapshot */
+ if (ov511->snap_enabled && frame->snapshot) {
+ frame->snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
+ PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
frame->bytes_read, frame->scanlength);
/* copy bytes to user space; we allow for partials reads */
- if ((count + frame->bytes_read) > frame->scanlength)
- count = frame->scanlength - frame->bytes_read;
+// if ((count + frame->bytes_read) > frame->scanlength)
+// count = frame->scanlength - frame->bytes_read;
+ /* FIXME - count hardwired to be one frame... */
+ count = frame->width * frame->height * frame->depth;
if (copy_to_user(buf, frame->data + frame->bytes_read, count))
return -EFAULT;
frame->bytes_read += count;
- PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld",
+ PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld",
count, frame->bytes_read);
if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
@@ -1679,7 +1800,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s
if (!ov511->dev)
return -EIO;
- PDEBUG("mmap: %ld (%lX) bytes", size, size);
+ PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
return -EINVAL;
@@ -1702,24 +1823,22 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s
}
static struct video_device ov511_template = {
- "OV511 USB Camera",
- VID_TYPE_CAPTURE,
- VID_HARDWARE_OV511,
- ov511_open,
- ov511_close,
- ov511_read,
- ov511_write,
- NULL,
- ov511_ioctl,
- ov511_mmap,
- ov511_init_done,
- NULL,
- 0,
- 0
+ name: "OV511 USB Camera",
+ type: VID_TYPE_CAPTURE,
+ hardware: VID_HARDWARE_OV511,
+ open: ov511_open,
+ close: ov511_close,
+ read: ov511_read,
+ write: ov511_write,
+ ioctl: ov511_ioctl,
+ mmap: ov511_mmap,
+ initialize: ov511_init_done,
};
static int ov7610_configure(struct usb_device *dev)
{
+ int tries;
+
if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
OV7610_I2C_WRITE_ID) < 0)
return -1;
@@ -1731,6 +1850,7 @@ static int ov7610_configure(struct usb_device *dev)
if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
return -1;
+ /* Reset the 7610 and wait a bit for it to initialize */
if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
schedule_timeout (1 + 150 * HZ / 1000);
@@ -1738,8 +1858,14 @@ static int ov7610_configure(struct usb_device *dev)
if(ov511_i2c_read(dev, 0x00) < 0)
return -1;
- if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
- (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) {
+ tries = 5;
+ while((tries > 0) &&
+ ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
+ (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
+ --tries;
+ }
+
+ if (tries == 0) {
err("Failed to read OV7610 ID. You might not have an OV7610,");
err("or it may be not responding. Report this to");
err("mmcclelland@delphi.com");
@@ -1786,12 +1912,11 @@ static int ov511_configure(struct usb_ov511 *ov511)
{OV511_I2C_BUS, 0x16, 0x06},
{OV511_I2C_BUS, 0x28, 0x24}, /* 24 */
{OV511_I2C_BUS, 0x2b, 0xac},
- {OV511_I2C_BUS, 0x5, 0x00},
- {OV511_I2C_BUS, 0x6, 0x00},
-#if 0
-#endif
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x06, 0x00},
+
{OV511_I2C_BUS, 0x12, 0x00},
- {OV511_I2C_BUS, 0x13, 0x00},
+// {OV511_I2C_BUS, 0x13, 0x00},
{OV511_I2C_BUS, 0x38, 0x81},
{OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
{OV511_I2C_BUS, 0x05, 0x00},
@@ -1813,7 +1938,7 @@ static int ov511_configure(struct usb_ov511 *ov511)
{OV511_I2C_BUS, 0x33, 0x20},
{OV511_I2C_BUS, 0x34, 0x48},
{OV511_I2C_BUS, 0x12, 0x24},
- {OV511_I2C_BUS, 0x13, 0x01},
+// {OV511_I2C_BUS, 0x13, 0x01},
{OV511_I2C_BUS, 0x11, 0x01},
{OV511_I2C_BUS, 0x0c, 0x24},
{OV511_I2C_BUS, 0x0d, 0x24},
@@ -1853,7 +1978,8 @@ static int ov511_configure(struct usb_ov511 *ov511)
}
ov511->compress = 0;
-
+ ov511->snap_enabled = snapshot;
+
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */
ov511->frame[0].width = DEFAULT_WIDTH;
@@ -1864,10 +1990,16 @@ static int ov511_configure(struct usb_ov511 *ov511)
ov511->frame[1].bytes_read = 0;
/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
- if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc;
+ if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error;
if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
- VIDEO_PALETTE_RGB24, 0)) < 0) return rc;
+ VIDEO_PALETTE_RGB24, 0)) < 0) goto error;
+ if (autoadjust) {
+ if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
+ }
+ else {
+ if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error;
+ }
return 0;
@@ -1887,7 +2019,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
struct usb_ov511 *ov511;
int rc;
- PDEBUG("probing for device...");
+ PDEBUG(1, "probing for device...");
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
@@ -1933,19 +2065,25 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
case 3:
printk("ov511: Camera is a D-Link DSB-C300\n");
break;
+ case 4:
+ printk("ov511: Camera is a generic OV511/OV7610\n");
+ break;
case 5:
printk("ov511: Camera is a Puretek PT-6007\n");
break;
case 21:
printk("ov511: Camera is a Creative Labs WebCam 3\n");
break;
+ case 36:
+ printk("ov511: Camera is a Koala-Cam\n");
+ break;
case 100:
printk("ov511: Camera is a Lifeview RoboCam\n");
break;
case 102:
printk("ov511: Camera is a AverMedia InterCam Elite\n");
break;
- case 112:
+ case 112: /* The OmniVision OV7110 evaluation kit uses this too */
printk("ov511: Camera is a MediaForte MV300\n");
break;
default:
@@ -2025,31 +2163,21 @@ static struct usb_driver ov511_driver = {
{ NULL, NULL }
};
-int usb_ov511_init(void)
+static int __init usb_ov511_init(void)
{
- PDEBUG("usb_ov511_init()");
-
- EXPORT_NO_SYMBOLS;
-
- return usb_register(&ov511_driver);
-}
+ if (usb_register(&ov511_driver) < 0)
+ return -1;
-void usb_ov511_cleanup(void)
-{
- usb_deregister(&ov511_driver);
-}
+ info("ov511 driver registered");
-#ifdef MODULE
-int init_module(void)
-{
- return usb_ov511_init();
+ return 0;
}
-void cleanup_module(void)
+static void __exit usb_ov511_exit(void)
{
- usb_ov511_cleanup();
-
- PDEBUG("Module unloaded");
+ usb_deregister(&ov511_driver);
+ info("ov511 driver deregistered");
}
-#endif
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h
index ba54fb47a..6a4a332bf 100644
--- a/drivers/usb/ov511.h
+++ b/drivers/usb/ov511.h
@@ -6,9 +6,10 @@
#define OV511_DEBUG /* Turn on debug messages */
#ifdef OV511_DEBUG
-# define PDEBUG(fmt, args...) printk("ov511: " fmt "\n" , ## args)
+# define PDEBUG(level, fmt, args...) \
+if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#else
-# define PDEBUG(fmt, args...) do {} while(0)
+# define PDEBUG(level, fmt, args...) do {} while(0)
#endif
/* Camera interface register numbers */
@@ -227,6 +228,8 @@ struct ov511_frame {
long bytes_read; /* amount of scanlength that has been read from *data */
wait_queue_head_t wq; /* Processes waiting */
+
+ int snapshot; /* True if frame was a snapshot */
};
#define OV511_NUMFRAMES 2
@@ -269,6 +272,8 @@ struct usb_ov511 {
int scratchlen;
wait_queue_head_t wq; /* Processes waiting */
+
+ int snap_enabled; /* Snapshot mode enabled */
};
#endif
diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c
index d3761bf52..2d5de6f73 100644
--- a/drivers/usb/pegasus.c
+++ b/drivers/usb/pegasus.c
@@ -1,578 +1,474 @@
/*
-**
** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
**
-** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
-**
+** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**
** Distribute under GPL version 2 or later.
*/
-
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/usb.h>
-
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/usb.h>
-#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
-#error You can not compile this driver on this kernel with this C options!
-#endif
-
-
-#define ADMTEK_VENDOR_ID 0x07a6
-#define ADMTEK_HPNA_PEGASUS 0x0986
-
-#define HPNA_MTU 1500
-#define MAX_MTU 1536
-#define TX_TIMEOUT (HZ*5)
-#define SOMETHING (jiffies + TX_TIMEOUT)
+static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
-static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+#define ADMTEK_VENDOR_ID 0x07a6
+#define ADMTEK_DEVICE_ID_PEGASUS 0x0986
+#define PEGASUS_MTU 1500
+#define PEGASUS_MAX_MTU 1536
+#define PEGASUS_TX_TIMEOUT (HZ*5)
+#define ALIGN(x) x __attribute__((aligned(16)))
-typedef struct usb_hpna
-{
- struct usb_device *usb_dev;
- struct net_device *net_dev;
- int present;
- int active;
- void *irq_handler;
- struct list_head list;
+struct pegasus {
+ struct usb_device *usb;
+ struct net_device *net;
struct net_device_stats stats;
- spinlock_t hpna_lock;
- struct timer_list timer;
-
- unsigned int rx_pipe;
- unsigned char * rx_buff;
- urb_t rx_urb;
-
- unsigned int tx_pipe;
- unsigned char * tx_buff;
- urb_t tx_urb;
- struct sk_buff * tx_skbuff;
-
- __u8 intr_ival;
- unsigned int intr_pipe;
- unsigned char intr_buff[8];
- urb_t intr_urb;
-} usb_hpna_t;
-
-
-usb_hpna_t usb_dev_hpna;
-static int loopback = 0;
-int multicast_filter_limit = 32;
-static LIST_HEAD(hpna_list);
+ spinlock_t pegasus_lock;
+ struct urb rx_urb, tx_urb, intr_urb;
+ unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(intr_buff[8]);
+};
+static int loopback = 0;
+static int multicast_filter_limit = 32;
MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
-MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
-
-/*** vendor specific commands ***/
-static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
- indx, data, size, HZ);
-}
-
-
-static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
-{
- __u8 data = value;
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
- data, indx, &data, 1, HZ);
-}
+#define pegasus_get_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
+#define pegasus_set_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
+#define pegasus_set_register(dev, indx, value) \
+ { __u8 data = value; \
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
-static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
{
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
- indx, data, size, HZ);
-}
-
-
-static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
-{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = 0;
- data[2] = 0;
- data[3] = 0x40 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x25, 4, data );
- if ( data[3] & 0x80 ) {
- *regdata = *(__u16 *)(data+1);
- return 0;
+ int i;
+ __u8 data[4] = { 1, 0, 0, 0x40 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x26, 3, data);
+ if (data[2] & 0x80) {
+ *regdata = *(__u16 *)(data);
+ return 0;
}
udelay(100);
}
+
warn("read_phy_word() failed");
- return 1;
+ return 1;
}
-
-static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = regdata;
- data[2] = regdata >> 8;
- data[3] = 0x20 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x28, 1, data );
- if ( data[0] & 0x80 ) {
- return 0;
- }
+ int i;
+ __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x28, 1, data);
+ if (data[0] & 0x80)
+ return 0;
udelay(100);
}
+
warn("write_phy_word() failed");
- return 1;
+ return 1;
}
-
-int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
{
- int i;
- __u8 data[4];
-
- data[0] = index;
- data[1] = data[2] = 0;
- data[3] = 0x02;
- hpna_set_registers(dev, 0x20, 4, data);
- for ( i=0; i<100; i++ ) {
- hpna_get_registers(dev, 0x23, 1, data);
- if ( data[0] & 4 ) {
- hpna_get_registers(dev, 0x21, 2, data);
+ int i;
+ __u8 data[4] = { index, 0, 0, 0x02 };
+
+ pegasus_set_registers(dev, 0x20, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x23, 1, data);
+ if (data[0] & 4) {
+ pegasus_get_registers(dev, 0x21, 2, data);
*retdata = *(__u16 *)data;
- return 0;
+ return 0;
}
}
+
warn("read_srom_word() failed");
- return 1;
+ return 1;
}
-/*** end ***/
-
-
-
-int get_node_id( struct usb_device *dev, __u8 *id )
+static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
{
- int i;
-
- for ( i=0; i<3; i++ ) {
- if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
- return 1;
- }
- return 0;
+ int i;
+ for (i = 0; i < 3; i++)
+ if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+ return 1;
+ return 0;
}
-
-static int reset_mac( struct usb_device *dev )
+static int pegasus_reset_mac(struct usb_device *dev)
{
- __u8 data = 0x8;
- int i;
-
- hpna_set_register( dev, 1, 0x08 );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 1, 1, &data);
- if ( !(data & 0x08) ) {
- if ( loopback & 1 )
- return 0;
- else if ( loopback & 2 ) {
- write_phy_word( dev, 0, 0x4000 );
- /*return 0;*/
- }
- hpna_set_register( dev, 0x7e, 0x24 );
- hpna_set_register( dev, 0x7e, 0x27 );
- return 0;
+ __u8 data = 0x8;
+ int i;
+
+ pegasus_set_register(dev, 1, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 1, 1, &data);
+ if (~data & 0x08) {
+ if (loopback & 1)
+ return 0;
+ if (loopback & 2)
+ pegasus_write_phy_word(dev, 0, 0x4000);
+ pegasus_set_register(dev, 0x7e, 0x24);
+ pegasus_set_register(dev, 0x7e, 0x27);
+ return 0;
}
}
+
return 1;
}
-
-int start_net( struct net_device *dev, struct usb_device *usb_dev )
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
{
- __u16 partmedia, temp;
- __u8 node_id[6];
- __u8 data[4];
-
- if ( get_node_id(usb_dev, node_id) )
- return 1;
- hpna_set_registers(usb_dev, 0x10, 6, node_id);
+ __u16 partmedia, temp;
+ __u8 node_id[6];
+ __u8 data[4];
+
+ if (pegasus_get_node_id(usb, node_id))
+ return 1;
+
+ pegasus_set_registers(usb, 0x10, 6, node_id);
memcpy(dev->dev_addr, node_id, 6);
- if ( read_phy_word(usb_dev, 1, &temp) )
- return 2;
- if ( !(temp & 4) ) {
- if ( loopback )
- goto ok;
+ if (pegasus_read_phy_word(usb, 1, &temp))
+ return 2;
+
+ if ((~temp & 4) && !loopback) {
err("link NOT established - %x", temp);
- return 3;
- }
-ok:
- if ( read_phy_word(usb_dev, 5, &partmedia) )
- return 4;
- temp = partmedia;
- partmedia &= 0x1f;
- if ( partmedia != 1 ) {
- err("party FAIL %x", temp);
- return 5;
+ return 3;
}
- partmedia = temp;
- if ( partmedia & 0x100 )
- data[1] = 0x30;
- else {
- if ( partmedia & 0x80 )
- data[1] = 0x10;
- else
- data[1] = 0;
+
+ if (pegasus_read_phy_word(usb, 5, &partmedia))
+ return 4;
+
+ if ((partmedia & 0x1f) != 1) {
+ err("party FAIL %x", partmedia);
+ return 5;
}
-
- data[0] = 0xc9;
- data[2] = (loopback & 1) ? 0x08 : 0x00;
-
- hpna_set_registers(usb_dev, 0, 3, data);
-
- return 0;
-}
+ data[0] = 0xc9;
+ data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+ data[2] = (loopback & 1) ? 0x08 : 0x00;
-static void hpna_read_irq( purb_t urb )
-{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
- int count = urb->actual_length, res;
- int rx_status = *(int *)(hpna->rx_buff + count - 4);
+ pegasus_set_registers(usb, 0, 3, data);
+ return 0;
+}
- if ( urb->status ) {
- info( "%s: RX status %d\n", net_dev->name, urb->status );
+static void pegasus_read_bulk(struct urb *urb)
+{
+ struct pegasus *pegasus = urb->context;
+ struct net_device *net = pegasus->net;
+ int count = urb->actual_length, res;
+ int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+ struct sk_buff *skb;
+ __u16 pkt_len;
+
+ if (urb->status) {
+ info("%s: RX status %d", net->name, urb->status);
goto goon;
}
- if ( !count )
+ if (!count)
goto goon;
-/* if ( rx_status & 0x00010000 )
+#if 0
+ if (rx_status & 0x00010000)
+ goto goon;
+#endif
+ if (rx_status & 0x000e0000) {
+
+ dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+ pegasus->stats.rx_errors++;
+ if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
+ if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
+ if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+
goto goon;
-*/
- if ( rx_status & 0x000e0000 ) {
- dbg("%s: error receiving packet %x",
- net_dev->name, rx_status & 0xe0000);
- hpna->stats.rx_errors++;
- if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
- if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
- if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
- } else {
- struct sk_buff *skb;
- __u16 pkt_len = (rx_status & 0xfff) - 8;
-
-
- if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
- skb->dev = net_dev;
- skb_reserve(skb, 2);
- eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
- skb_put(skb, pkt_len);
- } else
- goto goon;
- skb->protocol = eth_type_trans(skb, net_dev);
- netif_rx(skb);
- hpna->stats.rx_packets++;
- hpna->stats.rx_bytes += pkt_len;
}
+
+ pkt_len = (rx_status & 0xfff) - 8;
+
+ if(!(skb = dev_alloc_skb(pkt_len+2)))
+ goto goon;
+
+ skb->dev = net;
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+ skb_put(skb, pkt_len);
+
+ skb->protocol = eth_type_trans(skb, net);
+ netif_rx(skb);
+ pegasus->stats.rx_packets++;
+ pegasus->stats.rx_bytes += pkt_len;
+
goon:
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(prb)failed rx_urb %d", res);
}
-
-static void hpna_irq( urb_t *urb)
+static void pegasus_irq(urb_t *urb)
{
- if( urb->status ) {
+ if(urb->status) {
__u8 *d = urb->transfer_buffer;
printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
- d[0], d[1], d[2], d[3], d[4], d[5] );
+ d[0], d[1], d[2], d[3], d[4], d[5]);
}
}
-
-static void hpna_write_irq( purb_t urb )
+static void pegasus_write_bulk(struct urb *urb)
{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
+ struct pegasus *pegasus = urb->context;
+ spin_lock(&pegasus->pegasus_lock);
- spin_lock( &hpna->hpna_lock );
-
- if ( urb->status )
- info("%s: TX status %d\n", net_dev->name, urb->status);
- netif_wake_queue( net_dev );
+ if (urb->status)
+ info("%s: TX status %d", pegasus->net->name, urb->status);
+ netif_wake_queue(pegasus->net);
- spin_unlock( &hpna->hpna_lock );
+ spin_unlock(&pegasus->pegasus_lock);
}
-
-static void tx_timeout( struct net_device *dev )
+static void pegasus_tx_timeout(struct net_device *net)
{
- usb_hpna_t *hpna = dev->priv;
+ struct pegasus *pegasus = net->priv;
- warn( "%s: Tx timed out. Reseting...", dev->name );
- hpna->stats.tx_errors++;
- dev->trans_start = jiffies;
- netif_wake_queue( dev );
-}
+ warn("%s: Tx timed out. Reseting...", net->name);
+ pegasus->stats.tx_errors++;
+ net->trans_start = jiffies;
+ netif_wake_queue(net);
+}
-static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
- int res;
+ struct pegasus *pegasus = net->priv;
+ int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+ int res;
- spin_lock( &hpna->hpna_lock );
+ spin_lock(&pegasus->pegasus_lock);
- netif_stop_queue( net_dev );
- ((__u16 *)hpna->tx_buff)[0] = skb->len;
- memcpy(hpna->tx_buff+2, skb->data, skb->len);
- (&hpna->tx_urb)->transfer_buffer_length = count;
- if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+ netif_stop_queue(net);
+
+ ((__u16 *)pegasus->tx_buff)[0] = skb->len;
+ memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+ (&pegasus->tx_urb)->transfer_buffer_length = count;
+
+ if ((res = usb_submit_urb(&pegasus->tx_urb))) {
warn("failed tx_urb %d", res);
- hpna->stats.tx_errors++;
- netif_start_queue( net_dev );
+ pegasus->stats.tx_errors++;
+ netif_start_queue(net);
} else {
- hpna->stats.tx_packets++;
- hpna->stats.tx_bytes += skb->len;
- net_dev->trans_start = jiffies;
+ pegasus->stats.tx_packets++;
+ pegasus->stats.tx_bytes += skb->len;
+ net->trans_start = jiffies;
}
- dev_kfree_skb( skb );
- spin_unlock( &hpna->hpna_lock );
- return 0;
-}
+ dev_kfree_skb(skb);
-static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
-{
- return &((usb_hpna_t *)dev->priv)->stats;
+ spin_unlock(&pegasus->pegasus_lock);
+
+ return 0;
}
-static int hpna_open( struct net_device *net_dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int res;
+ return &((struct pegasus *)dev->priv)->stats;
+}
- if ( hpna->active )
- return -EBUSY;
- else
- hpna->active = 1;
+static int pegasus_open(struct net_device *net)
+{
+ struct pegasus *pegasus = (struct pegasus *)net->priv;
+ int res;
- if ( start_net(net_dev, hpna->usb_dev) ) {
- err("can't start_net()");
- return -EIO;
+ if ((res = pegasus_start_net(net, pegasus->usb))) {
+ err("can't start_net() - %d", res);
+ return -EIO;
}
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(open)failed rx_urb %d", res);
-/* usb_submit_urb( &hpna->intr_urb );*/
- netif_start_queue( net_dev );
+/* usb_submit_urb(&pegasus->intr_urb);*/
+ netif_start_queue(net);
MOD_INC_USE_COUNT;
return 0;
}
-
-static int hpna_close( struct net_device *net_dev )
+static int pegasus_close(struct net_device *net)
{
- usb_hpna_t *hpna = net_dev->priv;
-
-
- netif_stop_queue( net_dev );
+ struct pegasus *pegasus = net->priv;
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( hpna->intr_urb );*/
+ netif_stop_queue(net);
- hpna->active = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb); */
MOD_DEC_USE_COUNT;
return 0;
}
-
-static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
- __u16 *data = (__u16 *)&rq->ifr_data;
- usb_hpna_t *hpna = dev->priv;
+ __u16 *data = (__u16 *)&rq->ifr_data;
+ struct pegasus *pegasus = net->priv;
- switch( cmd ) {
- case SIOCDEVPRIVATE:
+ switch(cmd) {
+ case SIOCDEVPRIVATE:
data[0] = 1;
case SIOCDEVPRIVATE+1:
- read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
- return 0;
+ pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+ return 0;
case SIOCDEVPRIVATE+2:
- if ( !capable(CAP_NET_ADMIN) )
- return -EPERM;
- write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
- return 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+ return 0;
default:
- return -EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
}
-
-static void set_rx_mode( struct net_device *net_dev )
+static void pegasus_set_rx_mode(struct net_device *net)
{
- usb_hpna_t *hpna=net_dev->priv;
+ struct pegasus *pegasus = net->priv;
- netif_stop_queue( net_dev );
-
- if ( net_dev->flags & IFF_PROMISC ) {
- info("%s: Promiscuous mode enabled", net_dev->name);
- hpna_set_register( hpna->usb_dev, 2, 0x04 );
- } else if ((net_dev->mc_count > multicast_filter_limit) ||
- (net_dev->flags & IFF_ALLMULTI)) {
- hpna_set_register(hpna->usb_dev, 0, 0xfa);
- hpna_set_register(hpna->usb_dev, 2, 0);
+ netif_stop_queue(net);
+
+ if (net->flags & IFF_PROMISC) {
+ info("%s: Promiscuous mode enabled", net->name);
+ pegasus_set_register(pegasus->usb, 2, 0x04);
+ } else if ((net->mc_count > multicast_filter_limit) ||
+ (net->flags & IFF_ALLMULTI)) {
+ pegasus_set_register(pegasus->usb, 0, 0xfa);
+ pegasus_set_register(pegasus->usb, 2, 0);
} else {
- dbg("%s: set Rx mode", net_dev->name);
+ dbg("%s: set Rx mode", net->name);
}
- netif_wake_queue( net_dev );
+ netif_wake_queue(net);
}
-
-static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct net_device *net_dev;
- usb_hpna_t *hpna = &usb_dev_hpna;
-
+ struct net_device *net;
+ struct pegasus *pegasus;
-
- if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
- dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
- return NULL;
+ if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+ dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+ return NULL;
}
- printk("USB HPNA Pegasus found\n");
-
- if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
return NULL;
}
- hpna->usb_dev = dev;
-
- hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
- hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
- hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
-
- if ( reset_mac(dev) ) {
- err("can't reset MAC");
+ if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+ err("out of memory allocating device structure");
+ return NULL;
}
+ memset(pegasus, 0, sizeof(struct pegasus));
- hpna->present = 1;
-
- if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- err("not enough mem for out buff");
- return NULL;
- }
- if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- kfree_s(hpna->rx_buff, MAX_MTU);
- err("not enough mem for out buff");
- return NULL;
+ if (pegasus_reset_mac(dev)) {
+ err("can't reset MAC");
+ kfree(pegasus);
+ return NULL;
}
-
- net_dev = init_etherdev( 0, 0 );
- hpna->net_dev = net_dev;
- net_dev->priv = hpna;
- net_dev->open = hpna_open;
- net_dev->stop = hpna_close;
- net_dev->watchdog_timeo = TX_TIMEOUT;
- net_dev->tx_timeout = tx_timeout;
- net_dev->do_ioctl = hpna_ioctl;
- net_dev->hard_start_xmit = hpna_start_xmit;
- net_dev->set_multicast_list = set_rx_mode;
- net_dev->get_stats = hpna_netdev_stats;
- net_dev->mtu = HPNA_MTU;
- hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
-
- FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe,
- hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
- FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe,
- hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
- FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
- hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
-/* list_add( &hpna->list, &hpna_list );*/
-
- return net_dev;
+ net = init_etherdev(0, 0);
+ net->priv = pegasus;
+ net->open = pegasus_open;
+ net->stop = pegasus_close;
+ net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+ net->tx_timeout = pegasus_tx_timeout;
+ net->do_ioctl = pegasus_ioctl;
+ net->hard_start_xmit = pegasus_start_xmit;
+ net->set_multicast_list = pegasus_set_rx_mode;
+ net->get_stats = pegasus_netdev_stats;
+ net->mtu = PEGASUS_MTU;
+
+ pegasus->usb = dev;
+ pegasus->net = net;
+ pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+
+ FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+ pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk,
+ pegasus);
+ FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+ pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
+ pegasus);
+ FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 0),
+ pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
+
+
+ printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+
+ return pegasus;
}
-
-static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
{
- struct net_device *net_dev = ptr;
- struct usb_hpna *hpna = net_dev->priv;
+ struct pegasus *pegasus = ptr;
+ if (!pegasus) {
+ warn("unregistering non-existant device");
+ return;
+ }
- if ( net_dev->flags & IFF_UP )
- dev_close(net_dev);
-
- unregister_netdev( net_dev );
+ if (pegasus->net->flags & IFF_UP)
+ dev_close(pegasus->net);
- if ( !hpna ) /* should never happen */
- return;
-
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( &hpna->intr_urb );*/
- kfree_s(hpna->rx_buff, MAX_MTU);
- kfree_s(hpna->tx_buff, MAX_MTU);
+ unregister_netdev(pegasus->net);
- hpna->usb_dev = NULL;
- hpna->present = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb);*/
- printk("USB HPNA disconnected\n");
+ kfree(pegasus);
}
-
-static struct usb_driver usb_hpna_driver = {
- "ADMtek \"Pegasus\" USB Ethernet",
- usb_hpna_probe,
- usb_hpna_disconnect,
- {NULL, NULL}
+static struct usb_driver pegasus_driver = {
+ name: "pegasus",
+ probe: pegasus_probe,
+ disconnect: pegasus_disconnect,
};
-
-
-static int __init start_hpna( void )
+int __init pegasus_init(void)
{
printk( version );
- return usb_register( &usb_hpna_driver );
+ return usb_register(&pegasus_driver);
}
-
-static void __exit stop_hpna( void )
+void __exit pegasus_exit(void)
{
- usb_deregister( &usb_hpna_driver );
+ usb_deregister(&pegasus_driver);
}
-
-module_init( start_hpna );
-module_exit( stop_hpna );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index d5c245bf1..20dc5dde5 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -6,14 +6,15 @@
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
+MOD_IN_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS)
# The target object and module list name.
-O_TARGET := usbdrv.o
-M_OBJS :=
-O_OBJS :=
-MOD_LIST_NAME := USB_MODULES
+O_TARGET := serial.o
+M_OBJS := usb-serial.o
+O_OBJS := usb-serial.o
+#MOD_LIST_NAME := USB_MODULES
# Objects that export symbols.
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index c3b4ebc2e..480c6252d 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -236,7 +236,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *prevtd = NULL;
+ struct uhci_td *td, *prevtd;
if (!urbp)
return;
@@ -617,6 +617,8 @@ static int uhci_submit_control(urb_t *urb)
return -EINPROGRESS;
}
+static int usb_control_retrigger_status(urb_t *urb);
+
static int uhci_result_control(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@ static int uhci_result_control(urb_t *urb)
if (!td)
return -EINVAL;
+ if (urbp->short_control_packet)
+ goto status_phase;
+
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@ static int uhci_result_control(urb_t *urb)
/* If SPD is set then we received a short packet */
/* There will be no status phase at the end */
- /* FIXME: Re-setup the queue to run the STATUS phase? */
if ((td->status & TD_CTRL_SPD) &&
(uhci_actual_length(td->status) < uhci_expected_length(td->info)))
- return 0;
+ return usb_control_retrigger_status(urb);
if (status)
goto td_error;
@@ -664,12 +668,13 @@ static int uhci_result_control(urb_t *urb)
td = td->list.next;
}
+status_phase:
/* Control status phase */
status = uhci_status_bits(td->status);
/* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of */
- /* the amount we requested */
+ /* It tries to send all of the descriptor instead of the amount */
+ /* we requested */
if (td->status & TD_CTRL_IOC &&
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
@@ -700,6 +705,47 @@ td_error:
return uhci_map_status(status, uhci_packetout(td->info));
}
+static int usb_control_retrigger_status(urb_t *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci *uhci = urb->dev->bus->hcpriv;
+ struct uhci_td *td, *nexttd;
+
+ urbp->short_control_packet = 1;
+
+ /* Delete all of the TD's except for the status TD at the end */
+ td = urbp->list.begin;
+ while (td && td->list.next) {
+ nexttd = td->list.next;
+
+ uhci_remove_td_from_urb(urb, td);
+
+ uhci_remove_td(uhci, td);
+
+ uhci_free_td(td);
+
+ td = nexttd;
+ }
+
+ /* Create a new QH to avoid pointer overwriting problems */
+ uhci_remove_qh(uhci, urbp->qh);
+
+ urbp->qh = uhci_alloc_qh(urb->dev);
+ if (!urbp->qh)
+ return -ENOMEM;
+
+ /* One TD, who cares about Breadth first? */
+ uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+ /* Low speed or small transfers gets a different queue and treatment */
+ if (urb->pipe & TD_CTRL_LS)
+ uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+ else
+ uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+ return -EINPROGRESS;
+}
+
/*
* Interrupt transfers
*/
diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h
index 62d4e772e..9f4c45e96 100644
--- a/drivers/usb/uhci.h
+++ b/drivers/usb/uhci.h
@@ -338,7 +338,11 @@ struct uhci {
struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
- int fsbr;
+ int fsbr; /* Did this URB turn on FSBR? */
+
+ char short_control_packet; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
unsigned long inserttime; /* In jiffies */
diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c
index 2bba728d9..cb6a70621 100644
--- a/drivers/usb/usb-core.c
+++ b/drivers/usb/usb-core.c
@@ -31,7 +31,6 @@ void usb_major_cleanup(void);
int usb_audio_init(void);
int usb_cpia_init(void);
int usb_ibmcam_init(void);
-int usb_ov511_init(void);
int dabusb_init(void);
int plusb_init(void);
@@ -78,12 +77,12 @@ int usb_init(void)
#ifdef CONFIG_USB_IBMCAM
usb_ibmcam_init();
#endif
-#ifdef CONFIG_USB_OV511
- usb_ov511_init();
-#endif
#ifdef CONFIG_USB_DABUSB
dabusb_init();
#endif
+#ifdef CONFIG_USB_DSBR
+ dsbr100_init();
+#endif
#ifdef CONFIG_USB_PLUSB
plusb_init();
#endif
diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c
index 0e2ab20a4..956e80a0a 100644
--- a/drivers/usb/usb-storage.c
+++ b/drivers/usb/usb-storage.c
@@ -6,9 +6,9 @@
* Further reference:
* This driver is based on the 'USB Mass Storage Class' document. This
* describes in detail the protocol used to communicate with such
- * devices. Clearly, the designers had SCSI commands in mind when they
- * created this document. The commands are all similar to commands
- * in the SCSI-II specification.
+ * devices. Clearly, the designers had SCSI and ATAPI commands in mind
+ * when they created this document. The commands are all very similar
+ * to commands in the SCSI-II and ATAPI specifications.
*
* It is important to note that in a number of cases this class exhibits
* class-specific exemptions from the USB specification. Notably the
@@ -65,8 +65,6 @@ unsigned char us_direction[256/8] = {
static int my_host_number;
-int usb_stor_debug = 1;
-
struct us_data;
typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -74,7 +72,7 @@ typedef int (*trans_reset)(struct us_data*);
typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
struct us_data {
- struct us_data *next; /* next device */
+ struct us_data *next; /* next device */
struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */
__u8 ifnum; /* interface number */
@@ -93,15 +91,17 @@ struct us_data {
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
+ Scsi_Cmnd *queue_srb; /* the single queue slot */
int action; /* what to do */
- wait_queue_head_t waitq; /* thread waits */
- wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ struct semaphore ip_waitq; /* for CBI interrupts */
__u16 ip_data; /* interrupt data */
int ip_wanted; /* needed */
int pid; /* control thread */
struct semaphore *notify; /* wait for thread to begin */
void *irq_handle; /* for USB int requests */
unsigned int irqpipe; /* pipe for release_irq */
+ struct semaphore sleeper; /* to sleep on */
+ struct semaphore queue_exclusion; /* to protect data structs */
};
/*
@@ -129,117 +129,100 @@ static struct usb_driver storage_driver = {
* Data transfer routines
***********************************************************************/
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory. Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
*
- * FIXME: is the above true? Or will the URB status show ETIMEDOUT after
- * retrying several times allready? Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit. Thus we don't have to worry about it for individual
+ * packets.
*/
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe,
+ char *buf, int length)
{
- int max_size;
- int this_xfer;
int result;
int partial;
- int maxtry;
-
- /* determine the maximum packet size for these transfers */
- max_size = usb_maxpacket(us->pusb_dev,
- pipe, usb_pipeout(pipe)) * 16;
-
- /* while we have data left to transfer */
- while (length) {
-
- /* calculate how long this will be -- maximum or a remainder */
- this_xfer = length > max_size ? max_size : length;
- length -= this_xfer;
-
- /* FIXME: this number is totally outrageous. We need to pick
- * a better (smaller) number).
- */
-
- /* setup the retry counter */
- maxtry = 100;
-
- /* set up the transfer loop */
- do {
- /* transfer the data */
- US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n",
- (unsigned int)buf, this_xfer, 101 - maxtry);
- result = usb_bulk_msg(us->pusb_dev, pipe, buf,
- this_xfer, &partial, HZ*5);
- US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
- result, partial, this_xfer);
-
- /* if we stall, we need to clear it before we go on */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
- }
-
- /* update to show what data was transferred */
- this_xfer -= partial;
- buf += partial;
-
- /* NAK - we retry a few times */
- if (result == -ETIMEDOUT) {
- US_DEBUGP("us_one_transfer: device NAKed\n");
-
- /* if our try counter reaches 0, bail out */
- if (!maxtry--)
- return -ETIMEDOUT;
+ /* transfer the data */
+ US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
+ result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5);
+ US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+ result, partial, length);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
- /* just continue the while loop */
- continue;
- }
-
- /* other errors (besides NAK) -- we just bail out*/
- if (result != 0) {
- US_DEBUGP("us_one_transfer: device returned error %d\n", result);
- return result;
- }
+ /* did we send all the data? */
+ if (partial == length) {
+ return US_BULK_TRANSFER_GOOD;
+ }
- /* continue until this transfer is done */
- } while ( this_xfer );
+ /* uh oh... we have an error code, so something went wrong. */
+ if (result) {
+ /* NAK - that means we've retried a few times allready */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("us_bulk_transfer: device NAKed\n");
+ }
+ return US_BULK_TRANSFER_FAILED;
}
- /* if we get here, we're done and successful */
- return 0;
+ /* no error code, so we must have transferred some data,
+ * just not all of it */
+ return US_BULK_TRANSFER_SHORT;
}
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately. For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, int dir_in)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
+ struct us_data *us;
int i;
int result = -1;
- unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
- usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+ unsigned int pipe;
+ struct scatterlist *sg;
- /* FIXME: stop transferring data at us_transfer_length(), not
- * bufflen */
+ /* calculate the appropriate pipe information */
+ us = (struct us_data*) srb->host_scribble;
+ if (dir_in)
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* are we scatter-gathering? */
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ /* loop over all the scatter gather structures and
+ * make the appropriate requests for each, until done
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
- result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+ result = us_bulk_transfer(us, pipe, sg[i].address,
+ sg[i].length);
if (result)
break;
}
}
else
- result = us_one_transfer(us, pipe, srb->request_buffer,
- us_transfer_length(srb));
+ /* no scatter-gather, just make the request */
+ result = us_bulk_transfer(us, pipe, srb->request_buffer,
+ srb->request_bufflen);
- if (result < 0)
- US_DEBUGP("us_transfer returning error %d\n", result);
- return result;
+ /* return the result in the data structure itself */
+ srb->result = result;
}
/* calculate the length of the data transfer (not the command) for any
@@ -265,6 +248,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
case MODE_SENSE:
return srb->cmnd[4];
+ case READ_CAPACITY:
+ return 8;
+
case LOG_SENSE:
case MODE_SENSE_10:
return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -274,8 +260,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
}
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ struct scatterlist *sg;
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
total += sg[i].length;
}
@@ -289,12 +276,148 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
* Protocol routines
***********************************************************************/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+ int result;
+
+ /* Fix some commands -- this is a form of mode translation
+ * ATAPI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (us->srb->cmnd[0]) {
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are ATAPI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* send the command to the transport layer */
+ result = us->transport(srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
+
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ int temp_result;
+ void* old_request_buffer;
+ int old_sg;
+
+ US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+ us->srb->cmnd[0] = REQUEST_SENSE;
+ us->srb->cmnd[1] = 0;
+ us->srb->cmnd[2] = 0;
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[4] = 18;
+ us->srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
+ us->srb->request_bufflen = 18;
+ us->srb->request_buffer = us->srb->sense_buffer;
+
+ /* FIXME: what if this command fails? */
+ temp_result = us->transport(us->srb, us);
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+ US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ us->srb->sense_buffer[2] & 0xf,
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
+
+ /* we're done here */
+ us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
+ return;
+ }
+
+ /* Fix the MODE_SENSE data if we translated the command
+ */
+ if (old_cmnd == MODE_SENSE) {
+ unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+ /* FIXME: we need to compress the entire data structure here
+ */
+ dta[0] = dta[1]; /* data len */
+ dta[1] = dta[2]; /* med type */
+ dta[2] = dta[3]; /* dev-spec prm */
+ dta[3] = dta[7]; /* block desc len */
+ printk (KERN_DEBUG USB_STORAGE
+ "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+ dta[0], dta[1], dta[2], dta[3]);
+ }
+
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+ */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+ }
+}
+
static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
{
int old_cmnd = 0;
+ int result;
/* fix some commands -- this is a form of mode translation
* UFI devices only accept 12 byte long commands
@@ -372,23 +495,31 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
} /* end switch on cmnd[0] */
/* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
+ result = us->transport(srb, us);
- /* if we have an error, we're going to do a
- * REQUEST_SENSE automatically */
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
- /* FIXME: we should only do this for device
- * errors, not system errors */
- if (us->srb->result) {
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
void* old_request_buffer;
+ int old_sg;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
@@ -398,49 +529,34 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
return;
}
- /* FIXME: if we need to send more data, or recieve data, we should
- * do it here. Then, we can do status handling here also.
- *
- * This includes MODE_SENSE from above
+ /* Fix the MODE_SENSE data here if we had to translate the command
*/
if (old_cmnd == MODE_SENSE) {
unsigned char *dta = (unsigned char *)us->srb->request_buffer;
- /* calculate the new length */
- int length = (dta[0] << 8) + dta[1] + 2;
-
- /* copy the available data length into the structure */
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length & 0xFF;
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
-
- /* FIXME: this assumes that the 2nd attempt is always
- * successful convert MODE_SENSE_10 return data format
- * to MODE_SENSE_6 format */
+ /* FIXME: we need to compress the entire data structure here
+ */
dta[0] = dta[1]; /* data len */
dta[1] = dta[2]; /* med type */
dta[2] = dta[3]; /* dev-spec prm */
@@ -450,126 +566,18 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
dta[0], dta[1], dta[2], dta[3]);
}
- /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
- * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry
- */
-
- /* FIXME: here is where we need to fix-up the return data from
- * an INQUIRY command to show ANSI SCSI rev 2
- */
-
- /* FIXME: The rest of this is bogus. usb_control_msg() will only
- * return an error if we've really honked things up. If it just
- * needs a START_STOP, then we'll get some data back via
- * REQUEST_SENSE -- either way, this belongs at a higher level
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
*/
-
-#if 0
- /* For UFI, if this is the first time we've sent this TEST_UNIT_READY
- * command, we can try again
- */
- if (!done_start && (us->subclass == US_SC_UFI)
- && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-
- /* as per spec try a start command, wait and retry */
- wait_ms(100);
-
- done_start++;
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = START_STOP;
- cmd[4] = 1; /* start */
-
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- cmd, 12, HZ*5);
- US_DEBUGP("Next usb_control_msg returns %d\n", result);
-
- /* allow another retry */
- retry++;
- continue;
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
}
-#endif
}
static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
{
- unsigned int savelen = us->srb->request_bufflen;
- unsigned int saveallocation = 0;
-
-#if 0
- /* force attention on first command */
- if (!us->attention_done) {
- if (us->srb->cmnd[0] == REQUEST_SENSE) {
- US_DEBUGP("forcing unit attention\n");
- us->attention_done = 1;
-
- if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
-
- if ((p[2] & 0x0f) != UNIT_ATTENTION) {
- p[2] = UNIT_ATTENTION;
- p[12] = 0x29; /* power on, reset or bus-reset */
- p[13] = 0;
- } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
- } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
- }
- } /* if (!us->attention_done) */
-#endif
-
- /* If the command has a variable-length payload, then we do them
- * in two steps -- first we do the minimum, then we recalculate
- * then length, and re-issue the command
- *
- * we use savelen to remember how much buffer we really have
- * we use savealloction to remember how much was really requested
- */
+ unsigned int result = 0;
- /* FIXME: remove savelen based on mods to us_transfer_length() */
- switch (us->srb->cmnd[0]) {
- case REQUEST_SENSE:
- if (us->srb->request_bufflen > 18)
- us->srb->request_bufflen = 18;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 18;
- break;
-
- case INQUIRY:
- if (us->srb->request_bufflen > 36)
- us->srb->request_bufflen = 36;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 36;
- break;
-
- case MODE_SENSE:
- if (us->srb->request_bufflen > 4)
- us->srb->request_bufflen = 4;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 4;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- if (us->srb->request_bufflen > 8)
- us->srb->request_bufflen = 8;
- else
- break;
- saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
- us->srb->cmnd[7] = 0;
- us->srb->cmnd[8] = 8;
- break;
-
- default:
- break;
- } /* end switch on cmnd[0] */
-
/* This code supports devices which do not support {READ|WRITE}_6
* Apparently, neither Windows or MacOS will use these commands,
* so some devices do not support them
@@ -631,25 +639,33 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
US_DEBUG(us_show_command(us->srb));
}
- } /* end if (us->flags & US_FL_MODE_XLATE) */
+ } /* if (us->flags & US_FL_MODE_XLATE) */
/* send the command to the transport layer */
- us->srb->result = us->transport(us->srb, us);
+ result = us->transport(us->srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
/* if we have an error, we're going to do a REQUEST_SENSE
* automatically */
- /* FIXME: we should only do this for device errors, not
- * system errors */
- if (us->srb->result) {
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
+ int old_sg;
void* old_request_buffer;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
+ /* set up the REQUEST_SENSE command and parameters */
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
@@ -659,115 +675,32 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
+ us->srb->use_sg = old_sg;
us->srb->request_buffer = old_request_buffer;
return;
}
- if (savelen != us->srb->request_bufflen) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
- unsigned int length = 0;
-
- /* set correct length and retry */
- switch (us->srb->cmnd[0]) {
-
- /* FIXME: we should try to get all the sense data */
- case REQUEST_SENSE:
- /* simply return 18 bytes */
- p[7] = 10;
- length = us->srb->request_bufflen;
- break;
-
- case INQUIRY:
- length = p[4] + 5 > savelen ? savelen : p[4] + 5;
- us->srb->cmnd[4] = length;
- break;
-
- case MODE_SENSE:
- US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
- length = p[0] + 1 > savelen ? savelen : p[0] + 1;
- us->srb->cmnd[4] = length;
- break;
-
- case LOG_SENSE:
- length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
-
- case MODE_SENSE_10:
- US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
- (p[0] << 8) + p[1]);
- length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
- } /* end switch on cmnd[0] */
-
- US_DEBUGP("Old/New length = %d/%d\n",
- savelen, length);
-
- /* issue the new command */
- /* FIXME: this assumes that the second attempt is
- * always successful */
- if (us->srb->request_bufflen != length) {
- US_DEBUGP("redoing cmd with len=%d\n", length);
- us->srb->request_bufflen = length;
- us->srb->result = us->transport(us->srb, us);
- }
-
- /* reset back to original values */
- us->srb->request_bufflen = savelen;
-
- /* fix data as necessary */
- switch (us->srb->cmnd[0]) {
- case INQUIRY:
- if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) {
- US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
- ((unsigned char*)us->srb->request_buffer)[2] |= 2;
- }
- /* FALL THROUGH */
- case REQUEST_SENSE:
- case MODE_SENSE:
- if (us->srb->use_sg == 0 && length > 0) {
- int i;
- printk(KERN_DEBUG "Data is");
- for (i = 0; i < 32 && i < length; ++i)
- printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
- if (i < length)
- printk(" ...");
- printk("\n");
- }
-
- /* FIXME: is this really necessary? */
- us->srb->cmnd[4] = saveallocation;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- /* FIXME: is this really necessary? */
- us->srb->cmnd[7] = saveallocation >> 8;
- us->srb->cmnd[8] = saveallocation;
- break;
- } /* end switch on cmnd[0] */
- } /* if good command */
+ /* fix the results of an INQUIRY */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+ ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+ }
}
/***********************************************************************
@@ -789,7 +722,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
/* was this a wanted interrupt? */
if (us->ip_wanted) {
us->ip_wanted = 0;
- wake_up(&us->ip_waitq);
+ up(&(us->ip_waitq));
} else {
US_DEBUGP("ERROR: Unwanted interrupt received!\n");
}
@@ -801,9 +734,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
return 0;
}
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
*/
static int CB_reset(struct us_data *us)
{
@@ -816,41 +747,39 @@ static int CB_reset(struct us_data *us)
cmd[0] = SEND_DIAGNOSTIC;
cmd[1] = 4;
result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, us->ifnum, cmd, sizeof(cmd), HZ*5);
/* long wait for reset */
schedule_timeout(HZ*6);
US_DEBUGP("CB_reset: clearing endpoint halt\n");
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("CB_reset done\n");
return 0;
}
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
*/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
US_DEBUGP("CBI gets a command:\n");
US_DEBUG(us_show_command(srb));
- /* FIXME: we aren't setting the ip_wanted indicator early enough, which
- * causes some commands to never complete. This hangs the driver.
- */
-
+ /* COMMAND STAGE */
/* let's send the command via the control pipe */
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- srb->cmnd, srb->cmd_len, HZ*5);
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
/* check the return code for the command */
if (result < 0) {
@@ -858,131 +787,160 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
- US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
usb_clear_halt(us->pusb_dev,
- usb_sndctrlpipe(us->pusb_dev, 0)));
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
+ /* Set up for status notification */
+ us->ip_wanted = 1;
+
+ /* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (us_transfer_length(srb)) {
- result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
- US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
-
- /* FIXME: what do the return codes from us_transfer mean? */
- if ((result < 0) &&
- (result != USB_ST_DATAUNDERRUN) &&
- (result != USB_ST_STALL)) {
- return DID_ERROR << 16;
- }
- } /* if (us_transfer_length(srb)) */
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBI data stage result is 0x%x\n", result);
+ }
- /* get status and return it */
- return pop_CB_status(srb);
+ /* STATUS STAGE */
+
+ /* go to sleep until we get this interrup */
+ /* FIXME: this should be changed to use a timeout */
+ down(&(us->ip_waitq));
+
+ /* FIXME: currently this code is unreachable, but the idea is
+ * necessary. See above comment.
+ */
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+
+ /* UFI gives us ASC and ASCQ, like a request sense */
+ /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special
+ * case handling?
+ */
+ if (us->subclass == US_SC_UFI) {
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return USB_STOR_TRANSPORT_GOOD;
+ else
+ if (us->ip_data)
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ /* otherwise, we interpret the data normally */
+ switch (us->ip_data) {
+ case 0x0001:
+ return USB_STOR_TRANSPORT_GOOD;
+ case 0x0002:
+ return USB_STOR_TRANSPORT_FAILED;
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("CBI_transport() reached end of function\n");
+ return USB_STOR_TRANSPORT_ERROR;
}
/*
- * Control/Bulk status handler
+ * Control/Bulk transport
*/
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
- int result = 0;
+ int result;
__u8 status[2];
- int retry = 5;
- US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
- switch (us->protocol) {
- case US_PR_CB:
- /* get from control */
-
- while (retry--) {
- result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
- USB_REQ_GET_STATUS, USB_DIR_IN |
- USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- 0, us->ifnum, status, sizeof(status), HZ*5);
- if (result != USB_ST_TIMEOUT)
- break;
- }
- if (result) {
- US_DEBUGP("Bad AP status request %d\n", result);
- return DID_ABORT << 16;
- }
- US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
- if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
- ( (status[0] & ~3) || status[1]))
- return (DID_OK << 16) | 2;
- else
- return USB_STOR_TRANSPORT_GOOD;
- break;
+ US_DEBUGP("CBC gets a command:\n");
+ US_DEBUG(us_show_command(srb));
- /* FIXME: this should be in a separate function */
- case US_PR_CBI:
- /* get from interrupt pipe */
+ /* COMMAND STAGE */
+ /* let's send the command via the control pipe */
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
- /* add interrupt transfer, marked for removal */
- us->ip_wanted = 1;
+ /* check the return code for the command */
+ if (result < 0) {
+ US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
- /* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
- sleep_on(&us->ip_waitq);
-
- if (us->ip_wanted) {
- US_DEBUGP("Did not get interrupt on CBI\n");
- us->ip_wanted = 0;
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+ usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
-
- US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
- /* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special
- * case handling?
- */
- if (us->subclass == US_SC_UFI) {
- if (srb->cmnd[0] == REQUEST_SENSE ||
- srb->cmnd[0] == INQUIRY)
- return USB_STOR_TRANSPORT_GOOD;
- else
- if (us->ip_data)
- return USB_STOR_TRANSPORT_FAILED;
- else
- return USB_STOR_TRANSPORT_GOOD;
- }
+ /* FIXME: we need to handle NAKs here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
- /* otherwise, we interpret the data normally */
- switch (us->ip_data) {
- case 0x0001:
- return USB_STOR_TRANSPORT_GOOD;
- case 0x0002:
- return USB_STOR_TRANSPORT_FAILED;
- default:
- return USB_STOR_TRANSPORT_ERROR;
- }
+ /* DATA STAGE */
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb)) {
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBC data stage result is 0x%x\n", result);
}
- US_DEBUGP("pop_CB_status, reached end of function\n");
+
+
+ /* STATUS STAGE */
+ /* FIXME: this is wrong */
+ result = usb_control_msg(us->pusb_dev,
+ usb_rcvctrlpipe(us->pusb_dev,0),
+ USB_REQ_GET_STATUS, USB_DIR_IN |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 0, us->ifnum, status, sizeof(status), HZ*5);
+
+ if (result < 0) {
+ US_DEBUGP("CBC Status stage returns %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+ if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+ ( (status[0] & ~3) || status[1]))
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+
+ US_DEBUGP("CB_transport() reached end of function\n");
return USB_STOR_TRANSPORT_ERROR;
}
+/* FIXME: Does this work? */
static int Bulk_reset(struct us_data *us)
{
int result;
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- US_BULK_RESET_HARD, us->ifnum,
- NULL, 0, HZ*5);
- if (result)
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ US_BULK_RESET,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+ if (result < 0)
US_DEBUGP("Bulk hard reset failed %d\n", result);
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out));
/* long wait for reset */
schedule_timeout(HZ*6);
@@ -991,8 +949,7 @@ static int Bulk_reset(struct us_data *us)
}
/*
- * The bulk only protocol handler.
- * Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
*/
static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
{
@@ -1001,7 +958,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
int result;
int pipe;
int partial;
-
+
/* set up the command wrapper */
bcb.Signature = US_BULK_CB_SIGN;
bcb.DataTransferLength = us_transfer_length(srb);
@@ -1009,14 +966,14 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
bcb.Tag = srb->serial_number;
bcb.Lun = 0;
bcb.Length = srb->cmd_len;
-
+
/* construct the pipe handle */
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+
/* copy the command payload */
memset(bcb.CDB, 0, sizeof(bcb.CDB));
memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+
/* send it to out endpoint */
US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
bcb.Signature, bcb.Tag, bcb.DataTransferLength,
@@ -1024,94 +981,83 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
US_BULK_CB_WRAP_LEN, &partial, HZ*5);
US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
}
-
+
/* if the command transfered well, then we go to the data stage */
- /* FIXME: Regardless of the status of the data stage, we go on to the
- * status stage. Note that this implies that if a command is
- * partially successful, we rely on the device reporting an error
- * the CSW. The spec says that the device may just decide to short us.
- */
if (result == 0) {
/* send/receive data payload, if there is any */
if (bcb.DataTransferLength) {
- result = us_transfer(srb, bcb.Flags);
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
- if ((result < 0) && (result != USB_ST_DATAUNDERRUN)
- && (result != USB_ST_STALL)) {
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
- return DID_ABORT << 16;
- }
-#endif
+ us_transfer(srb, bcb.Flags);
+ US_DEBUGP("Bulk data transfer result 0x%x\n",
+ srb->result);
}
}
-
+
/* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
-
+
/* construct the pipe handle */
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+
/* get CSW for device status */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
-
+
/* get the status again */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* if it fails again, we need a reset and return an error*/
if (result == -EPIPE) {
Bulk_reset(us);
- return (DID_ABORT << 16);
+ return USB_STOR_TRANSPORT_ERROR;
}
}
-
+
/* if we still have a failure at this point, we're in trouble */
if (result) {
- US_DEBUGP("Bulk status result = 0x%x\n", result);
- return DID_ABORT << 16;
+ US_DEBUGP("Bulk status result = %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* check bulk status */
US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
US_DEBUGP("Bulk logical error\n");
- return DID_ABORT << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* based on the status code, we report good or bad */
switch (bcs.Status) {
case US_BULK_STAT_OK:
- /* if there is residue, we really didn't finish the command */
- if (bcs.Residue)
- return DID_ERROR << 16;
- else
- return DID_OK << 16;
+ /* command good -- note that we could be short on data */
+ return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
- return DID_ERROR << 16;
-
+ /* command failed */
+ return USB_STOR_TRANSPORT_FAILED;
+
case US_BULK_STAT_PHASE:
+ /* phase error */
Bulk_reset(us);
- return DID_ERROR << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
- return DID_OK << 16; /* check sense required */
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
}
/***********************************************************************
@@ -1163,14 +1109,20 @@ static int us_release(struct Scsi_Host *psh)
usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
us->irq_handle = NULL;
}
- if (us->pusb_dev)
- usb_deregister(&storage_driver);
+
+ /* FIXME: release the interface claim here? */
+ // if (us->pusb_dev)
+ // usb_deregister(&storage_driver);
/* FIXME - leaves hanging host template copy */
/* (because scsi layer uses it after removal !!!) */
- while (prev->next != us)
- prev = prev->next;
- prev->next = us->next;
+ if (us_list == us)
+ us_list = us->next;
+ else {
+ while (prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ }
return 0;
}
@@ -1188,17 +1140,19 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
struct us_data *us = (struct us_data *)srb->host->hostdata[0];
US_DEBUGP("Command wakeup\n");
- if (us->srb) {
- /* busy */
- }
srb->host_scribble = (unsigned char *)us;
- us->srb = srb;
+
+ /* get exclusive access to the structures we want */
+ down(&(us->queue_exclusion));
+
+ /* enqueue the command */
+ us->queue_srb = srb;
srb->scsi_done = done;
us->action = US_ACT_COMMAND;
/* wake up the process task */
-
- wake_up_interruptible(&us->waitq);
+ up(&(us->queue_exclusion));
+ up(&(us->sleeper));
return 0;
}
@@ -1209,6 +1163,7 @@ static int us_abort( Scsi_Cmnd *srb )
return 0;
}
+/* FIXME: this doesn't do anything right now */
static int us_bus_reset( Scsi_Cmnd *srb )
{
// struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -1340,11 +1295,11 @@ static Scsi_Host_Template my_host_template = {
NULL, /* select_queue_depths */
1, /* can_queue */
-1, /* this_id */
- SG_ALL, /* sg_tablesize */
+ SG_ALL, /* sg_tablesize */
1, /* cmd_per_lun */
0, /* present */
- FALSE, /* unchecked_isa_dma */
- FALSE, /* use_clustering */
+ FALSE, /* unchecked_isa_dma */
+ TRUE, /* use_clustering */
TRUE, /* use_new_eh_code */
TRUE /* emulated */
};
@@ -1391,10 +1346,18 @@ static int usb_stor_control_thread(void * __us)
siginfo_t info;
int unsigned long signr;
- interruptible_sleep_on(&us->waitq);
+ US_DEBUGP("*** thread sleeping.\n");
+ down(&(us->sleeper));
+ down(&(us->queue_exclusion));
+ US_DEBUGP("*** thread awakened.\n");
+ /* take the command off the queue */
action = us->action;
us->action = 0;
+ us->srb = us-> queue_srb;
+
+ /* release the queue lock as fast as possible */
+ up(&(us->queue_exclusion));
/* FIXME: we need to examine placment of break; and
* scsi_done() calls */
@@ -1460,29 +1423,20 @@ static int usb_stor_control_thread(void * __us)
break;
} /* end switch on action */
-
+
+ /* FIXME: we ignore TERM and KILL... is this right? */
if (signal_pending(current)) {
/* sending SIGUSR1 makes us print out some info */
spin_lock_irq(&current->sigmask_lock);
signr = dequeue_signal(&current->blocked, &info);
spin_unlock_irq(&current->sigmask_lock);
-
- if (signr == SIGUSR2) {
- usb_stor_debug = !usb_stor_debug;
- printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
- } else {
- break; /* exit the loop on any other signal */
- }
- }
- }
+ } /* if (singal_pending(current)) */
+ } /* for (;;) */
// MOD_DEC_USE_COUNT;
printk("usb_stor_control_thread exiting\n");
- /* FIXME: this is a hack to allow for debugging */
- // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
return 0;
}
@@ -1498,7 +1452,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
unsigned int flags = 0;
GUID(guid); /* Global Unique Identifier */
struct us_data *prev;
- Scsi_Host_Template *htmplt;
int protocol = 0;
int subclass = 0;
struct usb_interface_descriptor *altsetting =
@@ -1565,14 +1518,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
return NULL;
}
memset(ss, 0, sizeof(struct us_data));
+
+ /* Initialize the mutexes only when the struct is new */
+ init_MUTEX_LOCKED(&(ss->sleeper));
+ init_MUTEX(&(ss->queue_exclusion));
}
- /* Initialize the us_data structure with some useful info */
+ /* establish the connection to the new device */
interface = altsetting;
ss->flags = flags;
ss->ifnum = ifnum;
- ss->pusb_dev = dev;
ss->attention_done = 0;
+ ss->pusb_dev = dev;
/* If the device has subclass and protocol, then use that. Otherwise,
* take data from the specific interface.
@@ -1596,7 +1553,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
case US_PR_CBI:
US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CB_transport;
+ ss->transport = CBI_transport;
ss->transport_reset = CB_reset;
break;
@@ -1620,7 +1577,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
*/
for (i = 0; i < interface->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK) {
if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in = interface->endpoint[i].bEndpointAddress &
@@ -1646,7 +1603,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
(ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
US_DEBUGP("Problems with device\n");
if (ss->host) {
- scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
kfree(ss->htmplt->name);
kfree(ss->htmplt);
}
@@ -1667,11 +1623,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
US_DEBUGP("Protocol: ");
switch (ss->subclass) {
case US_SC_RBC:
- US_DEBUGPX("Reduced Block Commands\n");
+ US_DEBUGPX("Reduced Block Commands (RBC)\n");
+ ss->proto_handler = transparent_scsi_command;
break;
case US_SC_8020:
- US_DEBUGPX("8020\n");
+ US_DEBUGPX("8020i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_QIC:
@@ -1679,7 +1637,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
case US_SC_8070:
- US_DEBUGPX("8070\n");
+ US_DEBUGPX("8070i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_SCSI:
@@ -1697,22 +1656,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
}
- /* We only handle certain protocols. Currently, these are
- *the only ones that devices use.
- */
- if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
- US_DEBUGP("Sorry, we do not support that protocol yet.\n");
- US_DEBUGP("If you have a device which uses one of the unsupported\n");
- US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-
- kfree(ss);
- return NULL;
- }
-
/* Allocate memory for the SCSI Host Template */
- if ((htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+ if ((ss->htmplt = (Scsi_Host_Template *)
+ kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n");
kfree(ss);
@@ -1720,7 +1666,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
/* Initialize the host template based on the default one */
- memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+ memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
/* Grab the next host number */
ss->host_number = my_host_number++;
@@ -1729,32 +1675,34 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
* can pass the ss pointer to the host controler thread
* in us_detect
*/
- (struct us_data *)htmplt->proc_dir = ss;
+ (struct us_data *)ss->htmplt->proc_dir = ss;
/* shuttle E-USB */
if (dev->descriptor.idVendor == 0x04e6 &&
dev->descriptor.idProduct == 0x0001) {
__u8 qstat[2];
int result;
-
- result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
1, 0xC0,
0, ss->ifnum,
qstat, 2, HZ*5);
US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_waitqueue_head(&ss->ip_waitq);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result)
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
return NULL;
-
- interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
- } else if (ss->protocol == US_PR_CBI)
- {
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ } else if (ss->protocol == US_PR_CBI) {
int result;
-
- init_waitqueue_head(&ss->ip_waitq);
+
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
/* set up the IRQ pipe and handler */
/* FIXME: This needs to get the period from the device */
@@ -1768,18 +1716,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
- /* start up our thread */
+ /* start up our thread */
{
DECLARE_MUTEX_LOCKED(sem);
- init_waitqueue_head(&ss->waitq);
-
ss->notify = &sem;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (ss->pid < 0) {
printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(htmplt);
+ kfree(ss->htmplt);
kfree(ss);
return NULL;
@@ -1790,17 +1736,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
/* now register - our detect function will be called */
- scsi_register_module(MODULE_SCSI_HA, htmplt);
+ ss->htmplt->module = &__this_module;
+ scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
/* put us in the list */
- prev = (struct us_data *)&us_list;
- while (prev->next)
- prev = prev->next;
- prev->next = ss;
+ ss->next = us_list;
+ us_list = ss;
}
- printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+ printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
return ss;
}
@@ -1814,7 +1759,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
return;
ss->pusb_dev = NULL;
- // MOD_DEC_USE_COUNT;
}
@@ -1824,8 +1768,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
int __init usb_stor_init(void)
{
- // MOD_INC_USE_COUNT;
-
if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n",
@@ -1844,6 +1786,14 @@ int __init usb_stor_init(void)
void __exit usb_stor_exit(void)
{
+ static struct us_data *ptr;
+
+ // FIXME: this needs to be put back to free _all_ the hosts
+ // for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+ // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
+ printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
+ scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+
usb_deregister(&storage_driver) ;
}
diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h
index 80a03f3c9..06e6d958b 100644
--- a/drivers/usb/usb-storage.h
+++ b/drivers/usb/usb-storage.h
@@ -9,13 +9,11 @@
#define USB_STORAGE "usb-storage: "
-extern int usb_stor_debug;
-
#ifdef CONFIG_USB_STORAGE_DEBUG
void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x) { if(usb_stor_debug) x; }
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x
#else
#define US_DEBUGP(x...)
#define US_DEBUGPX(x...)
@@ -83,15 +81,22 @@ struct bulk_cs_wrap {
#define US_BULK_RESET_HARD 0
/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD 0
+#define US_BULK_TRANSFER_SHORT 1
+#define US_BULK_TRANSFER_FAILED 2
+
+/*
* Transport return codes
*/
-#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
-#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
-#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */
+#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
+#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
/*
- * CBI style
+ * CBI accept device specific command
*/
#define US_CBI_ADSC 0
@@ -128,3 +133,4 @@ static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *seri
#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
#define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for
Win/MacOS compatibility */
+
diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h
index 73d16937a..4eedc4183 100644
--- a/drivers/usb/usb-uhci-debug.h
+++ b/drivers/usb/usb-uhci-debug.h
@@ -1,41 +1,32 @@
#ifdef DEBUG
-
static void uhci_show_qh (puhci_desc_t qh)
{
if (qh->type != QH_TYPE) {
dbg("qh has not QH_TYPE");
return;
}
- dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
+ dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
if (qh->hw.qh.head & UHCI_PTR_TERM)
- dbg("Head Terminate");
- else {
- if (qh->hw.qh.head & UHCI_PTR_QH)
- dbg("Head points to QH");
- else
- dbg("Head points to TD");
-
- dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
- }
+ dbg(" Head Terminate");
+ else
+ dbg(" Head: %s @ %08X",
+ (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.head & ~UHCI_PTR_BITS);
+
if (qh->hw.qh.element & UHCI_PTR_TERM)
- dbg("Element Terminate");
- else {
-
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Element points to QH");
- else
- dbg("Element points to TD");
- dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
- }
+ dbg(" Element Terminate");
+ else
+ dbg(" Element: %s @ %08X",
+ (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.element & ~UHCI_PTR_BITS);
}
#endif
static void uhci_show_td (puhci_desc_t td)
{
char *spid;
- warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
-
+
switch (td->hw.td.info & 0xff) {
case USB_PID_SETUP:
spid = "SETUP";
@@ -51,16 +42,16 @@ static void uhci_show_td (puhci_desc_t td)
break;
}
- warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
+ warn(" TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+ td, virt_to_bus (td),
td->hw.td.info >> 21,
((td->hw.td.info >> 19) & 1),
(td->hw.td.info >> 15) & 15,
(td->hw.td.info >> 8) & 127,
- (td->hw.td.info & 0xff),
spid,
td->hw.td.buffer);
- warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+ warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
td->hw.td.status & 0x7ff,
((td->hw.td.status >> 27) & 3),
(td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
@@ -74,50 +65,41 @@ static void uhci_show_td (puhci_desc_t td)
(td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
);
-#if 1
+
if (td->hw.td.link & UHCI_PTR_TERM)
- warn("Link Terminate");
- else {
- if (td->hw.td.link & UHCI_PTR_QH)
- warn("%s, link points to QH @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- else
- warn("%s, link points to TD @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- }
-#endif
+ warn(" TD Link Terminate");
+ else
+ warn(" Link points to %s @ %08x, %s",
+ (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+ td->hw.td.link & ~UHCI_PTR_BITS,
+ (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
}
#ifdef DEBUG
static void uhci_show_td_queue (puhci_desc_t td)
{
- dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+ //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
while (1) {
uhci_show_td (td);
if (td->hw.td.link & UHCI_PTR_TERM)
break;
- //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
- // break;
if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
else {
dbg("td points to itself!");
break;
}
-// schedule();
}
}
static void uhci_show_queue (puhci_desc_t qh)
{
+ uhci_desc_t *start_qh=qh;
+
dbg("uhci_show_queue %p:", qh);
while (1) {
uhci_show_qh (qh);
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Warning: qh->element points to qh!");
- else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+ if (!(qh->hw.qh.element & UHCI_PTR_TERM))
uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
if (qh->hw.qh.head & UHCI_PTR_TERM)
@@ -129,7 +111,12 @@ static void uhci_show_queue (puhci_desc_t qh)
dbg("qh points to itself!");
break;
}
- }
+
+ if (qh==start_qh) { // avoid loop
+ dbg("Loop detect");
+ break;
+ }
+ }
}
static void uhci_show_sc (int port, unsigned short status)
diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c
index aed79f849..85a5cd476 100644
--- a/drivers/usb/usb-uhci.c
+++ b/drivers/usb/usb-uhci.c
@@ -12,7 +12,7 @@
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
+ * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $
*/
#include <linux/config.h>
@@ -28,9 +28,9 @@
#include <linux/unistd.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/init.h>
-/* This enables debug printks */
-#define DEBUG
-#include <linux/usb.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
+#include <linux/pm.h>
+#endif
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -40,51 +40,56 @@
/* This enables more detailed sanity checks in submit_iso */
//#define ISO_SANITY_CHECK
+/* This enables debug printks */
+#define DEBUG
+
/* This enables all symbols to be exported, to ease debugging oopses */
//#define DEBUG_SYMBOLS
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
+#include <linux/usb.h>
#include "usb-uhci.h"
#include "usb-uhci-debug.h"
#undef DEBUG
#undef dbg
#define dbg(format, arg...) do {} while (0)
-
-#include <linux/pm.h>
-
+#define DEBUG_SYMBOLS
#ifdef DEBUG_SYMBOLS
#define _static
#ifndef EXPORT_SYMTAB
- #define EXPORT_SYMTAB
+ #define EXPORT_SYMTAB
#endif
#else
#define _static static
#endif
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
#ifdef DEBUG_SLAB
static kmem_cache_t *uhci_desc_kmem;
static kmem_cache_t *urb_priv_kmem;
#endif
+#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-#define USE_RECLAMATION_LOOP
-#else
-//#define USE_RECLAMATION_LOOP
-#endif
-
-// stop bandwidth reclamation after (roughly) 50ms (depends also on
-// hub polling interval)
+// stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT (HZ/20)
_static int rh_submit_urb (urb_t *urb);
_static int rh_unlink_urb (urb_t *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
static uhci_t *devs = NULL;
@@ -105,58 +110,47 @@ void clean_descs(uhci_t *s, int force)
qh = list_entry (q, uhci_desc_t, horizontal);
if ((qh->last_used!=now) || force)
delete_qh(s,qh);
+
q=qh->horizontal.prev;
}
}
/*-------------------------------------------------------------------*/
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
_static void enable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("enable_desc_loop: enter");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
- s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+ s->chain_end->hw.qh.head&=~UHCI_PTR_TERM;
+ mb();
s->loop_usage++;
((urb_priv_t*)urb->hcpriv)->use_loop=1;
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("enable_desc_loop: finished");
}
/*-------------------------------------------------------------------*/
_static void disable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("disable_desc_loop: enter\n");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
if (((urb_priv_t*)urb->hcpriv)->use_loop) {
s->loop_usage--;
- if (!s->loop_usage)
- s->chain_end->hw.qh.head=UHCI_PTR_TERM;
-
+ if (!s->loop_usage) {
+ s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+ mb();
+ }
((urb_priv_t*)urb->hcpriv)->use_loop=0;
}
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("disable_desc_loop: finished");
-
}
#endif
/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
{
- unsigned long flags=0;
struct list_head *p=&urb->urb_list;
-
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
{
int type;
type=usb_pipetype (urb->pipe);
@@ -166,15 +160,21 @@ _static void queue_urb (uhci_t *s, urb_t *urb)
}
#endif
((urb_priv_t*)urb->hcpriv)->started=jiffies;
- list_add_tail (p, &s->urb_list);
-
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ list_add (p, &s->urb_list);
}
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+ unsigned long flags=0;
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ queue_urb_unlocked(s,urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
/*-------------------------------------------------------------------*/
_static void dequeue_urb (uhci_t *s, urb_t *urb)
{
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type;
type=usb_pipetype (urb->pipe);
@@ -189,9 +189,9 @@ _static void dequeue_urb (uhci_t *s, urb_t *urb)
_static int alloc_td (uhci_desc_t ** new, int flags)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
@@ -205,6 +205,19 @@ _static int alloc_td (uhci_desc_t ** new, int flags)
return 0;
}
/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags)
+{
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+
+ mb();
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
/* insert td at last position in td-list of qh (vertical) */
_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
{
@@ -271,10 +284,9 @@ _static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
if (prev->type == TD_TYPE)
prev->hw.td.link = element->hw.td.link;
else
- prev->hw.qh.element = element->hw.td.link;
+ prev->hw.qh.element = element->hw.td.link;
}
- element->hw.td.link=UHCI_PTR_TERM;
mb ();
if (dir == 0)
@@ -302,9 +314,9 @@ _static int delete_desc (uhci_desc_t *element)
_static int alloc_qh (uhci_desc_t ** new)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
@@ -350,9 +362,10 @@ _static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
mb ();
spin_unlock_irqrestore (&s->qh_lock, flags);
-
+
return 0;
}
+
/*-------------------------------------------------------------------*/
_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
{
@@ -406,6 +419,14 @@ _static void clean_td_chain (uhci_desc_t *td)
delete_desc (td);
}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+ td->hw.td.status = status;
+ td->hw.td.info = info;
+ td->hw.td.buffer = buffer;
+}
/*-------------------------------------------------------------------*/
// Removes ALL qhs in chain (paranoia!)
_static void cleanup_skel (uhci_t *s)
@@ -441,19 +462,20 @@ _static void cleanup_skel (uhci_t *s)
qh = s->control_chain;
while ((p = qh->horizontal.next) != &qh->horizontal) {
qh1 = list_entry (p, uhci_desc_t, horizontal);
- dbg("delete_qh @ %p",qh1);
delete_qh (s, qh1);
}
- dbg("delete_qh last @ %p",qh);
+
delete_qh (s, qh);
}
else {
+ if (s->ls_control_chain)
+ delete_desc (s->ls_control_chain);
if (s->control_chain)
- kfree (s->control_chain);
+ delete_desc(s->control_chain);
if (s->bulk_chain)
- kfree (s->bulk_chain);
+ delete_desc (s->bulk_chain);
if (s->chain_end)
- kfree (s->chain_end);
+ delete_desc (s->chain_end);
}
dbg("cleanup_skel finished");
}
@@ -480,6 +502,7 @@ _static int init_skel (uhci_t *s)
if (!s->iso_td)
goto init_skel_cleanup;
+ s->ls_control_chain = NULL;
s->control_chain = NULL;
s->bulk_chain = NULL;
s->chain_end = NULL;
@@ -499,26 +522,46 @@ _static int init_skel (uhci_t *s)
if (ret)
goto init_skel_cleanup;
-
+
s->chain_end = qh;
+ ret = alloc_td (&td, 0);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt
+ insert_td (s, qh, td, 0);
+
dbg("allocating qh: bulk_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->chain_end, qh, 0);
s->bulk_chain = qh;
+
dbg("allocating qh: control_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->bulk_chain, qh, 0);
s->control_chain = qh;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ // disabled reclamation loop
+ s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+ dbg("allocating qh: ls_control_chain");
+ ret = alloc_qh (&qh);
+ if (ret)
+ goto init_skel_cleanup;
+
+ insert_qh (s, s->control_chain, qh, 0);
+ s->ls_control_chain = qh;
+
for (n = 0; n < 8; n++)
s->int_chain[n] = 0;
@@ -532,7 +575,7 @@ _static int init_skel (uhci_t *s)
goto init_skel_cleanup;
s->int_chain[n] = td;
if (n == 0) {
- s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+ s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
}
else {
s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
@@ -547,20 +590,16 @@ _static int init_skel (uhci_t *s)
dbg("framelist[%i]=%x",n,s->framelist[n]);
if ((n&127)==127)
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
- else {
- for (o = 1, m = 2; m <= 128; o++, m += m) {
- // n&(m-1) = n%m
- if ((n & (m - 1)) == ((m - 1) / 2)) {
+ else
+ for (o = 1, m = 2; m <= 128; o++, m += m)
+ if ((n & (m - 1)) == ((m - 1) / 2))
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
- }
- }
- }
}
mb();
//uhci_show_queue(s->control_chain);
dbg("init_skel exit");
- return 0; // OK
+ return 0;
init_skel_cleanup:
cleanup_skel (s);
@@ -568,14 +607,6 @@ _static int init_skel (uhci_t *s)
}
/*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
- td->hw.td.status = status;
- td->hw.td.info = info;
- td->hw.td.buffer = buffer;
-}
-
-/*-------------------------------------------------------------------*/
// LOW LEVEL STUFF
// assembles QHs und TDs for control, bulk and iso
/*-------------------------------------------------------------------*/
@@ -586,10 +617,15 @@ _static int uhci_submit_control_urb (urb_t *urb)
urb_priv_t *urb_priv = urb->hcpriv;
unsigned long destination, status;
int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
- unsigned long len, bytesrequested;
+ unsigned long len;
char *data;
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method
+ if (!maxsze) {
+ err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+ return -EINVAL;
+ }
+
dbg("uhci_submit_control start");
alloc_qh (&qh); // alloc qh for this request
@@ -615,26 +651,21 @@ _static int uhci_submit_control_urb (urb_t *urb)
insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
#if 0
- dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
- urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
- urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+ {
+ char *sp=urb->setup_packet;
+ dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+ sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+ }
//uhci_show_td(td);
#endif
- /* Build the DATA TD's */
len = urb->transfer_buffer_length;
- bytesrequested = len;
data = urb->transfer_buffer;
/* If direction is "send", change the frame from SETUP (0x2D)
to OUT (0xE1). Else change it from SETUP to IN (0x69). */
- destination &= ~UHCI_PID;
-
- if (usb_pipeout (urb->pipe))
- destination |= USB_PID_OUT;
- else
- destination |= USB_PID_IN;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
while (len > 0) {
int pktsze = len;
@@ -664,7 +695,7 @@ _static int uhci_submit_control_urb (urb_t *urb)
destination &= ~UHCI_PID;
- if (usb_pipeout (urb->pipe) || (bytesrequested == 0))
+ if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
@@ -690,32 +721,34 @@ _static int uhci_submit_control_urb (urb_t *urb)
urb->status = -EINPROGRESS;
queue_urb (s, urb); // queue before inserting in desc chain
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
//uhci_show_queue(qh);
/* Start it up... put low speed first */
if (urb->pipe & TD_CTRL_LS)
- insert_qh (s, s->control_chain, qh, 1); // insert after control chain
+ insert_qh (s, s->control_chain, qh, 0);
else
- insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
- //uhci_show_queue(qh);
+ insert_qh (s, s->bulk_chain, qh, 0);
dbg("uhci_submit_control end");
return 0;
}
/*-------------------------------------------------------------------*/
-_static int uhci_submit_bulk_urb (urb_t *urb)
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
- uhci_desc_t *qh, *td;
+ uhci_desc_t *qh, *td, *nqh, *bqh;
unsigned long destination, status;
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
-
+ urb_priv_t *upriv, *bpriv;
if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
return -EPIPE;
@@ -727,12 +760,55 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
if (!maxsze)
return -EMSGSIZE;
- /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+
+ queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+ urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
- alloc_qh (&qh); // get qh for this request
+ upriv=(urb_priv_t*)urb->hcpriv;
- if (!qh)
- return -ENOMEM;
+ if (!bulk_urb) {
+ alloc_qh (&qh); // get qh for this request
+
+ if (!qh)
+ return -ENOMEM;
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh(&nqh); // placeholder for clean unlink
+ if (!nqh) {
+ delete_desc (qh);
+ return -ENOMEM;
+ }
+ upriv->next_qh = nqh;
+ queue_dbg("new next qh %p",nqh);
+ }
+ }
+ else {
+ bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+ qh = bpriv->bottom_qh; // re-use bottom qh and next qh
+ nqh = bpriv->next_qh;
+ upriv->next_qh=nqh;
+ bpriv->next_queued_urb=urb;
+ upriv->prev_queued_urb=bulk_urb;
+ }
+
+ queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh (&bqh); // "bottom" QH,
+
+ if (!bqh) {
+ if (!bulk_urb) {
+ delete_desc(qh);
+ delete_desc(nqh);
+ }
+ return -ENOMEM;
+ }
+ bqh->hw.qh.element = UHCI_PTR_TERM;
+ bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+ upriv->bottom_qh = bqh;
+ queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+ }
+
/* The "pipe" thing contains the destination in bits 8--18. */
destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
@@ -744,7 +820,6 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
/* Build the TDs for the bulk request */
len = urb->transfer_buffer_length;
data = urb->transfer_buffer;
- dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
do { // TBD: Really allow zero-length packets?
int pktsze = len;
@@ -770,54 +845,149 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
if (!len)
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
- //dbg("insert td %p, len %i",td,pktsze);
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-
- /* Alternate Data0/1 (start with Data0) */
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
} while (len > 0);
list_add (&qh->desc_list, &urb_priv->desc_list);
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ qh->hw.qh.element&=~UHCI_PTR_TERM;
+ append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+ }
+
urb->status = -EINPROGRESS;
- queue_urb (s, urb);
+ queue_urb_unlocked (s, urb);
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
- insert_qh (s, s->chain_end, qh, 0); // insert before end marker
+ if (!bulk_urb) {
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ spin_lock (&s->td_lock); // both QHs in one go
+ insert_qh (s, s->chain_end, qh, 0); // Main QH
+ insert_qh (s, s->chain_end, nqh, 0); // Helper QH
+ spin_unlock (&s->td_lock);
+ }
+ else
+ insert_qh (s, s->chain_end, qh, 0);
+ }
+
//uhci_show_queue(s->bulk_chain);
-
- dbg("uhci_submit_bulk_urb: exit");
+ //dbg("uhci_submit_bulk_urb: exit\n");
return 0;
}
-
/*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-_static int uhci_unlink_urb (urb_t *urb)
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
{
- uhci_t *s;
- uhci_desc_t *qh;
+ struct list_head *p;
uhci_desc_t *td;
- urb_priv_t *urb_priv;
- unsigned long flags=0;
+ for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ unlink_td (s, td, 1);
+ }
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
struct list_head *p;
+ uhci_desc_t *td;
- if (!urb || !urb->dev) // you never know...
- return -EINVAL;
+ while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ list_del (p);
+ delete_desc (td);
+ }
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
- s = (uhci_t*) urb->dev->bus->hcpriv; // get pointer to uhci struct
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+ uhci_desc_t *bqh, *nqh, *prevqh;
+ int now;
+ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
- if (usb_pipedevice (urb->pipe) == s->rh.devnum)
- return rh_unlink_urb (urb);
+ now=UHCI_GET_CURRENT_FRAME(s);
- if (!urb->hcpriv) // you never know...
- return -EINVAL;
+ dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+ bqh=priv->bottom_qh;
- //dbg("unlink_urb called %p",urb);
+ if (!priv->next_queued_urb) { // no more appended bulk queues
+
+ if (mode != 2)
+ unlink_qh (s, qh);
+
+ if (priv->prev_queued_urb) {
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+ ppriv->bottom_qh = priv->bottom_qh;
+ ppriv->next_queued_urb = NULL;
+ }
+ else if (bqh) { // queue dead
+ nqh=priv->next_qh;
+
+ if (mode != 2)
+ unlink_qh(s, nqh);
+
+ if (mode) {
+ nqh->last_used = bqh->last_used = now;
+ list_add_tail (&nqh->horizontal, &s->free_desc);
+ list_add_tail (&bqh->horizontal, &s->free_desc);
+ }
+ }
+ }
+ else { // there are queued urbs following
+ urb_t *nurb;
+ unsigned long flags;
+
+ nurb=priv->next_queued_urb;
+ spin_lock_irqsave (&s->qh_lock, flags);
+
+ if (!priv->prev_queued_urb) { // top
+ if (mode !=2) {
+ prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);
+
+ list_del (&qh->horizontal);
+ list_add (&bqh->horizontal, &prevqh->horizontal);
+ }
+ }
+ else { //intermediate
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+ uhci_desc_t * bnqh;
+
+ bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+ ppriv->bottom_qh=bnqh;
+ ppriv->next_queued_urb=nurb;
+
+ if (mode!=2) {
+ prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);
+ }
+ }
+ mb();
+ spin_unlock_irqrestore (&s->qh_lock, flags);
+ ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+ }
+
+ if (mode) {
+ qh->last_used = now;
+ list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ }
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+ unsigned long flags=0;
spin_lock_irqsave (&s->urb_list_lock, flags);
@@ -833,32 +1003,22 @@ _static int uhci_unlink_urb (urb_t *urb)
switch (usb_pipetype (urb->pipe)) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
- for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
- td = list_entry (p, uhci_desc_t, desc_list);
- unlink_td (s, td, 1);
- }
- // wait at least 1 Frame
- uhci_wait_ms(1);
- while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
- td = list_entry (p, uhci_desc_t, desc_list);
- list_del (p);
- delete_desc (td);
- }
+ uhci_clean_iso_step1(s, urb_priv);
+ uhci_wait_ms(1);
+ uhci_clean_iso_step2(s, urb_priv);
break;
case PIPE_BULK:
case PIPE_CONTROL:
qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-
- unlink_qh (s, qh); // remove this qh from qh-list
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
- // wait at least 1 Frame
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ uhci_clean_transfer(s, urb, qh, 1);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
uhci_wait_ms(1);
}
#ifdef DEBUG_SLAB
- kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+ kmem_cache_free (urb_priv_kmem, urb->hcpriv);
#else
kfree (urb->hcpriv);
#endif
@@ -874,6 +1034,147 @@ _static int uhci_unlink_urb (urb_t *urb)
return 0;
}
/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+ struct list_head *q;
+ urb_t *urb;
+ struct usb_device *dev;
+ int pipe,now;
+ urb_priv_t *urb_priv;
+
+ q=s->urb_unlinked.next;
+ now=UHCI_GET_CURRENT_FRAME(s);
+
+ while (q != &s->urb_unlinked) {
+
+ urb = list_entry (q, urb_t, urb_list);
+
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+ q = urb->urb_list.next;
+
+ if (force ||
+ ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+ async_dbg("async cleanup %p",urb);
+ switch (usb_pipetype (urb->pipe)) { // process descriptors
+ case PIPE_CONTROL:
+ process_transfer (s, urb, 2);
+ break;
+ case PIPE_BULK:
+ if (!s->avoid_bulk.counter)
+ process_transfer (s, urb, 2); // don't unlink (already done)
+ else
+ continue;
+ break;
+ case PIPE_ISOCHRONOUS:
+ process_iso (s, urb, 1); // force, don't unlink
+ break;
+ case PIPE_INTERRUPT:
+ process_interrupt (s, urb);
+ break;
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ECONNRESET; // mark as asynchronously killed
+
+ pipe = urb->pipe; // completion may destroy all...
+ dev = urb->dev;
+ urb_priv = urb->hcpriv;
+
+ if (urb->complete) {
+ spin_unlock(&s->urb_list_lock);
+ urb->complete ((struct urb *) urb);
+ spin_lock(&s->urb_list_lock);
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ENOENT; // now the urb is really dead
+
+ usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+ kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+ kfree (urb_priv);
+#endif
+ switch (usb_pipetype (pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step2(s, urb_priv);
+ break;
+ }
+ list_del (&urb->urb_list);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+
+ async_dbg("unlink_urb_async called %p",urb);
+
+ if (urb->status == -EINPROGRESS) {
+ ((urb_priv_t*)urb->hcpriv)->started = ~0;
+ dequeue_urb (s, urb);
+ list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+ s->unlink_urb_done = 1;
+
+ urb->status = -ECONNABORTED; // mark urb as "waiting to be killed"
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step1 (s, urb_priv);
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+ uhci_clean_transfer (s, urb, qh, 0);
+ break;
+ }
+ ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ }
+
+ return -EINPROGRESS;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+ uhci_t *s;
+ unsigned long flags=0;
+ dbg("uhci_unlink_urb called for %p",urb);
+ if (!urb || !urb->dev) // you never know...
+ return -EINVAL;
+
+ s = (uhci_t*) urb->dev->bus->hcpriv;
+
+ if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+ return rh_unlink_urb (urb);
+
+ if (!urb->hcpriv)
+ return -EINVAL;
+
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ int ret;
+
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ ret = uhci_unlink_urb_async(s, urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ return ret;
+ }
+ else
+ return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
// In case of ASAP iso transfer, search the URB-list for already queued URBs
// for this EP and calculate the earliest start frame for the new
// URB (easy seamless URB continuation!)
@@ -886,9 +1187,9 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
unsigned long flags;
spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
+ p=s->urb_list.prev;
- for (; p != &s->urb_list; p = p->next) {
+ for (; p != &s->urb_list; p = p->prev) {
u = list_entry (p, urb_t, urb_list);
// look for pending URBs with identical pipe handle
// works only because iso doesn't toggle the data bit!
@@ -906,8 +1207,7 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
spin_unlock_irqrestore(&s->urb_list_lock, flags);
- return ret; // no previous urb found
-
+ return ret;
}
/*-------------------------------------------------------------------*/
// adjust start_frame according to scheduling constraints (ASAP etc)
@@ -940,35 +1240,7 @@ _static int iso_find_start (urb_t *urb)
info("iso_find_start: gap in seamless isochronous scheduling");
dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
now, urb->start_frame, urb->number_of_packets, urb->pipe);
-// The following code is only for debugging purposes...
-#if 0
- {
- uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
- struct list_head *p;
- urb_t *u;
- int a = -1, b = -1;
- unsigned long flags;
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
-
- for (; p != &s->urb_list; p = p->next) {
- u = list_entry (p, urb_t, urb_list);
- if (urb->dev != u->dev)
- continue;
- dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
- u->pipe, u->status, u->start_frame, u->number_of_packets);
- if (!usb_pipeisoc (u->pipe))
- continue;
- if (a == -1)
- a = u->start_frame;
- b = (u->start_frame + u->number_of_packets - 1) & 1023;
- }
- spin_unlock_irqrestore(&s->urb_list_lock, flags);
- }
-#endif
urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME!
- //return -EAGAIN; //FIXME
}
}
}
@@ -996,7 +1268,7 @@ _static int iso_find_start (urb_t *urb)
/*-------------------------------------------------------------------*/
// submits USB interrupt (ie. polling ;-)
// ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
+// if period==0, the the transfer is only done once
_static int uhci_submit_int_urb (urb_t *urb)
{
@@ -1005,12 +1277,9 @@ _static int uhci_submit_int_urb (urb_t *urb)
int nint, n, ret;
uhci_desc_t *td;
int status, destination;
- int now;
int info;
unsigned int pipe = urb->pipe;
- //dbg("SUBMIT INT");
-
if (urb->interval < 0 || urb->interval >= 256)
return -EINVAL;
@@ -1029,8 +1298,7 @@ _static int uhci_submit_int_urb (urb_t *urb)
dbg("Rounded interval to %i, chain %i", urb->interval, nint);
- now = UHCI_GET_CURRENT_FRAME (s) & 1023;
- urb->start_frame = now; // remember start frame, just in case...
+ urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case...
urb->number_of_packets = 1;
@@ -1062,13 +1330,6 @@ _static int uhci_submit_int_urb (urb_t *urb)
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-#if 0
- td = tdm[urb->number_of_packets];
- fill_td (td, TD_CTRL_IOC, 0, 0);
- insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td);
- list_add_tail (&td->desc_list, &urb_priv->desc_list);
-#endif
-
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1086,11 +1347,11 @@ _static int uhci_submit_iso_urb (urb_t *urb)
__save_flags(flags);
__cli(); // Disable IRQs to schedule all ISO-TDs in time
ret = iso_find_start (urb); // adjusts urb->start_frame for later use
-
+
if (ret)
goto err;
- tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
if (!tdm) {
ret = -ENOMEM;
@@ -1105,14 +1366,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
tdm[n] = 0;
continue;
}
- #ifdef ISO_SANITY_CHECK
+
if(urb->iso_frame_desc[n].length > maxsze) {
+#ifdef ISO_SANITY_CHECK
err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
tdm[n] = 0;
ret=-EINVAL;
goto inval;
+#endif
}
- #endif
+
ret = alloc_td (&td, UHCI_PTR_DEPTH);
inval:
if (ret) {
@@ -1120,7 +1383,7 @@ _static int uhci_submit_iso_urb (urb_t *urb)
for (i = 0; i < n; n++)
if (tdm[i])
- kfree (tdm[i]);
+ delete_desc(tdm[i]);
kfree (tdm);
goto err;
}
@@ -1165,15 +1428,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
}
/*-------------------------------------------------------------------*/
-_static int search_dev_ep (uhci_t *s, urb_t *urb)
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
{
- unsigned long flags;
struct list_head *p;
urb_t *tmp;
unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
dbg("search_dev_ep:");
- spin_lock_irqsave (&s->urb_list_lock, flags);
+
p=s->urb_list.next;
for (; p != &s->urb_list; p = p->next) {
@@ -1181,13 +1445,12 @@ _static int search_dev_ep (uhci_t *s, urb_t *urb)
dbg("urb: %p", tmp);
// we can accept this urb if it is not queued at this time
// or if non-iso transfer requests should be scheduled for the same device and pipe
- if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) ||
+ if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
(urb == tmp)) {
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
- return 1; // found another urb already queued for processing
+ return tmp; // found another urb already queued for processing
}
}
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1196,60 +1459,93 @@ _static int uhci_submit_urb (urb_t *urb)
uhci_t *s;
urb_priv_t *urb_priv;
int ret = 0;
-
+ unsigned long flags;
+ urb_t *bulk_urb=NULL;
+
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
s = (uhci_t*) urb->dev->bus->hcpriv;
//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-
+
+ if (!s->running)
+ return -ENODEV;
+
if (usb_pipedevice (urb->pipe) == s->rh.devnum)
return rh_submit_urb (urb); /* virtual root hub */
usb_inc_dev_use (urb->dev);
- if (search_dev_ep (s, urb)) {
- usb_dec_dev_use (urb->dev);
- return -ENXIO; // urb already queued
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ bulk_urb = search_dev_ep (s, urb);
+ if (bulk_urb) {
+
+ queue_dbg("found bulk urb %p\n",bulk_urb);
+
+ if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+ ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+ (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ usb_dec_dev_use (urb->dev);
+ err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+ return -ENXIO; // urb already queued
+ }
}
#ifdef DEBUG_SLAB
- urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
#else
- urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
#endif
if (!urb_priv) {
usb_dec_dev_use (urb->dev);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
return -ENOMEM;
}
urb->hcpriv = urb_priv;
INIT_LIST_HEAD (&urb_priv->desc_list);
- urb_priv->short_control_packet=0;
+ urb_priv->short_control_packet = 0;
dbg("submit_urb: scheduling %p", urb);
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- ret = uhci_submit_iso_urb (urb);
- break;
- case PIPE_INTERRUPT:
- ret = uhci_submit_int_urb (urb);
- break;
- case PIPE_CONTROL:
- //dump_urb (urb);
- ret = uhci_submit_control_urb (urb);
- break;
- case PIPE_BULK:
- ret = uhci_submit_bulk_urb (urb);
- break;
- default:
- ret = -EINVAL;
+ urb_priv->next_queued_urb = NULL;
+ urb_priv->prev_queued_urb = NULL;
+ urb_priv->bottom_qh = NULL;
+ urb_priv->next_qh = NULL;
+
+ if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+
+ if (bulk_urb) {
+ while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb) // find last queued bulk
+ bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+
+ ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+ }
+ atomic_inc (&s->avoid_bulk);
+ ret = uhci_submit_bulk_urb (urb, bulk_urb);
+ atomic_dec (&s->avoid_bulk);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ }
+ else {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ret = uhci_submit_iso_urb (urb);
+ break;
+ case PIPE_INTERRUPT:
+ ret = uhci_submit_int_urb (urb);
+ break;
+ case PIPE_CONTROL:
+ ret = uhci_submit_control_urb (urb);
+ break;
+ default:
+ ret = -EINVAL;
+ }
}
dbg("submit_urb: scheduled with ret: %d", ret);
-
if (ret != 0) {
usb_dec_dev_use (urb->dev);
#ifdef DEBUG_SLAB
@@ -1262,41 +1558,43 @@ _static int uhci_submit_urb (urb_t *urb)
return 0;
}
-#ifdef USE_RECLAMATION_LOOP
-// Removes bandwidth reclamation if URB idles too long
-void check_idling_urbs(uhci_t *s)
+
+// Checks for URB timeout and removes bandwidth reclamation
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
{
struct list_head *p,*p2;
urb_t *urb;
int type;
- //dbg("check_idling_urbs: enter i:%d",in_interrupt());
-
- spin_lock (&s->urb_list_lock);
p = s->urb_list.prev;
while (p != &s->urb_list) {
+ urb_priv_t *hcpriv;
+
p2 = p;
p = p->prev;
- urb=list_entry (p2, urb_t, urb_list);
- type=usb_pipetype (urb->pipe);
-
-#if 0
- err("URB timers: %li now: %li %i\n",
- ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
- type);
-#endif
- if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
- (((urb_priv_t*)urb->hcpriv)->use_loop) &&
- ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
- disable_desc_loop(s,urb);
+ urb = list_entry (p2, urb_t, urb_list);
+ type = usb_pipetype (urb->pipe);
+
+ hcpriv = (urb_priv_t*)urb->hcpriv;
+
+ if ( urb->timeout &&
+ ((hcpriv->started + urb->timeout) < jiffies)) {
+ urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+ async_dbg("uhci_check_timeout: timeout for %p",urb);
+ uhci_unlink_urb_async(s, urb);
+ }
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
+ (hcpriv->use_loop) &&
+ ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+ disable_desc_loop(s, urb);
+#endif
}
- spin_unlock (&s->urb_list_lock);
-
- //dbg("check_idling_urbs: finished");
}
-#endif
+
/*-------------------------------------------------------------------
Virtual Root Hub
-------------------------------------------------------------------*/
@@ -1396,7 +1694,6 @@ _static int rh_send_irq (urb_t *urb)
dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
urb->complete (urb);
-
}
return 0;
}
@@ -1411,10 +1708,6 @@ _static void rh_int_timer_do (unsigned long ptr)
urb_t *urb = (urb_t*) ptr;
uhci_t *uhci = urb->dev->bus->hcpriv;
-#ifdef USE_RECLAMATION_LOOP
- check_idling_urbs(uhci);
-#endif
-
if (uhci->rh.send) {
len = rh_send_irq (urb);
if (len > 0) {
@@ -1427,7 +1720,9 @@ _static void rh_int_timer_do (unsigned long ptr)
}
/*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
_static int rh_init_int_timer (urb_t *urb)
{
uhci_t *uhci = urb->dev->bus->hcpriv;
@@ -1436,7 +1731,7 @@ _static int rh_init_int_timer (urb_t *urb)
init_timer (&uhci->rh.rh_int_timer);
uhci->rh.rh_int_timer.function = rh_int_timer_do;
uhci->rh.rh_int_timer.data = (unsigned long) urb;
- uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+ uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
add_timer (&uhci->rh.rh_int_timer);
return 0;
@@ -1640,7 +1935,6 @@ _static int rh_submit_urb (urb_t *urb)
stat = -EPIPE;
}
-
dbg("Root-Hub stat port1: %x port2: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
@@ -1716,13 +2010,19 @@ _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_
p = s->urb_list.prev;
while (p != &s->urb_list) {
p2 = p;
- p = p->prev;
+ p = p->prev ;
urb = list_entry (p2, urb_t, urb_list);
- dbg("urb: %p", urb);
+ dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+
+ //urb->transfer_flags |=USB_ASYNC_UNLINK;
+
if (remove_all || (usb_dev == urb->dev)) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
warn("forced removing of queued URB %p due to disconnect",urb);
uhci_unlink_urb(urb);
- urb->dev = NULL; // avoid further processing of this URB
+ urb->dev = NULL; // avoid further processing of this UR
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ p = s->urb_list.prev;
}
}
spin_unlock_irqrestore (&s->urb_list_lock, flags);
@@ -1732,13 +2032,11 @@ _static int uhci_free_dev (struct usb_device *usb_dev)
{
uhci_t *s;
- dbg("uhci_free_dev");
if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
return -EINVAL;
- s=(uhci_t*) usb_dev->bus->hcpriv;
-
+ s=(uhci_t*) usb_dev->bus->hcpriv;
uhci_unlink_urbs(s, usb_dev, 0);
return 0;
@@ -1769,12 +2067,10 @@ struct usb_operations uhci_device_operations =
* have announced. This leads to a queue abort due to the short packet,
* the status stage is not executed. If this happens, the status stage
* is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
+ * mode: 0: QHs already unlinked
*/
-_static int process_transfer (uhci_t *s, urb_t *urb)
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
{
int ret = 0;
urb_priv_t *urb_priv = urb->hcpriv;
@@ -1784,25 +2080,21 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle
-
-
- // extracted and remapped info from TD
- int maxlength;
+ int maxlength; // extracted and remapped info from TD
int actual_length;
int status = 0;
- dbg("process_transfer: urb contains bulk/control request");
-
+ //dbg("process_transfer: urb contains bulk/control request");
/* if the status phase has been retriggered and the
queue is empty or the last status-TD is inactive, the retriggered
status stage is completed
*/
-#if 1
+
if (urb_priv->short_control_packet &&
((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE))))
goto transfer_finished;
-#endif
+
urb->actual_length=0;
for (; p != &qh->vertical; p = p->next) {
@@ -1810,22 +2102,20 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs
return ret;
-
- // extract transfer parameters from TD
- actual_length = (desc->hw.td.status + 1) & 0x7ff;
+
+ actual_length = (desc->hw.td.status + 1) & 0x7ff; // extract transfer parameters from TD
maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
- // see if EP is stalled
- if (status == -EPIPE) {
+ if (status == -EPIPE) { // see if EP is stalled
// set up stalled condition
usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
}
- // if any error occured stop processing of further TDs
- if (status != 0) {
+ if (status != 0) { // if any error occured stop processing of further TDs
// only set ret if status returned an error
- uhci_show_td (desc);
+ if (status != -EPIPE)
+ uhci_show_td (desc);
ret = status;
urb->error_count++;
break;
@@ -1833,11 +2123,6 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
urb->actual_length += actual_length;
-#if 0
- // if (i++==0)
- uhci_show_td (desc); // show first TD of each transfer
-#endif
-
// got less data than requested
if ( (actual_length < maxlength)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
@@ -1852,8 +2137,8 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
- uhci_show_td (desc);
- uhci_show_td (last_desc);
+ //uhci_show_td (desc);
+ //uhci_show_td (last_desc);
urb_priv->short_control_packet=1;
return 0;
}
@@ -1864,34 +2149,24 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
}
data_toggle = uhci_toggle (desc->hw.td.info);
- //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
+ queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
}
+
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
- transfer_finished:
- unlink_qh (s, qh);
- //delete_qh (s, qh);
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ transfer_finished:
+
+ uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
urb->status = status;
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
disable_desc_loop(s,urb);
#endif
- dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
+ queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
- //dbg("process_transfer: exit");
-#if 0
- if (urb->actual_length){
- char *uu;
- uu=urb->transfer_buffer;
- dbg("%x %x %x %x %x %x %x %x",
- *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
- }
-#endif
return ret;
}
@@ -1934,15 +2209,13 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
// if any error occured: ignore this td, and continue
if (status != 0) {
- uhci_show_td (desc);
+ //uhci_show_td (desc);
urb->error_count++;
goto recycle;
}
else
urb->actual_length = actual_length;
- // FIXME: SPD?
-
recycle:
if (urb->complete) {
//dbg("process_interrupt: calling completion, status %i",status);
@@ -1962,6 +2235,7 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
if (status==0) {
+ ((urb_priv_t*)urb->hcpriv)->started=jiffies;
desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
@@ -1981,8 +2255,8 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
return ret;
}
-
-_static int process_iso (uhci_t *s, urb_t *urb)
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
{
int i;
int ret = 0;
@@ -1991,16 +2265,18 @@ _static int process_iso (uhci_t *s, urb_t *urb)
uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
dbg("urb contains iso request");
- if (desc->hw.td.status & TD_CTRL_ACTIVE)
+ if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
return -EXDEV; // last TD not finished
urb->error_count = 0;
urb->actual_length = 0;
urb->status = 0;
+ dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+ urb->number_of_packets,mode,desc->hw.td.status);
for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {
desc = list_entry (p, uhci_desc_t, desc_list);
-
+
//uhci_show_td(desc);
if (desc->hw.td.status & TD_CTRL_ACTIVE) {
// means we have completed the last TD, but not the TDs before
@@ -2013,7 +2289,8 @@ _static int process_iso (uhci_t *s, urb_t *urb)
goto err;
}
- unlink_td (s, desc, 1);
+ if (!mode)
+ unlink_td (s, desc, 1);
if (urb->number_of_packets <= i) {
dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
@@ -2038,13 +2315,14 @@ _static int process_iso (uhci_t *s, urb_t *urb)
urb->error_count++;
urb->status = urb->iso_frame_desc[i].status;
}
- dbg("process_iso: len:%d status:%x",
- urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status);
+ dbg("process_iso: %i: len:%d %08x status:%x",
+ i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
delete_desc (desc);
list_del (p);
}
- dbg("process_iso: exit %i (%d)", i, ret);
+
+ dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
return ret;
}
@@ -2056,15 +2334,20 @@ _static int process_urb (uhci_t *s, struct list_head *p)
urb=list_entry (p, urb_t, urb_list);
- dbg("found queued urb: %p", urb);
+ //dbg("process_urb: found queued urb: %p", urb);
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL:
+ ret = process_transfer (s, urb, 1);
+ break;
case PIPE_BULK:
- ret = process_transfer (s, urb);
+ if (!s->avoid_bulk.counter)
+ ret = process_transfer (s, urb, 1);
+ else
+ return 0;
break;
case PIPE_ISOCHRONOUS:
- ret = process_iso (s, urb);
+ ret = process_iso (s, urb, 0);
break;
case PIPE_INTERRUPT:
ret = process_interrupt (s, urb);
@@ -2158,23 +2441,18 @@ _static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
if (status != 1) {
warn("interrupt, status %x, frame# %i", status,
UHCI_GET_CURRENT_FRAME(s));
- //uhci_show_queue(s->control_chain);
+
// remove host controller halted state
if ((status&0x20) && (s->running)) {
- // more to be done - check TDs for invalid entries
- // but TDs are only invalid if somewhere else is a (memory ?) problem
outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
}
//uhci_show_status (s);
}
- //beep(1000);
/*
- * the following is very subtle and was blatantly wrong before
* traverse the list in *reverse* direction, because new entries
* may be added at the end.
* also, because process_urb may unlink the current urb,
* we need to advance the list before
- * - Thomas Sailer
*/
spin_lock (&s->urb_list_lock);
@@ -2186,18 +2464,23 @@ restart:
p2 = p;
p = p->prev;
process_urb (s, p2);
- if(s->unlink_urb_done)
- {
+ if (s->unlink_urb_done) {
s->unlink_urb_done=0;
goto restart;
}
}
- spin_unlock (&s->urb_list_lock);
- clean_descs(s,0);
+ if ((s->frame_counter & 63) == 0)
+ uhci_check_timeouts(s);
+ clean_descs(s,0);
+ uhci_cleanup_unlink(s, 0);
+
+ spin_unlock (&s->urb_list_lock);
+
+ s->frame_counter++;
outw (status, io_addr + USBSTS);
- dbg("done");
+ //dbg("uhci_interrupt: done");
}
_static void reset_hc (uhci_t *s)
@@ -2249,15 +2532,19 @@ _static void __exit uhci_cleanup_dev(uhci_t *s)
{
struct usb_device *root_hub = s->bus->root_hub;
+ s->running = 0; // Don't allow submit_urb
+
if (root_hub)
usb_disconnect (&root_hub);
- uhci_unlink_urbs(s, 0, 1); // Forced unlink of remaining URBs
+ reset_hc (s);
+ wait_ms (1);
+ uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs
+ uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs
+
usb_deregister_bus (s->bus);
- s->running = 0;
- reset_hc (s);
release_region (s->io_addr, s->io_size);
free_irq (s->irq, s);
usb_free_bus (s->bus);
@@ -2285,6 +2572,7 @@ _static int __init uhci_start_usb (uhci_t *s)
return 0;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
{
uhci_t *s = (uhci_t*) dev->data;
@@ -2301,6 +2589,7 @@ _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
}
return 0;
}
+#endif
_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
{
@@ -2315,14 +2604,17 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
memset (s, 0, sizeof (uhci_t));
INIT_LIST_HEAD (&s->free_desc);
INIT_LIST_HEAD (&s->urb_list);
+ INIT_LIST_HEAD (&s->urb_unlinked);
spin_lock_init (&s->urb_list_lock);
spin_lock_init (&s->qh_lock);
spin_lock_init (&s->td_lock);
+ atomic_set(&s->avoid_bulk, 0);
s->irq = -1;
s->io_addr = io_addr;
s->io_size = io_size;
s->next = devs; //chain new uhci device into global list
-
+ s->frame_counter = 0;
+
bus = usb_alloc_bus (&uhci_device_operations);
if (!bus) {
kfree (s);
@@ -2389,11 +2681,11 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
//chain new uhci device into global list
devs = s;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
if (pmdev)
pmdev->data = s;
-
+#endif
return 0;
}
@@ -2407,7 +2699,7 @@ _static int __init start_uhci (struct pci_dev *dev)
unsigned int io_addr = dev->resource[i].start;
unsigned int io_size =
dev->resource[i].end - dev->resource[i].start + 1;
- if (!(dev->resource[i].flags & IORESOURCE_IO))
+ if (!(dev->resource[i].flags & 1))
continue;
#else
unsigned int io_addr = dev->base_address[i];
@@ -2422,6 +2714,10 @@ _static int __init start_uhci (struct pci_dev *dev)
break;
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+ if(dev->vendor==0x8086) {
+ info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
+ }
return alloc_uhci(dev, dev->irq, io_addr, io_size);
}
return -1;
@@ -2452,6 +2748,9 @@ int __init uhci_init (void)
#endif
info(VERSTR);
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ info("High bandwidth mode enabled");
+#endif
for (;;) {
dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
if (!dev)
@@ -2506,7 +2805,9 @@ int init_module (void)
void cleanup_module (void)
{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pm_unregister_all (handle_pm_event);
+#endif
uhci_cleanup ();
}
diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h
index e74e23bd8..93173f2c2 100644
--- a/drivers/usb/usb-uhci.h
+++ b/drivers/usb/usb-uhci.h
@@ -2,10 +2,11 @@
#define __LINUX_UHCI_H
/*
- $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
+ $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $
*/
#define MODNAME "usb-uhci"
-#define VERSTR "version v1.184 time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__
+#define UHCI_LATENCY_TIMER 0
static __inline__ void uhci_wait_ms(unsigned int ms)
{
@@ -154,9 +155,13 @@ typedef struct {
typedef struct {
struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
- int short_control_packet;
unsigned long started;
- int use_loop;
+ urb_t *next_queued_urb; // next queued urb for this EP
+ urb_t *prev_queued_urb;
+ uhci_desc_t *bottom_qh;
+ uhci_desc_t *next_qh; // next helper QH
+ char use_loop;
+ char short_control_packet;
} urb_priv_t, *purb_priv_t;
struct virt_root_hub {
@@ -186,12 +191,14 @@ typedef struct uhci {
spinlock_t urb_list_lock; // lock to keep consistency
int unlink_urb_done;
+ atomic_t avoid_bulk;
struct usb_bus *bus; // our bus
__u32 *framelist;
uhci_desc_t **iso_td;
uhci_desc_t *int_chain[8];
+ uhci_desc_t *ls_control_chain;
uhci_desc_t *control_chain;
uhci_desc_t *bulk_chain;
uhci_desc_t *chain_end;
@@ -200,6 +207,9 @@ typedef struct uhci {
spinlock_t td_lock;
struct virt_root_hub rh; //private data of the virtual root hub
int loop_usage; // URBs using bandwidth reclamation
+
+ struct list_head urb_unlinked; // list of all unlinked urbs
+ int frame_counter;
} uhci_t, *puhci_t;
diff --git a/drivers/video/aty128.h b/drivers/video/aty128.h
index 7b6342e6a..d934d0c2d 100644
--- a/drivers/video/aty128.h
+++ b/drivers/video/aty128.h
@@ -264,6 +264,8 @@
/* DAC_CNTL bit constants */
#define DAC_8BIT_EN 0x00000100
#define DAC_MASK 0xFF000000
+#define DAC_BLANKING 0x00000004
+#define DAC_RANGE_CNTL 0x00000003
/* GEN_RESET_CNTL bit constants */
#define SOFT_RESET_GUI 0x00000001
diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c
index d7d4116c0..fcc0f8c5c 100644
--- a/drivers/video/aty128fb.c
+++ b/drivers/video/aty128fb.c
@@ -48,13 +48,13 @@
#include <linux/ioport.h>
#include <asm/io.h>
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <video/macmodes.h>
#ifdef CONFIG_NVRAM
#include <linux/nvram.h>
#endif
-#include <video/macmodes.h>
#endif
#ifdef CONFIG_FB_COMPAT_XPMAC
@@ -69,7 +69,7 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
-#endif /* CONFIG_MTRR */
+#endif
#include "aty128.h"
@@ -158,13 +158,18 @@ struct aty128_meminfo {
u8 LoopLatency;
u8 DspOn;
u8 Rloop;
+ const char *name;
};
/* various memory configurations */
-const struct aty128_meminfo sdr_128 = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 };
-const struct aty128_meminfo sdr_64 = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 };
-const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 };
-const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 };
+const struct aty128_meminfo sdr_128 =
+ { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_64 =
+ { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_sgram =
+ { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+const struct aty128_meminfo ddr_sgram =
+ { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
static int currcon = 0;
@@ -176,20 +181,23 @@ static unsigned int initdepth __initdata = 8;
#ifndef MODULE
static const char *mode_option __initdata = NULL;
#endif
+#ifndef CONFIG_PPC
+static void *bios_seg = NULL;
+#endif
#ifdef CONFIG_PPC
#ifdef CONFIG_NVRAM_NOT_DEFINED
+static int default_vmode __initdata = VMODE_640_480_60;
+static int default_cmode __initdata = CMODE_8;
+#else
static int default_vmode __initdata = VMODE_NVRAM;
static int default_cmode __initdata = CMODE_NVRAM;
-#else
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_8;
#endif
#endif
#ifdef CONFIG_MTRR
static int mtrr = 1;
-#endif /* CONFIG_MTRR */
+#endif
/* PLL constants */
struct aty128_constants {
@@ -236,17 +244,13 @@ struct aty128fb_par {
struct fb_info_aty128 {
struct fb_info fb_info;
struct fb_info_aty128 *next;
- struct aty128_constants constants;
- unsigned long regbase_phys; /* mmio */
- unsigned long frame_buffer_phys; /* framebuffer memory */
+ struct aty128_constants constants; /* PLL and others */
+ unsigned long regbase_phys; /* physical mmio */
+ void *regbase; /* remapped mmio */
+ unsigned long frame_buffer_phys; /* physical fb memory */
unsigned long frame_buffer; /* remaped framebuffer */
- void *regbase;
const struct aty128_meminfo *mem; /* onboard mem info */
u32 vram_size; /* onboard video ram */
-#ifndef CONFIG_PPC
- void *bios_seg; /* video BIOS segment */
-#endif
- unsigned short card_revision; /* video card revision */
struct aty128fb_par default_par, current_par;
struct display disp;
struct display_switch dispsw; /* for cursor and font */
@@ -268,7 +272,7 @@ struct fb_info_aty128 {
#endif
#ifdef CONFIG_MTRR
struct { int vram; int vram_valid; } mtrr;
-#endif /* CONFIG_MTRR */
+#endif
};
static struct fb_info_aty128 *board_list = NULL;
@@ -321,12 +325,18 @@ static int aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info);
static void do_install_cmap(int con, struct fb_info *info);
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+ const struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
static int aty128_pci_register(struct pci_dev *pdev,
const struct aty128_chip_info *aci);
static struct fb_info_aty128 *aty128_board_list_add(struct fb_info_aty128
*board_list, struct fb_info_aty128 *new_node);
-#ifndef CONFIG_PPC
static int aty128find_ROM(struct fb_info_aty128 *info);
+#ifndef CONFIG_PPC
static void aty128_get_pllinfo(struct fb_info_aty128 *info);
#endif
static void aty128_timings(struct fb_info_aty128 *info);
@@ -513,7 +523,7 @@ aty_pll_writeupdate(const struct fb_info_aty128 *info)
/* write to the scratch register to test r/w functionality */
-static u32
+static int __init
register_test(const struct fb_info_aty128 *info)
{
u32 val, flag = 0;
@@ -720,6 +730,9 @@ aty128_set_crtc(const struct aty128_crtc *crtc,
aty_st_le32(CRTC_PITCH, crtc->pitch);
aty_st_le32(CRTC_OFFSET, crtc->offset);
aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+
+ /* Disable ATOMIC updating. Is this the right place? */
+ aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
}
@@ -818,7 +831,7 @@ aty128_var_to_crtc(const struct fb_var_screeninfo *var,
c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
- crtc->gen_cntl = 0x03000000L | c_sync | (depth << 8);
+ crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
crtc->h_total = h_total | (h_disp << 16);
crtc->v_total = v_total | (v_disp << 16);
@@ -896,7 +909,7 @@ aty128_bpp_to_var(int pix_width, struct fb_var_screeninfo *var)
break;
default:
printk(KERN_ERR "Invalid pixel width\n");
- return -1;
+ return -EINVAL;
}
return 0;
@@ -913,31 +926,31 @@ aty128_crtc_to_var(const struct aty128_crtc *crtc,
u32 pix_width;
/* fun with masking */
- h_total = crtc->h_total & 0x1ff;
- h_disp = (crtc->h_total>>16) & 0xff;
+ h_total = crtc->h_total & 0x1ff;
+ h_disp = (crtc->h_total>>16) & 0xff;
h_sync_strt = (crtc->h_sync_strt_wid>>3) & 0x1ff;
- h_sync_dly = crtc->h_sync_strt_wid & 0x7;
- h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
- h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
- v_total = crtc->v_total & 0x7ff;
- v_disp = (crtc->v_total>>16) & 0x7ff;
+ h_sync_dly = crtc->h_sync_strt_wid & 0x7;
+ h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
+ h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
+ v_total = crtc->v_total & 0x7ff;
+ v_disp = (crtc->v_total>>16) & 0x7ff;
v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
- v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
- v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
- c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
- pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
-
- xres = (h_disp+1)*8;
- yres = v_disp+1;
- left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
+ v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
+ v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
+ c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+ pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+ xres = (h_disp+1) << 3;
+ yres = v_disp+1;
+ left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
right = (h_sync_strt-h_disp)*8+h_sync_dly;
hslen = h_sync_wid*8;
upper = v_total-v_sync_strt-v_sync_wid;
lower = v_sync_strt-v_disp;
vslen = v_sync_wid;
- sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
- (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
- (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+ sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+ (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+ (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
aty128_bpp_to_var(pix_width, var);
@@ -1007,7 +1020,7 @@ aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
const struct fb_info_aty128 *info)
{
const struct aty128_constants c = info->constants;
- unsigned char post_dividers [] = {1,2,4,8,3,6,12};
+ unsigned char post_dividers[] = {1,2,4,8,3,6,12};
u32 output_freq;
u32 vclk; /* in .01 MHz */
int i;
@@ -1110,6 +1123,7 @@ aty128_ddafifo(struct aty128_ddafifo *dsp,
n <<= (11 - p);
x = round_div(n, d);
roff = x * (fifo_depth - 4);
+
if ((ron + m->Rloop) >= roff) {
printk(KERN_ERR "aty128fb: Mode out of range!\n");
return -EINVAL;
@@ -1134,7 +1148,14 @@ aty128_set_par(struct aty128fb_par *par,
struct fb_info_aty128 *info)
{
u32 config;
-
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* enable this when macmodes gets updated */
+ struct vc_mode disp_info;
+#endif
+ struct fb_var_screeninfo var;
+ int cmode, vmode;
+#endif
+
info->current_par = *par;
if (info->blitter_may_be_busy)
@@ -1174,6 +1195,46 @@ aty128_set_par(struct aty128fb_par *par,
if (par->accel_flags & FB_ACCELF_TEXT)
aty128_init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* use this when macmodes gets updated */
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ disp_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ disp_info.depth = par->crtc.bpp;
+ disp_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ disp_info.mode = 0;
+ else
+ disp_info.mode = vmode;
+ strcpy(disp_info.name, aty128fb_name);
+ disp_info.fb_address = info->frame_buffer_phys;
+ disp_info.cmap_adr_address = 0;
+ disp_info.cmap_data_address = 0;
+ disp_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(disp_info);
+ }
+#else
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ display_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ display_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ display_info.depth = par->crtc.bpp;
+ display_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ display_info.mode = 0;
+ else
+ display_info.mode = vmode;
+ strcpy(display_info.name, aty128fb_name);
+ display_info.fb_address = info->frame_buffer_phys;
+ display_info.cmap_adr_address = 0;
+ display_info.cmap_data_address = 0;
+ display_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(display_info);
+ }
+#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
}
@@ -1293,16 +1354,23 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
var->xres_virtual = var->xres;
if (var->yres > var->yres_virtual)
var->yres_virtual = var->yres;
- if (var->bits_per_pixel <= 8)
- var->bits_per_pixel = 8;
- else if (var->bits_per_pixel <= 16)
- var->bits_per_pixel = 16;
- else if (var->bits_per_pixel <= 24)
- var->bits_per_pixel = 24;
- else if (var->bits_per_pixel <= 32)
- var->bits_per_pixel = 32;
- else
- return -EINVAL;
+
+ switch (var->bits_per_pixel) {
+ case 0 ... 8:
+ var->bits_per_pixel = 8;
+ break;
+ case 9 ... 16:
+ var->bits_per_pixel = 16;
+ break;
+ case 17 ... 24:
+ var->bits_per_pixel = 24;
+ break;
+ case 25 ... 32:
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
if ((err = aty128_decode_var(var, &par, info)))
return err;
@@ -1357,26 +1425,6 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
do_install_cmap(con, &info->fb_info);
}
-#ifdef CONFIG_FB_COMPAT_XPMAC
- if (!console_fb_info || console_fb_info == &info->fb_info) {
- int vmode, cmode;
-
- display_info.width = var->xres;
- display_info.height = var->yres;
- display_info.depth = var->bits_per_pixel;
- display_info.pitch = (var->xres_virtual)*(var->bits_per_pixel)/8;
- if (mac_var_to_vmode(var, &vmode, &cmode))
- display_info.mode = 0;
- else
- display_info.mode = vmode;
- strcpy(info->fb_info.modename, aty128fb_name);
- display_info.fb_address = info->frame_buffer_phys;
- display_info.cmap_adr_address = 0;
- display_info.cmap_data_address = 0;
- display_info.disp_reg_address = info->regbase_phys;
- }
-#endif
-
return 0;
}
@@ -1435,14 +1483,14 @@ aty128_encode_fix(struct fb_fix_screeninfo *fix,
fix->smem_len = (u32)info->vram_size;
fix->mmio_len = 0x1fff;
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->type_aux = 0;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
- fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
- : FB_VISUAL_DIRECTCOLOR;
+ fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+ : FB_VISUAL_DIRECTCOLOR;
fix->ywrapstep = 0;
- fix->xpanstep = 8;
- fix->ypanstep = 1;
+ fix->xpanstep = 8;
+ fix->ypanstep = 1;
fix->accel = FB_ACCEL_ATI_RAGE128;
@@ -1540,6 +1588,7 @@ aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
disp = &fb_display[con];
else
disp = info->disp;
+
if (!disp->cmap.len) { /* no colormap allocated? */
int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
@@ -1614,7 +1663,7 @@ aty128fb_setup(char *options)
mtrr = 0;
}
#endif /* CONFIG_MTRR */
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
/* vmode and cmode depreciated */
else if (!strncmp(this_opt, "vmode:", 6)) {
unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
@@ -1660,27 +1709,22 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
const struct aty128_chip_info *aci = &aty128_pci_probe_list[0];
char *video_card = "Rage128";
- if (!register_test(info)) {
- printk(KERN_ERR "aty128fb: Can't write to video registers\n");
- return 0;
- }
-
if (!info->vram_size) /* may have already been probed */
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ /* Get the chip revision */
chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
/* put a name with the face */
while (aci->name && info->pdev->device != aci->device) { aci++; }
video_card = (char *)aci->name;
- printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
- video_card, chip_rev, info->card_revision);
+ printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
if (info->vram_size % (1024 * 1024) == 0)
- printk("%dM\n", info->vram_size / (1024*1024));
+ printk("%dM %s\n", info->vram_size / (1024*1024), info->mem->name);
else
- printk("%dk\n", info->vram_size / 1024);
+ printk("%dk %s\n", info->vram_size / 1024, info->mem->name);
/* fill in info */
strcpy(info->fb_info.modename, aty128fb_name);
@@ -1697,32 +1741,34 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
var = default_var;
#else
memset(&var, 0, sizeof(var));
+#ifdef CONFIG_FB_COMPAT_XPMAC /* CONFIG_PPC implied */
+ if (_machine == _MACH_Pmac) {
+ if (mode_option) {
+ if (!mac_find_mode(&var, &info->fb_info, mode_option, 8))
+ var = default_var;
+ } else {
#ifdef CONFIG_NVRAM
- if (default_vmode == VMODE_NVRAM) {
- default_vmode = nvram_read_byte(NV_VMODE);
- if (default_vmode <= 0 || default_vmode > VMODE_MAX)
- default_vmode = VMODE_CHOOSE;
- }
-#endif
-#ifdef CONFIG_PPC
- if (default_vmode == VMODE_CHOOSE) {
- var = default_var;
-#endif /* CONFIG_PPC */
+ if (default_vmode == VMODE_NVRAM)
+ default_vmode = nvram_read_byte(NV_VMODE);
- if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
- &defaultmode, initdepth))
- var = default_var;
-
-#ifdef CONFIG_PPC
-#ifdef CONFIG_NVRAM
- if (default_cmode == CMODE_NVRAM)
- default_cmode = nvram_read_byte(NV_CMODE);
+ if (default_cmode == CMODE_NVRAM)
+ default_cmode = nvram_read_byte(NV_CMODE);
#endif
- } else if (_machine == _MACH_Pmac)
- if (mac_vmode_to_var(default_vmode, default_cmode, &var))
- var = default_var;
+ if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+ default_vmode = VMODE_640_480_60;
-#endif
+ if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+ default_cmode = CMODE_8;
+
+ if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+ var = default_var;
+ }
+ }
+#else
+ if (fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
+ &defaultmode, initdepth) == 0)
+ var = default_var;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
#endif /* MODULE */
if (noaccel)
@@ -1743,8 +1789,8 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
info->palette[j].blue = default_blu[k];
}
- dac = aty_ld_le32(DAC_CNTL) & 15; /* preserve lower three bits */
- dac |= DAC_8BIT_EN; /* set 8 bit dac */
+ dac = aty_ld_le32(DAC_CNTL);
+ dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
dac |= DAC_MASK; /* set DAC mask */
aty_st_le32(DAC_CNTL, dac);
@@ -1845,40 +1891,51 @@ aty128_pci_register(struct pci_dev *pdev,
}
memset(info, 0, sizeof(struct fb_info_aty128));
+ /* Copy PCI device info into info->pdev */
info->pdev = pdev;
+ /* Virtualize mmio region */
info->regbase_phys = reg_addr;
info->regbase = ioremap(reg_addr, 0x1FFF);
if (!info->regbase)
goto err_out;
- pci_read_config_word(pdev, 0x08, &tmp);
- info->card_revision = tmp;
-
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+ if (!(tmp & PCI_COMMAND_MEMORY)) {
+ tmp |= PCI_COMMAND_MEMORY;
+ pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ }
+
+ /* Virtualize the framebuffer */
info->frame_buffer_phys = fb_addr;
info->frame_buffer = (unsigned long)ioremap(fb_addr, info->vram_size);
if (!info->frame_buffer)
goto err_out;
- pci_read_config_word(pdev, PCI_COMMAND, &tmp);
- if (!(tmp & PCI_COMMAND_MEMORY)) {
- tmp |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ /* If we can't test scratch registers, something is seriously wrong */
+ if (!register_test(info)) {
+ printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+ goto err_out;
}
-#if defined(CONFIG_PPC)
- aty128_timings(info);
-#else
if (!aty128find_ROM(info)) {
- printk(KERN_INFO "Rage128 BIOS not located. Guessing...\n");
+ printk(KERN_INFO "aty128fb: Rage128 BIOS not located. Guessing...\n");
aty128_timings(info);
}
- else
+#ifndef CONFIG_PPC
+ else
aty128_get_pllinfo(info);
+
+ /* free up to-be unused resources. bios_seg is mapped by
+ * aty128find_ROM() and used by aty128_get_pllinfo()
+ *
+ * TODO: make more elegant. doesn't need to be global */
+ if (bios_seg)
+ iounmap(bios_seg);
#endif
#ifdef CONFIG_MTRR
if (mtrr) {
@@ -1910,27 +1967,26 @@ unmap_out:
#endif /* CONFIG_PCI */
-#ifndef CONFIG_PPC
+/* PPC cannot read video ROM, so we fail by default */
static int __init
aty128find_ROM(struct fb_info_aty128 *info)
{
- u32 segstart;
+ int flag = 0;
+#ifndef CONFIG_PPC
+ u32 segstart;
char *rom_base;
- char *rom_base1;
char *rom;
- int stage;
- int i;
+ int stage;
+ int i;
char aty_rom_sig[] = "761295520"; /* ATI ROM Signature */
char R128_sig[] = "R128"; /* Rage128 ROM identifier */
- int flag = 0;
for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
stage = 1;
rom_base = (char *) ioremap(segstart, 0x1000);
- rom_base1 = (char *) (rom_base+1);
- if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa))
+ if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
stage = 2;
if (stage != 2) {
@@ -1967,16 +2023,18 @@ aty128find_ROM(struct fb_info_aty128 *info)
continue;
}
+ bios_seg = rom_base;
printk(KERN_INFO "aty128fb: Rage128 BIOS located at segment %4.4X\n",
- (u32)rom_base);
- info->bios_seg = rom_base;
+ (unsigned int)rom_base);
flag = 1;
break;
}
+#endif /* !CONFIG_PPC */
return (flag);
}
+#ifndef CONFIG_PPC
static void __init
aty128_get_pllinfo(struct fb_info_aty128 *info)
{
@@ -1985,16 +2043,16 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
u16 bios_header_offset, pll_info_offset;
PLL_BLOCK pll;
- bios_header = info->bios_seg + 0x48L;
- header_ptr = bios_header;
+ bios_header = bios_seg + 0x48L;
+ header_ptr = bios_header;
bios_header_offset = readw(header_ptr);
- bios_header = info->bios_seg + bios_header_offset;
+ bios_header = bios_seg + bios_header_offset;
bios_header += 0x30;
header_ptr = bios_header;
pll_info_offset = readw(header_ptr);
- header_ptr = info->bios_seg + pll_info_offset;
+ header_ptr = bios_seg + pll_info_offset;
memcpy_fromio(&pll, header_ptr, 50);
@@ -2032,10 +2090,6 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
info->mem = &sdr_sgram;
}
- /* free up to-be unused resources */
- if (info->bios_seg)
- iounmap(info->bios_seg);
-
return;
}
#endif /* ! CONFIG_PPC */
@@ -2056,7 +2110,7 @@ aty128_timings(struct fb_info_aty128 *info)
info->constants.ppll_max = 25000; /* 23000 on some cards? */
#if 1
- /* XXX TODO. Calculuate properly. Fix OF's pll ideas. */
+ /* XXX TODO. Calculuate properly. */
if (!info->constants.ref_divider)
info->constants.ref_divider = 0x3b;
aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider);
@@ -2473,10 +2527,9 @@ static struct display_switch fbcon_aty128_32 = {
};
#endif
-#if defined(MODULE)
-MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>, Anthony Tong "
- "<atong@uiuc.edu>");
-MODULE_DESCRIPTION("FBDev driver for ATI Rage128 cards");
+#ifdef MODULE
+MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
int __init
init_module(void)
diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c
index f7fe19965..b2efe721f 100644
--- a/drivers/video/atyfb.c
+++ b/drivers/video/atyfb.c
@@ -1,4 +1,4 @@
-/* $Id: atyfb.c,v 1.140 2000/02/25 05:46:27 davem Exp $
+/* $Id: atyfb.c,v 1.141 2000/03/12 03:53:16 davem Exp $
* linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
*
* Copyright (C) 1997-1998 Geert Uytterhoeven
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 0a227193b..b51014eea 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -32,6 +32,7 @@
#include <video/fbcon-cfb24.h>
#define MMIO_SIZE 0x000c0000
+/*#define CFB16_IS_CFB15*/
static char *CyberRegs;
@@ -275,18 +276,8 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
#endif
#ifdef FBCON_HAS_CFB16
- case 15:
- if (regno < 32) {
- cyber2000_outb(regno << 3, 0x3c8);
- cyber2000_outb(red, 0x3c9);
- cyber2000_outb(green, 0x3c9);
- cyber2000_outb(blue, 0x3c9);
- }
- if (regno < 16)
- current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
- break;
-
case 16:
+#ifndef CFB16_IS_CFB15
if (regno < 64) {
/* write green */
cyber2000_outb(regno << 2, 0x3c8);
@@ -308,6 +299,19 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
break;
#endif
+ case 15:
+ if (regno < 32) {
+ cyber2000_outb(regno << 3, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+ }
+ if (regno < 16)
+ current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+ break;
+
+#endif
+
#ifdef FBCON_HAS_CFB24
case 24:
cyber2000_outb(regno, 0x3c8);
@@ -345,6 +349,7 @@ struct par_info {
* Other
*/
unsigned int visual;
+ unsigned char palette_ctrl;
};
static const char crtc_idx[] = {
@@ -418,7 +423,7 @@ static void cyber2000fb_set_timing(struct par_info *hw)
cyber2000_outb(0x56, 0x3ce);
i = cyber2000_inb(0x3cf);
cyber2000_outb(i | 4, 0x3cf);
- cyber2000_outb(0x04, 0x3c6);
+ cyber2000_outb(hw->palette_ctrl, 0x3c6);
cyber2000_outb(i, 0x3cf);
cyber2000_outb(0x20, 0x3c0);
@@ -776,37 +781,76 @@ cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, struct par_info *
int err;
hw->width = var->xres_virtual;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+
switch (var->bits_per_pixel) {
#ifdef FBCON_HAS_CFB8
case 8: /* PSEUDOCOLOUR, 256 */
- hw->visual = FB_VISUAL_PSEUDOCOLOR;
- hw->pixformat = PIXFORMAT_8BPP;
- hw->visualid = VISUALID_256;
- hw->pitch = hw->width >> 3;
+ var->bits_per_pixel = 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;
+ hw->visual = FB_VISUAL_PSEUDOCOLOR;
+ hw->pixformat = PIXFORMAT_8BPP;
+ hw->visualid = VISUALID_256;
+ hw->pitch = hw->width >> 3;
+ hw->palette_ctrl = 0x04;
break;
#endif
#ifdef FBCON_HAS_CFB16
+ case 16:/* DIRECTCOLOUR, 64k */
+#ifndef CFB16_IS_CFB15
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ hw->visual = FB_VISUAL_DIRECTCOLOR;
+ hw->pixformat = PIXFORMAT_16BPP;
+ hw->visualid = VISUALID_64K;
+ hw->pitch = hw->width >> 2;
+ hw->palette_ctrl = 0x14;
+ break;
+#endif
case 15:/* DIRECTCOLOUR, 32k */
- hw->visual = FB_VISUAL_DIRECTCOLOR;
- hw->pixformat = PIXFORMAT_16BPP;
- hw->visualid = VISUALID_32K;
- hw->pitch = hw->width >> 2;
+ var->bits_per_pixel = 15;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ hw->visual = FB_VISUAL_DIRECTCOLOR;
+ hw->pixformat = PIXFORMAT_16BPP;
+ hw->visualid = VISUALID_32K;
+ hw->pitch = hw->width >> 2;
+ hw->palette_ctrl = 0x14;
break;
- case 16:/* DIRECTCOLOUR, 64k */
- hw->visual = FB_VISUAL_DIRECTCOLOR;
- hw->pixformat = PIXFORMAT_16BPP;
- hw->visualid = VISUALID_64K;
- hw->pitch = hw->width >> 2;
- break;
#endif
#ifdef FBCON_HAS_CFB24
case 24:/* TRUECOLOUR, 16m */
- hw->visual = FB_VISUAL_TRUECOLOR;
- hw->pixformat = PIXFORMAT_24BPP;
- hw->visualid = VISUALID_16M;
- hw->width *= 3;
- hw->pitch = hw->width >> 3;
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ hw->visual = FB_VISUAL_TRUECOLOR;
+ hw->pixformat = PIXFORMAT_24BPP;
+ hw->visualid = VISUALID_16M;
+ hw->width *= 3;
+ hw->pitch = hw->width >> 3;
+ hw->palette_ctrl = 0x14;
break;
#endif
default:
diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c
index 8da8117c9..aedbc8b17 100644
--- a/drivers/video/fbcon.c
+++ b/drivers/video/fbcon.c
@@ -111,10 +111,6 @@
#define LOGO_W 80
#define LOGO_LINE (LOGO_W/8)
-static int first_fb_vc = 0;
-static int last_fb_vc = MAX_NR_CONSOLES-1;
-static int fbcon_is_default = 1;
-
struct display fb_display[MAX_NR_CONSOLES];
char con2fb_map[MAX_NR_CONSOLES];
static int logo_lines;
@@ -132,8 +128,6 @@ static int softback_lines;
#define FNTSUM(fd) (((int *)(fd))[-4])
#define FONT_EXTRA_WORDS 4
-static char fontname[40] __initdata = { 0 }; /* default font name */
-
#define CM_SOFTBACK (8)
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
@@ -203,8 +197,7 @@ static int fbcon_scrolldelta(struct vc_data *conp, int lines);
* Internal routines
*/
-static int __init fbcon_setup(char *options);
-static void fbcon_set_disp(int con, int init, int logo);
+static void fbcon_setup(int con, int init, int logo);
static __inline__ int real_y(struct display *p, int ypos);
static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
static __inline__ void updatescrollmode(struct display *p);
@@ -300,8 +293,6 @@ void set_con2fb_map(int unit, int newidx)
newfb = registered_fb[newidx];
if (newfb->fbops->fb_open(newfb,0))
return;
- newfb->count++;
- oldfb->count--;
oldfb->fbops->fb_release(oldfb,0);
conp = fb_display[unit].conp;
fontdata = fb_display[unit].fontdata;
@@ -332,57 +323,6 @@ void set_con2fb_map(int unit, int newidx)
}
}
-static int __init fbcon_setup(char *options)
-{
- int i, j;
-
- if (!options || !*options)
- return 0;
-
- if (!strncmp(options, "font:", 5))
- strcpy(fontname, options+5);
-
- if (!strncmp(options, "scrollback:", 11)) {
- options += 11;
- if (*options) {
- fbcon_softback_size = simple_strtoul(options, &options, 0);
- if (*options == 'k' || *options == 'K') {
- fbcon_softback_size *= 1024;
- options++;
- }
- if (*options != ',')
- return 0;
- options++;
- } else
- return 0;
- }
-
- if (!strncmp(options, "map:", 4)) {
- options += 4;
- if (*options)
- for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
- if (!options[j])
- j = 0;
- con2fb_map[i] = (options[j++]-'0') % FB_MAX;
- }
- return 0;
- }
-
- if (!strncmp(options, "vc:", 3)) {
- options += 3;
- if (*options)
- first_fb_vc = simple_strtoul(options, &options, 10) - 1;
- if (first_fb_vc < 0)
- first_fb_vc = 0;
- if (*options++ == '-')
- last_fb_vc = simple_strtoul(options, &options, 10) - 1;
- fbcon_is_default = 0;
- }
- return 0;
-}
-
-__setup("fbcon=", fbcon_setup);
-
/*
* Low Level Operations
*/
@@ -479,23 +419,15 @@ static const char *fbcon_startup(void)
return display_desc;
}
+
static void fbcon_init(struct vc_data *conp, int init)
{
- int j, unit = conp->vc_num;
+ 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]];
- /*
- * We assume initial frame buffer devices can be opened this
- * many times
- */
- for (j = 0; j < (last_fb_vc - first_fb_vc + 1); j++) {
- info->fbops->fb_open(info,0);
- info->count++;
- }
-
info->changevar = &fbcon_changevar;
fb_display[unit] = *(info->disp); /* copy from default */
DPRINTK("mode: %s\n",info->modename);
@@ -511,8 +443,8 @@ static void fbcon_init(struct vc_data *conp, int init)
fb_display[unit].cmap.green = 0;
fb_display[unit].cmap.blue = 0;
fb_display[unit].cmap.transp = 0;
- fbcon_set_disp(unit, init, !init);
- /* Must be done after fbcon_set_disp to prevent excess updates */
+ fbcon_setup(unit, init, !init);
+ /* Must be done after fbcon_setup to prevent excess updates */
conp->vc_display_fg = &info->display_fg;
if (!info->display_fg)
info->display_fg = conp;
@@ -526,7 +458,6 @@ static void fbcon_deinit(struct vc_data *conp)
fbcon_free_font(p);
p->dispsw = &fbcon_dummy;
- p->fb_info->count = 0;
p->conp = 0;
}
@@ -534,7 +465,7 @@ static void fbcon_deinit(struct vc_data *conp)
static int fbcon_changevar(int con)
{
if (fb_display[con].conp)
- fbcon_set_disp(con, 0, 0);
+ fbcon_setup(con, 0, 0);
return 0;
}
@@ -573,7 +504,7 @@ static void fbcon_font_widths(struct display *p)
#define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
-static void fbcon_set_disp(int con, int init, int logo)
+static void fbcon_setup(int con, int init, int logo)
{
struct display *p = &fb_display[con];
struct vc_data *conp = p->conp;
@@ -637,8 +568,9 @@ static void fbcon_set_disp(int con, int init, int logo)
}
if (!p->fontdata) {
- if (!fontname[0] || !(font = fbcon_find_font(fontname)))
- font = fbcon_get_default_font(p->var.xres, p->var.yres);
+ if (!p->fb_info->fontname[0] ||
+ !(font = fbcon_find_font(p->fb_info->fontname)))
+ font = fbcon_get_default_font(p->var.xres, p->var.yres);
p->_fontwidth = font->width;
p->_fontheight = font->height;
p->fontdata = font->data;
@@ -654,7 +586,7 @@ static void fbcon_set_disp(int con, int init, int logo)
#endif
{
/* ++Geert: changed from panic() to `correct and continue' */
- printk(KERN_ERR "fbcon_set_disp: No support for fontwidth %d\n", fontwidth(p));
+ printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p));
p->dispsw = &fbcon_dummy;
}
}
@@ -737,7 +669,7 @@ static void fbcon_set_disp(int con, int init, int logo)
}
if (p->dispsw == &fbcon_dummy)
- printk(KERN_WARNING "fbcon_set_disp: type %d (aux %d, depth %d) not "
+ printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
"supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
p->dispsw->setup(p);
@@ -2462,13 +2394,6 @@ struct consw fb_con = {
con_getxy: fbcon_getxy,
};
-void __init fbconsole_init(void)
-{
- if (!num_registered_fb || (first_fb_vc > last_fb_vc))
- return;
-
- take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
-}
/*
* Dummy Low Level Operations
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 1f5492d4f..a63b4fd2c 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -239,8 +239,14 @@ extern const char *global_mode_option;
static initcall_t pref_init_funcs[FB_MAX];
static int num_pref_init_funcs __initdata = 0;
+
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb = 0;
+extern int fbcon_softback_size;
+
+static int first_fb_vc = 0;
+static int last_fb_vc = MAX_NR_CONSOLES-1;
+static int fbcon_is_default = 1;
static int fbmem_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *private)
@@ -418,10 +424,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
+ unsigned long off;
+#if !defined(__sparc__) || defined(__sparc_v9__)
struct fb_fix_screeninfo fix;
struct fb_var_screeninfo var;
- unsigned long start, off;
+ unsigned long start;
u32 len;
+#endif
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
@@ -520,7 +529,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
#endif /* !sparc32 */
}
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
int GET_FB_IDX(kdev_t rdev)
{
int fbidx = MINOR(rdev);
@@ -550,8 +559,6 @@ fb_open(struct inode *inode, struct file *file)
#endif /* CONFIG_KMOD */
if (!(info = registered_fb[fbidx]))
return -ENODEV;
- if (info->flags & FBINFO_FLAG_OPEN) return -EBUSY;
- info->flags |= FBINFO_FLAG_OPEN;
return info->fbops->fb_open(info,1);
}
@@ -562,7 +569,6 @@ fb_release(struct inode *inode, struct file *file)
struct fb_info *info = registered_fb[fbidx];
info->fbops->fb_release(info,1);
- info->flags &= ~FBINFO_FLAG_OPEN;
return 0;
}
@@ -580,8 +586,10 @@ static devfs_handle_t devfs_handle = NULL;
int
register_framebuffer(struct fb_info *fb_info)
{
+ int i, j;
char name_buf[8];
- int i;
+ static int fb_ever_opened[FB_MAX];
+ static int first = 1;
if (num_registered_fb == FB_MAX)
return -ENXIO;
@@ -590,9 +598,22 @@ register_framebuffer(struct fb_info *fb_info)
if (!registered_fb[i])
break;
fb_info->node = MKDEV(FB_MAJOR, i);
- fb_info->flags &= ~FBINFO_FLAG_OPEN;
- fb_info->count = 0;
registered_fb[i] = fb_info;
+ if (!fb_ever_opened[i]) {
+ /*
+ * We assume initial frame buffer devices can be opened this
+ * many times
+ */
+ for (j = 0; j < MAX_NR_CONSOLES; j++)
+ if (con2fb_map[j] == i)
+ fb_info->fbops->fb_open(fb_info,0);
+ fb_ever_opened[i] = 1;
+ }
+
+ if (first) {
+ first = 0;
+ take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
+ }
sprintf (name_buf, "%d", i);
fb_info->devfs_handle =
devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
@@ -605,12 +626,12 @@ register_framebuffer(struct fb_info *fb_info)
int
unregister_framebuffer(struct fb_info *fb_info)
{
- int i;
+ int i, j;
i = GET_FB_IDX(fb_info->node);
-
- if (fb_info->count || (fb_info->flags & FBINFO_FLAG_OPEN))
- return -EBUSY;
+ for (j = 0; j < MAX_NR_CONSOLES; j++)
+ if (con2fb_map[j] == i)
+ return -EBUSY;
if (!registered_fb[i])
return -EINVAL;
devfs_unregister (fb_info->devfs_handle);
@@ -654,6 +675,43 @@ int __init video_setup(char *options)
if (!options || !*options)
return 0;
+
+ if (!strncmp(options, "scrollback:", 11)) {
+ options += 11;
+ if (*options) {
+ fbcon_softback_size = simple_strtoul(options, &options, 0);
+ if (*options == 'k' || *options == 'K') {
+ fbcon_softback_size *= 1024;
+ options++;
+ }
+ if (*options != ',')
+ return 0;
+ options++;
+ } else
+ return 0;
+ }
+
+ if (!strncmp(options, "map:", 4)) {
+ options += 4;
+ if (*options)
+ for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!options[j])
+ j = 0;
+ con2fb_map[i] = (options[j++]-'0') % FB_MAX;
+ }
+ return 0;
+ }
+
+ if (!strncmp(options, "vc:", 3)) {
+ options += 3;
+ if (*options)
+ first_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ if (first_fb_vc < 0)
+ first_fb_vc = 0;
+ if (*options++ == '-')
+ last_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ fbcon_is_default = 0;
+ }
if (num_pref_init_funcs == FB_MAX)
return 0;
@@ -695,6 +753,6 @@ EXPORT_SYMBOL(register_framebuffer);
EXPORT_SYMBOL(unregister_framebuffer);
EXPORT_SYMBOL(registered_fb);
EXPORT_SYMBOL(num_registered_fb);
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
EXPORT_SYMBOL(GET_FB_IDX);
#endif
diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c
index bffa6b02e..dead9038b 100644
--- a/drivers/video/hgafb.c
+++ b/drivers/video/hgafb.c
@@ -7,6 +7,8 @@
*
* History:
*
+ * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc
+ * minor fixes
* - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for
* HGA-only systems
* - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure
@@ -27,6 +29,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
@@ -53,7 +56,7 @@
#define DPRINTK(args...)
#endif
-#if 1
+#if 0
#define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; }
#else
#define CHKINFO(ret)
@@ -97,6 +100,10 @@ static char *hga_type_name;
#define HGA_GFX_MODE_EN 0x01
#define HGA_GFX_PAGE_EN 0x02
+/* Global locks */
+
+spinlock_t hga_reg_lock = SPIN_LOCK_UNLOCKED;
+
/* Framebuffer driver structures */
static struct fb_var_screeninfo hga_default_var = {
@@ -158,55 +165,46 @@ static int nologo = 0;
static void write_hga_b(unsigned int val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT);
outb_p(val, HGA_VALUE_PORT);
-
- restore_flags(flags);
}
static void write_hga_w(unsigned int val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT); outb_p(val >> 8, HGA_VALUE_PORT);
outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT);
-
- restore_flags(flags);
}
static int test_hga_b(unsigned char val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT);
outb (val, HGA_VALUE_PORT);
-
udelay(20); val = (inb_p(HGA_VALUE_PORT) == val);
-
- restore_flags(flags);
-
return val;
}
static void hga_clear_screen(void)
{
+ unsigned char fillchar = 0xbf; /* magic */
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
if (hga_mode == HGA_TXT)
- memset((char *)hga_vram_base, ' ', hga_vram_len);
+ fillchar = ' ';
else if (hga_mode == HGA_GFX)
- memset((char *)hga_vram_base, 0, hga_vram_len);
+ fillchar = 0x00;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
+ if (fillchar != 0xbf)
+ memset((char *)hga_vram_base, fillchar, hga_vram_len);
}
+#ifdef MODULE
static void hga_txt_mode(void)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT);
outb_p(0x00, HGA_GFX_PORT);
outb_p(0x00, HGA_STATUS_PORT);
@@ -230,10 +228,15 @@ static void hga_txt_mode(void)
write_hga_w(0x0000, 0x0e); /* cursor location */
hga_mode = HGA_TXT;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
}
+#endif /* MODULE */
static void hga_gfx_mode(void)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
outb_p(0x00, HGA_STATUS_PORT);
outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT);
outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
@@ -257,6 +260,7 @@ static void hga_gfx_mode(void)
write_hga_w(0x0000, 0x0e); /* cursor location */
hga_mode = HGA_GFX;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
}
#ifdef MODULE
@@ -274,12 +278,29 @@ static void hga_show_logo(void)
static void hga_pan(unsigned int xoffset, unsigned int yoffset)
{
unsigned int base;
+ unsigned long flags;
+
base = (yoffset / 8) * 90 + xoffset;
+ spin_lock_irqsave(&hga_reg_lock, flags);
write_hga_w(base, 0x0c); /* start address */
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
DPRINTK("hga_pan: base:%d\n", base);
}
-static int hga_card_detect(void)
+static void hga_blank(int blank_mode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
+ if (blank_mode) {
+ outb_p(0x00, HGA_MODE_PORT); /* disable video */
+ } else {
+ outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
+ }
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
+}
+
+static int __init hga_card_detect(void)
{
int count=0;
u16 *p, p_save;
@@ -288,11 +309,6 @@ static int hga_card_detect(void)
hga_vram_base = VGA_MAP_MEM(0xb0000);
hga_vram_len = 0x08000;
- if (!request_mem_region(hga_vram_base, hga_vram_len, "hgafb")) {
- printk(KERN_ERR "hgafb: cannot reserve video memory at 0x%lX\n",
- hga_vram_base);
- return 0;
- }
if (request_region(0x3b0, 12, "hgafb"))
release_io_ports = 1;
if (request_region(0x3bf, 1, "hgafb"))
@@ -598,11 +614,7 @@ static void hgafbcon_blank(int blank_mode, struct fb_info *info)
CHKINFO( );
DPRINTK("hga_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
- if (blank_mode) {
- outb_p(0x00, HGA_MODE_PORT); /* disable video */
- } else {
- outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
- }
+ hga_blank(blank_mode);
}
@@ -642,7 +654,12 @@ int __init hgafb_init(void)
disp.line_length = hga_fix.line_length;
disp.can_soft_blank = 1;
disp.inverse = 0;
+#ifdef FBCON_HAS_HGA
disp.dispsw = &fbcon_hga;
+#else
+#warning HGAFB will not work as a console!
+ disp.dispsw = &fbcon_dummy;
+#endif
disp.dispsw_data = NULL;
disp.scrollmode = SCROLL_YREDRAW;
@@ -723,7 +740,6 @@ static void hgafb_cleanup(struct fb_info *info)
unregister_framebuffer(info);
if (release_io_ports) release_region(0x3b0, 12);
if (release_io_port) release_region(0x3bf, 1);
- release_mem_region(hga_vram_base, hga_vram_len);
}
#endif /* MODULE */
diff --git a/fs/Config.in b/fs/Config.in
index ff031ab44..e9eb8099c 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -9,7 +9,10 @@ tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
-dep_tristate 'ADFS file system support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+dep_tristate 'ADFS file system support' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_ADFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW
+fi
tristate 'Amiga FFS file system support' CONFIG_AFFS_FS
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
index 794097871..c9226513e 100644
--- a/fs/adfs/dir.c
+++ b/fs/adfs/dir.c
@@ -1,10 +1,11 @@
/*
* linux/fs/adfs/dir.c
*
- * Copyright (C) 1999 Russell King
+ * Copyright (C) 1999-2000 Russell King
*
* Common directory handling for ADFS
*/
+#include <linux/config.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -33,13 +34,16 @@ adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
struct object_info obj;
struct adfs_dir dir;
- int ret;
+ int ret = 0;
+
+ if (filp->f_pos >> 32)
+ goto out;
ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
if (ret)
goto out;
- switch (filp->f_pos) {
+ switch ((unsigned long)filp->f_pos) {
case 0:
if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
goto free_out;
@@ -79,13 +83,14 @@ out:
int
adfs_dir_update(struct super_block *sb, struct object_info *obj)
{
+ int ret = -EINVAL;
+#ifdef CONFIG_ADFS_FS_RW
struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
struct adfs_dir dir;
- int ret = -EINVAL;
printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
obj->file_id, obj->parent_id);
-#if 0
+
if (!ops->update) {
ret = -EINVAL;
goto out;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 5f2387bde..46c11a8e2 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -672,24 +672,17 @@ affs_statfs(struct super_block *sb, struct statfs *buf)
static DECLARE_FSTYPE_DEV(affs_fs_type, "affs", affs_read_super);
-int __init init_affs_fs(void)
+static int __init init_affs_fs(void)
{
return register_filesystem(&affs_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int
-init_module(void)
-{
- return register_filesystem(&affs_fs_type);
-}
-
-void
-cleanup_module(void)
+static void __exit exit_affs_fs(void)
{
unregister_filesystem(&affs_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_affs_fs)
+module_exit(exit_affs_fs)
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index 2c38ccafe..6f3765399 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -322,16 +322,15 @@ out:
static DECLARE_FSTYPE_DEV( bfs_fs_type, "bfs", bfs_read_super);
-#ifdef MODULE
-#define init_bfs_fs init_module
-
-void cleanup_module(void)
+static int __init init_bfs_fs(void)
{
- unregister_filesystem(&bfs_fs_type);
+ return register_filesystem(&bfs_fs_type);
}
-#endif
-int __init init_bfs_fs(void)
+static void __exit exit_bfs_fs(void)
{
- return register_filesystem(&bfs_fs_type);
+ unregister_filesystem(&bfs_fs_type);
}
+
+module_init(init_bfs_fs)
+module_exit(exit_bfs_fs)
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index 41138e3fd..5aa2f1b35 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -78,8 +78,7 @@ if (file->f_op->llseek) { \
* dumping of the process results in another error..
*/
-static inline int
-do_aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
+static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
{
mm_segment_t fs;
int has_dumped = 0;
@@ -183,17 +182,6 @@ end_coredump:
return has_dumped;
}
-static int
-aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_aout_core_dump(signr, regs, file);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/*
* create_aout_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
@@ -261,7 +249,7 @@ static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
* libraries. There is no binary dependent code anywhere else.
*/
-static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct exec ex;
struct file * file;
@@ -398,14 +386,11 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
}
}
beyond_if:
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
if (current->binfmt && current->binfmt->module)
__MOD_DEC_USE_COUNT(current->binfmt->module);
current->exec_domain = lookup_exec_domain(current->personality);
current->binfmt = &aout_format;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
if (current->binfmt && current->binfmt->module)
__MOD_INC_USE_COUNT(current->binfmt->module);
@@ -429,20 +414,7 @@ beyond_if:
return 0;
}
-
-static int
-load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_aout_binary(bprm, regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
-static inline int
-do_load_aout_library(int fd)
+static int load_aout_library(int fd)
{
struct file * file;
struct inode * inode;
@@ -529,18 +501,6 @@ out:
return retval;
}
-static int
-load_aout_library(int fd)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_aout_library(fd);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
-
static int __init init_aout_binfmt(void)
{
return register_binfmt(&aout_format);
@@ -555,4 +515,3 @@ EXPORT_NO_SYMBOLS;
module_init(init_aout_binfmt);
module_exit(exit_aout_binfmt);
-
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 96190fed2..877d9e6d7 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -35,8 +35,6 @@
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
-#include <linux/config.h>
-
#define DLINFO_ITEMS 13
#include <linux/elf.h>
@@ -383,8 +381,7 @@ out:
#define INTERPRETER_ELF 2
-static inline int
-do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct file * file;
struct dentry *interpreter_dentry = NULL; /* to shut gcc up */
@@ -703,14 +700,11 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (interpreter_type != INTERPRETER_AOUT)
sys_close(elf_exec_fileno);
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
if (current->binfmt && current->binfmt->module)
__MOD_DEC_USE_COUNT(current->binfmt->module);
current->exec_domain = lookup_exec_domain(current->personality);
current->binfmt = &elf_format;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
if (current->binfmt && current->binfmt->module)
__MOD_INC_USE_COUNT(current->binfmt->module);
@@ -800,22 +794,10 @@ out_free_ph:
goto out;
}
-static int
-load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_elf_binary(bprm, regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/* This is really simpleminded and specialized - we are loading an
a.out library that is given an ELF header. */
-static inline int
-do_load_elf_library(int fd)
+static int load_elf_library(int fd)
{
struct file * file;
struct dentry * dentry;
@@ -907,16 +889,6 @@ out:
return error;
}
-static int load_elf_library(int fd)
-{
- int retval;
-
- MOD_INC_USE_COUNT;
- retval = do_load_elf_library(fd);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
/*
* Note that some platforms still use traditional core dumps and not
* the ELF core dump. Each platform can select it as appropriate.
@@ -1069,10 +1041,6 @@ static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)
elf_fpregset_t fpu; /* NT_PRFPREG */
struct elf_prpsinfo psinfo; /* NT_PRPSINFO */
-#ifndef CONFIG_BINFMT_ELF
- MOD_INC_USE_COUNT;
-#endif
-
/* Count what's needed to dump, up to the limit of coredump size */
segs = 0;
size = 0;
@@ -1302,9 +1270,6 @@ static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)
end_coredump:
set_fs(fs);
-#ifndef CONFIG_BINFMT_ELF
- MOD_DEC_USE_COUNT;
-#endif
return has_dumped;
}
#endif /* USE_ELF_CORE_DUMP */
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
index d89bd9ca8..189d130ca 100644
--- a/fs/binfmt_em86.c
+++ b/fs/binfmt_em86.c
@@ -20,7 +20,7 @@
#define EM86_INTERP "/usr/bin/em86"
#define EM86_I_NAME "em86"
-static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
+static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
{
char *interp, *i_name, *i_arg;
struct dentry * dentry;
@@ -95,15 +95,6 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
return search_binary_handler(bprm, regs);
}
-static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
-{
- int retval;
- MOD_INC_USE_COUNT;
- retval = do_load_em86(bprm,regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
struct linux_binfmt em86_format = {
NULL, THIS_MODULE, load_em86, NULL, NULL, 0
};
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 5d5d17bc4..c530a6ff2 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -185,7 +185,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
char *iname_addr = iname;
int retval;
- MOD_INC_USE_COUNT;
retval = -ENOEXEC;
if (!enabled)
goto _ret;
@@ -224,7 +223,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
if (retval >= 0)
retval = search_binary_handler(bprm, regs);
_ret:
- MOD_DEC_USE_COUNT;
return retval;
}
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 8c71ec4a9..84dbf11b0 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -13,7 +13,7 @@
#include <linux/init.h>
#include <linux/smp_lock.h>
-static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
+static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
{
char *cp, *i_name, *i_arg;
struct dentry * dentry;
@@ -94,15 +94,6 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
return search_binary_handler(bprm,regs);
}
-static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
-{
- int retval;
- MOD_INC_USE_COUNT;
- retval = do_load_script(bprm,regs);
- MOD_DEC_USE_COUNT;
- return retval;
-}
-
struct linux_binfmt script_format = {
NULL, THIS_MODULE, load_script, NULL, NULL, 0
};
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index c00a6945c..cc12ccaef 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -260,7 +260,6 @@ static int coda_psdev_open(struct inode * inode, struct file * file)
}
vcp->vc_inuse++;
- MOD_INC_USE_COUNT;
if ( file->f_flags == O_RDWR ) {
vcp->vc_pid = current->pid;
@@ -290,7 +289,6 @@ static int coda_psdev_release(struct inode * inode, struct file * file)
}
vcp->vc_inuse--;
- MOD_DEC_USE_COUNT;
CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
vcp->vc_inuse, vcp->vc_pid, current->pid);
diff --git a/fs/efs/super.c b/fs/efs/super.c
index 092c2f0a7..7917bef5c 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -17,26 +17,22 @@ static DECLARE_FSTYPE_DEV(efs_fs_type, "efs", efs_read_super);
static struct super_operations efs_superblock_operations = {
read_inode: efs_read_inode,
- put_super: efs_put_super,
statfs: efs_statfs,
};
-int __init init_efs_fs(void) {
- return register_filesystem(&efs_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void) {
+static int __init init_efs_fs(void) {
printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
- return init_efs_fs();
+ return register_filesystem(&efs_fs_type);
}
-void cleanup_module(void) {
+static void __exit exit_efs_fs(void) {
unregister_filesystem(&efs_fs_type);
}
-#endif
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_efs_fs)
+module_exit(exit_efs_fs)
static efs_block_t efs_validate_vh(struct volume_header *vh) {
int i;
@@ -209,9 +205,6 @@ out_no_fs:
return(NULL);
}
-void efs_put_super(struct super_block *s) {
-}
-
int efs_statfs(struct super_block *s, struct statfs *buf) {
struct efs_sb_info *sb = SUPER_INFO(s);
diff --git a/fs/exec.c b/fs/exec.c
index 1b881e123..e3ff2d005 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -33,6 +33,9 @@
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#define __NO_VERSION__
+#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
@@ -43,6 +46,7 @@
#endif
static struct linux_binfmt *formats = (struct linux_binfmt *) NULL;
+static spinlock_t binfmt_lock = SPIN_LOCK_UNLOCKED;
int register_binfmt(struct linux_binfmt * fmt)
{
@@ -52,13 +56,17 @@ int register_binfmt(struct linux_binfmt * fmt)
return -EINVAL;
if (fmt->next)
return -EBUSY;
+ spin_lock(&binfmt_lock);
while (*tmp) {
- if (fmt == *tmp)
+ if (fmt == *tmp) {
+ spin_unlock(&binfmt_lock);
return -EBUSY;
+ }
tmp = &(*tmp)->next;
}
fmt->next = formats;
formats = fmt;
+ spin_unlock(&binfmt_lock);
return 0;
}
@@ -66,16 +74,25 @@ int unregister_binfmt(struct linux_binfmt * fmt)
{
struct linux_binfmt ** tmp = &formats;
+ spin_lock(&binfmt_lock);
while (*tmp) {
if (fmt == *tmp) {
*tmp = fmt->next;
+ spin_unlock(&binfmt_lock);
return 0;
}
tmp = &(*tmp)->next;
}
+ spin_unlock(&binfmt_lock);
return -EINVAL;
}
+static inline void put_binfmt(struct linux_binfmt * fmt)
+{
+ if (fmt->module)
+ __MOD_DEC_USE_COUNT(fmt->module);
+}
+
/* N.B. Error returns must be < 0 */
int open_dentry(struct dentry * dentry, int mode)
{
@@ -146,15 +163,22 @@ asmlinkage long sys_uselib(const char * library)
file = fget(fd);
retval = -ENOEXEC;
if (file && file->f_dentry && file->f_op && file->f_op->read) {
+ spin_lock(&binfmt_lock);
for (fmt = formats ; fmt ; fmt = fmt->next) {
int (*fn)(int) = fmt->load_shlib;
if (!fn)
continue;
+ if (!try_inc_mod_count(fmt->module))
+ continue;
+ spin_unlock(&binfmt_lock);
/* N.B. Should use file instead of fd */
retval = fn(fd);
+ spin_lock(&binfmt_lock);
+ put_binfmt(fmt);
if (retval != -ENOEXEC)
break;
}
+ spin_unlock(&binfmt_lock);
}
fput(file);
sys_close(fd);
@@ -767,12 +791,17 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
}
#endif
for (try=0; try<2; try++) {
+ spin_lock(&binfmt_lock);
for (fmt = formats ; fmt ; fmt = fmt->next) {
int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
if (!fn)
continue;
+ if (!try_inc_mod_count(fmt->module))
+ continue;
+ spin_unlock(&binfmt_lock);
retval = fn(bprm, regs);
if (retval >= 0) {
+ put_binfmt(fmt);
if (bprm->dentry) {
lock_kernel();
dput(bprm->dentry);
@@ -782,11 +811,16 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
current->did_exec = 1;
return retval;
}
+ spin_lock(&binfmt_lock);
+ put_binfmt(fmt);
if (retval != -ENOEXEC)
break;
- if (!bprm->dentry) /* We don't have the dentry anymore */
+ if (!bprm->dentry) {
+ spin_unlock(&binfmt_lock);
return retval;
+ }
}
+ spin_unlock(&binfmt_lock);
if (retval != -ENOEXEC) {
break;
#ifdef CONFIG_KMOD
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 29753ba9e..67dedf6d6 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -151,14 +151,14 @@ revalidate:
* not the directory has been modified
* during the copy operation.
*/
- unsigned long version = inode->i_version;
+ unsigned long version = filp->f_version;
error = filldir(dirent, de->name,
de->name_len,
filp->f_pos, le32_to_cpu(de->inode));
if (error)
break;
- if (version != inode->i_version)
+ if (version != filp->f_version)
goto revalidate;
stored ++;
}
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index 3a2e4ea82..22ce56b68 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -7,6 +7,7 @@
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mm.h>
#include <linux/msdos_fs.h>
@@ -54,8 +55,10 @@ EXPORT_SYMBOL(fat_add_entries);
EXPORT_SYMBOL(fat_dir_empty);
EXPORT_SYMBOL(fat_truncate);
-int init_fat_fs(void)
+static int __init init_fat_fs(void)
{
fat_hash_init();
return 0;
}
+
+module_init(init_fat_fs)
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index f95adc0fd..cedd3ba2b 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -920,19 +920,3 @@ int fat_notify_change(struct dentry * dentry, struct iattr * attr)
~MSDOS_SB(sb)->options.fs_umask;
return 0;
}
-
-
-#ifdef MODULE
-int init_module(void)
-{
- return init_fat_fs();
-}
-
-
-void cleanup_module(void)
-{
- /* Nothing to be done, really! */
- return;
-}
-#endif
-
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 807f8306d..6ad3ec407 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -9,30 +9,10 @@
#include <linux/config.h>
#include <linux/fs.h>
-#include <linux/minix_fs.h>
-#include <linux/ext2_fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/umsdos_fs.h>
-#include <linux/proc_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/nfs_fs.h>
-#include <linux/iso_fs.h>
-#include <linux/sysv_fs.h>
-#include <linux/hpfs_fs.h>
-#include <linux/smb_fs.h>
-#include <linux/ncp_fs.h>
-#include <linux/affs_fs.h>
-#include <linux/ufs_fs.h>
-#include <linux/efs_fs.h>
-#include <linux/romfs_fs.h>
#include <linux/auto_fs.h>
-#include <linux/qnx4_fs.h>
-#include <linux/udf_fs.h>
-#include <linux/ntfs_fs.h>
-#include <linux/hfs_fs.h>
#include <linux/devpts_fs.h>
-#include <linux/bfs_fs.h>
-#include <linux/openprom_fs.h>
#include <linux/major.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -42,7 +22,6 @@
#include <linux/lockd/bind.h>
#include <linux/lockd/xdr.h>
#include <linux/init.h>
-#include <linux/nls.h>
#ifdef CONFIG_CODA_FS
extern int init_coda(void);
@@ -52,40 +31,8 @@ extern int init_coda(void);
extern int init_devpts_fs(void);
#endif
-#ifdef CONFIG_SUN_OPENPROMFS
-extern int init_openprom_fs(void);
-#endif
-
void __init filesystem_setup(void)
{
-#ifdef CONFIG_MINIX_FS
- init_minix_fs();
-#endif
-
-#ifdef CONFIG_ROMFS_FS
- init_romfs_fs();
-#endif
-
-#ifdef CONFIG_UMSDOS_FS
- init_umsdos_fs();
-#endif
-
-#ifdef CONFIG_FAT_FS
- init_fat_fs();
-#endif
-
-#ifdef CONFIG_MSDOS_FS
- init_msdos_fs();
-#endif
-
-#ifdef CONFIG_VFAT_FS
- init_vfat_fs();
-#endif
-
-#ifdef CONFIG_PROC_FS
- init_proc_fs();
-#endif
-
init_devfs_fs(); /* Header file may make this empty */
#ifdef CONFIG_NFS_FS
@@ -96,69 +43,9 @@ void __init filesystem_setup(void)
init_coda();
#endif
-#ifdef CONFIG_SMB_FS
- init_smb_fs();
-#endif
-
-#ifdef CONFIG_NCP_FS
- init_ncp_fs();
-#endif
-
-#ifdef CONFIG_ISO9660_FS
- init_iso9660_fs();
-#endif
-
-#ifdef CONFIG_SYSV_FS
- init_sysv_fs();
-#endif
-
-#ifdef CONFIG_HPFS_FS
- init_hpfs_fs();
-#endif
-
-#ifdef CONFIG_NTFS_FS
- init_ntfs_fs();
-#endif
-
-#ifdef CONFIG_HFS_FS
- init_hfs_fs();
-#endif
-
-#ifdef CONFIG_AFFS_FS
- init_affs_fs();
-#endif
-
-#ifdef CONFIG_UFS_FS
- init_ufs_fs();
-#endif
-
-#ifdef CONFIG_EFS_FS
- init_efs_fs();
-#endif
-
#ifdef CONFIG_DEVPTS_FS
init_devpts_fs();
#endif
-
-#ifdef CONFIG_QNX4FS_FS
- init_qnx4_fs();
-#endif
-
-#ifdef CONFIG_UDF_FS
- init_udf_fs();
-#endif
-
-#ifdef CONFIG_BFS_FS
- init_bfs_fs();
-#endif
-
-#ifdef CONFIG_SUN_OPENPROMFS
- init_openprom_fs();
-#endif
-
-#ifdef CONFIG_NLS
- init_nls();
-#endif
}
#ifndef CONFIG_NFSD
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 251690dc8..8c91758d6 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -472,38 +472,19 @@ bail3:
return NULL;
}
-int __init init_hfs_fs(void)
+static int __init init_hfs_fs(void)
{
hfs_cat_init();
return register_filesystem(&hfs_fs);
}
-#ifdef MODULE
-int init_module(void) {
- int error;
-
-#if defined(DEBUG_SIZES) || defined(DEBUG_ALL)
- hfs_warn("HFS inode: %d bytes available\n",
- sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info));
- hfs_warn("HFS super_block: %d bytes available\n",
- sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info));
- if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) ||
- (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) {
- return -ENOMEM; /* well sort of */
- }
-#endif
- error = init_hfs_fs();
- if (!error) {
- /* register_symtab(NULL); */
- }
- return error;
-}
-
-void cleanup_module(void) {
+static void __exit exit_hfs_fs(void) {
hfs_cat_free();
unregister_filesystem(&hfs_fs);
}
-#endif
+
+module_init(init_hfs_fs)
+module_exit(exit_hfs_fs)
#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
long int hfs_alloc = 0;
diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c
index 8bcbf28fc..dc9ccf624 100644
--- a/fs/hpfs/buffer.c
+++ b/fs/hpfs/buffer.c
@@ -125,7 +125,8 @@ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head
kdev_t dev = s->s_dev;
struct buffer_head *bh;
- if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
+ /* vvvv - workaround for the breada bug */
+ if (!ahead || secno + ahead + (read_ahead[MAJOR(dev)] >> 9) >= s->s_hpfs_fs_size)
*bhp = bh = bread(dev, secno, 512);
else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9);
if (bh != NULL)
@@ -144,6 +145,7 @@ void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head
/*return hpfs_map_sector(s, secno, bhp, 0);*/
if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+ if (!buffer_uptodate(bh)) wait_on_buffer(bh);
mark_buffer_uptodate(bh, 1);
return bh->b_data;
} else {
@@ -172,8 +174,9 @@ void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffe
goto bail;
}
- if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
- qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048);
+ /* vvvv - workaround for the breada bug */
+ if (!ahead || secno + 4 + ahead + (read_ahead[MAJOR(dev)] >> 9) > s->s_hpfs_fs_size)
+ qbh->bh[0] = bh = bread(dev, secno, 512);
else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9);
if (!bh)
goto bail0;
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
index 677fda52c..5071782b0 100644
--- a/fs/hpfs/hpfs_fn.h
+++ b/fs/hpfs/hpfs_fn.h
@@ -312,6 +312,5 @@ int hpfs_remount_fs(struct super_block *, int *, char *);
void hpfs_put_super(struct super_block *);
unsigned hpfs_count_one_bitmap(struct super_block *, secno);
int hpfs_statfs(struct super_block *, struct statfs *);
-struct super_block *hpfs_read_super(struct super_block *, void *, int);
extern struct address_space_operations hpfs_aops;
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
index c8283c672..69303cb9e 100644
--- a/fs/hpfs/inode.c
+++ b/fs/hpfs/inode.c
@@ -314,4 +314,5 @@ void hpfs_write_if_changed(struct inode *inode)
void hpfs_delete_inode(struct inode *inode)
{
hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+ clear_inode(inode);
}
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index d29f6e574..04cd6428d 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include "hpfs_fn.h"
#include <linux/module.h>
+#include <linux/init.h>
/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
@@ -557,26 +558,17 @@ bail0:
DECLARE_FSTYPE_DEV(hpfs_fs_type, "hpfs", hpfs_read_super);
-int init_hpfs_fs(void)
+static int __init init_hpfs_fs(void)
{
return register_filesystem(&hpfs_fs_type);
}
-#ifdef MODULE
-
-/*int register_symtab_from(struct symbol_table *, long *);*/
-
-int init_module(void)
-{
- /*int status;
- if (!(status = init_hpfs_fs())) register_symtab(NULL);
- return status;*/
- return init_hpfs_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_hpfs_fs(void)
{
unregister_filesystem(&hpfs_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_hpfs_fs)
+module_exit(exit_hpfs_fs)
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index b13ee0285..4ecd72cd2 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -994,16 +994,20 @@ static struct address_space_operations isofs_aops = {
bmap: _isofs_bmap
};
-static void test_and_set_uid(uid_t *p, uid_t value)
+static inline void test_and_set_uid(uid_t *p, uid_t value)
{
if(value) {
*p = value;
-#if 0
- printk("Resetting to %d\n", value);
-#endif
}
}
+static inline void test_and_set_gid(gid_t *p, gid_t value)
+{
+ if(value) {
+ *p = value;
+ }
+}
+
static int isofs_read_level3_size(struct inode * inode)
{
unsigned long ino = inode->i_ino;
@@ -1207,6 +1211,7 @@ static void isofs_read_inode(struct inode * inode)
parse_rock_ridge_inode(raw_inode, inode);
/* hmm..if we want uid or gid set, override the rock ridge setting */
test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
+ test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
}
#ifdef DEBUG
@@ -1431,22 +1436,17 @@ void leak_check_brelse(struct buffer_head * bh){
static DECLARE_FSTYPE_DEV(iso9660_fs_type, "iso9660", isofs_read_super);
-int __init init_iso9660_fs(void)
+static int __init init_iso9660_fs(void)
{
return register_filesystem(&iso9660_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static void __exit exit_iso9660_fs(void)
{
- return init_iso9660_fs();
+ unregister_filesystem(&iso9660_fs_type);
}
-void cleanup_module(void)
-{
- unregister_filesystem(&iso9660_fs_type);
-}
+EXPORT_NO_SYMBOLS;
-#endif
+module_init(init_iso9660_fs)
+module_exit(exit_iso9660_fs)
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 31d4c99c7..4c9fa16a3 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -1267,22 +1267,17 @@ int minix_sync_inode(struct inode * inode)
static DECLARE_FSTYPE_DEV(minix_fs_type,"minix",minix_read_super);
-int __init init_minix_fs(void)
+static int __init init_minix_fs(void)
{
return register_filesystem(&minix_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static void __exit exit_minix_fs(void)
{
- return init_minix_fs();
+ unregister_filesystem(&minix_fs_type);
}
-void cleanup_module(void)
-{
- unregister_filesystem(&minix_fs_type);
-}
+EXPORT_NO_SYMBOLS;
-#endif
+module_init(init_minix_fs)
+module_exit(exit_minix_fs)
diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c
index 8e9897f2f..7b578f00f 100644
--- a/fs/msdos/msdosfs_syms.c
+++ b/fs/msdos/msdosfs_syms.c
@@ -26,10 +26,17 @@ EXPORT_SYMBOL(msdos_unlink);
EXPORT_SYMBOL(msdos_read_super);
EXPORT_SYMBOL(msdos_put_super);
+static DECLARE_FSTYPE_DEV(msdos_fs_type, "msdos", msdos_read_super);
-DECLARE_FSTYPE_DEV(msdos_fs_type, "msdos", msdos_read_super);
-
-int __init init_msdos_fs(void)
+static int __init init_msdos_fs(void)
{
return register_filesystem(&msdos_fs_type);
}
+
+static void __exit exit_msdos_fs(void)
+{
+ unregister_filesystem(&msdos_fs_type);
+}
+
+module_init(init_msdos_fs)
+module_exit(exit_msdos_fs)
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 7febeaa8b..495df7cd1 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -599,28 +599,7 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data, int sile
MSDOS_SB(sb)->options.isvfat = 0;
res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
- if (res == NULL)
- goto out_fail;
- sb->s_root->d_op = &msdos_dentry_operations;
+ if (res)
+ sb->s_root->d_op = &msdos_dentry_operations;
return res;
-
-out_fail:
- return NULL;
-}
-
-
-
-#ifdef MODULE
-int init_module(void)
-{
- return init_msdos_fs();
}
-
-
-void cleanup_module(void)
-{
- unregister_filesystem(&msdos_fs_type);
-}
-
-#endif
-
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 349eec589..f6ff5e420 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -698,15 +698,7 @@ int ncp_current_malloced;
static DECLARE_FSTYPE(ncp_fs_type, "ncpfs", ncp_read_super, 0);
-int __init init_ncp_fs(void)
-{
- return register_filesystem(&ncp_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
+static int __init init_ncp_fs(void)
{
DPRINTK("ncpfs: init_module called\n");
@@ -714,10 +706,10 @@ int init_module(void)
ncp_malloced = 0;
ncp_current_malloced = 0;
#endif
- return init_ncp_fs();
+ return register_filesystem(&ncp_fs_type);
}
-void cleanup_module(void)
+static void __exit exit_ncp_fs(void)
{
DPRINTK("ncpfs: cleanup_module called\n");
unregister_filesystem(&ncp_fs_type);
@@ -727,4 +719,7 @@ void cleanup_module(void)
#endif
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ncp_fs)
+module_exit(exit_ncp_fs)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 37a8715cf..717d12bbb 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -161,7 +161,7 @@ nfs_readpage_result(struct rpc_task *task)
}
kunmap(page);
UnlockPage(page);
- free_page(address);
+ __free_page(page);
rpc_release_task(task);
kfree(req);
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 4a7e63dcb..7baab32d9 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -8,6 +8,7 @@
* Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/fs.h>
diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c
index c1ea11ff8..1a0893b7e 100644
--- a/fs/nls/nls_base.c
+++ b/fs/nls/nls_base.c
@@ -17,9 +17,11 @@
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
+#include <linux/spinlock.h>
#include <asm/byteorder.h>
static struct nls_table *tables = (struct nls_table *) NULL;
+static spinlock_t nls_lock = SPIN_LOCK_UNLOCKED;
/*
* Sample implementation from Unicode home page.
@@ -165,14 +167,18 @@ int register_nls(struct nls_table * nls)
return -EINVAL;
if (nls->next)
return -EBUSY;
+
+ spin_lock(&nls_lock);
while (*tmp) {
if (nls == *tmp) {
+ spin_unlock(&nls_lock);
return -EBUSY;
}
tmp = &(*tmp)->next;
}
nls->next = tables;
tables = nls;
+ spin_unlock(&nls_lock);
return 0;
}
@@ -180,25 +186,30 @@ int unregister_nls(struct nls_table * nls)
{
struct nls_table ** tmp = &tables;
+ spin_lock(&nls_lock);
while (*tmp) {
if (nls == *tmp) {
*tmp = nls->next;
+ spin_unlock(&nls_lock);
return 0;
}
tmp = &(*tmp)->next;
}
+ spin_unlock(&nls_lock);
return -EINVAL;
}
-struct nls_table *find_nls(char *charset)
+static struct nls_table *find_nls(char *charset)
{
- struct nls_table *nls = tables;
- while (nls) {
+ struct nls_table *nls;
+ spin_lock(&nls_lock);
+ for (nls = tables; nls; nls = nls->next)
if (! strcmp(nls->charset, charset))
- return nls;
- nls = nls->next;
- }
- return NULL;
+ break;
+ if (nls && !try_inc_mod_count(nls->owner))
+ nls = NULL;
+ spin_unlock(&nls_lock);
+ return nls;
}
struct nls_table *load_nls(char *charset)
@@ -210,16 +221,13 @@ struct nls_table *load_nls(char *charset)
#endif
nls = find_nls(charset);
- if (nls) {
- nls->inc_use_count();
+ if (nls)
return nls;
- }
-#ifndef CONFIG_KMOD
- return NULL;
-#else
+#ifdef CONFIG_KMOD
if (strlen(charset) > sizeof(buf) - sizeof("nls_")) {
- printk("Unable to load NLS charset %s: name too long\n", charset);
+ printk("Unable to load NLS charset %s: name too long\n",
+ charset);
return NULL;
}
@@ -230,16 +238,14 @@ struct nls_table *load_nls(char *charset)
return NULL;
}
nls = find_nls(charset);
- if (nls) {
- nls->inc_use_count();
- }
- return nls;
#endif
+ return nls;
}
void unload_nls(struct nls_table *nls)
{
- nls->dec_use_count();
+ if (nls->owner)
+ __MOD_DEC_USE_COUNT(nls->owner);
}
struct nls_unicode charset2uni[256] = {
@@ -437,28 +443,15 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-void inc_use_count(void)
-{
-}
-
-void dec_use_count(void)
-{
-}
-
static struct nls_table default_table = {
"default",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ NULL,
};
-
-
/* Returns a simple default translation table */
struct nls_table *load_nls_default(void)
{
@@ -468,111 +461,9 @@ struct nls_table *load_nls_default(void)
EXPORT_SYMBOL(register_nls);
EXPORT_SYMBOL(unregister_nls);
EXPORT_SYMBOL(unload_nls);
-EXPORT_SYMBOL(find_nls);
EXPORT_SYMBOL(load_nls);
EXPORT_SYMBOL(load_nls_default);
EXPORT_SYMBOL(utf8_mbtowc);
EXPORT_SYMBOL(utf8_mbstowcs);
EXPORT_SYMBOL(utf8_wctomb);
EXPORT_SYMBOL(utf8_wcstombs);
-
-int init_nls(void)
-{
-#ifdef CONFIG_NLS_ISO8859_1
- init_nls_iso8859_1();
-#endif
-#ifdef CONFIG_NLS_ISO8859_2
- init_nls_iso8859_2();
-#endif
-#ifdef CONFIG_NLS_ISO8859_3
- init_nls_iso8859_3();
-#endif
-#ifdef CONFIG_NLS_ISO8859_4
- init_nls_iso8859_4();
-#endif
-#ifdef CONFIG_NLS_ISO8859_5
- init_nls_iso8859_5();
-#endif
-#ifdef CONFIG_NLS_ISO8859_6
- init_nls_iso8859_6();
-#endif
-#ifdef CONFIG_NLS_ISO8859_7
- init_nls_iso8859_7();
-#endif
-#ifdef CONFIG_NLS_ISO8859_8
- init_nls_iso8859_8();
-#endif
-#ifdef CONFIG_NLS_ISO8859_9
- init_nls_iso8859_9();
-#endif
-#ifdef CONFIG_NLS_ISO8859_14
- init_nls_iso8859_14();
-#endif
-#ifdef CONFIG_NLS_ISO8859_15
- init_nls_iso8859_15();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_437
- init_nls_cp437();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_737
- init_nls_cp737();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_775
- init_nls_cp775();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_850
- init_nls_cp850();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_852
- init_nls_cp852();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_855
- init_nls_cp855();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_857
- init_nls_cp857();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_860
- init_nls_cp860();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_861
- init_nls_cp861();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_862
- init_nls_cp862();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_863
- init_nls_cp863();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_864
- init_nls_cp864();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_865
- init_nls_cp865();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_866
- init_nls_cp866();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_869
- init_nls_cp869();
-#endif
-#ifdef CONFIG_NLS_CODEPAGE_874
- init_nls_cp874();
-#endif
-#ifdef CONFIG_NLS_KOI8_R
- init_nls_koi8_r();
-#endif
- return 0;
-}
-
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls();
-}
-
-
-void cleanup_module(void)
-{
-}
-#endif /* ifdef MODULE */
diff --git a/fs/nls/nls_cp437.c b/fs/nls/nls_cp437.c
index a64a12d4b..dc664e484 100644
--- a/fs/nls/nls_cp437.c
+++ b/fs/nls/nls_cp437.c
@@ -427,46 +427,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp437",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp437(void)
+static int __init init_nls_cp437(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp437();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp437(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp437)
+module_exit(exit_nls_cp437)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp737.c b/fs/nls/nls_cp737.c
index 0ae5575e0..30879b749 100644
--- a/fs/nls/nls_cp737.c
+++ b/fs/nls/nls_cp737.c
@@ -355,46 +355,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp737",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp737(void)
+static int __init init_nls_cp737(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp737();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp737(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp737)
+module_exit(exit_nls_cp737)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp775.c b/fs/nls/nls_cp775.c
index d740adbba..a2efcba1a 100644
--- a/fs/nls/nls_cp775.c
+++ b/fs/nls/nls_cp775.c
@@ -355,46 +355,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp775",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp775(void)
+static int __init init_nls_cp775(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp775();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp775(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp775)
+module_exit(exit_nls_cp775)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp850.c b/fs/nls/nls_cp850.c
index d15cfd893..38f0b86af 100644
--- a/fs/nls/nls_cp850.c
+++ b/fs/nls/nls_cp850.c
@@ -319,46 +319,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp850",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp850(void)
+static int __init init_nls_cp850(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp850();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp850(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp850)
+module_exit(exit_nls_cp850)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp852.c b/fs/nls/nls_cp852.c
index f270943ff..c57b77bc7 100644
--- a/fs/nls/nls_cp852.c
+++ b/fs/nls/nls_cp852.c
@@ -319,46 +319,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xeb, 0xfc, 0xfc, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp852",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp852(void)
+static int __init init_nls_cp852(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp852();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp852(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp852)
+module_exit(exit_nls_cp852)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp855.c b/fs/nls/nls_cp855.c
index c0b6eb7fc..e8a9fa2c2 100644
--- a/fs/nls/nls_cp855.c
+++ b/fs/nls/nls_cp855.c
@@ -319,46 +319,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xfa, 0xfa, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp855",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp855(void)
+static int __init init_nls_cp855(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp855();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp855(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp855)
+module_exit(exit_nls_cp855)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp857.c b/fs/nls/nls_cp857.c
index 8125401f1..857fa04e2 100644
--- a/fs/nls/nls_cp857.c
+++ b/fs/nls/nls_cp857.c
@@ -283,46 +283,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp857",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp857(void)
+static int __init init_nls_cp857(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp857();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp857(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp857)
+module_exit(exit_nls_cp857)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp860.c b/fs/nls/nls_cp860.c
index 187187f24..cbc8d0494 100644
--- a/fs/nls/nls_cp860.c
+++ b/fs/nls/nls_cp860.c
@@ -391,46 +391,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp860",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp860(void)
+static int __init init_nls_cp860(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp860();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp860(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp860)
+module_exit(exit_nls_cp860)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp861.c b/fs/nls/nls_cp861.c
index d42259d0a..697a19d07 100644
--- a/fs/nls/nls_cp861.c
+++ b/fs/nls/nls_cp861.c
@@ -427,46 +427,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp861",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp861(void)
+static int __init init_nls_cp861(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp861();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp861(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp861)
+module_exit(exit_nls_cp861)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp862.c b/fs/nls/nls_cp862.c
index 6a86e5ef5..710382d39 100644
--- a/fs/nls/nls_cp862.c
+++ b/fs/nls/nls_cp862.c
@@ -463,46 +463,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp862",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp862(void)
+static int __init init_nls_cp862(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp862();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp862(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp862)
+module_exit(exit_nls_cp862)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp863.c b/fs/nls/nls_cp863.c
index 06e5361bc..ded7b89ee 100644
--- a/fs/nls/nls_cp863.c
+++ b/fs/nls/nls_cp863.c
@@ -427,46 +427,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp863",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp863(void)
+static int __init init_nls_cp863(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp863();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp863(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp863)
+module_exit(exit_nls_cp863)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp864.c b/fs/nls/nls_cp864.c
index 6f3182aec..f6e6fae5d 100644
--- a/fs/nls/nls_cp864.c
+++ b/fs/nls/nls_cp864.c
@@ -418,46 +418,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp864",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp864(void)
+static int __init init_nls_cp864(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp864();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp864(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp864)
+module_exit(exit_nls_cp864)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp865.c b/fs/nls/nls_cp865.c
index 24dd1dabb..c77ba51f7 100644
--- a/fs/nls/nls_cp865.c
+++ b/fs/nls/nls_cp865.c
@@ -427,46 +427,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp865",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp865(void)
+static int __init init_nls_cp865(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp865();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp865(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp865)
+module_exit(exit_nls_cp865)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp866.c b/fs/nls/nls_cp866.c
index b78dd5148..54d5a2ad1 100644
--- a/fs/nls/nls_cp866.c
+++ b/fs/nls/nls_cp866.c
@@ -355,46 +355,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp866",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp866(void)
+static int __init init_nls_cp866(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp866();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp866(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp866)
+module_exit(exit_nls_cp866)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp869.c b/fs/nls/nls_cp869.c
index 76a693299..eb119e546 100644
--- a/fs/nls/nls_cp869.c
+++ b/fs/nls/nls_cp869.c
@@ -319,46 +319,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xd5, 0x96, 0xfc, 0x98, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp869",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp869(void)
+static int __init init_nls_cp869(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp869();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp869(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp869)
+module_exit(exit_nls_cp869)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_cp874.c b/fs/nls/nls_cp874.c
index 108a7045e..a40b6b07b 100644
--- a/fs/nls/nls_cp874.c
+++ b/fs/nls/nls_cp874.c
@@ -283,46 +283,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"cp874",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_cp874(void)
+static int __init init_nls_cp874(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_cp874();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_cp874(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_cp874)
+module_exit(exit_nls_cp874)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-1.c b/fs/nls/nls_iso8859-1.c
index 070d97920..4b0bb644f 100644
--- a/fs/nls/nls_iso8859-1.c
+++ b/fs/nls/nls_iso8859-1.c
@@ -207,46 +207,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-1",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_1(void)
+static int __init init_nls_iso8859_1(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_1();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_1(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_1)
+module_exit(exit_nls_iso8859_1)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-14.c b/fs/nls/nls_iso8859-14.c
index 7cbd8c316..7c7afe7f9 100644
--- a/fs/nls/nls_iso8859-14.c
+++ b/fs/nls/nls_iso8859-14.c
@@ -291,46 +291,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xaf, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-14",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_14(void)
+static int __init init_nls_iso8859_14(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
+static void __exit exit_nls_iso8859_14(void)
{
- return init_nls_iso8859_14();
-}
-
-
-void cleanup_module(void)
-{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_14)
+module_exit(exit_nls_iso8859_14)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-15.c b/fs/nls/nls_iso8859-15.c
index 1376d1f4a..694576f64 100644
--- a/fs/nls/nls_iso8859-15.c
+++ b/fs/nls/nls_iso8859-15.c
@@ -284,46 +284,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xbe, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-15",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_15(void)
+static int __init init_nls_iso8859_15(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_15();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_15(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_15)
+module_exit(exit_nls_iso8859_15)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-2.c b/fs/nls/nls_iso8859-2.c
index 274a717c9..5ba62da11 100644
--- a/fs/nls/nls_iso8859-2.c
+++ b/fs/nls/nls_iso8859-2.c
@@ -279,46 +279,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-2",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_2(void)
+static int __init init_nls_iso8859_2(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_2();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_2(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_2)
+module_exit(exit_nls_iso8859_2)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-3.c b/fs/nls/nls_iso8859-3.c
index 34eebad93..7e469c8fe 100644
--- a/fs/nls/nls_iso8859-3.c
+++ b/fs/nls/nls_iso8859-3.c
@@ -279,46 +279,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-3",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_3(void)
+static int __init init_nls_iso8859_3(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_3();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_3(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_3)
+module_exit(exit_nls_iso8859_3)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-4.c b/fs/nls/nls_iso8859-4.c
index 18ef00e49..63a105d7e 100644
--- a/fs/nls/nls_iso8859-4.c
+++ b/fs/nls/nls_iso8859-4.c
@@ -279,46 +279,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-4",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_4(void)
+static int __init init_nls_iso8859_4(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_4();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_4(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_4)
+module_exit(exit_nls_iso8859_4)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-5.c b/fs/nls/nls_iso8859-5.c
index 2ba940d78..7ca56a3da 100644
--- a/fs/nls/nls_iso8859-5.c
+++ b/fs/nls/nls_iso8859-5.c
@@ -283,46 +283,27 @@ static unsigned char charset2upper[256] = {
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xfd, 0xae, 0xaf, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-5",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_5(void)
+static int __init init_nls_iso8859_5(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_5();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_5(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_5)
+module_exit(exit_nls_iso8859_5)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-6.c b/fs/nls/nls_iso8859-6.c
index 78b0203cf..877fde7cc 100644
--- a/fs/nls/nls_iso8859-6.c
+++ b/fs/nls/nls_iso8859-6.c
@@ -243,46 +243,27 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-6",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_6(void)
+static int __init init_nls_iso8859_6(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_6();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_6(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_6)
+module_exit(exit_nls_iso8859_6)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-7.c b/fs/nls/nls_iso8859-7.c
index a21a56d03..44a709cb5 100644
--- a/fs/nls/nls_iso8859-7.c
+++ b/fs/nls/nls_iso8859-7.c
@@ -319,46 +319,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xbc, 0xbe, 0xbf, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-7",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_7(void)
+static int __init init_nls_iso8859_7(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_7();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_7(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_7)
+module_exit(exit_nls_iso8859_7)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-8.c b/fs/nls/nls_iso8859-8.c
index c8282dad3..f22994d4b 100644
--- a/fs/nls/nls_iso8859-8.c
+++ b/fs/nls/nls_iso8859-8.c
@@ -283,46 +283,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-8",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_8(void)
+static int __init init_nls_iso8859_8(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_8();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_8(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_8)
+module_exit(exit_nls_iso8859_8)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_iso8859-9.c b/fs/nls/nls_iso8859-9.c
index 74baa761e..d2f531ad2 100644
--- a/fs/nls/nls_iso8859-9.c
+++ b/fs/nls/nls_iso8859-9.c
@@ -243,46 +243,27 @@ static unsigned char charset2upper[256] = {
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0x49, 0xde, 0x00, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"iso8859-9",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_iso8859_9(void)
+static int __init init_nls_iso8859_9(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_iso8859_9();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_iso8859_9(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_iso8859_9)
+module_exit(exit_nls_iso8859_9)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/nls/nls_koi8-r.c b/fs/nls/nls_koi8-r.c
index 3dc3effaa..59bb2a40a 100644
--- a/fs/nls/nls_koi8-r.c
+++ b/fs/nls/nls_koi8-r.c
@@ -355,46 +355,27 @@ static unsigned char charset2upper[256] = {
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
-static void inc_use_count(void)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void dec_use_count(void)
-{
- MOD_DEC_USE_COUNT;
-}
-
static struct nls_table table = {
"koi8-r",
page_uni2charset,
charset2uni,
charset2lower,
charset2upper,
- inc_use_count,
- dec_use_count,
- NULL
+ THIS_MODULE,
};
-int init_nls_koi8_r(void)
+static int __init init_nls_koi8_r(void)
{
return register_nls(&table);
}
-#ifdef MODULE
-int init_module(void)
-{
- return init_nls_koi8_r();
-}
-
-
-void cleanup_module(void)
+static void __exit exit_nls_koi8_r(void)
{
unregister_nls(&table);
- return;
}
-#endif
+
+module_init(init_nls_koi8_r)
+module_exit(exit_nls_koi8_r)
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c
index a24faac65..e95a36179 100644
--- a/fs/ntfs/fs.c
+++ b/fs/ntfs/fs.c
@@ -921,13 +921,7 @@ ntfs_read_super_dec:
*/
static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super);
-/* When this code is not compiled as a module, this is the main entry point,
- * called by do_sys_setup() in fs/filesystems.c
- *
- * NOTE : __init is a macro used to remove this function from memory
- * once initialization is done
- */
-int __init init_ntfs_fs(void)
+static int __init init_ntfs_fs(void)
{
/* Comment this if you trust klogd. There are reasons not to trust it
*/
@@ -942,44 +936,21 @@ int __init init_ntfs_fs(void)
return register_filesystem(&ntfs_fs_type);
}
-#ifdef MODULE
-/* A module is a piece of code which can be inserted in and removed
- * from the running kernel whenever you want using lsmod, or on demand using
- * kmod
- */
+static __exit void exit_ntfs_fs(void)
+{
+ SYSCTL(0);
+ ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
+ unregister_filesystem(&ntfs_fs_type);
+}
-/* No function of this module is needed by another module */
EXPORT_NO_SYMBOLS;
-/* Only used for documentation purposes at the moment,
- * see include/linux/module.h
- */
MODULE_AUTHOR("Martin von Löwis");
MODULE_DESCRIPTION("NTFS driver");
-/* no MODULE_SUPPORTED_DEVICE() */
-/* Load-time parameter */
MODULE_PARM(ntdebug, "i");
MODULE_PARM_DESC(ntdebug, "Debug level");
-/* When this code is compiled as a module, if you use mount -t ntfs when no
- * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
- * fs/super.c asks kmod to load the module named ntfs in memory.
- *
- * Therefore, this function is the main entry point in this case
- */
-int init_module(void)
-{
- return init_ntfs_fs();
-}
-
-/* Called by kmod just before the kernel removes the module from memory */
-void cleanup_module(void)
-{
- SYSCTL(0);
- ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
- unregister_filesystem(&ntfs_fs_type);
-}
-#endif
-
+module_init(init_ntfs_fs)
+module_exit(exit_ntfs_fs)
/*
* Local variables:
* c-file-style: "linux"
diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index a95ad1acd..27b4be8cb 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -1,4 +1,4 @@
-/* $Id: inode.c,v 1.7 2000/03/10 04:45:50 davem Exp $
+/* $Id: inode.c,v 1.9 2000/03/13 21:59:43 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
* Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com)
@@ -1026,7 +1026,7 @@ out_no_root:
static DECLARE_FSTYPE(openprom_fs_type, "openpromfs", openprom_read_super, 0);
-int init_openprom_fs(void)
+static int __init init_openprom_fs(void)
{
nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
if (!nodes) {
@@ -1041,25 +1041,7 @@ int init_openprom_fs(void)
return register_filesystem(&openprom_fs_type);
}
-#ifdef MODULE
-
-EXPORT_NO_SYMBOLS;
-
-int init_module (void)
-{
- return init_openprom_fs();
-}
-
-#else
-
-void __init openpromfs_init (void)
-{
- init_openprom_fs();
-}
-#endif
-
-#ifdef MODULE
-void cleanup_module (void)
+static void __exit exit_openprom_fs(void)
{
int i;
unregister_filesystem(&openprom_fs_type);
@@ -1069,4 +1051,8 @@ void cleanup_module (void)
kfree (alias_names [i]);
nodes = NULL;
}
-#endif
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_openprom_fs)
+module_exit(exit_openprom_fs)
diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c
index 4ffc43b0b..856cc30aa 100644
--- a/fs/partitions/acorn.c
+++ b/fs/partitions/acorn.c
@@ -17,8 +17,6 @@
#include "check.h"
#include "acorn.h"
-extern void add_gd_partition(struct gendisk *hd, int minor, int start, int size);
-
static void
adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads,
unsigned long totalblocks)
@@ -41,21 +39,22 @@ struct linux_part {
unsigned long nr_sects;
};
-static struct disc_record *adfs_partition(struct gendisk *hd, char *name, char *data,
+static struct adfs_discrecord *adfs_partition(struct gendisk *hd, char *name, char *data,
unsigned long first_sector, unsigned int minor)
{
- struct disc_record *dr;
+ struct adfs_discrecord *dr;
unsigned int nr_sects;
if (adfs_checkbblk(data))
return NULL;
- dr = (struct disc_record *)(data + 0x1c0);
+ dr = (struct adfs_discrecord *)(data + 0x1c0);
if (dr->disc_size == 0 && dr->disc_size_high == 0)
return NULL;
- nr_sects = (dr->disc_size_high << 23) | (dr->disc_size >> 9);
+ nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
+ (le32_to_cpu(dr->disc_size) >> 9);
if (name)
printk(" [%s]", name);
@@ -93,8 +92,8 @@ static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_
if (rr->part[part].one &&
memcmp(rr->part[part].name, "All\0", 4)) {
add_gd_partition(hd, minor++,
- rr->part[part].start,
- rr->part[part].length);
+ le32_to_cpu(rr->part[part].start),
+ le32_to_cpu(rr->part[part].length));
printk("(%s)", rr->part[part].name);
}
}
@@ -131,11 +130,13 @@ static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_s
linuxp = (struct linux_part *)bh->b_data;
printk(" <");
- while (linuxp->magic == LINUX_NATIVE_MAGIC || linuxp->magic == LINUX_SWAP_MAGIC) {
+ while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
+ linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
if (!(minor & mask))
break;
- add_gd_partition(hd, minor++, first_sect + linuxp->start_sect,
- linuxp->nr_sects);
+ add_gd_partition(hd, minor++, first_sect +
+ le32_to_cpu(linuxp->start_sect),
+ le32_to_cpu(linuxp->nr_sects));
linuxp ++;
}
printk(" >");
@@ -174,7 +175,7 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
* Hence it is totally untested.
*/
do {
- struct disc_record *dr;
+ struct adfs_discrecord *dr;
unsigned int nr_sects;
if (!(minor & mask))
@@ -189,7 +190,8 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
name = NULL;
nr_sects = (bh->b_data[0x1fd] + (bh->b_data[0x1fe] << 8)) *
- (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) * dr->secspertrack;
+ (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) *
+ dr->secspertrack;
if (!nr_sects)
break;
@@ -244,7 +246,7 @@ static int adfspart_check_ADFS(struct gendisk *hd, kdev_t dev, unsigned long fir
{
unsigned long start_sect, nr_sects, sectscyl, heads;
struct buffer_head *bh;
- struct disc_record *dr;
+ struct adfs_discrecord *dr;
if(get_ptable_blocksize(dev)!=1024)
return 0;
@@ -300,9 +302,6 @@ static int adfspart_check_ICSLinux(kdev_t dev, unsigned long block)
unsigned int offset = block & 1 ? 512 : 0;
int result = 0;
- if(get_ptable_blocksize(dev)!=1024)
- return 0;
-
bh = bread(dev, block >> 1, 1024);
if (bh != NULL) {
@@ -350,7 +349,8 @@ static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long firs
for (i = 0, sum = 0x50617274; i < 508; i++)
sum += bh->b_data[i];
- if (sum != *(unsigned long *)(&bh->b_data[508])) {
+ sum -= le32_to_cpu(*(unsigned long *)(&bh->b_data[508]));
+ if (sum) {
brelse(bh);
return 0; /* not ICS partition table */
}
@@ -358,19 +358,32 @@ static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long firs
printk(" [ICS]");
for (p = (struct ics_part *)bh->b_data; p->size; p++) {
+ unsigned long start;
+ long size;
+
if ((minor & mask) == 0)
break;
- if (p->size < 0 && adfspart_check_ICSLinux(dev, p->start)) {
+ start = le32_to_cpu(p->start);
+ size = le32_to_cpu(p->size);
+
+ if (size < 0) {
+ size = -size;
+
/*
* We use the first sector to identify what type
* this partition is...
*/
- if (-p->size > 1)
- add_gd_partition(hd, minor, first_sector + p->start + 1, -p->size - 1);
- } else
- add_gd_partition(hd, minor, first_sector + p->start, p->size);
- minor++;
+ if (size > 1 && adfspart_check_ICSLinux(dev, start)) {
+ start += 1;
+ size -= 1;
+ }
+ }
+
+ if (size) {
+ add_gd_partition(hd, minor, first_sector + start, size);
+ minor++;
+ }
}
brelse(bh);
@@ -422,8 +435,15 @@ static int adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev, unsigned long
printk(" [POWERTEC]");
for (i = 0, p = (struct ptec_partition *)bh->b_data; i < 12; i++, p++) {
- if (p->size)
- add_gd_partition(hd, minor, first_sector + p->start, p->size);
+ unsigned long start;
+ unsigned long size;
+
+ start = le32_to_cpu(p->start);
+ size = le32_to_cpu(p->size);
+
+ if (size)
+ add_gd_partition(hd, minor, first_sector + start,
+ size);
minor++;
}
diff --git a/fs/partitions/acorn.h b/fs/partitions/acorn.h
index 932c305e6..748c7c83f 100644
--- a/fs/partitions/acorn.h
+++ b/fs/partitions/acorn.h
@@ -6,42 +6,6 @@
#include <linux/adfs_fs.h>
/*
- * Offset in bytes of the boot block on the disk.
- */
-#define BOOT_SECTOR_ADDRESS 0xc00
-
-/*
- * Disc record size
- */
-#define RECSIZE 60
-
-/*
- * Disc record
- */
-struct disc_record {
- unsigned char log2secsize;
- unsigned char secspertrack;
- unsigned char heads;
- unsigned char density;
- unsigned char idlen;
- unsigned char log2bpmb;
- unsigned char skew;
- unsigned char bootoption;
- unsigned char lowsector;
- unsigned char nzones;
- unsigned short zone_spare;
- unsigned long root;
- unsigned long disc_size;
- unsigned short disc_id;
- unsigned char disc_name[10];
- unsigned long disc_type;
- unsigned long disc_size_high;
- unsigned char log2sharesize:4;
- unsigned char unused:4;
- unsigned char big_flag:1;
-};
-
-/*
* Partition types. (Oh for reusability)
*/
#define PARTITION_RISCIX_MFM 1
diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c
index be7028e14..a94f8f5fc 100644
--- a/fs/partitions/atari.c
+++ b/fs/partitions/atari.c
@@ -34,9 +34,9 @@
be32_to_cpu((pi)->st) + be32_to_cpu((pi)->siz) <= (hdsiz))
int atari_partition (struct gendisk *hd, kdev_t dev,
- unsigned long first_sector, int first_part_minor)
+ unsigned long first_sector, int minor)
{
- int minor = first_part_minor, m_lim = first_part_minor + hd->max_p;
+ int m_lim = minor + hd->max_p;
struct buffer_head *bh;
struct rootsector *rs;
struct partition_info *pi;
diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c
index 8027abda9..3806b5f31 100644
--- a/fs/partitions/sgi.c
+++ b/fs/partitions/sgi.c
@@ -17,9 +17,9 @@
#include "check.h"
#include "sgi.h"
-int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor)
+int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int current_minor)
{
- int i, csum, magic, current_minor = first_part_minor;
+ int i, csum, magic;
unsigned int *ui, start, blocks, cs;
struct buffer_head *bh;
struct sgi_disklabel {
diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c
index 0203b9776..097e83468 100644
--- a/fs/proc/procfs_syms.c
+++ b/fs/proc/procfs_syms.c
@@ -22,7 +22,15 @@ EXPORT_SYMBOL(proc_root_driver);
static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, 0);
-int __init init_proc_fs(void)
+static int __init init_proc_fs(void)
{
- return register_filesystem(&proc_fs_type) == 0;
+ return register_filesystem(&proc_fs_type);
}
+
+static void __exit exit_proc_fs(void)
+{
+ unregister_filesystem(&proc_fs_type);
+}
+
+module_init(init_proc_fs)
+module_exit(exit_proc_fs)
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index b85483d3d..9e78119c9 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -491,23 +491,18 @@ static void qnx4_read_inode(struct inode *inode)
static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super);
-int __init init_qnx4_fs(void)
+static int __init init_qnx4_fs(void)
{
printk("QNX4 filesystem 0.2.2 registered.\n");
return register_filesystem(&qnx4_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
- return init_qnx4_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_qnx4_fs(void)
{
unregister_filesystem(&qnx4_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_qnx4_fs)
+module_exit(exit_qnx4_fs)
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index 15e80519f..7f5f2dee6 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -532,26 +532,19 @@ static struct super_operations romfs_ops = {
static DECLARE_FSTYPE_DEV(romfs_fs_type, "romfs", romfs_read_super);
-int __init init_romfs_fs(void)
+static int __init init_romfs_fs(void)
{
return register_filesystem(&romfs_fs_type);
}
-#ifdef MODULE
+static void __exit exit_romfs_fs(void)
+{
+ unregister_filesystem(&romfs_fs_type);
+}
/* Yes, works even as a module... :) */
EXPORT_NO_SYMBOLS;
-int
-init_module(void)
-{
- return init_romfs_fs();
-}
-
-void
-cleanup_module(void)
-{
- unregister_filesystem(&romfs_fs_type);
-}
-#endif
+module_init(init_romfs_fs)
+module_exit(exit_romfs_fs)
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 94923fbad..cef1e93cf 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -536,16 +536,7 @@ int smb_current_vmalloced;
static DECLARE_FSTYPE( smb_fs_type, "smbfs", smb_read_super, 0);
-int __init init_smb_fs(void)
-{
- return register_filesystem(&smb_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int
-init_module(void)
+static int __init init_smb_fs(void)
{
pr_debug("smbfs: init_module called\n");
@@ -558,8 +549,7 @@ init_module(void)
return init_smb_fs();
}
-void
-cleanup_module(void)
+static void __exit exit_smb_fs(void)
{
pr_debug("smbfs: cleanup_module called\n");
unregister_filesystem(&smb_fs_type);
@@ -570,4 +560,7 @@ cleanup_module(void)
#endif
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_smb_fs)
+module_exit(exit_smb_fs)
diff --git a/fs/super.c b/fs/super.c
index c365c556c..8aed9ce90 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -966,10 +966,10 @@ bdput_and_out:
sb->s_bdev = NULL;
sb->s_dev = 0;
sb->s_type = NULL;
-fsput_and_out:
- put_filesystem(fs_type);
if (bdev)
bdput(bdev);
+fsput_and_out:
+ put_filesystem(fs_type);
dput_and_out:
dput(dir_d);
up(&mount_sem);
@@ -1134,7 +1134,7 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
dentry = namei(dev_name);
retval = PTR_ERR(dentry);
if (IS_ERR(dentry))
- goto out;
+ goto fs_out;
inode = dentry->d_inode;
retval = -ENOTBLK;
@@ -1163,6 +1163,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
dput_and_out:
dput(dentry);
+fs_out:
+ put_filesystem(fstype);
out:
unlock_kernel();
return retval;
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index c1b95f28d..3367c02ef 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -1188,22 +1188,17 @@ int sysv_sync_inode(struct inode * inode)
static DECLARE_FSTYPE_DEV(sysv_fs_type, "sysv", sysv_read_super);
-int __init init_sysv_fs(void)
+static int __init init_sysv_fs(void)
{
return register_filesystem(&sysv_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
- return init_sysv_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_sysv_fs(void)
{
unregister_filesystem(&sysv_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_sysv_fs)
+module_exit(exit_sysv_fs)
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 761cdcb6c..81f59e9a3 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -127,62 +127,22 @@ struct udf_options
uid_t uid;
};
-#if defined(MODULE)
-
-/*
- * cleanup_module
- *
- * PURPOSE
- * Unregister the UDF filesystem type.
- *
- * DESCRIPTION
- * Clean-up before the module is unloaded.
- * This routine only applies when compiled as a module.
- *
- * HISTORY
- * July 1, 1997 - Andrew E. Mileski
- * Written, tested, and released.
- */
-int
-cleanup_module(void)
-{
- printk(KERN_NOTICE "udf: unregistering filesystem\n");
- return unregister_filesystem(&udf_fstype);
-}
-/*
- * init_module / init_udf_fs
- *
- * PURPOSE
- * Register the UDF filesystem type.
- *
- * HISTORY
- * July 1, 1997 - Andrew E. Mileski
- * Written, tested, and released.
- */
-int init_module(void)
-#else /* if !defined(MODULE) */
-int __init init_udf_fs(void)
-#endif
+static int __init init_udf_fs(void)
{
printk(KERN_NOTICE "udf: registering filesystem\n");
- {
- struct super_block sb;
- int size;
-
- size = sizeof(struct super_block) +
- (long)&sb.u - (long)&sb;
- if ( size < sizeof(struct udf_sb_info) )
- {
- printk(KERN_ERR "udf: Danger! Kernel was compiled without enough room for udf_sb_info\n");
- printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %lu\n",
- size, (unsigned long)sizeof(struct udf_sb_info));
- return 0;
- }
- }
return register_filesystem(&udf_fstype);
}
+static void __exit exit_udf_fs(void)
+{
+ printk(KERN_NOTICE "udf: unregistering filesystem\n");
+ unregister_filesystem(&udf_fstype);
+}
+
+module_init(init_udf_fs)
+module_exit(exit_udf_fs)
+
/*
* udf_parse_options
*
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index 28798e294..c1fb13c31 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -121,7 +121,7 @@ revalidate:
* version stamp to detect whether or
* not the directory has been modified
* during the copy operation. */
- unsigned long version = inode->i_version;
+ unsigned long version = filp->f_version;
UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino)))
UFSD(("namlen %u\n", ufs_get_de_namlen(de)))
@@ -129,7 +129,7 @@ revalidate:
filp->f_pos, SWAB32(de->d_ino));
if (error)
break;
- if (version != inode->i_version)
+ if (version != filp->f_version)
goto revalidate;
stored ++;
}
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 1fe9de9bd..d9620a1c6 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -951,22 +951,17 @@ static struct super_operations ufs_super_ops = {
static DECLARE_FSTYPE_DEV(ufs_fs_type, "ufs", ufs_read_super);
-int __init init_ufs_fs(void)
+static int __init init_ufs_fs(void)
{
return register_filesystem(&ufs_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
- return init_ufs_fs();
-}
-
-void cleanup_module(void)
+static void __exit exit_ufs_fs(void)
{
unregister_filesystem(&ufs_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ufs_fs)
+module_exit(exit_ufs_fs)
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index 61c832ed4..649ce16d4 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -435,22 +435,17 @@ out_noroot:
static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
-int __init init_umsdos_fs (void)
+static int __init init_umsdos_fs (void)
{
return register_filesystem (&umsdos_fs_type);
}
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module (void)
-{
- return init_umsdos_fs ();
-}
-
-void cleanup_module (void)
+static void __exit exit_umsdos_fs (void)
{
unregister_filesystem (&umsdos_fs_type);
}
-#endif
+EXPORT_NO_SYMBOLS;
+
+module_init(init_umsdos_fs)
+module_exit(exit_umsdos_fs)
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index 212494142..1f7c788f5 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -1292,16 +1292,3 @@ struct super_block *vfat_read_super(struct super_block *sb,void *data,
return res;
}
-
-#ifdef MODULE
-int init_module(void)
-{
- return init_vfat_fs();
-}
-
-void cleanup_module(void)
-{
- unregister_filesystem(&vfat_fs_type);
-}
-
-#endif /* ifdef MODULE */
diff --git a/fs/vfat/vfatfs_syms.c b/fs/vfat/vfatfs_syms.c
index d57d153ed..6f1a121e0 100644
--- a/fs/vfat/vfatfs_syms.c
+++ b/fs/vfat/vfatfs_syms.c
@@ -8,6 +8,7 @@
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mm.h>
#include <linux/msdos_fs.h>
@@ -22,8 +23,15 @@ EXPORT_SYMBOL(vfat_rename);
EXPORT_SYMBOL(vfat_read_super);
EXPORT_SYMBOL(vfat_lookup);
-int init_vfat_fs(void)
+static int __init init_vfat_fs(void)
{
return register_filesystem(&vfat_fs_type);
}
+static void __exit exit_vfat_fs(void)
+{
+ unregister_filesystem(&vfat_fs_type);
+}
+
+module_init(init_vfat_fs)
+module_exit(exit_vfat_fs)
diff --git a/include/asm-alpha/parport.h b/include/asm-alpha/parport.h
index d1099fd8c..c5ee7cbb2 100644
--- a/include/asm-alpha/parport.h
+++ b/include/asm-alpha/parport.h
@@ -1,7 +1,7 @@
/*
* parport.h: platform-specific PC-style parport initialisation
*
- * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This file should only be included by drivers/parport/parport_pc.c.
*/
@@ -9,53 +9,10 @@
#ifndef _ASM_AXP_PARPORT_H
#define _ASM_AXP_PARPORT_H 1
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- count += parport_pc_init_superio ();
-
- /* Probe all the likely ports. */
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
- count += parport_pc_init_pci (irq[0], dma[0]);
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_AXP_PARPORT_H) */
diff --git a/include/asm-arm/arch-cl7500/system.h b/include/asm-arm/arch-cl7500/system.h
index 24b64ede3..6daffc785 100644
--- a/include/asm-arm/arch-cl7500/system.h
+++ b/include/asm-arm/arch-cl7500/system.h
@@ -23,6 +23,4 @@
#define arch_power_off() do { } while (0)
-#define arch_power_off() do { } while (0)
-
#endif
diff --git a/include/asm-arm/arch-ebsa285/time.h b/include/asm-arm/arch-ebsa285/time.h
index b10bdd324..a837d8b0f 100644
--- a/include/asm-arm/arch-ebsa285/time.h
+++ b/include/asm-arm/arch-ebsa285/time.h
@@ -203,7 +203,8 @@ extern __inline__ void setup_timer(void)
{
int irq;
- if (machine_is_co285())
+ if (machine_is_co285() ||
+ machine_is_personal_server())
/*
* Add-in 21285s shouldn't access the RTC
*/
@@ -247,7 +248,9 @@ extern __inline__ void setup_timer(void)
rtc_base = 0;
}
- if (machine_is_ebsa285() || machine_is_co285()) {
+ if (machine_is_ebsa285() ||
+ machine_is_co285() ||
+ machine_is_personal_server()) {
gettimeoffset = timer1_gettimeoffset;
*CSR_TIMER1_CLR = 0;
diff --git a/include/asm-arm/parport.h b/include/asm-arm/parport.h
index 0f6c12151..bab49ad3a 100644
--- a/include/asm-arm/parport.h
+++ b/include/asm-arm/parport.h
@@ -1,7 +1,7 @@
/*
* parport.h: ARM-specific parport initialisation
*
- * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This file should only be included by drivers/parport/parport_pc.c.
*/
@@ -9,60 +9,10 @@
#ifndef __ASMARM_PARPORT_H
#define __ASMARM_PARPORT_H
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-static int __devinit parport_pc_init_superio(void);
-
-static int user_specified __maybe_initdata = 0;
-
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
-#ifdef CONFIG_PCI
- count += parport_pc_init_superio ();
-#endif
-
- /* Probe all the likely ports. */
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
-#ifdef CONFIG_PCI
- count += parport_pc_init_pci (irq[0], dma[0]);
-#endif
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASMARM_PARPORT_H) */
diff --git a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h
index 511a78005..0e6fcd87f 100644
--- a/include/asm-arm/procinfo.h
+++ b/include/asm-arm/procinfo.h
@@ -18,8 +18,10 @@ struct proc_info_item {
/*
* Note! struct processor is always defined if we're
* using MULTI_CPU, otherwise this entry is unused,
- * but still exists. NOTE! This structure is used
- * by assembler code! Check:
+ * but still exists.
+ *
+ * NOTE! The following structure is defined by assembly
+ * language, NOT C code. For more information, check:
* arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S
*/
struct proc_info_list {
diff --git a/include/asm-arm/ptrace.h b/include/asm-arm/ptrace.h
index 0b4714211..961363cc7 100644
--- a/include/asm-arm/ptrace.h
+++ b/include/asm-arm/ptrace.h
@@ -3,6 +3,11 @@
#include <asm/proc/ptrace.h>
+#define PTRACE_GETREGS 12
+#define PTRACE_SETREGS 13
+#define PTRACE_GETFPREGS 14
+#define PTRACE_SETFPREGS 15
+
#ifndef __ASSEMBLY__
#define pc_pointer(v) \
((v) & ~PCMASK)
diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h
index a0daf6edf..a43117330 100644
--- a/include/asm-arm/system.h
+++ b/include/asm-arm/system.h
@@ -16,22 +16,23 @@ extern unsigned int system_serial_high;
extern unsigned int __machine_arch_type;
/* see arch/arm/kernel/setup.c for a description of these */
-#define MACH_TYPE_EBSA110 0
-#define MACH_TYPE_RISCPC 1
-#define MACH_TYPE_NEXUSPCI 3
-#define MACH_TYPE_EBSA285 4
-#define MACH_TYPE_NETWINDER 5
-#define MACH_TYPE_CATS 6
-#define MACH_TYPE_TBOX 7
-#define MACH_TYPE_CO285 8
-#define MACH_TYPE_CLPS7110 9
-#define MACH_TYPE_ARCHIMEDES 10
-#define MACH_TYPE_A5K 11
-#define MACH_TYPE_ETOILE 12
-#define MACH_TYPE_LACIE_NAS 13
-#define MACH_TYPE_CLPS7500 14
-#define MACH_TYPE_SHARK 15
-#define MACH_TYPE_SA1100 16
+#define MACH_TYPE_EBSA110 0
+#define MACH_TYPE_RISCPC 1
+#define MACH_TYPE_NEXUSPCI 3
+#define MACH_TYPE_EBSA285 4
+#define MACH_TYPE_NETWINDER 5
+#define MACH_TYPE_CATS 6
+#define MACH_TYPE_TBOX 7
+#define MACH_TYPE_CO285 8
+#define MACH_TYPE_CLPS7110 9
+#define MACH_TYPE_ARCHIMEDES 10
+#define MACH_TYPE_A5K 11
+#define MACH_TYPE_ETOILE 12
+#define MACH_TYPE_LACIE_NAS 13
+#define MACH_TYPE_CLPS7500 14
+#define MACH_TYPE_SHARK 15
+#define MACH_TYPE_SA1100 16
+#define MACH_TYPE_PERSONAL_SERVER 17
/*
* Sort out a definition for machine_arch_type
@@ -41,6 +42,11 @@ extern unsigned int __machine_arch_type;
* 2. If two or more architectures are selected, then the selected
* machine_is_xxx() are variable, and the unselected machine_is_xxx()
* are constant zero.
+ *
+ * In general, you should use machine_is_xxxx() in your code, not:
+ * - switch (machine_arch_type) { }
+ * - if (machine_arch_type = xxxx)
+ * - __machine_arch_type
*/
#ifdef CONFIG_ARCH_EBSA110
# ifdef machine_arch_type
@@ -114,6 +120,54 @@ extern unsigned int __machine_arch_type;
# define machine_is_co285() (0)
#endif
+#ifdef CONFIG_ARCH_ARC
+# ifdef machine_arch_type
+# undef machine_arch_type
+# define machine_arch_type __machine_arch_type
+# else
+# define machine_arch_type MACH_TYPE_ARCHIMEDES
+# endif
+# define machine_is_arc() (machine_arch_type == MACH_TYPE_ARCHIMEDES)
+#else
+# define machine_is_arc() (0)
+#endif
+
+#ifdef CONFIG_ARCH_A5K
+# ifdef machine_arch_type
+# undef machine_arch_type
+# define machine_arch_type __machine_arch_type
+# else
+# define machine_arch_type MACH_TYPE_A5K
+# endif
+# define machine_is_a5k() (machine_arch_type == MACH_TYPE_A5K)
+#else
+# define machine_is_a5k() (0)
+#endif
+
+#ifdef CONFIG_ARCH_CLPS7500
+# ifdef machine_arch_type
+# undef machine_arch_type
+# define machine_arch_type __machine_arch_type
+# else
+# define machine_arch_type MACH_TYPE_CLPS7500
+# endif
+# define machine_is_clps7500() (machine_arch_type == MACH_TYPE_CLPS7500)
+#else
+# define machine_is_clps7500() (0)
+#endif
+
+#ifdef CONFIG_ARCH_SHARK
+# ifdef machine_arch_type
+# undef machine_arch_type
+# define machine_arch_type __machine_arch_type
+# else
+# define machine_arch_type MACH_TYPE_SHARK
+# endif
+# define machine_is_shark() (machine_arch_type == MACH_TYPE_SHARK)
+#else
+# define machine_is_shark() (0)
+#endif
+
#ifdef CONFIG_ARCH_SA1100
# ifdef machine_arch_type
# undef machine_arch_type
@@ -121,11 +175,23 @@ extern unsigned int __machine_arch_type;
# else
# define machine_arch_type MACH_TYPE_SA1100
# endif
-# define machine_is_sa1100() (machine_arch_type == MACH_TYPE_SA1100
+# define machine_is_sa1100() (machine_arch_type == MACH_TYPE_SA1100)
#else
# define machine_is_sa1100() (0)
#endif
+#ifdef CONFIG_PERSONAL_SERVER
+# ifdef machine_arch_type
+# undef machine_arch_type
+# define machine_arch_type __machine_arch_type
+# else
+# define machine_arch_type MACH_TYPE_PERSONAL_SERVER
+# endif
+# define machine_is_personal_server() (machine_arch_type == MACH_TYPE_PERSONAL_SERVER)
+#else
+# define machine_is_personal_server() (0)
+#endif
+
#ifndef machine_arch_type
#define machine_arch_type __machine_arch_type
#endif
diff --git a/include/asm-i386/mca_dma.h b/include/asm-i386/mca_dma.h
index 791152f3d..fb42da38d 100644
--- a/include/asm-i386/mca_dma.h
+++ b/include/asm-i386/mca_dma.h
@@ -55,17 +55,41 @@
#define MCA_DMA_MODE_16 0x40 /* 16 bit xfers */
+/**
+ * mca_enable_dma - channel to enable DMA on
+ * @dmanr: DMA channel
+ *
+ * Enable the MCA bus DMA on a channel. This can be called from
+ * IRQ context.
+ */
static __inline__ void mca_enable_dma(unsigned int dmanr)
{
outb(MCA_DMA_FN_RESET_MASK | dmanr, MCA_DMA_REG_FN);
}
+/**
+ * mca_disble_dma - channel to disable DMA on
+ * @dmanr: DMA channel
+ *
+ * Enable the MCA bus DMA on a channel. This can be called from
+ * IRQ context.
+ */
+
static __inline__ void mca_disable_dma(unsigned int dmanr)
{
outb(MCA_DMA_FN_MASK | dmanr, MCA_DMA_REG_FN);
}
+/**
+ * mca_set_dma_addr - load a 24bit DMA address
+ * @dmanr: DMA channel
+ * @a: 24bit bus address
+ *
+ * Load the address register in the DMA controller. This has a 24bit
+ * limitation (16Mb).
+ */
+
static __inline__ void mca_set_dma_addr(unsigned int dmanr, unsigned int a)
{
outb(MCA_DMA_FN_SET_ADDR | dmanr, MCA_DMA_REG_FN);
@@ -74,6 +98,14 @@ static __inline__ void mca_set_dma_addr(unsigned int dmanr, unsigned int a)
outb((a >> 16) & 0xff, MCA_DMA_REG_EXE);
}
+/**
+ * mca_get_dma_addr - load a 24bit DMA address
+ * @dmanr: DMA channel
+ *
+ * Read the address register in the DMA controller. This has a 24bit
+ * limitation (16Mb). The return is a bus address.
+ */
+
static __inline__ unsigned int mca_get_dma_addr(unsigned int dmanr)
{
unsigned int addr;
@@ -86,6 +118,15 @@ static __inline__ unsigned int mca_get_dma_addr(unsigned int dmanr)
return addr;
}
+/**
+ * mca_set_dma_count - load a 16bit transfer count
+ * @dmanr: DMA channel
+ * @count: count
+ *
+ * Set the DMA count for this channel. This can be up to 64Kbytes.
+ * Setting a count of zero will not do what you expect.
+ */
+
static __inline__ void mca_set_dma_count(unsigned int dmanr, unsigned int count)
{
count--; /* transfers one more than count -- correct for this */
@@ -95,6 +136,14 @@ static __inline__ void mca_set_dma_count(unsigned int dmanr, unsigned int count)
outb((count >> 8) & 0xff, MCA_DMA_REG_EXE);
}
+/**
+ * mca_get_dma_residue - get the remaining bytes to transfer
+ * @dmanr: DMA channel
+ *
+ * This function returns the number of bytes left to transfer
+ * on this DMA channel.
+ */
+
static __inline__ unsigned int mca_get_dma_residue(unsigned int dmanr)
{
unsigned short count;
@@ -106,6 +155,15 @@ static __inline__ unsigned int mca_get_dma_residue(unsigned int dmanr)
return count;
}
+/**
+ * mca_set_dma_io - set the port for an I/O transfer
+ * @dmanr: DMA channel
+ * @io_addr: an I/O port number
+ *
+ * Unlike the ISA bus DMA controllers the DMA on MCA bus can transfer
+ * with an I/O port target.
+ */
+
static __inline__ void mca_set_dma_io(unsigned int dmanr, unsigned int io_addr)
{
/*
@@ -117,6 +175,24 @@ static __inline__ void mca_set_dma_io(unsigned int dmanr, unsigned int io_addr)
outb((io_addr >> 8) & 0xff, MCA_DMA_REG_EXE);
}
+/**
+ * mca_set_dma_mode - set the DMA mode
+ * @dmanr: DMA channel
+ * @mode: The mode to set
+ *
+ * The DMA controller supports several modes. The mode values you can
+ * set are
+ *
+ * MCA_DMA_MODE_READ when reading from the DMA device.
+ *
+ * MCA_DMA_MODE_WRITE to writing to the DMA device.
+ *
+ * MCA_DMA_MODE_IO to do DMA to or from an I/O port.
+ *
+ * MCA_DMA_MODE_16 to do 16bit transfers.
+ *
+ */
+
static __inline__ void mca_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
outb(MCA_DMA_FN_SET_MODE | dmanr, MCA_DMA_REG_FN);
diff --git a/include/asm-i386/mman.h b/include/asm-i386/mman.h
index c5bd25f73..f953c436c 100644
--- a/include/asm-i386/mman.h
+++ b/include/asm-i386/mman.h
@@ -25,6 +25,12 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-i386/parport.h b/include/asm-i386/parport.h
index 2f7e8d295..fa0e321e4 100644
--- a/include/asm-i386/parport.h
+++ b/include/asm-i386/parport.h
@@ -1,7 +1,7 @@
/*
* parport.h: ia32-specific parport initialisation
*
- * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This file should only be included by drivers/parport/parport_pc.c.
*/
@@ -9,43 +9,10 @@
#ifndef _ASM_I386_PARPORT_H
#define _ASM_I386_PARPORT_H 1
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-static int parport_pc_init_pci(int irq, int dma);
-static int parport_pc_init_superio(void);
-
-static int user_specified __devinitdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- count += parport_pc_init_superio ();
-
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
-
- /* probe for other PCI parallel devices */
- count += parport_pc_init_pci (irq[0], dma[0]);
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
#endif /* !(_ASM_I386_PARPORT_H) */
diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h
index 6ec03679f..36f4e3176 100644
--- a/include/asm-i386/unistd.h
+++ b/include/asm-i386/unistd.h
@@ -222,6 +222,9 @@
#define __NR_setfsuid32 215
#define __NR_setfsgid32 216
#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_madvise1 219 /* delete when C lib stub is removed */
/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
diff --git a/include/asm-mips/mman.h b/include/asm-mips/mman.h
index e408affff..64dd2c674 100644
--- a/include/asm-mips/mman.h
+++ b/include/asm-mips/mman.h
@@ -56,6 +56,12 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-mips/parport.h b/include/asm-mips/parport.h
index 9ca9b628f..159e2345d 100644
--- a/include/asm-mips/parport.h
+++ b/include/asm-mips/parport.h
@@ -1,51 +1,16 @@
-/* $Id: parport.h,v 1.3 2000/03/02 02:37:13 ralf Exp $
+/* $Id$
*
- * parport.h: ia32-specific parport initialisation
- *
- * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This file should only be included by drivers/parport/parport_pc.c.
*/
#ifndef _ASM_PARPORT_H
-#define _ASM_PARPORT_H 1
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-static int parport_pc_init_pci(int irq, int dma);
-static int parport_pc_init_superio(void);
+#define _ASM_PARPORT_H
-static int user_specified __devinitdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- count += parport_pc_init_superio ();
-
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
-
- /* probe for other PCI parallel devices */
- count += parport_pc_init_pci (irq[0], dma[0]);
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
-#endif /* !(_ASM_PARPORT_H) */
+#endif /* _ASM_PARPORT_H */
diff --git a/include/asm-mips/sgialib.h b/include/asm-mips/sgialib.h
index c383fa6b6..42a390e89 100644
--- a/include/asm-mips/sgialib.h
+++ b/include/asm-mips/sgialib.h
@@ -1,4 +1,4 @@
-/* $Id: sgialib.h,v 1.3 1998/10/18 13:53:35 tsbogend Exp $
+/* $Id: sgialib.h,v 1.4 1999/02/25 20:55:07 tsbogend Exp $
* sgialib.h: SGI ARCS firmware interface library for the Linux kernel.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
@@ -17,10 +17,11 @@ extern char **prom_argv, **prom_envp;
extern int prom_flags;
#define PROM_FLAG_ARCS 1
-/* Init the PROM library and it's internal data structures. Called
+/*
+ * Init the PROM library and it's internal data structures. Called
* at boot time from head.S before start_kernel is invoked.
*/
-extern int prom_init(int argc, char **argv, char **envp);
+extern int prom_init(int argc, char **argv, char **envp, int *prom_vec);
/* Simple char-by-char console I/O. */
extern void prom_putchar(char c);
diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h
index c07c5154a..8ce76977a 100644
--- a/include/asm-mips/unistd.h
+++ b/include/asm-mips/unistd.h
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.19 2000/02/05 06:47:37 ralf Exp $
+/* $Id: unistd.h,v 1.20 2000/02/18 00:24:48 ralf Exp $
*
* 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
@@ -1203,11 +1203,13 @@
#define __NR_lstat64 (__NR_Linux + 214)
#define __NR_fstat64 (__NR_Linux + 215)
#define __NR_pivot_root (__NR_Linux + 216)
+#define __NR_mincore (__NR_Linux + 217)
+#define __NR_madvise (__NR_Linux + 218)
/*
* Offset of the last Linux flavoured syscall
*/
-#define __NR_Linux_syscalls 216
+#define __NR_Linux_syscalls 218
#ifndef _LANGUAGE_ASSEMBLY
diff --git a/include/asm-mips64/mman.h b/include/asm-mips64/mman.h
index edeaad87e..a7cba2bae 100644
--- a/include/asm-mips64/mman.h
+++ b/include/asm-mips64/mman.h
@@ -1,4 +1,4 @@
-/* $Id: mman.h,v 1.2 2000/02/04 23:09:08 ralf Exp $
+/* $Id: mman.h,v 1.3 2000/02/04 23:12:27 ralf Exp $
*
* 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
@@ -55,6 +55,12 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-mips64/parport.h b/include/asm-mips64/parport.h
index 57683df44..159e2345d 100644
--- a/include/asm-mips64/parport.h
+++ b/include/asm-mips64/parport.h
@@ -1,53 +1,16 @@
-/* $Id: parport.h,v 1.2 2000/02/18 00:24:48 ralf Exp $
+/* $Id$
*
- * parport.h: ia32-specific parport initialisation
- *
- * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This file should only be included by drivers/parport/parport_pc.c.
*/
#ifndef _ASM_PARPORT_H
-#define _ASM_PARPORT_H 1
-
-#include <linux/config.h>
-
-/* Maximum number of ports to support. It is useless to set this greater
- than PARPORT_MAX (in <linux/parport.h>). */
-#define PARPORT_PC_MAX_PORTS 8
-
-static int parport_pc_init_pci(int irq, int dma);
-static int parport_pc_init_superio(void);
+#define _ASM_PARPORT_H
-static int user_specified __devinitdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
- int count = 0, i = 0;
-
- if (io && *io) {
- /* Only probe the ports we were given. */
- user_specified = 1;
- do {
- if (!*io_hi) *io_hi = 0x400 + *io;
- if (parport_pc_probe_port(*(io++), *(io_hi++),
- *(irq++), *(dma++), NULL))
- count++;
- } while (*io && (++i < PARPORT_PC_MAX_PORTS));
- } else {
- count += parport_pc_init_superio ();
-
- if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
- count++;
- if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
- count++;
-
- /* probe for other PCI parallel devices */
- count += parport_pc_init_pci (irq[0], dma[0]);
- }
-
- return count;
+ return parport_pc_find_isa_ports (autoirq, autodma);
}
-#endif /* !(_ASM_PARPORT_H) */
+#endif /* _ASM_PARPORT_H */
diff --git a/include/asm-mips64/unistd.h b/include/asm-mips64/unistd.h
index 0308f3bf3..38c62a456 100644
--- a/include/asm-mips64/unistd.h
+++ b/include/asm-mips64/unistd.h
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.9 2000/02/05 06:47:37 ralf Exp $
+/* $Id: unistd.h,v 1.10 2000/02/18 00:24:49 ralf Exp $
*
* 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
@@ -1202,11 +1202,13 @@
#define __NR_Linux32_lstat64 (__NR_Linux32 + 214)
#define __NR_Linux32_fstat64 (__NR_Linux32 + 215)
#define __NR_Linux32_root_pivot (__NR_Linux32 + 216)
+#define __NR_Linux32_mincore (__NR_Linux32 + 217)
+#define __NR_Linux32_madvise (__NR_Linux32 + 218)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux32_syscalls 216
+#define __NR_Linux32_syscalls 218
/*
* Linux 64-bit syscalls are in the range from 5000 to 5999.
@@ -1423,11 +1425,13 @@
#define __NR_getpmsg (__NR_Linux + 208)
#define __NR_putpmsg (__NR_Linux + 209)
#define __NR_root_pivot (__NR_Linux + 210)
+#define __NR_mincore (__NR_Linux + 211)
+#define __NR_madvise (__NR_Linux + 212)
/*
* Offset of the last Linux flavoured syscall
*/
-#define __NR_Linux_syscalls 210
+#define __NR_Linux_syscalls 212
#ifndef _LANGUAGE_ASSEMBLY
diff --git a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h
index 3a4ac776a..25451ae0e 100644
--- a/include/asm-sparc/ide.h
+++ b/include/asm-sparc/ide.h
@@ -1,4 +1,4 @@
-/* $Id: ide.h,v 1.3 2000/03/10 04:46:47 davem Exp $
+/* $Id: ide.h,v 1.4 2000/03/12 03:56:12 davem Exp $
* ide.h: SPARC PCI specific IDE glue.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
diff --git a/include/asm-sparc/vaddrs.h b/include/asm-sparc/vaddrs.h
index 7f5c464a4..1c76a9b9d 100644
--- a/include/asm-sparc/vaddrs.h
+++ b/include/asm-sparc/vaddrs.h
@@ -1,4 +1,4 @@
-/* $Id: vaddrs.h,v 1.22 1999/04/20 13:22:55 anton Exp $ */
+/* $Id: vaddrs.h,v 1.23 2000/03/12 04:10:46 davem Exp $ */
#ifndef _SPARC_VADDRS_H
#define _SPARC_VADDRS_H
@@ -20,8 +20,8 @@
#define IOBASE_LEN 0x00300000 /* Length of the IO area */
#define IOBASE_END 0xfe300000
#define DVMA_VADDR 0xfff00000 /* Base area of the DVMA on suns */
-#define DVMA_LEN 0x00040000 /* Size of the DVMA address space */
-#define DVMA_END 0xfff40000
+#define DVMA_LEN 0x000c0000 /* Size of the DVMA address space */
+#define DVMA_END 0xfffc0000
/* IOMMU Mapping area, must be on a 16MB boundary! Note this
* doesn't count the DVMA areas, the prom lives between the
diff --git a/include/asm-sparc64/parport.h b/include/asm-sparc64/parport.h
index 259c755d5..dac004a6b 100644
--- a/include/asm-sparc64/parport.h
+++ b/include/asm-sparc64/parport.h
@@ -11,14 +11,6 @@
#include <asm/ebus.h>
#include <asm/ns87303.h>
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_init
-#define __maybe_initdata
-#else
-#define __maybe_init __init
-#define __maybe_initdata __initdata
-#endif
-
#define PARPORT_PC_MAX_PORTS PARPORT_MAX
static struct linux_ebus_dma *sparc_ebus_dmas[PARPORT_PC_MAX_PORTS];
@@ -108,12 +100,7 @@ get_dma_residue(unsigned int dmanr)
return res;
}
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __initdata = 0;
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int parport_pc_find_nonpci_ports (int autoirq, int autodma)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev;
@@ -155,7 +142,6 @@ parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
}
}
- count += parport_pc_init_pci(PARPORT_IRQ_AUTO, PARPORT_DMA_NONE);
return count;
}
diff --git a/include/linux/affs_fs.h b/include/linux/affs_fs.h
index ab3b8c078..60af0d40c 100644
--- a/include/linux/affs_fs.h
+++ b/include/linux/affs_fs.h
@@ -89,7 +89,6 @@ extern void affs_write_inode(struct inode *inode);
/* super.c */
extern int affs_fs(void);
-extern int init_affs_fs(void);
/* file.c */
diff --git a/include/linux/bfs_fs.h b/include/linux/bfs_fs.h
index 9525293cb..afe274a4c 100644
--- a/include/linux/bfs_fs.h
+++ b/include/linux/bfs_fs.h
@@ -78,9 +78,6 @@ struct bfs_super_block {
#ifdef __KERNEL__
-/* inode.c */
-extern int init_bfs_fs(void);
-
/* file.c */
extern struct inode_operations bfs_file_inops;
extern struct file_operations bfs_file_operations;
diff --git a/include/linux/blk.h b/include/linux/blk.h
index 1a818c4fd..f06443c04 100644
--- a/include/linux/blk.h
+++ b/include/linux/blk.h
@@ -18,17 +18,7 @@ extern spinlock_t io_request_lock;
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*/
-#define NR_REQUEST 128
-
-/*
- * This is used in the elevator algorithm. We don't prioritise reads
- * over writes any more --- although reads are more time-critical than
- * writes, by treating them equally we increase filesystem throughput.
- * This turns out to give better overall performance. -- sct
- */
-#define IN_ORDER(s1,s2) \
-((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
-(s1)->sector < (s2)->sector)))
+#define NR_REQUEST 256
/*
* Initialization functions.
@@ -102,7 +92,7 @@ extern inline void blkdev_dequeue_request(struct request * req)
{
if (req->cmd == READ)
req->q->elevator.read_pendings--;
- req->q->nr_segments -= req->nr_segments;
+ req->q->elevator.nr_segments -= req->nr_segments;
req->q = NULL;
}
list_del(&req->queue);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 166528473..8a05bdca2 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -43,6 +43,8 @@ struct request {
request_queue_t * q;
};
+#include <linux/elevator.h>
+
typedef int (merge_request_fn) (request_queue_t *q,
struct request *req,
struct buffer_head *bh,
@@ -57,21 +59,11 @@ typedef int (make_request_fn) (request_queue_t *q, int rw, struct buffer_head *b
typedef void (plug_device_fn) (request_queue_t *q, kdev_t device);
typedef void (unplug_device_fn) (void *q);
-typedef struct elevator_s
-{
- int sequence;
- int read_latency;
- int write_latency;
- int max_bomb_segments;
- int read_pendings;
-} elevator_t;
-
struct request_queue
{
struct list_head queue_head;
/* together with queue_head for cacheline sharing */
elevator_t elevator;
- unsigned int nr_segments;
request_fn_proc * request_fn;
merge_request_fn * back_merge_fn;
@@ -164,8 +156,6 @@ extern int * max_segments[MAX_BLKDEV];
#define MAX_READAHEAD 31
#define MIN_READAHEAD 3
-#define ELEVATOR_DEFAULTS ((elevator_t) { 0, NR_REQUEST>>1, NR_REQUEST<<5, 4, 0, })
-
#define blkdev_entry_to_request(entry) list_entry((entry), struct request, queue)
#define blkdev_entry_next_request(entry) blkdev_entry_to_request((entry)->next)
#define blkdev_entry_prev_request(entry) blkdev_entry_to_request((entry)->prev)
diff --git a/include/linux/compatmac.h b/include/linux/compatmac.h
new file mode 100644
index 000000000..07d8356d0
--- /dev/null
+++ b/include/linux/compatmac.h
@@ -0,0 +1,163 @@
+ /*
+ * This header tries to allow you to write 2.3-compatible drivers,
+ * but (using this header) still allows you to run them on 2.2 and
+ * 2.0 kernels.
+ *
+ * Sometimes, a #define replaces a "construct" that older kernels
+ * had. For example,
+ *
+ * DECLARE_MUTEX(name);
+ *
+ * replaces the older
+ *
+ * struct semaphore name = MUTEX;
+ *
+ * This file then declares the DECLARE_MUTEX macro to compile into the
+ * older version.
+ *
+ * In some cases, a macro or function changes the number of arguments.
+ * In that case, there is nothing we can do except define an access
+ * macro that provides the same functionality on both versions of Linux.
+ *
+ * This is the case for example with the "get_user" macro 2.0 kernels use:
+ *
+ * a = get_user (b);
+ *
+ * while newer kernels use
+ *
+ * get_user (a,b);
+ *
+ * This is unfortunate. We therefore define "Get_user (a,b)" which looks
+ * almost the same as the 2.2+ construct, and translates into the
+ * appropriate sequence for earlier constructs.
+ *
+ * Supported by this file are the 2.0 kernels, 2.2 kernels, and the
+ * most recent 2.3 kernel. 2.3 support will be dropped as soon when 2.4
+ * comes out. 2.0 support may someday be dropped. But then again, maybe
+ * not.
+ *
+ * I'll try to maintain this, provided that Linus agrees with the setup.
+ * Feel free to mail updates or suggestions.
+ *
+ * -- R.E.Wolff@BitWizard.nl
+ *
+ */
+
+#ifndef COMPATMAC_H
+#define COMPATMAC_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */
+#warning "Please use a 2.2.x kernel. "
+#else
+#if LINUX_VERSION_CODE < 0x020300 /* less than 2.3.x */
+#define TWO_TWO
+#else
+#define TWO_THREE
+#endif
+#endif
+#endif
+
+#ifdef TWO_ZERO
+
+/* Here is the section that makes the 2.2 compatible driver source
+ work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2,
+ and provide for compatibility stuff here if possible. */
+
+/* Some 200 days (on intel) */
+#define MAX_SCHEDULE_TIMEOUT ((long)(~0UL>>1))
+
+#include <linux/bios32.h>
+
+#define Get_user(a,b) a = get_user(b)
+#define Put_user(a,b) 0,put_user(a,b)
+#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
+
+static inline int copy_from_user(void *to,const void *from, int c)
+{
+ memcpy_fromfs(to, from, c);
+ return 0;
+}
+
+#define pci_present pcibios_present
+#define pci_read_config_word pcibios_read_config_word
+#define pci_read_config_dword pcibios_read_config_dword
+
+static inline unsigned char get_irq (unsigned char bus, unsigned char fn)
+{
+ unsigned char t;
+ pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t);
+ return t;
+}
+
+static inline void *ioremap(unsigned long base, long length)
+{
+ if (base < 0x100000) return (void *)base;
+ return vremap (base, length);
+}
+
+#define my_iounmap(x, b) (((long)x<0x100000)?0:vfree ((void*)x))
+
+#define capable(x) suser()
+
+#define queue_task queue_task_irq_off
+#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer)
+#define signal_pending(current) (current->signal & ~current->blocked)
+#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
+#define time_after(t1,t2) (((long)t1-t2) > 0)
+
+
+#define test_and_set_bit(nr, addr) set_bit(nr, addr)
+#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
+
+/* Not yet implemented on 2.0 */
+#define ASYNC_SPD_SHI -1
+#define ASYNC_SPD_WARP -1
+
+
+/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
+ to the "name" field that does exist. As long as the assignments are
+ done in the right order, there is nothing to worry about. */
+#define driver_name name
+
+/* Should be in a header somewhere. They are in tty.h on 2.2 */
+#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */
+#define TTY_HW_COOK_IN 15 /* in hardware - output and input */
+
+/* The return type of a "close" routine. */
+#define INT void
+#define NO_ERROR /* Nothing */
+
+#else
+
+/* The 2.2.x compatibility section. */
+#include <asm/uaccess.h>
+
+
+#define Get_user(a,b) get_user(a,b)
+#define Put_user(a,b) put_user(a,b)
+#define get_irq(pdev) pdev->irq
+
+#define INT int
+#define NO_ERROR 0
+
+#define my_iounmap(x,b) (iounmap((char *)(b)))
+
+#endif
+
+#ifndef TWO_THREE
+/* These are new in 2.3. The source now uses 2.3 syntax, and here is
+ the compatibility define... */
+#define wait_queue_head_t struct wait_queue *
+#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
+#define DECLARE_WAITQUEUE(wait, current) \
+ struct wait_queue wait = { current, NULL }
+
+#endif
+
+
+#endif
diff --git a/include/linux/efs_fs.h b/include/linux/efs_fs.h
index cc23e2e0c..ae049ed2f 100644
--- a/include/linux/efs_fs.h
+++ b/include/linux/efs_fs.h
@@ -49,10 +49,7 @@ extern struct inode_operations efs_dir_inode_operations;
extern struct file_operations efs_dir_operations;
extern struct address_space_operations efs_symlink_aops;
-extern int init_module(void);
-extern void cleanup_module(void);
extern struct super_block *efs_read_super(struct super_block *, void *, int);
-extern void efs_put_super(struct super_block *);
extern int efs_statfs(struct super_block *, struct statfs *);
extern void efs_read_inode(struct inode *);
@@ -61,7 +58,4 @@ extern efs_block_t efs_map_block(struct inode *, efs_block_t);
extern struct dentry *efs_lookup(struct inode *, struct dentry *);
extern int efs_bmap(struct inode *, int);
-extern int init_efs_fs(void);
-
#endif /* __EFS_FS_H__ */
-
diff --git a/include/linux/elevator.h b/include/linux/elevator.h
new file mode 100644
index 000000000..748a9834c
--- /dev/null
+++ b/include/linux/elevator.h
@@ -0,0 +1,124 @@
+#ifndef _LINUX_ELEVATOR_H
+#define _LINUX_ELEVATOR_H
+
+#define ELEVATOR_DEBUG
+
+struct elevator_s;
+typedef struct elevator_s elevator_t;
+
+typedef void (elevator_fn) (struct request *, elevator_t *,
+ struct list_head *,
+ struct list_head *, int);
+
+struct elevator_s
+{
+ int sequence;
+
+ int read_latency;
+ int write_latency;
+ int max_bomb_segments;
+
+ unsigned int nr_segments;
+ int read_pendings;
+
+ elevator_fn * elevator_fn;
+};
+
+#define ELEVATOR_DEFAULTS \
+((elevator_t) { \
+ 0, /* sequence */ \
+ \
+ 128, /* read_latency */ \
+ 8192, /* write_latency */ \
+ 4, /* max_bomb_segments */ \
+ \
+ 0, /* nr_segments */ \
+ 0, /* read_pendings */ \
+ \
+ elevator_default, /* elevator_fn */ \
+ })
+
+
+typedef struct blkelv_ioctl_arg_s {
+ void * queue_ID;
+ int read_latency;
+ int write_latency;
+ int max_bomb_segments;
+} blkelv_ioctl_arg_t;
+
+#define BLKELVGET _IO(0x12,106)
+#define BLKELVSET _IO(0x12,107)
+
+extern int blkelvget_ioctl(elevator_t *, blkelv_ioctl_arg_t *);
+extern int blkelvset_ioctl(elevator_t *, const blkelv_ioctl_arg_t *);
+
+
+extern void elevator_init(elevator_t *);
+
+#ifdef ELEVATOR_DEBUG
+extern void elevator_debug(request_queue_t *, kdev_t);
+#else
+#define elevator_debug(a,b) do { } while(0)
+#endif
+
+#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
+#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
+#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
+#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
+
+/*
+ * This is used in the elevator algorithm. We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance. -- sct
+ */
+#define IN_ORDER(s1,s2) \
+ ((((s1)->rq_dev == (s2)->rq_dev && \
+ (s1)->sector < (s2)->sector)) || \
+ (s1)->rq_dev < (s2)->rq_dev)
+
+static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
+{
+ if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
+ req->elevator_sequence = next->elevator_sequence;
+ if (req->cmd == READ)
+ e->read_pendings--;
+
+}
+
+static inline int elevator_sequence(elevator_t * e, int latency)
+{
+ return latency + e->sequence;
+}
+
+#define elevator_merge_before(q, req, lat) __elevator_merge((q), (req), (lat), 0)
+#define elevator_merge_after(q, req, lat) __elevator_merge((q), (req), (lat), 1)
+static inline void __elevator_merge(elevator_t * elevator, struct request * req, int latency, int after)
+{
+ int sequence = elevator_sequence(elevator, latency);
+ if (after)
+ sequence -= req->nr_segments;
+ if (elevator_sequence_before(sequence, req->elevator_sequence))
+ req->elevator_sequence = sequence;
+}
+
+static inline void elevator_account_request(elevator_t * elevator, struct request * req)
+{
+ elevator->sequence++;
+ if (req->cmd == READ)
+ elevator->read_pendings++;
+ elevator->nr_segments++;
+}
+
+static inline int elevator_request_latency(elevator_t * elevator, int rw)
+{
+ int latency;
+
+ latency = elevator->read_latency;
+ if (rw != READ)
+ latency = elevator->write_latency;
+
+ return latency;
+}
+
+#endif
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 43bf69f86..d763e4ba8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -232,7 +232,7 @@ struct fb_vblank {
#ifdef __KERNEL__
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
extern int GET_FB_IDX(kdev_t rdev);
#else
#define GET_FB_IDX(node) (MINOR(node))
@@ -283,15 +283,12 @@ struct fb_ops {
int (*fb_rasterimg)(struct fb_info *info, int start);
};
-/* fb_info flags */
-#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
-#define FBINFO_FLAG_OPEN 2 /* Has this been open already ? */
-
struct fb_info {
char modename[40]; /* default video mode */
kdev_t node;
int flags;
- int count; /* How many using the hardware */
+ int open; /* Has this been open already ? */
+#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0d472ec6e..bd37850b7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -160,6 +160,8 @@ extern int max_super_blocks, nr_super_blocks;
#define BLKSSZGET _IO(0x12,104)/* get block device sector size */
#if 0
#define BLKPG _IO(0x12,105)/* See blkpg.h */
+#define BLKELVGET _IO(0x12,106)/* elevator get */
+#define BLKELVSET _IO(0x12,107)/* elevator set */
/* This was here just to show that the number is taken -
probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
#endif
@@ -758,6 +760,7 @@ static inline int vfs_statfs(struct super_block *sb, struct statfs *buf)
if (!sb->s_op || !sb->s_op->statfs)
return -ENOSYS;
memset(buf, 0xff, sizeof(struct statfs));
+ buf->f_blocks = 0; /* Darn GNU df... */
return sb->s_op->statfs(sb, buf);
}
diff --git a/drivers/char/generic_serial.h b/include/linux/generic_serial.h
index 2e44bee95..c90f838a4 100644
--- a/drivers/char/generic_serial.h
+++ b/include/linux/generic_serial.h
@@ -12,7 +12,8 @@
#ifndef GENERIC_SERIAL_H
#define GENERIC_SERIAL_H
-#define RS_EVENT_WRITE_WAKEUP 0
+
+
struct real_driver {
void (*disable_tx_interrupts) (void *);
@@ -21,7 +22,7 @@ struct real_driver {
void (*enable_rx_interrupts) (void *);
int (*get_CD) (void *);
void (*shutdown_port) (void*);
- void (*set_real_termios) (void*);
+ int (*set_real_termios) (void*);
int (*chars_in_buffer) (void*);
void (*close) (void*);
void (*hungup) (void*);
diff --git a/include/linux/hfs_fs.h b/include/linux/hfs_fs.h
index 3c3913943..65392fc39 100644
--- a/include/linux/hfs_fs.h
+++ b/include/linux/hfs_fs.h
@@ -302,7 +302,6 @@ extern void hfs_sngl_ifill(struct inode *, ino_t, const int);
/* super.c */
extern struct super_block *hfs_read_super(struct super_block *,void *,int);
-extern int init_hfs_fs(void);
/* trans.c */
extern void hfs_colon2mac(struct hfs_name *, const char *, int);
diff --git a/include/linux/hpfs_fs.h b/include/linux/hpfs_fs.h
index 2b7926aba..a5028dd94 100644
--- a/include/linux/hpfs_fs.h
+++ b/include/linux/hpfs_fs.h
@@ -5,9 +5,4 @@
#define HPFS_SUPER_MAGIC 0xf995e849
-/* The entry point for a VFS */
-
-extern struct super_block *hpfs_read_super (struct super_block *, void *, int);
-extern int init_hpfs_fs(void);
-
#endif
diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h
index 18a265d18..f5d80a778 100644
--- a/include/linux/if_ppp.h
+++ b/include/linux/if_ppp.h
@@ -21,7 +21,7 @@
*/
/*
- * ==FILEVERSION 990806==
+ * ==FILEVERSION 20000115==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the above date.
@@ -60,8 +60,10 @@
#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */
#define SC_LOOP_TRAFFIC 0x00000200 /* send traffic to pppd */
#define SC_MULTILINK 0x00000400 /* do multilink encapsulation */
+#define SC_MP_SHORTSEQ 0x00000800 /* use short MP sequence numbers */
#define SC_COMP_RUN 0x00001000 /* compressor has been inited */
#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */
+#define SC_MP_XSHORTSEQ 0x00004000 /* transmit short MP seq numbers */
#define SC_DEBUG 0x00010000 /* enable debug messages */
#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */
#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */
@@ -133,7 +135,11 @@ struct ifpppcstatsreq {
#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */
#define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */
#define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */
-#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit */
+#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit/chan */
+#define PPPIOCSMRRU _IOW('t', 59, int) /* set multilink MRU */
+#define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */
+#define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */
+#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */
#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */
diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h
index 47fab8914..63543da77 100644
--- a/include/linux/iso_fs.h
+++ b/include/linux/iso_fs.h
@@ -186,7 +186,6 @@ int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
extern struct dentry *isofs_lookup(struct inode *, struct dentry *);
extern int isofs_get_block(struct inode *, long, struct buffer_head *, int);
extern int isofs_bmap(struct inode *, int);
-extern int init_iso9660_fs(void);
extern int isofs_lookup_grandparent(struct inode *, int);
extern struct inode_operations isofs_dir_inode_operations;
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index f42566696..4e880c937 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -10,7 +10,7 @@ extern int request_module(const char * name);
extern int exec_usermodehelper(char *program_path, char *argv[], char *envp[]);
#else
static inline int request_module(const char * name) { return -ENOSYS; }
-extern inline int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
+static inline int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
{
return -EACCES;
}
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index 6f63a54c4..1eca767b4 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -100,7 +100,6 @@ extern struct buffer_head * minix_getblk(struct inode *, int, int);
extern struct buffer_head * minix_bread(struct inode *, int, int);
extern void minix_truncate(struct inode *);
-extern int init_minix_fs(void);
extern int minix_sync_inode(struct inode *);
extern int minix_sync_file(struct file *, struct dentry *);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index bcf872ade..cb3a4f42f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -60,6 +60,7 @@ struct vm_area_struct {
struct vm_operations_struct * vm_ops;
unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
struct file * vm_file;
+ unsigned long vm_raend;
void * vm_private_data; /* was vm_pte (shared mem) */
};
@@ -83,10 +84,19 @@ struct vm_area_struct {
#define VM_EXECUTABLE 0x00001000
#define VM_LOCKED 0x00002000
-#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
+#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
+
+#define VM_SEQ_READ 0x00008000 /* App will access data sequentially */
+#define VM_RAND_READ 0x00010000 /* App will not benefit from clustered reads */
#define VM_STACK_FLAGS 0x00000177
+#define VM_READHINTMASK (VM_SEQ_READ | VM_RAND_READ)
+#define VM_ClearReadHint(v) (v)->vm_flags &= ~VM_READHINTMASK
+#define VM_NormalReadHint(v) (!((v)->vm_flags & VM_READHINTMASK))
+#define VM_SequentialReadHint(v) ((v)->vm_flags & VM_SEQ_READ)
+#define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ)
+
/*
* mapping from the currently active vm_flags protection bits (the
* low four bits) to a page protection mask..
@@ -105,7 +115,6 @@ struct vm_operations_struct {
void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
- void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page);
int (*swapout)(struct page *, struct file *);
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 17a64200e..82d3e62f4 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -15,8 +15,8 @@
#define MAX_ORDER 10
typedef struct free_area_struct {
- struct list_head free_list;
- unsigned int * map;
+ struct list_head free_list;
+ unsigned int *map;
} free_area_t;
struct pglist_data;
@@ -25,30 +25,31 @@ typedef struct zone_struct {
/*
* Commonly accessed fields:
*/
- spinlock_t lock;
- unsigned long offset;
- unsigned long free_pages;
- char low_on_memory;
- char zone_wake_kswapd;
- unsigned long pages_min, pages_low, pages_high;
+ spinlock_t lock;
+ unsigned long offset;
+ unsigned long free_pages;
+ char low_on_memory;
+ char zone_wake_kswapd;
+ unsigned long pages_min, pages_low, pages_high;
+ struct list_head lru_cache;
/*
* free areas of different sizes
*/
- free_area_t free_area[MAX_ORDER];
+ free_area_t free_area[MAX_ORDER];
/*
* rarely used fields:
*/
- char * name;
- unsigned long size;
+ char *name;
+ unsigned long size;
/*
* Discontig memory support fields.
*/
- struct pglist_data *zone_pgdat;
- unsigned long zone_start_paddr;
- unsigned long zone_start_mapnr;
- struct page * zone_mem_map;
+ struct pglist_data *zone_pgdat;
+ unsigned long zone_start_paddr;
+ unsigned long zone_start_mapnr;
+ struct page *zone_mem_map;
} zone_t;
#define ZONE_DMA 0
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index ce7837f1d..adcfb751d 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -272,14 +272,6 @@ extern ssize_t fat_file_read(struct file *, char *, size_t, loff_t *);
extern ssize_t fat_file_write(struct file *, const char *, size_t, loff_t *);
extern void fat_truncate(struct inode *inode);
-/* vfat.c */
-extern int init_vfat_fs(void);
-
-
-/* msdosfs_syms.c */
-extern int init_msdos_fs(void);
-extern struct file_system_type msdos_fs_type;
-
/* msdos.c */
extern struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent);
@@ -294,7 +286,6 @@ extern int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
struct inode *new_dir,struct dentry *new_dentry);
/* nls.c */
-extern int init_fat_nls(void);
extern struct fat_nls_table *fat_load_nls(int codepage);
/* tables.c */
diff --git a/include/linux/ncp_fs.h b/include/linux/ncp_fs.h
index b028f984c..87dfef8cb 100644
--- a/include/linux/ncp_fs.h
+++ b/include/linux/ncp_fs.h
@@ -228,7 +228,6 @@ struct super_block *ncp_read_super(struct super_block *, void *, int);
struct inode *ncp_iget(struct super_block *, struct ncp_entry_info *);
void ncp_update_inode(struct inode *, struct ncp_entry_info *);
void ncp_update_inode2(struct inode *, struct ncp_entry_info *);
-extern int init_ncp_fs(void);
/* linux/fs/ncpfs/dir.c */
extern struct inode_operations ncp_dir_inode_operations;
diff --git a/include/linux/nls.h b/include/linux/nls.h
index 9e39d3cd4..62f52e740 100644
--- a/include/linux/nls.h
+++ b/include/linux/nls.h
@@ -1,3 +1,5 @@
+#include <linux/init.h>
+
struct nls_unicode {
unsigned char uni1;
unsigned char uni2;
@@ -9,16 +11,13 @@ struct nls_table {
struct nls_unicode *charset2uni;
unsigned char *charset2lower;
unsigned char *charset2upper;
- void (*inc_use_count) (void);
- void (*dec_use_count) (void);
+ struct module *owner;
struct nls_table *next;
};
/* nls.c */
-extern int init_nls(void);
extern int register_nls(struct nls_table *);
extern int unregister_nls(struct nls_table *);
-extern struct nls_table *find_nls(char *);
extern struct nls_table *load_nls(char *);
extern void unload_nls(struct nls_table *);
extern struct nls_table *load_nls_default(void);
@@ -27,31 +26,3 @@ extern int utf8_mbtowc(__u16 *, const __u8 *, int);
extern int utf8_mbstowcs(__u16 *, const __u8 *, int);
extern int utf8_wctomb(__u8 *, __u16, int);
extern int utf8_wcstombs(__u8 *, const __u16 *, int);
-
-extern int init_nls_iso8859_1(void);
-extern int init_nls_iso8859_2(void);
-extern int init_nls_iso8859_3(void);
-extern int init_nls_iso8859_4(void);
-extern int init_nls_iso8859_5(void);
-extern int init_nls_iso8859_6(void);
-extern int init_nls_iso8859_7(void);
-extern int init_nls_iso8859_8(void);
-extern int init_nls_iso8859_9(void);
-extern int init_nls_iso8859_15(void);
-extern int init_nls_cp437(void);
-extern int init_nls_cp737(void);
-extern int init_nls_cp775(void);
-extern int init_nls_cp850(void);
-extern int init_nls_cp852(void);
-extern int init_nls_cp855(void);
-extern int init_nls_cp857(void);
-extern int init_nls_cp860(void);
-extern int init_nls_cp861(void);
-extern int init_nls_cp862(void);
-extern int init_nls_cp863(void);
-extern int init_nls_cp864(void);
-extern int init_nls_cp865(void);
-extern int init_nls_cp866(void);
-extern int init_nls_cp869(void);
-extern int init_nls_cp874(void);
-extern int init_nls_koi8_r(void);
diff --git a/include/linux/ntfs_fs.h b/include/linux/ntfs_fs.h
index acbfc2939..7b30dd7b0 100644
--- a/include/linux/ntfs_fs.h
+++ b/include/linux/ntfs_fs.h
@@ -1,7 +1,3 @@
#ifndef _LINUX_NTFS_FS_H
#define _LINUX_NTFS_FS_H
-
-int init_ntfs_fs(void);
-
#endif
-
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d32ff4828..413def0a9 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -577,6 +577,9 @@ unsigned int ss_vendor, unsigned int ss_device, const struct pci_dev *from)
extern inline void pci_set_master(struct pci_dev *dev) { }
extern inline int pci_enable_device(struct pci_dev *dev) { return -EIO; }
extern inline int pci_module_init(struct pci_driver *drv) { return -ENODEV; }
+extern inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUSY;}
+extern inline int pci_register_driver(struct pci_driver *drv) { return 0;}
+extern inline void pci_unregister_driver(struct pci_driver *drv) { }
#else
diff --git a/include/linux/personality.h b/include/linux/personality.h
index 31a42d62c..7607cf6ff 100644
--- a/include/linux/personality.h
+++ b/include/linux/personality.h
@@ -54,6 +54,8 @@ extern struct exec_domain default_exec_domain;
extern struct exec_domain *lookup_exec_domain(unsigned long personality);
extern int register_exec_domain(struct exec_domain *it);
extern int unregister_exec_domain(struct exec_domain *it);
+#define put_exec_domain(it) \
+ if (it && it->module) __MOD_DEC_USE_COUNT(it->module);
asmlinkage long sys_personality(unsigned long personality);
#endif /* _PERSONALITY_H */
diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h
index d1f9dd66c..fdcfb8f75 100644
--- a/include/linux/ppp_channel.h
+++ b/include/linux/ppp_channel.h
@@ -16,11 +16,9 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * ==FILEVERSION 990909==
+ * ==FILEVERSION 20000225==
*/
-/* $Id: ppp_channel.h,v 1.3 2000/01/31 01:42:48 davem Exp $ */
-
#include <linux/list.h>
#include <linux/skbuff.h>
@@ -30,18 +28,19 @@ struct ppp_channel_ops {
/* Send a packet (or multilink fragment) on this channel.
Returns 1 if it was accepted, 0 if not. */
int (*start_xmit)(struct ppp_channel *, struct sk_buff *);
-
+ /* Handle an ioctl call that has come in via /dev/ppp. */
+ int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
};
struct ppp_channel {
void *private; /* channel private data */
struct ppp_channel_ops *ops; /* operations for this channel */
- int xmit_qlen; /* length of transmit queue (bytes) */
- int speed; /* transfer rate (bytes/second) */
- int latency; /* overhead time in milliseconds */
+ int mtu; /* max transmit packet size */
int hdrlen; /* amount of headroom channel needs */
- struct list_head list; /* link in list of channels per unit */
void *ppp; /* opaque to channel */
+ /* the following are not used at present */
+ int speed; /* transfer rate (bytes/second) */
+ int latency; /* overhead time in milliseconds */
};
#ifdef __KERNEL__
@@ -57,10 +56,32 @@ extern void ppp_input(struct ppp_channel *, struct sk_buff *);
extern void ppp_input_error(struct ppp_channel *, int code);
/* Attach a channel to a given PPP unit. */
-extern int ppp_register_channel(struct ppp_channel *, int unit);
+extern int ppp_register_channel(struct ppp_channel *);
/* Detach a channel from its PPP unit (e.g. on hangup). */
extern void ppp_unregister_channel(struct ppp_channel *);
+/* Get the channel number for a channel */
+extern int ppp_channel_index(struct ppp_channel *);
+
+/*
+ * SMP locking notes:
+ * The channel code must ensure that when it calls ppp_unregister_channel,
+ * nothing is executing in any of the procedures above, for that
+ * channel. The generic layer will ensure that nothing is executing
+ * in the start_xmit and ioctl routines for the channel by the time
+ * that ppp_unregister_channel returns.
+ */
+
+/* The following are temporary compatibility stuff */
+ssize_t ppp_channel_read(struct ppp_channel *chan, struct file *file,
+ char *buf, size_t count);
+ssize_t ppp_channel_write(struct ppp_channel *chan, const char *buf,
+ size_t count);
+unsigned int ppp_channel_poll(struct ppp_channel *chan, struct file *file,
+ poll_table *wait);
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
+
#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h
index c506c90ab..a6eafea09 100644
--- a/include/linux/ppp_defs.h
+++ b/include/linux/ppp_defs.h
@@ -28,7 +28,7 @@
*/
/*
- * ==FILEVERSION 990114==
+ * ==FILEVERSION 20000114==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the above date.
@@ -70,12 +70,15 @@
#define PPP_IPX 0x2b /* IPX protocol */
#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_MP 0x3d /* Multilink protocol */
#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
+#define PPP_COMPFRAG 0xfb /* fragment compressed below bundle */
#define PPP_COMP 0xfd /* compressed packet */
#define PPP_IPCP 0x8021 /* IP Control Protocol */
#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
#define PPP_IPXCP 0x802b /* IPX Control Protocol */
#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_CCPFRAG 0x80fb /* CCP at link level (below MP bundle) */
#define PPP_CCP 0x80fd /* Compression Control Protocol */
#define PPP_LCP 0xc021 /* Link Control Protocol */
#define PPP_PAP 0xc023 /* Password Authentication Protocol */
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index f5927109a..f92195a3e 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -98,7 +98,6 @@ extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
extern struct super_block *proc_super_blocks;
extern struct super_block *proc_read_super(struct super_block *,void *,int);
-extern int init_proc_fs(void);
extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
extern int proc_match(int, const char *,struct proc_dir_entry *);
diff --git a/include/linux/qnx4_fs.h b/include/linux/qnx4_fs.h
index 4e68ee5a6..ad5ca5543 100644
--- a/include/linux/qnx4_fs.h
+++ b/include/linux/qnx4_fs.h
@@ -104,7 +104,6 @@ extern unsigned long qnx4_block_map(struct inode *inode, long iblock);
extern struct buffer_head *qnx4_getblk(struct inode *, int, int);
extern struct buffer_head *qnx4_bread(struct inode *, int, int);
-extern int init_qnx4_fs(void);
extern int qnx4_create(struct inode *dir, struct dentry *dentry, int mode);
extern struct inode_operations qnx4_file_inode_operations;
extern struct inode_operations qnx4_dir_inode_operations;
diff --git a/include/linux/romfs_fs.h b/include/linux/romfs_fs.h
index 844e22f99..8f452cbd4 100644
--- a/include/linux/romfs_fs.h
+++ b/include/linux/romfs_fs.h
@@ -56,7 +56,6 @@ struct romfs_inode {
#ifdef __KERNEL__
/* Not much now */
-extern int init_romfs_fs(void);
#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h
index d11573ca5..5df1ef295 100644
--- a/include/linux/smb_fs.h
+++ b/include/linux/smb_fs.h
@@ -120,7 +120,6 @@ int smb_revalidate_inode(struct dentry *);
int smb_notify_change(struct dentry *, struct iattr *);
unsigned long smb_invent_inos(unsigned long);
struct inode *smb_iget(struct super_block *, struct smb_fattr *);
-extern int init_smb_fs(void);
/* linux/fs/smbfs/proc.c */
__u32 smb_len(unsigned char *);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 2e7e27e25..d79fd68ef 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -67,7 +67,6 @@ FASTCALL(unsigned int nr_free_pages(void));
FASTCALL(unsigned int nr_free_buffer_pages(void));
FASTCALL(unsigned int nr_free_highpages(void));
extern int nr_lru_pages;
-extern struct list_head lru_cache;
extern atomic_t nr_async_pages;
extern struct address_space swapper_space;
extern atomic_t page_cache_size;
@@ -167,7 +166,7 @@ extern spinlock_t pagemap_lru_lock;
#define lru_cache_add(page) \
do { \
spin_lock(&pagemap_lru_lock); \
- list_add(&(page)->lru, &lru_cache); \
+ list_add(&(page)->lru, &page->zone->lru_cache); \
nr_lru_pages++; \
spin_unlock(&pagemap_lru_lock); \
} while (0)
diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h
index 9e177c3d4..d9c2557e5 100644
--- a/include/linux/sysv_fs.h
+++ b/include/linux/sysv_fs.h
@@ -377,7 +377,6 @@ extern unsigned long sysv_count_free_blocks(struct super_block *sb);
extern struct buffer_head * sysv_file_bread(struct inode *, int, int);
extern void sysv_truncate(struct inode *);
-extern int init_sysv_fs(void);
extern void sysv_write_inode(struct inode *);
extern int sysv_sync_inode(struct inode *);
extern int sysv_sync_file(struct file *, struct dentry *);
diff --git a/include/linux/telephony.h b/include/linux/telephony.h
index 082b885f1..c3e4c5573 100644
--- a/include/linux/telephony.h
+++ b/include/linux/telephony.h
@@ -158,6 +158,16 @@ typedef enum {
WSS = 12
} phone_codec;
+struct phone_codec_data
+{
+ phone_codec type;
+ unsigned short buf_min, buf_opt, buf_max;
+};
+
+#define PHONE_QUERY_CODEC _IOWR ('q', 0xA7, struct phone_codec_data *)
+#define PHONE_PSTN_LINETEST _IO ('q', 0xA8)
+
+
/******************************************************************************
*
* The exception structure allows us to multiplex multiple events onto the
diff --git a/include/linux/tty.h b/include/linux/tty.h
index df7b3fe7c..48b7e0819 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -305,6 +305,7 @@ struct tty_struct {
unsigned long canon_head;
unsigned int canon_column;
struct semaphore atomic_read;
+ spinlock_t read_lock;
};
/* tty magic number */
diff --git a/include/linux/udf_fs.h b/include/linux/udf_fs.h
index 47980eaaf..4f842ac30 100644
--- a/include/linux/udf_fs.h
+++ b/include/linux/udf_fs.h
@@ -60,7 +60,6 @@
/*
* Function prototypes (all other prototypes included in udfdecl.h)
*/
-extern int init_udf_fs(void);
#endif /* __KERNEL__ */
diff --git a/include/linux/ufs_fs.h b/include/linux/ufs_fs.h
index d7c14cd74..631791186 100644
--- a/include/linux/ufs_fs.h
+++ b/include/linux/ufs_fs.h
@@ -546,7 +546,6 @@ extern struct file_system_type ufs_fs_type;
extern void ufs_warning (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
extern void ufs_error (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
extern void ufs_panic (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
-extern int init_ufs_fs(void);
extern void ufs_write_super (struct super_block *);
/* symlink.c */
diff --git a/include/linux/umsdos_fs.h b/include/linux/umsdos_fs.h
index 6f113d28c..8dcb93719 100644
--- a/include/linux/umsdos_fs.h
+++ b/include/linux/umsdos_fs.h
@@ -177,7 +177,6 @@ extern struct inode_operations umsdos_dir_inode_operations;
extern struct inode_operations umsdos_rdir_inode_operations;
extern struct file_operations umsdos_dir_operations;
extern struct file_operations umsdos_rdir_operations;
-extern int init_umsdos_fs (void);
#include <linux/umsdos_fs.p>
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 69f0189aa..8326fb791 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -364,16 +364,20 @@ struct usb_driver {
*/
typedef int (*usb_device_irq)(int, void *, int, void *);
-/* --------------------------------------------------------------------------*
- * New USB Structures *
- * --------------------------------------------------------------------------*/
+/* -------------------------------------------------------------------------------------*
+ * New USB Structures *
+ * -------------------------------------------------------------------------------------*/
-#define USB_DISABLE_SPD 1
-#define USB_ISO_ASAP 2
-#define USB_URB_EARLY_COMPLETE 4
-#define USB_ASYNC_UNLINK 8
-typedef struct {
+#define USB_DISABLE_SPD 0x0001
+#define USB_ISO_ASAP 0x0002
+#define USB_URB_EARLY_COMPLETE 0x0004
+#define USB_ASYNC_UNLINK 0x0008
+#define USB_QUEUE_BULK 0x0010
+#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD!
+
+typedef struct
+{
unsigned int offset;
unsigned int length; // expected length
unsigned int actual_length;
@@ -402,6 +406,7 @@ typedef struct urb
int number_of_packets; // number of packets in this request (iso/irq only)
int interval; // polling interval (irq only)
int error_count; // number of errors in this transfer (iso only)
+ int timeout; // timeout (in jiffies)
//
void *context; // context for completion routine
usb_complete_t complete; // pointer to completion routine
@@ -445,6 +450,31 @@ typedef struct urb
(a)->start_frame=-1;\
} while (0)
+#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+ do {\
+ spin_lock_init(&(a)->lock);\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->setup_packet=c;\
+ (a)->transfer_buffer=d;\
+ (a)->transfer_buffer_length=e;\
+ (a)->complete=f;\
+ (a)->context=g;\
+ (a)->timeout=h;\
+ } while (0)
+
+#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+ do {\
+ spin_lock_init(&(a)->lock);\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->transfer_buffer=c;\
+ (a)->transfer_buffer_length=d;\
+ (a)->complete=e;\
+ (a)->context=f;\
+ (a)->timeout=g;\
+ } while (0)
+
purb_t usb_alloc_urb(int iso_packets);
void usb_free_urb (purb_t purb);
int usb_submit_urb(purb_t purb);
diff --git a/ipc/shm.c b/ipc/shm.c
index d614d3f2f..cb7e2c33a 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -435,6 +435,8 @@ static int shm_readdir (struct file *filp, void *dirent, filldir_t filldir)
default:
down(&shm_ids.sem);
for (; nr-2 <= shm_ids.max_id; nr++ ) {
+ if (nr-2 == zero_id)
+ continue;
if (!(shp = shm_get (nr-2)))
continue;
if (shp->shm_perm.mode & SHM_DEST)
@@ -462,8 +464,10 @@ static struct dentry *shm_lookup (struct inode *dir, struct dentry *dent)
down(&shm_ids.sem);
for(i = 0; i <= shm_ids.max_id; i++) {
+ if (i == zero_id)
+ continue;
if (!(shp = shm_lock(i)))
- continue;
+ continue;
if (!(shp->shm_perm.mode & SHM_DEST) &&
dent->d_name.len == shp->shm_namelen &&
strncmp(dent->d_name.name, shp->shm_name, shp->shm_namelen) == 0)
@@ -517,7 +521,7 @@ static pte_t **shm_alloc(unsigned long pages)
{
unsigned short dir = pages / PTRS_PER_PTE;
unsigned short last = pages % PTRS_PER_PTE;
- pte_t **ret, **ptr;
+ pte_t **ret, **ptr, *pte;
if (pages == 0)
return NULL;
@@ -531,7 +535,8 @@ static pte_t **shm_alloc(unsigned long pages)
*ptr = (pte_t *)__get_free_page (GFP_KERNEL);
if (!*ptr)
goto free;
- memset (*ptr, 0, PAGE_SIZE);
+ for (pte = *ptr; pte < *ptr + PTRS_PER_PTE; pte++)
+ pte_clear (pte);
}
/* The last one is probably not of PAGE_SIZE: we use kmalloc */
@@ -539,7 +544,8 @@ static pte_t **shm_alloc(unsigned long pages)
*ptr = kmalloc (last*sizeof(pte_t), GFP_KERNEL);
if (!*ptr)
goto free;
- memset (*ptr, 0, last*sizeof(pte_t));
+ for (pte = *ptr; pte < *ptr + last; pte++)
+ pte_clear (pte);
}
return ret;
@@ -696,9 +702,11 @@ asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
{
struct shmid_kernel *shp;
int err, id = 0;
+ static int count=0;
if (!shm_sb) {
- printk ("shmget: shm filesystem not mounted\n");
+ if(count++<5)
+ printk(KERN_WARNING "shmget: shm filesystem not mounted\n");
return -EINVAL;
}
@@ -886,9 +894,11 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
struct shm_setbuf setbuf;
struct shmid_kernel *shp;
int err, version;
+ static int count;
if (!shm_sb) {
- printk ("shmctl: shm filesystem not mounted\n");
+ if(count++<5)
+ printk (KERN_WARNING "shmctl: shm filesystem not mounted\n");
return -EINVAL;
}
@@ -1119,11 +1129,13 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
if (IS_ERR (name))
return PTR_ERR (name);
+ lock_kernel();
file = filp_open (name, O_RDWR, 0);
putname (name);
- if (IS_ERR (file))
+ if (IS_ERR (file)) {
+ unlock_kernel();
goto bad_file;
- lock_kernel();
+ }
*raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
(shmflg & SHM_RDONLY ? PROT_READ :
PROT_READ | PROT_WRITE), flags, 0);
diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c
index 1ee1eee4d..111a3d69c 100644
--- a/kernel/exec_domain.c
+++ b/kernel/exec_domain.c
@@ -23,7 +23,7 @@ struct exec_domain default_exec_domain = {
};
static struct exec_domain *exec_domains = &default_exec_domain;
-
+static spinlock_t exec_domains_lock = SPIN_LOCK_UNLOCKED;
static asmlinkage void no_lcall7(int segment, struct pt_regs * regs)
{
@@ -32,15 +32,10 @@ static asmlinkage void no_lcall7(int segment, struct pt_regs * regs)
* personality set incorrectly. Check to see whether SVr4 is available,
* and use it, otherwise give the user a SEGV.
*/
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
-
+ put_exec_domain(current->exec_domain);
current->personality = PER_SVR4;
current->exec_domain = lookup_exec_domain(current->personality);
- if (current->exec_domain && current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
-
if (current->exec_domain && current->exec_domain->handler
&& current->exec_domain->handler != no_lcall7) {
current->exec_domain->handler(segment, regs);
@@ -55,10 +50,15 @@ struct exec_domain *lookup_exec_domain(unsigned long personality)
unsigned long pers = personality & PER_MASK;
struct exec_domain *it;
+ spin_lock(&exec_domains_lock);
for (it=exec_domains; it; it=it->next)
- if (pers >= it->pers_low
- && pers <= it->pers_high)
+ if (pers >= it->pers_low && pers <= it->pers_high) {
+ if (!try_inc_mod_count(it->module))
+ continue;
+ spin_unlock(&exec_domains_lock);
return it;
+ }
+ spin_unlock(&exec_domains_lock);
/* Should never get this far. */
printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
@@ -73,11 +73,15 @@ int register_exec_domain(struct exec_domain *it)
return -EINVAL;
if (it->next)
return -EBUSY;
+ spin_lock(&exec_domains_lock);
for (tmp=exec_domains; tmp; tmp=tmp->next)
- if (tmp == it)
+ if (tmp == it) {
+ spin_unlock(&exec_domains_lock);
return -EBUSY;
+ }
it->next = exec_domains;
exec_domains = it;
+ spin_unlock(&exec_domains_lock);
return 0;
}
@@ -86,14 +90,17 @@ int unregister_exec_domain(struct exec_domain *it)
struct exec_domain ** tmp;
tmp = &exec_domains;
+ spin_lock(&exec_domains_lock);
while (*tmp) {
if (it == *tmp) {
*tmp = it->next;
it->next = NULL;
+ spin_unlock(&exec_domains_lock);
return 0;
}
tmp = &(*tmp)->next;
}
+ spin_unlock(&exec_domains_lock);
return -EINVAL;
}
@@ -113,12 +120,9 @@ asmlinkage long sys_personality(unsigned long personality)
goto out;
old_personality = current->personality;
- if (current->exec_domain && current->exec_domain->module)
- __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ put_exec_domain(current->exec_domain);
current->personality = personality;
current->exec_domain = it;
- if (current->exec_domain->module)
- __MOD_INC_USE_COUNT(current->exec_domain->module);
ret = old_personality;
out:
unlock_kernel();
@@ -130,9 +134,11 @@ int get_exec_domain_list(char * page)
int len = 0;
struct exec_domain * e;
+ spin_lock(&exec_domains_lock);
for (e=exec_domains; e && len < PAGE_SIZE - 80; e=e->next)
len += sprintf(page+len, "%d-%d\t%-16s\t[%s]\n",
e->pers_low, e->pers_high, e->name,
e->module ? e->module->name : "kernel");
+ spin_unlock(&exec_domains_lock);
return len;
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 333981ab9..4684b8c37 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -55,8 +55,8 @@ void release(struct task_struct * p)
* was given away by the parent in the first place.)
*/
current->counter += p->counter;
- if (current->counter > current->priority)
- current->counter = current->priority;
+ if (current->counter >= current->priority*2)
+ current->counter = current->priority*2-1;
free_task_struct(p);
} else {
printk("task releasing itself\n");
@@ -425,8 +425,7 @@ fake_volatile:
tsk->exit_code = code;
exit_notify();
task_unlock(tsk);
- if (tsk->exec_domain && tsk->exec_domain->module)
- __MOD_DEC_USE_COUNT(tsk->exec_domain->module);
+ put_exec_domain(tsk->exec_domain);
if (tsk->binfmt && tsk->binfmt->module)
__MOD_DEC_USE_COUNT(tsk->binfmt->module);
schedule();
diff --git a/kernel/fork.c b/kernel/fork.c
index f30adb908..2fbb08e32 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -647,8 +647,11 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
p->run_list.next = NULL;
p->run_list.prev = NULL;
- if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT))
- p->p_pptr = p->p_opptr = current;
+ if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) {
+ p->p_opptr = current;
+ if (!(current->flags & PF_PTRACED))
+ p->p_pptr = current;
+ }
p->p_cptr = NULL;
init_waitqueue_head(&p->wait_chldexit);
p->vfork_sem = NULL;
@@ -748,8 +751,7 @@ bad_fork_cleanup_fs:
bad_fork_cleanup_files:
exit_files(p); /* blocking */
bad_fork_cleanup:
- if (p->exec_domain && p->exec_domain->module)
- __MOD_DEC_USE_COUNT(p->exec_domain->module);
+ put_exec_domain(p->exec_domain);
if (p->binfmt && p->binfmt->module)
__MOD_DEC_USE_COUNT(p->binfmt->module);
bad_fork_cleanup_count:
diff --git a/kernel/module.c b/kernel/module.c
index 83b000342..a25596c51 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1040,4 +1040,9 @@ sys_get_kernel_syms(struct kernel_sym *table)
return -ENOSYS;
}
+int try_inc_mod_count(struct module *mod)
+{
+ return 1;
+}
+
#endif /* CONFIG_MODULES */
diff --git a/kernel/pm.c b/kernel/pm.c
index b2f60d65f..369d9b954 100644
--- a/kernel/pm.c
+++ b/kernel/pm.c
@@ -28,9 +28,17 @@ int pm_active = 0;
static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(pm_devs);
-/*
- * Register a device with power management
+/**
+ * pm_register - register a device with power management
+ * @type: The device type
+ * @id: Device ID
+ * @callback: Callback function
+ *
+ * Add a device to the list of devices that wish to be notified about
+ * power management events. A pm_dev structure is returnd on success,
+ * on failure the return is NULL
*/
+
struct pm_dev *pm_register(pm_dev_t type,
unsigned long id,
pm_callback callback)
@@ -51,9 +59,14 @@ struct pm_dev *pm_register(pm_dev_t type,
return dev;
}
-/*
- * Unregister a device with power management
+/**
+ * pm_unregister - unregister a device with power management
+ * @dev: device to unregister
+ *
+ * Remove a device from the power management notification lists. The
+ * dev passed must be a handle previously returned by pm_register.
*/
+
void pm_unregister(struct pm_dev *dev)
{
if (dev) {
@@ -67,9 +80,16 @@ void pm_unregister(struct pm_dev *dev)
}
}
-/*
- * Unregister all devices with matching callback
+/**
+ * pm_unregister_all - unregister all devices with matching callback
+ * @callback: callback function pointer
+ *
+ * Unregister every device that would call the callback passed. This
+ * is primarily meant as a helper function for loadable modules. It
+ * enables a module to give up all its managed devices without keeping
+ * its own private list.
*/
+
void pm_unregister_all(pm_callback callback)
{
struct list_head *entry;
@@ -86,9 +106,21 @@ void pm_unregister_all(pm_callback callback)
}
}
-/*
- * Send request to a single device
+/**
+ * pm_send - send request to a single device
+ * @dev: device to send to
+ * @rqst: power management request
+ * @data: data for the callback
+ *
+ * Issue a power management request to a given device. The
+ * PM_SUSPEND and PM_RESUME events are handled specially. The
+ * data field must hold the intented next state. No call is made
+ * if the state matches.
+ *
+ * BUGS: what stops two power management requests occuring in parallel
+ * and conflicting.
*/
+
int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
{
int status = 0;
@@ -138,9 +170,26 @@ static void pm_undo_all(struct pm_dev *last)
}
}
-/*
- * Send a request to all devices
+/**
+ * pm_send - send request to all managed device
+ * @rqst: power management request
+ * @data: data for the callback
+ *
+ * Issue a power management request to a all devices. The
+ * PM_SUSPEND events are handled specially. Any device is
+ * permitted to fail a suspend by returning a non zero (error)
+ * value from its callback function. If any device vetoes a
+ * suspend request then all other devices that have suspended
+ * during the processing of this request are restored to their
+ * previous state.
+ *
+ * Zero is returned on success. If a suspend fails then the status
+ * from the device that vetoes the suspend is returned.
+ *
+ * BUGS: what stops two power management requests occuring in parallel
+ * and conflicting.
*/
+
int pm_send_all(pm_request_t rqst, void *data)
{
struct list_head *entry = pm_devs.next;
@@ -162,9 +211,19 @@ int pm_send_all(pm_request_t rqst, void *data)
return 0;
}
-/*
- * Find a device
+/**
+ * pm_find - find a device
+ * @type: type of device
+ * @from: Where to start looking
+ *
+ * Scan the power management list for devices of a specific type. The
+ * return value for a matching device may be passed to further calls
+ * to this function to find further matches. A NULL indicates the end
+ * of the list.
+ *
+ * To search from the beginning pass NULL as the from value.
*/
+
struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
{
struct list_head *entry = from ? from->entry.next:pm_devs.next;
diff --git a/mm/filemap.c b/mm/filemap.c
index b5febc2e5..3fb7d011c 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -25,6 +25,7 @@
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
+#include <asm/mman.h>
#include <linux/highmem.h>
@@ -220,15 +221,18 @@ int shrink_mmap(int priority, int gfp_mask, zone_t *zone)
struct list_head * page_lru, * dispose;
struct page * page;
+ if (!zone)
+ BUG();
+
count = nr_lru_pages / (priority+1);
spin_lock(&pagemap_lru_lock);
- while (count > 0 && (page_lru = lru_cache.prev) != &lru_cache) {
+ while (count > 0 && (page_lru = zone->lru_cache.prev) != &zone->lru_cache) {
page = list_entry(page_lru, struct page, lru);
list_del(page_lru);
- dispose = &lru_cache;
+ dispose = &zone->lru_cache;
if (test_and_clear_bit(PG_referenced, &page->flags))
/* Roll the page at the top of the lru list,
* we could also be more aggressive putting
@@ -355,8 +359,8 @@ made_buffer_progress:
nr_lru_pages--;
out:
- list_splice(&young, &lru_cache);
- list_splice(&old, lru_cache.prev);
+ list_splice(&young, &zone->lru_cache);
+ list_splice(&old, zone->lru_cache.prev);
spin_unlock(&pagemap_lru_lock);
@@ -1294,6 +1298,61 @@ out:
}
/*
+ * Read-ahead and flush behind for MADV_SEQUENTIAL areas. Since we are
+ * sure this is sequential access, we don't need a flexible read-ahead
+ * window size -- we can always use a large fixed size window.
+ */
+static void nopage_sequential_readahead(struct vm_area_struct * vma,
+ unsigned long pgoff, unsigned long filesize)
+{
+ unsigned long ra_window;
+
+ ra_window = get_max_readahead(vma->vm_file->f_dentry->d_inode);
+ ra_window = CLUSTER_OFFSET(ra_window + CLUSTER_PAGES - 1);
+
+ /* vm_raend is zero if we haven't read ahead in this area yet. */
+ if (vma->vm_raend == 0)
+ vma->vm_raend = vma->vm_pgoff + ra_window;
+
+ /*
+ * If we've just faulted the page half-way through our window,
+ * then schedule reads for the next window, and release the
+ * pages in the previous window.
+ */
+ if ((pgoff + (ra_window >> 1)) == vma->vm_raend) {
+ unsigned long start = vma->vm_pgoff + vma->vm_raend;
+ unsigned long end = start + ra_window;
+
+ if (end > ((vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff))
+ end = (vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (start > end)
+ return;
+
+ while ((start < end) && (start < filesize)) {
+ if (read_cluster_nonblocking(vma->vm_file,
+ start, filesize) < 0)
+ break;
+ start += CLUSTER_PAGES;
+ }
+ run_task_queue(&tq_disk);
+
+ /* if we're far enough past the beginning of this area,
+ recycle pages that are in the previous window. */
+ if (vma->vm_raend > (vma->vm_pgoff + ra_window + ra_window)) {
+ unsigned long window = ra_window << PAGE_SHIFT;
+
+ end = vma->vm_start + (vma->vm_raend << PAGE_SHIFT);
+ end -= window + window;
+ filemap_sync(vma, end - window, window, MS_INVALIDATE);
+ }
+
+ vma->vm_raend += ra_window;
+ }
+
+ return;
+}
+
+/*
* filemap_nopage() is invoked via the vma operations vector for a
* mapped memory region to read in file data during a page fault.
*
@@ -1339,6 +1398,12 @@ retry_find:
goto page_not_uptodate;
success:
+ /*
+ * Try read-ahead for sequential areas.
+ */
+ if (VM_SequentialReadHint(area))
+ nopage_sequential_readahead(area, pgoff, size);
+
/*
* Found the page and have a reference on it, need to check sharing
* and possibly copy it over to another page..
@@ -1355,7 +1420,7 @@ success:
page_cache_release(page);
return new_page;
}
-
+
flush_page_to_ram(old_page);
return old_page;
@@ -1367,7 +1432,7 @@ no_cached_page:
* Otherwise, we're off the end of a privately mapped file,
* so we need to map a zero page.
*/
- if (pgoff < size)
+ if ((pgoff < size) && !VM_RandomReadHint(area))
error = read_cluster_nonblocking(file, pgoff, size);
else
error = page_cache_read(file, pgoff);
@@ -1646,7 +1711,6 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
return 0;
}
-
/*
* The msync() system call.
*/
@@ -1727,6 +1791,505 @@ out:
return error;
}
+static inline void setup_read_behavior(struct vm_area_struct * vma,
+ int behavior)
+{
+ VM_ClearReadHint(vma);
+ switch(behavior) {
+ case MADV_SEQUENTIAL:
+ vma->vm_flags |= VM_SEQ_READ;
+ break;
+ case MADV_RANDOM:
+ vma->vm_flags |= VM_RAND_READ;
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static long madvise_fixup_start(struct vm_area_struct * vma,
+ unsigned long end, int behavior)
+{
+ struct vm_area_struct * n;
+
+ n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!n)
+ return -EAGAIN;
+ *n = *vma;
+ n->vm_end = end;
+ setup_read_behavior(n, behavior);
+ n->vm_raend = 0;
+ get_file(n->vm_file);
+ if (n->vm_ops && n->vm_ops->open)
+ n->vm_ops->open(n);
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT;
+ vma->vm_start = end;
+ insert_vm_struct(current->mm, n);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+static long madvise_fixup_end(struct vm_area_struct * vma,
+ unsigned long start, int behavior)
+{
+ struct vm_area_struct * n;
+
+ n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!n)
+ return -EAGAIN;
+ *n = *vma;
+ n->vm_start = start;
+ n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
+ setup_read_behavior(n, behavior);
+ n->vm_raend = 0;
+ get_file(n->vm_file);
+ if (n->vm_ops && n->vm_ops->open)
+ n->vm_ops->open(n);
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_end = start;
+ insert_vm_struct(current->mm, n);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+static long madvise_fixup_middle(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end, int behavior)
+{
+ struct vm_area_struct * left, * right;
+
+ left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!left)
+ return -EAGAIN;
+ right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!right) {
+ kmem_cache_free(vm_area_cachep, left);
+ return -EAGAIN;
+ }
+ *left = *vma;
+ *right = *vma;
+ left->vm_end = start;
+ right->vm_start = end;
+ right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
+ atomic_add(2, &vma->vm_file->f_count);
+
+ if (vma->vm_ops && vma->vm_ops->open) {
+ vma->vm_ops->open(left);
+ vma->vm_ops->open(right);
+ }
+ vmlist_modify_lock(vma->vm_mm);
+ vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT;
+ vma->vm_start = start;
+ vma->vm_end = end;
+ setup_read_behavior(vma, behavior);
+ vma->vm_raend = 0;
+ insert_vm_struct(current->mm, left);
+ insert_vm_struct(current->mm, right);
+ vmlist_modify_unlock(vma->vm_mm);
+ return 0;
+}
+
+/*
+ * We can potentially split a vm area into separate
+ * areas, each area with its own behavior.
+ */
+static long madvise_behavior(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end, int behavior)
+{
+ int error = 0;
+
+ /* This caps the number of vma's this process can own */
+ if (vma->vm_mm->map_count > MAX_MAP_COUNT)
+ return -ENOMEM;
+
+ if (start == vma->vm_start) {
+ if (end == vma->vm_end) {
+ setup_read_behavior(vma, behavior);
+ vma->vm_raend = 0;
+ } else
+ error = madvise_fixup_start(vma, end, behavior);
+ } else {
+ if (end == vma->vm_end)
+ error = madvise_fixup_end(vma, start, behavior);
+ else
+ error = madvise_fixup_middle(vma, start, end, behavior);
+ }
+
+ return error;
+}
+
+/*
+ * Schedule all required I/O operations, then run the disk queue
+ * to make sure they are started. Do not wait for completion.
+ */
+static long madvise_willneed(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end)
+{
+ long error = -EBADF;
+ struct file * file;
+ unsigned long size, rlim_rss;
+
+ /* Doesn't work if there's no mapped file. */
+ if (!vma->vm_file)
+ return error;
+ file = vma->vm_file;
+ size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT;
+
+ start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
+ end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+ /* Make sure this doesn't exceed the process's max rss. */
+ error = -EIO;
+ rlim_rss = current->rlim ? current->rlim[RLIMIT_RSS].rlim_cur :
+ LONG_MAX; /* default: see resource.h */
+ if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
+ return error;
+
+ /* round to cluster boundaries if this isn't a "random" area. */
+ if (!VM_RandomReadHint(vma)) {
+ start = CLUSTER_OFFSET(start);
+ end = CLUSTER_OFFSET(end + CLUSTER_PAGES - 1);
+
+ while ((start < end) && (start < size)) {
+ error = read_cluster_nonblocking(file, start, size);
+ start += CLUSTER_PAGES;
+ if (error < 0)
+ break;
+ }
+ } else {
+ while ((start < end) && (start < size)) {
+ error = page_cache_read(file, start);
+ start++;
+ if (error < 0)
+ break;
+ }
+ }
+
+ /* Don't wait for someone else to push these requests. */
+ run_task_queue(&tq_disk);
+
+ return error;
+}
+
+/*
+ * Application no longer needs these pages. If the pages are dirty,
+ * it's OK to just throw them away. The app will be more careful about
+ * data it wants to keep. Be sure to free swap resources too. The
+ * zap_page_range call sets things up for shrink_mmap to actually free
+ * these pages later if no one else has touched them in the meantime,
+ * although we could add these pages to a global reuse list for
+ * shrink_mmap to pick up before reclaiming other pages.
+ *
+ * NB: This interface discards data rather than pushes it out to swap,
+ * as some implementations do. This has performance implications for
+ * applications like large transactional databases which want to discard
+ * pages in anonymous maps after committing to backing store the data
+ * that was kept in them. There is no reason to write this data out to
+ * the swap area if the application is discarding it.
+ *
+ * An interface that causes the system to free clean pages and flush
+ * dirty pages is already available as msync(MS_INVALIDATE).
+ */
+static long madvise_dontneed(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end)
+{
+ if (vma->vm_flags & VM_LOCKED)
+ return -EINVAL;
+
+ lock_kernel(); /* is this really necessary? */
+
+ flush_cache_range(vma->vm_mm, start, end);
+ zap_page_range(vma->vm_mm, start, end - start);
+ flush_tlb_range(vma->vm_mm, start, end);
+
+ unlock_kernel();
+ return 0;
+}
+
+static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
+ unsigned long end, int behavior)
+{
+ long error = -EBADF;
+
+ switch (behavior) {
+ case MADV_NORMAL:
+ case MADV_SEQUENTIAL:
+ case MADV_RANDOM:
+ error = madvise_behavior(vma, start, end, behavior);
+ break;
+
+ case MADV_WILLNEED:
+ error = madvise_willneed(vma, start, end);
+ break;
+
+ case MADV_DONTNEED:
+ error = madvise_dontneed(vma, start, end);
+ break;
+
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * The madvise(2) system call.
+ *
+ * Applications can use madvise() to advise the kernel how it should
+ * handle paging I/O in this VM area. The idea is to help the kernel
+ * use appropriate read-ahead and caching techniques. The information
+ * provided is advisory only, and can be safely disregarded by the
+ * kernel without affecting the correct operation of the application.
+ *
+ * behavior values:
+ * MADV_NORMAL - the default behavior is to read clusters. This
+ * results in some read-ahead and read-behind.
+ * MADV_RANDOM - the system should read the minimum amount of data
+ * on any access, since it is unlikely that the appli-
+ * cation will need more than what it asks for.
+ * MADV_SEQUENTIAL - pages in the given range will probably be accessed
+ * once, so they can be aggressively read ahead, and
+ * can be freed soon after they are accessed.
+ * MADV_WILLNEED - the application is notifying the system to read
+ * some pages ahead.
+ * MADV_DONTNEED - the application is finished with the given range,
+ * so the kernel can free resources associated with it.
+ *
+ * return values:
+ * zero - success
+ * -EINVAL - start + len < 0, start is not page-aligned,
+ * "behavior" is not a valid value, or application
+ * is attempting to release locked or shared pages.
+ * -ENOMEM - addresses in the specified range are not currently
+ * mapped, or are outside the AS of the process.
+ * -EIO - an I/O error occurred while paging in data.
+ * -EBADF - map exists, but area maps something that isn't a file.
+ * -EAGAIN - a kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior)
+{
+ unsigned long end;
+ struct vm_area_struct * vma;
+ int unmapped_error = 0;
+ int error = -EINVAL;
+
+ down(&current->mm->mmap_sem);
+
+ if (start & ~PAGE_MASK)
+ goto out;
+ len = (len + ~PAGE_MASK) & PAGE_MASK;
+ end = start + len;
+ if (end < start)
+ goto out;
+
+ error = 0;
+ if (end == start)
+ goto out;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ */
+ vma = find_vma(current->mm, start);
+ for (;;) {
+ /* Still start < end. */
+ error = -ENOMEM;
+ if (!vma)
+ goto out;
+
+ /* Here start < vma->vm_end. */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end. */
+ if (end <= vma->vm_end) {
+ if (start < end) {
+ error = madvise_vma(vma, start, end,
+ behavior);
+ if (error)
+ goto out;
+ }
+ error = unmapped_error;
+ goto out;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end < end. */
+ error = madvise_vma(vma, start, vma->vm_end, behavior);
+ if (error)
+ goto out;
+ start = vma->vm_end;
+ vma = vma->vm_next;
+ }
+
+out:
+ up(&current->mm->mmap_sem);
+ return error;
+}
+
+/*
+ * Later we can get more picky about what "in core" means precisely.
+ * For now, simply check to see if the page is in the page cache,
+ * and is up to date; i.e. that no page-in operation would be required
+ * at this time if an application were to map and access this page.
+ */
+static unsigned char mincore_page(struct vm_area_struct * vma,
+ unsigned long pgoff)
+{
+ unsigned char present = 0;
+ struct address_space * as = &vma->vm_file->f_dentry->d_inode->i_data;
+ struct page * page, ** hash = page_hash(as, pgoff);
+
+ spin_lock(&pagecache_lock);
+ page = __find_page_nolock(as, pgoff, *hash);
+ if ((page) && (Page_Uptodate(page)))
+ present = 1;
+ spin_unlock(&pagecache_lock);
+
+ return present;
+}
+
+static long mincore_vma(struct vm_area_struct * vma,
+ unsigned long start, unsigned long end, unsigned char * vec)
+{
+ long error, i, remaining;
+ unsigned char * tmp;
+
+ error = -ENOMEM;
+ if (!vma->vm_file)
+ return error;
+
+ start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
+ end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+ error = -EAGAIN;
+ tmp = (unsigned char *) __get_free_page(GFP_KERNEL);
+ if (!tmp)
+ return error;
+
+ /* (end - start) is # of pages, and also # of bytes in "vec */
+ remaining = (end - start),
+
+ error = 0;
+ for (i = 0; remaining > 0; remaining -= PAGE_SIZE, i++) {
+ int j = 0;
+ long thispiece = (remaining < PAGE_SIZE) ?
+ remaining : PAGE_SIZE;
+
+ while (j < thispiece)
+ tmp[j++] = mincore_page(vma, start++);
+
+ if (copy_to_user(vec + PAGE_SIZE * i, tmp, thispiece)) {
+ error = -EFAULT;
+ break;
+ }
+ }
+
+ free_page((unsigned long) tmp);
+ return error;
+}
+
+/*
+ * The mincore(2) system call.
+ *
+ * mincore() returns the memory residency status of the pages in the
+ * current process's address space specified by [addr, addr + len).
+ * The status is returned in a vector of bytes. The least significant
+ * bit of each byte is 1 if the referenced page is in memory, otherwise
+ * it is zero.
+ *
+ * Because the status of a page can change after mincore() checks it
+ * but before it returns to the application, the returned vector may
+ * contain stale information. Only locked pages are guaranteed to
+ * remain in memory.
+ *
+ * return values:
+ * zero - success
+ * -EFAULT - vec points to an illegal address
+ * -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE,
+ * or len has a nonpositive value
+ * -ENOMEM - Addresses in the range [addr, addr + len] are
+ * invalid for the address space of this process, or
+ * specify one or more pages which are not currently
+ * mapped
+ * -EAGAIN - A kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_mincore(unsigned long start, size_t len,
+ unsigned char * vec)
+{
+ int index = 0;
+ unsigned long end;
+ struct vm_area_struct * vma;
+ int unmapped_error = 0;
+ long error = -EINVAL;
+
+ down(&current->mm->mmap_sem);
+
+ if (start & ~PAGE_MASK)
+ goto out;
+ len = (len + ~PAGE_MASK) & PAGE_MASK;
+ end = start + len;
+ if (end < start)
+ goto out;
+
+ error = 0;
+ if (end == start)
+ goto out;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ */
+ vma = find_vma(current->mm, start);
+ for (;;) {
+ /* Still start < end. */
+ error = -ENOMEM;
+ if (!vma)
+ goto out;
+
+ /* Here start < vma->vm_end. */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end. */
+ if (end <= vma->vm_end) {
+ if (start < end) {
+ error = mincore_vma(vma, start, end,
+ &vec[index]);
+ if (error)
+ goto out;
+ }
+ error = unmapped_error;
+ goto out;
+ }
+
+ /* Here vma->vm_start <= start < vma->vm_end < end. */
+ error = mincore_vma(vma, start, vma->vm_end, &vec[index]);
+ if (error)
+ goto out;
+ index += (vma->vm_end - start) >> PAGE_CACHE_SHIFT;
+ start = vma->vm_end;
+ vma = vma->vm_next;
+ }
+
+out:
+ up(&current->mm->mmap_sem);
+ return error;
+}
+
struct page *read_cache_page(struct address_space *mapping,
unsigned long index,
int (*filler)(void *,struct page*),
diff --git a/mm/mlock.c b/mm/mlock.c
index c3e40db54..a3d10ff99 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -31,6 +31,7 @@ static inline int mlock_fixup_start(struct vm_area_struct * vma,
*n = *vma;
n->vm_end = end;
n->vm_flags = newflags;
+ n->vm_raend = 0;
if (n->vm_file)
get_file(n->vm_file);
if (n->vm_ops && n->vm_ops->open)
@@ -55,6 +56,7 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma,
n->vm_start = start;
n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
n->vm_flags = newflags;
+ n->vm_raend = 0;
if (n->vm_file)
get_file(n->vm_file);
if (n->vm_ops && n->vm_ops->open)
@@ -85,6 +87,8 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma,
right->vm_start = end;
right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
vma->vm_flags = newflags;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
if (vma->vm_file)
atomic_add(2, &vma->vm_file->f_count);
@@ -97,6 +101,7 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma,
vma->vm_start = start;
vma->vm_end = end;
vma->vm_flags = newflags;
+ vma->vm_raend = 0;
insert_vm_struct(current->mm, left);
insert_vm_struct(current->mm, right);
vmlist_modify_unlock(vma->vm_mm);
diff --git a/mm/mmap.c b/mm/mmap.c
index b650babc8..7bc2cf910 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -249,6 +249,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon
vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;
if (file) {
+ VM_ClearReadHint(vma);
+ vma->vm_raend = 0;
+
if (file->f_mode & 1)
vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
if (flags & MAP_SHARED) {
@@ -549,6 +552,7 @@ static struct vm_area_struct * unmap_fixup(struct vm_area_struct *area,
mpnt->vm_end = area->vm_end;
mpnt->vm_page_prot = area->vm_page_prot;
mpnt->vm_flags = area->vm_flags;
+ mpnt->vm_raend = 0;
mpnt->vm_ops = area->vm_ops;
mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
mpnt->vm_file = area->vm_file;
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 70f1d8e2c..53fc53acb 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -105,6 +105,7 @@ static inline int mprotect_fixup_start(struct vm_area_struct * vma,
*n = *vma;
n->vm_end = end;
n->vm_flags = newflags;
+ n->vm_raend = 0;
n->vm_page_prot = prot;
if (n->vm_file)
get_file(n->vm_file);
@@ -131,6 +132,7 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma,
n->vm_start = start;
n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
n->vm_flags = newflags;
+ n->vm_raend = 0;
n->vm_page_prot = prot;
if (n->vm_file)
get_file(n->vm_file);
@@ -162,6 +164,8 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
left->vm_end = start;
right->vm_start = end;
right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+ left->vm_raend = 0;
+ right->vm_raend = 0;
if (vma->vm_file)
atomic_add(2,&vma->vm_file->f_count);
if (vma->vm_ops && vma->vm_ops->open) {
@@ -173,6 +177,7 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
vma->vm_start = start;
vma->vm_end = end;
vma->vm_flags = newflags;
+ vma->vm_raend = 0;
vma->vm_page_prot = prot;
insert_vm_struct(current->mm, left);
insert_vm_struct(current->mm, right);
diff --git a/mm/mremap.c b/mm/mremap.c
index 5721fc5d5..d8d18cf62 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -135,8 +135,8 @@ static inline unsigned long move_vma(struct vm_area_struct * vma,
*new_vma = *vma;
new_vma->vm_start = new_addr;
new_vma->vm_end = new_addr+new_len;
- new_vma->vm_pgoff = vma->vm_pgoff;
new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
+ new_vma->vm_raend = 0;
if (new_vma->vm_file)
get_file(new_vma->vm_file);
if (new_vma->vm_ops && new_vma->vm_ops->open)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 02ba33bd5..0204cf141 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -26,7 +26,6 @@
int nr_swap_pages = 0;
int nr_lru_pages;
-LIST_HEAD(lru_cache);
pg_data_t *pgdat_list = (pg_data_t *)0;
static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
@@ -59,6 +58,19 @@ static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, };
*/
#define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->offset) || (((x)-mem_map) >= (zone)->offset+(zone)->size))
+static inline unsigned long classfree(zone_t *zone)
+{
+ unsigned long free = 0;
+ zone_t *z = zone->zone_pgdat->node_zones;
+
+ while (z != zone) {
+ free += z->free_pages;
+ z++;
+ }
+ free += zone->free_pages;
+ return(free);
+}
+
/*
* Buddy system. Hairy. You really aren't expected to understand this
*
@@ -135,6 +147,9 @@ void __free_pages_ok (struct page *page, unsigned long order)
memlist_add_head(&(base + page_idx)->list, &area->free_list);
spin_unlock_irqrestore(&zone->lock, flags);
+
+ if (classfree(zone) > zone->pages_high)
+ zone->zone_wake_kswapd = 0;
}
#define MARK_USED(index, order, area) \
@@ -201,19 +216,6 @@ static inline struct page * rmqueue (zone_t *zone, unsigned long order)
return NULL;
}
-static inline unsigned long classfree(zone_t *zone)
-{
- unsigned long free = 0;
- zone_t *z = zone->zone_pgdat->node_zones;
-
- while (z != zone) {
- free += z->free_pages;
- z++;
- }
- free += zone->free_pages;
- return(free);
-}
-
static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
{
int freed;
@@ -263,21 +265,12 @@ struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
{
unsigned long free = classfree(z);
- if (free > z->pages_high)
- {
- if (z->low_on_memory)
- z->low_on_memory = 0;
- z->zone_wake_kswapd = 0;
- }
- else
+ if (free <= z->pages_high)
{
extern wait_queue_head_t kswapd_wait;
- if (free <= z->pages_low) {
- z->zone_wake_kswapd = 1;
- wake_up_interruptible(&kswapd_wait);
- } else
- z->zone_wake_kswapd = 0;
+ z->zone_wake_kswapd = 1;
+ wake_up_interruptible(&kswapd_wait);
if (free <= z->pages_min)
z->low_on_memory = 1;
@@ -585,6 +578,7 @@ void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
unsigned long bitmap_size;
memlist_init(&zone->free_area[i].free_list);
+ memlist_init(&zone->lru_cache);
mask += mask;
size = (size + ~mask) & mask;
bitmap_size = size >> i;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 603b9a2e0..d3dfb8db6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -504,8 +504,7 @@ int kswapd(void *unused)
while (pgdat) {
for (i = 0; i < MAX_NR_ZONES; i++) {
zone = pgdat->node_zones + i;
- if ((!zone->size) ||
- (!zone->zone_wake_kswapd))
+ if ((!zone->size) || (!zone->zone_wake_kswapd))
continue;
do_try_to_free_pages(GFP_KSWAPD, zone);
}
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 80fc635f2..9a1b18270 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1378,11 +1378,12 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (n < 0)
return n;
- } else
+ } else {
sk->protinfo.af_at.src_port = addr->sat_port;
- if (atalk_find_or_insert_socket(sk, addr) != NULL)
- return -EADDRINUSE;
+ if (atalk_find_or_insert_socket(sk, addr) != NULL)
+ return -EADDRINUSE;
+ }
sk->zapped = 0;
diff --git a/net/atm/Makefile b/net/atm/Makefile
index 999fe1203..1c525f601 100644
--- a/net/atm/Makefile
+++ b/net/atm/Makefile
@@ -58,4 +58,4 @@ endif
include $(TOPDIR)/Rules.make
mpoa.o: mpc.o mpoa_caches.o mpoa_proc.o
- ld -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o
+ $(LD) -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index a676660dc..949c2b3e9 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -96,6 +96,10 @@
* AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel
* independent of AX25_MAX_DIGIS used by applications.
* Tomi(OH2BNS) Fixed ax25_getname().
+ * Joerg(DL1BKE) Starting to phase out the support for full_sockaddr_ax25
+ * with only 6 digipeaters and sockaddr_ax25 in ax25_bind(),
+ * ax25_connect() and ax25_sendmsg()
+ * Joerg(DL1BKE) Added support for SO_BINDTODEVICE
*/
#include <linux/config.h>
@@ -624,6 +628,8 @@ ax25_cb *ax25_create_cb(void)
static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
int opt;
if (level != SOL_AX25)
@@ -702,6 +708,22 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
sk->protinfo.ax25->paclen = opt;
return 0;
+ case SO_BINDTODEVICE:
+ if (optlen > IFNAMSIZ) optlen=IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ dev = dev_get_by_name(devname);
+ if (dev == NULL) return -ENODEV;
+
+ if (sk->type == SOCK_SEQPACKET &&
+ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+ return -EADDRNOTAVAIL;
+
+ sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev);
+ ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -710,15 +732,24 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
struct sock *sk = sock->sk;
+ struct ax25_dev *ax25_dev;
+ char devname[IFNAMSIZ];
+ void *valptr;
int val = 0;
- int len;
+ int maxlen, length;
if (level != SOL_AX25)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
+ if (get_user(maxlen, optlen))
+ return -EFAULT;
+
+ if (maxlen < 1)
return -EFAULT;
+ valptr = (void *) &val;
+ length = min(maxlen, sizeof(int));
+
switch (optname) {
case AX25_WINDOW:
val = sk->protinfo.ax25->window;
@@ -763,17 +794,30 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
case AX25_PACLEN:
val = sk->protinfo.ax25->paclen;
break;
+
+ case SO_BINDTODEVICE:
+ ax25_dev = sk->protinfo.ax25->ax25_dev;
+
+ if (ax25_dev != NULL && ax25_dev->dev != NULL) {
+ strncpy(devname, ax25_dev->dev->name, IFNAMSIZ);
+ length = min(strlen(ax25_dev->dev->name)+1, maxlen);
+ devname[length-1] = '\0';
+ } else {
+ *devname = '\0';
+ length = 1;
+ }
+
+ valptr = (void *) devname;
+ break;
default:
return -ENOPROTOOPT;
}
- len = min(len, sizeof(int));
-
- if (put_user(len, optlen))
+ if (put_user(length, optlen))
return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ if (copy_to_user(optval, valptr, length))
return -EFAULT;
return 0;
@@ -997,9 +1041,9 @@ static int ax25_release(struct socket *sock)
/*
* We support a funny extension here so you can (as root) give any callsign
- * digipeated via a local address as source. This is a hack until we add
- * BSD 4.4 ADDIFADDR type support. It is however small and trivially backward
- * compatible 8)
+ * digipeated via a local address as source. This hack is obsolete now
+ * that we've implemented support for SO_BINDTODEVICE. It is however small
+ * and trivially backward compatible.
*/
static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
@@ -1011,11 +1055,16 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
- return -EINVAL;
+ if (addr_len != sizeof(struct sockaddr_ax25) &&
+ addr_len != sizeof(struct full_sockaddr_ax25)) {
+ /* support for old structure may go away some time */
+ if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+ (addr_len > sizeof(struct full_sockaddr_ax25)))
+ return -EINVAL;
- if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
- return -EINVAL;
+ printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n",
+ current->comm);
+ }
if (addr->fsa_ax25.sax25_family != AF_AX25)
return -EINVAL;
@@ -1029,34 +1078,28 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
else
sk->protinfo.ax25->source_addr = *call;
- SOCK_DEBUG(sk, "AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
+ /*
+ * User already set interface with SO_BINDTODEVICE
+ */
+
+ if (sk->protinfo.ax25->ax25_dev != NULL)
+ goto done;
if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
- if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
- ax25_dev = NULL;
- SOCK_DEBUG(sk, "AX25: bound to any device\n");
- } else {
- if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
- SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
- return -EADDRNOTAVAIL;
- }
- SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
- }
- } else {
- if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
- SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
+ if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
+ (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ } else {
+ if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL)
return -EADDRNOTAVAIL;
- }
- SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
}
if (ax25_dev != NULL)
ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
+done:
ax25_insert_socket(sk->protinfo.ax25);
-
sk->zapped = 0;
- SOCK_DEBUG(sk, "AX25: socket is bound\n");
return 0;
}
@@ -1095,8 +1138,21 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
/*
* some sanity checks. code further down depends on this
*/
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
- return -EINVAL;
+
+ if (addr_len == sizeof(struct sockaddr_ax25)) {
+ /* support for this will go away in early 2.5.x */
+ printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n",
+ current->comm);
+ }
+ else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
+ /* support for old structure may go away some time */
+ if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+ (addr_len > sizeof(struct full_sockaddr_ax25)))
+ return -EINVAL;
+
+ printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n",
+ current->comm);
+ }
if (fsa->fsa_ax25.sax25_family != AF_AX25)
return -EINVAL;
@@ -1105,7 +1161,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
kfree(sk->protinfo.ax25->digipeat);
sk->protinfo.ax25->digipeat = NULL;
}
-
+
/*
* Handle digi-peaters to be used.
*/
@@ -1138,6 +1194,9 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
* been filled in, error if it hasn't.
*/
if (sk->zapped) {
+ /* check if we can remove this feature. It is broken. */
+ printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@poboxes.com\n",
+ current->comm);
if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
return err;
ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
@@ -1330,10 +1389,20 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
if (usax != NULL) {
if (usax->sax25_family != AF_AX25)
return -EINVAL;
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
- return -EINVAL;
- if (addr_len < (usax->sax25_ndigis * AX25_ADDR_LEN + sizeof(struct sockaddr_ax25)))
- return -EINVAL;
+
+ if (addr_len == sizeof(struct sockaddr_ax25)) {
+ printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n",
+ current->comm);
+ }
+ else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
+ /* support for old structure may go away some time */
+ if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
+ (addr_len > sizeof(struct full_sockaddr_ax25)))
+ return -EINVAL;
+
+ printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n",
+ current->comm);
+ }
if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) {
int ct = 0;
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index 091155076..a22098207 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -51,12 +51,13 @@ static struct sock *econet_sklist;
how you count) it makes sense to use a simple lookup table. */
static struct net_device *net2dev_map[256];
+#define EC_PORT_IP 0xd2
+
#ifdef CONFIG_ECONET_AUNUDP
static spinlock_t aun_queue_lock;
static struct socket *udpsock;
#define AUN_PORT 0x8000
-#define EC_PORT_IP 0xd2
struct aunhdr
{
@@ -750,6 +751,28 @@ struct sock *ec_listening_socket(unsigned char port, unsigned char
return NULL;
}
+/*
+ * Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+ unsigned char stn, unsigned char net,
+ unsigned char cb, unsigned char port)
+{
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->sec_family = AF_ECONET;
+ sec->type = ECTYPE_PACKET_RECEIVED;
+ sec->port = port;
+ sec->cb = cb;
+ sec->addr.net = net;
+ sec->addr.station = stn;
+
+ return sock_queue_rcv_skb(sk, skb);
+}
+
#ifdef CONFIG_ECONET_AUNUDP
/*
@@ -792,27 +815,6 @@ static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
set_fs(oldfs);
}
-/*
- * Queue a received packet for a socket.
- */
-
-static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
- unsigned char stn, unsigned char net,
- unsigned char cb, unsigned char port)
-{
- struct ec_cb *eb = (struct ec_cb *)&skb->cb;
- struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
-
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->sec_family = AF_ECONET;
- sec->type = ECTYPE_PACKET_RECEIVED;
- sec->port = port;
- sec->cb = cb;
- sec->addr.net = net;
- sec->addr.station = stn;
-
- return sock_queue_rcv_skb(sk, skb);
-}
/*
* Handle incoming AUN packets. Work out if anybody wants them,
@@ -1029,7 +1031,7 @@ release:
* Receive an Econet frame from a device.
*/
-static int econet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
struct ec_framehdr *hdr = (struct ec_framehdr *)skb->data;
struct sock *sk;
@@ -1132,9 +1134,9 @@ void __exit econet_proto_exit(void)
int __init econet_proto_init(struct net_proto *pro)
{
extern void econet_sysctl_register(void);
- spin_lock_init(&aun_queue_lock);
sock_register(&econet_family_ops);
#ifdef CONFIG_ECONET_AUNUDP
+ spin_lock_init(&aun_queue_lock);
aun_udp_initialise();
#endif
#ifdef CONFIG_ECONET_NATIVE
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 3a9d20e9c..799ec9476 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -5,7 +5,7 @@
*
* PACKET - implements raw packet sockets.
*
- * Version: $Id: af_packet.c,v 1.32 2000/02/21 16:25:55 davem Exp $
+ * Version: $Id: af_packet.c,v 1.33 2000/03/13 22:11:50 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1439,6 +1439,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg
case SIOCGIFBR:
case SIOCSIFBR:
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#ifdef CONFIG_INET
#ifdef CONFIG_KMOD
if (br_ioctl_hook == NULL)
request_module("bridge");
@@ -1446,6 +1447,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg
if (br_ioctl_hook != NULL)
return br_ioctl_hook(arg);
#endif
+#endif
return -ENOPKG;
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index 64a915d7e..ddc738fcc 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -229,7 +229,6 @@ static struct nf_hook_ops ing_ops =
{
{ NULL, NULL},
ing_hook,
- NULL,
PF_INET,
NF_IP_PRE_ROUTING,
1
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 82b39c1b5..e042ce1d8 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -16,6 +16,7 @@
* X.25 001 Jonathan Naylor Started coding.
* X.25 002 Jonathan Naylor Centralised disconnect handling.
* New timer architecture.
+ * 2000-11-03 Henner Eisen MSG_EOR handling more POSIX compliant.
*/
#include <linux/config.h>
@@ -851,7 +852,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB | MSG_EOR))
return -EINVAL;
- /* we currently don't support segments at the user interface */
+ /* we currently don't support segmented records at the user interface */
if (!(msg->msg_flags & MSG_EOR))
return -EINVAL;
@@ -1034,6 +1035,9 @@ static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int fl
msg->msg_flags |= MSG_TRUNC;
}
+ /* Currently, each datagram always contains a complete record */
+ msg->msg_flags |= MSG_EOR;
+
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (sx25 != NULL) {
diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c
index be9f7edd3..d33dc63c3 100644
--- a/net/x25/x25_link.c
+++ b/net/x25/x25_link.c
@@ -73,18 +73,25 @@ static void x25_stop_t20timer(struct x25_neigh *neigh)
del_timer(&neigh->t20timer);
}
+static int x25_t20timer_pending(struct x25_neigh *neigh)
+{
+ return timer_pending(&neigh->t20timer);
+}
+
/*
* This handles all restart and diagnostic frames.
*/
void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned short frametype)
{
struct sk_buff *skbn;
+ int confirm;
switch (frametype) {
case X25_RESTART_REQUEST:
+ confirm = !x25_t20timer_pending(neigh);
x25_stop_t20timer(neigh);
neigh->state = X25_LINK_STATE_3;
- x25_transmit_restart_confirmation(neigh);
+ if (confirm) x25_transmit_restart_confirmation(neigh);
break;
case X25_RESTART_CONFIRMATION:
diff --git a/scripts/Makefile b/scripts/Makefile
index 3db649d66..66311cbd0 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -33,6 +33,12 @@ tkgen.o: tkgen.c tkparse.h
tkparse.o tkcond.o tkgen.o:
$(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c)
+docproc.o: docproc.c
+ $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c)
+
+docproc: docproc.o
+ ${HOSTCC} -o docproc docproc.o
+
clean:
rm -f *~ kconfig.tk *.o tkparse mkdep split-include
diff --git a/scripts/docgen b/scripts/docgen
new file mode 100644
index 000000000..8d5fefef3
--- /dev/null
+++ b/scripts/docgen
@@ -0,0 +1,3 @@
+#!/bin/sh
+X=`$TOPDIR/scripts/gen-all-syms "$*"`
+$TOPDIR/scripts/docproc $X
diff --git a/scripts/docproc.c b/scripts/docproc.c
new file mode 100644
index 000000000..0c23e87c1
--- /dev/null
+++ b/scripts/docproc.c
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/*
+ * A simple filter for the templates
+ */
+
+int main(int argc, char *argv[])
+{
+ char buf[1024];
+ char *vec[1024];
+ char type[64];
+ int i;
+ int vp=2;
+ pid_t pid;
+
+
+ if(chdir(getenv("TOPDIR")))
+ {
+ perror("chdir");
+ exit(1);
+ }
+
+ /*
+ * Build the exec array ahead of time.
+ */
+ vec[0]="kernel-doc";
+ vec[1]="-docbook";
+ for(i=1;vp<1021;i++)
+ {
+ if(argv[i]==NULL)
+ break;
+ vec[vp++]=type;
+ vec[vp++]=argv[i];
+ }
+ vec[vp++]=buf+2;
+ vec[vp++]=NULL;
+
+ /*
+ * Now process the template
+ */
+
+ while(fgets(buf, 1024, stdin))
+ {
+ if(*buf!='!')
+ printf("%s", buf);
+ else
+ {
+ fflush(stdout);
+ if(buf[1]=='E')
+ strcpy(type, "-function");
+ else if(buf[1]=='I')
+ strcpy(type, "-nofunction");
+ else
+ {
+ fprintf(stderr, "Unknown ! escape.\n");
+ exit(1);
+ }
+ switch(pid=fork())
+ {
+ case -1:
+ perror("fork");
+ exit(1);
+ case 0:
+ execvp("scripts/kernel-doc", vec);
+ perror("exec");
+ exit(1);
+ default:
+ waitpid(pid, NULL,0);
+ }
+ }
+ }
+ exit(0);
+}
diff --git a/scripts/gen-all-syms b/scripts/gen-all-syms
new file mode 100644
index 000000000..d65e196ca
--- /dev/null
+++ b/scripts/gen-all-syms
@@ -0,0 +1,7 @@
+#!/bin/sh
+for i in $*
+do
+ grep "EXPORT_SYMBOL.*(.*)" "$i" \
+ | sed -e "s/EXPORT_SYMBOL.*(/ /" \
+ | sed -e "s/).*$//"
+done
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
new file mode 100644
index 000000000..1e24b8169
--- /dev/null
+++ b/scripts/kernel-doc
@@ -0,0 +1,865 @@
+#!/usr/bin/perl
+
+## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
+## ##
+## This software falls under the GNU Public License. Please read ##
+## the COPYING file for more information ##
+
+#
+# This will read a 'c' file and scan for embedded comments in the
+# style of gnome comments (+minor extensions - see below).
+#
+
+# Note: This only supports 'c'.
+
+# usage:
+# kerneldoc [ -docbook | -html | -text | -man ]
+# [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile
+# or
+# [ -nofunction funcname [ -function funcname ...] ] c file(s)s > outputfile
+#
+# Set output format using one of -docbook -html -text or -man. Default is man.
+#
+# -function funcname
+# If set, then only generate documentation for the given function(s). All
+# other functions are ignored.
+#
+# -nofunction funcname
+# If set, then only generate documentation for the other function(s). All
+# other functions are ignored. Cannot be used with -function together
+# (yes thats a bug - perl hackers can fix it 8))
+#
+# c files - list of 'c' files to process
+#
+# All output goes to stdout, with errors to stderr.
+
+#
+# format of comments.
+# In the following table, (...)? signifies optional structure.
+# (...)* signifies 0 or more structure elements
+# /**
+# * function_name(:)? (- short description)?
+# (* @parameterx: (description of parameter x)?)*
+# (* a blank line)?
+# * (Description:)? (Description of function)?
+# * (section header: (section description)? )*
+# (*)?*/
+#
+# So .. the trivial example would be:
+#
+# /**
+# * my_function
+# **/
+#
+# If the Description: header tag is ommitted, then there must be a blank line
+# after the last parameter specification.
+# e.g.
+# /**
+# * my_function - does my stuff
+# * @my_arg: its mine damnit
+# *
+# * Does my stuff explained.
+# */
+#
+# or, could also use:
+# /**
+# * my_function - does my stuff
+# * @my_arg: its mine damnit
+# * Description: Does my stuff explained.
+# */
+# etc.
+#
+# All descriptions can be multiline, apart from the short function description.
+#
+# All descriptive text is further processed, scanning for the following special
+# patterns, which are highlighted appropriately.
+#
+# 'funcname()' - function
+# '$ENVVAR' - environmental variable
+# '&struct_name' - name of a structure
+# '@parameter' - name of a parameter
+# '%CONST' - name of a constant.
+
+# match expressions used to find embedded type information
+$type_constant = "\\\%(\\w+)";
+$type_func = "(\\w+\\(\\))";
+$type_param = "\\\@(\\w+)";
+$type_struct = "\\\&(\\w+)";
+$type_env = "(\\\$\\w+)";
+
+
+# Output conversion substitutions.
+# One for each output format
+
+# these work fairly well
+%highlights_html = ( $type_constant, "<i>\$1</i>",
+ $type_func, "<b>\$1</b>",
+ $type_struct, "<i>\$1</i>",
+ $type_param, "<tt><b>\$1</b></tt>" );
+$blankline_html = "<p>";
+
+# sgml, docbook format
+%highlights_sgml = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+ $type_func, "<function>\$1</function>",
+ $type_struct, "<structname>\$1</structname>",
+ $type_env, "<envar>\$1</envar>",
+ $type_param, "<parameter>\$1</parameter>" );
+$blankline_sgml = "</para><para>\n";
+
+# gnome, docbook format
+%highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+ $type_func, "<function>\$1</function>",
+ $type_struct, "<structname>\$1</structname>",
+ $type_env, "<envar>\$1</envar>",
+ $type_param, "<parameter>\$1</parameter>" );
+$blankline_gnome = "</para><para>\n";
+
+# these are pretty rough
+%highlights_man = ( $type_constant, "\\n.I \\\"\$1\\\"\\n",
+ $type_func, "\\n.B \\\"\$1\\\"\\n",
+ $type_struct, "\\n.I \\\"\$1\\\"\\n",
+ $type_param."([\.\, ]*)\n?", "\\n.I \\\"\$1\$2\\\"\\n" );
+$blankline_man = "";
+
+# text-mode
+%highlights_text = ( $type_constant, "\$1",
+ $type_func, "\$1",
+ $type_struct, "\$1",
+ $type_param, "\$1" );
+$blankline_text = "";
+
+
+sub usage {
+ print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man ]\n";
+ print " [ -function funcname [ -function funcname ...] ]\n";
+ print " [ -nofunction funcname [ -nofunction funcname ...] ]\n";
+ print " c source file(s) > outputfile\n";
+ exit 1;
+}
+
+# read arguments
+if ($#ARGV==-1) {
+ usage();
+}
+
+$verbose = 0;
+$output_mode = "man";
+%highlights = %highlights_man;
+$blankline = $blankline_man;
+$modulename = "API Documentation";
+$function_only = 0;
+while ($ARGV[0] =~ m/^-(.*)/) {
+ $cmd = shift @ARGV;
+ if ($cmd eq "-html") {
+ $output_mode = "html";
+ %highlights = %highlights_html;
+ $blankline = $blankline_html;
+ } elsif ($cmd eq "-man") {
+ $output_mode = "man";
+ %highlights = %highlights_man;
+ $blankline = $blankline_man;
+ } elsif ($cmd eq "-text") {
+ $output_mode = "text";
+ %highlights = %highlights_text;
+ $blankline = $blankline_text;
+ } elsif ($cmd eq "-docbook") {
+ $output_mode = "sgml";
+ %highlights = %highlights_sgml;
+ $blankline = $blankline_sgml;
+ } elsif ($cmd eq "-gnome") {
+ $output_mode = "gnome";
+ %highlights = %highlights_gnome;
+ $blankline = $blankline_gnome;
+ } elsif ($cmd eq "-module") { # not needed for sgml, inherits from calling document
+ $modulename = shift @ARGV;
+ } elsif ($cmd eq "-function") { # to only output specific functions
+ $function_only = 1;
+ $function = shift @ARGV;
+ $function_table{$function} = 1;
+ } elsif ($cmd eq "-nofunction") { # to only output specific functions
+ $function_only = 2;
+ $function = shift @ARGV;
+ $function_table{$function} = 1;
+ } elsif ($cmd eq "-v") {
+ $verbose = 1;
+ } elsif (($cmd eq "-h") || ($cmd eq "--help")) {
+ usage();
+ }
+}
+
+
+# generate a sequence of code that will splice in highlighting information
+# using the s// operator.
+$dohighlight = "";
+foreach $pattern (keys %highlights) {
+# print "scanning pattern $pattern ($highlights{$pattern})\n";
+ $dohighlight .= "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n";
+}
+
+##
+# dumps section contents to arrays/hashes intended for that purpose.
+#
+sub dump_section {
+ my $name = shift @_;
+ my $contents = join "\n", @_;
+
+ if ($name =~ m/$type_constant/) {
+ $name = $1;
+# print STDERR "constant section '$1' = '$contents'\n";
+ $constants{$name} = $contents;
+ } elsif ($name =~ m/$type_param/) {
+# print STDERR "parameter def '$1' = '$contents'\n";
+ $name = $1;
+ $parameters{$name} = $contents;
+ } else {
+# print STDERR "other section '$name' = '$contents'\n";
+ $sections{$name} = $contents;
+ push @sectionlist, $name;
+ }
+}
+
+##
+# output function
+#
+# parameters, a hash.
+# function => "function name"
+# parameterlist => @list of parameters
+# parameters => %parameter descriptions
+# sectionlist => @list of sections
+# sections => %descriont descriptions
+#
+
+sub output_highlight {
+ my $contents = join "\n", @_;
+ my $line;
+
+ eval $dohighlight;
+ foreach $line (split "\n", $contents) {
+ if ($line eq ""){
+ print $lineprefix, $blankline;
+ } else {
+ print $lineprefix, $line;
+ }
+ print "\n";
+ }
+}
+
+
+# output in html
+sub output_html {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+ print "<h2>Function</h2>\n";
+
+ print "<i>".$args{'functiontype'}."</i>\n";
+ print "<b>".$args{'function'}."</b>\n";
+ print "(";
+ $count = 0;
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print "<i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
+ if ($count != $#{$args{'parameterlist'}}) {
+ $count++;
+ print ", ";
+ }
+ }
+ print ")\n";
+
+ print "<h3>Arguments</h3>\n";
+ print "<dl>\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print "<dt><i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
+ print "<dd>";
+ output_highlight($args{'parameters'}{$parameter});
+ }
+ print "</dl>\n";
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "<h3>$section</h3>\n";
+ print "<ul>\n";
+ output_highlight($args{'sections'}{$section});
+ print "</ul>\n";
+ }
+ print "<hr>\n";
+}
+
+
+# output in html
+sub output_intro_html {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "<h3>$section</h3>\n";
+ print "<ul>\n";
+ output_highlight($args{'sections'}{$section});
+ print "</ul>\n";
+ }
+ print "<hr>\n";
+}
+
+
+
+# output in sgml DocBook
+sub output_sgml {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+ my $id;
+
+ $id = $args{'module'}."-".$args{'function'};
+ $id =~ s/[^A-Za-z0-9]/-/g;
+
+ print "<refentry>\n";
+ print "<refmeta>\n";
+ print "<refentrytitle><phrase id=\"$id\">".$args{'function'}."</phrase></refentrytitle>\n";
+ print "</refmeta>\n";
+ print "<refnamediv>\n";
+ print " <refname>".$args{'function'}."</refname>\n";
+ print " <refpurpose>\n";
+ print " ".$args{'purpose'}."\n";
+ print " </refpurpose>\n";
+ print "</refnamediv>\n";
+
+ print "<refsynopsisdiv>\n";
+ print " <title>Synopsis</title>\n";
+ print " <funcsynopsis>\n";
+ print " <funcdef>".$args{'functiontype'}." ";
+ print "<function>".$args{'function'}." ";
+ print "</function></funcdef>\n";
+
+# print "<refsect1>\n";
+# print " <title>Synopsis</title>\n";
+# print " <funcsynopsis>\n";
+# print " <funcdef>".$args{'functiontype'}." ";
+# print "<function>".$args{'function'}." ";
+# print "</function></funcdef>\n";
+
+ $count = 0;
+ if ($#{$args{'parameterlist'}} >= 0) {
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print " <paramdef>".$args{'parametertypes'}{$parameter};
+ print " <parameter>$parameter</parameter></paramdef>\n";
+ }
+ } else {
+ print " <void>\n";
+ }
+ print " </funcsynopsis>\n";
+ print "</refsynopsisdiv>\n";
+# print "</refsect1>\n";
+
+ # print parameters
+ print "<refsect1>\n <title>Arguments</title>\n";
+# print "<para>\nArguments\n";
+ if ($#{$args{'parameterlist'}} >= 0) {
+ print " <variablelist>\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print " <varlistentry>\n <term><parameter>$parameter</parameter></term>\n";
+ print " <listitem>\n <para>\n";
+ $lineprefix=" ";
+ output_highlight($args{'parameters'}{$parameter});
+ print " </para>\n </listitem>\n </varlistentry>\n";
+ }
+ print " </variablelist>\n";
+ } else {
+ print " <para>\n None\n </para>\n";
+ }
+ print "</refsect1>\n";
+
+ # print out each section
+ $lineprefix=" ";
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "<refsect1>\n <title>$section</title>\n <para>\n";
+# print "<para>\n$section\n";
+ if ($section =~ m/EXAMPLE/i) {
+ print "<example><para>\n";
+ }
+ output_highlight($args{'sections'}{$section});
+# print "</para>";
+ if ($section =~ m/EXAMPLE/i) {
+ print "</para></example>\n";
+ }
+ print " </para>\n</refsect1>\n";
+ }
+
+ print "</refentry>\n\n";
+}
+
+# output in sgml DocBook
+sub output_intro_sgml {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+ my $id;
+
+ $id = $args{'module'};
+ $id =~ s/[^A-Za-z0-9]/-/g;
+
+ # print out each section
+ $lineprefix=" ";
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "<refsect1>\n <title>$section</title>\n <para>\n";
+# print "<para>\n$section\n";
+ if ($section =~ m/EXAMPLE/i) {
+ print "<example><para>\n";
+ }
+ output_highlight($args{'sections'}{$section});
+# print "</para>";
+ if ($section =~ m/EXAMPLE/i) {
+ print "</para></example>\n";
+ }
+ print " </para>\n</refsect1>\n";
+ }
+
+ print "\n\n";
+}
+
+# output in sgml DocBook
+sub output_gnome {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+ my $id;
+
+ $id = $args{'module'}."-".$args{'function'};
+ $id =~ s/[^A-Za-z0-9]/-/g;
+
+ print "<sect2>\n";
+ print " <title id=\"$id\">".$args{'function'}."</title>\n";
+
+# print "<simplesect>\n";
+# print " <title>Synopsis</title>\n";
+ print " <funcsynopsis>\n";
+ print " <funcdef>".$args{'functiontype'}." ";
+ print "<function>".$args{'function'}." ";
+ print "</function></funcdef>\n";
+
+ $count = 0;
+ if ($#{$args{'parameterlist'}} >= 0) {
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print " <paramdef>".$args{'parametertypes'}{$parameter};
+ print " <parameter>$parameter</parameter></paramdef>\n";
+ }
+ } else {
+ print " <void>\n";
+ }
+ print " </funcsynopsis>\n";
+# print "</simplesect>\n";
+# print "</refsect1>\n";
+
+ # print parameters
+# print "<simplesect>\n <title>Arguments</title>\n";
+# if ($#{$args{'parameterlist'}} >= 0) {
+# print " <variablelist>\n";
+# foreach $parameter (@{$args{'parameterlist'}}) {
+# print " <varlistentry>\n <term><parameter>$parameter</parameter></term>\n";
+# print " <listitem>\n <para>\n";
+# $lineprefix=" ";
+# output_highlight($args{'parameters'}{$parameter});
+# print " </para>\n </listitem>\n </varlistentry>\n";
+# }
+# print " </variablelist>\n";
+# } else {
+# print " <para>\n None\n </para>\n";
+# }
+# print "</simplesect>\n";
+
+# print "<simplesect>\n <title>Arguments</title>\n";
+ if ($#{$args{'parameterlist'}} >= 0) {
+ print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";
+ print "<tgroup cols=\"2\">\n";
+ print "<colspec colwidth=\"2*\">\n";
+ print "<colspec colwidth=\"8*\">\n";
+ print "<tbody>\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print " <row><entry align=\"right\"><parameter>$parameter</parameter></entry>\n";
+ print " <entry>\n";
+ $lineprefix=" ";
+ output_highlight($args{'parameters'}{$parameter});
+ print " </entry></row>\n";
+ }
+ print " </tbody></tgroup></informaltable>\n";
+ } else {
+ print " <para>\n None\n </para>\n";
+ }
+# print "</simplesect>\n";
+
+ # print out each section
+ $lineprefix=" ";
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "<simplesect>\n <title>$section</title>\n";
+# print "<para>\n$section\n";
+ if ($section =~ m/EXAMPLE/i) {
+ print "<example><programlisting>\n";
+ } else {
+ }
+ print "<para>\n";
+ output_highlight($args{'sections'}{$section});
+# print "</para>";
+ print "</para>\n";
+ if ($section =~ m/EXAMPLE/i) {
+ print "</programlisting></example>\n";
+ } else {
+ }
+ print " </simplesect>\n";
+ }
+
+ print "</sect2>\n\n";
+}
+
+##
+# output in man
+sub output_man {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ print ".TH \"$args{'module'}\" \"$args{'function'}\" \"25 May 1998\" \"API Manual\" LINUX\n";
+
+ print ".SH Function\n";
+
+ print ".I \"".$args{'functiontype'}."\"\n";
+ print ".B \"".$args{'function'}."\"\n";
+ print "(\n";
+ $count = 0;
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print ".I \"".$args{'parametertypes'}{$parameter}."\"\n.B \"".$parameter."\"\n";
+ if ($count != $#{$args{'parameterlist'}}) {
+ $count++;
+ print ",\n";
+ }
+ }
+ print ")\n";
+
+ print ".SH Arguments\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n";
+ output_highlight($args{'parameters'}{$parameter});
+ }
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+sub output_intro_man {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ print ".TH \"$args{'module'}\" \"$args{'module'}\" \"25 May 1998\" \"API Manual\" LINUX\n";
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# output in text
+sub output_text {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ print "Function = ".$args{'function'}."\n";
+ print " return type: ".$args{'functiontype'}."\n\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n";
+ print " -> ".$args{'parameters'}{$parameter}."\n";
+ }
+ foreach $section (@{$args{'sectionlist'}}) {
+ print " $section:\n";
+ print " -> ";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+sub output_intro_text {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print " $section:\n";
+ print " -> ";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# generic output function - calls the right one based
+# on current output mode.
+sub output_function {
+# output_html(@_);
+ eval "output_".$output_mode."(\@_);";
+}
+
+##
+# generic output function - calls the right one based
+# on current output mode.
+sub output_intro {
+# output_html(@_);
+ eval "output_intro_".$output_mode."(\@_);";
+}
+
+
+##
+# takes a function prototype and spits out all the details
+# stored in the global arrays/hsahes.
+sub dump_function {
+ my $prototype = shift @_;
+
+ $prototype =~ s/^static+ //;
+ $prototype =~ s/^extern+ //;
+ $prototype =~ s/^inline+ //;
+ $prototype =~ s/^__inline__+ //;
+
+ if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
+ $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/) {
+ $return_type = $1;
+ $function_name = $2;
+ $args = $3;
+
+# print STDERR "ARGS = '$args'\n";
+
+ foreach $arg (split ',', $args) {
+ # strip leading/trailing spaces
+ $arg =~ s/^\s*//;
+ $arg =~ s/\s*$//;
+# print STDERR "SCAN ARG: '$arg'\n";
+ @args = split('\s', $arg);
+
+# print STDERR " -> @args\n";
+ $param = pop @args;
+# print STDERR " -> @args\n";
+ if ($param =~ m/^(\*+)(.*)/) {
+ $param = $2;
+ push @args, $1;
+ }
+ $type = join " ", @args;
+
+ if ($type eq "")
+ {
+ $type="";
+ $param="void";
+ $parameters{void} = "no arguments";
+ }
+ if ($parameters{$param} eq "") {
+ $parameters{$param} = "-- undescribed --";
+ print STDERR "Warning($lineno): Function parameter '$param' not described in '$function_name'\n";
+ }
+
+ push @parameterlist, $param;
+ $parametertypes{$param} = $type;
+# print STDERR "param = '$param', type = '$type'\n";
+ }
+ } else {
+ print STDERR "Error($lineno): cannot understand prototype: '$prototype'\n";
+ return;
+ }
+
+ if ($function_only==0 ||
+ ( $function_only == 1 && defined($function_table{$function_name})) ||
+ ( $function_only == 2 && !defined($function_table{$function_name})))
+ {
+ output_function({'function' => $function_name,
+ 'module' => $modulename,
+ 'functiontype' => $return_type,
+ 'parameterlist' => \@parameterlist,
+ 'parameters' => \%parameters,
+ 'parametertypes' => \%parametertypes,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $function_purpose
+ });
+ }
+}
+
+######################################################################
+# main
+# states
+# 0 - normal code
+# 1 - looking for function name
+# 2 - scanning field start.
+# 3 - scanning prototype.
+$state = 0;
+$section = "";
+
+$doc_special = "\@\%\$\&";
+
+$doc_start = "^/\\*\\*\$";
+$doc_end = "\\*/";
+$doc_com = "\\s*\\*\\s*";
+$doc_func = $doc_com."(\\w+):?";
+$doc_sect = $doc_com."([".$doc_special."]?[\\w ]+):(.*)";
+$doc_content = $doc_com."(.*)";
+$doc_block = $doc_com."DOC:\\s*(.*)?";
+
+%constants = ();
+%parameters = ();
+@parameterlist = ();
+%sections = ();
+@sectionlist = ();
+
+$contents = "";
+$section_default = "Description"; # default section
+$section_intro = "Introduction";
+$section = $section_default;
+
+$lineno = 0;
+foreach $file (@ARGV) {
+ if (!open(IN,"<$file")) {
+ print STDERR "Error: Cannot open file $file\n";
+ next;
+ }
+ while (<IN>) {
+ $lineno++;
+
+ if ($state == 0) {
+ if (/$doc_start/o) {
+ $state = 1; # next line is always the function name
+ }
+ } elsif ($state == 1) { # this line is the function name (always)
+ if (/$doc_block/o) {
+ $state = 4;
+ $contents = "";
+ if ( $1 eq "" ) {
+ $section = $section_intro;
+ } else {
+ $section = $1;
+ }
+ }
+ elsif (/$doc_func/o) {
+ $function = $1;
+ $state = 2;
+ if (/-(.*)/) {
+ $function_purpose = $1;
+ } else {
+ $function_purpose = "";
+ }
+ if ($verbose) {
+ print STDERR "Info($lineno): Scanning doc for $function\n";
+ }
+ } else {
+ print STDERR "WARN($lineno): Cannot understand $_ on line $lineno",
+ " - I thought it was a doc line\n";
+ $state = 0;
+ }
+ } elsif ($state == 2) { # look for head: lines, and include content
+ if (/$doc_sect/o) {
+ $newsection = $1;
+ $newcontents = $2;
+
+ if ($contents ne "") {
+ dump_section($section, $contents);
+ $section = $section_default;
+ }
+
+ $contents = $newcontents;
+ if ($contents ne "") {
+ $contents .= "\n";
+ }
+ $section = $newsection;
+ } elsif (/$doc_end/) {
+
+ if ($contents ne "") {
+ dump_section($section, $contents);
+ $section = $section_default;
+ $contents = "";
+ }
+
+# print STDERR "end of doc comment, looking for prototype\n";
+ $prototype = "";
+ $state = 3;
+ } elsif (/$doc_content/) {
+ # miguel-style comment kludge, look for blank lines after
+ # @parameter line to signify start of description
+ if ($1 eq "" && $section =~ m/^@/) {
+ dump_section($section, $contents);
+ $section = $section_default;
+ $contents = "";
+ } else {
+ $contents .= $1."\n";
+ }
+ } else {
+ # i dont know - bad line? ignore.
+ print STDERR "WARNING($lineno): bad line: $_";
+ }
+ } elsif ($state == 3) { # scanning for function { (end of prototype)
+ if (m#\s*/\*\s+MACDOC\s*#io) {
+ # do nothing
+ }
+ elsif (/([^\{]*)/) {
+ $prototype .= $1;
+ }
+ if (/\{/) {
+ $prototype =~ s@/\*.*?\*/@@gos; # strip comments.
+ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
+ $prototype =~ s@^ +@@gos; # strip leading spaces
+ dump_function($prototype);
+
+ $function = "";
+ %constants = ();
+ %parameters = ();
+ %parametertypes = ();
+ @parameterlist = ();
+ %sections = ();
+ @sectionlist = ();
+ $prototype = "";
+
+ $state = 0;
+ }
+ } elsif ($state == 4) {
+ # Documentation block
+ if (/$doc_block/) {
+ dump_section($section, $contents);
+ output_intro({'sectionlist' => \@sectionlist,
+ 'sections' => \%sections });
+ $contents = "";
+ $function = "";
+ %constants = ();
+ %parameters = ();
+ %parametertypes = ();
+ @parameterlist = ();
+ %sections = ();
+ @sectionlist = ();
+ $prototype = "";
+ if ( $1 eq "" ) {
+ $section = $section_intro;
+ } else {
+ $section = $1;
+ }
+ }
+ elsif (/$doc_end/)
+ {
+ dump_section($section, $contents);
+ output_intro({'sectionlist' => \@sectionlist,
+ 'sections' => \%sections });
+ $contents = "";
+ $function = "";
+ %constants = ();
+ %parameters = ();
+ %parametertypes = ();
+ @parameterlist = ();
+ %sections = ();
+ @sectionlist = ();
+ $prototype = "";
+ $state = 0;
+ }
+ elsif (/$doc_content/)
+ {
+ if ( $1 eq "" )
+ {
+ $contents .= $blankline;
+ }
+ else
+ {
+ $contents .= $1 . "\n";
+ }
+ }
+ }
+ }
+}
+