From 8abb719409c9060a7c0676f76e9182c1e0b8ca46 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 19 Mar 2000 01:28:40 +0000 Subject: [PATCH] Merge with 2.3.99-pre1. --- CREDITS | 9 +- Documentation/Configure.help | 364 ++- Documentation/DocBook/Makefile | 29 + Documentation/DocBook/mcabook.tmpl | 105 + Documentation/DocBook/parport-multi.fig | 59 + Documentation/DocBook/parport-share.fig | 154 + Documentation/DocBook/parport-structure.fig | 60 + Documentation/DocBook/parportbook.sgml | 1747 +++++++++++ Documentation/DocBook/videobook.tmpl | 1663 +++++++++++ Documentation/DocBook/wanbook.tmpl | 97 + Documentation/DocBook/z8530book.tmpl | 383 +++ Documentation/networking/comx.txt | 248 ++ Documentation/networking/dmfe.txt | 63 + Documentation/sound/ALS | 43 + Documentation/sound/Maestro | 29 +- Documentation/usb/ov511.txt | 160 +- Documentation/video4linux/CQcam.txt | 414 +++ Documentation/video4linux/bttv/README | 2 +- MAINTAINERS | 56 +- Makefile | 190 +- arch/alpha/config.in | 13 + arch/alpha/defconfig | 13 +- arch/arm/Makefile | 15 + arch/arm/boot/compressed/Makefile | 5 +- arch/arm/config.in | 15 + arch/arm/defconfig | 90 +- arch/arm/kernel/Makefile | 78 +- arch/arm/kernel/arch.c | 322 ++ arch/arm/kernel/arch.h | 15 + arch/arm/kernel/armksyms.c | 3 + arch/arm/kernel/bios32.c | 79 +- arch/arm/kernel/head-armv.S | 16 +- arch/arm/kernel/ptrace.c | 42 + arch/arm/kernel/setup.c | 392 +-- arch/arm/lib/Makefile | 36 +- arch/arm/lib/io-shark.c | 79 + arch/arm/mm/init.c | 31 +- arch/arm/mm/proc-arm6,7.S | 135 +- .../{vmlinux-armv.lds.in => vmlinux-armo.lds.in} | 38 +- arch/arm/vmlinux-armv.lds.in | 8 + arch/i386/boot/tools/build.c | 6 +- arch/i386/config.in | 13 + arch/i386/defconfig | 105 +- arch/i386/kernel/Makefile | 2 +- arch/i386/kernel/acpi.c | 454 ++- arch/i386/kernel/entry.S | 4 +- arch/i386/kernel/head.S | 1147 +++---- arch/i386/kernel/i386_ksyms.c | 14 - arch/i386/kernel/irq.c | 129 +- arch/i386/kernel/mca.c | 185 +- arch/i386/kernel/mtrr.c | 68 +- arch/i386/kernel/setup.c | 54 +- arch/i386/kernel/time.c | 7 + arch/i386/lib/delay.c | 36 +- arch/ia64/config.in | 13 + arch/ia64/defconfig | 45 +- arch/m68k/config.in | 13 + arch/m68k/defconfig | 12 +- arch/mips/arc/init.c | 4 +- arch/mips/config.in | 15 +- arch/mips/defconfig | 20 +- arch/mips/defconfig-ip22 | 20 +- arch/mips/kernel/irixelf.c | 31 +- arch/mips/kernel/syscalls.h | 4 +- arch/mips64/config.in | 15 +- arch/mips64/defconfig | 21 +- arch/mips64/defconfig-ip27 | 21 +- arch/mips64/kernel/scall_64.S | 4 +- arch/mips64/kernel/scall_o32.S | 4 +- arch/ppc/config.in | 13 + arch/ppc/defconfig | 62 +- arch/sh/config.in | 13 + arch/sh/defconfig | 25 +- arch/sparc/boot/piggyback.c | 19 +- arch/sparc/config.in | 23 +- arch/sparc/defconfig | 6 +- arch/sparc/kernel/sys_solaris.c | 2 + arch/sparc/kernel/sys_sunos.c | 53 +- arch/sparc/kernel/systbls.S | 6 +- arch/sparc64/config.in | 41 +- arch/sparc64/defconfig | 80 +- arch/sparc64/kernel/binfmt_aout32.c | 48 +- arch/sparc64/kernel/ioctl32.c | 12 +- arch/sparc64/kernel/sys_sparc32.c | 13 +- arch/sparc64/kernel/sys_sunos32.c | 52 +- arch/sparc64/kernel/systbls.S | 8 +- arch/sparc64/solaris/misc.c | 7 +- arch/sparc64/solaris/systbl.S | 4 +- drivers/Makefile | 17 +- drivers/block/Config.in | 325 +- drivers/block/DAC960.c | 10 +- drivers/block/Makefile | 216 +- drivers/block/blkpg.c | 7 + drivers/block/elevator.c | 150 + drivers/block/ll_rw_blk.c | 341 +-- drivers/cdrom/aztcd.c | 7 +- drivers/char/Config.in | 2 +- drivers/char/bttv.c | 8 + drivers/char/bttv.h | 2 +- drivers/char/console.c | 2 +- drivers/char/drm/auth.c | 7 +- drivers/char/drm/bufs.c | 8 +- drivers/char/drm/context.c | 5 +- drivers/char/drm/dma.c | 5 +- drivers/char/drm/drawable.c | 5 +- drivers/char/drm/drm.h | 5 +- drivers/char/drm/drmP.h | 10 +- drivers/char/drm/fops.c | 18 +- drivers/char/drm/gamma_dma.c | 5 +- drivers/char/drm/gamma_drv.c | 9 +- drivers/char/drm/gamma_drv.h | 5 +- drivers/char/drm/init.c | 5 +- drivers/char/drm/ioctl.c | 5 +- drivers/char/drm/lists.c | 7 +- drivers/char/drm/lock.c | 5 +- drivers/char/drm/memory.c | 5 +- drivers/char/drm/proc.c | 21 +- drivers/char/drm/tdfx_context.c | 5 +- drivers/char/drm/tdfx_drv.c | 25 +- drivers/char/drm/tdfx_drv.h | 6 +- drivers/char/drm/vm.c | 5 +- drivers/char/generic_serial.c | 168 +- drivers/char/mem.c | 8 - drivers/char/misc.c | 26 + drivers/char/mixcomwd.c | 58 +- drivers/char/n_tty.c | 50 +- drivers/char/ppdev.c | 11 + drivers/char/pty.c | 4 +- drivers/char/radio-gemtek.c | 4 +- drivers/char/serial.c | 35 +- drivers/char/sx.c | 437 ++- drivers/char/sx.h | 38 +- drivers/char/sxboards.h | 1 + drivers/char/tty_io.c | 4 + drivers/char/videodev.c | 72 +- drivers/ide/Config.in | 153 + drivers/{block => ide}/Makefile | 204 +- drivers/{block => ide}/aec6210.c | 0 drivers/{block => ide}/ali14xx.c | 0 drivers/{block => ide}/alim15x3.c | 0 drivers/{block => ide}/amd7409.c | 0 drivers/{block => ide}/buddha.c | 0 drivers/{block => ide}/cmd640.c | 0 drivers/{block => ide}/cmd64x.c | 0 drivers/{block => ide}/cs5530.c | 0 drivers/{block => ide}/cy82c693.c | 0 drivers/{block => ide}/dtc2278.c | 0 drivers/{block => ide}/falconide.c | 0 drivers/{block => ide}/gayle.c | 0 drivers/{block => ide}/hd.c | 0 drivers/{block => ide}/hpt34x.c | 0 drivers/{block => ide}/hpt366.c | 0 drivers/{block => ide}/ht6560b.c | 0 drivers/{block => ide}/icside.c | 0 drivers/{block => ide}/ide-cd.c | 0 drivers/{block => ide}/ide-cd.h | 0 drivers/{block => ide}/ide-cs.c | 0 drivers/{block => ide}/ide-disk.c | 0 drivers/{block => ide}/ide-dma.c | 0 drivers/{block => ide}/ide-features.c | 0 drivers/{block => ide}/ide-floppy.c | 8 +- drivers/{block => ide}/ide-geometry.c | 0 drivers/{block => ide}/ide-pci.c | 0 drivers/{block => ide}/ide-pmac.c | 0 drivers/{block => ide}/ide-pnp.c | 0 drivers/{block => ide}/ide-probe.c | 0 drivers/{block => ide}/ide-proc.c | 0 drivers/{block => ide}/ide-tape.c | 0 drivers/{block => ide}/ide.c | 2 + drivers/{block => ide}/ide_modes.h | 0 drivers/{block => ide}/macide.c | 0 drivers/{block => ide}/ns87415.c | 0 drivers/{block => ide}/opti621.c | 0 drivers/{block => ide}/pdc202xx.c | 0 drivers/{block => ide}/pdc4030.c | 0 drivers/{block => ide}/pdc4030.h | 0 drivers/{block => ide}/piix.c | 0 drivers/{block => ide}/q40ide.c | 0 drivers/{block => ide}/qd6580.c | 0 drivers/{block => ide}/rapide.c | 0 drivers/{block => ide}/rz1000.c | 0 drivers/{block => ide}/sis5513.c | 0 drivers/{block => ide}/sl82c105.c | 0 drivers/{block => ide}/trm290.c | 0 drivers/{block => ide}/umc8672.c | 0 drivers/{block => ide}/via82cxxx.c | 0 drivers/net/3c527.c | 52 +- drivers/net/3c59x.c | 2 +- drivers/net/Config.in | 12 +- drivers/net/aironet4500.h | 2 +- drivers/net/appletalk/Config.in | 43 +- drivers/net/bsd_comp.c | 42 +- drivers/net/dgrs.c | 5 + drivers/net/dmfe.c | 586 ++-- drivers/net/eexpress.c | 1 + drivers/net/epic100.c | 2 +- drivers/net/eql.c | 4 +- drivers/net/ewrk3.c | 4 +- drivers/net/hamradio/baycom_epp.c | 8 +- drivers/net/hamradio/baycom_par.c | 2 +- drivers/net/hamradio/baycom_ser_fdx.c | 2 +- drivers/net/hamradio/baycom_ser_hdx.c | 2 +- drivers/net/hamradio/bpqether.c | 2 +- drivers/net/hamradio/hdlcdrv.c | 6 +- drivers/net/hamradio/pi2.c | 4 +- drivers/net/hamradio/pt.c | 4 +- drivers/net/hamradio/scc.c | 12 +- drivers/net/hamradio/soundmodem/sm.c | 2 +- drivers/net/hamradio/soundmodem/sm_sbc.c | 4 +- drivers/net/hamradio/soundmodem/sm_wss.c | 2 +- drivers/net/hamradio/yam.c | 6 +- drivers/net/lance.c | 46 +- drivers/net/pcmcia/3c574_cs.c | 12 +- drivers/net/pcmcia/Config.in | 1 - drivers/net/pcmcia/Makefile | 2 - drivers/net/pcmcia/ray_cs.c | 2 +- drivers/net/pcmcia/tulip_cb.c | 3150 -------------------- drivers/net/pcnet32.c | 2 +- drivers/net/plip.c | 59 +- drivers/net/ppp_async.c | 627 ++-- drivers/net/ppp_deflate.c | 49 +- drivers/net/ppp_generic.c | 1510 +++++++--- drivers/net/ppp_synctty.c | 2 +- drivers/net/rcpci45.c | 15 +- drivers/net/rrunner.c | 8 +- drivers/net/sb1000.c | 4 +- drivers/net/setup.c | 5 +- drivers/net/shaper.c | 11 + drivers/net/sis900.c | 2 +- drivers/net/skfp/drvfbi.c | 2 +- drivers/net/skfp/skfddi.c | 2 +- drivers/net/sunhme.c | 4 +- drivers/net/tokenring/Config.in | 1 + drivers/net/tokenring/Makefile | 1 + drivers/net/tokenring/lanstreamer.c | 1776 +++++++++++ drivers/net/tokenring/lanstreamer.h | 319 ++ drivers/net/via-rhine.c | 2 +- drivers/net/wan/Config.in | 19 + drivers/net/wan/Makefile | 56 +- drivers/net/wan/comx-hw-comx.c | 1426 +++++++++ drivers/net/wan/comx-hw-locomx.c | 496 +++ drivers/net/wan/comx-hw-mixcom.c | 948 ++++++ drivers/net/wan/comx-proto-fr.c | 1006 +++++++ drivers/net/wan/comx-proto-lapb.c | 548 ++++ drivers/net/wan/comx-proto-ppp.c | 269 ++ drivers/net/wan/comx.c | 1238 ++++++++ drivers/net/wan/comx.h | 240 ++ drivers/net/wan/comxhw.h | 113 + drivers/net/wan/cosa.c | 10 +- drivers/net/wan/hscx.h | 103 + drivers/net/wan/mixcom.h | 35 + drivers/net/wan/sbni.c | 6 +- drivers/net/wan/sdla.c | 2 +- drivers/net/wan/syncppp.c | 142 +- drivers/net/wan/syncppp.h | 4 + drivers/net/wan/z85230.c | 94 +- drivers/net/wavelan.c | 6 +- drivers/net/wavelan.h | 6 +- drivers/net/yellowfin.c | 1 - drivers/parport/ChangeLog | 21 + drivers/parport/Config.in | 3 + drivers/parport/parport_pc.c | 119 +- drivers/pcmcia/yenta.c | 25 +- drivers/sbus/audio/audio.c | 410 +-- drivers/sbus/char/sab82532.c | 8 +- drivers/sbus/char/su.c | 8 +- drivers/sbus/char/zs.c | 8 +- drivers/scsi/ChangeLog.ncr53c8xx | 17 + drivers/scsi/ChangeLog.sym53c8xx | 9 + drivers/scsi/Config.in | 4 - drivers/scsi/README.st | 8 +- drivers/scsi/atp870u.c | 5 + drivers/scsi/constants.c | 563 +++- drivers/scsi/eata_dma_proc.c | 18 +- drivers/scsi/hosts.h | 28 +- drivers/scsi/ncr53c8xx.c | 2690 ++--------------- drivers/scsi/scsi.c | 18 - drivers/scsi/scsi.h | 11 +- drivers/scsi/scsi_debug.c | 12 +- drivers/scsi/scsi_ioctl.c | 64 +- drivers/scsi/scsi_lib.c | 9 + drivers/scsi/scsi_merge.c | 8 +- drivers/scsi/scsi_scan.c | 80 +- drivers/scsi/scsi_syms.c | 1 - drivers/scsi/sd.c | 74 +- drivers/scsi/sr.c | 4 +- drivers/scsi/st.c | 738 +++-- drivers/scsi/st.h | 11 +- drivers/scsi/st_options.h | 9 +- drivers/scsi/sym53c8xx.c | 175 +- drivers/scsi/sym53c8xx_comm.h | 2863 ++++++++++++++++++ drivers/sound/Config.in | 5 +- drivers/sound/Makefile | 37 +- drivers/sound/dev_table.c | 2 - drivers/sound/dev_table.h | 36 +- drivers/sound/dmabuf.c | 8 + drivers/sound/miroaci.h | 1 - drivers/sound/mpu401.c | 21 +- drivers/sound/sb_card.c | 2 - drivers/sound/sound_core.c | 112 +- drivers/sound/sound_firmware.c | 18 + drivers/sound/soundcard.c | 57 +- drivers/sound/waveartist.c | 12 + drivers/telephony/ixj.c | 29 +- drivers/telephony/phonedev.c | 5 +- drivers/usb/Config.in | 4 +- drivers/usb/Makefile | 18 +- drivers/usb/dsbr100.c | 353 +++ drivers/usb/inode.c | 1 + drivers/usb/ov511.c | 484 +-- drivers/usb/ov511.h | 9 +- drivers/usb/pegasus.c | 1052 +++---- drivers/usb/serial/Makefile | 9 +- drivers/usb/uhci.c | 56 +- drivers/usb/uhci.h | 6 +- drivers/usb/usb-core.c | 7 +- drivers/usb/usb-storage.c | 1124 ++++--- drivers/usb/usb-storage.h | 24 +- drivers/usb/usb-uhci-debug.h | 79 +- drivers/usb/usb-uhci.c | 975 +++--- drivers/usb/usb-uhci.h | 18 +- drivers/video/aty128.h | 2 + drivers/video/aty128fb.c | 351 ++- drivers/video/atyfb.c | 2 +- drivers/video/cyber2000fb.c | 106 +- drivers/video/fbcon.c | 101 +- drivers/video/fbmem.c | 84 +- drivers/video/hgafb.c | 86 +- fs/Config.in | 5 +- fs/adfs/dir.c | 15 +- fs/affs/super.c | 19 +- fs/bfs/inode.c | 15 +- fs/binfmt_aout.c | 49 +- fs/binfmt_elf.c | 41 +- fs/binfmt_em86.c | 11 +- fs/binfmt_misc.c | 2 - fs/binfmt_script.c | 11 +- fs/coda/psdev.c | 2 - fs/efs/super.c | 23 +- fs/exec.c | 38 +- fs/ext2/dir.c | 4 +- fs/fat/fatfs_syms.c | 5 +- fs/fat/inode.c | 16 - fs/filesystems.c | 113 - fs/hfs/super.c | 29 +- fs/hpfs/buffer.c | 9 +- fs/hpfs/hpfs_fn.h | 1 - fs/hpfs/inode.c | 1 + fs/hpfs/super.c | 22 +- fs/isofs/inode.c | 30 +- fs/minix/inode.c | 17 +- fs/msdos/msdosfs_syms.c | 13 +- fs/msdos/namei.c | 25 +- fs/ncpfs/inode.c | 19 +- fs/nfs/read.c | 2 +- fs/nfsd/nfsfh.c | 1 + fs/nls/nls_base.c | 161 +- fs/nls/nls_cp437.c | 31 +- fs/nls/nls_cp737.c | 31 +- fs/nls/nls_cp775.c | 31 +- fs/nls/nls_cp850.c | 31 +- fs/nls/nls_cp852.c | 31 +- fs/nls/nls_cp855.c | 31 +- fs/nls/nls_cp857.c | 31 +- fs/nls/nls_cp860.c | 31 +- fs/nls/nls_cp861.c | 31 +- fs/nls/nls_cp862.c | 31 +- fs/nls/nls_cp863.c | 31 +- fs/nls/nls_cp864.c | 31 +- fs/nls/nls_cp865.c | 31 +- fs/nls/nls_cp866.c | 31 +- fs/nls/nls_cp869.c | 31 +- fs/nls/nls_cp874.c | 31 +- fs/nls/nls_iso8859-1.c | 31 +- fs/nls/nls_iso8859-14.c | 31 +- fs/nls/nls_iso8859-15.c | 31 +- fs/nls/nls_iso8859-2.c | 31 +- fs/nls/nls_iso8859-3.c | 31 +- fs/nls/nls_iso8859-4.c | 31 +- fs/nls/nls_iso8859-5.c | 31 +- fs/nls/nls_iso8859-6.c | 31 +- fs/nls/nls_iso8859-7.c | 31 +- fs/nls/nls_iso8859-8.c | 31 +- fs/nls/nls_iso8859-9.c | 31 +- fs/nls/nls_koi8-r.c | 31 +- fs/ntfs/fs.c | 47 +- fs/openpromfs/inode.c | 30 +- fs/partitions/acorn.c | 72 +- fs/partitions/acorn.h | 36 - fs/partitions/atari.c | 4 +- fs/partitions/sgi.c | 4 +- fs/proc/procfs_syms.c | 12 +- fs/qnx4/inode.c | 17 +- fs/romfs/inode.c | 21 +- fs/smbfs/inode.c | 19 +- fs/super.c | 8 +- fs/sysv/inode.c | 17 +- fs/udf/super.c | 60 +- fs/ufs/dir.c | 4 +- fs/ufs/super.c | 17 +- fs/umsdos/inode.c | 17 +- fs/vfat/namei.c | 13 - fs/vfat/vfatfs_syms.c | 10 +- include/asm-alpha/parport.h | 79 +- include/asm-arm/arch-cl7500/system.h | 2 - include/asm-arm/arch-ebsa285/time.h | 7 +- include/asm-arm/parport.h | 86 +- include/asm-arm/procinfo.h | 6 +- include/asm-arm/ptrace.h | 5 + include/asm-arm/system.h | 100 +- include/asm-i386/mca_dma.h | 76 + include/asm-i386/mman.h | 6 + include/asm-i386/parport.h | 69 +- include/asm-i386/unistd.h | 3 + include/asm-mips/mman.h | 6 + include/asm-mips/parport.h | 67 +- include/asm-mips/sgialib.h | 7 +- include/asm-mips/unistd.h | 6 +- include/asm-mips64/mman.h | 8 +- include/asm-mips64/parport.h | 69 +- include/asm-mips64/unistd.h | 10 +- include/asm-sparc/ide.h | 2 +- include/asm-sparc/vaddrs.h | 6 +- include/asm-sparc64/parport.h | 16 +- include/linux/affs_fs.h | 1 - include/linux/bfs_fs.h | 3 - include/linux/blk.h | 14 +- include/linux/blkdev.h | 14 +- include/linux/compatmac.h | 163 + include/linux/efs_fs.h | 6 - include/linux/elevator.h | 124 + include/linux/fb.h | 9 +- include/linux/fs.h | 3 + {drivers/char => include/linux}/generic_serial.h | 5 +- include/linux/hfs_fs.h | 1 - include/linux/hpfs_fs.h | 5 - include/linux/if_ppp.h | 10 +- include/linux/iso_fs.h | 1 - include/linux/kmod.h | 2 +- include/linux/minix_fs.h | 1 - include/linux/mm.h | 13 +- include/linux/mmzone.h | 31 +- include/linux/msdos_fs.h | 9 - include/linux/ncp_fs.h | 1 - include/linux/nls.h | 85 +- include/linux/ntfs_fs.h | 4 - include/linux/pci.h | 3 + include/linux/personality.h | 2 + include/linux/ppp_channel.h | 39 +- include/linux/ppp_defs.h | 5 +- include/linux/proc_fs.h | 1 - include/linux/qnx4_fs.h | 1 - include/linux/romfs_fs.h | 1 - include/linux/smb_fs.h | 1 - include/linux/swap.h | 3 +- include/linux/sysv_fs.h | 1 - include/linux/telephony.h | 10 + include/linux/tty.h | 1 + include/linux/udf_fs.h | 1 - include/linux/ufs_fs.h | 1 - include/linux/umsdos_fs.h | 1 - include/linux/usb.h | 46 +- ipc/shm.c | 28 +- kernel/exec_domain.c | 34 +- kernel/exit.c | 7 +- kernel/fork.c | 10 +- kernel/module.c | 5 + kernel/pm.c | 83 +- mm/filemap.c | 577 +++- mm/mlock.c | 5 + mm/mmap.c | 4 + mm/mprotect.c | 5 + mm/mremap.c | 2 +- mm/page_alloc.c | 46 +- mm/vmscan.c | 3 +- net/appletalk/ddp.c | 7 +- net/atm/Makefile | 2 +- net/ax25/af_ax25.c | 145 +- net/econet/af_econet.c | 50 +- net/packet/af_packet.c | 4 +- net/sched/sch_ingress.c | 1 - net/x25/af_x25.c | 6 +- net/x25/x25_link.c | 9 +- scripts/Makefile | 6 + scripts/docgen | 3 + scripts/docproc.c | 78 + scripts/gen-all-syms | 7 + scripts/kernel-doc | 865 ++++++ 488 files changed, 30912 insertions(+), 15617 deletions(-) create mode 100644 Documentation/DocBook/Makefile create mode 100644 Documentation/DocBook/mcabook.tmpl create mode 100644 Documentation/DocBook/parport-multi.fig create mode 100644 Documentation/DocBook/parport-share.fig create mode 100644 Documentation/DocBook/parport-structure.fig create mode 100644 Documentation/DocBook/parportbook.sgml create mode 100644 Documentation/DocBook/videobook.tmpl create mode 100644 Documentation/DocBook/wanbook.tmpl create mode 100644 Documentation/DocBook/z8530book.tmpl create mode 100644 Documentation/networking/comx.txt create mode 100644 Documentation/networking/dmfe.txt create mode 100644 Documentation/sound/ALS create mode 100644 Documentation/video4linux/CQcam.txt create mode 100644 arch/arm/kernel/arch.c create mode 100644 arch/arm/kernel/arch.h create mode 100644 arch/arm/lib/io-shark.c copy arch/arm/{vmlinux-armv.lds.in => vmlinux-armo.lds.in} (80%) rewrite arch/i386/kernel/head.S (68%) rewrite drivers/block/Config.in (80%) create mode 100644 drivers/block/elevator.c create mode 100644 drivers/ide/Config.in copy drivers/{block => ide}/Makefile (56%) rename drivers/{block => ide}/aec6210.c (100%) rename drivers/{block => ide}/ali14xx.c (100%) rename drivers/{block => ide}/alim15x3.c (100%) rename drivers/{block => ide}/amd7409.c (100%) rename drivers/{block => ide}/buddha.c (100%) rename drivers/{block => ide}/cmd640.c (100%) rename drivers/{block => ide}/cmd64x.c (100%) rename drivers/{block => ide}/cs5530.c (100%) rename drivers/{block => ide}/cy82c693.c (100%) rename drivers/{block => ide}/dtc2278.c (100%) rename drivers/{block => ide}/falconide.c (100%) rename drivers/{block => ide}/gayle.c (100%) rename drivers/{block => ide}/hd.c (100%) rename drivers/{block => ide}/hpt34x.c (100%) rename drivers/{block => ide}/hpt366.c (100%) rename drivers/{block => ide}/ht6560b.c (100%) rename drivers/{block => ide}/icside.c (100%) rename drivers/{block => ide}/ide-cd.c (100%) rename drivers/{block => ide}/ide-cd.h (100%) rename drivers/{block => ide}/ide-cs.c (100%) rename drivers/{block => ide}/ide-disk.c (100%) rename drivers/{block => ide}/ide-dma.c (100%) rename drivers/{block => ide}/ide-features.c (100%) rename drivers/{block => ide}/ide-floppy.c (99%) rename drivers/{block => ide}/ide-geometry.c (100%) rename drivers/{block => ide}/ide-pci.c (100%) rename drivers/{block => ide}/ide-pmac.c (100%) rename drivers/{block => ide}/ide-pnp.c (100%) rename drivers/{block => ide}/ide-probe.c (100%) rename drivers/{block => ide}/ide-proc.c (100%) rename drivers/{block => ide}/ide-tape.c (100%) rename drivers/{block => ide}/ide.c (99%) rename drivers/{block => ide}/ide_modes.h (100%) rename drivers/{block => ide}/macide.c (100%) rename drivers/{block => ide}/ns87415.c (100%) rename drivers/{block => ide}/opti621.c (100%) rename drivers/{block => ide}/pdc202xx.c (100%) rename drivers/{block => ide}/pdc4030.c (100%) rename drivers/{block => ide}/pdc4030.h (100%) rename drivers/{block => ide}/piix.c (100%) rename drivers/{block => ide}/q40ide.c (100%) rename drivers/{block => ide}/qd6580.c (100%) rename drivers/{block => ide}/rapide.c (100%) rename drivers/{block => ide}/rz1000.c (100%) rename drivers/{block => ide}/sis5513.c (100%) rename drivers/{block => ide}/sl82c105.c (100%) rename drivers/{block => ide}/trm290.c (100%) rename drivers/{block => ide}/umc8672.c (100%) rename drivers/{block => ide}/via82cxxx.c (100%) rewrite drivers/net/appletalk/Config.in (89%) delete mode 100644 drivers/net/pcmcia/tulip_cb.c create mode 100644 drivers/net/tokenring/lanstreamer.c create mode 100644 drivers/net/tokenring/lanstreamer.h create mode 100644 drivers/net/wan/comx-hw-comx.c create mode 100644 drivers/net/wan/comx-hw-locomx.c create mode 100644 drivers/net/wan/comx-hw-mixcom.c create mode 100644 drivers/net/wan/comx-proto-fr.c create mode 100644 drivers/net/wan/comx-proto-lapb.c create mode 100644 drivers/net/wan/comx-proto-ppp.c create mode 100644 drivers/net/wan/comx.c create mode 100644 drivers/net/wan/comx.h create mode 100644 drivers/net/wan/comxhw.h create mode 100644 drivers/net/wan/hscx.h create mode 100644 drivers/net/wan/mixcom.h create mode 100644 drivers/scsi/sym53c8xx_comm.h create mode 100644 drivers/usb/dsbr100.c rewrite drivers/usb/pegasus.c (88%) rewrite include/asm-alpha/parport.h (85%) rewrite include/asm-arm/parport.h (86%) rewrite include/asm-i386/parport.h (82%) rewrite include/asm-mips/parport.h (92%) rewrite include/asm-mips64/parport.h (92%) create mode 100644 include/linux/compatmac.h create mode 100644 include/linux/elevator.h rename {drivers/char => include/linux}/generic_serial.h (97%) rewrite include/linux/nls.h (60%) create mode 100644 scripts/docgen create mode 100644 scripts/docproc.c create mode 100644 scripts/gen-all-syms create mode 100644 scripts/kernel-doc diff --git a/CREDITS b/CREDITS index 4e1d57242ec..b98bae81098 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 cee6bef35a1..64bdd50f2ca 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 . + + 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 00000000000..6bc727e4391 --- /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.sgml + +z8530book.sgml: z8530book.tmpl + $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \ + z8530book.sgml + +mcabook.sgml: mcabook.tmpl + $(TOPDIR)/scripts/docgen $(TOPDIR)/arch/i386/kernel/mca.c \ + mcabook.sgml + +videobook.sgml: videobook.tmpl + $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \ + 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 00000000000..a8902e333a1 --- /dev/null +++ b/Documentation/DocBook/mcabook.tmpl @@ -0,0 +1,105 @@ + + + + + MCA Driver Programming Interface + + + + Alan + Cox + +
+ alan@redhat.com +
+
+
+ + David + Weinehall + + + Chris + Beauregard + +
+ + + 2000 + Alan Cox + David Weinehall + Chris Beauregard + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + 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. + + + 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. + + + 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. + + + + Known Bugs And Assumptions + + None. + + + + + Public Functions Provided +!Earch/i386/kernel/mca.c + + + + DMA Functions Provided +!Iinclude/asm-i386/mca_dma.h + + +
diff --git a/Documentation/DocBook/parport-multi.fig b/Documentation/DocBook/parport-multi.fig new file mode 100644 index 00000000000..e0517b36fe8 --- /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 00000000000..fe4f37322bc --- /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 00000000000..4299ce68717 --- /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 00000000000..1644748addd --- /dev/null +++ b/Documentation/DocBook/parportbook.sgml @@ -0,0 +1,1747 @@ + + + + + The Parallel Port Subsystem + + + + Tim + Waugh + +
+ twaugh@redhat.com +
+
+
+
+ + + 1999-2000 + Tim Waugh + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + +Design goals + + +The problems + + + + + + +The first parallel port support for Linux came with the line +printer driver, lp. The printer driver is a +character special device, and (in Linux 2.0) had support for writing, +via write, and configuration and statistics +reporting via ioctl. + +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. + +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. + +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 check_region function to make sure +that the IO region associated with the parallel port was free, and +then it would call request_region to allocate it. +The Zip drive used the same mechanism. Whichever driver initialised +first would gain exclusive control of the parallel port. + +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. + +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. + +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). + + + +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: compatibility, +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 compatibility mode +(i.e. normal printer protocol) and reverse nibble mode. + + + + +The solutions + + + +The parport 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. + + + +There are two layers to the +parport 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. + + + +The sharing model provided by the parport +subsystem is one of exclusive access. A device driver, such as the +printer driver, must ask the parport layer for +access to the port, and can only use the port once access has been +granted. When it has finished a transaction, it can +tell the parport layer that it may release the +port for other device drivers to use. + + + +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. + + + + + + + +This technique relies on certain special signals +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. + + + +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. + + + + + + +Standard transfer modes + + + + +The standard transfer modes in use over the +parallel port are defined 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. + +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. + +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. + +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). + +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). + + + + +Structure + + + + + + + + + + + + +Sharing core + + + +At the core of the parport subsystem is the +sharing mechanism (see drivers/parport/share.c). +This module, parport, 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). + + + + +Parports and their overrides + + +The generic parport sharing code doesn't +directly handle the parallel port hardware. That is done instead by +low-level parport drivers. The +function of a low-level parport driver is to +detect parallel ports, register them with the sharing code, and +provide a list of access functions for each port. + +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 +forward direction (that is, the computer drives them), +but some ports allow switching to reverse mode (driven +by the peripheral). There is an access function for examining the +data lines once in reverse mode. + + + + +IEEE 1284 transfer modes + + +Stacked on top of the sharing mechanism, but still in the +parport 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 parport core they must +use the lowest common denominator 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. + +The low-level parport 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. + + + + + + + + +Pardevices and parport_drivers + +When a parallel port device driver (such as +lp) initialises it tells the sharing layer about +itself using parport_register_driver. The +information is put into a struct +parport_driver, which is put into a linked list. The +information in a struct parport_driver really +just amounts to some function pointers to callbacks in the parallel +port device driver. + +During its initialisation, a low-level port driver tells the +sharing layer about all the ports that it has found (using +parport_register_port), and the sharing layer +creates a struct parport for each of them. +Each struct parport contains (among other +things) a pointer to a struct +parport_operations, which is a list of function pointers +for the various operations that can be performed on a port. You can +think of a struct parport as a parallel port +object, if object-orientated programming +is your thing. The parport structures are +chained in a linked list, whose head is portlist +(in drivers/parport/share.c). + +Once the port has been registered, the low-level port driver +announces it. The parport_announce_port function +walks down the list of parallel port device drivers +(struct parport_drivers) calling the +attach function of each. + +Similarly, a low-level port driver can undo the effect of +registering a port with the +parport_unregister_port function, and device +drivers are notified using the detach +callback. + +Device drivers can undo the effect of registering themselves +with the parport_unregister_driver +function. + + + + + + +The IEEE 1284.3 API + +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. + +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 +parport code in Linux. + +At initialisation of the parallel port bus, 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. + +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. + +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! + +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. + + + + + + + + + + + + + + + +Each device in the system is numbered at initialisation (by +parport_daisy_init). You can convert between +this device number and its co-ordinates with +parport_device_num and +parport_device_coords. + + + int parport_device_num + int parport + int mux + int daisy + + + + int parport_device_coords + int devnum + int *parport + int *mux + int *daisy + + +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 -1 is +used to indicate these cases. + +Two functions are provided for finding devices based on their +IEEE 1284 Device ID: parport_find_device and +parport_find_class. + + + int parport_find_device + const char *mfg + const char *mdl + int from + + + + int parport_find_class + parport_device_class cls + int from + + +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 -1 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 +from+1, in other words). + + + + + + +Device driver's view + + + + + + + + + +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 parport interface to find +parallel ports, use them, and share them with other device +drivers. + +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. + +The interactions between the device driver and the +parport layer are as follows. First, the device +driver registers its existence with parport, 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 +parport 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. + +So, the first thing for the device driver to do is tell +parport that it wants to know what parallel ports +are on the system. To do this, it uses the +parport_register_device function: + + + + + + int parport_register_driver + struct parport_driver *driver + + +In other words, the device driver passes pointers to a couple of +functions to parport, and +parport calls attach for +each port that's detected (and detach for each +port that disappears -- yes, this can happen). + +The next thing that happens is that the device driver tells +parport that it thinks there's a device on the +port that it can drive. This typically will happen in the driver's +attach function, and is done with +parport_register_device: + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + void (*kf) + void * + void (*irq_func) + int, void *, struct pt_regs * + int flags + void *handle + + +The port comes from the parameter supplied +to the attach function when it is called, or +alternatively can be found from the list of detected parallel ports +directly with the (now deprecated) +parport_enumerate function. + +The next three parameters, pf, +kf, and irq_func, are +more function pointers. These callback functions get called under +various circumstances, and are always given the +handle as one of their parameters. + +The preemption callback, pf, 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 parport_release. If +pf 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. + +The kick callback, kf, is +called when the port can be claimed for exclusive access; that is, +parport_claim is guaranteed to succeed inside the +kick callback. If the driver wants to claim the port +it should do so; otherwise, it need not take any action. + +The irq_func 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 +request_irq; 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 irq_func function is called. + +None of the callback functions are allowed to block. + +The flags are for telling +parport any requirements or hints that are +useful. The only useful value here (other than +0, which is the usual value) is +PARPORT_DEV_EXCL. The point of that flag is to +request exclusive access at all times---once a driver has successfully +called parport_register_device 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). + +The PARPORT_DEV_EXCL 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! + +Devices can also be registered by device drivers based on their +device numbers (the same device numbers as in the previous +section). + +The parport_open function is similar to +parport_register_device, and +parport_close is the equivalent of +parport_unregister_device. The difference is +that parport_open takes a device number rather +than a pointer to a struct parport. + + + struct pardevice *parport_open + int devnum + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + void parport_close + struct pardevice *dev + + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + void parport_unregister_device + struct pardevice *dev + + +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: + + + + +Once your device driver has registered its device and been +handed a pointer to a struct pardevice, 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. + + + int parport_claim + struct pardevice *dev + + + + int parport_claim_or_block + struct pardevice *dev + + + + void parport_release + struct pardevice *dev + + +To claim access to the port, use +parport_claim or +parport_claim_or_block. The first of these will +not block, and so can be used from interrupt context. If +parport_claim 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. + +The other function, parport_claim_or_block, +will block if necessary to wait for the port to be free. If it slept, +it returns 1; if it succeeded without needing to +sleep it returns 0. If it fails it will return a +negative error code. + +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 parport_release 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. + +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. + +The parport_yield and +parport_yield_blocking 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 timeslice. The default timeslice is half a +second, but it can be adjusted via a /proc +entry. + + + int parport_yield + struct pardevice *dev + + + + int parport_yield_blocking + struct pardevice *dev + + +The first of these, parport_yield, will not +block but as a result may fail. The return value for +parport_yield is the same as for +parport_claim. The blocking version, +parport_yield_blocking, has the same return code +as parport_claim_or_block. + +Once the port has been claimed, the device driver can use the +functions in the struct parport_operations +pointer in the struct parport it has a +pointer to. For example: + + +ops->write_data (port, d); +]]> + +Some of these operations have shortcuts. For +instance, parport_write_data 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 port and +ops). + + + + +Port drivers + + + +To recap, then: + + + + + +The device driver registers itself with parport. + + + + + +A low-level driver finds a parallel port and registers it with +parport (these first two things can happen in +either order). This registration creates a struct +parport which is linked onto a list of known ports. + + + + + +parport calls the attach +function of each registered device driver, passing it the pointer to +the new struct parport. + + + + + +The device driver gets a handle from parport, for +use with +parport_claim/release. This +handle takes the form of a pointer to a struct +pardevice, representing a particular device on the +parallel port, and is acquired using +parport_register_device. + + + + + +The device driver claims the port using +parport_claim (or +function_claim_or_block). + + + + + +Then it goes ahead and uses the port. When finished it releases the +port. + + + + + +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 struct +parport_operations). + + + + + + +A more complete description of which operation is supposed to do +what is available in +Documentation/parport-lowlevel.txt. + + + + +The printer driver + + + + +The printer driver, lp is a character +special device driver and a parport client. As a +character special device driver it registers a struct +file_operations using +register_chrdev, with pointers filled in for +write, ioctl, +open and +release. As a client of +parport, it registers a struct +parport_driver using +parport_register_driver, so that +parport knows to call +lp_attach when a new parallel port is discovered +(and lp_detach when it goes away). + +The parallel port console functionality is also implemented in +lp.c, but that won't be covered here (it's quite +simple though). + +The initialisation of the driver is quite easy to understand +(see lp_init). The lp_table +is an array of structures that contain information about a specific +device (the struct pardevice associated with +it, for example). That array is initialised to sensible values first +of all. + +Next, the printer driver calls +register_chrdev passing it a pointer to +lp_fops, which contains function pointers for the +printer driver's implementation of open, +write, and so on. This part is the same as for +any character special device driver. + +After successfully registering itself as a character special +device driver, the printer driver registers itself as a +parport client using +parport_register_driver. It passes a pointer to +this structure: + + + + +The lp_detach function is not very +interesting (it does nothing); the interesting bit is +lp_attach. 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 auto, +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. + +For each port that the printer driver wants to use (see +lp_register), it calls +parport_register_device and stores the resulting +struct pardevice pointer in the +lp_table. If the user told it to do so, it then +resets the printer. + +The other interesting piece of the printer driver, from the +point of view of parport, is +lp_write. In this function, the user space +process has data that it wants printed, and the printer driver hands +it off to the parport code to deal with. + +The parport functions it uses that we have +not seen yet are parport_negotiate, +parport_set_timeout, and +parport_write. These functions are part of the +IEEE 1284 implementation. + +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. + +The particular transfer mode that the printer driver wants to +use is named in IEEE 1284 as compatibility mode, and +the function to request a particular mode is called +parport_negotiate. + + + int parport_negotiate + struct parport *port + int mode + + +The modes parameter is a symbolic +constant representing an IEEE 1284 mode; in this instance, it is +IEEE1284_MODE_COMPAT. (Compatibility mode is +slightly different to the other modes---rather than being specifically +requested, it is the default until another mode is selected.) + +Back to lp_write then. First, access to +the parallel port is secured with +parport_claim_or_block. 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 parport_negotiate. + +The main work is done in the write-loop. In particular, the +line that hands the data over to parport +reads: + + + + +The parport_write 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: + + + ssize_t parport_write + struct parport *port + const void *buf + size_t len + + + + ssize_t parport_read + struct parport *port + void *buf + size_t len + + +(parport_read does what it sounds like, but +only works for modes in which reverse transfer is possible. Of +course, parport_write only works in modes in +which forward transfer is possible, too.) + +The buf pointer should be to kernel space +memory, and obviously the len parameter +specifies the amount of data to transfer. + +In fact what parport_write does is call the +appropriate block transfer function from the struct +parport_operations: + + + + +The transfer code in parport will tolerate +a data transfer stall only for so long, and this timeout can be +specified with parport_set_timeout, which returns +the previous timeout: + + + long parport_set_timeout + struct pardevice *dev + long inactivity + + +This timeout is specific to the device, and is restored on +parport_claim. + + + + +User-level device drivers + + + +Introduction to ppdev + +The printer is accessible through /dev/lp0; +in the same way, the parallel port itself is accessible through +/dev/parport0. The difference is in the level of +control that you have over the wires in the parallel port +cable. + +With the printer driver, a user-space program (such as the +printer spooler) can send bytes in printer protocol. +Briefly, this means that for each byte, the eight data lines are set +up, then a strobe line tells the printer to look at the +data lines, and the printer sets an acknowledgement +line to say that it got the byte. The printer driver also allows the +user-space program to read bytes in nibble mode, 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). + +In contrast, the ppdev driver (accessed via +/dev/parport0) allows you to: + + + + + +examine status lines, + + + + + +set control lines, + + + + + +set/examine data lines (and control the direction of the data lines), + + + + + +wait for an interrupt (triggered by one of the status lines), + + + + + +find out how many new interrupts have occurred, + + + + + +set up a response to an interrupt, + + + + + +use IEEE 1284 negotiation (for telling peripheral which transfer mode, +to use) + + + + + +transfer data using a specified IEEE 1284 mode. + + + + + + + + +User-level or kernel-level driver? + +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. + +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. + + + + +Programming interface + +The ppdev interface is largely the same as +that of other character special devices, in that it supports +open, close, +read, write, and +ioctl. + + +Starting and stopping: <function>open</function> and +<function>close</function> + +The device node /dev/parport0 represents +any device that is connected to parport0, 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 /dev/parport0 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. + + + +Control: <function>ioctl</function> + +Most of the control is done, naturally enough, via the +ioctl call. Using ioctl, +the user-land driver can control both the ppdev +driver in the kernel and the physical parallel port itself. The +ioctl call takes as parameters a file descriptor +(the one returned from opening the device node), a command, and +optionally (a pointer to) some data. + + +PPCLAIM + + +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 ppdev driver and not the port, such as +PPSETMODE; they can be performed while access to +the port is not claimed. + + + +PPEXCL + + +Instructs the kernel driver to forbid any sharing of the port +with other drivers, i.e. it requests exclusivity. The +PPEXCL command is only valid when the port is not +already claimed for use, and it may mean that the next +PPCLAIM ioctl will fail: +some other driver may already have registered itself on that +port. + +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). + +Note that the PPEXCL +ioctl doesn't actually claim the port there and +then---action is deferred until the PPCLAIM +ioctl is performed. + + + +PPRELEASE + + +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). + + + +PPYIELD + + +Yields the port to another driver. This +ioctl 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. + +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 +PPEXCL). + + + +PPNEGOT + + +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. + +An IEEE 1284 compliant device will start out in compatibility +mode, and then the host can negotiate to another mode (such as +ECP). + +The ioctl parameter should be a pointer to +an int; values for this are in +parport.h and include: + + +IEEE1284_MODE_COMPAT +IEEE1284_MODE_NIBBLE +IEEE1284_MODE_BYTE +IEEE1284_MODE_EPP +IEEE1284_MODE_ECP + + +The PPNEGOT ioctl +actually does two things: it performs the on-the-wire negotiation, and +it sets the behaviour of subsequent +read/write calls so that +they use that mode (but see PPSETMODE). + + + +PPSETMODE + + +Sets which IEEE 1284 protocol to use for the +read and write calls. + +The ioctl parameter should be a pointer to +an int. + + + +PPGETTIME + + +Retrieves the time-out value. The read and +write calls will time out if the peripheral +doesn't respond quickly enough. The PPGETTIME +ioctl retrieves the length of time that the +peripheral is allowed to have before giving up. + +The ioctl parameter should be a pointer to +a struct timeval. + + + +PPSETTIME + + +Sets the time-out. The ioctl parameter +should be a pointer to a struct +timeval. + + + +PPWCONTROL + + +Sets the control lines. The ioctl +parameter is a pointer to an unsigned char, the bitwise +OR of the control line values in +parport.h. + + + +PPRCONTROL + + +Returns the last value written to the control register, in the +form of an unsigned char: each bit corresponds to a +control line (although some are unused). The +ioctl parameter should be a pointer to an +unsigned char. + +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. + +The control lines bits are defined in +parport.h: + + +PARPORT_CONTROL_STROBE +PARPORT_CONTROL_AUTOFD +PARPORT_CONTROL_SELECT +PARPORT_CONTROL_INIT + + + + +PPFCONTROL + + +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 +PPRCONTROL, make the change, and then use +PPWCONTROL. 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 +PPRCONTROL, ppdev must +remember the state of the control lines anyway. + +The PPFCONTROL ioctl +is for frobbing control lines, and is like +PPWCONTROL but acts on a restricted set of +control lines. The ioctl parameter is a pointer +to a struct ppdev_frob_struct: + + + + + +The mask and +val fields are bitwise ORs of control line +names (such as in PPWCONTROL). The operation +performed by PPFCONTROL is: + + + + + +In other words, the signals named in +mask are set to the values in +val. + + + +PPRSTATUS + + +Returns an unsigned char containing bits set for +each status line that is set (for instance, +PARPORT_STATUS_BUSY). The +ioctl parameter should be a pointer to an +unsigned char. + + + +PPDATADIR + + +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.) + +This is only needed in conjunction with +PPWDATA or PPRDATA. + +The ioctl parameter is a pointer to an +int. If the int is zero, the drivers are +turned on (forward direction); if non-zero, the drivers are turned off +(reverse direction). + + + +PPWDATA + + +Sets the data lines (if in forward mode). The +ioctl parameter is a pointer to an unsigned +char. + + + +PPRDATA + + +Reads the data lines (if in reverse mode). The +ioctl parameter is a pointer to an unsigned +char. + + + +PPCLRIRQ + + +Clears the interrupt count. The ppdev +driver keeps a count of interrupts as they are triggered. +PPCLRIRQ stores this count in an +int, a pointer to which is passed in as the +ioctl parameter. + +In addition, the interrupt count is reset to zero. + + + +PPWCTLONIRQ + + +Set a trigger response. Afterwards when an interrupt is +triggered, the interrupt handler will set the control lines as +requested. The ioctl parameter is a pointer to +an unsigned char, which is interpreted in the same way as +for PPWCONTROL. + +The reason for this ioctl is simply speed. +Without this ioctl, responding to an interrupt +would start in the interrupt handler, switch context to the user-land +driver via poll or select, +and then switch context back to the kernel in order to handle +PPWCONTROL. Doing the whole lot in the interrupt +handler is a lot faster. + + + + + + + + + + +Transferring data: <function>read</function> and +<function>write</function> + +Transferring data using read and +write is straightforward. The data is +transferring using the current IEEE 1284 mode (see the +PPSETMODE ioctl). For modes +which can only transfer data in one direction, only the appropriate +function will work, of course. + + + +Waiting for events: <function>poll</function> and +<function>select</function> + +The ppdev driver provides user-land device +drivers with the ability to wait for interrupts, and this is done +using poll (and select, +which is implemented in terms of poll). + +When a user-land device driver wants to wait for an interrupt, +it sleeps with poll. When the interrupt arrives, +ppdev wakes it up (with a read +event, although strictly speaking there is nothing to actually +read). + + + + + + +Examples + +Presented here are two demonstrations of how to write a simple +printer driver for ppdev. Firstly we will use +the write function, and after that we will drive +the control and data lines directly. + +The first thing to do is to actually open the device. + + + +Here name should be something along the lines +of "/dev/parport0". (If you don't have any +/dev/parport files, you can make them with +mknod; they are character special device nodes with +major 99.) + +In order to do anything with the port we need to claim access to +it. + + + +Our printer driver will copy its input (from +stdin) 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 compatibility mode. + + 0) { + int written = write_printer (fd, ptr, got); + + if (written < 0) { + perror ("write"); + close (fd); + return 1; + } + + ptr += written; + got -= written; + } + } +]]> + +The write_printer 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 +write_printer: + + + +We hand the data to the kernel-level driver (using +write) and it handles the printer +protocol. + +Now let's do it the hard way! In this particular example there +is no practical reason to do anything other than just call +write, 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. + +So, here is the alternative implementation of +write_printer (for brevity, error checking has +been omitted): + + + +To show a bit more of the ppdev interface, +here is a small piece of code that is intended to mimic the printer's +side of printer protocol. + + 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); + } +]]> + + + + +
\ No newline at end of file diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl new file mode 100644 index 00000000000..c152abf3134 --- /dev/null +++ b/Documentation/DocBook/videobook.tmpl @@ -0,0 +1,1663 @@ + + + + + Video4Linux Programming + + + + Alan + Cox + +
+ alan@redhat.com +
+
+
+
+ + + 2000 + Alan Cox + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + Parts of this document first appeared in Linux Magazine under a + ninety day exclusivity. + + + 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. + + + + Radio Devices + + 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. + + + 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. + + + Registering Radio Devices + + The Video4linux core provides an interface for registering devices. The + first step in writing our radio card driver is to register it. + + + + +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 */ +}; + + + + + This declares our video4linux device driver interface. The VID_TYPE_ value + defines what kind of an interface we are, and defines basic capabilities. + + + 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. + + + The VID_HARDWARE_ types are unique to each device. Numbers are assigned by + alan@redhat.com 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. + + + 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. + + + 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. + + + Having the structure defined is all very well but we now need to register it + with the kernel. + + + + +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(&my_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + request_region(io, MY_IO_SIZE, "myradio"); + return 0; +} + + + + 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. + + + 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. + + + The types available are + + Device Types + + + + 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. + + VFL_TYPE_GRABBER<>/dev/video{n}<> + Video capture devices and also -- counter-intuitively for the name -- + hardware video playback devices such as MPEG2 cards. + + 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. + + 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). + + + + +
+ + We are most definitely a radio. + + + 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. + +
+ + Opening And Closing The Radio + + + 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. + + + + +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; +} + + + + 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. + + + + +static int radio_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + + + + At close time we simply need to reduce the user count and allow the module + to become unloadable. + + + 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. + + + + The Ioctl Interface + + This leaves the ioctl routine, without which the driver will not be + terribly useful to anyone. + + + + +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, &v, sizeof(v))) + return -EFAULT; + return 0; + } + + + + 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 + + struct video_capability fields + + + + name<>The device text name. This is intended for the user. + + 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. + + audios<>The number of audio inputs on this device. For our + radio there is only one audio input. + + minwidth,minheight<>The smallest size the card is capable of capturing + images in. We set these to zero. Radios do not + capture pictures + + maxwidth,maxheight<>The largest image size the card is capable of + capturing. For our radio we report 0. + + + type<>This reports the capabilities of the device, and + matches the field we filled in in the struct + video_device when registering. + + + +
+ + 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. + + + 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. + + + + + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&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(&v, arg, sizeof(v))!=0) + return -EFAULT; + return 0; + } + + + + 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. + + + 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. + + + The video_tuner structure has the following fields + + struct video_tuner fields + + + + int tunerThe number of the tuner in question + + char name[32]A text description of this tuner. "FM" will do fine. + This is intended for the application. + + u32 flags + Tuner capability flags + + + u16 modeThe current reception mode + + + u16 signalThe 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. + + + u32 rangelow, rangehigh + The range of frequencies supported by the radio + or TV. It is scaled according to the VIDEO_TUNER_LOW + flag. + + + + +
+ + struct video_tuner flags + + + + VIDEO_TUNER_PALA PAL TV tuner + + VIDEO_TUNER_NTSCAn NTSC (US) TV tuner + + VIDEO_TUNER_SECAMA SECAM (French) TV tuner + + 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 + + + VIDEO_TUNER_NORMThe tuner can set its format + + VIDEO_TUNER_STEREO_ONThe tuner is currently receiving a stereo signal + + + +
+ + struct video_tuner modes + + + + VIDEO_MODE_PAL<>PAL Format + + VIDEO_MODE_NTSC<>NTSC Format (USA) + + VIDEO_MODE_SECAM<>French Format + + VIDEO_MODE_AUTO<>A device that does not need to do + TV format switching + + + +
+ + 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. + + + 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. + + + 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. + + + 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. + + + + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner != 0) + return -EINVAL; + return 0; + } + + + + 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. + + + 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. + + + +static unsigned long current_freq; + + + + case VIDIOCGFREQ: + if(copy_to_user(arg, &current_freq, + sizeof(unsigned long)) + return -EFAULT; + return 0; + + + + 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. + + + + + case VIDIOCSFREQ: + { + u32 freq; + if(copy_from_user(arg, &freq, + sizeof(unsigned long))!=0) + return -EFAULT; + if(hardware_set_freq(freq)<0) + return -EINVAL; + current_freq = freq; + return 0; + } + + + + 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. + + + 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. + + + There are two ioctls with audio we need to support + + + +static int current_volume=0; + + case VIDIOCGAUDIO: + { + struct video_audio v; + if(copy_from_user(&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. &v, sizeof(v))) + return -EFAULT; + return 0; + } + + + + 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. + + + Then we fill in the video_audio structure. This has the following format + + struct video_audio fields + + + + audio<>The input the user wishes to query + + volume<>The volume setting on a scale of 0-65535 + + base<>The base level on a scale of 0-65535 + + treble<>The treble level on a scale of 0-65535 + + flags<>The features this audio device supports + + + name<>A text name to display to the user. We picked + "Radio" as it explains things quite nicely. + + 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. + + + balance<>The stereo balance on a scale of 0-65535, 32768 is + middle. + + step<>The step by which the volume control jumps. This is + used to help make it easy for applications to set + slider behaviour. + + + +
+ + struct video_audio flags + + + + VIDEO_AUDIO_MUTE<>The audio is currently muted. We + could fake this in our driver but we + choose not to bother. + + VIDEO_AUDIO_MUTABLE<>The input has a mute option + + VIDEO_AUDIO_TREBLE<>The input has a treble control + + VIDEO_AUDIO_BASS<>The input has a base control + + + +
+ + struct video_audio modes + + + + VIDEO_SOUND_MONO<>Mono sound + + VIDEO_SOUND_STEREO<>Stereo sound + + VIDEO_SOUND_LANG1<>Alternative language 1 (TV specific) + + VIDEO_SOUND_LANG2<>Alternative language 2 (TV specific) + + + +
+ + Having filled in the structure we copy it back to user space. + + + 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. + + + + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + current_volume = v/16384; + hardware_set_volume(current_volume); + return 0; + } + + + + 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 + + + + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + current_volume = v/16384; + if(v.flags&VIDEO_AUDIO_MUTE) + hardware_set_volume(0); + else + hardware_set_volume(current_volume); + current_muted = v.flags & + VIDEO_AUDIO_MUTE; + return 0; + } + + + + 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. + + + We now have a working radio ioctl handler. So we just wrap up the function + + + + + } + return -ENOIOCTLCMD; +} + + + + and pass the Video4Linux layer back an error so that it knows we did not + understand the request we got passed. + +
+ + Module Wrapper + + Finally we add in the usual module wrapping and the driver is done. + + + +#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(&my_radio); + release_region(io, MY_IO_SIZE); +} + +#endif + + + + 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. + + + 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. + + + 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). + + +
+ + Video Capture Devices + + Video Capture Device Types + + 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. + + + 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 . + + + 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. + + + 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. + + + 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. + + + 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. + + + + Registering Video Capture Devices + + This time we need to add more functions for our camera device. + + +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 */ +}; + + + 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. + + + We use the extra video capability flags that did not apply to the + radio interface. The video related flags are + + Capture Capabilities + + + +VID_TYPE_CAPTURE<>We support image capture + +VID_TYPE_TELETEXT<>A teletext capture device (vbi{n]) + +VID_TYPE_OVERLAY<>The image can be directly overlaid onto the + frame buffer + +VID_TYPE_CHROMAKEY<>Chromakey can be used to select which parts + of the image to display + +VID_TYPE_CLIPPING<>It is possible to give the board a list of + rectangles to draw around. + +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 + +VID_TYPE_SCALES<>The image can be scaled to various sizes, + rather than being a single fixed size. + +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. + +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. + + + +
+ + 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. + + + 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(). + + + + +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(&my_camera, + VFL_TYPE_GRABBER)==-1) + return -EINVAL; + request_region(io, MY_IO_SIZE, "mycamera"); + return 0; +} + + + + 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. + +
+ + Opening And Closing The Capture Device + + + +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)<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; +} + + + 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. + + + + Interrupt Handling + + 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. + + + Our interrupt routine needs to do little if we assume the card can simply + queue one frame to be read after it captures it. + + + + +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(&capture_wait); +} + + + 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. + + + 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. + + + + +static int camera_poll(struct video_device *dev, + struct file *file, struct poll_table *wait) +{ + poll_wait(file, &capture_wait, wait); + if(capture_read) + return POLLIN|POLLRDNORM; + return 0; +} + + + + 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. + + + + Reading The Video Image + + + +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(&capture_wait, &wait); + + while(!capture_ready) + { + if(file->flags&O_NDELAY) + { + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; + return -EWOULDBLOCK; + } + if(signal_pending(current)) + { + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; + return -ERESTARTSYS; + } + schedule(); + current->state = TASK_INTERRUPTIBLE; + } + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; + + + + 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. + + + 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. + + + 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. + + + + + + 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<len; i++) + { + put_user(inb(io+IMAGE_DATA), ptr); + ptr++; + } + + hardware_restart_capture(); + + return i; +} + + + + 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. + + + Having captured the image and put it into user space we can kick the card to + get the next frame acquired. + + + + Video Ioctl Handling + + 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. + + + + + +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, &v, sizeof(v))) + return -EFAULT; + return 0; + } + + + + + 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. + + + 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. + + + To keep things simple we report no audio and no tuning capabilities at all. + + + + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&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(&v, arg, sizeof(v))) + return -EFAULT; + return 0; + } + + + + + 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. + + + 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 + + struct video_channel fields + + + + + channel<>The channel number we are selecting + + 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" + + flags<>Channel properties + + type<>Input type + + norm<>The current television encoding being used + if relevant for this channel. + + + + +
+ struct video_channel flags + + + + VIDEO_VC_TUNER<>Channel has a tuner. + + VIDEO_VC_AUDIO<>Channel has audio. + + + +
+ struct video_channel types + + + + VIDEO_TYPE_TV<>Television input. + + VIDEO_TYPE_CAMERA<>Fixed camera input. + + 0<>Type is unknown. + + + +
+ struct video_channel norms + + + + VIDEO_MODE_PAL<>PAL encoded Television + + VIDEO_MODE_NTSC<>NTSC (US) encoded Television + + VIDEO_MODE_SECAM<>SECAM (French) Televison + + VIDEO_MODE_AUTO<>Automatic switching, or format does not + matter + + + +
+ + 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. + + + + + case VIDIOCSCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.channel != 0) + return -EINVAL; + if(v.norm != VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + + + + + 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. + + + 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. + + + + + 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(&v, arg, + sizeof(v))) + return -EFAULT; + return 0; + } + + + + + 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. + + + 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 + + Framebuffer Encodings + + + + GREY<>Linear greyscale. This is for simple cameras and the + like + + RGB565<>The top 5 bits hold 32 red levels, the next six bits + hold green and the low 5 bits hold blue. + + RGB555<>The top bit is clear. The red green and blue levels + each occupy five bits. + + + +
+ + Additional modes are support for YUV capture formats. These are common for + TV and video conferencing applications. + + + 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. + + + + + case VIDIOCSPICT: + { + struct video_picture v; + if(copy_from_user(&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; + } + + + + + 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. + + + 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. + + + 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. + + + + +static struct video_buffer capture_fb; + + case VIDIOCGFBUF: + { + if(copy_to_user(arg, &capture_fb, + sizeof(capture_fb))) + return -EFAULT; + return 0; + + } + + + + + 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. + + + + case VIDIOCSFBUF: + { + struct video_buffer v; + + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.width!=320 && v.width!=640) + return -EINVAL; + if(v.height!=200 && v.height!=240 + && v.height!=400 + && v.height !=480) + return -EINVAL; + memcpy(&capture_fb, &v, sizeof(v)); + hardware_set_fb(&v); + return 0; + } + + + + + + 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. + + + 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. + + + 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. + + + 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. + + struct video_window fields + + + + width<>The width in pixels of the desired image. The card + may use a smaller size if this size is not available + + height<>The height of the image. The card may use a smaller + size if this size is not available. + + 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. + + 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. + + chromakey<>The colour (expressed in RGB32 format) for the + chromakey colour if chroma keying is being used. + + clips<>An array of rectangles that must not be drawn + over. + + clipcount<>The number of clips in this array. + + + +
+ + Each clip is a struct video_clip which has the following fields + + video_clip fields + + + + x, y<>Co-ordinates relative to the display + + width, height<>Width and height in pixels + + next<>A spare field for the application to use + + + +
+ + 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. + + + 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. + + + + + case VIDIOCGWIN: + { + if(copy_to_user(arg, &capture_win, + sizeof(capture_win))) + return -EFAULT; + return 0; + } + + + case VIDIOCSWIN: + { + struct video_window v; + if(copy_from_user(&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(&capture_win, &v, sizeof(v)); + capture_w = v.width; + capture_h = v.height; + return 0; + } + + + + + 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. + + + 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. + + + + + 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; + } + + + + + 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. + + + + + default: + return -ENOIOCTLCMD; + } +} + + + + 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. + +
+ + Other Functionality + + 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. + + +
+ + Known Bugs And Assumptions + + + Multiple Opens + + + The driver assumes multiple opens should not be allowed. A driver + can work around this but not cleanly. + + + + API Deficiences + + + The existing API poorly reflects compression capable devices. There + are plans afoot to merge V4L, V4L2 and some other ideas into a + better interface. + + + + + + + + + Public Functions Provided +!Edrivers/char/videodev.c + + +
diff --git a/Documentation/DocBook/wanbook.tmpl b/Documentation/DocBook/wanbook.tmpl new file mode 100644 index 00000000000..9b18bb2d891 --- /dev/null +++ b/Documentation/DocBook/wanbook.tmpl @@ -0,0 +1,97 @@ + + + + + Synchronous PPP and Cisco HDLC Programming Guide + + + + Alan + Cox + +
+ alan@redhat.com +
+
+
+
+ + + 2000 + Alan Cox + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + 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 + + + + Known Bugs And Assumptions + + + PPP is minimal + + + The current PPP implementation is very basic, although sufficient + for most wan usages. + + + + Cisco HDLC Quirks + + + 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. + + + + + + + + + Public Functions Provided +!Edrivers/net/wan/syncppp.c + + +
diff --git a/Documentation/DocBook/z8530book.tmpl b/Documentation/DocBook/z8530book.tmpl new file mode 100644 index 00000000000..364c9126da0 --- /dev/null +++ b/Documentation/DocBook/z8530book.tmpl @@ -0,0 +1,383 @@ + + + + + Z8530 Programming Guide + + + + Alan + Cox + +
+ alan@redhat.com +
+
+
+
+ + + 2000 + Alan Cox + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + 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. + + + 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 + + + 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. + + + + + Driver Modes + + 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). + + + 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. + + + 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. + + + 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. + + + + + Using the Z85230 driver + + 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. + + + 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 z8530_interrupt. 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. + + + 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 &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. + + + 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. + + + 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. + + + Having intialised the device you can then call + z8530_init. 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. + + + Once you have called z8530_init you can also make use of the utility + function z8530_describe. This provides a + consistant reporting format for the Z8530 devices, and allows all + the drivers to provide consistent reporting. + + + + + Attaching Network Interfaces + + 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. + + + The network device used for each channel should be pointed to by + the netdevice field of each channel. The dev-> 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. + + + 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. + + + 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 + sppp_attach 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. + + + 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. + + + + + Configuring And Activating The Port + + 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. + + + z8530_channel_load 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. + + + 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. + + + 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. + + + 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. + + + 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 z8530_null_rx + to discard the data. + + + To active PIO mode sending and receiving the + z8530_sync_open 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 z8530_sync_close + function shuts down a PIO channel. This must be done before the + channel is opened again and before the driver shuts down + and unloads. + + + 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 z8530_sync_dma_open + function. On failure a non zero error value is returned. + Once this mode is activated it can be shut down by calling the + z8530_sync_dma_close. You must call the close + function matching the open mode you used. + + + 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 z8530_sync_txdma_open + . This returns a non zero error code on failure. The + z8530_sync_txdma_close function closes down + the Z8530 interface from this mode. + + + + + Network Layer Functions + + 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. + + + The function z8530_queue_xmit 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. + + + The function z8530_get_stats 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. + + + + + Porting The Z8530 Driver + + 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. + + + 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 + z8530_read_port and z8530_write_port + is intended to be all that is required to port this + driver layer. + + + + + Known Bugs And Assumptions + + + Interrupt Locking + + + 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. + + + + Occasional Failures + + + 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. + + + + + + + + + Public Functions Provided +!Edrivers/net/wan/z85230.c + + + + Internal Functions +!Idrivers/net/wan/z85230.c + + +
diff --git a/Documentation/networking/comx.txt b/Documentation/networking/comx.txt new file mode 100644 index 00000000000..a58e78d905f --- /dev/null +++ b/Documentation/networking/comx.txt @@ -0,0 +1,248 @@ + + COMX drivers for the 2.2 kernel + +Originally written by: Tivadar Szemethy, +Currently maintained by: Gergely Madarasz + +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 +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 /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 00000000000..158e94ba6fe --- /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 00000000000..db98daf30ff --- /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 8d0fd215da9..b7e1334cd2f 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 c13fc4c5227..757f7458fd1 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 00000000000..6d54c07c044 --- /dev/null +++ b/Documentation/video4linux/CQcam.txt @@ -0,0 +1,414 @@ +c-qcam - Connectix Color QuickCam video4linux kernel driver + +Copyright (C) 1999 Dave Forrest + 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 + * Copied from http://www.tazenda.demon.co.uk/phil/vgrabber.c + * with minor modifications (Dave Forrest, drf5n@virginia.edu). + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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= 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 9d0709a576c..4d829877223 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 20c1de3a05f..6762ce048d9 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 +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 f7787297dd4..4f9affb7e8e 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 6e760c917f1..df7ae01ca2f 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 cc244e560e7..c4752a2167c 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 6b4033705ec..ad8e9423ab9 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 fca3e8c9220..37d79da39e4 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 4c7a5fe196f..ff4f954dfba 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 e43a8d2a9a4..45c89eeb301 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 3920edadef0..c454c135060 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 00000000000..8645497ae8e --- /dev/null +++ b/arch/arm/kernel/arch.c @@ -0,0 +1,322 @@ +/* + * linux/arch/arm/kernel/arch.c + * + * Architecture specifics + */ +#include +#include +#include + +#include +#include +#include + +#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 00000000000..b7635b11e5b --- /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 c445daeee47..5a1b4ed31b0 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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 7a1b57c229f..1b0a064d058 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 35e71a4a958..27a280b8bd0 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 0692d1a6cc8..0ace4c898b0 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 5815529aa9e..6a3429bdd1e 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 #include #include #include -#include #include #include #include @@ -23,6 +22,8 @@ #include #include +#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)< 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 5046a3377b2..510a3845173 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 00000000000..41595bc6fb9 --- /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 + +#include + +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 c58e6664772..e7b8c8bb9ea 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 c6c776acf85..9814d2a9c3b 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-armv.lds.in b/arch/arm/vmlinux-armo.lds.in similarity index 80% copy from arch/arm/vmlinux-armv.lds.in copy to arch/arm/vmlinux-armo.lds.in index d2a79ca9df1..9d5ee058d0b 100644 --- a/arch/arm/vmlinux-armv.lds.in +++ b/arch/arm/vmlinux-armo.lds.in @@ -7,12 +7,16 @@ ENTRY(stext) SECTIONS { . = TEXTADDR; - .init : { /* Init code and data */ - __init_begin = .; + + .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 = .; @@ -21,11 +25,20 @@ SECTIONS __initcall_start = .; *(.initcall.init) __initcall_end = .; - . = ALIGN(4096); + . = ALIGN(32768); __init_end = .; } - .text : { /* Real text segment */ + .init.task : { + *(.init.task) + } + + /DISCARD/ : { /* Exit code and data */ + *(.text.exit) + *(.data.exit) + } + + .text : { _text = .; /* Text and read-only data */ *(.text) *(.fixup) @@ -47,17 +60,9 @@ SECTIONS _etext = .; /* End of text section */ } - . = ALIGN(8192); - .data : { /* - * first, the init task union, aligned - * to an 8192 byte boundary. - */ - *(.init.task) - - /* - * then the cacheline aligned data + * The cacheline aligned data */ . = ALIGN(32); *(.data.cacheline_aligned) @@ -71,12 +76,13 @@ SECTIONS _edata = .; } + .bss : { - __bss_start = .; /* BSS */ + __bss_start = .; /* BSS */ *(.bss) - *(COMMON) - _end = . ; + _end = . ; } + /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } diff --git a/arch/arm/vmlinux-armv.lds.in b/arch/arm/vmlinux-armv.lds.in index d2a79ca9df1..1476ba93927 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 bf16abf2429..2149dcf5130 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 e1ba021e1ec..2a083ad09bc 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 813a2ecea41..0f3f5cb8ab4 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 6671bb35bb7..525bb2c07c8 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 d95bbe6b196..8efa2832a73 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 0c3cae5d9ad..56500e46610 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 dissimilarity index 68% index f7138faa364..3340946be8a 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -1,695 +1,452 @@ -/* - * linux/arch/i386/head.S -- the 32-bit startup code. - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Enhanced CPU detection and feature setting code by Mike Jagdis - * and Martin Mares, November 1997. - */ - -.text -#include -#include -#include -#include -#include -#include -#include - - -#define CL_MAGIC_ADDR 0x90020 -#define CL_MAGIC 0xA33F -#define CL_BASE_ADDR 0x90000 -#define CL_OFFSET 0x90022 - -/* - * References to members of the boot_cpu_data structure. - */ - -#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data) -#define X86 CPU_PARAMS+0 -#define X86_VENDOR CPU_PARAMS+1 -#define X86_MODEL CPU_PARAMS+2 -#define X86_MASK CPU_PARAMS+3 -#define X86_HARD_MATH CPU_PARAMS+6 -#define X86_CPUID CPU_PARAMS+8 -#define X86_CAPABILITY CPU_PARAMS+12 -#define X86_VENDOR_ID CPU_PARAMS+16 - -/* - * swapper_pg_dir is the main page directory, address 0x00101000 - */ -ENTRY(stext) -ENTRY(_stext) -startup_32: -/* - * Set segments to known values - */ - cld - movl $(__KERNEL_DS),%eax - movl %eax,%ds - movl %eax,%es - movl %eax,%fs - movl %eax,%gs -#ifdef __SMP__ - orw %bx,%bx - jz 1f -/* - * New page tables may be in 4Mbyte page mode and may - * be using the global pages. - * - * NOTE! We have to correct for the fact that we're - * not yet offset PAGE_OFFSET.. - */ -#define cr4_bits mmu_cr4_features-__PAGE_OFFSET - movl %cr4,%eax # Turn on 4Mb pages - orl cr4_bits,%eax - movl %eax,%cr4 -#endif -/* - * Setup paging (the tables are already set up, just switch them on) - */ -1: - movl $0x101000,%eax - movl %eax,%cr3 /* set the page table pointer.. */ - movl %cr0,%eax - orl $0x80000000,%eax - movl %eax,%cr0 /* ..and set paging (PG) bit */ - jmp 1f /* flush the prefetch-queue */ -1: - movl $1f,%eax - jmp *%eax /* make sure eip is relocated */ -1: - /* Set up the stack pointer */ - lss stack_start,%esp - -#ifdef __SMP__ - orw %bx,%bx - jz 1f /* Initial CPU cleans BSS */ - pushl $0 - popfl - jmp checkCPUtype -1: -#endif __SMP__ -/* - * Clear BSS first so that there are no surprises... - */ - xorl %eax,%eax - movl $ SYMBOL_NAME(__bss_start),%edi - movl $ SYMBOL_NAME(_end),%ecx - subl %edi,%ecx - cld - rep - stosb -/* - * start system 32-bit setup. We need to re-do some of the things done - * in 16-bit mode for the "real" operations. - */ - call setup_idt -/* - * Initialize eflags. Some BIOS's leave bits like NT set. This would - * confuse the debugger if this code is traced. - * XXX - best to initialize before switching to protected mode. - */ - pushl $0 - popfl -/* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - */ - movl $0x90000,%esi - movl $ SYMBOL_NAME(empty_zero_page),%edi - movl $512,%ecx - cld - rep - movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - cmpw $(CL_MAGIC),CL_MAGIC_ADDR - jne 1f - movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi - movzwl CL_OFFSET,%esi - addl $(CL_BASE_ADDR),%esi - movl $512,%ecx - rep - movsl -1: -#ifdef __SMP__ -checkCPUtype: -#endif - - movl $-1,X86_CPUID # -1 for no CPUID initially - -/* check if it is 486 or 386. */ -/* - * XXX - this does a lot of unnecessary setup. Alignment checks don't - * apply at our cpl of 0 and the stack ought to be aligned already, and - * we don't need to preserve eflags. - */ - - movl $3,X86 # at least 386 - pushfl # push EFLAGS - popl %eax # get EFLAGS - movl %eax,%ecx # save original EFLAGS - xorl $0x40000,%eax # flip AC bit in EFLAGS - pushl %eax # copy to EFLAGS - popfl # set EFLAGS - pushfl # get new EFLAGS - popl %eax # put it in eax - xorl %ecx,%eax # change in flags - andl $0x40000,%eax # check if AC bit changed - je is386 - - movl $4,X86 # at least 486 - movl %ecx,%eax - xorl $0x200000,%eax # check ID flag - pushl %eax - popfl # if we are on a straight 486DX, SX, or - pushfl # 487SX we can't change it - popl %eax - xorl %ecx,%eax - pushl %ecx # restore original EFLAGS - popfl - andl $0x200000,%eax - je is486 - - /* get vendor info */ - xorl %eax,%eax # call CPUID with 0 -> return vendor ID - cpuid - movl %eax,X86_CPUID # save CPUID level - movl %ebx,X86_VENDOR_ID # lo 4 chars - movl %edx,X86_VENDOR_ID+4 # next 4 chars - movl %ecx,X86_VENDOR_ID+8 # last 4 chars - - orl %eax,%eax # do we have processor info as well? - je is486 - - movl $1,%eax # Use the CPUID instruction to get CPU type - cpuid - movb %al,%cl # save reg for future use - andb $0x0f,%ah # mask processor family - movb %ah,X86 - andb $0xf0,%al # mask model - shrb $4,%al - movb %al,X86_MODEL - andb $0x0f,%cl # mask mask revision - movb %cl,X86_MASK - movl %edx,X86_CAPABILITY - -is486: - movl %cr0,%eax # 486 or better - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f - -is386: pushl %ecx # restore original EFLAGS - popfl - movl %cr0,%eax # 386 - andl $0x80000011,%eax # Save PG,PE,ET - orl $2,%eax # set MP -2: movl %eax,%cr0 - call check_x87 -4: -#ifdef __SMP__ - incb ready -#endif - lgdt gdt_descr - lidt idt_descr - ljmp $(__KERNEL_CS),$1f -1: movl $(__KERNEL_DS),%eax # reload all the segment registers - movl %eax,%ds # after changing gdt. - movl %eax,%es - movl %eax,%fs - movl %eax,%gs -#ifdef __SMP__ - movl $(__KERNEL_DS), %eax - movl %eax,%ss # Reload the stack pointer (segment only) -#else - lss stack_start,%esp # Load processor stack -#endif - xorl %eax,%eax - lldt %ax - cld # gcc2 wants the direction flag cleared at all times -#ifdef __SMP__ - movb ready, %cl - cmpb $1,%cl - je 1f # the first CPU calls start_kernel - # all other CPUs call initialize_secondary - call SYMBOL_NAME(initialize_secondary) - jmp L6 -1: -#endif - call SYMBOL_NAME(start_kernel) -L6: - jmp L6 # main should never return here, but - # just in case, we know what happens. - -#ifdef __SMP__ -ready: .byte 0 -#endif - -/* - * We depend on ET to be correct. This checks for 287/387. - */ -check_x87: - movb $0,X86_HARD_MATH - clts - fninit - fstsw %ax - cmpb $0,%al - je 1f - movl %cr0,%eax /* no coprocessor: have to set bits */ - xorl $4,%eax /* set EM */ - movl %eax,%cr0 - ret - ALIGN -1: movb $1,X86_HARD_MATH - .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ - ret - -/* - * setup_idt - * - * sets up a idt with 256 entries pointing to - * ignore_int, interrupt gates. It doesn't actually load - * idt - that can be done only after paging has been enabled - * and the kernel moved to PAGE_OFFSET. Interrupts - * are enabled elsewhere, when we can be relatively - * sure everything is ok. - */ -setup_idt: - lea ignore_int,%edx - movl $(__KERNEL_CS << 16),%eax - movw %dx,%ax /* selector = 0x0010 = cs */ - movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ - - lea SYMBOL_NAME(idt_table),%edi - mov $256,%ecx -rp_sidt: - movl %eax,(%edi) - movl %edx,4(%edi) - addl $8,%edi - dec %ecx - jne rp_sidt - ret - -ENTRY(stack_start) - .long SYMBOL_NAME(init_task_union)+8192 - .long __KERNEL_DS - -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt\n" - ALIGN -ignore_int: - cld - pushl %eax - pushl %ecx - pushl %edx - pushl %es - pushl %ds - movl $(__KERNEL_DS),%eax - movl %eax,%ds - movl %eax,%es - pushl $int_msg - call SYMBOL_NAME(printk) - popl %eax - popl %ds - popl %es - popl %edx - popl %ecx - popl %eax - iret - -/* - * The interrupt descriptor table has room for 256 idt's, - * the global descriptor table is dependent on the number - * of tasks we can have.. - */ -#define IDT_ENTRIES 256 -#define GDT_ENTRIES (__TSS(NR_CPUS)) - - -.globl SYMBOL_NAME(idt) -.globl SYMBOL_NAME(gdt) - - ALIGN - .word 0 -idt_descr: - .word IDT_ENTRIES*8-1 # idt contains 256 entries -SYMBOL_NAME(idt): - .long SYMBOL_NAME(idt_table) - - .word 0 -gdt_descr: - .word GDT_ENTRIES*8-1 -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 - * PAGE_OFFSET. - */ -.org 0x1000 -ENTRY(swapper_pg_dir) - .long 0x00102007 - .long 0x00103007 - .fill BOOT_USER_PGD_PTRS-2,4,0 - /* default: 766 entries */ - .long 0x00102007 - .long 0x00103007 - /* default: 254 entries */ - .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 - */ -.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 - -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 -.org 0x4000 -ENTRY(empty_zero_page) - -.org 0x5000 -ENTRY(empty_bad_page) - -.org 0x6000 -ENTRY(empty_bad_pte_table) - -#if CONFIG_X86_PAE - - .org 0x7000 - ENTRY(empty_bad_pmd_table) - - .org 0x8000 - -#else - - .org 0x7000 - -#endif - -/* - * This starts the data section. Note that the above is all - * in the text section because it has alignment requirements - * that we cannot fulfill any other way. - */ -.data - -ALIGN -/* - * This contains typically 140 quadwords, depending on NR_CPUS. - * - * NOTE! Make sure the gdt descriptor in head.S matches this if you - * change anything. - */ -ENTRY(gdt_table) - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* not used */ - .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ - .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ - .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ - .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ - .quad 0x0000000000000000 /* not used */ - /* - * The APM segments have byte granularity and their bases - * and limits are set at run time. - */ - .quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */ - .quad 0x00409a0000000000 /* 0x48 APM CS code */ - .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ - .quad 0x0040920000000000 /* 0x58 APM DS data */ - .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ - -/* - * This is to aid debugging, the various locking macros will be putting - * code fragments here. When an oops occurs we'd rather know that it's - * inside the .text.lock section rather than as some offset from whatever - * function happens to be last in the .text segment. - */ -.section .text.lock -ENTRY(stext_lock) +/* + * linux/arch/i386/head.S -- the 32-bit startup code. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Enhanced CPU detection and feature setting code by Mike Jagdis + * and Martin Mares, November 1997. + */ + +.text +#include +#include +#include +#include +#include +#include +#include + + +#define CL_MAGIC_ADDR 0x90020 +#define CL_MAGIC 0xA33F +#define CL_BASE_ADDR 0x90000 +#define CL_OFFSET 0x90022 + +/* + * References to members of the boot_cpu_data structure. + */ + +#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data) +#define X86 CPU_PARAMS+0 +#define X86_VENDOR CPU_PARAMS+1 +#define X86_MODEL CPU_PARAMS+2 +#define X86_MASK CPU_PARAMS+3 +#define X86_HARD_MATH CPU_PARAMS+6 +#define X86_CPUID CPU_PARAMS+8 +#define X86_CAPABILITY CPU_PARAMS+12 +#define X86_VENDOR_ID CPU_PARAMS+16 + +/* + * swapper_pg_dir is the main page directory, address 0x00101000 + */ +ENTRY(stext) +ENTRY(_stext) +startup_32: +/* + * Set segments to known values + */ + cld + movl $(__KERNEL_DS),%eax + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs +#ifdef __SMP__ + orw %bx,%bx + jz 1f +/* + * New page tables may be in 4Mbyte page mode and may + * be using the global pages. + * + * NOTE! We have to correct for the fact that we're + * not yet offset PAGE_OFFSET.. + */ +#define cr4_bits mmu_cr4_features-__PAGE_OFFSET + movl %cr4,%eax # Turn on 4Mb pages + orl cr4_bits,%eax + movl %eax,%cr4 +#endif +/* + * Setup paging (intialize tables, then switch them on) + */ +1: + 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 + movl %eax,%cr0 /* ..and set paging (PG) bit */ + jmp 1f /* flush the prefetch-queue */ +1: + movl $1f,%eax + jmp *%eax /* make sure eip is relocated */ +1: + /* Set up the stack pointer */ + lss stack_start,%esp + +#ifdef __SMP__ + orw %bx,%bx + jz 1f /* Initial CPU cleans BSS */ + pushl $0 + popfl + jmp checkCPUtype +1: +#endif __SMP__ +/* + * Clear BSS first so that there are no surprises... + */ + xorl %eax,%eax + movl $ SYMBOL_NAME(__bss_start),%edi + movl $ SYMBOL_NAME(_end),%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * start system 32-bit setup. We need to re-do some of the things done + * in 16-bit mode for the "real" operations. + */ + call setup_idt +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ + pushl $0 + popfl +/* + * Copy bootup parameters out of the way. First 2kB of + * _empty_zero_page is for boot parameters, second 2kB + * is for the command line. + */ + movl $0x90000,%esi + movl $ SYMBOL_NAME(empty_zero_page),%edi + movl $512,%ecx + cld + rep + movsl + xorl %eax,%eax + movl $512,%ecx + rep + stosl + cmpw $(CL_MAGIC),CL_MAGIC_ADDR + jne 1f + movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi + movzwl CL_OFFSET,%esi + addl $(CL_BASE_ADDR),%esi + movl $512,%ecx + rep + movsl +1: +#ifdef __SMP__ +checkCPUtype: +#endif + + movl $-1,X86_CPUID # -1 for no CPUID initially + +/* check if it is 486 or 386. */ +/* + * XXX - this does a lot of unnecessary setup. Alignment checks don't + * apply at our cpl of 0 and the stack ought to be aligned already, and + * we don't need to preserve eflags. + */ + + movl $3,X86 # at least 386 + pushfl # push EFLAGS + popl %eax # get EFLAGS + movl %eax,%ecx # save original EFLAGS + xorl $0x40000,%eax # flip AC bit in EFLAGS + pushl %eax # copy to EFLAGS + popfl # set EFLAGS + pushfl # get new EFLAGS + popl %eax # put it in eax + xorl %ecx,%eax # change in flags + andl $0x40000,%eax # check if AC bit changed + je is386 + + movl $4,X86 # at least 486 + movl %ecx,%eax + xorl $0x200000,%eax # check ID flag + pushl %eax + popfl # if we are on a straight 486DX, SX, or + pushfl # 487SX we can't change it + popl %eax + xorl %ecx,%eax + pushl %ecx # restore original EFLAGS + popfl + andl $0x200000,%eax + je is486 + + /* get vendor info */ + xorl %eax,%eax # call CPUID with 0 -> return vendor ID + cpuid + movl %eax,X86_CPUID # save CPUID level + movl %ebx,X86_VENDOR_ID # lo 4 chars + movl %edx,X86_VENDOR_ID+4 # next 4 chars + movl %ecx,X86_VENDOR_ID+8 # last 4 chars + + orl %eax,%eax # do we have processor info as well? + je is486 + + movl $1,%eax # Use the CPUID instruction to get CPU type + cpuid + movb %al,%cl # save reg for future use + andb $0x0f,%ah # mask processor family + movb %ah,X86 + andb $0xf0,%al # mask model + shrb $4,%al + movb %al,X86_MODEL + andb $0x0f,%cl # mask mask revision + movb %cl,X86_MASK + movl %edx,X86_CAPABILITY + +is486: + movl %cr0,%eax # 486 or better + andl $0x80000011,%eax # Save PG,PE,ET + orl $0x50022,%eax # set AM, WP, NE and MP + jmp 2f + +is386: pushl %ecx # restore original EFLAGS + popfl + movl %cr0,%eax # 386 + andl $0x80000011,%eax # Save PG,PE,ET + orl $2,%eax # set MP +2: movl %eax,%cr0 + call check_x87 +4: +#ifdef __SMP__ + incb ready +#endif + lgdt gdt_descr + lidt idt_descr + ljmp $(__KERNEL_CS),$1f +1: movl $(__KERNEL_DS),%eax # reload all the segment registers + movl %eax,%ds # after changing gdt. + movl %eax,%es + movl %eax,%fs + movl %eax,%gs +#ifdef __SMP__ + movl $(__KERNEL_DS), %eax + movl %eax,%ss # Reload the stack pointer (segment only) +#else + lss stack_start,%esp # Load processor stack +#endif + xorl %eax,%eax + lldt %ax + cld # gcc2 wants the direction flag cleared at all times +#ifdef __SMP__ + movb ready, %cl + cmpb $1,%cl + je 1f # the first CPU calls start_kernel + # all other CPUs call initialize_secondary + call SYMBOL_NAME(initialize_secondary) + jmp L6 +1: +#endif + call SYMBOL_NAME(start_kernel) +L6: + jmp L6 # main should never return here, but + # just in case, we know what happens. + +#ifdef __SMP__ +ready: .byte 0 +#endif + +/* + * We depend on ET to be correct. This checks for 287/387. + */ +check_x87: + movb $0,X86_HARD_MATH + clts + fninit + fstsw %ax + cmpb $0,%al + je 1f + movl %cr0,%eax /* no coprocessor: have to set bits */ + xorl $4,%eax /* set EM */ + movl %eax,%cr0 + ret + ALIGN +1: movb $1,X86_HARD_MATH + .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ + ret + +/* + * setup_idt + * + * sets up a idt with 256 entries pointing to + * ignore_int, interrupt gates. It doesn't actually load + * idt - that can be done only after paging has been enabled + * and the kernel moved to PAGE_OFFSET. Interrupts + * are enabled elsewhere, when we can be relatively + * sure everything is ok. + */ +setup_idt: + lea ignore_int,%edx + movl $(__KERNEL_CS << 16),%eax + movw %dx,%ax /* selector = 0x0010 = cs */ + movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ + + lea SYMBOL_NAME(idt_table),%edi + mov $256,%ecx +rp_sidt: + movl %eax,(%edi) + movl %edx,4(%edi) + addl $8,%edi + dec %ecx + jne rp_sidt + ret + +ENTRY(stack_start) + .long SYMBOL_NAME(init_task_union)+8192 + .long __KERNEL_DS + +/* This is the default interrupt "handler" :-) */ +int_msg: + .asciz "Unknown interrupt\n" + ALIGN +ignore_int: + cld + pushl %eax + pushl %ecx + pushl %edx + pushl %es + pushl %ds + movl $(__KERNEL_DS),%eax + movl %eax,%ds + movl %eax,%es + pushl $int_msg + call SYMBOL_NAME(printk) + popl %eax + popl %ds + popl %es + popl %edx + popl %ecx + popl %eax + iret + +/* + * The interrupt descriptor table has room for 256 idt's, + * the global descriptor table is dependent on the number + * of tasks we can have.. + */ +#define IDT_ENTRIES 256 +#define GDT_ENTRIES (__TSS(NR_CPUS)) + + +.globl SYMBOL_NAME(idt) +.globl SYMBOL_NAME(gdt) + + ALIGN + .word 0 +idt_descr: + .word IDT_ENTRIES*8-1 # idt contains 256 entries +SYMBOL_NAME(idt): + .long SYMBOL_NAME(idt_table) + + .word 0 +gdt_descr: + .word GDT_ENTRIES*8-1 +SYMBOL_NAME(gdt): + .long SYMBOL_NAME(gdt_table) + +/* + * 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 +ENTRY(swapper_pg_dir) + .long 0x00102007 + .long 0x00103007 + .fill BOOT_USER_PGD_PTRS-2,4,0 + /* default: 766 entries */ + .long 0x00102007 + .long 0x00103007 + /* default: 254 entries */ + .fill BOOT_KERNEL_PGD_PTRS-2,4,0 + +/* + * 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) + +.org 0x3000 +ENTRY(pg1) + +/* + * empty_zero_page must immediately follow the page tables ! (The + * initialization loop counts until empty_zero_page) + */ + +.org 0x4000 +ENTRY(empty_zero_page) + +.org 0x5000 +ENTRY(empty_bad_page) + +.org 0x6000 +ENTRY(empty_bad_pte_table) + +#if CONFIG_X86_PAE + + .org 0x7000 + ENTRY(empty_bad_pmd_table) + + .org 0x8000 + +#else + + .org 0x7000 + +#endif + +/* + * This starts the data section. Note that the above is all + * in the text section because it has alignment requirements + * that we cannot fulfill any other way. + */ +.data + +ALIGN +/* + * This contains typically 140 quadwords, depending on NR_CPUS. + * + * NOTE! Make sure the gdt descriptor in head.S matches this if you + * change anything. + */ +ENTRY(gdt_table) + .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x0000000000000000 /* not used */ + .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ + .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ + .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ + .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ + .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* not used */ + /* + * The APM segments have byte granularity and their bases + * and limits are set at run time. + */ + .quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */ + .quad 0x00409a0000000000 /* 0x48 APM CS code */ + .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ + .quad 0x0040920000000000 /* 0x58 APM DS data */ + .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + +/* + * This is to aid debugging, the various locking macros will be putting + * code fragments here. When an oops occurs we'd rather know that it's + * inside the .text.lock section rather than as some offset from whatever + * function happens to be last in the .text segment. + */ +.section .text.lock +ENTRY(stext_lock) diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index a3389c5f09d..e21c33b6fe6 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 120c861e799..d2ba2fd9c02 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 7061e55db3c..f0f0f9f371c 100644 --- a/arch/i386/kernel/mca.c +++ b/arch/i386/kernel/mca.c @@ -34,6 +34,7 @@ * - switched to regular procfs methods. */ +#include #include #include #include @@ -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 cc9c7eafe68..1d6203f658f 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. The starting (base) address of the region. 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. The register. If this is less than 0 then <> and <> must diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index b5602ebecf5..a12b6b73d75 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 84e20b225b2..b4a7753b8cc 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 6918451a6b0..ca5eeb796e5 100644 --- a/arch/i386/lib/delay.c +++ b/arch/i386/lib/delay.c @@ -12,12 +12,38 @@ #include #include +#include #ifdef __SMP__ #include #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 3d1dd7e0256..e22e1dd01dd 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 a965998891f..f0e36f6b2b3 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 5b12f470eb2..52b81739e82 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 cbea6ba6303..e11c32b95bc 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 81cf6cf74b7..05200cb4238 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 c26394e3344..1f70c46ad1f 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 1fda5e771f7..90b6b48829b 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 1fda5e771f7..90b6b48829b 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 fa9e68e6fcd..35055ac68b9 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 #include -#include - #define DLINFO_ITEMS 12 #include @@ -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 84ab4142540..d5edf16604e 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 be0501f60da..5efcf06e6e2 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 f34f2e63a4e..8838fed35a7 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 f34f2e63a4e..8838fed35a7 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 d739e50c1cf..af3dd1d87e4 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 9d09499c667..246fd834614 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 7d0ab5fa086..38d9dc72e12 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 bcab91ec6c1..5b5288b3040 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 fd858e00e33..c711ee931ce 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 ea5851d3925..a3f90122045 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 b75edb4181f..04049be7aa5 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 #include -/* 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 756e531c203..adb0ebe4c6a 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 e719d92142f..de483af6eb4 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 6c46c60f0e4..8a9d5491306 100644 --- a/arch/sparc/kernel/sys_solaris.c +++ b/arch/sparc/kernel/sys_solaris.c @@ -15,12 +15,14 @@ #include #include +/* 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 1d6f208f66f..262f6afdd64 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 8746958d738..42c072164f8 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 47c6a5afd21..a1fb222c160 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 a61d4d04984..db85cfe1e79 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 009c506a0fa..3e95ed9cfaf 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 39a000ef370..d3a3814a841 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 #include #include +#include #include #include #include @@ -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 1fc0b1ba5bf..d4ecb0f4fc6 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 9673cdd36e9..f7f5964e901 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 1f7ab3fef35..0a0edbf8291 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 9e2bd4118f2..b77a27236dd 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 17562bafd28..ca78499b1e5 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 1d46532331b..eca864b624b 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 dissimilarity index 80% index 0e29e3a2e15..51a6db89c24 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -1,260 +1,65 @@ -# -# Block device driver configuration -# -mainmenu_option next_comment -comment 'Block devices' - -tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD -if [ "$CONFIG_AMIGA" = "y" ]; then - tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY -fi -if [ "$CONFIG_ATARI" = "y" ]; then - tristate 'Atari floppy support' CONFIG_ATARI_FLOPPY -fi -if [ "$CONFIG_MAC" = "y" ]; then - if [ "$CONFIG_EXPERIMENTAL" = "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 -if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM -fi -if [ "$CONFIG_ATARI" = "y" ]; then - tristate 'Atari ACSI support' CONFIG_ATARI_ACSI - if [ "$CONFIG_ATARI_ACSI" != "n" ]; then - comment 'Some devices (e.g. CD jukebox) support multiple LUNs' - bool ' Probe all LUNs on each ACSI device' CONFIG_ACSI_MULTI_LUN - tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM - fi -fi -if [ "$CONFIG_PCI" = "y" ]; then - tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA -fi - -comment 'Additional Block Devices' - -tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP -if [ "$CONFIG_NET" = "y" ]; then - tristate 'Network block device support' CONFIG_BLK_DEV_NBD -fi -bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD -if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then - tristate ' Linear (append) mode' CONFIG_MD_LINEAR - tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED -# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING -# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 -fi -#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then -# bool ' Boot support (linear, striped)' CONFIG_MD_BOOT -#fi -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 +# +# Block device driver configuration +# +mainmenu_option next_comment +comment 'Block devices' + +tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD +if [ "$CONFIG_AMIGA" = "y" ]; then + tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY +fi +if [ "$CONFIG_ATARI" = "y" ]; then + tristate 'Atari floppy support' CONFIG_ATARI_FLOPPY +fi +if [ "$CONFIG_MAC" = "y" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP + fi +fi +if [ "$CONFIG_MCA" = "y" ]; then + tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2 +fi +if [ "$CONFIG_ZORRO" = "y" ]; then + tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM +fi +if [ "$CONFIG_ATARI" = "y" ]; then + tristate 'Atari ACSI support' CONFIG_ATARI_ACSI + if [ "$CONFIG_ATARI_ACSI" != "n" ]; then + comment 'Some devices (e.g. CD jukebox) support multiple LUNs' + bool ' Probe all LUNs on each ACSI device' CONFIG_ACSI_MULTI_LUN + 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' + +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +if [ "$CONFIG_NET" = "y" ]; then + tristate 'Network block device support' CONFIG_BLK_DEV_NBD +fi +bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD +if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then + tristate ' Linear (append) mode' CONFIG_MD_LINEAR + tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED +# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING +# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 +fi +#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then +# bool ' Boot support (linear, striped)' CONFIG_MD_BOOT +#fi +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 + +endmenu diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index e969e2dd701..87c597c3186 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 6882d03f396..9f5c813d769 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 591ff9310b5..c709ebdc908 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 00000000000..11532f8fd90 --- /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 SuSE + */ + +#include +#include +#include +#include +#include + +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 c14591b9218..63c5e4b8526 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -28,8 +28,6 @@ #include -#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 e7df93ac6bf..fe0d33a77ec 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 18f867c0450..21720c81ad3 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 19e252968c2..66dfc0fe525 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 ab3d88ab51a..3a2cd7e21ee 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 #include diff --git a/drivers/char/console.c b/drivers/char/console.c index 9d8bf6cb03a..93e71018880 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 865681956e3..ebf0671f442 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 * */ diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c index a71d6dde98a..1bb7bd6124a 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 * */ #define __NO_VERSION__ -#include #include "drmP.h" #include "linux/un.h" diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c index d7f8bdf2bcc..a8919d83d9b 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 * */ diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c index ea08a859e37..0ec14ede5d5 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 * */ diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c index c26953c1d8e..19e5da3b7ba 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 * */ diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 320db51ebe0..fe0f8defec2 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 * * Acknowledgements: * Dec 1999, Richard Henderson , move to generic cmpxchg. diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index fce2df7ec94..3a371c23d1d 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 * */ @@ -50,6 +49,10 @@ #ifdef CONFIG_MTRR #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include +#include +#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 24b17356bdd..a823db35638 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 + * Daryll Strauss * */ @@ -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 3b1592180e0..1f8c0a7de90 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 * */ diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c index 028772f26af..6df4440f37d 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 * */ -#include #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 15e77dca9ee..a87655cb975 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 * */ diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c index f416a99afe7..7a0115e8651 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 * */ diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c index 886ef661c59..13bb606595b 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 * */ diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c index b84561f2e51..212ed18edec 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 * */ @@ -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 e8c1eff1017..2523eb21ad5 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 * */ diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c index af8d510b583..a778a153911 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 * */ diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c index 33a5b20e817..4d5d1a964b4 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 * */ @@ -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 0c3c541d056..22bf59ff39f 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 * */ diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c index f56e2af95bc..82b2ac9a258 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 + * Daryll Strauss * */ -#include #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 bdff05ee19c..4c0c3282b24 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 + * Daryll Strauss * */ diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c index d649a6e75d8..b4c4c5bbf0a 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 * */ diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 95a6c4370d7..212525df759 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -18,90 +18,12 @@ #include #include #include -#include - - -#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 -#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 +#include +#include +#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 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 3bf3fbf2801..e9a34fbf3dc 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -26,9 +26,6 @@ #include #include -#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 d3687d9f69e..53939959fe8 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 199818ebf01..5894353e380 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 #include @@ -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 0da69c55c8f..f447dbbbcdf 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 + * who actually finally proved there really was a race. */ #include @@ -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 59853da1f16..4336009fc1a 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 8f25926e98d..9331852e15c 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 523ac09558c..8b53dbd0df5 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 9c3f2e2e55b..2d160acfd8c 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 7caf1058adb..1b85830345e 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 @@ -221,6 +231,9 @@ #include "sxboards.h" #include "sxwindow.h" +#include +#include +#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 - -#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 - - -#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 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;inbytes)?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;ihw_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;ihw_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 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 e046482e473..662b27ca2aa 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 45636c9e905..2f0f5428e68 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 ed504dcfec9..e1e504b793b 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 f0b1aaf4dd0..0a4e65c57e4 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -24,10 +24,9 @@ #include #include #include +#include -#if LINUX_VERSION_CODE >= 0x020100 #include -#endif #include #include @@ -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 00000000000..bcd17994bdb --- /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/block/Makefile b/drivers/ide/Makefile similarity index 56% copy from drivers/block/Makefile copy to drivers/ide/Makefile index 6882d03f396..699c48b8240 100644 --- a/drivers/block/Makefile +++ b/drivers/ide/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the kernel block device drivers. +# 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 @@ -14,94 +14,17 @@ # In the future, some of these should be built conditionally. # -SUB_DIRS := +SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) paride +ALL_SUB_DIRS := $(SUB_DIRS) - -L_TARGET := block.a -L_OBJS := genhd.o ide-geometry.o +L_TARGET := ide.a +L_OBJS := ide-geometry.o M_OBJS := -MOD_LIST_NAME := BLOCK_MODULES -LX_OBJS := ll_rw_blk.o blkpg.o +MOD_LIST_NAME := IDE_MODULES +LX_OBJS := MX_OBJS := -ifeq ($(CONFIG_MAC_FLOPPY),y) -L_OBJS += swim3.o -endif - -ifeq ($(CONFIG_BLK_DEV_FD),y) -L_OBJS += floppy.o -else - ifeq ($(CONFIG_BLK_DEV_FD),m) - M_OBJS += floppy.o - endif -endif - -ifeq ($(CONFIG_AMIGA_FLOPPY),y) - L_OBJS += amiflop.o -else - ifeq ($(CONFIG_AMIGA_FLOPPY),m) - M_OBJS += amiflop.o - endif -endif - -ifeq ($(CONFIG_ATARI_FLOPPY),y) - L_OBJS += ataflop.o -else - ifeq ($(CONFIG_ATARI_FLOPPY),m) - M_OBJS += ataflop.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_SWIM_IOP),y) - L_OBJS += swim_iop.o -endif - -ifeq ($(CONFIG_ATARI_ACSI),y) - LX_OBJS += acsi.o -else - ifeq ($(CONFIG_ATARI_ACSI),m) - MX_OBJS += acsi.o - endif -endif - -ifeq ($(CONFIG_ATARI_SLM),y) - L_OBJS += acsi_slm.o -else - ifeq ($(CONFIG_ATARI_SLM),m) - M_OBJS += acsi_slm.o - endif -endif - -ifeq ($(CONFIG_AMIGA_Z2RAM),y) -L_OBJS += z2ram.o -else - ifeq ($(CONFIG_AMIGA_Z2RAM),m) - M_OBJS += z2ram.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_RAM),y) -L_OBJS += rd.o -else - ifeq ($(CONFIG_BLK_DEV_RAM),m) - M_OBJS += rd.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_LOOP),y) -LX_OBJS += loop.o -else - ifeq ($(CONFIG_BLK_DEV_LOOP),m) - MX_OBJS += loop.o - endif -endif - -# -# IDE-STUFF -# - ifeq ($(CONFIG_BLK_DEV_AEC6210),y) IDE_OBJS += aec6210.o endif @@ -306,116 +229,6 @@ else endif endif -ifeq ($(CONFIG_BLK_DEV_PS2),y) -L_OBJS += ps2esdi.o -else - ifeq ($(CONFIG_BLK_DEV_PS2),m) - M_OBJS += ps2esdi.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_XD),y) -L_OBJS += xd.o -else - ifeq ($(CONFIG_BLK_DEV_XD),m) - M_OBJS += xd.o - endif -endif - -ifeq ($(CONFIG_BLK_CPQ_DA),y) -L_OBJS += cpqarray.o -else - ifeq ($(CONFIG_BLK_CPQ_DA),m) - M_OBJS += cpqarray.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_DAC960),y) -LX_OBJS += DAC960.o -else - ifeq ($(CONFIG_BLK_DEV_DAC960),m) - MX_OBJS += DAC960.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_LVM),y) -L_OBJS += lvm.o lvm-snap.o -else - ifeq ($(CONFIG_BLK_DEV_LVM),m) - M_OBJS += lvm-mod.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_MD),y) -LX_OBJS += md.o - -ifeq ($(CONFIG_MD_LINEAR),y) -L_OBJS += linear.o -else - ifeq ($(CONFIG_MD_LINEAR),m) - M_OBJS += linear.o - endif -endif - -ifeq ($(CONFIG_MD_STRIPED),y) -L_OBJS += raid0.o -else - ifeq ($(CONFIG_MD_STRIPED),m) - M_OBJS += raid0.o - endif -endif - -ifeq ($(CONFIG_MD_MIRRORING),y) -L_OBJS += raid1.o -else - ifeq ($(CONFIG_MD_MIRRORING),m) - M_OBJS += raid1.o - endif -endif - -ifeq ($(CONFIG_MD_RAID5),y) -L_OBJS += raid5.o -else - ifeq ($(CONFIG_MD_RAID5),m) - M_OBJS += raid5.o - endif -endif - -ifeq ($(CONFIG_MD_TRANSLUCENT),y) -L_OBJS += translucent.o -else - ifeq ($(CONFIG_MD_TRANSLUCENT),m) - M_OBJS += translucent.o - endif -endif - -ifeq ($(CONFIG_MD_HSM),y) -L_OBJS += hsm.o -else - ifeq ($(CONFIG_MD_HSM),m) - M_OBJS += hsm.o - endif -endif - -endif - -ifeq ($(CONFIG_BLK_DEV_NBD),y) -L_OBJS += nbd.o -else - ifeq ($(CONFIG_BLK_DEV_NBD),m) - M_OBJS += nbd.o - endif -endif - -ifeq ($(CONFIG_PARIDE),y) -SUB_DIRS += paride -MOD_IN_SUB_DIRS += paride -else - ifeq ($(CONFIG_PARIDE),m) - MOD_IN_SUB_DIRS += paride - endif -endif - include $(TOPDIR)/Rules.make ide-mod.o: ide.o ide-features.o $(IDE_OBJS) @@ -423,6 +236,3 @@ ide-mod.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/aec6210.c b/drivers/ide/aec6210.c similarity index 100% rename from drivers/block/aec6210.c rename to drivers/ide/aec6210.c diff --git a/drivers/block/ali14xx.c b/drivers/ide/ali14xx.c similarity index 100% rename from drivers/block/ali14xx.c rename to drivers/ide/ali14xx.c diff --git a/drivers/block/alim15x3.c b/drivers/ide/alim15x3.c similarity index 100% rename from drivers/block/alim15x3.c rename to drivers/ide/alim15x3.c diff --git a/drivers/block/amd7409.c b/drivers/ide/amd7409.c similarity index 100% rename from drivers/block/amd7409.c rename to drivers/ide/amd7409.c diff --git a/drivers/block/buddha.c b/drivers/ide/buddha.c similarity index 100% rename from drivers/block/buddha.c rename to drivers/ide/buddha.c diff --git a/drivers/block/cmd640.c b/drivers/ide/cmd640.c similarity index 100% rename from drivers/block/cmd640.c rename to drivers/ide/cmd640.c diff --git a/drivers/block/cmd64x.c b/drivers/ide/cmd64x.c similarity index 100% rename from drivers/block/cmd64x.c rename to drivers/ide/cmd64x.c diff --git a/drivers/block/cs5530.c b/drivers/ide/cs5530.c similarity index 100% rename from drivers/block/cs5530.c rename to drivers/ide/cs5530.c diff --git a/drivers/block/cy82c693.c b/drivers/ide/cy82c693.c similarity index 100% rename from drivers/block/cy82c693.c rename to drivers/ide/cy82c693.c diff --git a/drivers/block/dtc2278.c b/drivers/ide/dtc2278.c similarity index 100% rename from drivers/block/dtc2278.c rename to drivers/ide/dtc2278.c diff --git a/drivers/block/falconide.c b/drivers/ide/falconide.c similarity index 100% rename from drivers/block/falconide.c rename to drivers/ide/falconide.c diff --git a/drivers/block/gayle.c b/drivers/ide/gayle.c similarity index 100% rename from drivers/block/gayle.c rename to drivers/ide/gayle.c diff --git a/drivers/block/hd.c b/drivers/ide/hd.c similarity index 100% rename from drivers/block/hd.c rename to drivers/ide/hd.c diff --git a/drivers/block/hpt34x.c b/drivers/ide/hpt34x.c similarity index 100% rename from drivers/block/hpt34x.c rename to drivers/ide/hpt34x.c diff --git a/drivers/block/hpt366.c b/drivers/ide/hpt366.c similarity index 100% rename from drivers/block/hpt366.c rename to drivers/ide/hpt366.c diff --git a/drivers/block/ht6560b.c b/drivers/ide/ht6560b.c similarity index 100% rename from drivers/block/ht6560b.c rename to drivers/ide/ht6560b.c diff --git a/drivers/block/icside.c b/drivers/ide/icside.c similarity index 100% rename from drivers/block/icside.c rename to drivers/ide/icside.c diff --git a/drivers/block/ide-cd.c b/drivers/ide/ide-cd.c similarity index 100% rename from drivers/block/ide-cd.c rename to drivers/ide/ide-cd.c diff --git a/drivers/block/ide-cd.h b/drivers/ide/ide-cd.h similarity index 100% rename from drivers/block/ide-cd.h rename to drivers/ide/ide-cd.h diff --git a/drivers/block/ide-cs.c b/drivers/ide/ide-cs.c similarity index 100% rename from drivers/block/ide-cs.c rename to drivers/ide/ide-cs.c diff --git a/drivers/block/ide-disk.c b/drivers/ide/ide-disk.c similarity index 100% rename from drivers/block/ide-disk.c rename to drivers/ide/ide-disk.c diff --git a/drivers/block/ide-dma.c b/drivers/ide/ide-dma.c similarity index 100% rename from drivers/block/ide-dma.c rename to drivers/ide/ide-dma.c diff --git a/drivers/block/ide-features.c b/drivers/ide/ide-features.c similarity index 100% rename from drivers/block/ide-features.c rename to drivers/ide/ide-features.c diff --git a/drivers/block/ide-floppy.c b/drivers/ide/ide-floppy.c similarity index 99% rename from drivers/block/ide-floppy.c rename to drivers/ide/ide-floppy.c index bee6a4c6c9d..58eb2411d82 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 similarity index 100% rename from drivers/block/ide-geometry.c rename to drivers/ide/ide-geometry.c diff --git a/drivers/block/ide-pci.c b/drivers/ide/ide-pci.c similarity index 100% rename from drivers/block/ide-pci.c rename to drivers/ide/ide-pci.c diff --git a/drivers/block/ide-pmac.c b/drivers/ide/ide-pmac.c similarity index 100% rename from drivers/block/ide-pmac.c rename to drivers/ide/ide-pmac.c diff --git a/drivers/block/ide-pnp.c b/drivers/ide/ide-pnp.c similarity index 100% rename from drivers/block/ide-pnp.c rename to drivers/ide/ide-pnp.c diff --git a/drivers/block/ide-probe.c b/drivers/ide/ide-probe.c similarity index 100% rename from drivers/block/ide-probe.c rename to drivers/ide/ide-probe.c diff --git a/drivers/block/ide-proc.c b/drivers/ide/ide-proc.c similarity index 100% rename from drivers/block/ide-proc.c rename to drivers/ide/ide-proc.c diff --git a/drivers/block/ide-tape.c b/drivers/ide/ide-tape.c similarity index 100% rename from drivers/block/ide-tape.c rename to drivers/ide/ide-tape.c diff --git a/drivers/block/ide.c b/drivers/ide/ide.c similarity index 99% rename from drivers/block/ide.c rename to drivers/ide/ide.c index 326533e7fd0..9c409dfb69f 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 similarity index 100% rename from drivers/block/ide_modes.h rename to drivers/ide/ide_modes.h diff --git a/drivers/block/macide.c b/drivers/ide/macide.c similarity index 100% rename from drivers/block/macide.c rename to drivers/ide/macide.c diff --git a/drivers/block/ns87415.c b/drivers/ide/ns87415.c similarity index 100% rename from drivers/block/ns87415.c rename to drivers/ide/ns87415.c diff --git a/drivers/block/opti621.c b/drivers/ide/opti621.c similarity index 100% rename from drivers/block/opti621.c rename to drivers/ide/opti621.c diff --git a/drivers/block/pdc202xx.c b/drivers/ide/pdc202xx.c similarity index 100% rename from drivers/block/pdc202xx.c rename to drivers/ide/pdc202xx.c diff --git a/drivers/block/pdc4030.c b/drivers/ide/pdc4030.c similarity index 100% rename from drivers/block/pdc4030.c rename to drivers/ide/pdc4030.c diff --git a/drivers/block/pdc4030.h b/drivers/ide/pdc4030.h similarity index 100% rename from drivers/block/pdc4030.h rename to drivers/ide/pdc4030.h diff --git a/drivers/block/piix.c b/drivers/ide/piix.c similarity index 100% rename from drivers/block/piix.c rename to drivers/ide/piix.c diff --git a/drivers/block/q40ide.c b/drivers/ide/q40ide.c similarity index 100% rename from drivers/block/q40ide.c rename to drivers/ide/q40ide.c diff --git a/drivers/block/qd6580.c b/drivers/ide/qd6580.c similarity index 100% rename from drivers/block/qd6580.c rename to drivers/ide/qd6580.c diff --git a/drivers/block/rapide.c b/drivers/ide/rapide.c similarity index 100% rename from drivers/block/rapide.c rename to drivers/ide/rapide.c diff --git a/drivers/block/rz1000.c b/drivers/ide/rz1000.c similarity index 100% rename from drivers/block/rz1000.c rename to drivers/ide/rz1000.c diff --git a/drivers/block/sis5513.c b/drivers/ide/sis5513.c similarity index 100% rename from drivers/block/sis5513.c rename to drivers/ide/sis5513.c diff --git a/drivers/block/sl82c105.c b/drivers/ide/sl82c105.c similarity index 100% rename from drivers/block/sl82c105.c rename to drivers/ide/sl82c105.c diff --git a/drivers/block/trm290.c b/drivers/ide/trm290.c similarity index 100% rename from drivers/block/trm290.c rename to drivers/ide/trm290.c diff --git a/drivers/block/umc8672.c b/drivers/ide/umc8672.c similarity index 100% rename from drivers/block/umc8672.c rename to drivers/ide/umc8672.c diff --git a/drivers/block/via82cxxx.c b/drivers/ide/via82cxxx.c similarity index 100% rename from drivers/block/via82cxxx.c rename to drivers/ide/via82cxxx.c diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index dab3f99b906..a44153e311c 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 839c15ebcce..31e8c79b88e 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 b264def307a..b62986f8370 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 45e3bc04469..844f0ba03c6 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 dissimilarity index 89% index 951cf498d98..e42231cbfc8 100644 --- a/drivers/net/appletalk/Config.in +++ b/drivers/net/appletalk/Config.in @@ -1,20 +1,23 @@ -# -# Appletalk driver configuration -# - -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 -fi +# +# Appletalk driver configuration +# + +if [ "$CONFIG_ATALK" != "n" ]; then + 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 073a7392ab2..493c2f6c5db 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 -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include -#include -#include -#include /* used in new tty drivers */ -#include /* used in new tty drivers */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include #include @@ -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 b42b8d390c1..5a5f3cc62cf 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 01a32afd56e..87e5ccd7f1a 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 @@ -60,38 +74,41 @@ #include #include #include - +#include +#include +#include #include + #include #include #include #include -#include -#include -#include /* 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 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 03c69417a1d..d5230dddaea 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 1c546b4d1e6..f855d19789e 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 820bc5d4f50..e9f0254a7c1 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 90c82b341cc..ae92696c9cd 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 98d228b91a4..e770baacf86 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 b68063b07d7..63dc93cfb6f 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 cdcab262134..a90eb0f8cb4 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 64b27286c2b..111373ca9de 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 0cc9aa7c103..a48b3f6c6fc 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 a3560c207e5..eac33513d4d 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 b52a254c84c..220c4d92029 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 59762edd7dd..8baff63ff50 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 09bb422f1cd..c7f27414f9e 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 4e5e3e9b99d..af04a2c877d 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 efc2a329585..77294004956 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 f1dc3744e2e..23abd970a3a 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 400facbf867..27929e78b77 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 689ffbd22e2..ce94c3139db 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 2b5624b293e..53f841af293 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 606d5a606f0..534a4bdbd37 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 11c583dac58..5d0d36f4a3f 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 7c763cf8eb1..3c088df36f8 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 38dcdc0c117..00000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* Processor type for cache alignment. */ -#include -#include -#include - -/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. - This is only in the support-all-kernels source code. */ - -MODULE_AUTHOR("Donald Becker "); -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 . */ - -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; imc_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 7374eac66e6..167d3cb1244 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 d86ce1d85d7..64fafa17ffe 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 43b5508ae5e..3cbf0e5e494 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 #include #include @@ -31,9 +29,18 @@ #include #include #include +#include +#include #include -#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 3ef379ab2a9..761d8705ca5 100644 --- a/drivers/net/ppp_deflate.c +++ b/drivers/net/ppp_deflate.c @@ -32,26 +32,9 @@ */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include /* used in new tty drivers */ -#include /* used in new tty drivers */ - -#include - -#include -#include -#include -#include +#include #include #include @@ -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 0b3aeff4bc6..01a4e2f81e0 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 #include #include @@ -44,16 +42,9 @@ #include #include #include +#include -#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 1a69f6ede7f..8bae76e1c48 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 27b0c6474bc..e19f1e0fda3 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 a3a0018cb11..8e8236e9225 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 9b17cc79d60..bcf6e7e5b3b 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 d9019b0465b..f8f59b9fbce 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 89cbb0e9e94..9190b5a8781 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 f8af277eae1..2653270c35c 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 e1e48bc91cc..93533c5ef31 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 6aceec4d565..b6b11231893 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 344e682f818..059d4a4eaaf 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 31688f34d3d..316b638c2a4 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 f90055b45df..1fa4547c43a 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 00000000000..e0a339bfa38 --- /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. + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 00000000000..7ba86dfe51a --- /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 bbca052e750..19615012f97 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 bc4bd5331e0..e0a6f5e8ff3 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 67f6bb59baa..b2cd8aafb87 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 00000000000..7381dc8a9c4 --- /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 , + * Peter Bajan , + * Rewritten by: Tivadar Szemethy + * Currently maintained by: Gergely Madarasz + * + * Copyright (C) 1995-2000 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): + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Gergely Madarasz , Tivadar Szemethy , 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 00000000000..010f0237314 --- /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 + * + * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy + * and the hostess_sv11 driver + * + * Copyright (C) 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.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "z85230.h" + +MODULE_AUTHOR("Gergely Madarasz "); +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 00000000000..552443f886c --- /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 + * + * based on skeleton driver code and a preliminary hscx driver by + * Tivadar Szemethy + * + * Copyright (C) 1998-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.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "mixcom.h" +#include "hscx.h" + +MODULE_AUTHOR("Gergely Madarasz "); +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 00000000000..ad62e310c92 --- /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 + * Maintainer: Gergely Madarasz + * + * Copyright (C) 1998-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.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Author: Tivadar Szemethy "); +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 00000000000..c0fc0c6bd67 --- /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 + * Maintainer: Gergely Madarasz + * + * Copyright (C) 1997-1999 (C) 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/14): + * - cleaned up the source code a bit + * - ported back to kernel, now works as non-module + * + */ + +#define VERSION "0.80" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..0b791685d0d --- /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 + * + * based on skeleton code by Tivadar Szemethy + * + * Copyright (C) 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.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syncppp.h" +#include "comx.h" + +MODULE_AUTHOR("Author: Gergely Madarasz "); +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 00000000000..d3ca69e869d --- /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 , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Current maintainer: Gergely Madarasz + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#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 "); +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. \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 00000000000..e02849b90bf --- /dev/null +++ b/drivers/net/wan/comx.h @@ -0,0 +1,240 @@ +/* + * General definitions for the COMX driver + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Currently maintained by: Gergely Madarasz + * + * 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. + * + * + * 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 + +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 00000000000..15230dc1f43 --- /dev/null +++ b/drivers/net/wan/comxhw.h @@ -0,0 +1,113 @@ +/* + * Defines for comxhw.c + * + * Original authors: Arpad Bakay , + * Peter Bajan , + * Previous maintainer: Tivadar Szemethy + * Current maintainer: Gergely Madarasz + * + * 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. + * + */ + +#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 63351416609..544fcb8dd42 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 00000000000..675b7b1f1b8 --- /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 00000000000..1815eef75fd --- /dev/null +++ b/drivers/net/wan/mixcom.h @@ -0,0 +1,35 @@ +/* + * Defines for the mixcom board + * + * Author: Gergely Madarasz + * + * Copyright (C) 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. + * + */ + +#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 b161fbabc43..be665f0aee5 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 e7875649eb3..0454118a8db 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 e039bbc28b3..5b850461660 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 1e2056869cf..8f6ed5c1f3d 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 2ff818b04eb..f1c618a9054 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 9fc75e5e65e..2162d5ff00f 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 f55bf48e2ff..38534c93402 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 85febe47dc9..f28feea438c 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -77,7 +77,6 @@ static int gx_fix = 0; #include #include -#include #include #include #include diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index 7429ec38bd2..8d01df21e57 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,24 @@ +2000-03-13 + + * 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 * parport_pc.c (decode_winbond): Use correct 83877ATF chip ID. diff --git a/drivers/parport/Config.in b/drivers/parport/Config.in index d4222f353f1..1e486c6b350 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 86fc6f87408..7117c847ed9 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -60,6 +60,8 @@ #include #include +#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) && (ibase) && (i 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 e517fbf273c..c57fdfcc642 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 01c76073f5a..4c7e8003c35 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 @@ -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 220dc62f7a8..b2fc1ffc3e8 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 6e30f9b5824..a691db4307a 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 b08b73e18b4..86819374739 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 f884dfb1e11..8f4d4f19cfc 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 4abf98759ee..47448ec7140 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 4b68450e4ac..6eee7603392 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 b1b6362f30a..77121aa0b97 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 c0b3f3a62a3..6c92531fc97 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 849ce76c9fa..5a74bc2df9a 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 8768db48c0b..7961483e171 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 f1b82a5a17b..685925e7def 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 d2943f103b3..5fc1bfaabbd 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 #include #include -#ifdef __mips__ -#include -#include -#include -#endif /* __mips__ */ #include #include @@ -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; - - 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) +#define NAME53C "ncr53c" +#define NAME53C8XX "ncr53c8xx" +#define DRIVER_SMP_LOCK ncr53c8xx_lock -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 -#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,314 +381,54 @@ 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 +** Command control block states. ** **========================================================== */ -#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) +#define HS_IDLE (0) +#define HS_BUSY (1) +#define HS_NEGOTIATE (2) /* sync/wide data transfer*/ +#define HS_DISCONNECT (3) /* Disconnected by target */ + +#define HS_DONEMASK (0x80) +#define HS_COMPLETE (4|HS_DONEMASK) +#define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */ +#define HS_RESET (6|HS_DONEMASK) /* SCSI reset */ +#define HS_ABORTED (7|HS_DONEMASK) /* Transfer aborted */ +#define HS_TIMEOUT (8|HS_DONEMASK) /* Software timeout */ +#define HS_FAIL (9|HS_DONEMASK) /* SCSI or PCI bus errors */ +#define HS_UNEXPECTED (10|HS_DONEMASK)/* Unexpected disconnect */ /* -** Enable/Disable debug messages. -** Can be changed at runtime too. +** Invalid host status values used by the SCRIPTS processor +** when the nexus is not fully identified. +** Shall never appear in a CCB. */ -#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT - #define DEBUG_FLAGS ncr_debug -#else - #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS -#endif - - +#define HS_INVALMASK (0x40) +#define HS_SELECTING (0|HS_INVALMASK) +#define HS_IN_RESELECT (1|HS_INVALMASK) +#define HS_STARTING (2|HS_INVALMASK) -/*========================================================== -** -** assert () -** -**========================================================== -** -** modified copy from 386bsd:/usr/include/sys/assert.h -** -**---------------------------------------------------------- +/* +** Flags set by the SCRIPT processor for commands +** that have been skipped. */ - -#define assert(expression) { \ - if (!(expression)) { \ - (void)printk(KERN_ERR \ - "assertion \"%s\" failed: file \"%s\", line %d\n", \ - #expression, \ - __FILE__, __LINE__); \ - } \ -} +#define HS_SKIPMASK (0x20) /*========================================================== ** -** 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. -** -**========================================================== -*/ - -#define HS_IDLE (0) -#define HS_BUSY (1) -#define HS_NEGOTIATE (2) /* sync/wide data transfer*/ -#define HS_DISCONNECT (3) /* Disconnected by target */ - -#define HS_DONEMASK (0x80) -#define HS_COMPLETE (4|HS_DONEMASK) -#define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */ -#define HS_RESET (6|HS_DONEMASK) /* SCSI reset */ -#define HS_ABORTED (7|HS_DONEMASK) /* Transfer aborted */ -#define HS_TIMEOUT (8|HS_DONEMASK) /* Software timeout */ -#define HS_FAIL (9|HS_DONEMASK) /* SCSI or PCI bus errors */ -#define HS_UNEXPECTED (10|HS_DONEMASK)/* Unexpected disconnect */ - -/* -** Invalid host status values used by the SCRIPTS processor -** when the nexus is not fully identified. -** Shall never appear in a CCB. -*/ - -#define HS_INVALMASK (0x40) -#define HS_SELECTING (0|HS_INVALMASK) -#define HS_IN_RESELECT (1|HS_INVALMASK) -#define HS_STARTING (2|HS_INVALMASK) - -/* -** Flags set by the SCRIPT processor for commands -** that have been skipped. -*/ -#define HS_SKIPMASK (0x20) - -/*========================================================== -** -** Software Interrupt Codes +** Software Interrupt Codes ** **========================================================== */ @@ -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) @@ -9251,859 +8353,36 @@ static void __init ncr_getclock (ncb_p np, int mult) if (f1 > f2) f1 = f2; /* trust lower result */ - if (f1 < 45000) f1 = 40000; - else if (f1 < 55000) f1 = 50000; - else f1 = 80000; - - if (f1 < 80000 && mult > 1) { - if (bootverbose >= 2) - printk ("%s: clock multiplier assumed\n", ncr_name(np)); - np->multiplier = mult; - } - } else { - if ((scntl3 & 7) == 3) f1 = 40000; - else if ((scntl3 & 7) == 5) f1 = 80000; - else f1 = 160000; - - f1 /= np->multiplier; - } - - /* - ** Compute controller synchronous parameters. - */ - f1 *= np->multiplier; - np->clock_khz = f1; -} - -/*===================== LINUX ENTRY POINTS SECTION ==========================*/ - -#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 (f1 < 45000) f1 = 40000; + else if (f1 < 55000) f1 = 50000; + else f1 = 80000; - 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); + if (f1 < 80000 && mult > 1) { + if (bootverbose >= 2) + printk ("%s: clock multiplier assumed\n", ncr_name(np)); + np->multiplier = mult; } - } - - /* - * 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"); - } + } else { + if ((scntl3 & 7) == 3) f1 = 40000; + else if ((scntl3 & 7) == 5) f1 = 80000; + else f1 = 160000; - 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"); + f1 /= np->multiplier; } -#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; + /* + ** Compute controller synchronous parameters. + */ + f1 *= np->multiplier; + np->clock_khz = f1; } +/*===================== LINUX ENTRY POINTS SECTION ==========================*/ + /* ** 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 a6e2c14d935..21cb989ca8f 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 7a073f86efb..677d2141084 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 9f36d08c536..4bed377edbb 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 abdef85ef8b..a211980a5a6 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 2438aecc676..74ac6d24520 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 d917d93062e..84f29ce7417 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 4060872bf2e..a43f2988c8c 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 f6e8939a6bd..94820f5db8d 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 43187600bfb..584a84905dc 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 781cde85af8..dc671224748 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 1de7686dc2a..8df062781ee 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 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 ). + 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 ). + + 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 e751efc28d3..47b3fbff59d 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 8cbc1c69eb8..fa3926c5d96 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 36eb7b0c131..f9bbce41e87 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 00000000000..fa02ff585ef --- /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 +** +** 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 +** +** 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 +** Stefan Esser +** +**----------------------------------------------------------------------------- +** +** Major contributions: +** -------------------- +** +** NVRAM detection and reading. +** Copyright (C) 1997 Richard Waltham +** +******************************************************************************* +*/ + +/* +** 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 +# 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<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<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 1d74abb5073..947e0294eb0 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 6a3576df07d..5b4d35c8c00 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 52d4d79f84c..99a4d8f4496 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 db2b141d694..a5525a4bf21 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 c0edb593f04..ce5e4f73297 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 9fea58a5315..ee4e01d1f81 100644 --- a/drivers/sound/miroaci.h +++ b/drivers/sound/miroaci.h @@ -1,4 +1,3 @@ -#include 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 2757659aceb..a329f0e7219 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 40804417c3c..c471163efc3 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 923b46e909b..217bdb60557 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 c446e98e01c..393e6a780d2 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 00f8b6e7b49..f3b008f0c27 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 d1631defd01..75d1a897750 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 9665598b904..6a5af79437c 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.typebaseframe.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 21f850d9bb1..0f3195163e4 100644 --- a/drivers/telephony/phonedev.c +++ b/drivers/telephony/phonedev.c @@ -10,7 +10,8 @@ * * Author: Alan Cox, * - * Fixes: + * Fixes: Mar 01 2000 Thomas Sparr, + * phone_register_device now works with unit!=PHONE_UNIT_ANY */ #include @@ -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 5f1b61d076d..fbb7562dcac 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 7c5b02f211f..35bef0e4556 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 00000000000..8b81e06c0c3 --- /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 : + Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + + Version 0.2: + Brad Hards : Fixes to make it work as non-module + Markus: Copyright clarification + + Version 0.01: Markus: initial release + +*/ + + +#include + +#if CONFIG_MODVERSIONS==1 +#define MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 92a008d2f05..c30f2eaff5c 100644 --- a/drivers/usb/inode.c +++ b/drivers/usb/inode.c @@ -29,6 +29,7 @@ /*****************************************************************************/ #define __NO_VERSION__ +#include #include #include #include diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 4e9b4f65e52..d1f5048e395 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 /* retrieve the CONFIG_* macros */ -#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) -# define MODVERSIONS /* force it on */ -#endif - -#ifdef MODVERSIONS -#include -#endif - #include #include #include @@ -58,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -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;yheight;y++) + { + int lp=(y-shift)*rowBytes;//Previous line offset + int lc=y*rowBytes;//Current line offset + for (x=0;xheight-shift-1;y>=0;y--) + { + int ln=(y+shift)*rowBytes;//Next line offset + int lc=y*rowBytes;//Current line offset + for (x=0;xcurframe == -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 ba54fb47a2c..6a4a332bf49 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 dissimilarity index 88% index d3761bf5208..2d5de6f73b2 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) -** -** Distribute under GPL version 2 or later. -*/ - - -#include -#include -#include -#include -#include -#include - -#include -#include - -#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[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n"; - - -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 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); - - -MODULE_AUTHOR("Petko Manolov "); -MODULE_DESCRIPTION("ADMtek \"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); -} - - -static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data ) -{ - 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; - } - udelay(100); - } - warn("read_phy_word() failed"); - return 1; -} - - -static int 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; - } - udelay(100); - } - warn("write_phy_word() failed"); - return 1; -} - - -int 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); - *retdata = *(__u16 *)data; - return 0; - } - } - warn("read_srom_word() failed"); - return 1; -} -/*** end ***/ - - - - -int 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; -} - - -static int 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; - } - } - return 1; -} - - -int start_net( struct net_device *dev, struct usb_device *usb_dev ) -{ - __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); - memcpy(dev->dev_addr, node_id, 6); - if ( read_phy_word(usb_dev, 1, &temp) ) - return 2; - if ( !(temp & 4) ) { - if ( loopback ) - goto ok; - 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; - } - partmedia = temp; - if ( partmedia & 0x100 ) - data[1] = 0x30; - else { - if ( partmedia & 0x80 ) - data[1] = 0x10; - else - data[1] = 0; - } - - data[0] = 0xc9; - data[2] = (loopback & 1) ? 0x08 : 0x00; - - hpna_set_registers(usb_dev, 0, 3, data); - - return 0; -} - - -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); - - - if ( urb->status ) { - info( "%s: RX status %d\n", net_dev->name, urb->status ); - goto goon; - } - - if ( !count ) - goto goon; -/* if ( rx_status & 0x00010000 ) - 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; - } -goon: - if ( (res = usb_submit_urb( &hpna->rx_urb )) ) - warn("failed rx_urb %d", res); -} - - -static void hpna_irq( urb_t *urb) -{ - 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] ); - } -} - - -static void hpna_write_irq( purb_t urb ) -{ - struct net_device *net_dev = urb->context; - usb_hpna_t *hpna = net_dev->priv; - - - spin_lock( &hpna->hpna_lock ); - - if ( urb->status ) - info("%s: TX status %d\n", net_dev->name, urb->status); - netif_wake_queue( net_dev ); - - spin_unlock( &hpna->hpna_lock ); -} - - -static void tx_timeout( struct net_device *dev ) -{ - usb_hpna_t *hpna = dev->priv; - - warn( "%s: Tx timed out. Reseting...", dev->name ); - hpna->stats.tx_errors++; - dev->trans_start = jiffies; - netif_wake_queue( dev ); -} - - -static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev ) -{ - usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; - int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3; - int res; - - spin_lock( &hpna->hpna_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 )) ) { - warn("failed tx_urb %d", res); - hpna->stats.tx_errors++; - netif_start_queue( net_dev ); - } else { - hpna->stats.tx_packets++; - hpna->stats.tx_bytes += skb->len; - net_dev->trans_start = jiffies; - } - dev_kfree_skb( skb ); - spin_unlock( &hpna->hpna_lock ); - return 0; -} - - -static struct net_device_stats *hpna_netdev_stats( struct net_device *dev ) -{ - return &((usb_hpna_t *)dev->priv)->stats; -} - -static int hpna_open( struct net_device *net_dev ) -{ - usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; - int res; - - if ( hpna->active ) - return -EBUSY; - else - hpna->active = 1; - - if ( start_net(net_dev, hpna->usb_dev) ) { - err("can't start_net()"); - return -EIO; - } - - if ( (res = usb_submit_urb( &hpna->rx_urb )) ) - warn("failed rx_urb %d", res); - -/* usb_submit_urb( &hpna->intr_urb );*/ - netif_start_queue( net_dev ); - - MOD_INC_USE_COUNT; - - return 0; -} - - -static int hpna_close( struct net_device *net_dev ) -{ - usb_hpna_t *hpna = net_dev->priv; - - - netif_stop_queue( net_dev ); - - usb_unlink_urb( &hpna->rx_urb ); - usb_unlink_urb( &hpna->tx_urb ); -/* usb_unlink_urb( hpna->intr_urb );*/ - - hpna->active = 0; - - MOD_DEC_USE_COUNT; - - return 0; -} - - -static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd ) -{ - __u16 *data = (__u16 *)&rq->ifr_data; - usb_hpna_t *hpna = dev->priv; - - switch( cmd ) { - case SIOCDEVPRIVATE: - data[0] = 1; - case SIOCDEVPRIVATE+1: - read_phy_word(hpna->usb_dev, 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; - default: - return -EOPNOTSUPP; - } -} - - -static void set_rx_mode( struct net_device *net_dev ) -{ - usb_hpna_t *hpna=net_dev->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); - } else { - dbg("%s: set Rx mode", net_dev->name); - } - - netif_wake_queue( net_dev ); -} - - -static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum ) -{ - struct net_device *net_dev; - usb_hpna_t *hpna = &usb_dev_hpna; - - - - if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID || - dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) { - return NULL; - } - - printk("USB HPNA Pegasus found\n"); - - 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"); - } - - 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; - } - - 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; -} - - -static void usb_hpna_disconnect( struct usb_device *dev, void *ptr ) -{ - struct net_device *net_dev = ptr; - struct usb_hpna *hpna = net_dev->priv; - - - if ( net_dev->flags & IFF_UP ) - dev_close(net_dev); - - unregister_netdev( net_dev ); - - 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); - - hpna->usb_dev = NULL; - hpna->present = 0; - - printk("USB HPNA disconnected\n"); -} - - -static struct usb_driver usb_hpna_driver = { - "ADMtek \"Pegasus\" USB Ethernet", - usb_hpna_probe, - usb_hpna_disconnect, - {NULL, NULL} -}; - - - -static int __init start_hpna( void ) -{ - printk( version ); - return usb_register( &usb_hpna_driver ); -} - - -static void __exit stop_hpna( void ) -{ - usb_deregister( &usb_hpna_driver ); -} - - -module_init( start_hpna ); -module_exit( stop_hpna ); +/* +** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller +** +** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** +** Distribute under GPL version 2 or later. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *version = __FILE__ ": v0.3.3 2000/03/13 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))) + +struct pegasus { + struct usb_device *usb; + struct net_device *net; + struct net_device_stats stats; + 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 "); +MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); +MODULE_PARM(loopback, "i"); + + +#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 int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) +{ + 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; +} + +static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) +{ + 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; +} + +static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata) +{ + 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; + } + } + + warn("read_srom_word() failed"); + return 1; +} + +static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) +{ + 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 pegasus_reset_mac(struct usb_device *dev) +{ + __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; +} + +static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) +{ + __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 (pegasus_read_phy_word(usb, 1, &temp)) + return 2; + + if ((~temp & 4) && !loopback) { + err("link NOT established - %x", temp); + return 3; + } + + 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[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); + data[2] = (loopback & 1) ? 0x08 : 0x00; + + pegasus_set_registers(usb, 0, 3, data); + + return 0; +} + +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) + goto goon; +#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; + } + + 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(&pegasus->rx_urb))) + warn("(prb)failed rx_urb %d", res); +} + +static void pegasus_irq(urb_t *urb) +{ + 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]); + } +} + +static void pegasus_write_bulk(struct urb *urb) +{ + struct pegasus *pegasus = urb->context; + + spin_lock(&pegasus->pegasus_lock); + + if (urb->status) + info("%s: TX status %d", pegasus->net->name, urb->status); + netif_wake_queue(pegasus->net); + + spin_unlock(&pegasus->pegasus_lock); +} + +static void pegasus_tx_timeout(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + warn("%s: Tx timed out. Reseting...", net->name); + pegasus->stats.tx_errors++; + net->trans_start = jiffies; + + netif_wake_queue(net); +} + +static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; + int res; + + spin_lock(&pegasus->pegasus_lock); + + 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); + pegasus->stats.tx_errors++; + netif_start_queue(net); + } else { + pegasus->stats.tx_packets++; + pegasus->stats.tx_bytes += skb->len; + net->trans_start = jiffies; + } + + dev_kfree_skb(skb); + + spin_unlock(&pegasus->pegasus_lock); + + return 0; +} + +static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) +{ + return &((struct pegasus *)dev->priv)->stats; +} + +static int pegasus_open(struct net_device *net) +{ + struct pegasus *pegasus = (struct pegasus *)net->priv; + int res; + + if ((res = pegasus_start_net(net, pegasus->usb))) { + err("can't start_net() - %d", res); + return -EIO; + } + + if ((res = usb_submit_urb(&pegasus->rx_urb))) + warn("(open)failed rx_urb %d", res); + +/* usb_submit_urb(&pegasus->intr_urb);*/ + netif_start_queue(net); + + MOD_INC_USE_COUNT; + + return 0; +} + +static int pegasus_close(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + netif_stop_queue(net); + + 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 pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ + __u16 *data = (__u16 *)&rq->ifr_data; + struct pegasus *pegasus = net->priv; + + switch(cmd) { + case SIOCDEVPRIVATE: + data[0] = 1; + case SIOCDEVPRIVATE+1: + pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static void pegasus_set_rx_mode(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + 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->name); + } + + netif_wake_queue(net); +} + +static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct net_device *net; + struct pegasus *pegasus; + + if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID || + dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) { + return NULL; + } + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + err("usb_set_configuration() failed"); + return NULL; + } + + if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { + err("out of memory allocating device structure"); + return NULL; + } + memset(pegasus, 0, sizeof(struct pegasus)); + + if (pegasus_reset_mac(dev)) { + err("can't reset MAC"); + kfree(pegasus); + return NULL; + } + + 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 pegasus_disconnect(struct usb_device *dev, void *ptr) +{ + struct pegasus *pegasus = ptr; + + if (!pegasus) { + warn("unregistering non-existant device"); + return; + } + + if (pegasus->net->flags & IFF_UP) + dev_close(pegasus->net); + + unregister_netdev(pegasus->net); + + usb_unlink_urb(&pegasus->rx_urb); + usb_unlink_urb(&pegasus->tx_urb); +/* usb_unlink_urb(&pegasus->intr_urb);*/ + + kfree(pegasus); +} + +static struct usb_driver pegasus_driver = { + name: "pegasus", + probe: pegasus_probe, + disconnect: pegasus_disconnect, +}; + +int __init pegasus_init(void) +{ + printk( version ); + return usb_register(&pegasus_driver); +} + +void __exit pegasus_exit(void) +{ + usb_deregister(&pegasus_driver); +} + +module_init(pegasus_init); +module_exit(pegasus_exit); diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index d5c245bf117..20dc5dde572 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 c3b4ebc2e42..480c6252df2 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 62d4e772e19..9f4c45e9633 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 2bba728d907..cb6a70621d9 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 0e2ab20a4a6..956e80a0abc 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(¤t->sigmask_lock); signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->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 80a03f3c91f..06e6d958b31 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 73d16937a2d..4eedc418360 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 aed79f849fc..85a5cd476d5 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 @@ -28,9 +28,9 @@ #include #include /* for in_interrupt() */ #include -/* This enables debug printks */ -#define DEBUG -#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) +#include +#endif #include #include @@ -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 #include "usb-uhci.h" #include "usb-uhci-debug.h" #undef DEBUG #undef dbg #define dbg(format, arg...) do {} while (0) - -#include - +#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 e74e23bd812..93173f2c26c 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 7b6342e6a0b..d934d0c2df5 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 d7d4116c0a6..fcc0f8c5cc8 100644 --- a/drivers/video/aty128fb.c +++ b/drivers/video/aty128fb.c @@ -48,13 +48,13 @@ #include #include -#if defined(CONFIG_PPC) +#ifdef CONFIG_PPC #include #include +#include