# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.154 -> 1.173 # drivers/net/8139too.c 1.24 -> 1.26 # drivers/video/fbmem.c 1.13 -> 1.14 # arch/i386/kernel/process.c 1.9 -> 1.11 # drivers/net/depca.c 1.8 -> 1.9 # include/asm-s390/ide.h 1.1 -> 1.2 # drivers/net/de620.c 1.9 -> 1.10 # drivers/scsi/scsi_obsolete.c 1.3 -> 1.4 # include/asm-sparc64/ioctls.h 1.1 -> 1.2 # fs/ufs/super.c 1.9 -> 1.10 # drivers/net/defxx.c 1.11 -> 1.12 # drivers/char/drm/drm_agpsupport.h 1.6 -> 1.7 # drivers/video/sis/vgatypes.h 1.1 -> 1.2 # include/asm-i386/desc.h 1.2 -> 1.3 # drivers/net/znet.c 1.4 -> 1.5 # drivers/net/3c59x.c 1.12 -> 1.13 # drivers/ide/ide-probe.c 1.10 -> 1.11 # drivers/net/eexpress.c 1.5 -> 1.7 # drivers/char/drm/i810_drm.h 1.2 -> 1.3 # drivers/net/irda/nsc-ircc.c 1.11 -> 1.12 # include/linux/kernel.h 1.15 -> 1.16 # include/asm-i386/mmu_context.h 1.2 -> 1.3 # include/net/route.h 1.6 -> 1.7 # mm/oom_kill.c 1.9 -> 1.12 # include/linux/interrupt.h 1.7 -> 1.8 # drivers/net/au1000_eth.c 1.4 -> 1.5 # arch/i386/mm/fault.c 1.10 -> 1.12 # drivers/net/sunbmac.c 1.9 -> 1.10 # drivers/scsi/qlogicisp.h 1.1 -> 1.2 # drivers/ide/ide-disk.c 1.8 -> 1.9 # drivers/video/matrox/matroxfb_maven.c 1.2 -> 1.3 # drivers/scsi/ips.c 1.10 -> 1.11 # net/sunrpc/sched.c 1.8 -> 1.9 # drivers/media/video/Config.in 1.5 -> 1.7 # arch/i386/kernel/nmi.c 1.2 -> 1.4 # drivers/ide/serverworks.c 1.2 -> 1.3 # include/linux/tty_flip.h 1.2 -> 1.3 # drivers/net/daynaport.c 1.4 -> 1.5 # drivers/pnp/Makefile 1.2 -> 1.3 # fs/jffs2/background.c 1.3 -> 1.4 # mm/mremap.c 1.5 -> 1.7 # drivers/scsi/scsi_error.c 1.5 -> 1.6 # include/asm-mips/softirq.h 1.2 -> 1.3 # kernel/acct.c 1.3 -> 1.4 # drivers/net/3c501.c 1.7 -> 1.8 # include/linux/sysctl.h 1.11 -> 1.13 # drivers/media/video/tvaudio.h 1.2 -> 1.3 # drivers/net/saa9730.c 1.4 -> 1.5 # drivers/net/irda/toshoboe.c 1.9 -> 1.10 # include/asm-mips64/ide.h 1.3 -> 1.4 # drivers/char/drm/drm_fops.h 1.3 -> 1.4 # include/asm-m68k/ide.h 1.2 -> 1.3 # drivers/video/igafb.c 1.5 -> 1.6 # drivers/ide/ide-geometry.c 1.2 -> 1.3 # include/asm-sh/signal.h 1.1 -> 1.2 # include/linux/smp.h 1.1 -> 1.3 # include/linux/pagemap.h 1.16 -> 1.19 # include/linux/list.h 1.6 -> 1.7 # drivers/pnp/Config.in 1.3 -> 1.4 # include/linux/mmzone.h 1.6 -> 1.7 # include/asm-s390x/ide.h 1.1 -> 1.2 # kernel/ksyms.c 1.48 -> 1.54 # scripts/mkspec 1.2 -> 1.3 # drivers/net/3c527.c 1.5 -> 1.6 # include/asm-i386/msr.h 1.3 -> 1.4 # drivers/net/pcmcia/axnet_cs.c 1.2 -> 1.3 # include/asm-sparc/pgtable.h 1.4 -> 1.5 # drivers/video/matrox/matroxfb_Ti3026.c 1.3 -> 1.4 # include/asm-ia64/ide.h 1.1 -> 1.2 # include/linux/cdrom.h 1.5 -> 1.6 # drivers/net/a2065.c 1.4 -> 1.5 # arch/sparc64/solaris/ioctl.c 1.1 -> 1.2 # include/asm-cris/pgtable.h 1.5 -> 1.6 # drivers/i2c/Makefile 1.3 -> 1.4 # include/asm-sparc64/pgtable.h 1.14 -> 1.15 # drivers/video/matrox/matroxfb_accel.c 1.4 -> 1.5 # lib/dec_and_lock.c 1.3 -> 1.4 # drivers/net/hamradio/baycom_ser_fdx.c 1.3 -> 1.5 # drivers/ide/amd74xx.c 1.1 -> 1.2 # drivers/net/wan/cosa.c 1.7 -> 1.8 # fs/buffer.c 1.58 -> 1.64 # drivers/net/de4x5.c 1.8 -> 1.9 # drivers/block/loop.c 1.21 -> 1.23 # drivers/net/pci-skeleton.c 1.14 -> 1.15 # drivers/scsi/aacraid/comminit.c 1.1 -> 1.2 # arch/sparc/mm/srmmu.c 1.10 -> 1.11 # drivers/pci/quirks.c 1.10 -> 1.11 # drivers/scsi/ide-scsi.c 1.4 -> 1.5 # drivers/net/bagetlance.c 1.5 -> 1.6 # net/khttpd/main.c 1.2 -> 1.3 # drivers/net/irda/vlsi_ir.c 1.8 -> 1.9 # include/linux/swap.h 1.31 -> 1.33 # fs/ext3/namei.c 1.1 -> 1.2 # drivers/net/hamradio/baycom_ser_hdx.c 1.3 -> 1.5 # include/asm-arm/smplock.h 1.2 -> 1.3 # include/linux/elevator.h 1.4 -> 1.5 # net/sched/sch_generic.c 1.1 -> 1.2 # drivers/net/bmac.c 1.7 -> 1.8 # include/asm-i386/smp.h 1.7 -> 1.8 # drivers/net/declance.c 1.7 -> 1.8 # drivers/ide/slc90e66.c 1.4 -> 1.5 # drivers/net/3c509.c 1.13 -> 1.14 # include/linux/mm.h 1.34 -> 1.37 # drivers/scsi/aacraid/linit.c 1.1 -> 1.2 # include/asm-i386/processor.h 1.11 -> 1.12 # arch/i386/kernel/mtrr.c 1.12 -> 1.13 # drivers/video/sis/init301.c 1.1 -> 1.2 # drivers/telephony/ixj.c 1.13 -> 1.14 # fs/proc/array.c 1.8 -> 1.10 # include/asm-arm/hardirq.h 1.4 -> 1.5 # drivers/net/ni52.c 1.5 -> 1.6 # fs/file_table.c 1.4 -> 1.5 # MAINTAINERS 1.54 -> 1.58 # drivers/net/sundance.c 1.17 -> 1.18 # fs/reiserfs/journal.c 1.19 -> 1.21 # drivers/net/arcnet/arc-rimi.c 1.3 -> 1.5 # drivers/ide/ide-proc.c 1.3 -> 1.4 # drivers/net/ariadne2.c 1.2 -> 1.3 # drivers/net/natsemi.c 1.20 -> 1.21 # drivers/pcmcia/yenta.c 1.11 -> 1.12 # drivers/net/dgrs.c 1.10 -> 1.11 # lib/Makefile 1.6 -> 1.7 # include/asm-i386/fixmap.h 1.2 -> 1.3 # include/asm-i386/pgalloc.h 1.8 -> 1.11 # arch/ia64/ia32/sys_ia32.c 1.8 -> 1.9 # drivers/net/skfp/skfddi.c 1.5 -> 1.6 # include/linux/tqueue.h 1.2 -> 1.3 # fs/partitions/msdos.c 1.7 -> 1.8 # drivers/media/video/msp3400.c 1.7 -> 1.8 # drivers/net/hp-plus.c 1.6 -> 1.7 # drivers/net/sb1000.c 1.6 -> 1.8 # drivers/char/raw.c 1.6 -> 1.7 # include/asm-s390x/signal.h 1.3 -> 1.4 # drivers/net/Config.in 1.29 -> 1.30 # drivers/net/ioc3-eth.c 1.10 -> 1.11 # mm/mmap.c 1.18 -> 1.21 # drivers/net/myri_sbus.c 1.6 -> 1.7 # drivers/net/fmv18x.c 1.7 -> 1.8 # fs/proc/base.c 1.10 -> 1.11 # drivers/net/wd.c 1.6 -> 1.7 # fs/nls/nls_base.c 1.2 -> 1.3 # mm/highmem.c 1.11 -> 1.12 # arch/i386/kernel/irq.c 1.6 -> 1.7 # include/asm-cris/ioctls.h 1.1 -> 1.2 # drivers/video/matrox/matroxfb_misc.h 1.1 -> 1.2 # drivers/message/i2o/i2o_core.c 1.9 -> 1.10 # fs/ext2/inode.c 1.13 -> 1.14 # net/unix/af_unix.c 1.17 -> 1.18 # mm/page_alloc.c 1.40 -> 1.43 # mm/Makefile 1.3 -> 1.4 # drivers/char/pcwd.c 1.6 -> 1.7 # drivers/char/drm-4.0/ffb_drv.c 1.2 -> 1.3 # drivers/char/drm-4.0/i810_dma.c 1.1 -> 1.2 # drivers/net/arlan.c 1.8 -> 1.9 # drivers/net/tokenring/ibmtr.c 1.9 -> 1.10 # arch/sparc64/kernel/sys_sparc32.c 1.13 -> 1.14 # drivers/net/aironet4500_card.c 1.7 -> 1.8 # drivers/net/wan/sealevel.c 1.5 -> 1.7 # drivers/ide/alim15x3.c 1.5 -> 1.6 # drivers/net/pppoe.c 1.11 -> 1.12 # include/asm-ppc/signal.h 1.2 -> 1.3 # drivers/net/arcnet/rfc1051.c 1.2 -> 1.3 # drivers/sound/sb_card.c 1.11 -> 1.12 # arch/i386/kernel/smp.c 1.8 -> 1.10 # fs/vfat/namei.c 1.4 -> 1.5 # drivers/ide/Makefile 1.6 -> 1.7 # include/asm-sh/ioctls.h 1.1 -> 1.2 # fs/jbd/revoke.c 1.3 -> 1.4 # fs/reiserfs/bitmap.c 1.11 -> 1.12 # include/asm-i386/highmem.h 1.3 -> 1.4 # drivers/net/ariadne.c 1.4 -> 1.5 # include/asm-i386/bitops.h 1.3 -> 1.4 # include/asm-i386/ide.h 1.1 -> 1.2 # fs/namespace.c 1.8 -> 1.9 # fs/udf/super.c 1.8 -> 1.9 # drivers/net/eepro100.c 1.15 -> 1.17 # arch/i386/kernel/head.S 1.6 -> 1.7 # drivers/char/drm-4.0/tdfx_drv.c 1.1 -> 1.2 # drivers/net/lance.c 1.8 -> 1.9 # drivers/net/tokenring/tmsisa.c 1.5 -> 1.6 # drivers/net/wan/comx-hw-comx.c 1.6 -> 1.7 # include/linux/serialP.h 1.4 -> 1.5 # kernel/fork.c 1.19 -> 1.23 # include/linux/sched.h 1.23 -> 1.29 # drivers/video/matrox/matroxfb_misc.c 1.3 -> 1.4 # drivers/net/sk_mca.c 1.4 -> 1.5 # drivers/net/wan/comx-hw-locomx.c 1.4 -> 1.6 # kernel/sys.c 1.9 -> 1.11 # fs/Config.in 1.12 -> 1.13 # drivers/block/ll_rw_blk.c 1.28 -> 1.30 # arch/i386/kernel/vm86.c 1.3 -> 1.4 # kernel/sysctl.c 1.15 -> 1.17 # net/atm/pppoatm.c 1.1 -> 1.2 # drivers/net/ac3200.c 1.6 -> 1.7 # include/asm-i386/ioctls.h 1.1 -> 1.2 # Makefile 1.143 -> 1.146 # drivers/net/wireless/airo.c 1.12 -> 1.13 # drivers/scsi/aacraid/commsup.c 1.1 -> 1.2 # Documentation/video4linux/bttv/CARDLIST 1.4 -> 1.5 # include/asm-i386/irq.h 1.3 -> 1.5 # include/asm-m68k/ioctls.h 1.1 -> 1.2 # kernel/capability.c 1.1 -> 1.2 # drivers/video/Makefile 1.7 -> 1.8 # fs/ext3/inode.c 1.4 -> 1.6 # drivers/net/smc-mca.c 1.5 -> 1.6 # include/asm-i386/io_apic.h 1.4 -> 1.5 # include/asm-parisc/ioctls.h 1.1 -> 1.2 # drivers/net/irda/irport.c 1.9 -> 1.10 # include/net/tcp.h 1.8 -> 1.9 # drivers/media/video/bttv.h 1.6 -> 1.7 # include/linux/swapctl.h 1.2 -> 1.3 # arch/i386/defconfig 1.48 -> 1.49 # drivers/isdn/hisax/hisax_isac.c 1.2 -> 1.3 # include/asm-ia64/signal.h 1.3 -> 1.4 # drivers/net/stnic.c 1.4 -> 1.5 # drivers/media/video/videodev.c 1.8 -> 1.9 # fs/dcache.c 1.16 -> 1.18 # drivers/net/tlan.c 1.8 -> 1.9 # fs/dquot.c 1.16 -> 1.17 # drivers/net/lasi_82596.c 1.7 -> 1.8 # include/asm-ia64/ioctls.h 1.1 -> 1.2 # drivers/char/mxser.c 1.8 -> 1.9 # mm/vmscan.c 1.56 -> 1.58 # net/ipv4/tcp_output.c 1.11 -> 1.12 # drivers/video/sis/sis_main.c 1.8 -> 1.9 # drivers/net/arcnet/arcnet.c 1.5 -> 1.6 # Documentation/video4linux/bttv/Insmod-options 1.4 -> 1.5 # drivers/ide/ide-features.c 1.2 -> 1.3 # drivers/video/matrox/matroxfb_base.c 1.9 -> 1.10 # fs/proc/proc_misc.c 1.11 -> 1.13 # drivers/net/arcnet/com20020-isa.c 1.3 -> 1.4 # drivers/net/smc-ultra.c 1.7 -> 1.8 # drivers/ide/hpt366.c 1.5 -> 1.6 # drivers/char/agp/agpgart_be.c 1.22 -> 1.23 # drivers/net/3c515.c 1.10 -> 1.11 # drivers/net/pcmcia/xircom_tulip_cb.c 1.13 -> 1.14 # drivers/i2c/i2c-core.c 1.5 -> 1.6 # drivers/net/sk98lin/skge.c 1.9 -> 1.10 # drivers/net/hamradio/soundmodem/sm_sbc.c 1.1 -> 1.2 # drivers/block/floppy.c 1.9 -> 1.10 # drivers/net/wan/farsync.c 1.5 -> 1.6 # drivers/cdrom/cdrom.c 1.11 -> 1.12 # fs/reiserfs/stree.c 1.16 -> 1.17 # drivers/net/fealnx.c 1.11 -> 1.12 # drivers/net/3c503.c 1.8 -> 1.9 # drivers/net/arcnet/com90xx.c 1.4 -> 1.6 # include/asm-sh/softirq.h 1.4 -> 1.5 # drivers/char/drm/r128_state.c 1.2 -> 1.3 # CREDITS 1.39 -> 1.42 # drivers/net/arcnet/com20020-pci.c 1.7 -> 1.8 # drivers/media/video/tuner.h 1.4 -> 1.5 # drivers/net/82596.c 1.7 -> 1.8 # include/linux/fs_struct.h 1.3 -> 1.4 # net/ipv4/netfilter/ip_fw_compat_redir.c 1.3 -> 1.4 # fs/jbd/journal.c 1.3 -> 1.4 # drivers/ide/ide-tape.c 1.8 -> 1.9 # drivers/video/sis/300vtbl.h 1.1 -> 1.2 # drivers/message/i2o/i2o_block.c 1.15 -> 1.16 # drivers/media/video/tda7432.c 1.5 -> 1.6 # drivers/net/wan/hostess_sv11.c 1.5 -> 1.6 # include/asm-i386/signal.h 1.2 -> 1.3 # include/asm-cris/ide.h 1.2 -> 1.3 # init/main.c 1.22 -> 1.26 # drivers/net/hamradio/6pack.c 1.6 -> 1.7 # arch/i386/kernel/traps.c 1.11 -> 1.13 # drivers/char/drm/drm_memory.h 1.2 -> 1.3 # include/asm-i386/param.h 1.1 -> 1.2 # drivers/s390/block/dasd_int.h 1.3 -> 1.4 # drivers/scsi/eata.h 1.4 -> 1.5 # drivers/net/7990.c 1.2 -> 1.3 # include/asm-m68k/signal.h 1.1 -> 1.2 # include/net/sock.h 1.8 -> 1.9 # include/linux/quota.h 1.3 -> 1.4 # include/asm-mips/ide.h 1.3 -> 1.4 # include/asm-alpha/ioctls.h 1.2 -> 1.3 # include/asm-s390/signal.h 1.2 -> 1.3 # fs/ntfs/fs.c 1.12 -> 1.13 # drivers/net/wan/sdlamain.c 1.7 -> 1.8 # drivers/ide/ide-dma.c 1.4 -> 1.5 # drivers/net/eepro.c 1.9 -> 1.10 # include/asm-i386/hardirq.h 1.4 -> 1.7 # drivers/char/drm/drmP.h 1.6 -> 1.7 # include/linux/ide.h 1.4 -> 1.5 # drivers/net/winbond-840.c 1.17 -> 1.18 # arch/arm/config.in 1.11 -> 1.12 # Documentation/video4linux/bttv/Cards 1.1 -> 1.2 # include/asm-mips64/signal.h 1.2 -> 1.3 # drivers/net/hamradio/hdlcdrv.c 1.4 -> 1.5 # drivers/net/hamradio/yam.c 1.7 -> 1.9 # mm/swapfile.c 1.22 -> 1.25 # arch/i386/kernel/acpitable.c 1.2 -> 1.3 # include/asm-i386/pgtable.h 1.4 -> 1.5 # include/asm-sh/smplock.h 1.2 -> 1.3 # drivers/char/Config.in 1.16 -> 1.18 # drivers/net/oaknet.c 1.3 -> 1.4 # drivers/net/wan/c101.c 1.2 -> 1.3 # fs/ioctl.c 1.2 -> 1.3 # drivers/scsi/sd.c 1.18 -> 1.19 # drivers/net/ne2k-pci.c 1.7 -> 1.8 # drivers/net/ibmlana.c 1.4 -> 1.5 # drivers/net/seeq8005.c 1.5 -> 1.6 # arch/i386/mm/init.c 1.9 -> 1.10 # arch/sparc/mm/sun4c.c 1.8 -> 1.9 # include/asm-ppc/pgtable.h 1.7 -> 1.8 # drivers/net/hp.c 1.6 -> 1.7 # drivers/net/wan/n2.c 1.2 -> 1.3 # drivers/ieee1394/csr.c 1.3 -> 1.4 # include/asm-i386/i387.h 1.3 -> 1.4 # drivers/video/matrox/matroxfb_accel.h 1.1 -> 1.2 # drivers/sound/i810_audio.c 1.19 -> 1.20 # drivers/media/video/bttv-driver.c 1.11 -> 1.12 # include/linux/blkdev.h 1.17 -> 1.19 # include/linux/i2o.h 1.8 -> 1.9 # drivers/net/smc9194.c 1.9 -> 1.10 # drivers/net/fc/iph5526.c 1.7 -> 1.8 # drivers/char/drm/drm_bufs.h 1.4 -> 1.5 # drivers/ide/pdc4030.c 1.3 -> 1.4 # drivers/char/drm/drm.h 1.5 -> 1.6 # drivers/net/atarilance.c 1.6 -> 1.7 # arch/i386/lib/dec_and_lock.c 1.1 -> 1.2 # drivers/scsi/3w-xxxx.h 1.8 -> 1.9 # drivers/sound/btaudio.c 1.7 -> 1.8 # drivers/net/tokenring/olympic.c 1.10 -> 1.12 # include/asm-i386/spinlock.h 1.4 -> 1.5 # fs/nfs/inode.c 1.14 -> 1.15 # drivers/net/pcmcia/xircom_cb.c 1.4 -> 1.5 # drivers/ide/ide-floppy.c 1.8 -> 1.9 # mm/slab.c 1.12 -> 1.15 # drivers/net/rrunner.c 1.5 -> 1.7 # drivers/video/fbcon-cfb24.c 1.3 -> 1.4 # include/linux/major.h 1.4 -> 1.5 # drivers/block/elevator.c 1.5 -> 1.6 # kernel/softirq.c 1.8 -> 1.10 # drivers/scsi/scsi_scan.c 1.11 -> 1.12 # include/linux/rtnetlink.h 1.4 -> 1.5 # drivers/net/starfire.c 1.15 -> 1.16 # drivers/net/wan/dscc4.c 1.8 -> 1.9 # include/asm-s390/pgtable.h 1.4 -> 1.5 # drivers/net/rcpci45.c 1.11 -> 1.12 # drivers/scsi/scsi.h 1.5 -> 1.6 # kernel/sched.c 1.24 -> 1.28 # include/asm-sparc/elf.h 1.1 -> 1.2 # include/asm-sparc64/signal.h 1.1 -> 1.2 # include/linux/smp_lock.h 1.1 -> 1.2 # drivers/Makefile 1.8 -> 1.9 # include/linux/fs.h 1.57 -> 1.61 # drivers/net/tokenring/abyss.c 1.7 -> 1.8 # net/8021q/vlan.c 1.2 -> 1.3 # drivers/pcmcia/cs.c 1.9 -> 1.10 # drivers/video/sis/sis_main.h 1.1 -> 1.2 # include/asm-arm/pgtable.h 1.5 -> 1.6 # drivers/net/es3210.c 1.5 -> 1.6 # arch/i386/mm/ioremap.c 1.3 -> 1.4 # drivers/scsi/Makefile 1.12 -> 1.13 # drivers/char/drm/sis_ds.c 1.1 -> 1.2 # Documentation/fb/matroxfb.txt 1.3 -> 1.4 # include/asm-sh/ide.h 1.3 -> 1.4 # include/linux/iobuf.h 1.2 -> 1.3 # arch/i386/kernel/entry.S 1.11 -> 1.15 # drivers/video/matrox/Makefile 1.1 -> 1.2 # include/linux/quotaops.h 1.5 -> 1.6 # drivers/net/ni65.c 1.5 -> 1.6 # drivers/ide/qd65xx.h 1.1 -> 1.2 # drivers/char/drm/r128_drv.c 1.2 -> 1.3 # drivers/video/matrox/matroxfb_g450.c 1.3 -> 1.4 # arch/arm/tools/getconstants.c 1.4 -> 1.5 # drivers/char/mwave/mwavedd.c 1.2 -> 1.3 # include/asm-cris/signal.h 1.1 -> 1.2 # drivers/net/pcnet32.c 1.14 -> 1.15 # drivers/video/virgefb.c 1.5 -> 1.6 # drivers/mtd/ftl.c 1.10 -> 1.11 # drivers/char/drm/drm_drv.h 1.3 -> 1.4 # drivers/net/lp486e.c 1.4 -> 1.5 # drivers/net/mace.c 1.10 -> 1.11 # drivers/pci/pci.c 1.23 -> 1.24 # drivers/video/sis/init.c 1.1 -> 1.2 # fs/fat/cache.c 1.6 -> 1.7 # drivers/ide/qd65xx.c 1.2 -> 1.3 # drivers/isdn/hisax/hisax_fcpcipnp.c 1.4 -> 1.5 # drivers/video/matrox/matroxfb_crtc2.c 1.3 -> 1.4 # drivers/video/sis/init.h 1.1 -> 1.2 # kernel/timer.c 1.3 -> 1.5 # drivers/net/eth16i.c 1.8 -> 1.9 # include/asm-mips/pgtable.h 1.3 -> 1.4 # drivers/mtd/nand/nand_ecc.c 1.2 -> 1.3 # drivers/md/md.c 1.29 -> 1.30 # drivers/net/gmac.c 1.6 -> 1.7 # drivers/sound/via82cxxx_audio.c 1.14 -> 1.15 # include/linux/blk.h 1.5 -> 1.6 # include/asm-alpha/pgtable.h 1.7 -> 1.8 # drivers/media/video/tuner.c 1.7 -> 1.8 # drivers/net/sunlance.c 1.8 -> 1.9 # drivers/net/pcmcia/pcnet_cs.c 1.7 -> 1.8 # drivers/net/appletalk/ltpc.c 1.4 -> 1.5 # net/ipv6/netfilter/ip6_queue.c 1.1 -> 1.2 # include/asm-i386/smplock.h 1.2 -> 1.3 # kernel/kmod.c 1.3 -> 1.4 # include/linux/mtd/compatmac.h 1.1 -> 1.2 # fs/jbd/transaction.c 1.3 -> 1.4 # drivers/char/serial.c 1.20 -> 1.22 # arch/sparc64/config.in 1.21 -> 1.22 # include/asm-ia64/pgtable.h 1.6 -> 1.7 # drivers/net/hamradio/scc.c 1.12 -> 1.14 # drivers/net/smc-ultra32.c 1.3 -> 1.4 # include/linux/fb.h 1.1 -> 1.2 # mm/bootmem.c 1.6 -> 1.7 # drivers/scsi/u14-34f.c 1.5 -> 1.6 # drivers/net/sun3lance.c 1.7 -> 1.8 # arch/sparc64/kernel/binfmt_elf32.c 1.1 -> 1.2 # drivers/scsi/eata.c 1.6 -> 1.7 # drivers/net/arcnet/com20020.c 1.3 -> 1.5 # arch/arm/kernel/entry-armv.S 1.7 -> 1.8 # include/asm-alpha/ide.h 1.1 -> 1.2 # drivers/net/arcnet/com90io.c 1.5 -> 1.7 # drivers/scsi/hosts.h 1.4 -> 1.5 # drivers/net/wan/lmc/lmc_main.c 1.6 -> 1.7 # arch/i386/config.in 1.23 -> 1.26 # fs/qnx4/inode.c 1.8 -> 1.9 # kernel/exit.c 1.10 -> 1.13 # include/asm-parisc/pgtable.h 1.2 -> 1.3 # drivers/media/video/tvaudio.c 1.7 -> 1.8 # README 1.3 -> 1.4 # drivers/ide/ide-cs.c 1.3 -> 1.4 # include/asm-mips/signal.h 1.1 -> 1.2 # fs/jbd/commit.c 1.3 -> 1.4 # include/math-emu/op-4.h 1.2 -> 1.3 # drivers/net/sunhme.c 1.13 -> 1.14 # mm/filemap.c 1.56 -> 1.59 # drivers/char/drm/i810_dma.c 1.5 -> 1.6 # fs/hfs/super.c 1.5 -> 1.6 # arch/sparc64/kernel/ioctl32.c 1.21 -> 1.22 # drivers/media/video/tvmixer.c 1.5 -> 1.6 # drivers/net/wan/sbni.c 1.10 -> 1.11 # drivers/scsi/u14-34f.h 1.3 -> 1.4 # drivers/net/wavelan.c 1.8 -> 1.9 # net/socket.c 1.9 -> 1.11 # include/asm-ppc/ide.h 1.6 -> 1.7 # arch/sh/kernel/irq.c 1.5 -> 1.6 # include/asm-parisc/ide.h 1.1 -> 1.2 # kernel/user.c 1.1 -> 1.3 # drivers/message/i2o/i2o_config.c 1.7 -> 1.8 # drivers/net/e2100.c 1.7 -> 1.8 # drivers/net/lne390.c 1.4 -> 1.5 # fs/nfs/pagelist.c 1.2 -> 1.3 # drivers/media/video/Makefile 1.3 -> 1.5 # drivers/net/macmace.c 1.4 -> 1.5 # arch/sparc/kernel/sunos_ioctl.c 1.1 -> 1.2 # drivers/ide/cmd64x.c 1.1 -> 1.2 # net/ax25/af_ax25.c 1.7 -> 1.8 # drivers/char/drm/Config.in 1.7 -> 1.8 # drivers/net/sunqe.c 1.8 -> 1.9 # drivers/ide/Config.in 1.6 -> 1.7 # include/linux/brlock.h 1.2 -> 1.3 # drivers/net/sgiseeq.c 1.4 -> 1.5 # include/asm-arm/arch-cl7500/system.h 1.2 -> 1.3 # arch/i386/kernel/dmi_scan.c 1.14 -> 1.15 # drivers/net/irda/w83977af_ir.c 1.7 -> 1.8 # fs/partitions/ldm.c 1.4 -> 1.5 # drivers/net/irda/sa1100_ir.c 1.1 -> 1.2 # drivers/media/video/bttvp.h 1.4 -> 1.5 # include/asm-sparc64/ide.h 1.2 -> 1.3 # include/asm-arm/softirq.h 1.5 -> 1.6 # drivers/ide/sis5513.c 1.4 -> 1.5 # drivers/ide/ide-cd.h 1.3 -> 1.5 # Documentation/Configure.help 1.76 -> 1.84 # drivers/net/yellowfin.c 1.14 -> 1.15 # fs/exec.c 1.19 -> 1.21 # drivers/net/hp100.c 1.4 -> 1.5 # arch/i386/kernel/i8259.c 1.4 -> 1.5 # arch/sh/kernel/entry.S 1.5 -> 1.6 # include/linux/irq_cpustat.h 1.4 -> 1.5 # drivers/scsi/scsi_merge.c 1.7 -> 1.8 # net/netlink/netlink_dev.c 1.4 -> 1.5 # arch/ia64/kernel/perfmon.c 1.5 -> 1.6 # drivers/char/applicom.c 1.4 -> 1.5 # mm/swap.c 1.16 -> 1.18 # drivers/ide/piix.c 1.7 -> 1.8 # fs/locks.c 1.7 -> 1.8 # drivers/net/at1700.c 1.7 -> 1.9 # include/asm-sh/hardirq.h 1.3 -> 1.4 # drivers/char/drm/i810_drv.h 1.2 -> 1.3 # arch/sparc64/solaris/timod.c 1.8 -> 1.9 # arch/ppc/kernel/setup.c 1.15 -> 1.16 # include/linux/netdevice.h 1.13 -> 1.14 # fs/reiserfs/buffer2.c 1.7 -> 1.9 # kernel/signal.c 1.4 -> 1.5 # drivers/net/tokenring/madgemc.c 1.7 -> 1.8 # include/asm-arm/pgalloc.h 1.3 -> 1.4 # arch/sparc64/kernel/sunos_ioctl32.c 1.1 -> 1.2 # drivers/net/irda/ali-ircc.c 1.5 -> 1.7 # drivers/char/Makefile 1.14 -> 1.16 # include/pcmcia/ss.h 1.3 -> 1.4 # drivers/net/ne3210.c 1.4 -> 1.5 # net/x25/af_x25.c 1.5 -> 1.6 # drivers/net/dl2k.c 1.9 -> 1.10 # include/linux/pci_ids.h 1.28 -> 1.29 # drivers/scsi/aic7xxx/aic7xxx_linux_host.h 1.3 -> 1.4 # include/linux/irq.h 1.4 -> 1.5 # net/lapb/lapb_iface.c 1.4 -> 1.5 # fs/partitions/Makefile 1.3 -> 1.4 # drivers/video/acornfb.c 1.8 -> 1.9 # include/linux/kernel_stat.h 1.2 -> 1.4 # drivers/net/sungem.c 1.19 -> 1.20 # include/asm-s390x/pgtable.h 1.4 -> 1.5 # drivers/net/sonic.c 1.3 -> 1.4 # include/asm-arm/dma.h 1.2 -> 1.3 # drivers/net/arcnet/arc-rawmode.c 1.2 -> 1.3 # mm/shmem.c 1.30 -> 1.31 # drivers/char/drm/i810_drv.c 1.2 -> 1.3 # include/asm-mips/spinlock.h 1.2 -> 1.3 # drivers/net/hamradio/soundmodem/sm_wss.c 1.2 -> 1.3 # include/asm-parisc/signal.h 1.1 -> 1.2 # drivers/net/ns83820.c 1.10 -> 1.11 # drivers/net/wireless/orinoco_plx.c 1.4 -> 1.5 # drivers/scsi/Config.in 1.10 -> 1.11 # drivers/i2c/Config.in 1.4 -> 1.5 # include/asm-parisc/pgalloc.h 1.2 -> 1.3 # include/asm-arm/ide.h 1.1 -> 1.2 # drivers/net/wireless/airport.c 1.7 -> 1.8 # drivers/net/sis900.c 1.20 -> 1.21 # include/asm-i386/softirq.h 1.10 -> 1.11 # drivers/net/wan/comx-hw-mixcom.c 1.6 -> 1.7 # drivers/media/video/tda9875.c 1.5 -> 1.6 # drivers/net/isa-skeleton.c 1.5 -> 1.6 # drivers/sound/sound_core.c 1.5 -> 1.6 # drivers/net/hamradio/dmascc.c 1.2 -> 1.4 # include/asm-mips64/spinlock.h 1.1 -> 1.2 # drivers/ide/ide-cd.c 1.13 -> 1.15 # mm/swap_state.c 1.17 -> 1.19 # drivers/net/de600.c 1.10 -> 1.11 # drivers/net/hamradio/mkiss.c 1.4 -> 1.5 # arch/ppc/math-emu/op-4.h 1.2 -> 1.3 # drivers/net/via-rhine.c 1.17 -> 1.18 # drivers/net/tokenring/lanstreamer.c 1.10 -> 1.11 # drivers/video/matrox/matroxfb_DAC1064.h 1.2 -> 1.3 # drivers/pci/setup-bus.c 1.3 -> 1.4 # drivers/net/ewrk3.c 1.10 -> 1.11 # drivers/sound/nm256_audio.c 1.4 -> 1.5 # drivers/ide/ide.c 1.18 -> 1.19 # drivers/usb/pegasus.h 1.6 -> 1.7 # include/linux/hdreg.h 1.2 -> 1.3 # fs/binfmt_elf.c 1.16 -> 1.17 # drivers/ide/ataraid.c 1.3 -> 1.4 # drivers/net/ne.c 1.7 -> 1.8 # drivers/net/wan/lmc/lmc_var.h 1.3 -> 1.4 # drivers/usb/pegasus.c 1.11 -> 1.12 # mm/memory.c 1.45 -> 1.47 # drivers/video/matrox/matroxfb_DAC1064.c 1.6 -> 1.7 # drivers/net/ni5010.c 1.7 -> 1.8 # include/asm-mips64/pgtable.h 1.3 -> 1.4 # drivers/net/tokenring/olympic.h 1.5 -> 1.6 # include/asm-sparc/ioctls.h 1.1 -> 1.2 # drivers/net/hamradio/bpqether.c 1.4 -> 1.5 # drivers/video/chipsfb.c 1.4 -> 1.5 # drivers/net/epic100.c 1.16 -> 1.17 # fs/inode.c 1.32 -> 1.36 # include/asm-i386/apic.h 1.4 -> 1.5 # include/asm-i386/hw_irq.h 1.4 -> 1.6 # drivers/net/acenic.c 1.17 -> 1.19 # drivers/net/8139cp.c 1.5 -> 1.6 # drivers/video/matrox/i2c-matroxfb.c 1.3 -> 1.4 # fs/adfs/map.c 1.2 -> 1.3 # include/asm-arm/arch-sa1100/keyboard.h 1.5 -> 1.6 # drivers/net/pcmcia/com20020_cs.c 1.3 -> 1.4 # drivers/net/arcnet/rfc1201.c 1.2 -> 1.3 # drivers/sound/es1370.c 1.9 -> 1.10 # include/linux/slab.h 1.8 -> 1.9 # include/linux/pci.h 1.20 -> 1.21 # include/asm-i386/io.h 1.7 -> 1.8 # drivers/net/hydra.c 1.5 -> 1.6 # drivers/net/atp.c 1.6 -> 1.7 # include/linux/ext2_fs_i.h 1.3 -> 1.4 # arch/i386/kernel/setup.c 1.35 -> 1.38 # drivers/net/ne2.c 1.5 -> 1.6 # include/asm-arm/signal.h 1.2 -> 1.3 # include/linux/dcache.h 1.8 -> 1.9 # arch/i386/kernel/pci-irq.c 1.12 -> 1.13 # arch/i386/kernel/smpboot.c 1.8 -> 1.9 # drivers/media/video/bttv-cards.c 1.6 -> 1.7 # drivers/net/hamradio/baycom_epp.c 1.4 -> 1.5 # drivers/net/apne.c 1.3 -> 1.4 # drivers/net/3c523.c 1.6 -> 1.7 # kernel/printk.c 1.8 -> 1.10 # drivers/net/hamachi.c 1.15 -> 1.16 # drivers/video/sgivwfb.c 1.5 -> 1.6 # drivers/net/wan/cycx_main.c 1.6 -> 1.7 # drivers/video/sis/init301.h 1.1 -> 1.2 # arch/i386/kernel/apic.c 1.9 -> 1.10 # drivers/net/3c505.c 1.10 -> 1.11 # drivers/scsi/aacraid/dpcsup.c 1.1 -> 1.2 # drivers/char/drm/drm_vm.h 1.9 -> 1.10 # drivers/media/video/id.h 1.2 -> 1.3 # drivers/net/tokenring/smctr.c 1.12 -> 1.13 # fs/ufs/truncate.c 1.6 -> 1.7 # include/linux/spinlock.h 1.4 -> 1.5 # drivers/net/wan/sdla.c 1.5 -> 1.6 # drivers/scsi/scsi_syms.c 1.3 -> 1.4 # net/econet/af_econet.c 1.4 -> 1.5 # net/netrom/af_netrom.c 1.7 -> 1.8 # net/rose/af_rose.c 1.6 -> 1.7 # kernel/ptrace.c 1.8 -> 1.9 # arch/i386/kernel/i387.c 1.5 -> 1.6 # drivers/video/matrox/matroxfb_base.h 1.7 -> 1.8 # drivers/net/appletalk/cops.c 1.7 -> 1.8 # include/linux/tty.h 1.3 -> 1.4 # drivers/net/slip.c 1.6 -> 1.7 # net/sunrpc/pmap_clnt.c 1.2 -> 1.3 # drivers/net/irda/irda-usb.c 1.9 -> 1.10 # net/core/dev.c 1.23 -> 1.24 # arch/sh/config.in 1.6 -> 1.8 # include/asm-sh/pgtable.h 1.6 -> 1.7 # Documentation/video4linux/bttv/Sound-FAQ 1.4 -> 1.5 # net/ipv4/netfilter/ip_queue.c 1.6 -> 1.7 # drivers/scsi/scsi.c 1.15 -> 1.16 # drivers/char/mem.c 1.15 -> 1.17 # include/asm-mips64/softirq.h 1.2 -> 1.3 # include/asm-alpha/signal.h 1.1 -> 1.2 # include/asm-sparc/ide.h 1.1 -> 1.2 # drivers/video/tridentfb.c 1.1 -> 1.2 # include/linux/videodev.h 1.10 -> 1.11 # drivers/net/mac89x0.c 1.6 -> 1.7 # drivers/net/sk_g16.c 1.7 -> 1.8 # arch/mips/sni/pci.c 1.2 -> 1.3 # scripts/header.tk 1.2 -> 1.3 # drivers/video/Config.in 1.9 -> 1.10 # net/ipv4/netfilter/ipt_ULOG.c 1.1 -> 1.2 # drivers/char/tty_io.c 1.17 -> 1.18 # drivers/net/tulip/tulip_core.c 1.23 -> 1.24 # include/asm-sparc/signal.h 1.1 -> 1.2 # drivers/net/3c507.c 1.8 -> 1.9 # drivers/block/DAC960.c 1.14 -> 1.15 # drivers/net/gt96100eth.c 1.3 -> 1.4 # arch/mips/kernel/signal.c 1.4 -> 1.5 # drivers/net/am79c961a.c 1.4 -> 1.5 # drivers/net/cs89x0.c 1.8 -> 1.9 # drivers/isdn/avmb1/kcapi.c 1.8 -> 1.9 # drivers/ide/ide-pci.c 1.12 -> 1.13 # drivers/char/drm/drm_stub.h 1.1 -> 1.2 # drivers/net/tokenring/tmspci.c 1.7 -> 1.8 # include/linux/bootmem.h 1.1 -> 1.2 # drivers/net/dmfe.c 1.20 -> 1.21 # drivers/net/hamradio/baycom_par.c 1.3 -> 1.4 # drivers/scsi/aacraid/aacraid.h 1.2 -> 1.3 # drivers/scsi/aacraid/aachba.c 1.1 -> 1.2 # drivers/char/drm/Makefile 1.7 -> 1.8 # drivers/ide/pdc202xx.c 1.8 -> 1.9 # (new) -> 1.1 include/asm-cris/bootmem.h # (new) -> 1.1 drivers/media/video/margi/AUTHORS # (new) -> 1.1 include/asm-um/sembuf.h # (new) -> 1.1 include/linux/hostfs_fs_i.h # (new) -> 1.1 arch/um/drivers/hostaudio_user.c # (new) -> 1.1 arch/um/ptproxy/proxy.c # (new) -> 1.1 include/asm-m68k/bootmem.h # (new) -> 1.1 arch/um/kernel/trap_kern.c # (new) -> 1.1 include/asm-um/mmu.h # (new) -> 1.1 arch/um/drivers/stdio_console.h # (new) -> 1.1 drivers/scsi/ch.c # (new) -> 1.1 arch/um/kernel/irq_user.c # (new) -> 1.1 include/asm-um/ipc.h # (new) -> 1.1 arch/um/fs/Makefile # (new) -> 1.1 arch/um/drivers/ssl.h # (new) -> 1.1 include/asm-sparc64/bootmem.h # (new) -> 1.1 arch/um/drivers/net_user.h # (new) -> 1.1 arch/um/drivers/mconsole_user.c # (new) -> 1.1 include/asm-um/termios.h # (new) -> 1.1 arch/um/drivers/daemon_user.c # (new) -> 1.1 drivers/media/video/margi/ost/demux.h # (new) -> 1.1 drivers/sensors/w83781d.c # (new) -> 1.1 drivers/sensors/Makefile # (new) -> 1.1 drivers/sensors/lm75.c # (new) -> 1.1 include/asm-um/bootmem.h # (new) -> 1.1 include/asm-um/pgtable.h # (new) -> 1.1 drivers/media/video/margi/margi.opts # (new) -> 1.1 include/asm-um/boot.h # (new) -> 1.1 arch/um/fs/hostfs/hostfs_kern.c # (new) -> 1.1 arch/um/include/sysdep-i386/sigcontext.h # (new) -> 1.1 drivers/media/video/margi/osd.c # (new) -> 1.1 drivers/char/drm/picker.c # (new) -> 1.1 drivers/sensors/it87.c # (new) -> 1.1 arch/um/drivers/ssl.c # (new) -> 1.1 include/asm-um/sigcontext-i386.h # (new) -> 1.1 arch/um/Makefile-i386 # (new) -> 1.1 drivers/sensors/eeprom.c # (new) -> 1.1 include/asm-um/div64.h # (new) -> 1.1 arch/um/sys-ppc/miscthings.c # (new) -> 1.1 drivers/media/video/margi/video.c # (new) -> 1.1 drivers/sensors/adm9240.c # (new) -> 1.1 include/asm-um/pgalloc.h # (new) -> 1.1 arch/um/kernel/user_util.c # (new) -> 1.1 include/asm-s390x/bootmem.h # (new) -> 1.1 drivers/media/video/margi/audio.h # (new) -> 1.1 include/asm-um/mmu_context.h # (new) -> 1.1 arch/um/drivers/stdio_console.c # (new) -> 1.1 include/asm-um/smp.h # (new) -> 1.1 arch/um/kernel/process.c # (new) -> 1.1 arch/um/include/sysdep-ia64/sigcontext.h # (new) -> 1.1 arch/um/drivers/ethertap_kern.c # (new) -> 1.1 arch/um/include/user_util.h # (new) -> 1.1 arch/um/drivers/ubd.c # (new) -> 1.1 include/asm-arm/bootmem.h # (new) -> 1.1 include/asm-um/highmem.h # (new) -> 1.1 include/asm-um/bugs.h # (new) -> 1.1 Documentation/vm/Changelog.rmap # (new) -> 1.1 drivers/sensors/adm1024.c # (new) -> 1.1 include/asm-um/atomic.h # (new) -> 1.1 arch/um/drivers/mconsole_kern.c # (new) -> 1.1 drivers/video/neofb.c # (new) -> 1.1 arch/um/sys-ia64/Makefile # (new) -> 1.1 arch/um/kernel/umid.c # (new) -> 1.1 include/asm-um/elf.h # (new) -> 1.1 arch/um/config.release # (new) -> 1.1 include/asm-um/statfs.h # (new) -> 1.1 include/asm-mips64/rmap.h # (new) -> 1.1 drivers/i2c/i2c-tsunami.c # (new) -> 1.1 drivers/sensors/adm1025.c # (new) -> 1.1 arch/um/ptproxy/Makefile # (new) -> 1.1 include/asm-um/unistd.h # (new) -> 1.1 arch/um/kernel/unmap.c # (new) -> 1.1 include/asm-um/keyboard.h # (new) -> 1.1 arch/um/drivers/hostaudio_kern.c # (new) -> 1.1 include/asm-parisc/bootmem.h # (new) -> 1.1 include/asm-um/stat.h # (new) -> 1.1 arch/um/include/kern_util.h # (new) -> 1.1 arch/um/kernel/exec_kern.c # (new) -> 1.1 arch/um/include/sysdep-i386/ptrace.h # (new) -> 1.1 drivers/media/video/margi/l64021.h # (new) -> 1.1 drivers/media/video/margi/dvb_filter.h # (new) -> 1.1 arch/um/sys-ppc/ptrace.c # (new) -> 1.1 arch/um/drivers/tuntap_kern.h # (new) -> 1.1 drivers/sensors/sis5595.c # (new) -> 1.1 arch/um/include/sysdep-ia64/syscalls.h # (new) -> 1.1 drivers/media/video/margi/margi.c # (new) -> 1.1 drivers/sensors/ddcmon.c # (new) -> 1.1 arch/um/include/2_5compat.h # (new) -> 1.1 drivers/media/video/margi/audio.c # (new) -> 1.1 include/linux/videodev2.h # (new) -> 1.1 arch/um/include/sysdep-ppc/syscalls.h # (new) -> 1.1 arch/um/ptproxy/wait.h # (new) -> 1.1 arch/um/config.in # (new) -> 1.1 arch/um/link.ld.in # (new) -> 1.1 arch/um/main.c # (new) -> 1.1 drivers/video/matrox/g450_pll.h # (new) -> 1.1 arch/um/sys-i386/ptrace_user.c # (new) -> 1.1 include/asm-sparc/rmap.h # (new) -> 1.1 drivers/media/video/margi/video.h # (new) -> 1.1 drivers/sensors/lm78.c # (new) -> 1.1 drivers/media/video/margi/dvb_filter.c # (new) -> 1.1 arch/um/kernel/time_kern.c # (new) -> 1.1 arch/um/ptproxy/ptrace.c # (new) -> 1.1 arch/um/drivers/slip_kern.c # (new) -> 1.1 drivers/media/video/margi/margi.h # (new) -> 1.1 Documentation/scsi-changer.txt # (new) -> 1.1 include/asm-um/scatterlist.h # (new) -> 1.1 arch/um/kernel/tlb.c # (new) -> 1.1 arch/um/ptproxy/ptproxy.h # (new) -> 1.1 arch/um/Makefile-ia64 # (new) -> 1.1 arch/um/drivers/tuntap.h # (new) -> 1.1 include/linux/treap.h # (new) -> 1.1 drivers/sensors/gl520sm.c # (new) -> 1.1 arch/um/include/debug.h # (new) -> 1.1 include/asm-alpha/bootmem.h # (new) -> 1.1 mm/TODO # (new) -> 1.1 drivers/video/matrox/g450_pll.c # (new) -> 1.1 drivers/media/video/margi/cardbase.c # (new) -> 1.1 drivers/media/video/margi/ringbuffy.h # (new) -> 1.1 drivers/sensors/Config.in # (new) -> 1.1 drivers/media/video/margi/ost/audio.h # (new) -> 1.1 include/asm-um/init.h # (new) -> 1.1 arch/um/include/user.h # (new) -> 1.1 arch/um/include/chan_user.h # (new) -> 1.1 include/asm-i386/bootmem.h # (new) -> 1.1 include/asm-generic/bootmem.h # (new) -> 1.1 include/asm-um/sigcontext-generic.h # (new) -> 1.1 include/asm-sparc/bootmem.h # (new) -> 1.1 include/asm-sh/bootmem.h # (new) -> 1.1 include/asm-um/termbits.h # (new) -> 1.1 arch/um/kernel/Makefile # (new) -> 1.1 arch/um/fs/hostfs/hostfs.h # (new) -> 1.1 drivers/media/video/margi/crc.h # (new) -> 1.1 arch/um/drivers/daemon_kern.h # (new) -> 1.1 include/asm-um/poll.h # (new) -> 1.1 arch/um/include/sysrq.h # (new) -> 1.1 include/asm-um/ioctl.h # (new) -> 1.1 include/asm-um/dma.h # (new) -> 1.1 arch/um/drivers/mcast_user.c # (new) -> 1.1 drivers/sensors/lm87.c # (new) -> 1.1 arch/um/sys-ppc/ptrace_user.c # (new) -> 1.1 drivers/media/video/margi/po # (new) -> 1.1 drivers/media/video/margi/dram.h # (new) -> 1.1 drivers/media/video/margi/i2c.h # (new) -> 1.1 include/asm-parisc/rmap.h # (new) -> 1.1 arch/um/drivers/port.h # (new) -> 1.1 arch/um/defconfig # (new) -> 1.1 include/asm-um/vga.h # (new) -> 1.1 include/asm-um/linux_logo.h # (new) -> 1.1 include/asm-s390/rmap.h # (new) -> 1.1 arch/um/drivers/net_user.c # (new) -> 1.1 include/asm-um/hdreg.h # (new) -> 1.1 arch/um/fs/hostfs/hostfs_user.c # (new) -> 1.1 arch/um/sys-ppc/Makefile # (new) -> 1.1 include/asm-um/rwsem.h # (new) -> 1.1 arch/um/kernel/sys_call_table.c # (new) -> 1.1 include/asm-um/archparam-ppc.h # (new) -> 1.1 drivers/media/video/margi/ost/dmx.h # (new) -> 1.1 drivers/media/video/margi/dmxdev.h # (new) -> 1.1 arch/um/drivers/port.c # (new) -> 1.1 arch/um/drivers/chan_kern.c # (new) -> 1.1 include/asm-i386/rmap.h # (new) -> 1.1 drivers/media/video/margi/spu.c # (new) -> 1.1 drivers/media/video/margi/osd.h # (new) -> 1.1 include/asm-um/floppy.h # (new) -> 1.1 include/asm-um/siginfo.h # (new) -> 1.1 drivers/i2c/i2c-hydra.c # (new) -> 1.1 drivers/i2c/i2c-viapro.c # (new) -> 1.1 include/asm-um/smplock.h # (new) -> 1.1 arch/um/include/line.h # (new) -> 1.1 include/asm-um/semaphore.h # (new) -> 1.1 arch/um/drivers/ethertap_user.c # (new) -> 1.1 arch/um/kernel/setup.c # (new) -> 1.1 include/asm-mips64/bootmem.h # (new) -> 1.1 drivers/media/video/v4l1-compat.c # (new) -> 1.1 drivers/char/drm/i830_dma.c # (new) -> 1.1 include/asm-um/shmparam.h # (new) -> 1.1 include/asm-um/ipcbuf.h # (new) -> 1.1 include/asm-um/sigcontext-ppc.h # (new) -> 1.1 include/asm-um/bitops.h # (new) -> 1.1 drivers/video/neofb.h # (new) -> 1.1 include/asm-um/processor-ppc.h # (new) -> 1.1 include/asm-um/socket.h # (new) -> 1.1 include/asm-um/checksum.h # (new) -> 1.1 drivers/media/video/margi/ost/osd.h # (new) -> 1.1 arch/um/sys-i386/sysrq.c # (new) -> 1.1 include/asm-um/segment.h # (new) -> 1.1 arch/um/kernel/uaccess_user.c # (new) -> 1.1 include/asm-mips/bootmem.h # (new) -> 1.1 drivers/media/video/margi/cvdv.h # (new) -> 1.1 drivers/media/video/margi/streams.c # (new) -> 1.1 include/asm-um/param.h # (new) -> 1.1 drivers/media/video/margi/ost/ca.h # (new) -> 1.1 arch/um/include/sysdep-i386/syscalls.h # (new) -> 1.1 arch/um/drivers/tty.c # (new) -> 1.1 arch/um/include/initrd.h # (new) -> 1.1 drivers/sensors/matorb.c # (new) -> 1.1 arch/um/include/signal_kern.h # (new) -> 1.1 include/asm-um/tlb.h # (new) -> 1.1 include/asm-um/timex.h # (new) -> 1.1 include/linux/blkcdb.h # (new) -> 1.1 drivers/media/video/margi/ringbuffy.c # (new) -> 1.1 arch/um/kernel/sysrq.c # (new) -> 1.1 drivers/i2c/i2c-piix4.c # (new) -> 1.1 include/asm-um/namei.h # (new) -> 1.1 arch/um/include/sysdep-ia64/ptrace.h # (new) -> 1.1 lib/radix-tree.c # (new) -> 1.1 drivers/media/video/margi/README # (new) -> 1.1 drivers/media/video/margi/CHANGES # (new) -> 1.1 drivers/media/video/margi/dvb_demux.h # (new) -> 1.1 include/asm-um/page.h # (new) -> 1.1 include/asm-ia64/rmap.h # (new) -> 1.1 arch/um/kernel/mprot.h # (new) -> 1.1 drivers/sensors/ds1621.c # (new) -> 1.1 arch/um/drivers/mmapper_kern.c # (new) -> 1.1 include/asm-um/hardirq.h # (new) -> 1.1 include/asm-s390/bootmem.h # (new) -> 1.1 drivers/ide/ide-taskfile.c # (new) -> 1.1 arch/um/kernel/syscall_kern.c # (new) -> 1.1 include/asm-um/spinlock.h # (new) -> 1.1 include/linux/mm_inline.h # (new) -> 1.1 arch/um/include/umn.h # (new) -> 1.1 include/asm-um/pci.h # (new) -> 1.1 arch/um/kernel/reboot.c # (new) -> 1.1 include/asm-um/errno.h # (new) -> 1.1 arch/um/kernel/syscall_user.c # (new) -> 1.1 include/linux/pnpbios.h # (new) -> 1.1 arch/um/include/sysdep-ppc/ptrace.h # (new) -> 1.1 drivers/media/video/margi/margi # (new) -> 1.1 arch/um/kernel/process_kern.c # (new) -> 1.1 include/asm-um/fcntl.h # (new) -> 1.1 include/asm-um/types.h # (new) -> 1.1 arch/um/include/irq_user.h # (new) -> 1.1 drivers/media/video/margi/decoder.c # (new) -> 1.1 drivers/media/video/margi/dmxdev.c # (new) -> 1.1 drivers/sensors/via686a.c # (new) -> 1.1 include/asm-um/module.h # (new) -> 1.1 arch/um/boot/Makefile # (new) -> 1.1 arch/um/drivers/daemon_kern.c # (new) -> 1.1 include/asm-um/softirq.h # (new) -> 1.1 drivers/media/video/margi/ost/video.h # (new) -> 1.1 drivers/media/video/margi/margi_cs.4 # (new) -> 1.1 arch/um/drivers/etap.h # (new) -> 1.1 arch/um/sys-i386/sigcontext.c # (new) -> 1.1 arch/um/kernel/smp.c # (new) -> 1.1 arch/um/include/sigcontext.h # (new) -> 1.1 drivers/media/video/margi/cvdvtypes.h # (new) -> 1.1 include/asm-um/string.h # (new) -> 1.1 drivers/sensors/bt869.c # (new) -> 1.1 arch/um/sys-ppc/misc.S # (new) -> 1.1 arch/um/kernel/gprof_syms.c # (new) -> 1.1 arch/um/include/mconsole_kern.h # (new) -> 1.1 arch/um/include/hostaudio.h # (new) -> 1.1 arch/um/include/signal_user.h # (new) -> 1.1 arch/um/include/chan_kern.h # (new) -> 1.1 arch/um/ptproxy/sysdep.h # (new) -> 1.1 arch/um/kernel/signal_user.c # (new) -> 1.1 include/asm-um/io.h # (new) -> 1.1 drivers/ide/pdcadma.c # (new) -> 1.1 drivers/media/video/margi/ost/sec.h # (new) -> 1.1 drivers/sensors/thmc50.c # (new) -> 1.1 arch/um/include/process.h # (new) -> 1.1 drivers/i2c/i2c-isa.c # (new) -> 1.1 arch/um/include/kern.h # (new) -> 1.1 arch/um/drivers/xterm.c # (new) -> 1.1 drivers/char/drm/i830_drv.c # (new) -> 1.1 drivers/video/matrox/matroxfb_proc.c # (new) -> 1.1 drivers/pnp/pnpbios_proc.c # (new) -> 1.1 drivers/i2c/i2c-ali1535.c # (new) -> 1.1 drivers/sensors/fscpos.c # (new) -> 1.1 arch/um/drivers/slip_user.c # (new) -> 1.1 include/asm-um/cache.h # (new) -> 1.1 drivers/media/video/margi/COPYING # (new) -> 1.1 include/asm-um/mca_dma.h # (new) -> 1.1 arch/um/kernel/um_arch.c # (new) -> 1.1 arch/um/kernel/irq.c # (new) -> 1.1 include/asm-um/ioctls.h # (new) -> 1.1 drivers/media/video/margi/dvb_demux.c # (new) -> 1.1 drivers/i2c/i2c-i801.c # (new) -> 1.1 arch/um/drivers/ubd_user.c # (new) -> 1.1 arch/um/drivers/mcast_kern.h # (new) -> 1.1 arch/um/drivers/net_kern.c # (new) -> 1.1 include/asm-um/signal.h # (new) -> 1.1 drivers/scsi/ch.h # (new) -> 1.1 include/asm-um/mman.h # (new) -> 1.1 arch/um/kernel/signal_kern.c # (new) -> 1.1 drivers/media/video/margi/margi_cs.mk.MAIN # (new) -> 1.1 drivers/media/video/margi/README.CVS # (new) -> 1.1 include/asm-alpha/rmap.h # (new) -> 1.1 drivers/sensors/adm1021.c # (new) -> 1.1 arch/um/kernel/mem.c # (new) -> 1.1 drivers/char/drm/i830_drv.h # (new) -> 1.1 include/asm-um/rwlock.h # (new) -> 1.1 arch/um/kernel/initrd_kern.c # (new) -> 1.1 include/asm-ia64/bootmem.h # (new) -> 1.1 drivers/i2c/i2c-amd756.c # (new) -> 1.1 arch/um/drivers/Makefile # (new) -> 1.1 include/asm-m68k/rmap.h # (new) -> 1.1 drivers/char/amd768_rng.c # (new) -> 1.1 drivers/media/video/margi/margi_cs.mk # (new) -> 1.1 drivers/media/video/margi/margi.conf # (new) -> 1.1 arch/um/kernel/trap_user.c # (new) -> 1.1 include/asm-um/system-generic.h # (new) -> 1.1 include/asm-mips/rmap.h # (new) -> 1.1 drivers/media/video/v4l2-common.c # (new) -> 1.1 arch/um/drivers/chan_user.c # (new) -> 1.1 include/asm-s390x/rmap.h # (new) -> 1.1 drivers/char/drm/i830.h # (new) -> 1.1 drivers/media/video/margi/cvdvext.h # (new) -> 1.1 include/asm-um/cobalt.h # (new) -> 1.1 include/asm-um/resource.h # (new) -> 1.1 arch/um/Makefile-ppc # (new) -> 1.1 drivers/media/video/margi/l64014.h # (new) -> 1.1 drivers/i2c/i2c-voodoo3.c # (new) -> 1.1 arch/um/sys-ppc/sigcontext.c # (new) -> 1.1 arch/um/drivers/pty.c # (new) -> 1.1 arch/um/kernel/resource.c # (new) -> 1.1 drivers/media/video/margi/crc.c # (new) -> 1.1 drivers/media/video/margi/streams.h # (new) -> 1.1 include/asm-um/system-ppc.h # (new) -> 1.1 arch/um/sys-ppc/sysrq.c # (new) -> 1.1 arch/um/sys-i386/ptrace.c # (new) -> 1.1 include/asm-um/processor-i386.h # (new) -> 1.1 include/asm-um/user.h # (new) -> 1.1 arch/um/include/sysdep-ppc/sigcontext.h # (new) -> 1.1 include/asm-sh/rmap.h # (new) -> 1.1 include/asm-um/serial.h # (new) -> 1.1 arch/um/include/ubd_user.h # (new) -> 1.1 arch/um/drivers/tuntap_user.c # (new) -> 1.1 include/asm-um/sockios.h # (new) -> 1.1 include/asm-generic/rmap.h # (new) -> 1.1 arch/um/drivers/line.c # (new) -> 1.1 include/linux/segment_tree.h # (new) -> 1.1 drivers/media/video/margi/dvb_formats.h # (new) -> 1.1 arch/um/drivers/daemon.h # (new) -> 1.1 include/asm-um/hw_irq.h # (new) -> 1.1 arch/um/kernel/exec_user.c # (new) -> 1.1 arch/um/kernel/ksyms.c # (new) -> 1.1 drivers/media/video/margi/dvbdev.c # (new) -> 1.1 include/asm-um/fixmap.h # (new) -> 1.1 arch/um/ptproxy/sysdep.c # (new) -> 1.1 arch/um/drivers/port_kern.c # (new) -> 1.1 Documentation/preempt-locking.txt # (new) -> 1.1 include/asm-um/ptrace.h # (new) -> 1.1 drivers/media/video/margi/makedev.napi # (new) -> 1.1 drivers/media/video/margi/dvbdev.h # (new) -> 1.1 drivers/sensors/lm80.c # (new) -> 1.1 arch/um/kernel/time.c # (new) -> 1.1 arch/um/kernel/ptrace.c # (new) -> 1.1 arch/um/sys-i386/ldt.c # (new) -> 1.1 drivers/media/video/margi/spu.h # (new) -> 1.1 include/linux/radix-tree.h # (new) -> 1.1 drivers/media/video/margi/cardbase.h # (new) -> 1.1 include/asm-cris/rmap.h # (new) -> 1.1 include/asm-um/mtrr.h # (new) -> 1.1 drivers/char/drm/i830_drm.h # (new) -> 1.1 drivers/i2c/i2c-ali15x3.c # (new) -> 1.1 include/linux/sensors.h # (new) -> 1.1 arch/um/drivers/mcast_kern.c # (new) -> 1.1 drivers/media/video/margi/i2c.c # (new) -> 1.1 drivers/pnp/pnpbios_core.c # (new) -> 1.1 include/asm-um/irq.h # (new) -> 1.1 arch/um/include/mconsole.h # (new) -> 1.1 arch/um/drivers/slip.h # (new) -> 1.1 drivers/media/video/margi/cvdv.c # (new) -> 1.1 include/linux/chio.h # (new) -> 1.1 arch/um/fs/hostfs/Makefile # (new) -> 1.1 include/asm-um/a.out.h # (new) -> 1.1 drivers/media/video/margi/Makefile # (new) -> 1.2 mm/rmap.c # (new) -> 1.1 arch/um/kernel/user_syms.c # (new) -> 1.1 drivers/media/video/margi/decoder.h # (new) -> 1.1 drivers/char/amd768_pm.c # (new) -> 1.1 include/asm-um/desc.h # (new) -> 1.1 include/asm-ppc/bootmem.h # (new) -> 1.1 include/asm-um/page_offset.h # (new) -> 1.1 drivers/media/video/margi/ost/frontend.h # (new) -> 1.1 drivers/i2c/i2c-i810.c # (new) -> 1.1 arch/um/drivers/etap_kern.h # (new) -> 1.1 include/asm-um/posix_types.h # (new) -> 1.1 arch/um/drivers/tuntap_kern.c # (new) -> 1.1 include/asm-um/shmbuf.h # (new) -> 1.1 include/asm-um/uaccess.h # (new) -> 1.1 arch/um/include/mem_user.h # (new) -> 1.1 arch/um/drivers/mcast.h # (new) -> 1.1 arch/um/drivers/fd.c # (new) -> 1.1 include/asm-um/unaligned.h # (new) -> 1.1 arch/um/drivers/net_kern.h # (new) -> 1.1 include/asm-um/processor-generic.h # (new) -> 1.1 arch/um/Makefile # (new) -> 1.1 arch/um/sys-i386/Makefile # (new) -> 1.1 arch/um/include/umid.h # (new) -> 1.1 drivers/media/video/margi/dram.c # (new) -> 1.1 arch/um/include/init.h # (new) -> 1.1 include/asm-um/rmap.h # (new) -> 1.1 arch/um/kernel/mem_user.c # (new) -> 1.1 include/asm-um/ide.h # (new) -> 1.1 include/asm-ppc/rmap.h # (new) -> 1.1 drivers/sensors/sensors.c # (new) -> 1.1 include/asm-um/current.h # (new) -> 1.1 include/asm-um/delay.h # (new) -> 1.1 include/asm-um/msgbuf.h # (new) -> 1.1 drivers/i2c/i2c-via.c # (new) -> 1.1 drivers/sensors/gl518sm.c # (new) -> 1.1 include/asm-um/archparam-i386.h # (new) -> 1.1 include/asm-sparc64/rmap.h # (new) -> 1.1 drivers/i2c/i2c-sis5595.c # (new) -> 1.1 arch/um/sys-i386/ksyms.c # (new) -> 1.1 include/linux/lock_break.h # (new) -> 1.1 arch/um/ptproxy/wait.c # (new) -> 1.1 arch/um/drivers/slip_kern.h # (new) -> 1.1 drivers/media/video/margi/ost/net.h # (new) -> 1.1 drivers/sensors/mtp008.c # (new) -> 1.1 arch/um/kernel/init_task.c # (new) -> 1.1 drivers/sensors/maxilife.c # (new) -> 1.1 include/asm-um/byteorder.h # (new) -> 1.1 arch/um/sys-i386/syscalls.c # (new) -> 1.1 arch/um/kernel/initrd_user.c # (new) -> 1.1 include/asm-um/locks.h # (new) -> 1.1 include/asm-um/system-i386.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.155 # Many files: # Import patch 01_pre9-ac3 # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.156 # 2.4.18-pre9-ac3 # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.157 # O(1) version K3 (Ingo Molnar) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.158 # Preemptible Kernel (Robert M. Love) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.159 # Lock-Break for Preemptible Kernel (Robert M. Love) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.160 # Misc. Hashing (William Lee Irwin III) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.161 # Minor optimizations # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.162 # Loop Deadlock fix (Andrew Morton) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.163 # IRQ Rate Limit (Ingo Molnar) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.164 # Boot-time Memory reservation (William Lee Irwin III) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.165 # bdflush tuning, more hashing # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.166 # UserMode Linux (UML team) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.167 # Trie Pagecache (Momchil Velikov) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.168 # IDE CD-Audio DMA fix (Andrew Morton) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.169 # Xconfig Help Fix (Olaf Diestche) # Verbose BUG() (Hugh Dickins) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.170 # lm_sensors hardware monitoring (lm_sensors team) # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.171 # Video Updates (V4L2 team) # AMD Elan Updates # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.172 # sis.patch.20020128_1 # akpm_82cxx_devexit # 20_scsi-changer-2.4.18-pre9.diff # 37_akpm_make_request # -------------------------------------------- # 02/02/13 me@ohdarn.net 1.173 # Network Devices contribute to /dev/random (Robert M. Love) # -------------------------------------------- # diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Wed Feb 13 20:03:36 2002 +++ b/CREDITS Wed Feb 13 20:03:36 2002 @@ -434,6 +434,7 @@ E: lars@nocrew.org W: http://lars.nocrew.org/ D: dsp56k device driver +D: ptrace proxy in user mode kernel port S: Kopmansg 2 S: 411 13 Goteborg S: Sweden @@ -586,6 +587,13 @@ S: University of Michigan S: Ann Arbor, MI +N: Michael Cornwell +E: cornwell@acm.org +D: Original designer and co-author of ATA Taskfile +D: Kernel module SMART utilities +S: Santa Cruz, California +S: USA + N: Kees Cook E: cook@cpoint.net W: http://outflux.net/ @@ -706,7 +714,7 @@ E: jdike@karaya.com W: http://user-mode-linux.sourceforge.net D: User mode kernel port -S: RR1 Box 67C +S: 375 Tubbs Hill Rd S: Deering NH 03244 S: USA @@ -981,8 +989,8 @@ N: Nigel Gamble E: nigel@nrg.org -E: nigel@sgi.com D: Interrupt-driven printer driver +D: Preemptible kernel S: 120 Alley Way S: Mountain View, California 94040 S: USA @@ -1184,22 +1192,19 @@ N: Andre Hedrick E: andre@linux-ide.org -E: andre@aslab.com -E: andre@suse.com +E: andre@linuxdiskcert.org W: http://www.linux-ide.org/ +W: http://www.linuxdiskcert.org/ D: Random SMP kernel hacker... D: Uniform Multi-Platform E-IDE driver D: Active-ATA-Chipset maddness.......... -D: Ultra DMA 100/66/33 -D: ATA-Disconnect +D: Ultra DMA 133/100/66/33 w/48-bit Addressing +D: ATA-Disconnect, ATA-TCQ D: ATA-Smart Kernel Daemon +D: Serial ATA +D: ATA Command Block and Taskfile S: Linux ATA Development (LAD) S: Concord, CA -S: ASL, Inc. 1-877-ASL-3535 -S: 1757 Houret Court, Milpitas, CA 95035 -S: SuSE Linux, Inc. -S: 580 Second Street, Suite 210 Oakland, CA 94607 -S: USA N: Jochen Hein E: jochen@jochen.org diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Wed Feb 13 20:03:49 2002 +++ b/Documentation/Configure.help Wed Feb 13 20:03:49 2002 @@ -266,6 +266,29 @@ If you have a system with several CPUs, you do not need to say Y here: the local APIC will be used automatically. +Preemptible Kernel +CONFIG_PREEMPT + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + real-time system. Say N if you are unsure. + +Break Selected Locks +CONFIG_LOCK_BREAK + This option will break certain locks in high-latency regions + throughout the kernel. It is intended for use in conjunction with + the preemptible kernel (CONFIG_PREEMPT). Since in-kernel preemption + can not occur while locks are held, temporarily releasing and then + reacquiring long-held locks will further improve system response. + + Say Y if you are compiling for a system with strict latency + requirements such as an embedded, real-time, or audio processing + system. Say N otherwise. + Kernel math emulation CONFIG_MATH_EMULATION Linux can emulate a math coprocessor (used for floating point @@ -723,6 +746,59 @@ say M here and read . The module will be called ide-floppy.o. +AWARD Bios Work-Around +CONFIG_IDEDISK_STROKE + Should you have a system w/ an AWARD Bios and your drives are larger + than 32GB and it will not boot, one is required to perform a few OEM + operations first. The option is called "STROKE" because it allows + one to "soft clip" the drive to work around a barrier limit. For + Maxtor drives it is called "jumpon.exe". Please search Maxtor's + web-site for "JUMPON.EXE". IBM has a similar tool at: + . + + If you are unsure, say N here. + +Raw Access to Media +CONFIG_IDE_TASK_IOCTL + This is a direct raw access to the media. It is a complex but + elegant solution to test and validate the domain of the hardware and + perform below the driver data recover if needed. This is the most + basic form of media-forensics. + + If you are unsure, say N here. + +Use Taskfile I/O +CONFIG_IDE_TASKFILE_IO + This is the "Jewel" of the patch. It will go away and become the new + driver core. Since all the chipsets/host side hardware deal w/ their + exceptions in "their local code" currently, adoption of a + standardized data-transport is the only logical solution. + Additionally we packetize the requests and gain rapid performance and + a reduction in system latency. Additionally by using a memory struct + for the commands we can redirect to a MMIO host hardware in the next + generation of controllers, specifically second generation Ultra133 + and Serial ATA. + + Since this is a major transition, it was deemed necessary to make the + driver paths buildable in separtate models. Therefore if using this + option fails for your arch then we need to address the needs for that + arch. + + If you want to test this functionality, say Y here. + +Force DMA +CONFIG_BLK_DEV_IDEDMA_FORCED + This is an old piece of lost code from Linux 2.0 Kernels. + + Generally say N here. + +DMA Only on Disks +CONFIG_IDEDMA_ONLYDISK + This is used if you know your ATAPI Devices are going to fail DMA + Transfers. + + Generally say N here. + SCSI emulation support CONFIG_BLK_DEV_IDESCSI This will provide SCSI host adapter emulation for IDE ATAPI devices, @@ -747,6 +823,14 @@ If both this SCSI emulation and native ATAPI support are compiled into the kernel, the native support will be used. +Use the NOOP Elevator (WARNING) +CONFIG_BLK_DEV_ELEVATOR_NOOP + If you are using a raid class top-level driver above the ATA/IDE core, + one may find a performance boost by preventing a merging and re-sorting + of the new requests. + + If unsure, say N. + ISA-PNP EIDE support CONFIG_BLK_DEV_ISAPNP If you have an ISA EIDE card that is PnP (Plug and Play) and @@ -4207,6 +4291,16 @@ This driver supports the L7200 Color LCD. Say Y if you want graphics support. +NeoMagic display support (EXPERIMENTAL) +CONFIG_FB_NEOMAGIC + This driver supports notebooks with NeoMagic PCI chips. + Say Y if you have such a graphics card. + + The driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). The + module will be called neofb.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + PowerMac "control" frame buffer device support CONFIG_FB_CONTROL This driver supports a frame buffer for the graphics adapter in the @@ -4414,7 +4508,8 @@ CONFIG_FB_MATROX Say Y here if you have a Matrox Millennium, Millennium II, Mystique, Mystique 220, Productiva G100, Mystique G200, Millennium G200, - Marvel G200 video, G400, G450, or G550 card in your box. + Matrox G400, G450 or G550 card in your box. At this time, support for + the G-series digital output is almost non-existant. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). @@ -4453,6 +4548,10 @@ "I2C support" and "I2C bit-banging support" in the character devices section, and then to "Matrox I2C support" and "G400 second head support" here in the framebuffer section. + + If you have G550, you must also compile support for G450/G550 secondary + head into kernel, otherwise picture will be shown only on the output you + are probably not using... If you need support for G450 or G550 secondary head, say Y to "Matrox G450/G550 second head support" below. @@ -4503,7 +4602,8 @@ Matrox G450 second head support CONFIG_FB_MATROX_G450 Say Y or M here if you want to use a secondary head (meaning two - monitors in parallel) on G450 or G550. + monitors in parallel) on G450, or if you are using analog output + of G550. If you compile it as module, two modules are created, matroxfb_crtc2.o and matroxfb_g450.o. Both modules are needed if you @@ -6771,6 +6871,21 @@ . The module will be called sg.o. If unsure, say N. +SCSI media changer support +CONFIG_CHR_DEV_SCH + This is a driver for SCSI media changers. Most common devices are + tape libraries and MOD/CDROM jukeboxes. *Real* jukeboxes, you + don't need this for those tiny 6-slot cdrom changers. Media + changers are listed as "Type: Medium Changer" in /proc/scsi/scsi. + If you have such hardware and want to use it with linux, say Y + here. Check for details. + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read and + . The module will be called ch.o. + If unsure, say N. + Probe all LUNs on each SCSI device CONFIG_SCSI_MULTI_LUN If you have a SCSI device that supports more than one LUN (Logical @@ -9100,6 +9215,20 @@ If you don't know what to use this for, you don't need it. +Allow Net Devices to contribute to /dev/random +CONFIG_NET_RANDOM + If you say Y here, network device interrupts will contribute to the + kernel entropy pool. Normally, block devices and some other devices + feed the pool. Some systems, such as those that are headless or diskless, + need additional entropy sources. Some people, however, feel that network + devices should not contribute to /dev/random because an external attacker + could observe incoming packets in an attempt to learn the entropy pool's + state. If you say N, no network device will contribute entropy. + + If you believe there is a chance of your network packets being observed + and you doubt the security of the entropy pool's one-way hash, do not + enable this. If unsure, say N. + Ethertap network tap (OBSOLETE) CONFIG_ETHERTAP If you say Y here (and have said Y to "Kernel/User network link @@ -13642,7 +13771,7 @@ Synchronous operation (i.e. always writing data to the host's disk immediately) is configurable on a per-UBD basis by using a special kernel command line option. Alternatively, you can say Y here to - turn on synchronous operation by default for all block. + turn on synchronous operation by default for all block devices. If you're running a journalling file system (like reiserfs, for example) in your virtual machine, you will want to say Y here. If @@ -13654,6 +13783,7 @@ CONFIG_PT_PROXY This option enables a debugging interface which allows gdb to debug the kernel without needing to actually attach to kernel threads. + CONFIG_XTERM_CHAN must be enabled in order to enable CONFIG_PT_PROXY. If you want to do kernel debugging, say Y here; otherwise say N. Management console @@ -13848,26 +13978,116 @@ SLIP transport CONFIG_UML_NET_SLIP - The Slip User-Mode Linux network transport allows a running UML to + The slip User-Mode Linux network transport allows a running UML to network with its host over a point-to-point link. Unlike Ethertap, which can carry any Ethernet frame (and hence even non-IP packets), - the Slip transport can only carry IP packets. + the slip transport can only carry IP packets. - To use this, your host must support Slip devices. + To use this, your host must support slip devices. For more information, see . That site - has examples of the UML command line to use to enable Slip + has examples of the UML command line to use to enable slip networking, and details of a few quirks with it. - The Ethertap Transport is preferred over Slip because of its - limitation. If you prefer Slip, however, say Y here. Otherwise + The Ethertap Transport is preferred over slip because of its + limitations. If you prefer slip, however, say Y here. Otherwise choose the Multicast transport (to network multiple UMLs on multiple hosts), Ethertap (to network with the host and the outside world), and/or the Daemon transport (to network multiple UMLs on a single host). You may choose more than one without conflict. If you don't need UML networking, say N. +Default main console channel initialization +CONFIG_CON_ZERO_CHAN + This is the string describing the channel to which the main console + will be attached by default. This value can be overridden from the + command line. The default value is "fd:0,fd:1", which attaches the + main console to stdin and stdout. + It is safe to leave this unchanged. + +Default console channel initialization +CONFIG_CON_CHAN + This is the string describing the channel to which all consoles + except the main console will be attached by default. This value can + be overridden from the command line. The default value is "xterm", + which brings them up in xterms. + It is safe to leave this unchanged, although you may wish to change + this if you expect the UML that you build to be run in environments + which don't have X or xterm available. + +Default serial line channel initialization +CONFIG_SSL_CHAN + This is the string describing the channel to which the serial lines + will be attached by default. This value can be overridden from the + command line. The default value is "pty", which attaches them to + traditional pseudo-terminals. + It is safe to leave this unchanged, although you may wish to change + this if you expect the UML that you build to be run in environments + which don't have a set of /dev/pty* devices. + +UML sound support +CONFIG_UML_SOUND + This option enables UML sound support. If enabled, it will pull in + soundcore and the UML hostaudio relay, which acts as a intermediary + between the host's dsp and mixer devices and the UML sound system. + It is safe to say 'Y' here. + +UML SMP support +CONFIG_UML_SMP + This option enables UML SMP support. UML implements virtual SMP by + allowing as many processes to run simultaneously on the host as + there are virtual processors configured. Obviously, if the host is + a uniprocessor, those processes will timeshare, but, inside UML, + will appear to be running simultaneously. If the host is a + multiprocessor, then UML processes may run simultaneously, depending + on the host scheduler. + CONFIG_SMP will be set to whatever this option is set to. + It is safe to leave this unchanged. + +file descriptor channel support +CONFIG_FD_CHAN + This option enables support for attaching UML consoles and serial + lines to already set up file descriptors. Generally, the main + console is attached to file descriptors 0 and 1 (stdin and stdout), + so it would be wise to leave this enabled unless you intend to + attach it to some other host device. + +port channel support +CONFIG_PORT_CHAN + This option enables support for attaching UML consoles and serial + lines to host portals. They may be accessed with 'telnet + '. Any number of consoles and serial lines may be + attached to a single portal, although what UML device you get when + you telnet to that portal will be unpredictable. + It is safe to say 'Y' here. + +pty channel support +CONFIG_PTY_CHAN + This option enables support for attaching UML consoles and serial + lines to host pseudo-terminals. Access to both traditional + pseudo-terminals (/dev/pty*) and pts pseudo-terminals are controlled + with this option. The assignment of UML devices to host devices + will be announced in the kernel message log. + It is safe to say 'Y' here. + +tty channel support +CONFIG_TTY_CHAN + This option enables support for attaching UML consoles and serial + lines to host terminals. Access to both virtual consoles + (/dev/tty*) and the slave side of pseudo-terminals (/dev/ttyp* and + /dev/pts/*) are controlled by this option. + It is safe to say 'Y' here. + +xterm channel support +CONFIG_XTERM_CHAN + This option enables support for attaching UML consoles and serial + lines to xterms. Each UML device so assigned will be brought up in + its own xterm. + If you disable this option, then CONFIG_PT_PROXY will be disabled as + well, since UML's gdb currently requires an xterm. + It is safe to say 'Y' here. + Microtek USB scanner support CONFIG_USB_MICROTEK Say Y here if you want support for the Microtek X6USB and @@ -14665,10 +14885,12 @@ QNX4 file system support (read only) CONFIG_QNX4FS_FS - This is the file system used by the operating system QNX 4. Say Y if - you intend to mount QNX hard disks or floppies. Unless you say Y to - "QNX4FS read-write support" below, you will only be able to read - these file systems. + This is the file system used by the real-time operating systems + QNX 4 and QNX 6 (the latter is also called QNX RTP). + Further information is available at . + Say Y if you intend to mount QNX hard disks or floppies. + Unless you say Y to "QNX4FS read-write support" below, you will + only be able to read these file systems. This file system support is also available as a module ( = code which can be inserted in and removed from the running kernel @@ -14683,6 +14905,9 @@ CONFIG_QNX4FS_RW Say Y if you want to test write support for QNX4 file systems. + It's currently broken, so for now: + answer N. + Kernel automounter support CONFIG_AUTOFS_FS The automounter is a tool to automatically mount remote file systems @@ -21265,15 +21490,33 @@ this are available from . - If you are interested in writing a driver for such an audio/video - device or user software interacting with such a driver, please read - the file . + This kernel includes support for the new Video for Linux Two API, + (V4L2) as well as the original system. Drivers and applications + need to be rewritten to use V4L2, but drivers for popular cards + and applications for most video capture functions already exist. + + Documentation for the original API is included in the file + Documentation/video4linux/API.html. Documentation for V4L2 is + available on the web at http://www.thedirks.org/v4l2/ This driver is also available as a module called videodev.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 . +Backward compatibility for V4L2 drivers +CONFIG_VIDEO_V4LCOMPAT + Choose Y here to enable drivers written to the new V4L2 spec to + work with older applications written for the original V4L spec. + + This code is also available as module called v4l1-compat.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 M, and the module will be loaded automatically if it + is ever needed. + Video For Linux /proc file system information CONFIG_VIDEO_PROC_FS If you say Y here, you are able to access video device information @@ -24696,4 +24939,323 @@ # fill-prefix:" " # adaptive-fill:nil # fill-column:70 +I2C mainboard interfaces +CONFIG_I2C_MAINBOARD + Many modern mainboards have some kind of I2C interface integrated. This + is often in the form of a SMBus, or System Management Bus, which is + basically the same as I2C but which uses only a subset of the I2C + protocol. + + You will also want the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Acer Labs ALI 1535 +CONFIG_I2C_ALI1535 + If you say yes to this option, support will be included for the Acer + Labs ALI 1535 mainboard I2C interface. This can also be + built as a module. + +Acer Labs ALI 1533 and 1543C +CONFIG_I2C_ALI15X3 + If you say yes to this option, support will be included for the Acer + Labs ALI 1533 and 1543C mainboard I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +AMD 756/766 +CONFIG_I2C_AMD756 + If you say yes to this option, support will be included for the AMD + 756/766 mainboard I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Apple Hydra Mac I/O +CONFIG_I2C_HYDRA + If you say yes to this option, support will be included for the + Hydra mainboard I2C interface. This can also be built as a module + which can be inserted and removed while the kernel is running. + +Intel I801 +CONFIG_I2C_I801 + If you say yes to this option, support will be included for the + Intel I801 mainboard I2C interfaces. "I810" mainboard sensor chips are + generally located on the I801's I2C bus. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Intel I810/I815 +CONFIG_I2C_I810 + If you say yes to this option, support will be included for the + Intel I810/I815 mainboard I2C interfaces. The I2C busses these chips + are generally used only for video devices. For "810" mainboard sensor + chips, use the I801 I2C driver instead. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Intel 82371AB PIIX4(E) / ServerWorks OSB4 and CSB5 +CONFIG_I2C_PIIX4 + If you say yes to this option, support will be included for the + Intel PIIX4 and PIIX4E and Serverworks OSB4/CSB5 mainboard + I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Silicon Integrated Systems Corp. SiS5595 +CONFIG_I2C_SIS5595 + If you say yes to this option, support will be included for the + SiS5595 mainboard I2C interfaces. For integrated sensors on the + Sis5595, use CONFIG_SENSORS_SIS5595. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +VIA Technologies, Inc. VT82C586B +CONFIG_I2C_VIA + If you say yes to this option, support will be included for the VIA + Technologies I2C adapter found on some motherboards. This can also + be built as a module which can be inserted and removed while the + kernel is running. + +VIA Technologies, Inc. VT82C596, 596B, 686A/B +CONFIG_I2C_VIAPRO + If you say yes to this option, support will be included for the VIA + Technologies I2C adapter on these chips. For integrated sensors on the + Via 686A/B, use CONFIG_SENSORS_VIA686A. This can also be + be built as a module which can be inserted and removed while the + kernel is running. + +3DFX Banshee / Voodoo3 +CONFIG_I2C_VOODOO3 + If you say yes to this option, support will be included for the + 3DFX Banshee and Voodoo3 I2C interfaces. The I2C busses on the these + chips are generally used only for video devices. + This can also be + built as a module which can be inserted and removed while the kernel + is running. + +DEC Tsunami 21272 +CONFIG_I2C_TSUNAMI + If you say yes to this option, support will be included for the DEC + Tsunami chipset I2C adapter. Requires the Alpha architecture; + do not enable otherwise. This can also be built as a module which + can be inserted and removed while the kernel is running. + +Pseudo ISA adapter (for hardware sensors modules) +CONFIG_I2C_ISA + This provides support for accessing some hardware sensor chips over + the ISA bus rather than the I2C or SMBus. If you want to do this, + say yes here. This feature can also be built as a module which can + be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1021 and compatibles +CONFIG_SENSORS_ADM1021 + If you say yes here you get support for Analog Devices ADM1021 + and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, + Genesys Logic GL523SM, National Semi LM84, TI THMC10, + and the XEON processor built-in sensor. This can also + be built as a module which can be inserted and removed while the + kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1024 +CONFIG_SENSORS_ADM1024 + If you say yes here you get support for Analog Devices ADM1024 sensor + chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1025 +CONFIG_SENSORS_ADM1025 + If you say yes here you get support for Analog Devices ADM1025 sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM9240 and compatibles +CONFIG_SENSORS_ADM9240 + If you say yes here you get support for Analog Devices ADM9240 + sensor chips and clones: the Dallas Semiconductor DS1780 and + the National Semiconductor LM81. This can also be built as a + module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Dallas DS1621 and DS1625 +CONFIG_SENSORS_DS1621 + If you say yes here you get support for the Dallas DS1621 and DS1625x + sensor chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Fujitsu-Siemens Poseidon +CONFIG_SENSORS_FSCPOS + If you say yes here you get support for the Fujitsu-Siemens Poseidon + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Genesys Logic GL518SM +CONFIG_SENSORS_GL518SM + If you say yes here you get support for Genesys Logic GL518SM sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Genesys Logic GL520SM +CONFIG_SENSORS_GL520SM + If you say yes here you get support for Genesys Logic GL518SM sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +HP Maxilife +CONFIG_SENSORS_MAXILIFE + If you say yes here you get support for the HP Maxilife + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +ITE 8705, 8712, Sis950 +CONFIG_SENSORS_IT87 + If you say yes here you get support for the ITE 8705 and 8712 and + SiS950 sensor chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Myson MTP008 +CONFIG_SENSORS_MTP008 + If you say yes here you get support for the Myson MTP008 + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductor LM75 and compatibles +CONFIG_SENSORS_LM75 + If you say yes here you get support for National Semiconductor LM75 + sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon + TCN75, and National Semi LM77. This can also be built as a module which + can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductor LM78 +CONFIG_SENSORS_LM78 + If you say yes here you get support for National Semiconductor LM78 + sensor chips family: the LM78-J and LM79. Many clone chips will + also work at least somewhat with this driver. This can also be built + as a module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductor LM80 +CONFIG_SENSORS_LM80 + If you say yes here you get support for National Semiconductor LM80 + sensor chips. This can also be built as a module which can be + inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductor LM87 +CONFIG_SENSORS_LM87 + If you say yes here you get support for National Semiconductor LM87 + sensor chips. This can also be built as a module which can be + inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Silicon Integrated Systems Corp. SiS5595 +CONFIG_SENSORS_SIS5595 + If you say yes here you get support for the integrated sensors in + SiS5595 South Bridges. This can also be built as a module + which can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Texas Instruments THMC50 / Analog Devices ADM1022 +CONFIG_SENSORS_THMC50 + If you say yes here you get support for Texas Instruments THMC50 + sensor chips and clones: the Analog Devices ADM1022. + This can also be built as a module which + can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Via VT82C686A/B +CONFIG_SENSORS_VIA686A + If you say yes here you get support for the integrated sensors in + Via 686A/B South Bridges. This can also be built as a module + which can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Winbond W83781D, W83782D, W83783S, W83627HF, AS99127F +CONFIG_SENSORS_W83781D + If you say yes here you get support for the Winbond W8378x series + of sensor chips: the W83781D, W83782D, W83783S and W83682HF, + and the similar Asus AS99127F. This + can also be built as a module which can be inserted and removed + while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +EEprom (DIMM) reader +CONFIG_SENSORS_EEPROM + If you say yes here you get read-only access to the EEPROM data + available on modern memory DIMMs, and which could theoretically + also be available on other devices. This can also be built as a + module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + # End: diff -Nru a/Documentation/fb/matroxfb.txt b/Documentation/fb/matroxfb.txt --- a/Documentation/fb/matroxfb.txt Wed Feb 13 20:03:41 2002 +++ b/Documentation/fb/matroxfb.txt Wed Feb 13 20:03:42 2002 @@ -10,7 +10,7 @@ * It provides a nice large console (128 cols + 48 lines with 1024x768) without using tiny, unreadable fonts. - * You can run XF68_FBDev on top of /dev/fb0 + * You can run XF{68,86}_FBDev or XFree86 fbdev driver on top of /dev/fb0 * Most important: boot logo :-) Disadvantages: @@ -27,9 +27,6 @@ If you want, for example, enable a resolution of 1280x1024x24bpp you should pass to the kernel this command line: "video=matrox:vesa:0x1BB". -Note that the same line, if 'appended' as a lilo parameter in lilo.conf will -read "video=matrox:vesa:443" because lilo pass integer parameters as decimal -numbers to the kernel. You should compile in both vgacon (to boot if you remove you Matrox from box) and matroxfb (for graphics mode). You should not compile-in vesafb @@ -82,13 +79,16 @@ X11 === -XF68_FBDev should work just fine, but it is non-accelerated. On non-intel +XF{68,86}_FBDev should work just fine, but it is non-accelerated. On non-intel architectures there are some glitches for 24bpp videomodes. 8, 16 and 32bpp works fine. Running another (accelerated) X-Server like XF86_SVGA works too. But (at least) XFree servers have big troubles in multihead configurations (even on first -head, not even talking about second). +head, not even talking about second). Running XFree86 4.x accelerated mga +driver is possible, but you must not enable DRI - if you do, resolution and +color depth of your X desktop must match resolution and color depths of your +virtual consoles, otherwise X will corrupt accelerator settings. SVGALib diff -Nru a/Documentation/preempt-locking.txt b/Documentation/preempt-locking.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/preempt-locking.txt Wed Feb 13 20:04:01 2002 @@ -0,0 +1,104 @@ + Proper Locking Under a Preemptible Kernel: + Keeping Kernel Code Preempt-Safe + Robert Love + Last Updated: 22 Jan 2002 + + +INTRODUCTION + + +A preemptible kernel creates new locking issues. The issues are the same as +those under SMP: concurrency and reentrancy. Thankfully, the Linux preemptible +kernel model leverages existing SMP locking mechanisms. Thus, the kernel +requires explicit additional locking for very few additional situations. + +This document is for all kernel hackers. Developing code in the kernel +requires protecting these situations. + + +RULE #1: Per-CPU data structures need explicit protection + + +Two similar problems arise. An example code snippet: + + struct this_needs_locking tux[NR_CPUS]; + tux[smp_processor_id()] = some_value; + /* task is preempted here... */ + something = tux[smp_processor_id()]; + +First, since the data is per-CPU, it may not have explicit SMP locking, but +require it otherwise. Second, when a preempted task is finally rescheduled, +the previous value of smp_processor_id may not equal the current. You must +protect these situations by disabling preemption around them. + + +RULE #2: CPU state must be protected. + + +Under preemption, the state of the CPU must be protected. This is arch- +dependent, but includes CPU structures and state not preserved over a context +switch. For example, on x86, entering and exiting FPU mode is now a critical +section that must occur while preemption is disabled. Think what would happen +if the kernel is executing a floating-point instruction and is then preempted. +Remember, the kernel does not save FPU state except for user tasks. Therefore, +upon preemption, the FPU registers will be sold to the lowest bidder. Thus, +preemption must be disabled around such regions. + +Note, some FPU functions are already explicitly preempt safe. For example, +kernel_fpu_begin and kernel_fpu_end will disable and enable preemption. +However, math_state_restore must be called with preemption disabled. + + +RULE #3: Lock acquire and release must be performed by same task + + +A lock acquired in one task must be released by the same task. This +means you can't do oddball things like acquire a lock and go off to +play while another task releases it. If you want to do something +like this, acquire and release the task in the same code path and +have the caller wait on an event by the other task. + + +SOLUTION + + +Data protection under preemption is achieved by disabling preemption for the +duration of the critical region. + +preempt_enable() decrement the preempt counter +preempt_disable() increment the preempt counter +preempt_enable_no_resched() decrement, but do not immediately preempt +preempt_get_count() return the preempt counter + +The functions are nestable. In other words, you can call preempt_disable +n-times in a code path, and preemption will not be reenabled until the n-th +call to preempt_enable. The preempt statements define to nothing if +preemption is not enabled. + +Note that you do not need to explicitly prevent preemption if you are holding +any locks or interrupts are disabled, since preemption is implicitly disabled +in those cases. + +Example: + + cpucache_t *cc; /* this is per-CPU */ + preempt_disable(); + cc = cc_data(searchp); + if (cc && cc->avail) { + __free_block(searchp, cc_entry(cc), cc->avail); + cc->avail = 0; + } + preempt_enable(); + return 0; + +Notice how the preemption statements must encompass every reference of the +critical variables. Another example: + + int buf[NR_CPUS]; + set_cpu_val(buf); + if (buf[smp_processor_id()] == -1) printf(KERN_INFO "wee!\n"); + spin_lock(&buf_lock); + /* ... */ + +This code is not preempt-safe, but see how easily we can fix it by simply +moving the spin_lock up two lines. diff -Nru a/Documentation/scsi-changer.txt b/Documentation/scsi-changer.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/scsi-changer.txt Wed Feb 13 20:03:57 2002 @@ -0,0 +1,109 @@ +README for the SCSI media changer driver +======================================== + +This is a driver for SCSI Medium Changer devices, which are listed +with "Type: Medium Changer" in /proc/scsi/scsi. + +This is for *real* Jukeboxes. It is *not* supported to work with +common small CD-ROM changers, neither one-lun-per-slot SCSI changers +nor IDE drives. + +Userland tools available from: http://bytesex.org/changer.html + + +General Information +------------------- + +First some words about how changers work: A changer has 2 (possibly +more) SCSI ID's. One for the changer device which controls the robot, +and one for the device which actually reads and writes the data. The +later may be anything, a MOD, a CD-ROM, a tape or whatever. For the +changer device this is a "don't care", he *only* shuffles around the +media, nothing else. + + +The SCSI changer model is complex, compared to - for example - IDE-CD +changers. But it allows to handle nearly all possible cases. It knows +4 different types of changer elements: + + media transport - this one shuffles around the media, i.e. the + transport arm + storage - a slot which can hold a media. + import/export - the same as above, but is accessable from outside, + i.e. there the operator (you !) can use this to + fill in and remove media from the changer. + data transfer - this is the device which reads/writes. + +None of these is limited to one: A huge Jukebox could have slots for +123 CD-ROM's, 5 CD-ROM readers (and therefore 6 SCSI ID's: the changer +and each CD-ROM) and 2 transport arms. No problem to handle. + + +How it is implemented +--------------------- + +I implemented the driver as character device driver with a NetBSD-like +ioctl interface. Just grabbed NetBSD's header file and one of the +other linux SCSI device drivers as starting point. The interface +should be source code compatible with NetBSD. So if there is any +software (anybody knows ???) which supports a BSDish changer driver, +it should work with this driver too. + + +Current State +------------- + +Support for more than one transport arm is not implemented yet (and +mobody asked for it so far...). + +I got some reports telling it works ok with tape autoloaders (Exabyte, +HP and DEC). Some People use this driver with amanda. It works fine +with small (11 slots) and a huge (4 MOs, 88 slots) magneto-optical +Jukebox too. Probably with other changers too, most (but not all :-) +people mail me only if it does *not* work... + + +Using it +-------- + +This is a character device with major number is 86, so use +"mknod /dev/sch0 c 86 0" to create the special file for the driver. + +If the module finds the changer, it prints some messages about the +device [ try "dmesg" if you don't see anything ] and should show up in +/proc/devices. If not.... some changers use ID ? / LUN 0 for the +device and ID ? / LUN 1 for the robot mechanism. But Linux does *not* +look for LUN's other than 0 as default, becauce there are to many +broken devices. So you can try: + + 1) echo "scsi add-single-device 0 0 ID 1" > /proc/scsi/scsi + (replace ID with the SCSI-ID of the device) + 2) boot the kernel with "max_scsi_luns=1" on the command line + (append="max_scsi_luns=1" in lilo.conf should do the trick) + +Trouble? +-------- + +If you insmod the driver with "insmod debug=1", it will be verbose and +prints a lot of stuff to the syslog. You can display these messages with +the dmesg command (or check the logfiles). If you email me some question +becauce of a problem with the driver, please include these messages. + + +Credits +------- + +I wrote this driver using the famous mailing-patches-around-the-world +method. With (more or less) help from: + + Daniel Moehwald + Dane Jasper + R. Scott Bailey + Jonathan Corbet + +Have fun, + + Gerd + +-- +Gerd Knorr diff -Nru a/Documentation/video4linux/bttv/CARDLIST b/Documentation/video4linux/bttv/CARDLIST --- a/Documentation/video4linux/bttv/CARDLIST Wed Feb 13 20:03:35 2002 +++ b/Documentation/video4linux/bttv/CARDLIST Wed Feb 13 20:03:35 2002 @@ -7,13 +7,13 @@ card=5 - Diamond DTV2000 card=6 - AVerMedia TVPhone card=7 - MATRIX-Vision MV-Delta - card=8 - Fly Video II (Bt848) - card=9 - TurboTV + card=8 - FlyVideo II (Bt848) LR26 + card=9 - IXMicro TurboTV card=10 - Hauppauge (bt878) card=11 - MIRO PCTV pro card=12 - ADS Technologies Channel Surfer TV card=13 - AVerMedia TVCapture 98 - card=14 - Aimslab VHX + card=14 - Aimslab Video Highway Xtreme (VHX) card=15 - Zoltrix TV-Max card=16 - Pixelview PlayTV (bt878) card=17 - Leadtek WinView 601 @@ -21,7 +21,7 @@ card=19 - LifeView FlyKit w/o Tuner card=20 - CEI Raffles Card card=21 - Lucky Star Image World ConferenceTV - card=22 - Phoebe Tv Master + FM + card=22 - Phoebe Tv Master + FM (CPH050) card=23 - Modular Technology MM205 PCTV, bt878 card=24 - [many vendors] CPH05X/06X (bt878) card=25 - Terratec/Vobis TV-Boostar @@ -54,12 +54,12 @@ card=52 - Pinnacle PCTV Studio Pro card=53 - Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS card=54 - Lifetec LT 9415 TV (LR90 Rev.F) - card=55 - BESTBUY Easy TV + card=55 - BESTBUY Easy TV (CPH031) card=56 - FlyVideo '98/FM card=57 - GrandTec 'Grand Video Capture' - card=58 - Phoebe TV Master Only (No FM) - card=59 - TV Capturer - card=60 - MM100PCTV + card=58 - Phoebe TV Master Only (No FM) CPH060 + card=59 - TV Capturer (CPH03X) + card=60 - Modular Technology MM100PCTV card=61 - AG Electronics GMV1 card=62 - BESTBUY Easy TV (bt878) card=63 - ATI TV-Wonder @@ -72,6 +72,10 @@ card=70 - PV-BT878P+ card=71 - Flyvideo 98EZ (capture only) card=72 - Prolink PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM) + card=73 - Sensoray 311 + card=74 - RemoteVision MX (RV605) + card=75 - Powercolor MTV878/ MTV878R/ MTV878F + card=76 - Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP) tuner.o type=0 - Temic PAL (4002 FH5) @@ -94,7 +98,7 @@ type=17 - Philips NTSC_M (MK2) type=18 - Temic PAL_I (4066 FY5) type=19 - Temic PAL* auto (4006 FN5) - type=20 - Temic PAL (4009 FR5) + type=20 - Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5) type=21 - Temic NTSC (4039 FR5) type=22 - Temic PAL/SECAM multi (4046 FM5) type=23 - Philips PAL_DK diff -Nru a/Documentation/video4linux/bttv/Cards b/Documentation/video4linux/bttv/Cards --- a/Documentation/video4linux/bttv/Cards Wed Feb 13 20:03:39 2002 +++ b/Documentation/video4linux/bttv/Cards Wed Feb 13 20:03:39 2002 @@ -15,6 +15,10 @@ All other cards only differ by additional components as tuners, sound decoders, EEPROMs, teletext decoders ... +Unsupported Cards: +------------------ +Cards with Zoran (ZR) or Philips (SAA) are not supported by this driver. + MATRIX Vision ------------- @@ -138,19 +142,25 @@ 1851:1851 = Flyvideo 98 EZ (capture only) 2) There is a print on the PCB: LR25 = Flyvideo (Zoran) - LR37 Rev.C = Capture only (ZR36120 + SAA7110) + LR26 Rev.N = Flyvideo II (Bt848) + Rev.O = Flyvideo II (Bt878) + LR37 Rev.C = Flyvideo EZ (Capture only, ZR36120 + SAA7110) + LR38 Rev.A1= Flyvideo II EZ (Bt848 capture only) LR50 Rev.Q = Flyvideo 98 (w/eeprom and PCI subsystem ID) - LR50 Rev.W = Flyvideo 98 (no eeprom) + Rev.W = Flyvideo 98 (no eeprom) LR51 Rev.E = Flyvideo 98 EZ (capture only) LR90 = Flyvideo 2000 series - LR90 Rev.F = Lifetec/Medion LT 9815 + LR91 = Stereo daughter card for LR90 LR97 = Flyvideo DVBS + LR138 Rev.C= Flyvideo 3000 (SAA7130/SAA7134) "Flyvideo II" had been the name for the 848 cards, nowadays (in Germany) this name is re-used for LR50 Rev.W. The Lifeview website has even more names: Flyvideo III,2100,3000,3100. These cards are sold by many OEMs too. + FlyVideo A2 = LR90 Rev.F (w/Remote, w/o FM, stereo TV by tda9821) + Typhoon TV card series: ----------------------- @@ -176,6 +186,7 @@ Guillemot --------- + Maxi-TV PCI (ZR36120) Maxi TV Video 2 = LR50 Rev.Q (FI1216MF, PAL BG+SECAM) Maxi TV Video 3 = CPH064 (PAL BG + SECAM) @@ -208,3 +219,125 @@ Genius/Kye ---------- Video Wonder/Genius Internet Video Kit = LR37 Rev.C + Video Wonder Pro II (848 or 878) = LR26 + +Tekram +------ + VideoCap C205 (Bt848) + VideoCap C210 (Zoran ZR36120 +Philips) + CaptureTV M200 (ISA) + CaptureTV M205 (Bt848) + +Lucky Star +---------- + Image World Conference TV = LR50 Rev. Q + +Leadtek +------- + WinView 601 (Bt848) + WinView 610 (Zoran) + WinFast2000 + +KNC One +------- + TV-Station + TV-Station SE (+Software Bundle) + TV-Station pro (+TV stereo) + TV-Station FM (+Radio) + TV-Station RDS (+RDS) + +PV951 cards: +------------ + These are sold as: + Boeder TV-FM Video Capture Card + Titanmedia Supervision TV-2400 + Provideo PV951 TF + 3DeMon PV951 + MediaForte TV-Vision PV951 + Yoko PV951 + +Highscreen +---------- + TV Karte = LR50 Rev.S + TV-Boostar = Terratec Terra TV+ Version 1.0 (Bt848, TDA9821) + +Zoltrix +------- + Face To Face TV MAX (Bt848) + +AVerMedia +--------- + AVer FunTV Lite (ISA, AV3001 chipset) "M101.C" + AVerTV + AVerTV Stereo + AVerTV Studio (w/FM) + AVerMedia TV98 with Remote + AVerMedia TV/FM98 Stereo + AVerMedia TVCAM98 + TVCapture (Bt848) + TVPhone (Bt848) + TVCapture98 (="AVerMedia TV98" in USA) (Bt878) + TVPhone98 (Bt878, w/FM) + + PCB PCI-ID Model-Name Eeprom Tuner Sound Country + -------------------------------------------------------------------- + M1A8-A -- AVer TV-Phone FM1216 -- + M168-T 1461:0003 AVerTV Studio 48:17 FM1216 TDA9840T D (1) w/FM w/Remote + M168-U 1461:0004 TVCapture98 40:11 FI1216 -- D w/Remote + M168II-B 1461:0003 Medion MD9592 48:16 FM1216 TDA9873H D w/FM + + (1) Daughterboard MB68-A with TDA9820T and TDA9840T + +Aimslab +------- + Video Highway Xtreme (aka "VHX") (Bt848, FM w/ TEA5757) + +IXMicro +------- + IXTV BT848 + IXTV BT878 + TurboTV (Bt848) + +Lifetec/Medion/Tevion/Aldi +------- + LT9415/MD9415 = LR90 Rev. F + MD9592 = Avermedia TVphone98 (PCI_ID=1461:0003), PCB-Rev=M168II-B (w/TDA9873H) + +Modular Technologies (www.modulartech.com) UK +-------------------- + MM100 PCTV (Bt848) + MM205 PCTV (Bt878) + MM210 PCTV (Bt878) (Galaxy TV) + +Terratec +-------- + Terra TV+ Version 1.0 (Bt848), "ceb105.PCB" printed on the PCB, TDA9821 + Terra TV+ Version 1.1 (Bt878), "LR74 Rev.E" printed on the PCB, TDA9821 + Terra TValueRadio, "LR102 Rev.C" printed on the PCB + Terra TV/Radio+ Version 1.0, "80-CP2830100-0" TTTV3 printed on the PCB, i + "CPH010-E83" on the back, SAA6588T, TDA9873H + Terra TValue Version BT878, "80-CP2830110-0 TTTV4" printed on the PCB, + "CPH011-D83" on back + Terra TValue Version 1.0 "ceb105.PCB" (really identical to Terra TV+ Version 1.0) + + LR74 is a newer PCB revision of ceb105 (both incl. connector for Active Radio Upgrade) + + +Technisat +--------- + Mediafocus I (ZR36120/ZR36125) + Mediafocus II (SAA7146) + +Siemens +------- + Multimedia eXtension Board (MXB) (SAA7146, SAA7111) + +Stradis +------- + SDM275,SDM250,SDM026,SDM025 (SAA7146, IBMMPEG2): MPEG2 decoder only + +Powercolor +---------- + MTV878 + MTV878R w/Remote Control + MTV878F w/Remote Control w/FM radio diff -Nru a/Documentation/video4linux/bttv/Insmod-options b/Documentation/video4linux/bttv/Insmod-options --- a/Documentation/video4linux/bttv/Insmod-options Wed Feb 13 20:03:35 2002 +++ b/Documentation/video4linux/bttv/Insmod-options Wed Feb 13 20:03:35 2002 @@ -89,6 +89,8 @@ insmod args for tda9874a: tda9874a_SIF=1/2 select sound IF input pin (1 or 2) (default is pin 1) + tda9874a_AMSEL=0/1 auto-mute select for NICAM (default=0) + Please read note 3 below! tda9874a_STD=n select TV sound standard (0..8): 0 - A2, B/G 1 - A2, M (Korea) @@ -100,11 +102,20 @@ 7 - NICAM, D/K (default) 8 - NICAM, L - Note: tda9874a is very similar to tda9874 (without 'A'-suffix), but - this driver will not work for the latter device (will not load). - Note: tda9874a and tda9875 (which is supported separately by + Note 1: tda9874a supports both tda9874h (old) and tda9874a (new) chips. + Note 2: tda9874h/a and tda9875 (which is supported separately by tda9875.o) use the same i2c address so both modules should not be used at the same time. + Note 3: Using tda9874a_AMSEL option depends on your TV card design! + AMSEL=0: auto-mute will switch between NICAM sound + and the sound on 1st carrier (i.e. FM mono or AM). + AMSEL=1: auto-mute will switch between NICAM sound + and the analog mono input (MONOIN pin). + If tda9874a decoder on your card has MONOIN pin not connected, then + use only tda9874_AMSEL=0 or don't specify this option at all. + For example: + card=65 (FlyVideo 2000S) - set AMSEL=1 or AMSEL=0 + card=72 (Prolink PV-BT878P rev.9B) - set AMSEL=0 only msp3400.o The driver for the msp34xx sound processor chips. If you have a diff -Nru a/Documentation/video4linux/bttv/Sound-FAQ b/Documentation/video4linux/bttv/Sound-FAQ --- a/Documentation/video4linux/bttv/Sound-FAQ Wed Feb 13 20:03:55 2002 +++ b/Documentation/video4linux/bttv/Sound-FAQ Wed Feb 13 20:03:55 2002 @@ -62,7 +62,7 @@ { [ ... ] u32 gpiomask; - u32 audiomux[5]; /* audio mux: tuner, radio, external, internal, mute */ + u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */ }; gpiomask specifies which pins are used to control the audio mux chip. @@ -127,6 +127,9 @@ *_modulename - hint whenever some card needs this or that audio module loaded to work properly. has_radio - whenever this TV card has a radio tuner. +no_msp34xx - "1" disables loading of msp3400.o module +no_tda9875 - "1" disables loading of tda9875.o module +needs_tvaudio - set to "1" to load tvaudio.o module If some config item is specified both from the tvcards array and as insmod option, the insmod option takes precedence. diff -Nru a/Documentation/vm/Changelog.rmap b/Documentation/vm/Changelog.rmap --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/vm/Changelog.rmap Wed Feb 13 20:03:56 2002 @@ -0,0 +1,118 @@ +The first maintenance release of the 12th version of the reverse +mapping based VM is now available. +This is an attempt at making a more robust and flexible VM +subsystem, while cleaning up a lot of code at the same time. +The patch is available from: + + http://surriel.com/patches/2.4/2.4.17-rmap-12a +and http://linuxvm.bkbits.net/ + + +My big TODO items for a next release are: + - RSS ulimit enforcement + - auto-tuning readahead, readahead per VMA + +rmap 12a: + - fix the compile warning in buffer.c (me) + - fix divide-by-zero on highmem initialisation DOH! (me) + - remove the pgd quicklist (suspicious ...) (DaveM, me) +rmap 12: + - keep some extra free memory on large machines (Arjan van de Ven, me) + - higher-order allocation bugfix (Adrian Drzewiecki) + - nr_free_buffer_pages() returns inactive + free mem (me) + - pages from unused objects directly to inactive_clean (me) + - use fast pte quicklists on non-pae machines (Andrea Arcangeli) + - remove sleep_on from wakeup_kswapd (Arjan van de Ven) + - page waitqueue cleanup (Christoph Hellwig) +rmap 11c: + - oom_kill race locking fix (Andres Salomon) + - elevator improvement (Andrew Morton) + - dirty buffer writeout speedup (hopefully ;)) (me) + - small documentation updates (me) + - page_launder() never does synchronous IO, kswapd + and the processes calling it sleep on higher level (me) + - deadlock fix in touch_page() (me) +rmap 11b: + - added low latency reschedule points in vmscan.c (me) + - make i810_dma.c include mm_inline.h too (William Lee Irwin) + - wake up kswapd sleeper tasks on OOM kill so the + killed task can continue on its way out (me) + - tune page allocation sleep point a little (me) +rmap 11a: + - don't let refill_inactive() progress count for OOM (me) + - after an OOM kill, wait 5 seconds for the next kill (me) + - agpgart_be fix for hashed waitqueues (William Lee Irwin) +rmap 11: + - fix stupid logic inversion bug in wakeup_kswapd() (Andrew Morton) + - fix it again in the morning (me) + - add #ifdef BROKEN_PPC_PTE_ALLOC_ONE to rmap.h, it + seems PPC calls pte_alloc() before mem_map[] init (me) + - disable the debugging code in rmap.c ... the code + is working and people are running benchmarks (me) + - let the slab cache shrink functions return a value + to help prevent early OOM killing (Ed Tomlinson) + - also, don't call the OOM code if we have enough + free pages (me) + - move the call to lru_cache_del into __free_pages_ok (Ben LaHaise) + - replace the per-page waitqueue with a hashed + waitqueue, reduces size of struct page from 64 + bytes to 52 bytes (48 bytes on non-highmem machines) (William Lee Irwin) +rmap 10: + - fix the livelock for real (yeah right), turned out + to be a stupid bug in page_launder_zone() (me) + - to make sure the VM subsystem doesn't monopolise + the CPU, let kswapd and some apps sleep a bit under + heavy stress situations (me) + - let __GFP_HIGH allocations dig a little bit deeper + into the free page pool, the SCSI layer seems fragile (me) +rmap 9: + - improve comments all over the place (Michael Cohen) + - don't panic if page_remove_rmap() cannot find the + rmap in question, it's possible that the memory was + PG_reserved and belonging to a driver, but the driver + exited and cleared the PG_reserved bit (me) + - fix the VM livelock by replacing > by >= in a few + critical places in the pageout code (me) + - treat the reclaiming of an inactive_clean page like + allocating a new page, calling try_to_free_pages() + and/or fixup_freespace() if required (me) + - when low on memory, don't make things worse by + doing swapin_readahead (me) +rmap 8: + - add ANY_ZONE to the balancing functions to improve + kswapd's balancing a bit (me) + - regularize some of the maximum loop bounds in + vmscan.c for cosmetic purposes (William Lee Irwin) + - move page_address() to architecture-independent + code, now the removal of page->virtual is portable (William Lee Irwin) + - speed up free_area_init_core() by doing a single + pass over the pages and not using atomic ops (William Lee Irwin) + - documented the buddy allocator in page_alloc.c (William Lee Irwin) +rmap 7: + - clean up and document vmscan.c (me) + - reduce size of page struct, part one (William Lee Irwin) + - add rmap.h for other archs (untested, not for ARM) (me) +rmap 6: + - make the active and inactive_dirty list per zone, + this is finally possible because we can free pages + based on their physical address (William Lee Irwin) + - cleaned up William's code a bit (me) + - turn some defines into inlines and move those to + mm_inline.h (the includes are a mess ...) (me) + - improve the VM balancing a bit (me) + - add back inactive_target to /proc/meminfo (me) +rmap 5: + - fixed recursive buglet, introduced by directly + editing the patch for making rmap 4 ;))) (me) +rmap 4: + - look at the referenced bits in page tables (me) +rmap 3: + - forgot one FASTCALL definition (me) +rmap 2: + - teach try_to_unmap_one() about mremap() (me) + - don't assign swap space to pages with buffers (me) + - make the rmap.c functions FASTCALL / inline (me) +rmap 1: + - fix the swap leak in rmap 0 (Dave McCracken) +rmap 0: + - port of reverse mapping VM to 2.4.16 (me) diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Wed Feb 13 20:03:31 2002 +++ b/MAINTAINERS Wed Feb 13 20:03:31 2002 @@ -145,6 +145,14 @@ W: http://www.ibm.com/linux/ltc/ S: Supported +AACRAID SCSI RAID DRIVER +P: Adaptec OEM Raid Solutions +M: linux-aacraid-devel@dell.com +L: linux-aacraid-devel@dell.com +L: linux-aacraid-announce@dell.com +W: http://domsch.com/linux +S: Supported + ACPI P: Andy Grover M: andrew.grover@intel.com @@ -706,12 +714,12 @@ IDE DRIVER [GENERAL] P: Andre Hedrick M: andre@linux-ide.org -M: andre@aslab.com -M: andre@suse.com +M: andre@linuxdiskcert.org L: linux-kernel@vger.kernel.org W: http://www.kernel.org/pub/linux/kernel/people/hedrick/ W: http://www.linux-ide.org/ -S: Supported +W: http://www.linuxdiskcert.org/ +S: Maintained IDE/ATAPI CDROM DRIVER P: Jens Axboe @@ -1248,6 +1256,14 @@ M: mostrows@styx.uwaterloo.ca S: Maintained +PREEMPTIBLE KERNEL +P: Robert M. Love +M: rml@tech9.net +L: linux-kernel@vger.kernel.org +L: kpreempt-tech@lists.sourceforge.net +W: http://tech9.net/rml/linux +S: Supported + PROMISE DC4030 CACHING DISK CONTROLLER DRIVER P: Peter Denison M: promise@pnd-pc.demon.co.uk @@ -1355,6 +1371,15 @@ L: linux-scsi@vger.kernel.org S: Maintained +SENSORS DRIVERS +P: Frodo Looijaard +M: frodol@dds.nl +P: Philip Edelbrock +M: phil@netroedge.com +L: sensors@stimpy.netroedge.com +W: http://www.lm-sensors.nu/ +S: Maintained + SGI VISUAL WORKSTATION 320 AND 540 P: Bent Hagemark M: bh@sgi.com @@ -1705,6 +1730,14 @@ L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net W: http://usb.in.tum.de +S: Maintained + +USER-MODE PORT +P: Jeff Dike +M: jdike@karaya.com +L: user-mode-linux-devel@lists.sourceforge.net +L: user-mode-linux-user@lists.sourceforge.net +W: http://user-mode-linux.sourceforge.net S: Maintained VFAT FILESYSTEM: diff -Nru a/Makefile b/Makefile --- a/Makefile Wed Feb 13 20:03:35 2002 +++ b/Makefile Wed Feb 13 20:03:35 2002 @@ -1,11 +1,19 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 18 -EXTRAVERSION = -pre9 +EXTRAVERSION = -pre9-mjc2 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) -ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +# SUBARCH tells the usermode build what the underlying arch is. That is set +# first, and if a usermode build is happening, the "ARCH=um" on the command +# line overrides the setting of ARCH below. If a native build is happening, +# then ARCH is assigned, getting whatever value it gets normally, and +# SUBARCH is subsequently ignored. + +SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +ARCH := $(SUBARCH) + KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g") CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ @@ -170,7 +178,7 @@ DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a DRIVERS-$(CONFIG_ALL_PPC) += drivers/macintosh/macintosh.o DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o -DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o +DRIVERS-$(CONFIG_PNP) += 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 @@ -186,6 +194,7 @@ DRIVERS-$(CONFIG_BLUEZ) += drivers/bluetooth/bluetooth.o DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o +DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensor.o DRIVERS := $(DRIVERS-y) diff -Nru a/README b/README --- a/README Wed Feb 13 20:03:46 2002 +++ b/README Wed Feb 13 20:03:46 2002 @@ -150,13 +150,12 @@ COMPILING the kernel: - - Make sure you have gcc-2.91.66 (egcs-1.1.2) available. gcc 2.95.2 may + - Make sure you have gcc 2.95.3 available. gcc 2.91.66 (egcs-1.1.2) may also work but is not as safe, and *gcc 2.7.2.3 is no longer supported*. Also remember to upgrade your binutils package (for as/ld/nm and company) if necessary. For more information, refer to ./Documentation/Changes. - Please note that you can still run a.out user programs with this - kernel. + Please note that you can still run a.out user programs with this kernel. - Do a "make bzImage" to create a compressed kernel image. If you want to make a boot disk (without root filesystem or LILO), insert a floppy diff -Nru a/arch/arm/config.in b/arch/arm/config.in --- a/arch/arm/config.in Wed Feb 13 20:03:38 2002 +++ b/arch/arm/config.in Wed Feb 13 20:03:39 2002 @@ -508,6 +508,7 @@ if [ "$CONFIG_ISDN" != "n" ]; then source drivers/isdn/Config.in fi +dep_bool 'Preemptible Kernel' CONFIG_PREEMPT $CONFIG_CPU_32 endmenu # diff -Nru a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S --- a/arch/arm/kernel/entry-armv.S Wed Feb 13 20:03:44 2002 +++ b/arch/arm/kernel/entry-armv.S Wed Feb 13 20:03:44 2002 @@ -672,6 +672,12 @@ add r4, sp, #S_SP mov r6, lr stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro +#ifdef CONFIG_PREEMPT + get_current_task r9 + ldr r8, [r9, #TSK_PREEMPT] + add r8, r8, #1 + str r8, [r9, #TSK_PREEMPT] +#endif 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp @ @@ -679,6 +685,25 @@ @ adrsvc ne, lr, 1b bne do_IRQ +#ifdef CONFIG_PREEMPT +2: ldr r8, [r9, #TSK_PREEMPT] + subs r8, r8, #1 + bne 3f + ldr r7, [r9, #TSK_NEED_RESCHED] + teq r7, #0 + beq 3f + ldr r6, .LCirqstat + ldr r0, [r6, #IRQSTAT_BH_COUNT] + teq r0, #0 + bne 3f + mov r0, #MODE_SVC + msr cpsr_c, r0 @ enable interrupts + bl SYMBOL_NAME(preempt_schedule) + mov r0, #I_BIT | MODE_SVC + msr cpsr_c, r0 @ disable interrupts + b 2b +3: str r8, [r9, #TSK_PREEMPT] +#endif ldr r0, [sp, #S_PSR] @ irqs are already disabled msr spsr, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr @@ -736,6 +761,9 @@ .LCprocfns: .word SYMBOL_NAME(processor) #endif .LCfp: .word SYMBOL_NAME(fp_enter) +#ifdef CONFIG_PREEMPT +.LCirqstat: .word SYMBOL_NAME(irq_stat) +#endif irq_prio_table @@ -775,6 +803,12 @@ stmdb r8, {sp, lr}^ alignment_trap r4, r7, __temp_irq zero_fp + get_current_task tsk +#ifdef CONFIG_PREEMPT + ldr r0, [tsk, #TSK_PREEMPT] + add r0, r0, #1 + str r0, [tsk, #TSK_PREEMPT] +#endif 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp adrsvc ne, lr, 1b @@ -782,8 +816,12 @@ @ routine called with r0 = irq number, r1 = struct pt_regs * @ bne do_IRQ +#ifdef CONFIG_PREEMPT + ldr r0, [tsk, #TSK_PREEMPT] + sub r0, r0, #1 + str r0, [tsk, #TSK_PREEMPT] +#endif mov why, #0 - get_current_task tsk b ret_to_user .align 5 diff -Nru a/arch/arm/tools/getconstants.c b/arch/arm/tools/getconstants.c --- a/arch/arm/tools/getconstants.c Wed Feb 13 20:03:42 2002 +++ b/arch/arm/tools/getconstants.c Wed Feb 13 20:03:42 2002 @@ -13,6 +13,7 @@ #include #include +#include /* * Make sure that the compiler and target are compatible. @@ -38,6 +39,11 @@ DEFN("TSS_SAVE", OFF_TSK(thread.save)); DEFN("TSS_FPESAVE", OFF_TSK(thread.fpstate.soft.save)); + +#ifdef CONFIG_PREEMPT +DEFN("TSK_PREEMPT", OFF_TSK(preempt_count)); +DEFN("IRQSTAT_BH_COUNT", (unsigned long)&(((irq_cpustat_t *)0)->__local_bh_count)); +#endif #ifdef CONFIG_CPU_32 DEFN("TSS_DOMAIN", OFF_TSK(thread.domain)); diff -Nru a/arch/i386/config.in b/arch/i386/config.in --- a/arch/i386/config.in Wed Feb 13 20:03:45 2002 +++ b/arch/i386/config.in Wed Feb 13 20:03:45 2002 @@ -185,6 +185,10 @@ bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Preemptible Kernel' CONFIG_PREEMPT +if [ "$CONFIG_PREEMPT" = "y" ]; then + bool 'Break selected locks' CONFIG_LOCK_BREAK +fi if [ "$CONFIG_SMP" != "y" ]; then bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC @@ -198,9 +202,12 @@ bool 'Multiquad NUMA system' CONFIG_MULTIQUAD fi -if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then - define_bool CONFIG_HAVE_DEC_LOCK y +if [ "$CONFIG_SMP" = "y" -o "$CONFIG_PREEMPT" = "y" ]; then + if [ "$CONFIG_X86_CMPXCHG" = "y" ]; then + define_bool CONFIG_HAVE_DEC_LOCK y + fi fi + endmenu mainmenu_option next_comment @@ -422,6 +429,9 @@ bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE + if [ "$CONFIG_HIGHMEM" = "y" ]; then + bool ' Emulate HIGHMEM on lowmem machines' CONFIG_HIGHMEM_EMULATION + fi fi endmenu diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig --- a/arch/i386/defconfig Wed Feb 13 20:03:35 2002 +++ b/arch/i386/defconfig Wed Feb 13 20:03:35 2002 @@ -31,6 +31,7 @@ # CONFIG_MPENTIUM4 is not set # CONFIG_MK6 is not set # CONFIG_MK7 is not set +# CONFIG_MELAN is not set # CONFIG_MCRUSOE is not set # CONFIG_MWINCHIPC6 is not set # CONFIG_MWINCHIP2 is not set @@ -153,7 +154,7 @@ # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -# CONFIG_NETLINK is not set +# CONFIG_NETLINK_DEV is not set # CONFIG_NETFILTER is not set # CONFIG_FILTER is not set CONFIG_UNIX=y @@ -204,6 +205,7 @@ # CONFIG_BLK_DEV_HD is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_IDEDISK_MULTI_MODE=y +# CONFIG_IDEDISK_STROKE is not set # CONFIG_BLK_DEV_IDEDISK_VENDOR is not set # CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set # CONFIG_BLK_DEV_IDEDISK_IBM is not set @@ -218,6 +220,8 @@ # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set # # IDE chipset support/bugfixes @@ -229,12 +233,15 @@ CONFIG_BLK_DEV_IDEPCI=y CONFIG_IDEPCI_SHARE_IRQ=y CONFIG_BLK_DEV_IDEDMA_PCI=y -CONFIG_BLK_DEV_ADMA=y # CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set +CONFIG_BLK_DEV_ADMA=y # CONFIG_BLK_DEV_AEC62XX is not set # CONFIG_AEC62XX_TUNING is not set # CONFIG_BLK_DEV_ALI15X3 is not set @@ -242,6 +249,7 @@ # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set # CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_CMD680 is not set # CONFIG_BLK_DEV_CY82C693 is not set # CONFIG_BLK_DEV_CS5530 is not set # CONFIG_BLK_DEV_HPT34X is not set @@ -251,6 +259,7 @@ CONFIG_PIIX_TUNING=y # CONFIG_BLK_DEV_NS87415 is not set # CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC_ADMA is not set # CONFIG_BLK_DEV_PDC202XX is not set # CONFIG_PDC202XX_BURST is not set # CONFIG_PDC202XX_FORCE is not set @@ -260,6 +269,7 @@ # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_IDE_CHIPSETS is not set +# CONFIG_BLK_DEV_ELEVATOR_NOOP is not set CONFIG_IDEDMA_AUTO=y # CONFIG_IDEDMA_IVB is not set # CONFIG_DMA_NONPCI is not set @@ -426,11 +436,13 @@ # CONFIG_8139TOO_PIO is not set # CONFIG_8139TOO_TUNE_TWISTER is not set # CONFIG_8139TOO_8129 is not set +# CONFIG_8139_NEW_RX_RESET is not set # CONFIG_SIS900 is not set # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set # CONFIG_WINBOND_840 is not set # CONFIG_NET_POCKET is not set @@ -473,10 +485,10 @@ # CONFIG_PCMCIA_3C574 is not set # CONFIG_PCMCIA_FMVJ18X is not set CONFIG_PCMCIA_PCNET=y +# CONFIG_PCMCIA_AXNET is not set # CONFIG_PCMCIA_NMCLAN is not set # CONFIG_PCMCIA_SMC91C92 is not set # CONFIG_PCMCIA_XIRC2PS is not set -# CONFIG_PCMCIA_AXNET is not set # CONFIG_ARCNET_COM20020_CS is not set # CONFIG_PCMCIA_IBMTR is not set # CONFIG_PCMCIA_XIRCOM is not set @@ -496,9 +508,6 @@ # IrDA (infrared) support # # CONFIG_IRDA is not set -CONFIG_IRDA_CACHE_LAST_LSAP=y -CONFIG_IRDA_FAST_RR=y -CONFIG_IRDA_DEBUG=y # # ISDN subsystem @@ -583,12 +592,18 @@ CONFIG_AGP_ALI=y # CONFIG_AGP_SWORKS is not set CONFIG_DRM=y +# CONFIG_DRM_OLD is not set + +# +# DRM 4.1 drivers +# +CONFIG_DRM_NEW=y CONFIG_DRM_TDFX=y -# CONFIG_DRM_GAMMA is not set # CONFIG_DRM_R128 is not set CONFIG_DRM_RADEON=y # CONFIG_DRM_I810 is not set # CONFIG_DRM_MGA is not set +# CONFIG_DRM_SIS is not set # # PCMCIA character devices @@ -802,6 +817,7 @@ # CONFIG_USB_SERIAL_EMPEG is not set # CONFIG_USB_SERIAL_FTDI_SIO is not set # CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set # CONFIG_USB_SERIAL_IR is not set # CONFIG_USB_SERIAL_EDGEPORT is not set # CONFIG_USB_SERIAL_KEYSPAN_PDA is not set @@ -815,6 +831,7 @@ # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set # CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set # CONFIG_USB_SERIAL_PL2303 is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set diff -Nru a/arch/i386/kernel/acpitable.c b/arch/i386/kernel/acpitable.c --- a/arch/i386/kernel/acpitable.c Wed Feb 13 20:03:39 2002 +++ b/arch/i386/kernel/acpitable.c Wed Feb 13 20:03:39 2002 @@ -172,40 +172,41 @@ /* - * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_0, + * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_END, * to map the target physical address. The problem is that set_fixmap() * provides a single page, and it is possible that the page is not * sufficient. * By using this area, we can map up to MAX_IO_APICS pages temporarily, * i.e. until the next __va_range() call. + * + * Important Safety Note: The fixed I/O APIC page numbers are *subtracted* + * from the fixed base. That's why we start at FIX_IO_APIC_BASE_END and + * count idx down while incrementing the phys address. */ -static __inline__ char * +static __init char * __va_range(unsigned long phys, unsigned long size) { - unsigned long base, offset, mapped_size, mapped_phys = phys; - int idx = FIX_IO_APIC_BASE_0; + unsigned long base, offset, mapped_size; + int idx; offset = phys & (PAGE_SIZE - 1); mapped_size = PAGE_SIZE - offset; - set_fixmap(idx, mapped_phys); - base = fix_to_virt(FIX_IO_APIC_BASE_0); + set_fixmap(FIX_IO_APIC_BASE_END, phys); + base = fix_to_virt(FIX_IO_APIC_BASE_END); + dprintk("__va_range(0x%lx, 0x%lx): idx=%d mapped at %lx\n", phys, size, + FIX_IO_APIC_BASE_END, base); /* * Most cases can be covered by the below. */ - if (mapped_size >= size) - return ((unsigned char *) base + offset); - - dprintk("__va_range: mapping more than a single page, size = 0x%lx\n", - size); - - do { - if (idx++ == FIX_IO_APIC_BASE_END) + idx = FIX_IO_APIC_BASE_END; + while (mapped_size < size) { + if (--idx < FIX_IO_APIC_BASE_0) return 0; /* cannot handle this */ - mapped_phys = mapped_phys + PAGE_SIZE; - set_fixmap(idx, mapped_phys); - mapped_size = mapped_size + PAGE_SIZE; - } while (mapped_size < size); + phys += PAGE_SIZE; + set_fixmap(idx, phys); + mapped_size += PAGE_SIZE; + } return ((unsigned char *) base + offset); } @@ -267,11 +268,14 @@ } for (i = 0; i < tables; i++) { - + /* Map in header, then map in full table length. */ header = (acpi_table_header *) __va_range(saved_rsdt.entry[i], sizeof(acpi_table_header)); - + if (!header) + break; + header = (acpi_table_header *) + __va_range(saved_rsdt.entry[i], header->length); if (!header) break; diff -Nru a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c --- a/arch/i386/kernel/apic.c Wed Feb 13 20:03:54 2002 +++ b/arch/i386/kernel/apic.c Wed Feb 13 20:03:54 2002 @@ -796,8 +796,7 @@ */ slice = clocks / (smp_num_cpus+1); - printk("cpu: %d, clocks: %d, slice: %d\n", - smp_processor_id(), clocks, slice); + printk("cpu: %d, clocks: %d, slice: %d\n", smp_processor_id(), clocks, slice); /* * Wait for IRQ0's slice: @@ -820,8 +819,7 @@ __setup_APIC_LVTT(clocks); - printk("CPU%d\n", - smp_processor_id(), t0, t1, delta, slice, clocks); + printk("CPU%d\n", smp_processor_id(), t0, t1, delta, slice, clocks); __restore_flags(flags); } @@ -922,6 +920,26 @@ /* and update all other cpus */ smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1); +} + +void __init disable_APIC_timer(void) +{ + if (using_apic_timer) { + unsigned long v; + + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); + } +} + +void enable_APIC_timer(void) +{ + if (using_apic_timer) { + unsigned long v; + + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); + } } /* diff -Nru a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c --- a/arch/i386/kernel/dmi_scan.c Wed Feb 13 20:03:49 2002 +++ b/arch/i386/kernel/dmi_scan.c Wed Feb 13 20:03:49 2002 @@ -9,6 +9,7 @@ #include #include #include +#include unsigned long dmi_broken; int is_sony_vaio_laptop; @@ -51,7 +52,7 @@ u8 *data; int i=1; - buf = ioremap(base, len); + buf = bt_ioremap(base, len); if(buf==NULL) return -1; @@ -83,7 +84,7 @@ data+=2; i++; } - iounmap(buf); + bt_iounmap(buf, len); return 0; } @@ -155,7 +156,7 @@ return; if (dmi_ident[slot]) return; - dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL); + dmi_ident[slot] = alloc_bootmem(strlen(p)+1); if(dmi_ident[slot]) strcpy(dmi_ident[slot], p); else @@ -414,6 +415,22 @@ return 0; } +/* + * Work around broken HP Pavilion Notebooks which assign USB to + * IRQ 9 even though it is actually wired to IRQ 11 + */ +static __init int fix_broken_hp_bios_irq9(struct dmi_blacklist *d) +{ +#ifdef CONFIG_PCI + extern int broken_hp_bios_irq9; + if (broken_hp_bios_irq9 == 0) + { + broken_hp_bios_irq9 = 1; + printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident); + } +#endif + return 0; +} /* * Simple "print if true" callback @@ -619,7 +636,14 @@ NO_MATCH, NO_MATCH } }, - + { fix_broken_hp_bios_irq9, "HP Pavilion N5400 Series Laptop", { + MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + MATCH(DMI_BIOS_VERSION, "GE.M1.03"), + MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"), + MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736") + } }, + + /* * Generic per vendor APM settings */ @@ -731,12 +755,9 @@ } } -static int __init dmi_scan_machine(void) +void __init dmi_scan_machine(void) { int err = dmi_iterate(dmi_decode); if(err == 0) dmi_check_blacklist(); - return err; } - -module_init(dmi_scan_machine); diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Wed Feb 13 20:03:42 2002 +++ b/arch/i386/kernel/entry.S Wed Feb 13 20:03:42 2002 @@ -71,16 +71,36 @@ * these are offsets into the task-struct. */ state = 0 -flags = 4 +preempt_count = 4 sigpending = 8 addr_limit = 12 exec_domain = 16 need_resched = 20 tsk_ptrace = 24 -processor = 52 +cpu = 32 + +/* These are offsets into the irq_stat structure + * There is one per cpu and it is aligned to 32 + * byte boundry (we put that here as a shift count) + */ +irq_array_shift = CONFIG_X86_L1_CACHE_SHIFT + +irq_stat_local_irq_count = 4 +irq_stat_local_bh_count = 8 ENOSYS = 38 +#ifdef CONFIG_SMP +#define GET_CPU_INDX movl cpu(%ebx),%eax; \ + shll $irq_array_shift,%eax +#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx); \ + GET_CPU_INDX +#define CPU_INDX (,%eax) +#else +#define GET_CPU_INDX +#define GET_CURRENT_CPU_INDX GET_CURRENT(%ebx) +#define CPU_INDX +#endif #define SAVE_ALL \ cld; \ @@ -91,9 +111,9 @@ pushl %edi; \ pushl %esi; \ pushl %edx; \ + movl $(__KERNEL_DS),%edx; \ pushl %ecx; \ pushl %ebx; \ - movl $(__KERNEL_DS),%edx; \ movl %edx,%ds; \ movl %edx,%es; @@ -132,6 +152,30 @@ movl $-8192, reg; \ andl %esp, reg +#ifdef CONFIG_DEBUG_BUGVERBOSE +BUG_format: + .asciz "kernel BUG at %s:%d!\n" +ENTRY(do_BUG) + pushfl # Save flags and registers changed in C + pushl %eax + pushl %ecx + pushl %edx + pushl $1 + call SYMBOL_NAME(bust_spinlocks) + movl 28(%esp),%eax + movl 24(%esp),%ecx + pushl %eax + pushl %ecx + pushl $BUG_format + call SYMBOL_NAME(printk) + addl $16,%esp + popl %edx # Restore registers and flags for display + popl %ecx + popl %eax + popfl + ret +#endif /* CONFIG_DEBUG_BUGVERBOSE */ + ENTRY(lcall7) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. @@ -141,13 +185,13 @@ movl EFLAGS(%esp),%ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places - movl %ecx,CS(%esp) # movl %esp,%ebx + movl %ecx,CS(%esp) # pushl %ebx andl $-8192,%ebx # GET_CURRENT movl exec_domain(%ebx),%edx # Get the execution domain - movl 4(%edx),%edx # Get the lcall7 handler for the domain pushl $0x7 + movl 4(%edx),%edx # Get the lcall7 handler for the domain call *%edx addl $4, %esp popl %eax @@ -162,13 +206,13 @@ movl EFLAGS(%esp),%ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places - movl %ecx,CS(%esp) # movl %esp,%ebx + movl %ecx,CS(%esp) # pushl %ebx andl $-8192,%ebx # GET_CURRENT movl exec_domain(%ebx),%edx # Get the execution domain - movl 4(%edx),%edx # Get the lcall7 handler for the domain pushl $0x27 + movl 4(%edx),%edx # Get the lcall7 handler for the domain call *%edx addl $4, %esp popl %eax @@ -176,9 +220,11 @@ ENTRY(ret_from_fork) +#if CONFIG_SMP pushl %ebx call SYMBOL_NAME(schedule_tail) addl $4, %esp +#endif GET_CURRENT(%ebx) testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS jne tracesys_exit @@ -247,12 +293,30 @@ ALIGN ENTRY(ret_from_intr) GET_CURRENT(%ebx) +#ifdef CONFIG_PREEMPT + cli + decl preempt_count(%ebx) +#endif ret_from_exception: movl EFLAGS(%esp),%eax # mix EFLAGS and CS movb CS(%esp),%al testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? jne ret_from_sys_call +#ifdef CONFIG_PREEMPT + cmpl $0,preempt_count(%ebx) + jnz restore_all + cmpl $0,need_resched(%ebx) + jz restore_all + movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx + addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx + jnz restore_all + incl preempt_count(%ebx) + sti + call SYMBOL_NAME(preempt_schedule) + jmp ret_from_intr +#else jmp restore_all +#endif ALIGN reschedule: @@ -289,6 +353,9 @@ GET_CURRENT(%ebx) call *%edi addl $8,%esp +#ifdef CONFIG_PREEMPT + cli +#endif jmp ret_from_exception ENTRY(coprocessor_error) @@ -308,12 +375,18 @@ movl %cr0,%eax testl $0x4,%eax # EM (math emulation bit) jne device_not_available_emulate +#ifdef CONFIG_PREEMPT + cli +#endif call SYMBOL_NAME(math_state_restore) jmp ret_from_exception device_not_available_emulate: pushl $0 # temporary storage for ORIG_EIP call SYMBOL_NAME(math_emulate) addl $4,%esp +#ifdef CONFIG_PREEMPT + cli +#endif jmp ret_from_exception ENTRY(debug) diff -Nru a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S --- a/arch/i386/kernel/head.S Wed Feb 13 20:03:35 2002 +++ b/arch/i386/kernel/head.S Wed Feb 13 20:03:35 2002 @@ -445,4 +445,15 @@ .quad 0x00409a0000000000 /* 0x48 APM CS code */ .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ + /* Segments used for calling PnP BIOS */ + .quad 0x00c09a0000000000 /* 0x60 32-bit code */ + .quad 0x00809a0000000000 /* 0x68 16-bit code */ + .quad 0x0080920000000000 /* 0x70 16-bit data */ + .quad 0x0080920000000000 /* 0x78 16-bit data */ + .quad 0x0080920000000000 /* 0x80 16-bit data */ + .quad 0x0000000000000000 /* 0x88 not used */ + .quad 0x0000000000000000 /* 0x90 not used */ + .quad 0x0000000000000000 /* 0x98 not used */ + /* Per CPU segments */ .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + diff -Nru a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c --- a/arch/i386/kernel/i387.c Wed Feb 13 20:03:55 2002 +++ b/arch/i386/kernel/i387.c Wed Feb 13 20:03:55 2002 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -65,6 +66,8 @@ { struct task_struct *tsk = current; + preempt_disable(); + if (tsk->flags & PF_USEDFPU) { __save_init_fpu(tsk); return; diff -Nru a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c --- a/arch/i386/kernel/i8259.c Wed Feb 13 20:03:49 2002 +++ b/arch/i386/kernel/i8259.c Wed Feb 13 20:03:49 2002 @@ -79,6 +79,7 @@ * through the ICC by us (IPIs) */ #ifdef CONFIG_SMP +BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR) BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR) BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) @@ -472,6 +473,9 @@ * IPI, driven by wakeup. */ set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); + + /* IPI for task migration */ + set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt); /* IPI for invalidation */ set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); diff -Nru a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c --- a/arch/i386/kernel/irq.c Wed Feb 13 20:03:33 2002 +++ b/arch/i386/kernel/irq.c Wed Feb 13 20:03:33 2002 @@ -1,7 +1,7 @@ /* * linux/arch/i386/kernel/irq.c * - * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * Copyright (C) 1992, 1998, 2001 Linus Torvalds, Ingo Molnar * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -66,9 +67,14 @@ * Controller mappings for all interrupt sources: */ irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = - { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED }}; -static void register_irq_proc (unsigned int irq); +/* + * Print warnings only once. + */ +static unsigned int rate_warning [NR_IRQS] = { [0 ... NR_IRQS-1] = 1 }; + +static void register_irq_proc(unsigned int irq); /* * Special irq handlers. @@ -230,35 +236,8 @@ show_stack(NULL); printk("\n"); } - -#define MAXCOUNT 100000000 - -/* - * I had a lockup scenario where a tight loop doing - * spin_unlock()/spin_lock() on CPU#1 was racing with - * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but - * apparently the spin_unlock() information did not make it - * through to CPU#0 ... nasty, is this by design, do we have to limit - * 'memory update oscillation frequency' artificially like here? - * - * Such 'high frequency update' races can be avoided by careful design, but - * some of our major constructs like spinlocks use similar techniques, - * it would be nice to clarify this issue. Set this define to 0 if you - * want to check whether your system freezes. I suspect the delay done - * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but - * i thought that such things are guaranteed by design, since we use - * the 'LOCK' prefix. - */ -#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0 -#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND -# define SYNC_OTHER_CORES(x) udelay(x+1) -#else -/* - * We have to allow irqs to arrive between __sti and __cli - */ -# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") -#endif +#define MAXCOUNT 100000000 static inline void wait_on_irq(int cpu) { @@ -276,7 +255,7 @@ break; /* Duh, we have to loop. Release the lock to avoid deadlocks */ - clear_bit(0,&global_irq_lock); + clear_bit(0, &global_irq_lock); for (;;) { if (!--count) { @@ -284,7 +263,8 @@ count = ~0; } __sti(); - SYNC_OTHER_CORES(cpu); + /* Allow irqs to arrive */ + __asm__ __volatile__ ("nop"); __cli(); if (irqs_running()) continue; @@ -467,6 +447,13 @@ * controller lock. */ +inline void __disable_irq(irq_desc_t *desc, unsigned int irq) +{ + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } +} /** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable @@ -485,10 +472,7 @@ unsigned long flags; spin_lock_irqsave(&desc->lock, flags); - if (!desc->depth++) { - desc->status |= IRQ_DISABLED; - desc->handler->disable(irq); - } + __disable_irq(desc, irq); spin_unlock_irqrestore(&desc->lock, flags); } @@ -517,23 +501,8 @@ } } -/** - * enable_irq - enable handling of an irq - * @irq: Interrupt to enable - * - * Undoes the effect of one call to disable_irq(). If this - * matches the last disable, processing of interrupts on this - * IRQ line is re-enabled. - * - * This function may be called from IRQ context. - */ - -void enable_irq(unsigned int irq) +static inline void __enable_irq(irq_desc_t *desc, unsigned int irq) { - irq_desc_t *desc = irq_desc + irq; - unsigned long flags; - - spin_lock_irqsave(&desc->lock, flags); switch (desc->depth) { case 1: { unsigned int status = desc->status & ~IRQ_DISABLED; @@ -552,10 +521,96 @@ printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0)); } +} + +/** + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + __enable_irq(desc, irq); spin_unlock_irqrestore(&desc->lock, flags); } /* + * This function, provided by every architecture, resets + * the irq-limit counters in every jiffy, and restarts + * pending IRQ handlers if necessery. + * + * Overhead is fairly small in the normal case, since it + * gets the irq-descriptor spinlock only if the IRQ got + * mitigated. + */ + +void irq_rate_check(struct pt_regs *regs) +{ + unsigned long flags; + irq_desc_t *desc; + int i; + + __save_flags(flags); + __cli(); + for (i = 0; i < NR_IRQS; i++) { + struct irqaction * action; + + desc = irq_desc + i; + desc->irq_contexts = 0; + desc->total_contexts = 0; + /* + * quick lockless test. + */ + if (!(desc->status & IRQ_MITIGATED)) + continue; + /* + * From this point on we re-execute pending IRQ + * handlers. This is necessery for IRQ controllers + * that are neither capable of recording pending + * interrupts, nor are capable of injecting IRQs + * artificially via hw_resend_irq(). So we are + * restarting them by hand - fortunately we are in + * an IRQ context already. + */ + action = NULL; + spin_lock(&desc->lock); + if (desc->status & IRQ_MITIGATED) { + desc->status &= ~IRQ_MITIGATED; + if (!(desc->status & IRQ_INPROGRESS) && + (desc->depth == 1)) { + action = desc->action; + desc->status |= IRQ_INPROGRESS; + } + } + if (action) { + for (;;) { + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + handle_IRQ_event(i, regs, action); + spin_lock(&desc->lock); + if (!(desc->status & IRQ_PENDING)) + break; + } + desc->status &= ~IRQ_INPROGRESS; + } + desc->status |= IRQ_REPLAY; + __enable_irq(desc, i); + spin_unlock(&desc->lock); + } + __restore_flags(flags); +} + +/* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). @@ -576,7 +631,7 @@ int cpu = smp_processor_id(); irq_desc_t *desc = irq_desc + irq; struct irqaction * action; - unsigned int status; + unsigned int status, user_stat; kstat.irqs[cpu][irq]++; spin_lock(&desc->lock); @@ -586,6 +641,19 @@ WAITING is used by probe to mark irqs that are being tested */ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + /* + * Our 'interrup load' estimator. The estimator counts the number + * of times we interrupt IRQ contexts (hardirqs, softirqs), as + * opposed to non-IRQ contexts (syscalls, user-space, idle task). + * + * If the ratio of irq-contexts goes above a certain limit then we + * go into 'overload mode' that involves the disabling of this + * interrupt source until the next timer tick. + */ + desc->total_contexts++; + if (0 && unlikely(in_interrupt())) + goto mitigate_irqload; +normal_irqpath: status |= IRQ_PENDING; /* we _want_ to handle it */ /* @@ -640,6 +708,39 @@ if (softirq_pending(cpu)) do_softirq(); return 1; + +mitigate_irqload: + /* + * avoid false positives due to statistical fluctuations. + * We need at least 10 interrupts in this jiffy for + * mitigation to happen. + */ + #define MIN_RATE (1000/HZ + 10) + + desc->irq_contexts++; + if (desc->total_contexts <= MIN_RATE) + goto normal_irqpath; + user_stat = desc->total_contexts - desc->irq_contexts; + /* + * Mitigate if less than 3% of contexts are + * non-irq contexts: + */ + if (user_stat >= (desc->total_contexts >> 5)) + goto normal_irqpath; + if (rate_warning[irq]) { + printk(KERN_WARNING "Possible IRQ overload: maximum irq load of 97%% exceeded for IRQ%d!\nThrottling IRQ%d.\n", irq, irq); + rate_warning[irq] = 0; + } + if (status & IRQ_MITIGATED) + goto out; + /* + * Disable interrupt source. It will be re-enabled + * by the next timer interrupt - and possibly be + * restarted if needed. + */ + desc->status |= IRQ_MITIGATED|IRQ_PENDING; + __disable_irq(desc, irq); + goto out; } /** @@ -812,7 +913,7 @@ * something may have generated an irq long ago and we want to * flush such a longstanding irq before considering it as spurious. */ - for (i = NR_IRQS-1; i > 0; i--) { + for (i = NR_IRQS-1; i > 0; i--) { desc = irq_desc + i; spin_lock_irq(&desc->lock); @@ -1035,7 +1136,7 @@ #define HEX_DIGITS 8 -static unsigned int parse_hex_value (const char *buffer, +static unsigned int parse_hex_value(const char *buffer, unsigned long count, unsigned long *ret) { unsigned char hexnum [HEX_DIGITS]; @@ -1074,18 +1175,17 @@ #if CONFIG_SMP -static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; - static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; -static int irq_affinity_read_proc (char *page, char **start, off_t off, + +static int irq_affinity_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - if (count < HEX_DIGITS+1) + if (count <= HEX_DIGITS) return -EINVAL; return sprintf (page, "%08lx\n", irq_affinity[(long)data]); } -static int irq_affinity_write_proc (struct file *file, const char *buffer, +static int irq_affinity_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { int irq = (long) data, full_count = count, err; @@ -1112,16 +1212,16 @@ #endif -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, +static int prof_cpu_mask_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { unsigned long *mask = (unsigned long *) data; - if (count < HEX_DIGITS+1) + if (count <= HEX_DIGITS) return -EINVAL; return sprintf (page, "%08lx\n", *mask); } -static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, +static int prof_cpu_mask_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { unsigned long *mask = (unsigned long *) data, full_count = count, err; @@ -1137,8 +1237,11 @@ #define MAX_NAMELEN 10 -static void register_irq_proc (unsigned int irq) +static void register_irq_proc(unsigned int irq) { +#if CONFIG_SMP + struct proc_dir_entry *entry; +#endif char name [MAX_NAMELEN]; if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || @@ -1152,27 +1255,21 @@ irq_dir[irq] = proc_mkdir(name, root_irq_dir); #if CONFIG_SMP - { - struct proc_dir_entry *entry; - - /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); - if (entry) { - entry->nlink = 1; - entry->data = (void *)(long)irq; - entry->read_proc = irq_affinity_read_proc; - entry->write_proc = irq_affinity_write_proc; - } - - smp_affinity_entry[irq] = entry; + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; } #endif } unsigned long prof_cpu_mask = -1; -void init_irq_proc (void) +void init_irq_proc(void) { struct proc_dir_entry *entry; int i; @@ -1184,7 +1281,7 @@ entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); if (!entry) - return; + return; entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; diff -Nru a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c --- a/arch/i386/kernel/mtrr.c Wed Feb 13 20:03:30 2002 +++ b/arch/i386/kernel/mtrr.c Wed Feb 13 20:03:30 2002 @@ -378,10 +378,8 @@ static int arr3_protected; /* Put the processor into a state where MTRRs can be safely set */ -static void set_mtrr_prepare (struct set_mtrr_context *ctxt) +static void set_mtrr_prepare_save (struct set_mtrr_context *ctxt) { - unsigned long tmp; - /* Disable interrupts locally */ __save_flags (ctxt->flags); __cli (); @@ -404,16 +402,27 @@ } if ( mtrr_if == MTRR_IF_INTEL ) { - /* Disable MTRRs, and set the default type to uncached */ + /* Save MTRR state */ rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + } else { + /* Cyrix ARRs - everything else were excluded at the top */ + ctxt->ccr3 = getCx86 (CX86_CCR3); + } +} /* End Function set_mtrr_prepare_save */ + +static void set_mtrr_disable (struct set_mtrr_context *ctxt) +{ + if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) + return; + + if ( mtrr_if == MTRR_IF_INTEL ) { + /* Disable MTRRs, and set the default type to uncached */ wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); } else { /* Cyrix ARRs - everything else were excluded at the top */ - tmp = getCx86 (CX86_CCR3); - setCx86 (CX86_CCR3, (tmp & 0x0f) | 0x10); - ctxt->ccr3 = tmp; + setCx86 (CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10); } -} /* End Function set_mtrr_prepare */ +} /* End Function set_mtrr_disable */ /* Restore the processor after a set_mtrr_prepare */ static void set_mtrr_done (struct set_mtrr_context *ctxt) @@ -674,7 +683,10 @@ { struct set_mtrr_context ctxt; - if (do_safe) set_mtrr_prepare (&ctxt); + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); + } if (size == 0) { /* The invalid bit is kept in the mask, so we simply clear the @@ -726,7 +738,10 @@ } } - if (do_safe) set_mtrr_prepare (&ctxt); + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); + } base <<= PAGE_SHIFT; setCx86(arr, ((unsigned char *) &base)[3]); setCx86(arr+1, ((unsigned char *) &base)[2]); @@ -750,7 +765,10 @@ u32 regs[2]; struct set_mtrr_context ctxt; - if (do_safe) set_mtrr_prepare (&ctxt); + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); + } /* * Low is MTRR0 , High MTRR 1 */ @@ -788,7 +806,10 @@ struct set_mtrr_context ctxt; unsigned long low, high; - if (do_safe) set_mtrr_prepare( &ctxt ); + if (do_safe) { + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); + } if (size == 0) { /* Disable */ @@ -985,6 +1006,7 @@ static atomic_t undone_count; +static volatile int wait_barrier_mtrr_disable = FALSE; static volatile int wait_barrier_execute = FALSE; static volatile int wait_barrier_cache_enable = FALSE; @@ -1003,18 +1025,21 @@ { struct set_mtrr_data *data = info; struct set_mtrr_context ctxt; - - set_mtrr_prepare (&ctxt); + set_mtrr_prepare_save (&ctxt); /* Notify master that I've flushed and disabled my cache */ atomic_dec (&undone_count); - while (wait_barrier_execute) barrier (); + while (wait_barrier_mtrr_disable) { rep_nop(); barrier(); } + set_mtrr_disable (&ctxt); + /* Notify master that I've flushed and disabled my cache */ + atomic_dec (&undone_count); + while (wait_barrier_execute) { rep_nop(); barrier(); } /* The master has cleared me to execute */ (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, FALSE); /* Notify master CPU that I've executed the function */ atomic_dec (&undone_count); /* Wait for master to clear me to enable cache and return */ - while (wait_barrier_cache_enable) barrier (); + while (wait_barrier_cache_enable) { rep_nop(); barrier(); } set_mtrr_done (&ctxt); } /* End Function ipi_handler */ @@ -1028,6 +1053,7 @@ data.smp_base = base; data.smp_size = size; data.smp_type = type; + wait_barrier_mtrr_disable = TRUE; wait_barrier_execute = TRUE; wait_barrier_cache_enable = TRUE; atomic_set (&undone_count, smp_num_cpus - 1); @@ -1035,15 +1061,22 @@ if (smp_call_function (ipi_handler, &data, 1, 0) != 0) panic ("mtrr: timed out waiting for other CPUs\n"); /* Flush and disable the local CPU's cache */ - set_mtrr_prepare (&ctxt); + set_mtrr_prepare_save (&ctxt); + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } + /* Set up for completion wait and then release other CPUs to change MTRRs*/ + atomic_set (&undone_count, smp_num_cpus - 1); + wait_barrier_mtrr_disable = FALSE; + set_mtrr_disable (&ctxt); + /* Wait for all other CPUs to flush and disable their caches */ - while (atomic_read (&undone_count) > 0) barrier (); + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } /* Set up for completion wait and then release other CPUs to change MTRRs*/ atomic_set (&undone_count, smp_num_cpus - 1); wait_barrier_execute = FALSE; (*set_mtrr_up) (reg, base, size, type, FALSE); /* Now wait for other CPUs to complete the function */ - while (atomic_read (&undone_count) > 0) barrier (); + while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } /* Now all CPUs should have finished the function. Release the barrier to allow them to re-enable their caches and return from their interrupt, then enable the local cache and return */ @@ -1889,7 +1922,9 @@ struct set_mtrr_context ctxt; int i; - set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + /* flush cache and enable MAPEN */ + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); /* the CCRs are not contiguous */ for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); @@ -1926,7 +1961,9 @@ int i; #endif - set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + /* flush cache and enable MAPEN */ + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); /* Save all CCRs locally */ ccr[0] = getCx86 (CX86_CCR0); @@ -2075,7 +2112,8 @@ { struct set_mtrr_context ctxt; - set_mtrr_prepare (&ctxt); + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); if(boot_cpu_data.x86_model==4) centaur_mcr0_init(); @@ -2192,7 +2230,8 @@ /* Note that this is not ideal, since the cache is only flushed/disabled for this CPU while the MTRRs are changed, but changing this requires more invasive changes to the way the kernel boots */ - set_mtrr_prepare (&ctxt); + set_mtrr_prepare_save (&ctxt); + set_mtrr_disable (&ctxt); mask = set_mtrr_state (&smp_mtrr_state, &ctxt); set_mtrr_done (&ctxt); /* Use the atomic bitops to update the global mask */ diff -Nru a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c --- a/arch/i386/kernel/nmi.c Wed Feb 13 20:03:30 2002 +++ b/arch/i386/kernel/nmi.c Wed Feb 13 20:03:30 2002 @@ -8,6 +8,7 @@ * Fixes: * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. * Mikael Pettersson : Power Management for local APIC NMI watchdog. + * Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog. */ #include @@ -43,6 +44,32 @@ #define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 #define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED +#define MSR_P4_MISC_ENABLE 0x1A0 +#define MSR_P4_MISC_ENABLE_PERF_AVAIL (1<<7) +#define MSR_P4_MISC_ENABLE_PEBS_UNAVAIL (1<<12) +#define MSR_P4_PERFCTR0 0x300 +#define MSR_P4_CCCR0 0x360 +#define P4_ESCR_EVENT_SELECT(N) ((N)<<25) +#define P4_ESCR_OS (1<<3) +#define P4_ESCR_USR (1<<2) +#define P4_CCCR_OVF_PMI (1<<26) +#define P4_CCCR_THRESHOLD(N) ((N)<<20) +#define P4_CCCR_COMPLEMENT (1<<19) +#define P4_CCCR_COMPARE (1<<18) +#define P4_CCCR_REQUIRED (3<<16) +#define P4_CCCR_ESCR_SELECT(N) ((N)<<13) +#define P4_CCCR_ENABLE (1<<12) +/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter + CRU_ESCR0 (with any non-null event selector) through a complemented + max threshold. [IA32-Vol3, Section 14.9.9] */ +#define MSR_P4_IQ_COUNTER0 0x30C +#define MSR_P4_IQ_CCCR0 0x36C +#define MSR_P4_CRU_ESCR0 0x3B8 +#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR) +#define P4_NMI_IQ_CCCR0 \ + (P4_CCCR_OVF_PMI|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ + P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) + int __init check_nmi_watchdog (void) { irq_cpustat_t tmp[NR_CPUS]; @@ -84,11 +111,11 @@ /* * If any other x86 CPU has a local APIC, then * please test the NMI stuff there and send me the - * missing bits. Right now Intel P6 and AMD K7 only. + * missing bits. Right now Intel P6/P4 and AMD K7 only. */ if ((nmi == NMI_LOCAL_APIC) && (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && - (boot_cpu_data.x86 == 6)) + (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15)) nmi_watchdog = nmi; if ((nmi == NMI_LOCAL_APIC) && (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && @@ -118,7 +145,15 @@ wrmsr(MSR_K7_EVNTSEL0, 0, 0); break; case X86_VENDOR_INTEL: - wrmsr(MSR_IA32_EVNTSEL0, 0, 0); + switch (boot_cpu_data.x86) { + case 6: + wrmsr(MSR_P6_EVNTSEL0, 0, 0); + break; + case 15: + wrmsr(MSR_P4_IQ_CCCR0, 0, 0); + wrmsr(MSR_P4_CRU_ESCR0, 0, 0); + break; + } break; } } @@ -157,17 +192,22 @@ * Original code written by Keith Owens. */ +static void __pminit clear_msr_range(unsigned int base, unsigned int n) +{ + unsigned int i; + + for(i = 0; i < n; ++i) + wrmsr(base+i, 0, 0); +} + static void __pminit setup_k7_watchdog(void) { - int i; unsigned int evntsel; nmi_perfctr_msr = MSR_K7_PERFCTR0; - for(i = 0; i < 4; ++i) { - wrmsr(MSR_K7_EVNTSEL0+i, 0, 0); - wrmsr(MSR_K7_PERFCTR0+i, 0, 0); - } + clear_msr_range(MSR_K7_EVNTSEL0, 4); + clear_msr_range(MSR_K7_PERFCTR0, 4); evntsel = K7_EVNTSEL_INT | K7_EVNTSEL_OS @@ -184,27 +224,54 @@ static void __pminit setup_p6_watchdog(void) { - int i; unsigned int evntsel; - nmi_perfctr_msr = MSR_IA32_PERFCTR0; + nmi_perfctr_msr = MSR_P6_PERFCTR0; - for(i = 0; i < 2; ++i) { - wrmsr(MSR_IA32_EVNTSEL0+i, 0, 0); - wrmsr(MSR_IA32_PERFCTR0+i, 0, 0); - } + clear_msr_range(MSR_P6_EVNTSEL0, 2); + clear_msr_range(MSR_P6_PERFCTR0, 2); evntsel = P6_EVNTSEL_INT | P6_EVNTSEL_OS | P6_EVNTSEL_USR | P6_NMI_EVENT; - wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); - Dprintk("setting IA32_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); - wrmsr(MSR_IA32_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); + Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); apic_write(APIC_LVTPC, APIC_DM_NMI); evntsel |= P6_EVNTSEL0_ENABLE; - wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); +} + +static int __pminit setup_p4_watchdog(void) +{ + unsigned int misc_enable, dummy; + + rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy); + if (!(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL)) + return 0; + + nmi_perfctr_msr = MSR_P4_IQ_COUNTER0; + + if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) + clear_msr_range(0x3F1, 2); + /* MSR 0x3F0 seems to have a default value of 0xFC00, but current + docs doesn't fully define it, so leave it alone for now. */ + clear_msr_range(0x3A0, 31); + clear_msr_range(0x3C0, 6); + clear_msr_range(0x3C8, 6); + clear_msr_range(0x3E0, 2); + clear_msr_range(MSR_P4_CCCR0, 18); + clear_msr_range(MSR_P4_PERFCTR0, 18); + + wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); + Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + return 1; } void __pminit setup_apic_nmi_watchdog (void) @@ -216,9 +283,17 @@ setup_k7_watchdog(); break; case X86_VENDOR_INTEL: - if (boot_cpu_data.x86 != 6) + switch (boot_cpu_data.x86) { + case 6: + setup_p6_watchdog(); + break; + case 15: + if (!setup_p4_watchdog()) + return; + break; + default: return; - setup_p6_watchdog(); + } break; default: return; @@ -283,7 +358,7 @@ * to get a message out. */ bust_spinlocks(1); - printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + printk("NMI Watchdog detected LOCKUP on CPU%d, eip %08lx, registers:\n", cpu, regs->eip); show_registers(regs); printk("console shuts up ...\n"); console_silent(); @@ -295,6 +370,18 @@ last_irq_sums[cpu] = sum; alert_counter[cpu] = 0; } - if (nmi_perfctr_msr) + if (nmi_perfctr_msr) { + if (nmi_perfctr_msr == MSR_P4_IQ_COUNTER0) { + /* + * P4 quirks: + * - An overflown perfctr will assert its interrupt + * until the OVF flag in its CCCR is cleared. + * - LVTPC is masked on interrupt and must be + * unmasked by the LVTPC handler. + */ + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + } wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); + } } diff -Nru a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c --- a/arch/i386/kernel/pci-irq.c Wed Feb 13 20:03:52 2002 +++ b/arch/i386/kernel/pci-irq.c Wed Feb 13 20:03:52 2002 @@ -22,6 +22,8 @@ #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) #define PIRQ_VERSION 0x0100 +int broken_hp_bios_irq9; + static struct irq_routing_table *pirq_table; /* @@ -564,6 +566,15 @@ } DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); mask &= pcibios_irq_mask; + + /* Work around broken HP Pavilion Notebooks which assign USB to + IRQ 9 even though it is actually wired to IRQ 11 */ + + if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) { + dev->irq = 11; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11); + r->set(pirq_router_dev, dev, pirq, 11); + } /* * Find the best IRQ to assign: use the one diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c --- a/arch/i386/kernel/process.c Wed Feb 13 20:03:29 2002 +++ b/arch/i386/kernel/process.c Wed Feb 13 20:03:29 2002 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_MATH_EMULATION @@ -123,15 +124,12 @@ void cpu_idle (void) { /* endless idle loop with no priority at all */ - init_idle(); - current->nice = 20; - current->counter = -100; while (1) { void (*idle)(void) = pm_idle; if (!idle) idle = default_idle; - while (!current->need_resched) + if (!current->need_resched) idle(); schedule(); check_pgt_cache(); @@ -542,6 +540,8 @@ BUG(); } } + + release_x86_irqs(dead_task); } /* @@ -694,15 +694,17 @@ asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs)); /* - * Restore %fs and %gs. + * Restore %fs and %gs if needed. */ - loadsegment(fs, next->fs); - loadsegment(gs, next->gs); + if (unlikely(prev->fs | prev->gs | next->fs | next->gs)) { + loadsegment(fs, next->fs); + loadsegment(gs, next->gs); + } /* * Now maybe reload the debug registers */ - if (next->debugreg[7]){ + if (unlikely(next->debugreg[7])) { loaddebug(next, 0); loaddebug(next, 1); loaddebug(next, 2); @@ -712,7 +714,7 @@ loaddebug(next, 7); } - if (prev->ioperm || next->ioperm) { + if (unlikely(prev->ioperm || next->ioperm)) { if (next->ioperm) { /* * 4 cachelines copy ... not good, but not that diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c Wed Feb 13 20:03:51 2002 +++ b/arch/i386/kernel/setup.c Wed Feb 13 20:03:51 2002 @@ -155,6 +155,7 @@ unsigned char aux_device_present; extern void mcheck_init(struct cpuinfo_x86 *c); +extern void dmi_scan_machine(void); extern int root_mountflags; extern char _text, _etext, _edata, _end; @@ -319,6 +320,7 @@ static char command_line[COMMAND_LINE_SIZE]; char saved_command_line[COMMAND_LINE_SIZE]; +/* ioport resources for standard PCs */ struct resource standard_io_resources[] = { { "dma1", 0x00, 0x1f, IORESOURCE_BUSY }, { "pic1", 0x20, 0x3f, IORESOURCE_BUSY }, @@ -329,9 +331,33 @@ { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } }; - #define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) +/* ioport resources for AMD Elan SC400, SC410 */ +struct resource elan4x0_io_resources[] = { + { "dma1", 0x00, 0x0f, IORESOURCE_BUSY }, + { "pic1", 0x20, 0x21, IORESOURCE_BUSY }, + { "csc", 0x22, 0x23, IORESOURCE_BUSY }, + { "timer", 0x40, 0x43, IORESOURCE_BUSY }, + { "keyboard", 0x60, 0x64, IORESOURCE_BUSY }, + { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY }, + { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY }, + { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, +}; +#define ELAN4X0_IO_RESOURCES (sizeof(elan4x0_io_resources)/sizeof(struct resource)) + +/* ioport resources for AMD Elan SC520 */ +struct resource elan520_io_resources[] = { + { "dma1", 0x00, 0x0f, IORESOURCE_BUSY }, + { "pic1", 0x20, 0x21, IORESOURCE_BUSY }, + { "timer", 0x40, 0x43, IORESOURCE_BUSY }, + { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY }, + { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY }, + { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, + { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } +}; +#define ELAN520_IO_RESOURCES (sizeof(elan520_io_resources)/sizeof(struct resource)) + static struct resource code_resource = { "Kernel code", 0x100000, 0 }; static struct resource data_resource = { "Kernel data", 0, 0 }; static struct resource vram_resource = { "Video RAM area", 0xa0000, 0xbffff, IORESOURCE_BUSY }; @@ -829,7 +855,20 @@ /* * Reserved space for vmalloc and iomap - defined in asm/page.h */ +#ifdef CONFIG_HIGHMEM_EMULATION +#define ORDER_DOWN(x) ((x >> (MAX_ORDER-1)) << (MAX_ORDER-1)) +#define MAXMEM_PFN \ +({ \ + int __max_pfn; \ + if (max_pfn > PFN_DOWN(MAXMEM)) \ + __max_pfn = PFN_DOWN(MAXMEM); \ + else \ + __max_pfn = ORDER_DOWN(max_pfn / 5); \ + __max_pfn; \ +}) +#else #define MAXMEM_PFN PFN_DOWN(MAXMEM) +#endif #define MAX_NONPAE_PFN (1 << 20) /* @@ -1026,9 +1065,22 @@ } request_resource(&iomem_resource, &vram_resource); - /* request I/O space for devices used on all i[345]86 PCs */ - for (i = 0; i < STANDARD_IO_RESOURCES; i++) - request_resource(&ioport_resource, standard_io_resources+i); + /* + * request I/O space for devices used on all i[345]86 PCs + * test for AMD Elan CPUs (which have reduced ressources): if we + * have one of these processors allocate resources later in init_amd() + * (x86=4,x86_model=10: AMD SC410; x86=4,x86_model=9: AMD SC520) + */ + + if (!( (strcmp(boot_cpu_data.x86_vendor_id,"AuthenticAMD")==0) && + boot_cpu_data.x86 == 4 && + ((boot_cpu_data.x86_model==10) || (boot_cpu_data.x86_model==9)) + ) + ) + { + for (i = 0; i < STANDARD_IO_RESOURCES; i++) + request_resource(&ioport_resource, standard_io_resources+i); + } /* Tell the PCI layer not to allocate too close to the RAM area.. */ low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff; @@ -1042,6 +1094,7 @@ conswitchp = &dummy_con; #endif #endif + dmi_scan_machine(); } static int cachesize_override __initdata = -1; @@ -1172,6 +1225,7 @@ u32 l, h; int mbytes = max_mapnr >> (20-PAGE_SHIFT); int r; + int i; /* * FIXME: We should handle the K5 here. Set up the write @@ -1187,6 +1241,24 @@ switch(c->x86) { + case 4: + if ( c->x86_model == 9 ) /* Elan SC520 FIXME: empirical value! */ + { + /* request resources */ + for (i = 0; i < ELAN520_IO_RESOURCES; i++) + request_resource(&ioport_resource, elan520_io_resources+i); + break; + } + + if ( c->x86_model == 10 ) /* Elan SC410 FIXME: empirical value! */ + { + /* request resources */ + for (i = 0; i < ELAN4X0_IO_RESOURCES; i++) + request_resource(&ioport_resource, elan4x0_io_resources+i); + break; + } + break; + case 5: if( c->x86_model < 6 ) { @@ -1306,7 +1378,7 @@ } /* - * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU + * Read NSC/Cyrix DEVID registers (DIR) to get more detailed info. about the CPU */ static void __init do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) { @@ -2270,6 +2342,8 @@ c->x86_vendor = X86_VENDOR_AMD; else if (!strcmp(v, "CyrixInstead")) c->x86_vendor = X86_VENDOR_CYRIX; + else if (!strcmp(v, "Geode by NSC")) + c->x86_vendor = X86_VENDOR_NSC; else if (!strcmp(v, "UMC UMC UMC ")) c->x86_vendor = X86_VENDOR_UMC; else if (!strcmp(v, "CentaurHauls")) @@ -2613,6 +2687,10 @@ init_cyrix(c); break; + case X86_VENDOR_NSC: + init_cyrix(c); + break; + case X86_VENDOR_AMD: init_amd(c); break; @@ -2713,14 +2791,17 @@ { get_cpu_vendor(&boot_cpu_data); - if ( boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX ) + if ( boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX || + boot_cpu_data.x86_vendor == X86_VENDOR_NSC ) init_cyrix(&boot_cpu_data); } /* These need to match */ static char *cpu_vendor_names[] __initdata = { - "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur", "Rise", "Transmeta" }; + "Intel", "Cyrix", "AMD", "UMC", "NexGen", + "Centaur", "Rise", "Transmeta", "NSC" +}; void __init print_cpu_info(struct cpuinfo_x86 *c) @@ -2920,9 +3001,10 @@ load_TR(nr); load_LDT(&init_mm); - /* - * Clear all 6 debug registers: - */ + /* Clear %fs and %gs. */ + asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); + + /* Clear all 6 debug registers: */ #define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) ); diff -Nru a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c --- a/arch/i386/kernel/smp.c Wed Feb 13 20:03:34 2002 +++ b/arch/i386/kernel/smp.c Wed Feb 13 20:03:34 2002 @@ -105,7 +105,7 @@ /* The 'big kernel lock' */ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; -struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { &init_mm, 0 }}; +struct tlb_state cpu_tlbstate[NR_CPUS] __cacheline_aligned = {[0 ... NR_CPUS-1] = { &init_mm, 0, }}; /* * the following functions deal with sending IPIs between CPUs. @@ -485,15 +485,54 @@ do_flush_tlb_all_local(); } +static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED; +static task_t *new_task; + +/* + * This function sends a 'task migration' IPI to another CPU. + * Must be called from syscall contexts, with interrupts *enabled*. + */ +void smp_migrate_task(int cpu, task_t *p) +{ + /* + * The target CPU will unlock the migration spinlock: + */ + _raw_spin_lock(&migration_lock); + new_task = p; + send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR); +} + +/* + * Task migration callback. + */ +asmlinkage void smp_task_migration_interrupt(void) +{ + task_t *p; + + ack_APIC_irq(); + p = new_task; + _raw_spin_unlock(&migration_lock); + sched_task_migrated(p); +} /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing * anything. Worst case is that we lose a reschedule ... */ - void smp_send_reschedule(int cpu) { send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR); +} + +/* + * this function sends a reschedule IPI to all (other) CPUs. + * This should only be used if some 'global' task became runnable, + * such as a RT task, that must be handled now. The first CPU + * that manages to grab the task will run it. + */ +void smp_send_reschedule_all(void) +{ + send_IPI_allbutself(RESCHEDULE_VECTOR); } /* diff -Nru a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c --- a/arch/i386/kernel/smpboot.c Wed Feb 13 20:03:52 2002 +++ b/arch/i386/kernel/smpboot.c Wed Feb 13 20:03:52 2002 @@ -308,14 +308,14 @@ if (tsc_values[i] < avg) realdelta = -realdelta; - printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n", - i, realdelta); + printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n", i, realdelta); } sum += delta; } if (!buggy) printk("passed.\n"); + ; } static void __init synchronize_tsc_ap (void) @@ -365,7 +365,7 @@ * (This works even if the APIC is not enabled.) */ phys_id = GET_APIC_ID(apic_read(APIC_ID)); - cpuid = current->processor; + cpuid = cpu(); if (test_and_set_bit(cpuid, &cpu_online_map)) { printk("huh, phys CPU#%d, CPU#%d already present??\n", phys_id, cpuid); @@ -435,6 +435,7 @@ */ smp_store_cpu_info(cpuid); + disable_APIC_timer(); /* * Allow the master to continue. */ @@ -465,6 +466,7 @@ smp_callin(); while (!atomic_read(&smp_commenced)) rep_nop(); + enable_APIC_timer(); /* * low-memory mappings have been cleared, flush them from * the local TLBs too. @@ -803,16 +805,13 @@ if (!idle) panic("No idle process for CPU %d", cpu); - idle->processor = cpu; - idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ + init_idle(idle, cpu); map_cpu_to_boot_apicid(cpu, apicid); idle->thread.eip = (unsigned long) start_secondary; - del_from_runqueue(idle); unhash_process(idle); - init_tasks[cpu] = idle; /* start_eip had better be page-aligned! */ start_eip = setup_trampoline(); @@ -925,6 +924,7 @@ } cycles_t cacheflush_time; +unsigned long cache_decay_ticks; static void smp_tune_scheduling (void) { @@ -958,9 +958,13 @@ cacheflush_time = (cpu_khz>>10) * (cachesize<<10) / bandwidth; } + cache_decay_ticks = (long)cacheflush_time/cpu_khz * HZ / 1000; + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", (long)cacheflush_time/(cpu_khz/1000), ((long)cacheflush_time*100/(cpu_khz/1000)) % 100); + printk("task migration cache decay timeout: %ld msecs.\n", + (cache_decay_ticks + 1) * 1000 / HZ); } /* @@ -1020,8 +1024,7 @@ map_cpu_to_boot_apicid(0, boot_cpu_apicid); global_irq_holder = 0; - current->processor = 0; - init_idle(); + current->cpu = 0; smp_tune_scheduling(); /* diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c Wed Feb 13 20:03:37 2002 +++ b/arch/i386/kernel/traps.c Wed Feb 13 20:03:37 2002 @@ -271,6 +271,22 @@ { if (vm86 && regs->eflags & VM_MASK) goto vm86_trap; + +#ifdef CONFIG_PNPBIOS + if (regs->xcs == 0x60 || regs->xcs == 0x68) + { + extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp; + extern u32 pnp_bios_is_utter_crap; + pnp_bios_is_utter_crap = 1; + printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n"); + __asm__ volatile( + "movl %0, %%esp\n\t" + "jmp %1\n\t" + : "=a" (pnp_bios_fault_esp), "=b" (pnp_bios_fault_eip)); + panic("do_trap: can't hit this"); + } +#endif + if (!(regs->xcs & 3)) goto kernel_trap; @@ -694,6 +710,8 @@ * * Careful.. There are problems with IBM-designed IRQ13 behaviour. * Don't touch unless you *really* know how it works. + * + * Must be called with kernel preemption disabled. */ asmlinkage void math_state_restore(struct pt_regs regs) { diff -Nru a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c --- a/arch/i386/kernel/vm86.c Wed Feb 13 20:03:35 2002 +++ b/arch/i386/kernel/vm86.c Wed Feb 13 20:03:35 2002 @@ -16,6 +16,7 @@ #include #include #include +#include /* * Known problems: @@ -610,6 +611,14 @@ } read_unlock(&tasklist_lock); return ret; +} + +void release_x86_irqs(struct task_struct *task) +{ + int i; + for (i=3; i<16; i++) + if (vm86_irqs[i].tsk == task) + free_vm86_irq(i); } static inline void handle_irq_zombies(void) diff -Nru a/arch/i386/lib/dec_and_lock.c b/arch/i386/lib/dec_and_lock.c --- a/arch/i386/lib/dec_and_lock.c Wed Feb 13 20:03:40 2002 +++ b/arch/i386/lib/dec_and_lock.c Wed Feb 13 20:03:40 2002 @@ -8,6 +8,7 @@ */ #include +#include #include int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) diff -Nru a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c --- a/arch/i386/mm/fault.c Wed Feb 13 20:03:29 2002 +++ b/arch/i386/mm/fault.c Wed Feb 13 20:03:29 2002 @@ -86,8 +86,7 @@ out_of_memory: if (current->pid == 1) { - current->policy |= SCHED_YIELD; - schedule(); + yield(); goto survive; } goto bad_area; @@ -125,12 +124,6 @@ } } -void do_BUG(const char *file, int line) -{ - bust_spinlocks(1); - printk("kernel BUG at %s:%d!\n", file, line); -} - asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); extern unsigned long idt; @@ -342,8 +335,7 @@ out_of_memory: up_read(&mm->mmap_sem); if (tsk->pid == 1) { - tsk->policy |= SCHED_YIELD; - schedule(); + yield(); down_read(&mm->mmap_sem); goto survive; } diff -Nru a/arch/i386/mm/init.c b/arch/i386/mm/init.c --- a/arch/i386/mm/init.c Wed Feb 13 20:03:39 2002 +++ b/arch/i386/mm/init.c Wed Feb 13 20:03:39 2002 @@ -128,7 +128,6 @@ static inline void set_pte_phys (unsigned long vaddr, unsigned long phys, pgprot_t flags) { - pgprot_t prot; pgd_t *pgd; pmd_t *pmd; pte_t *pte; @@ -144,10 +143,8 @@ return; } pte = pte_offset(pmd, vaddr); - if (pte_val(*pte)) - pte_ERROR(*pte); - pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags); - set_pte(pte, mk_pte_phys(phys, prot)); + /* stored as-is, to permit clearing entries */ + set_pte(pte, mk_pte_phys(phys, flags)); /* * It's enough to flush this one mapping. diff -Nru a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c --- a/arch/i386/mm/ioremap.c Wed Feb 13 20:03:41 2002 +++ b/arch/i386/mm/ioremap.c Wed Feb 13 20:03:41 2002 @@ -161,3 +161,68 @@ if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long) addr)); } + +void __init *bt_ioremap(unsigned long phys_addr, unsigned long size) +{ + unsigned long offset, last_addr; + unsigned int nrpages; + enum fixed_addresses idx; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Don't remap the low PCI/ISA area, it's always mapped.. + */ + if (phys_addr >= 0xA0000 && last_addr < 0x100000) + return phys_to_virt(phys_addr); + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr) - phys_addr; + + /* + * Mappings have to fit in the FIX_BTMAP area. + */ + nrpages = size >> PAGE_SHIFT; + if (nrpages > NR_FIX_BTMAPS) + return NULL; + + /* + * Ok, go for it.. + */ + idx = FIX_BTMAP_BEGIN; + while (nrpages > 0) { + set_fixmap(idx, phys_addr); + phys_addr += PAGE_SIZE; + --idx; + --nrpages; + } + return (void*) (offset + fix_to_virt(FIX_BTMAP_BEGIN)); +} + +void __init bt_iounmap(void *addr, unsigned long size) +{ + unsigned long virt_addr; + unsigned long offset; + unsigned int nrpages; + enum fixed_addresses idx; + + virt_addr = (unsigned long)addr; + if (virt_addr < fix_to_virt(FIX_BTMAP_BEGIN)) + return; + offset = virt_addr & ~PAGE_MASK; + nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT; + + idx = FIX_BTMAP_BEGIN; + while (nrpages > 0) { + __set_fixmap(idx, 0, __pgprot(0)); + --idx; + --nrpages; + } +} diff -Nru a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c --- a/arch/ia64/ia32/sys_ia32.c Wed Feb 13 20:03:32 2002 +++ b/arch/ia64/ia32/sys_ia32.c Wed Feb 13 20:03:33 2002 @@ -3691,24 +3691,25 @@ return result; } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + /* Not __u64 because of alignment */ + __u32 dqb_curspace[2]; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; asmlinkage long -sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *addr) +sys32_quotactl (int cmd, unsigned int special, int id, struct mem_dqblk32 *addr) { - extern asmlinkage long sys_quotactl (int, const char *, int, caddr_t); + extern asmlinkage long sys_quotactl (int, const char *, int, __kernel_caddr_t); int cmds = cmd >> SUBCMDSHIFT; mm_segment_t old_fs; - struct dqblk d; + struct mem_dqblk d; char *spec; long err; @@ -3718,13 +3719,15 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, addr, sizeof(struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof(struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; + memcpy(&d.dqb_curspace, ((struct mem_dqblk32 *)&d)->dqb_curspace, sizeof(__u64)); break; default: - return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr); + return sys_quotactl(cmd, (void *) A(special), id, (__kernel_caddr_t) addr); } spec = getname32((void *) A(special)); err = PTR_ERR(spec); @@ -3732,17 +3735,22 @@ return err; old_fs = get_fs (); set_fs(KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs(old_fs); putname(spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user(addr, &d, sizeof(struct dqblk32))) + qsize_t s = d.dqb_curspace; + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + memcpy(((struct mem_dqblk32 *)&d)->dqb_curspace, &s, sizeof(__u64)); + if (copy_to_user((struct mem_dqblk32 *)addr, &d, + sizeof(struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } asmlinkage long diff -Nru a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c --- a/arch/ia64/kernel/perfmon.c Wed Feb 13 20:03:49 2002 +++ b/arch/ia64/kernel/perfmon.c Wed Feb 13 20:03:49 2002 @@ -251,7 +251,7 @@ #define DBprintk(a) \ do { \ if (pfm_debug >0) { printk(__FUNCTION__" %d: ", __LINE__); printk a; } \ - } while (0); + } while (0) static void ia64_reset_pmu(void); @@ -282,7 +282,7 @@ /* * helper macros */ -#define SET_PMU_OWNER(t) do { pmu_owners[smp_processor_id()].owner = (t); } while(0); +#define SET_PMU_OWNER(t) do { pmu_owners[smp_processor_id()].owner = (t); } while(0) #define PMU_OWNER() pmu_owners[smp_processor_id()].owner #ifdef CONFIG_SMP diff -Nru a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c --- a/arch/mips/kernel/signal.c Wed Feb 13 20:03:56 2002 +++ b/arch/mips/kernel/signal.c Wed Feb 13 20:03:56 2002 @@ -204,7 +204,7 @@ #define restore_gp_reg(i) do { \ err |= __get_user(reg, &sc->sc_regs[i]); \ regs->regs[i] = reg; \ -} while(0); +} while(0) restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); diff -Nru a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c --- a/arch/mips/sni/pci.c Wed Feb 13 20:03:55 2002 +++ b/arch/mips/sni/pci.c Wed Feb 13 20:03:55 2002 @@ -25,7 +25,7 @@ ((dev->bus->number & 0xff) << 0x10) | \ ((dev->devfn & 0xff) << 0x08) | \ (where & 0xfc); \ -} while(0); +} while(0) #if 0 /* To do: Bring this uptodate ... */ diff -Nru a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c --- a/arch/ppc/kernel/setup.c Wed Feb 13 20:03:50 2002 +++ b/arch/ppc/kernel/setup.c Wed Feb 13 20:03:50 2002 @@ -694,8 +694,12 @@ id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues); id->word92 = __le16_to_cpu(id->word92); id->hw_config = __le16_to_cpu(id->hw_config); - for (i = 0; i < 32; i++) - id->words94_125[i] = __le16_to_cpu(id->words94_125[i]); + id->acoustic = __le16_to_cpu(id->acoustic); + for (i = 0; i < 5; i++) + id->words95_99[i] = __le16_to_cpu(id->words95_99[i]); + id->lba_capacity_2 = __le64_to_cpu(id->lba_capacity_2); + for (i = 0; i < 22; i++) + id->words104_125[i] = __le16_to_cpu(id->words104_125[i]); id->last_lun = __le16_to_cpu(id->last_lun); id->word127 = __le16_to_cpu(id->word127); id->dlf = __le16_to_cpu(id->dlf); @@ -705,6 +709,12 @@ id->word156 = __le16_to_cpu(id->word156); for (i = 0; i < 3; i++) id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); - for (i = 0; i < 96; i++) - id->words160_255[i] = __le16_to_cpu(id->words160_255[i]); + id->cfa_power = __le16_to_cpu(id->cfa_power); + for (i = 0; i < 14; i++) + id->words161_175[i] = __le16_to_cpu(id->words161_175[i]); + for (i = 0; i < 31; i++) + id->words176_205[i] = __le16_to_cpu(id->words176_205[i]); + for (i = 0; i < 48; i++) + id->words206_254[i] = __le16_to_cpu(id->words206_254[i]); + id->integrity_word = __le16_to_cpu(id->integrity_word); } diff -Nru a/arch/ppc/math-emu/op-4.h b/arch/ppc/math-emu/op-4.h --- a/arch/ppc/math-emu/op-4.h Wed Feb 13 20:03:50 2002 +++ b/arch/ppc/math-emu/op-4.h Wed Feb 13 20:03:50 2002 @@ -279,7 +279,7 @@ X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \ X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \ - } while (0); + } while (0) #define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \ do { \ diff -Nru a/arch/sh/config.in b/arch/sh/config.in --- a/arch/sh/config.in Wed Feb 13 20:03:55 2002 +++ b/arch/sh/config.in Wed Feb 13 20:03:55 2002 @@ -124,6 +124,8 @@ hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 hex 'Physical memory size' CONFIG_MEMORY_SIZE 00400000 fi +bool 'Preemptible Kernel' CONFIG_PREEMPT +dep_bool 'Break selected locks' CONFIG_LOCK_BREAK $CONFIG_PREEMPT endmenu if [ "$CONFIG_SH_HP690" = "y" ]; then diff -Nru a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S --- a/arch/sh/kernel/entry.S Wed Feb 13 20:03:49 2002 +++ b/arch/sh/kernel/entry.S Wed Feb 13 20:03:49 2002 @@ -60,10 +60,18 @@ /* * These are offsets into the task-struct. */ -flags = 4 +preempt_count = 4 sigpending = 8 need_resched = 20 tsk_ptrace = 24 +flags = 84 + +/* + * These offsets are into irq_stat. + * (Find irq_cpustat_t in asm-sh/hardirq.h) + */ +local_irq_count = 8 +local_bh_count = 12 PT_TRACESYS = 0x00000002 PF_USEDFPU = 0x00100000 @@ -143,7 +151,7 @@ mov.l __INV_IMASK, r11; \ stc sr, r10; \ and r11, r10; \ - stc k_g_imask, r11; \ + stc k_g_imask, r11; \ or r11, r10; \ ldc r10, sr @@ -304,8 +312,8 @@ mov.l @(tsk_ptrace,r0), r0 ! Is current PTRACE_SYSCALL'd? mov #PT_TRACESYS, r1 tst r1, r0 - bt ret_from_syscall - bra syscall_ret_trace + bf syscall_ret_trace + bra ret_from_syscall nop .align 2 @@ -505,8 +513,6 @@ .long syscall_ret_trace __syscall_ret: .long syscall_ret -__INV_IMASK: - .long 0xffffff0f ! ~(IMASK) .align 2 @@ -518,7 +524,84 @@ .align 2 1: .long SYMBOL_NAME(schedule) +#ifdef CONFIG_PREEMPT + ! + ! Returning from interrupt during kernel mode: check if + ! preempt_schedule should be called. If need_resched flag + ! is set, preempt_count is zero, and we're not currently + ! in an interrupt handler (local irq or bottom half) then + ! call preempt_schedule. + ! + ! Increment preempt_count to prevent a nested interrupt + ! from reentering preempt_schedule, then decrement after + ! and drop through to regular interrupt return which will + ! jump back and check again in case such an interrupt did + ! come in (and didn't preempt due to preempt_count). + ! + ! NOTE: because we just checked that preempt_count was + ! zero before getting to the call, can't we use immediate + ! values (1 and 0) rather than inc/dec? Also, rather than + ! drop through to ret_from_irq, we already know this thread + ! is kernel mode, can't we go direct to ret_from_kirq? In + ! fact, with proper interrupt nesting and so forth could + ! the loop simply be on the need_resched w/o checking the + ! other stuff again? Optimize later... + ! + .align 2 +ret_from_kirq: + ! Nonzero preempt_count prevents scheduling + stc k_current, r1 + mov.l @(preempt_count,r1), r0 + cmp/eq #0, r0 + bf restore_all + ! Zero need_resched prevents scheduling + mov.l @(need_resched,r1), r0 + cmp/eq #0, r0 + bt restore_all + ! If in_interrupt(), don't schedule + mov.l __irq_stat, r1 + mov.l @(local_irq_count,r1), r0 + mov.l @(local_bh_count,r1), r1 + or r1, r0 + cmp/eq #0, r0 + bf restore_all + ! Allow scheduling using preempt_schedule + ! Adjust preempt_count and SR as needed. + stc k_current, r1 + mov.l @(preempt_count,r1), r0 ! Could replace this ... + add #1, r0 ! ... and this w/mov #1? + mov.l r0, @(preempt_count,r1) + STI() + mov.l __preempt_schedule, r0 + jsr @r0 + nop + /* CLI */ + stc sr, r0 + or #0xf0, r0 + ldc r0, sr + ! + stc k_current, r1 + mov.l @(preempt_count,r1), r0 ! Could replace this ... + add #-1, r0 ! ... and this w/mov #0? + mov.l r0, @(preempt_count,r1) + ! Maybe should bra ret_from_kirq, or loop over need_resched? + ! For now, fall through to ret_from_irq again... +#endif /* CONFIG_PREEMPT */ + ret_from_irq: + mov #OFF_SR, r0 + mov.l @(r0,r15), r0 ! get status register + shll r0 + shll r0 ! kernel space? +#ifndef CONFIG_PREEMPT + bt restore_all ! Yes, it's from kernel, go back soon +#else /* CONFIG_PREEMPT */ + bt ret_from_kirq ! From kernel: maybe preempt_schedule +#endif /* CONFIG_PREEMPT */ + ! + bra ret_from_syscall + nop + ret_from_exception: mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register @@ -564,6 +647,13 @@ .long SYMBOL_NAME(do_signal) __irq_stat: .long SYMBOL_NAME(irq_stat) +#ifdef CONFIG_PREEMPT +__preempt_schedule: + .long SYMBOL_NAME(preempt_schedule) +#endif /* CONFIG_PREEMPT */ +__INV_IMASK: + .long 0xffffff0f ! ~(IMASK) + .align 2 restore_all: @@ -679,7 +769,7 @@ __fpu_prepare_fd: .long SYMBOL_NAME(fpu_prepare_fd) __init_task_flags: - .long SYMBOL_NAME(init_task_union)+4 + .long SYMBOL_NAME(init_task_union)+flags __PF_USEDFPU: .long PF_USEDFPU #endif diff -Nru a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c --- a/arch/sh/kernel/irq.c Wed Feb 13 20:03:48 2002 +++ b/arch/sh/kernel/irq.c Wed Feb 13 20:03:48 2002 @@ -229,6 +229,14 @@ struct irqaction * action; unsigned int status; + /* + * At this point we're now about to actually call handlers, + * and interrupts might get reenabled during them... bump + * preempt_count to prevent any preemption while the handler + * called here is pending... + */ + preempt_disable(); + /* Get IRQ number */ asm volatile("stc r2_bank, %0\n\t" "shlr2 %0\n\t" @@ -298,8 +306,17 @@ desc->handler->end(irq); spin_unlock(&desc->lock); + if (softirq_pending(cpu)) do_softirq(); + + /* + * We're done with the handlers, interrupts should be + * currently disabled; decrement preempt_count now so + * as we return preemption may be allowed... + */ + preempt_enable_no_resched(); + return 1; } diff -Nru a/arch/sparc/kernel/sunos_ioctl.c b/arch/sparc/kernel/sunos_ioctl.c --- a/arch/sparc/kernel/sunos_ioctl.c Wed Feb 13 20:03:48 2002 +++ b/arch/sparc/kernel/sunos_ioctl.c Wed Feb 13 20:03:48 2002 @@ -39,8 +39,12 @@ { int ret = -EBADF; - if (fd >= SUNOS_NR_OPEN || !fcheck(fd)) + read_lock(¤t->files->file_lock); + if (fd >= SUNOS_NR_OPEN || !fcheck(fd)) { + read_unlock(¤t->files->file_lock); goto out; + } + read_unlock(¤t->files->file_lock); /* First handle an easy compat. case for tty ldisc. */ if(cmd == TIOCSETD) { diff -Nru a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c --- a/arch/sparc/mm/srmmu.c Wed Feb 13 20:03:30 2002 +++ b/arch/sparc/mm/srmmu.c Wed Feb 13 20:03:30 2002 @@ -1957,7 +1957,7 @@ iaddr = &(insn); \ daddr = &(dest); \ *iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \ - } while(0); + } while(0) static void __init patch_window_trap_handlers(void) { diff -Nru a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c --- a/arch/sparc/mm/sun4c.c Wed Feb 13 20:03:39 2002 +++ b/arch/sparc/mm/sun4c.c Wed Feb 13 20:03:39 2002 @@ -425,7 +425,7 @@ daddr = &(dst); \ iaddr = &(src); \ *daddr = *iaddr; \ - } while (0); + } while (0) static void patch_kernel_fault_handler(void) { diff -Nru a/arch/sparc64/config.in b/arch/sparc64/config.in --- a/arch/sparc64/config.in Wed Feb 13 20:03:44 2002 +++ b/arch/sparc64/config.in Wed Feb 13 20:03:44 2002 @@ -157,6 +157,7 @@ int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 fi + dep_tristate ' SCSI media changer support' CONFIG_CHR_DEV_SCH $CONFIG_SCSI dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' diff -Nru a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c --- a/arch/sparc64/kernel/binfmt_elf32.c Wed Feb 13 20:03:44 2002 +++ b/arch/sparc64/kernel/binfmt_elf32.c Wed Feb 13 20:03:44 2002 @@ -43,7 +43,7 @@ dest[34] = (unsigned int) src->tnpc; \ dest[35] = src->y; \ dest[36] = dest[37] = 0; /* XXX */ \ -} while(0); +} while(0) typedef struct { union { diff -Nru a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c --- a/arch/sparc64/kernel/ioctl32.c Wed Feb 13 20:03:47 2002 +++ b/arch/sparc64/kernel/ioctl32.c Wed Feb 13 20:03:47 2002 @@ -97,6 +97,8 @@ #include #include +#include + /* Use this to get at 32-bit user passed pointers. See sys_sparc32.c for description about these. */ #define A(__x) ((unsigned long)(__x)) @@ -3878,6 +3880,44 @@ return ((0 == ret) ? 0 : -EFAULT); } +struct changer_element_status32 { + int ces_type; + u32 ces_data; /* unsigned char * */ +}; + +#define CHIOGSTATUS32 _IOW('c', 8,struct changer_element_status32) + +static inline int +chio_gstatus(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct thread_struct *t = ¤t->thread; + struct changer_element_status32 *uarg = + (struct changer_element_status32 *)arg; + struct changer_element_status *karg; + u32 data; + int type; + int err = -ENOTTY; + + if (get_user(type,&uarg->ces_type) || + get_user(data,&uarg->ces_data)) + return -EFAULT; + + /* Put 64-bit struct on USER stack. We have to do this + since the length of the segment pointed to by ces_data + isn't known, and thus we can't allocate or copy it. + */ + karg = (struct changer_element_status *) + ((t->rwbuf_stkptrs[t->w_saved] - + sizeof(struct changer_element_status)) & (~0x08L)); + + put_user(type, &karg->ces_type); + put_user(A(data), &karg->ces_data); + + err = sys_ioctl(fd, CHIOGSTATUS, (unsigned long) karg); + + return err; +} + struct ioctl_trans { unsigned int cmd; unsigned int handler; @@ -4576,6 +4616,14 @@ COMPATIBLE_IOCTL(NBD_PRINT_DEBUG) COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS) COMPATIBLE_IOCTL(NBD_DISCONNECT) +/* SCSI Changer */ +COMPATIBLE_IOCTL(CHIOMOVE) +COMPATIBLE_IOCTL(CHIOEXCHANGE) +COMPATIBLE_IOCTL(CHIOPOSITION) +COMPATIBLE_IOCTL(CHIOGPICKER) +COMPATIBLE_IOCTL(CHIOSPICKER) +COMPATIBLE_IOCTL(CHIOGPARAMS) +COMPATIBLE_IOCTL(CHIOGELEM) /* And these ioctls need translation */ HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob) HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob) @@ -4756,6 +4804,8 @@ HANDLE_IOCTL(USBDEVFS_REAPURB32, do_usbdevfs_reapurb) HANDLE_IOCTL(USBDEVFS_REAPURBNDELAY32, do_usbdevfs_reapurb) HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal) +/* SCSI Changer */ +HANDLE_IOCTL(CHIOGSTATUS32, chio_gstatus) IOCTL_TABLE_END unsigned int ioctl32_hash_table[1024]; diff -Nru a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c --- a/arch/sparc64/kernel/sunos_ioctl32.c Wed Feb 13 20:03:50 2002 +++ b/arch/sparc64/kernel/sunos_ioctl32.c Wed Feb 13 20:03:50 2002 @@ -100,8 +100,12 @@ if(fd >= SUNOS_NR_OPEN) goto out; - if(!fcheck(fd)) + read_lock(¤t->files->file_lock); + if(!fcheck(fd)) { + read_unlock(¤t->files->file_lock); goto out; + } + read_unlock(¤t->files->file_lock); if(cmd == TIOCSETD) { mm_segment_t old_fs = get_fs(); diff -Nru a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c --- a/arch/sparc64/kernel/sys_sparc32.c Wed Feb 13 20:03:34 2002 +++ b/arch/sparc64/kernel/sys_sparc32.c Wed Feb 13 20:03:34 2002 @@ -887,24 +887,24 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr); asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -914,33 +914,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) diff -Nru a/arch/sparc64/solaris/ioctl.c b/arch/sparc64/solaris/ioctl.c --- a/arch/sparc64/solaris/ioctl.c Wed Feb 13 20:03:30 2002 +++ b/arch/sparc64/solaris/ioctl.c Wed Feb 13 20:03:30 2002 @@ -289,11 +289,15 @@ { struct inode *ino; /* I wonder which of these tests are superfluous... --patrik */ + read_lock(¤t->files->file_lock); if (! current->files->fd[fd] || ! current->files->fd[fd]->f_dentry || ! (ino = current->files->fd[fd]->f_dentry->d_inode) || - ! ino->i_sock) + ! ino->i_sock) { + read_unlock(¤t->files->file_lock); return TBADF; + } + read_unlock(¤t->files->file_lock); switch (cmd & 0xff) { case 109: /* SI_SOCKPARAMS */ diff -Nru a/arch/sparc64/solaris/timod.c b/arch/sparc64/solaris/timod.c --- a/arch/sparc64/solaris/timod.c Wed Feb 13 20:03:50 2002 +++ b/arch/sparc64/solaris/timod.c Wed Feb 13 20:03:50 2002 @@ -149,7 +149,9 @@ struct socket *sock; SOLD("wakeing socket"); + read_lock(¤t->files->file_lock); sock = ¤t->files->fd[fd]->f_dentry->d_inode->u.socket_i; + read_unlock(¤t->files->file_lock); wake_up_interruptible(&sock->wait); read_lock(&sock->sk->callback_lock); if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) @@ -163,7 +165,9 @@ struct sol_socket_struct *sock; SOLD("queuing primsg"); + read_lock(¤t->files->file_lock); sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + read_unlock(¤t->files->file_lock); it->next = sock->pfirst; sock->pfirst = it; if (!sock->plast) @@ -177,7 +181,9 @@ struct sol_socket_struct *sock; SOLD("queuing primsg at end"); + read_lock(¤t->files->file_lock); sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + read_unlock(¤t->files->file_lock); it->next = NULL; if (sock->plast) sock->plast->next = it; @@ -355,7 +361,11 @@ (int (*)(int, unsigned long *))SYS(socketcall); int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto); - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); + if (!filp) + return -EBADF; ino = filp->f_dentry->d_inode; sock = (struct sol_socket_struct *)filp->private_data; SOLD("entry"); @@ -636,7 +646,11 @@ SOLD("entry"); SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p)); - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); + if (!filp) + return -EBADF; ino = filp->f_dentry->d_inode; sock = (struct sol_socket_struct *)filp->private_data; SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); @@ -847,7 +861,9 @@ lock_kernel(); if(fd >= NR_OPEN) goto out; - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); if(!filp) goto out; ino = filp->f_dentry->d_inode; @@ -914,7 +930,9 @@ lock_kernel(); if(fd >= NR_OPEN) goto out; - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); if(!filp) goto out; ino = filp->f_dentry->d_inode; diff -Nru a/arch/um/Makefile b/arch/um/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/Makefile Wed Feb 13 20:04:02 2002 @@ -0,0 +1,110 @@ +include arch/$(ARCH)/Makefile-$(SUBARCH) + +EXTRAVERSION := $(EXTRAVERSION)-7um +include/linux/version.h: arch/$(ARCH)/Makefile + +# Recalculate MODLIB to reflect the EXTRAVERSION changes (via KERNELRELEASE) +# The way the toplevel Makefile is written EXTRAVERSION is not supposed +# to be changed outside the toplevel Makefile, but recalculating MODLIB is +# a sufficient workaround until we no longer need architecture dependent +# EXTRAVERSION... +MODLIB := $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) + +ARCH_DIR = arch/um + +MAKEBOOT = $(MAKE) -C $(ARCH_DIR)/boot + +ifeq ($(CONFIG_DEBUGSYM),y) +DEBUG = -g +CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) +endif + +ifeq ($(CONFIG_GCOV),y) +CFLAGS += -fprofile-arcs -ftest-coverage +endif + +ifeq ($(CONFIG_GPROF), y) +PROFILE += -pg -DPROFILING +LINK_PROFILE = $(PROFILE) -Wl,--wrap,__monstartup +endif + +SUBDIRS += $(ARCH_DIR)/fs $(ARCH_DIR)/drivers $(ARCH_DIR)/kernel \ + $(ARCH_DIR)/sys-$(SUBARCH) + +LIBS += $(shell [ -e $(ARCH_DIR)/fs/fs.o ] && echo $(ARCH_DIR)/fs/fs.o) \ + $(ARCH_DIR)/kernel/um.o $(ARCH_DIR)/drivers/um_drivers.o \ + $(ARCH_DIR)/sys-$(SUBARCH)/sys.o + +ifeq ($(CONFIG_PT_PROXY), y) +SUBDIRS += $(ARCH_DIR)/ptproxy +LIBS += $(ARCH_DIR)/ptproxy/ptproxy.a +endif + +NESTING = 0 + +ARCH_INCLUDE = $(TOPDIR)/$(ARCH_DIR)/include + +# -Derrno=kernel_errno - This turns all kernel references to errno into +# kernel_errno to separate them from the libc errno. This allows -fno-common +# in CFLAGS. Otherwise, it would cause ld to complain about the two different +# errnos. + +CFLAGS += $(DEBUG) $(PROFILE) $(ARCH_CFLAGS) -D__arch_um__ \ + -DSUBARCH=\"$(SUBARCH)\" -DNESTING=$(NESTING) -D_LARGEFILE64_SOURCE \ + -I$(ARCH_INCLUDE) -Derrno=kernel_errno -DPATCHLEVEL=$(PATCHLEVEL) + +LINKFLAGS += -r + +LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc + +$(ARCH_DIR)/link.ld: $(ARCH_DIR)/link.ld.in + m4 -DSTART=$(START_ADDR) -DSUBARCH=$(SUBARCH) \ + -DELF_SUBARCH=$(ELF_SUBARCH) $< > $@ + +SYMLINK_HEADERS = include/asm-um/archparam.h include/asm-um/system.h \ + include/asm-um/sigcontext.h include/asm-um/processor.h + +ARCH_SYMLINKS = include/asm-um/arch arch/um/include/sysdep $(SYMLINK_HEADERS) + +linux: $(ARCH_SYMLINKS) $(ARCH_DIR)/main.o vmlinux $(ARCH_DIR)/link.ld + mv vmlinux vmlinux.o + $(CC) -Wl,-T,$(ARCH_DIR)/link.ld $(LINK_PROFILE) $(LINK_WRAPS) \ + -o linux -static $(ARCH_DIR)/main.o vmlinux.o -L/usr/lib + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -Derrno=kernel_errno,,$(USER_CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) -I$(ARCH_INCLUDE) + +# To get a definition of F_SETSIG +USER_CFLAGS += -D_GNU_SOURCE + +$(ARCH_DIR)/main.o: $(ARCH_DIR)/main.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +archmrproper: + $(MAKE) -C $(ARCH_DIR)/sys-$(SUBARCH) archmrproper + rm -f $(SYMLINK_HEADERS) $(ARCH_SYMLINKS) include/asm \ + $(ARCH_DIR)/link.ld \ + $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS)) + +archclean: + find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ + -o -name '*.gcov' \) -type f -print | xargs rm -f + rm -f linux x.i gmon.out $(ARCH_DIR)/link.ld + @$(MAKEBOOT) clean + +archdep: $(ARCH_SYMLINKS) + @$(MAKEBOOT) dep + +$(SYMLINK_HEADERS): + echo $@ + cd $(TOPDIR)/$(dir $@) ; \ + ln -sf $(basename $(notdir $@))-$(SUBARCH)$(suffix $@) $(notdir $@) + +include/asm-um/arch: + cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch + +arch/um/include/sysdep: + cd $(TOPDIR)/arch/um/include && ln -sf sysdep-$(SUBARCH) sysdep + +export SUBARCH USER_CFLAGS diff -Nru a/arch/um/Makefile-i386 b/arch/um/Makefile-i386 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/Makefile-i386 Wed Feb 13 20:03:56 2002 @@ -0,0 +1,8 @@ +ifeq ($(CONFIG_HOST_2G_2G), y) +START_ADDR = 0x60000000 +else +START_ADDR = 0xa0000000 +endif + +ARCH_CFLAGS = -U__$(SUBARCH)__ -U$(SUBARCH) -DUM_FASTCALL +ELF_SUBARCH = $(SUBARCH) diff -Nru a/arch/um/Makefile-ia64 b/arch/um/Makefile-ia64 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/Makefile-ia64 Wed Feb 13 20:03:57 2002 @@ -0,0 +1 @@ +START_ADDR = 0x1000000000000000 diff -Nru a/arch/um/Makefile-ppc b/arch/um/Makefile-ppc --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/Makefile-ppc Wed Feb 13 20:04:01 2002 @@ -0,0 +1,9 @@ +ifeq ($(CONFIG_HOST_2G_2G), y) +START_ADDR = 0x60000000 +else +START_ADDR = 0xa0000000 +endif +ARCH_CFLAGS = -U__powerpc__ -D__UM_PPC__ + +# The arch is ppc, but the elf32 name is powerpc +ELF_SUBARCH = powerpc diff -Nru a/arch/um/boot/Makefile b/arch/um/boot/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/boot/Makefile Wed Feb 13 20:03:59 2002 @@ -0,0 +1,3 @@ +dep: + +clean: diff -Nru a/arch/um/config.in b/arch/um/config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/config.in Wed Feb 13 20:03:56 2002 @@ -0,0 +1,118 @@ +define_bool CONFIG_USERMODE y + +mainmenu_name "Linux/Usermode Kernel Configuration" + +define_bool CONFIG_ISA n +define_bool CONFIG_SBUS n +define_bool CONFIG_PCI n + +define_bool CONFIG_UID16 y + +define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'General Setup' +define_bool CONFIG_STDIO_CONSOLE y +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi +bool 'Virtual serial line' CONFIG_SSL +tristate 'Host filesystem' CONFIG_HOSTFS +bool 'Management console' CONFIG_MCONSOLE +dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_MCONSOLE +bool '2G/2G host address space split' CONFIG_HOST_2G_2G +bool 'Symmetric multi-processing support' CONFIG_UML_SMP +define_bool CONFIG_SMP $CONFIG_UML_SMP +string 'Default main console channel initialization' CONFIG_CON_ZERO_CHAN \ + "fd:0,fd:1" +string 'Default console channel initialization' CONFIG_CON_CHAN "xterm" +string 'Default serial line channel initialization' CONFIG_SSL_CHAN "pty" +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then +# MODVERSIONS does not yet work in this architecture +# bool ' Set version information on all module symbols' CONFIG_MODVERSIONS + bool ' Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'Devices' +define_bool CONFIG_BLK_DEV_UBD y +bool 'Always do synchronous disk IO for UBD' CONFIG_BLK_DEV_UBD_SYNC +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET +tristate 'RAM disk support' CONFIG_BLK_DEV_RAM +if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 +fi +dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM +tristate 'Example IO memory driver' CONFIG_MMAPPER + +tristate 'Sound support' CONFIG_UML_SOUND +define_tristate CONFIG_SOUND $CONFIG_UML_SOUND +define_tristate CONFIG_HOSTAUDIO $CONFIG_UML_SOUND + +bool 'file descriptor channel support' CONFIG_FD_CHAN +bool 'port channel support' CONFIG_PORT_CHAN +bool 'pty channel support' CONFIG_PTY_CHAN +bool 'tty channel support' CONFIG_TTY_CHAN +bool 'xterm channel support' CONFIG_XTERM_CHAN + +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Virtual network device support' CONFIG_UML_NET + if [ "$CONFIG_UML_NET" != "n" ]; then + bool ' Ethertap transport' CONFIG_UML_NET_ETHERTAP + bool ' TUN/TAP transport' CONFIG_UML_NET_TUNTAP + bool ' SLIP transport' CONFIG_UML_NET_SLIP + bool ' Daemon transport' CONFIG_UML_NET_DAEMON + bool ' Multicast transport' CONFIG_UML_NET_MCAST + fi + + bool 'Software network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + fi + + endmenu +fi + +source fs/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' +bool 'Debug memory allocations' CONFIG_DEBUG_SLAB +bool 'Enable kernel debugging symbols' CONFIG_DEBUGSYM +if [ "$CONFIG_XTERM_CHAN" = "y" ]; then + dep_bool 'Enable ptrace proxy' CONFIG_PT_PROXY $CONFIG_DEBUGSYM +else + define_bool CONFIG_PT_PROXY n +fi +dep_bool 'Enable gprof support' CONFIG_GPROF $CONFIG_DEBUGSYM +dep_bool 'Enable gcov support' CONFIG_GCOV $CONFIG_DEBUGSYM +endmenu diff -Nru a/arch/um/config.release b/arch/um/config.release --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/config.release Wed Feb 13 20:03:56 2002 @@ -0,0 +1,300 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +# CONFIG_ISA is not set +# CONFIG_SBUS is not set +# CONFIG_PCI is not set +CONFIG_UID16=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=y +CONFIG_MCONSOLE=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_HOST_2G_2G is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_KMOD=y + +# +# Devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_MMAPPER is not set + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_MCAST=y +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=y +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=y +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +CONFIG_SHAPER=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_BFS_FS=m +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_UMSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_EFS_FS=m +CONFIG_JFFS_FS=m +CONFIG_JFFS_FS_VERBOSE=0 +# CONFIG_JFFS_PROC_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_CRAMFS=m +CONFIG_TMPFS=y +CONFIG_RAMFS=m +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +CONFIG_MINIX_FS=m +CONFIG_VXFS_FS=m +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +CONFIG_HPFS_FS=m +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UDF_FS=m +# CONFIG_UDF_RW is not set +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +CONFIG_ZLIB_FS_INFLATE=m + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUGSYM is not set +# CONFIG_PT_PROXY is not set +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff -Nru a/arch/um/defconfig b/arch/um/defconfig --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/defconfig Wed Feb 13 20:03:57 2002 @@ -0,0 +1,302 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +# CONFIG_ISA is not set +# CONFIG_SBUS is not set +# CONFIG_PCI is not set +CONFIG_UID16=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=m +CONFIG_MCONSOLE=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_HOST_2G_2G is not set +# CONFIG_SMP is not set +CONFIG_CON_ZERO_CHAN="fd:0,fd:1" +CONFIG_CON_CHAN="xterm" +CONFIG_SSL_CHAN="pty" + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_KMOD is not set + +# +# Devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_MMAPPER is not set + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_MCAST=y +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +CONFIG_ETHERTAP=y + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +CONFIG_SLIP=y +# CONFIG_SLIP_COMPRESSED is not set +# CONFIG_SLIP_SMART is not set +# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_UMSDOS_FS=m +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +CONFIG_MINIX_FS=m +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +# CONFIG_ZLIB_FS_INFLATE is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUGSYM=y +CONFIG_PT_PROXY=y +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff -Nru a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/Makefile Wed Feb 13 20:04:01 2002 @@ -0,0 +1,48 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +OBJ := um_drivers.o + +CHAN_OBJS = chan_kern.o chan_user.o line.o + +OBJS-y = +OBJS-$(CONFIG_SSL) += ssl.o +OBJS-$(CONFIG_UML_NET_SLIP) += slip_kern.o slip_user.o +OBJS-$(CONFIG_UML_NET_ETHERTAP) += ethertap_kern.o ethertap_user.o +OBJS-$(CONFIG_UML_NET_TUNTAP) += tuntap_kern.o tuntap_user.o +OBJS-$(CONFIG_UML_NET_DAEMON) += daemon_kern.o daemon_user.o +OBJS-$(CONFIG_UML_NET_MCAST) += mcast_user.o mcast_kern.o +OBJS-$(CONFIG_UML_NET) += net_kern.o net_user.o +OBJS-$(CONFIG_MCONSOLE) += mconsole_kern.o mconsole_user.o +OBJS-$(CONFIG_MMAPPER) += mmapper_kern.o +OBJS-$(CONFIG_BLK_DEV_UBD) += ubd.o ubd_user.o +OBJS-$(CONFIG_HOSTAUDIO) += hostaudio_kern.o hostaudio_user.o +OBJS-$(CONFIG_FD_CHAN) += fd.o +OBJS-$(CONFIG_PORT_CHAN) += port.o port_kern.o +OBJS-$(CONFIG_PTY_CHAN) += pty.o +OBJS-$(CONFIG_TTY_CHAN) += tty.o +OBJS-$(CONFIG_XTERM_CHAN) += xterm.o + +OBJS = stdio_console.o $(OBJS-y) $(CHAN_OBJS) + +USER_OBJS = $(filter %_user.o,$(OBJS)) fd.o pty.o socket.o tty.o xterm.o + +all : $(OBJ) + +$(USER_OBJS) : %.o: %.c + $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $< + +$(OBJ): $(OBJS) $(export-objs) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +clean: + rm -f $(OBJS) $(export-objs) + +modules: + +fastdep: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/chan_kern.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include "chan_kern.h" +#include "user_util.h" +#include "kern.h" +#include "irq_user.h" + +static void tty_receive_char(struct tty_struct *tty, char ch) +{ + if(tty == NULL) return; + + if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) { + if(ch == STOP_CHAR(tty)){ + stop_tty(tty); + return; + } + else if(ch == START_CHAR(tty)){ + start_tty(tty); + return; + } + } + + if((tty->flip.flag_buf_ptr == NULL) || + (tty->flip.char_buf_ptr == NULL)) + return; + tty_insert_flip_char(tty, ch, TTY_NORMAL); +} + +static int open_one_chan(struct chan *chan, int input, int output) +{ + int fd; + + if(chan->opened) return(0); + fd = (*chan->ops->open)(input, output, chan->data); + if(fd < 0) return(-fd); + chan->fd = fd; + + chan->opened = 1; + return(0); +} + +int open_chan(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + int ret, err = 0; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + ret = open_one_chan(chan, chan->input, chan->output); + if(chan->primary) err = ret; + } + return(err); +} + +void enable_chan(struct list_head *chans, + int (*irq_setup)(int fd, int input, int output, void *data), + void *data) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->opened) continue; + + (*irq_setup)(chan->fd, chan->input, chan->output, data); + } +} + +void disable_chan(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->opened) continue; + + free_irq_by_fd(chan->fd); + } +} + +void close_chan(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + + /* Close in reverse order as open in case more than one of them + * refers to the same device and they save and restore that device's + * state. Then, the first one opened will have the original state, + * so it must be the last closed. + */ + list_for_each_prev(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->ops->close != NULL) + (*chan->ops->close)(chan->fd, chan->data); + free_irq_by_fd(chan->fd); + } +} + +int write_chan(struct list_head *chans, const char *buf, int len) +{ + struct list_head *ele; + struct chan *chan; + int n, ret = 0; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->output) continue; + n = chan->ops->write(chan->fd, buf, len, chan->data); + if(chan->primary) ret = n; + } + return(ret); +} + +int console_write_chan(struct list_head *chans, const char *buf, int len) +{ + struct list_head *ele; + struct chan *chan; + int n, ret = 0; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->output) continue; + n = chan->ops->console_write(chan->fd, buf, len, chan->data); + if(chan->primary) ret = n; + } + return(ret); +} + +int chan_window_size(struct list_head *chans, unsigned short *rows_out, + unsigned short *cols_out) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->primary) + return(chan->ops->window_size(chan->fd, chan->data, + rows_out, cols_out)); + } + return(0); +} + +void free_one_chan(struct chan *chan) +{ + list_del(&chan->list); + (*chan->ops->free)(chan->data); + free_irq_by_fd(chan->fd); + kfree(chan); +} + +void free_chan(struct list_head *chans) +{ + struct list_head *ele, *next; + struct chan *chan; + + list_for_each_safe(ele, next, chans){ + chan = list_entry(ele, struct chan, list); + free_one_chan(chan); + } +} + +struct chan_type { + char *key; + struct chan_ops *ops; +}; + +struct chan_type chan_table[] = { +#ifdef CONFIG_PTY_CHAN + { "pty", &pty_ops }, + { "pts", &pts_ops }, +#endif +#ifdef CONFIG_TTY_CHAN + { "tty", &tty_ops }, +#endif +#ifdef CONFIG_XTERM_CHAN + { "xterm", &xterm_ops }, +#endif +#ifdef CONFIG_FD_CHAN + { "fd", &fd_ops }, +#endif +#ifdef CONFIG_PORT_CHAN + { "port", &port_ops }, +#endif +}; + +static struct chan *parse_chan(char *str, int pri, int device, + struct chan_opts *opts) +{ + struct chan_type *entry; + struct chan_ops *ops; + struct chan *chan; + void *data; + int i; + + ops = NULL; + for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){ + entry = &chan_table[i]; + if(!strncmp(str, entry->key, strlen(entry->key))){ + ops = entry->ops; + str += strlen(entry->key); + break; + } + } + if(ops == NULL){ + printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n", str); + return(NULL); + } + + data = (*ops->init)(str, device, opts); + if(data == NULL) return(NULL); + chan = kmalloc(sizeof(*chan), GFP_KERNEL); + if(chan == NULL) return(NULL); + *chan = ((struct chan) { list : LIST_HEAD_INIT(chan->list), + primary : 1, + input : 0, + output : 0, + opened : 0, + fd : -1, + pri : pri, + ops : ops, + data : data }); + return(chan); +} + +int parse_chan_pair(char *str, struct list_head *chans, int pri, int device, + struct chan_opts *opts) +{ + struct chan *new, *chan; + char *in, *out; + + if(!list_empty(chans)){ + chan = list_entry(chans->next, struct chan, list); + if(chan->pri >= pri) return(0); + free_chan(chans); + INIT_LIST_HEAD(chans); + } + + if((out = strchr(str, ',')) != NULL){ + in = str; + *out = '\0'; + out++; + new = parse_chan(in, pri, device, opts); + if(new == NULL) return(-1); + new->input = 1; + list_add(&new->list, chans); + + new = parse_chan(out, pri, device, opts); + if(new == NULL) return(-1); + list_add(&new->list, chans); + new->output = 1; + } + else { + new = parse_chan(str, pri, device, opts); + if(new == NULL) return(-1); + list_add(&new->list, chans); + new->input = 1; + new->output = 1; + } + return(0); +} + +int chan_out_fd(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->primary && chan->output) + return(chan->fd); + } + return(-1); +} + +void chan_interrupt(struct list_head *chans, struct tty_struct *tty) +{ + struct list_head *ele; + struct chan *chan; + char c; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->input) continue; + do { + c = chan->ops->read(chan->fd, chan->data); + if(c > 0) tty_receive_char(tty, c); + } while(c > 0); + if(c == 0) reactivate_fd(chan->fd); + if(c == -EIO){ + chan->ops->close(chan->fd, chan->data); + chan->opened = 0; + if(chan->primary){ + if(tty != NULL) tty_hangup(tty); + free_chan(chans); + return; + } + else free_one_chan(chan); + } + } + if(tty) tty_flip_buffer_push(tty); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/chan_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "user.h" + +void generic_close(int fd, void *unused) +{ + close(fd); +} + +int generic_read(int fd, void *unused) +{ + int n; + char c; + + n = read(fd, &c, sizeof(c)); + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-EIO); + return(c); +} + +int generic_write(int fd, const char *buf, int n, void *unused) +{ + return(write(fd, buf, n)); +} + +int generic_console_write(int fd, const char *buf, int n, void *state) +{ + struct termios save, *orig = state; + int err; + + if(isatty(fd)){ + tcgetattr(fd, &save); + tcsetattr(fd, TCSADRAIN, orig); + } + err = generic_write(fd, buf, n, NULL); + if(isatty(fd)) tcsetattr(fd, TCSADRAIN, &save); + return(err); +} + +int generic_window_size(int fd, void *unused, unsigned short *rows_out, + unsigned short *cols_out) +{ + struct winsize size; + int ret = 0; + + if(ioctl(fd, TIOCGWINSZ, &size) == 0){ + ret = ((*rows_out != size.ws_row) || + (*cols_out != size.ws_col)); + *rows_out = size.ws_row; + *cols_out = size.ws_col; + } + return(ret); +} + +void generic_free(void *data) +{ + kfree(data); +} + +int getmaster(char *line) +{ + struct stat stb; + char *pty, *bank, *cp; + int master; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (stat(line, &stb) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = open(line, O_RDWR); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + int ok; + + /* verify slave side is usable */ + *tp = 't'; + ok = access(line, R_OK|W_OK) == 0; + *tp = 'p'; + if (ok) return(master); + (void) close(master); + } + } + } + return(-1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/daemon.h b/arch/um/drivers/daemon.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/daemon.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct daemon_data { + char *sock_type; + char *ctl_sock; + char *data_sock; + void *ctl_addr; + void *data_addr; + void *local_addr; + unsigned char hwaddr[ETH_ADDR_LEN]; + int hw_setup; + int control; + void *dev; +}; + +extern struct net_user_info daemon_user_info; + +extern int daemon_user_set_mac(struct daemon_data *pri, unsigned char *hwaddr, + int len); +extern int daemon_user_write(int fd, void *buf, int len, + struct daemon_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/daemon_kern.c Wed Feb 13 20:03:59 2002 @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "daemon.h" +#include "daemon_kern.h" + +struct daemon_data daemon_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + sock_type: "unix", + ctl_sock: "/tmp/uml.ctl", + data_sock: "/tmp/uml.data", + hwaddr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + control: -1, + } +}; + +struct net_device *daemon_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct daemon_data *dpri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + dpri = (struct daemon_data *) pri->user; + *dpri = daemon_priv[index]; + memcpy(dev->dev_addr, dpri->hwaddr, ETH_ALEN); + printk("daemon backend"); + if(dpri->hw_setup) + printk("- ethernet address = %x:%x:%x:%x:%x:%x\n", + dpri->hwaddr[0], dpri->hwaddr[1], dpri->hwaddr[2], + dpri->hwaddr[3], dpri->hwaddr[4], dpri->hwaddr[5]); + printk("\n"); + return(dev); +} + +static unsigned short daemon_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int daemon_set_mac(struct sockaddr *addr, void *data) +{ + struct daemon_data *pri = data; + struct net_device *dev = pri->dev; + struct sockaddr *hwaddr = addr; + + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + return(daemon_user_set_mac(pri, hwaddr->sa_data, ETH_ALEN)); +} + +static int daemon_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(net_recvfrom(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER)); +} + +static int daemon_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(daemon_user_write(fd, (*skb)->data, (*skb)->len, + (struct daemon_data *) &lp->user)); +} + +static struct net_kern_info daemon_kern_info = { + init: daemon_init, + protocol: daemon_protocol, + set_mac: daemon_set_mac, + read: daemon_read, + write: daemon_write, +}; + +static int daemon_count = 0; + +void daemon_setup(char *str, struct uml_net *dev) +{ + int err, n = daemon_count; + + dev->user = &daemon_user_info; + dev->kern = &daemon_kern_info; + dev->private_size = sizeof(struct daemon_data); + dev->transport_index = daemon_count++; + if(*str != ',') return; + str++; + if(*str != ','){ + err = setup_etheraddr(str, daemon_priv[n].hwaddr); + if(!err) daemon_priv[n].hw_setup = 1; + } + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ',') daemon_priv[n].sock_type = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ',') daemon_priv[n].ctl_sock = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != '\0') daemon_priv[n].data_sock = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/daemon_kern.h b/arch/um/drivers/daemon_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/daemon_kern.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,8 @@ +#ifndef __UM_DAEMON_KERN_H +#define __UM_DAEMON_KERN_H + +#include "net_kern.h" + +extern void daemon_setup(char *arg, struct uml_net *dev); + +#endif diff -Nru a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/daemon_user.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include +#include +#include +#include +#include +#include "net_user.h" +#include "daemon.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +enum request_type { REQ_NEW_CONTROL }; + +struct request { + enum request_type type; + union { + struct { + unsigned char addr[ETH_ADDR_LEN]; + struct sockaddr_un name; + } new_control; + struct { + unsigned long cookie; + } new_data; + } u; +}; + +static struct sockaddr_un *new_addr(void *name, int len) +{ + struct sockaddr_un *sun; + + sun = um_kmalloc(sizeof(struct sockaddr_un)); + if(sun == NULL){ + printk("new_addr: allocation of sockaddr_un failed\n"); + return(NULL); + } + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, name, len); + return(sun); +} + +static void daemon_user_init(void *data, void *dev) +{ + struct daemon_data *pri = data; + struct timeval tv; + struct { + char zero; + int pid; + int usecs; + } name; + + if(!strcmp(pri->sock_type, "unix")){ + pri->ctl_addr = new_addr(pri->ctl_sock, + strlen(pri->ctl_sock) + 1); + pri->data_addr = new_addr(pri->data_sock, + strlen(pri->data_sock) + 1); + } + name.zero = 0; + name.pid = getpid(); + gettimeofday(&tv, NULL); + name.usecs = tv.tv_usec; + pri->local_addr = new_addr(&name, sizeof(name)); + pri->dev = dev; +} + +static int daemon_open(void *data) +{ + struct daemon_data *pri = data; + struct sockaddr_un *ctl_addr = pri->ctl_addr; + struct sockaddr_un *local_addr = pri->local_addr; + struct request req; + char addr[sizeof("255.255.255.255\0")]; + int fd, n, err; + + if(!pri->hw_setup){ + pri->hwaddr[0] = 0xfe; + pri->hwaddr[1] = 0xfd; + pri->hwaddr[2] = 0x0; + pri->hwaddr[3] = 0x0; + pri->hwaddr[4] = 0x0; + pri->hwaddr[5] = 0x0; + dev_ip_addr(pri->dev, addr, &pri->hwaddr[2]); + set_ether_mac(pri->dev, pri->hwaddr); + } + if((ctl_addr == NULL) || (pri->data_addr == NULL) || + (pri->local_addr == NULL)) + return(-EINVAL); + + if((pri->control = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + printk("daemon_open : control socket failed, errno = %d\n", + errno); + return(-ENOMEM); + } + + if(connect(pri->control, (struct sockaddr *) ctl_addr, + sizeof(*ctl_addr)) < 0){ + printk("daemon_open : control connect failed, errno = %d\n", + errno); + err = -ENOTCONN; + goto out; + } + + req.type = REQ_NEW_CONTROL; + memcpy(req.u.new_control.addr, pri->hwaddr, + sizeof(req.u.new_control.addr)); + req.u.new_control.name = *local_addr; + n = write(pri->control, &req, sizeof(req)); + if(n != sizeof(req)){ + printk("daemon_open : control setup request returned %d, " + "errno = %d\n", n, errno); + err = -ENOTCONN; + goto out; + } + + if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){ + printk("daemon_open : data socket failed, errno = %d\n", + errno); + err = -ENOMEM; + goto out; + } + if(bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0){ + printk("daemon_open : data bind failed, errno = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + return(fd); + out: + close(pri->control); + return(err); +} + +static void daemon_close(int fd, void *data) +{ + struct daemon_data *pri = data; + + close(fd); + close(pri->control); +} + +int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) +{ + struct sockaddr_un *data_addr = pri->data_addr; + + return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); +} + +static int daemon_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +int daemon_user_set_mac(struct daemon_data *pri, unsigned char *hwaddr, + int len) +{ + memcpy(pri->hwaddr, hwaddr, len); + return(0); +} + +struct net_user_info daemon_user_info = { + init: daemon_user_init, + open: daemon_open, + close: daemon_close, + set_mtu: daemon_set_mtu, + add_address: NULL, + delete_address: NULL, + max_packet: MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/etap.h b/arch/um/drivers/etap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/etap.h Wed Feb 13 20:03:59 2002 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct ethertap_data { + char *dev_name; + char *gate_addr; + int data_fd; + int control_fd; + void *dev; + unsigned char hw_addr[ETH_ADDR_LEN]; + int hw_setup; +}; + +extern struct net_user_info ethertap_user_info; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/etap_kern.h b/arch/um/drivers/etap_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/etap_kern.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_ETHERTAP_KERN_H +#define __UM_ETHERTAP_KERN_H + +#include "net_kern.h" + +extern void ethertap_setup(char *arg, struct uml_net *dev); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/ethertap_kern.c b/arch/um/drivers/ethertap_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/ethertap_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "etap.h" +#include "etap_kern.h" + +struct ethertap_setup { + char *dev_name; + unsigned char hw_addr[ETH_ALEN]; + int hw_setup; + char *gate_addr; +}; + +struct ethertap_setup ethertap_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev_name: NULL, + hw_addr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + gate_addr: NULL, + } +}; + +struct net_device *etap_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct ethertap_data *epri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + epri = (struct ethertap_data *) pri->user; + epri->dev_name = ethertap_priv[index].dev_name; + epri->gate_addr = ethertap_priv[index].gate_addr; + memcpy(dev->dev_addr, ethertap_priv[index].hw_addr, ETH_ALEN); + memcpy(epri->hw_addr, ethertap_priv[index].hw_addr, + sizeof(epri->hw_addr)); + printk("ethertap backend - %s", epri->dev_name); + if(epri->gate_addr != NULL) + printk(", IP = %s", epri->gate_addr); + epri->hw_setup = ethertap_priv[index].hw_setup; + if(epri->hw_setup) + printk(", ether = %x:%x:%x:%x:%x:%x", + epri->hw_addr[0], epri->hw_addr[1], epri->hw_addr[2], + epri->hw_addr[3], epri->hw_addr[4], epri->hw_addr[5]); + printk("\n"); + epri->data_fd = -1; + epri->control_fd = -1; + return(dev); +} + +static unsigned short etap_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int etap_set_mac(struct sockaddr *addr, void *data) +{ + struct ethertap_data *pri = data; + struct sockaddr *hwaddr = addr; + + memcpy(pri->hw_addr, hwaddr->sa_data, ETH_ALEN); + + return 0; +} + +static int etap_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + int len; + + *skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP); + if(*skb == NULL) return(-ENOMEM); + len = net_recvfrom(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + 2 * ETH_HEADER_ETHERTAP); + if(len <= 0) return(len); + skb_pull(*skb, 2); + len -= 2; + return(len); +} + +static int etap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + if(skb_headroom(*skb) < 2){ + struct sk_buff *skb2; + + skb2 = skb_realloc_headroom(*skb, 2); + dev_kfree_skb(*skb); + if (skb2 == NULL) return(-ENOMEM); + *skb = skb2; + } + skb_push(*skb, 2); + return(net_send(fd, (*skb)->data, (*skb)->len)); +} + +struct net_kern_info ethertap_kern_info = { + init: etap_init, + protocol: etap_protocol, + set_mac: etap_set_mac, + read: etap_read, + write: etap_write, +}; + +static int ethertap_count = 0; + +void ethertap_setup(char *str, struct uml_net *dev) +{ + struct ethertap_setup *pri; + + dev->user = ðertap_user_info; + dev->kern = ðertap_kern_info; + dev->private_size = sizeof(struct ethertap_data); + pri = ðertap_priv[ethertap_count]; + dev->transport_index = ethertap_count++; + tap_setup_common(str, "ethertap", &pri->dev_name, pri->hw_addr, + &pri->hw_setup, &pri->gate_addr); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/ethertap_user.c b/arch/um/drivers/ethertap_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/ethertap_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "kern_util.h" +#include "net_user.h" +#include "etap.h" + +#define MAX_PACKET ETH_MAX_PACKET + +void etap_user_init(void *data, void *dev) +{ + struct ethertap_data *pri = data; + + pri->dev = dev; +} + +struct etap_open_data { + char *name; + char *gate; + int data_remote; + int data_me; + int control_remote; + int control_me; + int err; + char *output; +}; + +struct addr_change { + enum { ADD_ADDR, DEL_ADDR } what; + unsigned char addr[4]; + unsigned char netmask[4]; +}; + +static void etap_change(int op, unsigned char *addr, unsigned char *netmask, + int fd) +{ + struct addr_change change; + char *output; + + change.what = op; + memcpy(change.addr, addr, sizeof(change.addr)); + memcpy(change.netmask, netmask, sizeof(change.netmask)); + if(write(fd, &change, sizeof(change)) != sizeof(change)) + printk("etap_change - request failed, errno = %d\n", + errno); + if(!read_output(fd, &output)) printk("%s", output); +} + +static void etap_open_addr(unsigned char *addr, unsigned char *netmask, + void *arg) +{ + etap_change(ADD_ADDR, addr, netmask, *((int *) arg)); +} + +static void etap_close_addr(unsigned char *addr, unsigned char *netmask, + void *arg) +{ + etap_change(DEL_ADDR, addr, netmask, *((int *) arg)); +} + +static void etap_tramp(void *arg) +{ + struct etap_open_data *data = arg; + int pid, status; + char version_buf[sizeof("nnnnn\0")]; + char data_fd_buf[sizeof("nnnnnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char *setup_args[] = { "uml_net", version_buf, "ethertap", data->name, + data_fd_buf, gate_buf, NULL }; + char *nosetup_args[] = { "uml_net", version_buf, "ethertap", + data->name, data_fd_buf, NULL }; + char **args, c; + + sprintf(data_fd_buf, "%d", data->data_remote); + sprintf(version_buf, "%d", UML_NET_VERSION); + if(data->gate != NULL){ + strcpy(gate_buf, data->gate); + args = setup_args; + } + else args = nosetup_args; + data->err = 0; + if((pid = fork()) == 0){ + dup2(data->control_remote, 1); + close(data->data_me); + close(data->control_me); + execvp(args[0], args); + printk("Exec of '%s' failed - errno = %d\n", args[0], errno); + exit(1); + } + else if(pid < 0) data->err = errno; + close(data->data_remote); + close(data->control_remote); + data->output = NULL; + if(read(data->control_me, &c, sizeof(c)) != sizeof(c)){ + printk("etap_tramp : read of status failed, errno = %d\n", + errno); + data->err = EINVAL; + return; + } + if(c != 1){ + printk("etap_tramp : uml_net failed\n"); + data->err = EINVAL; + if(waitpid(pid, &status, 0) < 0) data->err = errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)){ + printk("uml_net didn't exit with status 1\n"); + } + return; + } + if(read_output(data->control_me, &data->output)) + data->err = EINVAL; +} + +static int etap_open(void *data) +{ + struct ethertap_data *pri = data; + struct etap_open_data tap_data; + int data_fds[2], control_fds[2], err; + + err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr); + if(err) return(err); + + tap_data.name = pri->dev_name; + + if(socketpair(PF_UNIX, SOCK_DGRAM, 0, data_fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + tap_data.data_remote = data_fds[1]; + tap_data.data_me = data_fds[0]; + + if(socketpair(PF_UNIX, SOCK_STREAM, 0, control_fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + tap_data.control_remote = control_fds[1]; + tap_data.control_me = control_fds[0]; + + tap_data.gate = pri->gate_addr; + tracing_cb(etap_tramp, &tap_data); + if(tap_data.output){ + printk("%s", tap_data.output); + kfree(tap_data.output); + } + if(tap_data.err != 0){ + printk("etap_tramp failed - errno = %d\n", tap_data.err); + return(-tap_data.err); + } + pri->data_fd = data_fds[0]; + pri->control_fd = control_fds[0]; + iter_addresses(pri->dev, etap_open_addr, &pri->control_fd); + return(data_fds[0]); +} + +static void etap_close(int fd, void *data) +{ + struct ethertap_data *pri = data; + + iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); + close(fd); + shutdown(pri->data_fd, SHUT_RDWR); + close(pri->data_fd); + pri->data_fd = -1; + close(pri->control_fd); + pri->control_fd = -1; +} + +static int etap_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +static void etap_add_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct ethertap_data *pri = data; + + if(pri->control_fd == -1) return; + etap_open_addr(addr, netmask, &pri->control_fd); +} + +static void etap_del_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct ethertap_data *pri = data; + + if(pri->control_fd == -1) return; + etap_close_addr(addr, netmask, &pri->control_fd); +} + +struct net_user_info ethertap_user_info = { + init: etap_user_init, + open: etap_open, + close: etap_close, + set_mtu: etap_set_mtu, + add_address: etap_add_addr, + delete_address: etap_del_addr, + max_packet: MAX_PACKET - ETH_HEADER_ETHERTAP +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/fd.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include "user.h" +#include "user_util.h" +#include "chan_user.h" + +struct fd_chan { + int fd; + int raw; + struct termios tt; +}; + +void *fd_init(char *str, int device, struct chan_opts *opts) +{ + struct fd_chan *data; + char *end; + int n; + + if(*str != ':'){ + printk("fd_init : channel type 'fd' must specify a file " + "descriptor\n"); + return(NULL); + } + str++; + n = strtoul(str, &end, 0); + if(*end != '\0'){ + printk("fd_init : couldn't parse file descriptor '%s'\n", str); + return(NULL); + } + if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + *data = ((struct fd_chan) { fd : n, + raw : opts->raw }); + return(data); +} + +int fd_open(int input, int output, void *d) +{ + struct fd_chan *data = d; + + if(data->raw && isatty(data->fd)){ + tcgetattr(data->fd, &data->tt); + raw(data->fd, 0); + } + return(data->fd); +} + +void fd_close(int fd, void *d) +{ + struct fd_chan *data = d; + + if(data->raw && isatty(fd)){ + tcsetattr(fd, TCSADRAIN, &data->tt); + data->raw = 0; + } +} + +int fd_console_write(int fd, const char *buf, int n, void *d) +{ + struct fd_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops fd_ops = { + init: fd_init, + open: fd_open, + close: fd_close, + read: generic_read, + write: generic_write, + console_write: fd_console_write, + window_size: generic_window_size, + free: generic_free, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/hostaudio_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2002 Steve Schmidtke + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/module.h" +#include "linux/version.h" +#include "linux/init.h" +#include "linux/slab.h" +#include "linux/fs.h" +#include "linux/sound.h" +#include "linux/soundcard.h" +#include "init.h" +#include "hostaudio.h" + +char *dsp = HOSTAUDIO_DEV_DSP; +char *mixer = HOSTAUDIO_DEV_MIXER; + +static int set_dsp(char *name, int *add) +{ + dsp = name; + return(0); +} + +__uml_setup("dsp=", set_dsp, +"dsp=\n" +" This is used to specify the host dsp device to the hostaudio driver.\n" +" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n" +); + +static int set_mixer(char *name, int *add) +{ + mixer = name; + return(0); +} + +__uml_setup("mixer=", set_mixer, +"mixer=\n" +" This is used to specify the host mixer device to the hostaudio driver.\n" +" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n" +); + +/* /dev/dsp file operations */ + +static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct hostaudio_state *state = file->private_data; + +#ifdef DEBUG + printk("hostaudio: read called, count = %d\n", count); +#endif + + return(hostaudio_read_user(state, buffer, count, ppos)); +} + +static ssize_t hostaudio_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct hostaudio_state *state = file->private_data; + +#ifdef DEBUG + printk("hostaudio: write called, count = %d\n", count); +#endif + return(hostaudio_write_user(state, buffer, count, ppos)); +} + +static unsigned int hostaudio_poll(struct file *file, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + +#ifdef DEBUG + printk("hostaudio: poll called (unimplemented)\n"); +#endif + + return(mask); +} + +static int hostaudio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hostaudio_state *state = file->private_data; + +#ifdef DEBUG + printk("hostaudio: ioctl called, cmd = %u\n", cmd); +#endif + + return(hostaudio_ioctl_user(state, cmd, arg)); +} + +static int hostaudio_open(struct inode *inode, struct file *file) +{ + struct hostaudio_state *state; + int r = 0, w = 0; + int ret; + +#ifdef DEBUG + printk("hostaudio: open called (host: %s)\n", dsp); +#endif + + state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL); + if(state == NULL) return(-ENOMEM); + + if(file->f_mode & FMODE_READ) r = 1; + if(file->f_mode & FMODE_WRITE) w = 1; + + ret = hostaudio_open_user(state, r, w, dsp); + if(ret < 0){ + kfree(state); + return(ret); + } + + file->private_data = state; + return(0); +} + +static int hostaudio_release(struct inode *inode, struct file *file) +{ + struct hostaudio_state *state = file->private_data; + int ret; + +#ifdef DEBUG + printk("hostaudio: release called\n"); +#endif + + ret = hostaudio_release_user(state); + kfree(state); + + return(ret); +} + +/* /dev/mixer file operations */ + +static int hostmixer_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hostmixer_state *state = file->private_data; + +#ifdef DEBUG + printk("hostmixer: ioctl called\n"); +#endif + + return(hostmixer_ioctl_mixdev_user(state, cmd, arg)); +} + +static int hostmixer_open_mixdev(struct inode *inode, struct file *file) +{ + struct hostmixer_state *state; + int r = 0, w = 0; + int ret; + +#ifdef DEBUG + printk("hostmixer: open called (host: %s)\n", mixer); +#endif + + state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL); + if(state == NULL) return(-ENOMEM); + + if(file->f_mode & FMODE_READ) r = 1; + if(file->f_mode & FMODE_WRITE) w = 1; + + ret = hostmixer_open_mixdev_user(state, r, w, mixer); + + if(ret < 0){ + kfree(state); + return(ret); + } + + file->private_data = state; + return(0); +} + +static int hostmixer_release(struct inode *inode, struct file *file) +{ + struct hostmixer_state *state = file->private_data; + int ret; + +#ifdef DEBUG + printk("hostmixer: release called\n"); +#endif + + ret = hostmixer_release_mixdev_user(state); + kfree(state); + + return(ret); +} + + +/* kernel module operations */ + +static struct file_operations hostaudio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: hostaudio_read, + write: hostaudio_write, + poll: hostaudio_poll, + ioctl: hostaudio_ioctl, + mmap: NULL, + open: hostaudio_open, + release: hostaudio_release, +}; + +static struct file_operations hostmixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: hostmixer_ioctl_mixdev, + open: hostmixer_open_mixdev, + release: hostmixer_release, +}; + +struct { + int dev_audio; + int dev_mixer; +} module_data; + +MODULE_AUTHOR("Steve Schmidtke"); +MODULE_DESCRIPTION("UML Audio Relay"); +MODULE_LICENSE("GPL"); + +static int __init hostaudio_init_module(void) +{ + printk(KERN_INFO "UML Audio Relay: " __DATE__ " " __TIME__ "\n"); + + module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1); + if(module_data.dev_audio < 0){ + printk(KERN_ERR "hostaudio: couldn't register DSP device!\n"); + return -ENODEV; + } + + module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1); + if(module_data.dev_mixer < 0){ + printk(KERN_ERR "hostmixer: couldn't register mixer " + "device!\n"); + unregister_sound_dsp(module_data.dev_audio); + return -ENODEV; + } + + return 0; +} + +static void __exit hostaudio_cleanup_module (void) +{ + unregister_sound_mixer(module_data.dev_mixer); + unregister_sound_dsp(module_data.dev_audio); +} + +module_init(hostaudio_init_module); +module_exit(hostaudio_cleanup_module); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/hostaudio_user.c b/arch/um/drivers/hostaudio_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/hostaudio_user.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2002 Steve Schmidtke + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include "hostaudio.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +/* /dev/dsp file operations */ + +ssize_t hostaudio_read_user(struct hostaudio_state *state, char *buffer, + size_t count, loff_t *ppos) +{ + ssize_t ret; + +#ifdef DEBUG + printk("hostaudio: read_user called, count = %d\n", count); +#endif + + ret = read(state->fd, buffer, count); + + if(ret < 0) return(-errno); + return(ret); +} + +ssize_t hostaudio_write_user(struct hostaudio_state *state, const char *buffer, + size_t count, loff_t *ppos) +{ + ssize_t ret; + +#ifdef DEBUG + printk("hostaudio: write_user called, count = %d\n", count); +#endif + + ret = write(state->fd, buffer, count); + + if(ret < 0) return(-errno); + return(ret); +} + +int hostaudio_ioctl_user(struct hostaudio_state *state, unsigned int cmd, + unsigned long arg) +{ + int ret; +#ifdef DEBUG + printk("hostaudio: ioctl_user called, cmd = %u\n", cmd); +#endif + + ret = ioctl(state->fd, cmd, arg); + + if(ret < 0) return(-errno); + return(ret); +} + +int hostaudio_open_user(struct hostaudio_state *state, int r, int w, char *dsp) +{ + int flags = 0; + +#ifdef DEBUG + printk("hostaudio: open_user called\n"); +#endif + + if(r && !w) flags = O_RDONLY; + else if(!r && w) flags = O_WRONLY; + else if(r && w) flags = O_RDWR; + + state->fd = open(dsp, flags); + + if(state->fd >= 0) return(0); + + printk("hostaudio_open_user failed to open '%s', errno = %d\n", + dsp, errno); + + return(-errno); +} + +int hostaudio_release_user(struct hostaudio_state *state) +{ +#ifdef DEBUG + printk("hostaudio: release called\n"); +#endif + if(state->fd >= 0){ + close(state->fd); + state->fd=-1; + } + + return(0); +} + +/* /dev/mixer file operations */ + +int hostmixer_ioctl_mixdev_user(struct hostmixer_state *state, + unsigned int cmd, unsigned long arg) +{ + int ret; +#ifdef DEBUG + printk("hostmixer: ioctl_user called cmd = %u\n",cmd); +#endif + + ret = ioctl(state->fd, cmd, arg); + if(ret < 0) + return(-errno); + return(ret); +} + +int hostmixer_open_mixdev_user(struct hostmixer_state *state, int r, int w, + char *mixer) +{ + int flags = 0; + +#ifdef DEBUG + printk("hostmixer: open_user called\n"); +#endif + + if(r && !w) flags = O_RDONLY; + else if(!r && w) flags = O_WRONLY; + else if(r && w) flags = O_RDWR; + + state->fd = open(mixer, flags); + + if(state->fd >= 0) return(0); + + printk("hostaudio_open_mixdev_user failed to open '%s', errno = %d\n", + mixer, errno); + + return(-errno); +} + +int hostmixer_release_mixdev_user(struct hostmixer_state *state) +{ +#ifdef DEBUG + printk("hostmixer: release_user called\n"); +#endif + + if(state->fd >= 0){ + close(state->fd); + state->fd = -1; + } + + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/line.c b/arch/um/drivers/line.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/line.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "chan_kern.h" +#include "line.h" + +void line_interrupt(int irq, void *data, struct pt_regs *unused) +{ + struct line *dev = data; + + if(dev->count > 0) chan_interrupt(&dev->chan_list, dev->tty); +} + +int line_open(struct line *lines, int n, struct tty_struct *tty, + int (*setup_irq)(int fd, int input, int output, void *data), + struct chan_opts *opts) +{ + struct line *line = &lines[n]; + int err = 0; + + down(&line->sem); + if(line->count == 0){ + if(list_empty(&line->chan_list)){ + err = parse_chan_pair(line->init_str, &line->chan_list, + line->init_pri, n, opts); + if(err) goto out; + err = open_chan(&line->chan_list); + if(err) goto out; + } + enable_chan(&line->chan_list, setup_irq, line); + } + /* This is outside the if because the initial console is opened + * with tty == NULL + */ + line->tty = tty; + if(tty != NULL) tty->driver_data = line; + + line->count++; + out: + up(&line->sem); + return(err); +} + +void line_close(struct line *lines, int n) +{ + struct line *line = &lines[n]; + + line->count--; + if(line->count == 0){ + disable_chan(&line->chan_list); + line->tty = NULL; + } +} + +void line_setup(struct line *lines, int num, char *init) +{ + int i, n; + char *end; + + if(*init == '=') n = -1; + else { + n = simple_strtoul(init, &end, 0); + if(*end != '='){ + printk(KERN_ERR "line_setup failed to parse \"%s\"\n", + init); + return; + } + init = end; + } + init++; + if(n == -1){ + for(i = 0; i < num; i++){ + if(lines[i].init_pri <= INIT_ALL){ + lines[i].init_str = init; + lines[i].init_pri = INIT_ALL; + } + } + } + else if(lines[n].init_pri <= INIT_ONE){ + lines[n].init_str = init; + lines[n].init_pri = INIT_ONE; + } +} + +struct list_head winch_regs = LIST_HEAD_INIT(winch_regs); + +void register_winch(struct winch_lines *lines) +{ + list_add(&lines->list, &winch_regs); +} + +void run_winch_handlers(void) +{ + struct list_head *ele; + struct winch_lines *lines; + struct line *line; + int i; + + list_for_each(ele, &winch_regs){ + lines = list_entry(ele, struct winch_lines, list); + for(i = 0; i < lines->nlines; i++){ + line = &lines->lines[i]; + if((line->count > 0) && (line->tty != NULL) && + chan_window_size(&line->chan_list, + &line->tty->winsize.ws_row, + &line->tty->winsize.ws_col)) + kill_pg(line->tty->pgrp, SIGWINCH, 1); + } + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mcast.h b/arch/um/drivers/mcast.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mcast.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct mcast_data { + char *addr; + unsigned short port; + void *mcast_addr; + int ttl; + unsigned char hwaddr[ETH_ADDR_LEN]; + int hw_setup; + void *dev; +}; + +extern struct net_user_info mcast_user_info; + +extern int mcast_user_set_mac(struct mcast_data *pri, unsigned char *hwaddr, + int len); +extern int mcast_user_write(int fd, void *buf, int len, + struct mcast_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mcast_kern.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,167 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + */ + +#include "linux/kernel.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "linux/in.h" +#include "linux/inet.h" +#include "net_kern.h" +#include "net_user.h" +#include "mcast.h" +#include "mcast_kern.h" + +struct mcast_data mcast_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + addr: "239.192.168.1", + port: 1102, + ttl: 1, + hwaddr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + } +}; + +struct net_device *mcast_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct mcast_data *dpri; + + dev = init_etherdev(NULL, private_size); + if (!dev) + return NULL; + + pri = dev->priv; + dpri = (struct mcast_data *) pri->user; + *dpri = mcast_priv[index]; + memcpy(dev->dev_addr, dpri->hwaddr, ETH_ALEN); + printk("mcast backend "); + if(dpri->hw_setup) + printk("ethernet address=%x:%x:%x:%x:%x:%x ", + dpri->hwaddr[0], dpri->hwaddr[1], dpri->hwaddr[2], + dpri->hwaddr[3], dpri->hwaddr[4], dpri->hwaddr[5]); + + printk("multicast adddress: %s:%u, TTL:%u ", + dpri->addr, dpri->port, dpri->ttl); + + printk("\n"); + return(dev); +} + +static unsigned short mcast_protocol(struct sk_buff *skb) +{ + return eth_type_trans(skb, skb->dev); +} + +static int mcast_set_mac(struct sockaddr *addr, void *data) +{ + struct mcast_data *pri = data; + struct net_device *dev = pri->dev; + struct sockaddr *hwaddr = addr; + + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + return mcast_user_set_mac(pri, hwaddr->sa_data, ETH_ALEN); +} + +static int mcast_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(net_recvfrom(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER)); +} + +static int mcast_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return mcast_user_write(fd, (*skb)->data, (*skb)->len, + (struct mcast_data *) &lp->user); +} + +static struct net_kern_info mcast_kern_info = { + init: mcast_init, + protocol: mcast_protocol, + set_mac: mcast_set_mac, + read: mcast_read, + write: mcast_write, +}; + +static int mcast_count = 0; + +void mcast_setup(char *str, struct uml_net *dev) +{ + int err, n = mcast_count; + int num = 0; + char *p1, *p2; + + dev->user = &mcast_user_info; + dev->kern = &mcast_kern_info; + dev->private_size = sizeof(struct mcast_data); + dev->transport_index = mcast_count++; + + + /* somewhat more sophisticated parser, needed for in_aton */ + + p1 = str; + if (*str == ',') + p1++; + while (p1 && *p1) { + if ((p2 = strchr(p1, ','))) + *p2++ = '\0'; + if (strlen(p1) > 0) { + switch (num) { + case 0: + /* First argument: Ethernet address */ + err = setup_etheraddr(p1, + mcast_priv[n].hwaddr); + if (!err) + mcast_priv[n].hw_setup = 1; + break; + case 1: + /* Second argument: Multicast group */ + mcast_priv[n].addr = p1; + break; + case 2: + /* Third argument: Port number */ + mcast_priv[n].port = + htons(simple_strtoul(p1, NULL, 10)); + break; + case 3: + /* Fourth argument: TTL */ + mcast_priv[n].ttl = + simple_strtoul(p1, NULL, 10); + break; + } + } + p1 = p2; + num++; + } + + printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", + mcast_priv[n].addr, mcast_priv[n].port, + mcast_priv[n].ttl); + + return; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mcast_kern.h b/arch/um/drivers/mcast_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mcast_kern.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,8 @@ +#ifndef __UM_MCAST_KERN_H +#define __UM_MCAST_KERN_H + +#include "net_kern.h" + +extern void mcast_setup(char *arg, struct uml_net *dev); + +#endif diff -Nru a/arch/um/drivers/mcast_user.c b/arch/um/drivers/mcast_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mcast_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,194 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "net_user.h" +#include "mcast.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +static struct sockaddr_in *new_addr(char *addr, unsigned short port) +{ + struct sockaddr_in *sin; + + sin = um_kmalloc(sizeof(struct sockaddr_in)); + if(sin == NULL){ + printk("new_addr: allocation of sockaddr_in failed\n"); + return(NULL); + } + sin->sin_addr.s_addr = in_aton(addr); + sin->sin_port = port; + return(sin); +} + +static void mcast_user_init(void *data, void *dev) +{ + struct mcast_data *pri = data; + + pri->mcast_addr = new_addr(pri->addr, pri->port); + pri->dev = dev; +} + +static int mcast_open(void *data) +{ + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + struct ip_mreq mreq; + char addr[sizeof("255.255.255.255\0")]; + int fd, err, yes = 1; + + + if(!pri->hw_setup){ + pri->hwaddr[0] = 0xfe; + pri->hwaddr[1] = 0xfd; + pri->hwaddr[2] = 0x0; + pri->hwaddr[3] = 0x0; + pri->hwaddr[4] = 0x0; + pri->hwaddr[5] = 0x0; + dev_ip_addr(pri->dev, addr, &pri->hwaddr[2]); + set_ether_mac(pri->dev, pri->hwaddr); + } + + if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0)) { + err = -EINVAL; + goto out; + } + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ + printk("mcast_open : data socket failed, errno = %d\n", + errno); + err = -ENOMEM; + goto out; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + printk("mcast_open: SO_REUSEADDR failed, errno = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* set ttl according to config */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl, + sizeof(pri->ttl)) < 0) { + printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* set LOOP, so data does get fed back to local sockets */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n", + errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* bind socket to mcast address */ + if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) { + printk("mcast_open : data bind failed, errno = %d\n", errno); + close(fd); + err = -EINVAL; + goto out; + } + + /* subscribe to the multicast group */ + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n", + errno); + printk("There appears not to be a multicast-capable network" + "interface on the host.\n"); + printk("eth0 should be configured in order to use the " + "multicast transport.\n"); + close(fd); + err = -EINVAL; + goto out; + } + + return(fd); + out: + return(err); +} + +static void mcast_close(int fd, void *data) +{ + struct ip_mreq mreq; + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_DROP_MEMBERSHIP failed, error = %d\n", + errno); + } + + close(fd); +} + +int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri) +{ + struct sockaddr_in *data_addr = pri->mcast_addr; + + return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); +} + +static int mcast_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +int mcast_user_set_mac(struct mcast_data *pri, unsigned char *hwaddr, + int len) +{ + memcpy(pri->hwaddr, hwaddr, len); + return 0; +} + +struct net_user_info mcast_user_info = { + init: mcast_user_init, + open: mcast_open, + close: mcast_close, + set_mtu: mcast_set_mtu, + add_address: NULL, + delete_address: NULL, + max_packet: MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mconsole_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/slab.h" +#include "linux/init.h" +#include "linux/notifier.h" +#include "linux/reboot.h" +#include "linux/utsname.h" +#include "linux/ctype.h" +#include "linux/interrupt.h" +#include "linux/sysrq.h" +#include "asm/irq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole.h" +#include "mconsole_kern.h" +#include "irq_user.h" + +static int do_unlink_socket(struct notifier_block *notifier, + unsigned long what, void *data) +{ + return(mconsole_unlink_socket()); +} + + +static struct notifier_block reboot_notifier = { + notifier_call: do_unlink_socket, + priority: 0, +}; + +LIST_HEAD(mc_requests); + +void mc_task_proc(void *unused) +{ + struct mconsole_entry *req; + unsigned long flags; + int done; + + do { + save_flags(flags); + req = list_entry(mc_requests.next, struct mconsole_entry, + list); + list_del(&req->list); + done = list_empty(&mc_requests); + restore_flags(flags); + req->request.cmd->handler(&req->request); + kfree(req); + } while(!done); +} + +struct tq_struct mconsole_task = { + routine: mc_task_proc, + data: NULL +}; + +void mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int fd; + struct mconsole_entry *new; + struct mc_request req; + + fd = (int) dev_id; + while (mconsole_get_request(fd, &req)){ + if(req.cmd->as_interrupt) (*req.cmd->handler)(&req); + else { + new = kmalloc(sizeof(req), GFP_ATOMIC); + if(new == NULL) + mconsole_reply(&req, "Out of memory", 1, 0); + else { + new->request = req; + list_add(&new->list, &mc_requests); + } + } + } + if(!list_empty(&mc_requests)) schedule_task(&mconsole_task); + reactivate_fd(fd); +} + +void mconsole_version(struct mc_request *req) +{ + char version[256]; + + sprintf(version, "%s %s %s %s %s", system_utsname.sysname, + system_utsname.nodename, system_utsname.release, + system_utsname.version, system_utsname.machine); + mconsole_reply(req, version, 0, 0); +} + +#define UML_MCONSOLE_HELPTEXT \ +"Commands: + version - Get kernel version + help - Print this message + halt - Halt UML + reboot - Reboot UML + config = - Add a new device to UML; + same syntax as command line + remove - Remove a device from the client +" + +void mconsole_help(struct mc_request *req) +{ + mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); +} + +void mconsole_halt(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + machine_halt(); +} + +void mconsole_reboot(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + machine_restart(NULL); +} + +extern void ctrl_alt_del(void); + +void mconsole_cad(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + ctrl_alt_del(); +} + +LIST_HEAD(mconsole_devices); + +void mconsole_register_dev(struct mc_device *new) +{ + list_add(&new->list, &mconsole_devices); +} + +static struct mc_device *mconsole_find_dev(char *name) +{ + struct list_head *ele; + struct mc_device *dev; + + list_for_each(ele, &mconsole_devices){ + dev = list_entry(ele, struct mc_device, list); + if(!strncmp(name, dev->name, strlen(dev->name))) + return(dev); + } + return(NULL); +} + +void mconsole_config(struct mc_request *req) +{ + struct mc_device *dev; + char *ptr = req->request.data; + int err; + + ptr += strlen("config"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "Bad configuration option", 1, 0); + return; + } + err = (*dev->config)(&ptr[strlen(dev->name)]); + mconsole_reply(req, "", err, 0); +} + +void mconsole_remove(struct mc_request *req) +{ + struct mc_device *dev; + char *ptr = req->request.data; + int err; + + ptr += strlen("remove"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "Bad remove option", 1, 0); + return; + } + err = (*dev->remove)(&ptr[strlen(dev->name)]); + mconsole_reply(req, "", err, 0); +} + +#ifdef CONFIG_MAGIC_SYSRQ +void mconsole_sysrq(struct mc_request *req) +{ + char *ptr = req->request.data; + + ptr += strlen("sysrq"); + while(isspace(*ptr)) ptr++; + + handle_sysrq(*ptr, (struct pt_regs *) ¤t->thread.process_regs, + NULL, NULL); + mconsole_reply(req, "", 0, 0); +} +#else +void mconsole_sysrq(struct mc_request *req) +{ + mconsole_reply(req, "Sysrq not compiled in", 1, 0); +} +#endif + +int mconsole_init(void) +{ + int err; + int sock; + + sock = mconsole_create_listening_socket(); + if (sock < 0) { + printk("Failed to initialize management console\n"); + return 1; + } + + register_reboot_notifier(&reboot_notifier); + + err = um_request_irq(MCONSOLE_IRQ, sock, mconsole_interrupt, + SA_INTERRUPT | SA_SHIRQ, "mconsole", + (void *)sock); + if (err) { + printk("Failed to get IRQ for management console\n"); + return 1; + } + + printk("mconsole initialized on %s\n", mconsole_socket_name); + return 0; +} + +__initcall(mconsole_init); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mconsole_user.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "mconsole.h" +#include "umid.h" + +static struct mconsole_command commands[] = { + { "version", mconsole_version, 1 }, + { "halt", mconsole_halt, 0 }, + { "reboot", mconsole_reboot, 0 }, + { "config", mconsole_config, 0 }, + { "remove", mconsole_remove, 0 }, + { "sysrq", mconsole_sysrq, 1 }, + { "help", mconsole_help, 1 }, + { "cad", mconsole_cad, 1 }, +}; + +char mconsole_socket_name[256]; + +static int has_correct_credentials(struct msghdr *msg) +{ + struct cmsghdr *cmsg; + + cmsg = CMSG_FIRSTHDR(msg); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + struct ucred *cred; + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid == getuid()) + return 1; + } + cmsg = CMSG_NXTHDR(msg, cmsg); + } + + return 0; +} + +int mconsole_reply_v0(struct mc_request *req, char *reply) +{ + struct iovec iov; + struct msghdr msg; + + iov.iov_base = reply; + iov.iov_len = strlen(reply); + + msg.msg_name = &(req->origin); + msg.msg_namelen = req->originlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + return sendmsg(req->originating_fd, &msg, 0); +} + +static struct mconsole_command *mconsole_parse(struct mc_request *req) +{ + struct mconsole_command *cmd; + int i; + + for(i=0;irequest.data, cmd->command, + strlen(cmd->command))){ + return(cmd); + } + } + return(NULL); +} + +#define MIN(a,b) ((a)<(b) ? (a):(b)) + +int mconsole_get_request(int fd, struct mc_request *req) +{ + char anc[64]; + struct iovec iov; + struct msghdr msg; + int len; + + iov.iov_base = &req->request; + iov.iov_len = sizeof(req->request); + + msg.msg_name = &(req->origin); + msg.msg_namelen = sizeof(req->origin); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = anc; + msg.msg_controllen = sizeof(anc); + msg.msg_flags = 0; + + req->len = recvmsg(fd, &msg, 0); + if (req->len < 0) + return 0; + + if(!has_correct_credentials(&msg)){ + mconsole_reply(req, "Permission denied", 1, 0); + return(0); + } + + req->originlen = msg.msg_namelen; + req->originating_fd = fd; + + if(req->request.magic != MCONSOLE_MAGIC){ + /* Unversioned request */ + len = MIN(sizeof(req->request.data) - 1, + strlen((char *) &req->request)); + memmove(req->request.data, &req->request, len); + req->request.data[len] = '\0'; + + req->request.magic = MCONSOLE_MAGIC; + req->request.version = 0; + req->request.len = len; + + mconsole_reply_v0(req, "ERR Version 0 mconsole clients are " + "not supported by this driver"); + return(0); + } + + req->cmd = mconsole_parse(req); + if(req->cmd == NULL){ + mconsole_reply(req, "Unknown command", 1, 0); + return(0); + } + + return(1); +} + +int mconsole_reply(struct mc_request *req, char *str, int err, int more) +{ + struct iovec iov; + struct msghdr msg; + struct mconsole_reply reply; + int total, len, n; + + msg.msg_name = &(req->origin); + msg.msg_namelen = req->originlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + total = strlen(str); + do { + reply.err = err; + + /* err can only be true on the first packet */ + err = 0; + + len = MIN(total, MCONSOLE_MAX_DATA - 1); + + if(len == total) reply.more = more; + else reply.more = 1; + + memcpy(reply.data, str, len); + reply.data[len] = '\0'; + total -= len; + reply.len = len + 1; + + iov.iov_base = &reply; + iov.iov_len = sizeof(reply) + reply.len - sizeof(reply.data); + n = sendmsg(req->originating_fd, &msg, 0); + if(n < 0) return(-errno); + } while(total > 0); + return(0); +} + +int mconsole_unlink_socket(void) +{ + unlink(mconsole_socket_name); + return 0; +} + +int mconsole_create_listening_socket(void) +{ + struct sockaddr_un addr; + char file[256]; + int sock, err, yes = 1; + + sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) { + printk("create_listening_socket - socket failed, errno = %d\n", + errno); + return(-1); + } + + addr.sun_family = AF_UNIX; + + if(umid_file_name("mconsole", file, sizeof(file))) return(-1); + + strcpy(mconsole_socket_name, file); + strcpy(addr.sun_path, file); + + err = bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0) { + if (errno != EADDRINUSE) { + printk("create_listening_socket - bind failed, " + "errno = %d\n", errno); + return(-1); + } + } + + setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &yes, sizeof(yes)); + + return sock; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/mmapper_kern.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,146 @@ +/* + * arch/um/drivers/mmapper_kern.c + * + * BRIEF MODULE DESCRIPTION + * + * Copyright (C) 2000 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mem_user.h" +#include "user_util.h" + +static unsigned long mmapper_size; +static char *p_buf = NULL; +static char *v_buf = NULL; + +static ssize_t +mmapper_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_to_user(buf,&v_buf[*ppos],count); + + return count; +} + +static ssize_t +mmapper_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_from_user(&v_buf[*ppos],buf,count); + + return count; +} + +static int +mmapper_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return(-ENOIOCTLCMD); +} + +static int +mmapper_mmap(struct file *file, struct vm_area_struct * vma) +{ + int ret = -EINVAL; + int size; + + lock_kernel(); + if (vma->vm_pgoff != 0) + goto out; + + size = vma->vm_end - vma->vm_start; + + /* XXX A comment above remap_page_range says it should only be + * called when the mm semaphore is held + */ + if (remap_page_range(vma->vm_start, (unsigned long) p_buf, + size, vma->vm_page_prot)) + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int +mmapper_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +mmapper_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations mmapper_fops = { + owner: THIS_MODULE, + read: mmapper_read, + write: mmapper_write, + ioctl: mmapper_ioctl, + mmap: mmapper_mmap, + open: mmapper_open, + release: mmapper_release, +}; + +static int __init mmapper_init(void) +{ + printk(KERN_INFO "Mapper v0.1\n"); + + p_buf = (char *) find_iomem("mmapper", &mmapper_size); + + v_buf = p_buf; + + devfs_register (NULL, "mmapper", DEVFS_FL_DEFAULT, + 30, 0, S_IFCHR | S_IRUGO | S_IWUGO, + &mmapper_fops, NULL); + devfs_mk_symlink(NULL, "mmapper", DEVFS_FL_DEFAULT, "mmapper0", + NULL, NULL); + return(0); +} + +static void mmapper_exit(void) +{ +} + +module_init(mmapper_init); +module_exit(mmapper_exit); + +MODULE_AUTHOR("Greg Lonnon "); +MODULE_DESCRIPTION("DSPLinux simulator mmapper driver"); +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/net_kern.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/spinlock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "linux/etherdevice.h" +#include "linux/list.h" +#include "linux/inetdevice.h" +#include "linux/ctype.h" +#include "user_util.h" +#include "kern_util.h" +#include "net_kern.h" +#include "net_user.h" +#include "slip.h" +#include "slip_kern.h" +#include "etap.h" +#include "etap_kern.h" +#include "tuntap.h" +#include "tuntap_kern.h" +#include "daemon.h" +#include "daemon_kern.h" +#include "mcast.h" +#include "mcast_kern.h" +#include "mconsole_kern.h" +#include "init.h" +#include "irq_user.h" + +LIST_HEAD(opened); + +struct uml_net devices[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev: NULL, + user: NULL, + kern: NULL, + private_size: 0, + } +}; + +static int eth_setup_common(char *str, int *index_out) +{ + char *end; + int n; + + n = simple_strtoul(str, &end, 0); + if(end == str){ + printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str); + return(1); + } + if((n < 0) || (n > sizeof(devices)/sizeof(devices[0]))){ + printk(KERN_ERR "eth_setup: device %d out of range\n", n); + return(1); + } + str = end; + if(*str != '='){ + printk(KERN_ERR + "eth_setup: expected '=' after device number\n"); + return(1); + } + str++; + if(devices[n].dev != NULL){ + printk(KERN_ERR "eth_setup: Device %d already configured\n", + n); + return(1); + } + if(index_out) *index_out = n; +#ifdef CONFIG_UML_NET_ETHERTAP + if(!strncmp(str, "ethertap", strlen("ethertap"))){ + ethertap_setup(&str[strlen("ethertap")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_TUNTAP + if(!strncmp(str, "tuntap", strlen("tuntap"))){ + tuntap_setup(&str[strlen("tuntap")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_DAEMON + if(!strncmp(str, "daemon", strlen("daemon"))){ + daemon_setup(&str[strlen("daemon")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_SLIP + if(!strncmp(str, "slip", strlen("slip"))){ + slip_setup(&str[strlen("slip")], &devices[n]); + return(0); + } +#endif +#ifdef CONFIG_UML_NET_MCAST + if(!strncmp(str, "mcast", strlen("mcast"))){ + mcast_setup(&str[strlen("mcast")], &devices[n]); + return(0); + } +#endif + printk(KERN_ERR "Unknown transport in eth_setup : %s\n", str); + return(1); +} + +static int eth_setup(char *str) +{ + eth_setup_common(str, NULL); + return(1); +} + +#ifdef CONFIG_UML_NET_ETHERTAP +#define UML_NET_ETHERTAP_HELP \ +" eth[0-9]+=ethertap,,,\n" \ +" eth0=ethertap,tap0,,192.168.0.1\n\n" +#else +#define UML_NET_ETHERTAP_HELP +#endif +#ifdef CONFIG_UML_NET_TUNTAP +#define UML_NET_TUNTAP_HELP \ +" eth[0-9]+=tuntap,,,\n" \ +" eth0=tuntap,,fe:fd:0:0:0:1,192.168.0.1\n\n" +#else +#define UML_NET_TUNTAP_HELP +#endif +#ifdef CONFIG_UML_NET_DAEMON +#define UML_NET_DAEMON_HELP \ +" eth[0-9]+=daemon,,,,\n" \ +" eth0=daemon,unix,/tmp/uml.ctl,/tmp/uml.data\n\n" +#else +#define UML_NET_DAEMON_HELP +#endif +#ifdef CONFIG_UML_NET_SLIP +#define UML_NET_SLIP_HELP \ +" eth[0-9]+=slip,\n" \ +" eth0=slip,192.168.0.1\n\n" +#else +#define UML_NET_SLIP_HELP +#endif +#ifdef CONFIG_UML_NET_MCAST +#define UML_NET_MCAST_HELP \ +" eth[0-9]+=mcast,,
,,\n" \ +" eth0=mcast,,224.2.3.4:5555,3\n\n" +#else +#define UML_NET_MCAST_HELP +#endif + +__setup("eth", eth_setup); +__uml_help(eth_setup, +"eth[0-9]+=,\n" +" Configure a network device. Formats and examples follow (one \n" +" for each configured transport).\n\n" +UML_NET_ETHERTAP_HELP +UML_NET_TUNTAP_HELP +UML_NET_DAEMON_HELP +UML_NET_SLIP_HELP +UML_NET_MCAST_HELP +); +int ndev = 0; + +static int uml_net_rx(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + int pkt_len; + struct sk_buff *skb; + + /* If we can't allocate memory, try again next round. */ + if ((skb = dev_alloc_skb(dev->mtu)) == NULL) { + lp->stats.rx_dropped++; + reactivate_fd(lp->fd); + return 0; + } + + skb->dev = dev; + skb_put(skb, dev->mtu); + skb->mac.raw = skb->data; + pkt_len = (*lp->read)(lp->fd, &skb, lp); + + reactivate_fd(lp->fd); + if (pkt_len > 0) { + skb_trim(skb, pkt_len); + skb->protocol = (*lp->protocol)(skb); + netif_rx(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + return pkt_len; + } + + kfree_skb(skb); + return pkt_len; +} + +void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct uml_net_private *lp = dev->priv; + int err; + + if (netif_running(dev)) { + spin_lock(&lp->lock); + while((err = uml_net_rx(dev)) > 0) ; + if(err < 0) { + printk(KERN_ERR + "Device '%s' read returned %d, shutting it " + "down\n", dev->name, err); + dev->flags &= ~IFF_UP; + dev_close(dev); + } + spin_unlock(&lp->lock); + } +} + +static int uml_net_open(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + int err; + + spin_lock(&lp->lock); + + if(lp->fd >= 0){ + err = -ENXIO; + goto out; + } + + lp->fd = (*lp->open)(&lp->user); + if(lp->fd < 0){ + err = lp->fd; + goto out; + } + + err = um_request_irq(dev->irq, lp->fd, uml_net_interrupt, + SA_INTERRUPT | SA_SHIRQ, dev->name, dev); + if(err != 0){ + printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); + (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + err = -ENETUNREACH; + } + + lp->tl.data = (unsigned long) &lp->user; + netif_start_queue(dev); + + list_add(&lp->list, &opened); + MOD_INC_USE_COUNT; + out: + spin_unlock(&lp->lock); + return(err); +} + +static int uml_net_close(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + + netif_stop_queue(dev); + spin_lock(&lp->lock); + + free_irq(dev->irq, dev); + (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + list_del(&lp->list); + + MOD_DEC_USE_COUNT; + spin_unlock(&lp->lock); + return 0; +} + +static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + unsigned long flags; + int len; + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + + len = (*lp->write)(lp->fd, &skb, lp); + + if(len == skb->len) { + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* this is normally done in the interrupt when tx finishes */ + netif_wake_queue(dev); + } + else if(len == 0){ + netif_start_queue(dev); + lp->stats.tx_dropped++; + } + else { + netif_start_queue(dev); + printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len); + } + + spin_unlock_irqrestore(&lp->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +static struct net_device_stats *uml_net_get_stats(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + return &lp->stats; +} + +static void uml_net_set_multicast_list(struct net_device *dev) +{ + if (dev->flags & IFF_PROMISC) return; + else if (dev->mc_count) dev->flags |= IFF_ALLMULTI; + else dev->flags &= ~IFF_ALLMULTI; +} + +static void uml_net_tx_timeout(struct net_device *dev) +{ + dev->trans_start = jiffies; + netif_wake_queue(dev); +} + +static int uml_net_set_mac(struct net_device *dev, void *addr) +{ + struct uml_net_private *lp = dev->priv; + struct sockaddr *hwaddr = addr; + int err; + + spin_lock(&lp->lock); + + err = (*lp->set_mac)(hwaddr, &lp->user); + + spin_unlock(&lp->lock); + + return err; +} + +static int uml_net_change_mtu(struct net_device *dev, int new_mtu) +{ + struct uml_net_private *lp = dev->priv; + int err = 0; + + spin_lock(&lp->lock); + + new_mtu = (*lp->set_mtu)(new_mtu, &lp->user); + if(new_mtu < 0){ + err = new_mtu; + goto out; + } + + dev->mtu = new_mtu; + + out: + spin_unlock(&lp->lock); + return err; +} + +static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return(-EINVAL); +} + +void uml_net_user_timer_expire(unsigned long _conn) +{ +#ifdef undef + struct connection *conn = (struct connection *)_conn; + + dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn); + do_connect(conn); +#endif +} + +static int eth_configure(struct uml_net *device, int n) +{ + struct net_device *dev; + struct uml_net_private *lp; + + device->private_size += sizeof(struct uml_net_private) + + sizeof(((struct uml_net_private *) 0)->user); + printk(KERN_INFO "Netdevice %d : ", n); + dev = (*device->kern->init)(device->private_size, + device->transport_index); + device->dev = dev; + + if (dev == NULL){ + printk(KERN_ERR "eth_configure: Out of memory on device %d\n", + n); + return(1); + } + + dev->mtu = device->user->max_packet; + dev->open = uml_net_open; + dev->hard_start_xmit = uml_net_start_xmit; + dev->stop = uml_net_close; + dev->get_stats = uml_net_get_stats; + dev->set_multicast_list = uml_net_set_multicast_list; + dev->tx_timeout = uml_net_tx_timeout; + dev->set_mac_address = uml_net_set_mac; + dev->change_mtu = uml_net_change_mtu; + dev->do_ioctl = uml_net_ioctl; + dev->watchdog_timeo = (HZ >> 1); + dev->irq = UM_ETH_IRQ; + + lp = dev->priv; + spin_lock_init(&lp->lock); + init_timer(&lp->tl); + lp->tl.function = uml_net_user_timer_expire; + lp->list = ((struct list_head) LIST_HEAD_INIT(lp->list)); + memset(&lp->stats, 0, sizeof(lp->stats)); + lp->fd = -1; + lp->protocol = device->kern->protocol; + lp->set_mac = device->kern->set_mac; + lp->open = device->user->open; + lp->close = device->user->close; + lp->read = device->kern->read; + lp->write = device->kern->write; + lp->add_address = device->user->add_address; + lp->delete_address = device->user->delete_address; + lp->set_mtu = device->user->set_mtu; + + if(device->user->init) + (*device->user->init)(&lp->user, dev); + return(0); +} + +int __init uml_net_probe(void) +{ + int i; + + for(i = 0; i < sizeof(devices)/sizeof(devices[0]); i++){ + if(devices[i].user == NULL) continue; + eth_configure(&devices[i], i); + } + return(0); +} + +static int net_config(char *str) +{ + int err, n; + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "net_config failed to strdup string\n"); + return(1); + } + err = eth_setup_common(str, &n); + if(err){ + kfree(str); + return(err); + } + err = eth_configure(&devices[n], n); + return(err); +} + +static int net_remove(char *str) +{ + struct net_device *dev; + struct uml_net_private *lp; + int n; + + if(!isdigit(*str)) return(-1); + n = *str - '0'; + if(devices[n].dev == NULL) return(0); + dev = devices[n].dev; + lp = dev->priv; + if(lp->fd > 0) return(-1); + unregister_netdev(dev); + devices[n].dev = NULL; + return(0); +} + +static struct mc_device net_mc = { + name: "eth", + config: net_config, + remove: net_remove, +}; + +static int uml_inetaddr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = ptr; + u32 addr = ifa->ifa_address; + u32 netmask = ifa->ifa_mask; + struct net_device *dev = ifa->ifa_dev->dev; + struct uml_net_private *lp; + void (*proc)(unsigned char *, unsigned char *, void *); + unsigned char addr_buf[4], netmask_buf[4]; + + if(dev->open != uml_net_open) return(NOTIFY_DONE); + + lp = dev->priv; + if(lp->fd == -1) return(NOTIFY_DONE); + + proc = NULL; + switch (event){ + case NETDEV_UP: + proc = lp->add_address; + break; + case NETDEV_DOWN: + proc = lp->delete_address; + break; + } + if(proc != NULL){ + addr_buf[0] = addr & 0xff; + addr_buf[1] = (addr >> 8) & 0xff; + addr_buf[2] = (addr >> 16) & 0xff; + addr_buf[3] = addr >> 24; + netmask_buf[0] = netmask & 0xff; + netmask_buf[1] = (netmask >> 8) & 0xff; + netmask_buf[2] = (netmask >> 16) & 0xff; + netmask_buf[3] = netmask >> 24; + (*proc)(addr_buf, netmask_buf, &lp->user); + } + return(NOTIFY_DONE); +} + +struct notifier_block uml_inetaddr_notifier = { + notifier_call: uml_inetaddr_event, +}; + +static int uml_net_init(void) +{ + mconsole_register_dev(&net_mc); + register_inetaddr_notifier(¨_inetaddr_notifier); + return(0); +} + +__initcall(uml_net_init); + +static void close_devices(void) +{ + struct list_head *ele; + struct uml_net_private *lp; + + list_for_each(ele, &opened){ + lp = list_entry(ele, struct uml_net_private, list); + (*lp->close)(lp->fd, &lp->user); + } +} + +__uml_exitcall(close_devices); + +int setup_etheraddr(char *str, unsigned char *addr) +{ + char *end; + int i; + + for(i=0;i<6;i++){ + addr[i] = simple_strtoul(str, &end, 16); + if((end == str) || + ((*end != ':') && (*end != ',') && (*end != '\0'))){ + printk(KERN_ERR + "setup_etheraddr: failed to parse '%s' " + "as an ethernet address\n", str); + return(-1); + } + str = end + 1; + } + if(addr[0] & 1){ + printk(KERN_ERR + "Attempt to assign a broadcast ethernet address to a " + "device disallowed\n"); + return(-1); + } + return(0); +} + +void dev_ip_addr(void *d, char *buf, char *bin_buf) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + u32 addr; + + if(ip == NULL){ + printk(KERN_WARNING "dev_ip_addr - device not assigned an " + "IP address\n"); + return; + } + in = ip->ifa_list; + addr = in->ifa_address; + sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, + (addr >> 16) & 0xff, addr >> 24); + if(bin_buf){ + bin_buf[0] = addr & 0xff; + bin_buf[1] = (addr >> 8) & 0xff; + bin_buf[2] = (addr >> 16) & 0xff; + bin_buf[3] = addr >> 24; + } +} + +void set_ether_mac(void *d, unsigned char *addr) +{ + struct net_device *dev = d; + + memcpy(dev->dev_addr, addr, ETH_ALEN); +} + +struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) +{ + if((skb != NULL) && (skb_tailroom(skb) < extra)){ + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); + dev_kfree_skb(skb); + skb = skb2; + } + if(skb != NULL) skb_put(skb, extra); + return(skb); +} + +void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, + void *), + void *arg) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + unsigned char address[4], netmask[4]; + + if(ip == NULL) return; + in = ip->ifa_list; + while(in != NULL){ + address[0] = in->ifa_address & 0xff; + address[1] = (in->ifa_address >> 8) & 0xff; + address[2] = (in->ifa_address >> 16) & 0xff; + address[3] = in->ifa_address >> 24; + netmask[0] = in->ifa_mask & 0xff; + netmask[1] = (in->ifa_mask >> 8) & 0xff; + netmask[2] = (in->ifa_mask >> 16) & 0xff; + netmask[3] = in->ifa_mask >> 24; + (*cb)(address, netmask, arg); + in = in->ifa_next; + } +} + +void *get_output_buffer(int *len_out) +{ + void *ret; + + ret = (void *) __get_free_pages(GFP_KERNEL, 0); + if(ret) *len_out = PAGE_SIZE; + else *len_out = 0; + return(ret); +} + +void free_output_buffer(void *buffer) +{ + free_pages((unsigned long) buffer, 0); +} + +void tap_setup_common(char *str, char *type, char **dev_name, char *hw_addr, + int *hw_setup, char **gate_addr) +{ + int err; + + if(*str != ','){ + printk(KERN_ERR + "ethertap_setup: expected ',' after '%s'\n", type); + return; + } + str++; + if(*str != ',') *dev_name = str; + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != ','){ + err = setup_etheraddr(str, hw_addr); + if(!err) *hw_setup = 1; + } + str = strchr(str, ','); + if(str == NULL) return; + *str++ = '\0'; + if(*str != '\0') *gate_addr = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/net_kern.h b/arch/um/drivers/net_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/net_kern.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,66 @@ +#ifndef __UM_NET_KERN_H +#define __UM_NET_KERN_H + +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/list.h" + +#define MAX_UML_NETDEV (16) + +struct uml_net { + struct net_device *dev; + struct net_user_info *user; + struct net_kern_info *kern; + int private_size; + int transport_index; +}; + +struct uml_net_private { + spinlock_t lock; + + struct timer_list tl; + struct list_head list; + struct net_device_stats stats; + int fd; + unsigned short (*protocol)(struct sk_buff *); + int (*set_mac)(struct sockaddr *hwaddr, void *); + int (*open)(void *); + void (*close)(int, void *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); + + void (*add_address)(unsigned char *, unsigned char *, void *); + void (*delete_address)(unsigned char *, unsigned char *, void *); + int (*set_mtu)(int mtu, void *); + int user[1]; +}; + +struct net_kern_info { + struct net_device *(*init)(int, int); + unsigned short (*protocol)(struct sk_buff *); + int (*set_mac)(struct sockaddr *hwaddr, void *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); +}; + +extern struct net_device *ether_init(int); +extern unsigned short ether_protocol(struct sk_buff *); +extern int ether_set_mac(struct sockaddr *hwaddr, void *); +extern int setup_etheraddr(char *str, unsigned char *addr); +extern struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra); +extern void tap_setup_common(char *str, char *type, char **dev_name, + char *hw_addr, int *hw_setup, char **gate_addr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/net_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "user_util.h" +#include "kern_util.h" +#include "net_user.h" + +int tap_open_common(void *dev, int hw_setup, char *gate_addr) +{ + char addr[sizeof("255.255.255.255\0")]; + char ether[ETH_ADDR_LEN]; + + if((gate_addr != NULL) || !hw_setup){ + ether[0] = 0xfe; + ether[1] = 0xfd; + ether[2] = 0x0; + ether[3] = 0x0; + ether[4] = 0x0; + ether[5] = 0x0; + dev_ip_addr(dev, addr, ðer[2]); + } + if(gate_addr != NULL){ + int uml_addr[4], tap_addr[4]; + if(sscanf(addr, "%d.%d.%d.%d", ¨_addr[0], ¨_addr[1], + ¨_addr[2], ¨_addr[3]) != 4){ + printk("Invalid UML IP address - '%s'\n", addr); + return(-EINVAL); + } + if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], + &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){ + printk("Invalid tap IP address - '%s'\n", + gate_addr); + return(-EINVAL); + } + if((uml_addr[0] == tap_addr[0]) && + (uml_addr[1] == tap_addr[1]) && + (uml_addr[2] == tap_addr[2]) && + (uml_addr[3] == tap_addr[3])){ + printk("The tap IP address and the UML eth IP address" + " must be different\n"); + return(-EINVAL); + } + } + if(!hw_setup){ + ether[0] = 0xfe; + ether[1] = 0xfd; + set_ether_mac(dev, ether); + } + return(0); +} + +int read_output(int fd, char **output_out) +{ + int n; + + if(read(fd, &n, sizeof(n)) != sizeof(n)){ + printk("read_output - read of length failed, errno = %d\n", + errno); + return(-1); + } + if((*output_out = um_kmalloc(n)) == NULL){ + printk("read_output - kmalloc failed\n"); + return(-1); + } + if(read(fd, *output_out, n) != n){ + printk("read_output - read of data failed, errno = %d\n", + errno); + kfree(*output_out); + return(-1); + } + return(0); +} + +int net_read(int fd, void *buf, int len) +{ + int n; + + while(((n = read(fd, buf, len)) < 0) && (errno == EINTR)) ; + + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_recvfrom(int fd, void *buf, int len) +{ + int n; + + while(((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && + (errno == EINTR)) ; + + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_write(int fd, void *buf, int len) +{ + int n; + + while(((n = write(fd, buf, len)) < 0) && (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_send(int fd, void *buf, int len) +{ + int n; + + while(((n = send(fd, buf, len, 0)) < 0) && (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_sendto(int fd, void *buf, int len, void *to, int sock_len) +{ + int n; + + while(((n = sendto(fd, buf, len, 0, (struct sockaddr *) to, + sock_len)) < 0) && (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +struct change_data { + char *dev; + char *what; + char *address; + char *netmask; + char *output; +}; + +static void change_tramp(void *arg) +{ + int pid, fds[2]; + struct change_data *data = arg; + char version[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version, data->what, data->dev, + data->address, data->netmask, NULL }; + + sprintf(version, "%d", UML_NET_VERSION); + if(pipe(fds) < 0){ + printk("change_tramp - pipe failed, errno = %d\n", + errno); + return; + } + if((pid = fork()) == 0){ + dup2(fds[1], 1); + close(fds[0]); + execvp(argv[0], argv); + printk("Exec of '%s' failed - errno = %d\n", argv[0], errno); + exit(1); + } + close(fds[1]); + if(read_output(fds[0], &data->output)) data->output = NULL; + waitpid(pid, NULL, 0); +} + +static void change(char *dev, char *what, unsigned char *addr, + unsigned char *netmask) +{ + char addr_buf[sizeof("255.255.255.255\0")]; + char netmask_buf[sizeof("255.255.255.255\0")]; + struct change_data data; + + data.dev = dev; + data.what = what; + sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], + netmask[2], netmask[3]); + data.address = addr_buf; + data.netmask = netmask_buf; + tracing_cb(change_tramp, &data); + if(data.output != NULL){ + printk("%s", data.output); + kfree(data.output); + } +} + +void open_addr(unsigned char *addr, unsigned char *netmask, void *arg) +{ + change(arg, "add", addr, netmask); +} + +void close_addr(unsigned char *addr, unsigned char *netmask, void *arg) +{ + change(arg, "del", addr, netmask); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/net_user.h b/arch/um/drivers/net_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/net_user.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,55 @@ +#ifndef __UM_NET_USER_H__ +#define __UM_NET_USER_H__ + +#define ETH_ADDR_LEN (6) +#define ETH_HEADER_ETHERTAP (16) +#define ETH_HEADER_OTHER (14) +#define ETH_MAX_PACKET (1500) + +#define UML_NET_VERSION (4) + +struct net_user_info { + void (*init)(void *, void *); + int (*open)(void *); + void (*close)(int, void *); + int (*set_mtu)(int mtu, void *); + void (*add_address)(unsigned char *, unsigned char *, void *); + void (*delete_address)(unsigned char *, unsigned char *, void *); + int max_packet; +}; + +extern void ether_user_init(void *data, void *dev); +extern void dev_ip_addr(void *d, char *buf, char *bin_buf); +extern void set_ether_mac(void *d, unsigned char *addr); +extern void iter_addresses(void *d, void (*cb)(unsigned char *, + unsigned char *, void *), + void *arg); + +extern void *get_output_buffer(int *len_out); +extern void free_output_buffer(void *buffer); + +extern int tap_open_common(void *dev, int hw_setup, char *gate_addr); + +extern int read_output(int fd, char **output_out); + +extern int net_read(int fd, void *buf, int len); +extern int net_recvfrom(int fd, void *buf, int len); +extern int net_write(int fd, void *buf, int len); +extern int net_send(int fd, void *buf, int len); +extern int net_sendto(int fd, void *buf, int len, void *to, int sock_len); + +extern void open_addr(unsigned char *addr, unsigned char *netmask, void *arg); +extern void close_addr(unsigned char *addr, unsigned char *netmask, void *arg); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/port.c b/arch/um/drivers/port.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/port.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "chan_user.h" +#include "port.h" + +struct port_chan { + int fd; + int raw; + struct termios tt; + void *kernel_data; +}; + +void *port_init(char *str, int device, struct chan_opts *opts) +{ + struct port_chan *data; + void *kern_data; + char *end; + int port; + + if(*str != ':'){ + printk("port_init : channel type 'port' must specify a " + "port number\n"); + return(NULL); + } + str++; + port = strtoul(str, &end, 0); + if(*end != '\0'){ + printk("port_init : couldn't parse port '%s'\n", str); + return(NULL); + } + + if((kern_data = port_data(port)) == NULL) return(NULL); + + if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + *data = ((struct port_chan) { fd : -1, + raw : opts->raw, + kernel_data : kern_data }); + + return(data); +} + +int port_open(int input, int output, void *d) +{ + struct port_chan *data = d; + int fd; + + fd = port_wait(data->kernel_data); + if((fd >= 0) && data->raw){ + tcgetattr(fd, &data->tt); + raw(fd, 0); + } + data->fd = fd; + return(fd); +} + +void port_close(int fd, void *d) +{ + struct port_chan *data = d; + + close(data->fd); +} + +int port_console_write(int fd, const char *buf, int n, void *d) +{ + struct port_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +void port_free(void *d) +{ + struct port_chan *data = d; + + port_kern_free(data->kernel_data); + kfree(data); +} + +struct chan_ops port_ops = { + init: port_init, + open: port_open, + close: port_close, + read: generic_read, + write: generic_write, + console_write: port_console_write, + window_size: generic_window_size, + free: port_free, +}; + +int port_listen_fd(int port) +{ + struct sockaddr_in addr; + int fd; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if(fd == -1) return(-errno); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return(-errno); + + if(listen(fd, 1) < 0) return(-errno); + + return(fd); +} + +struct port_connect_data { + int sock_fd; + int pipe_fds[2]; + int err; +}; + +void port_connect_tramp(void *d) +{ + struct port_connect_data *data = d; + + data->err = 0; + if(fork() == 0){ + dup2(data->sock_fd, 0); + dup2(data->sock_fd, 1); + dup2(data->sock_fd, 2); + close(data->sock_fd); + dup2(data->pipe_fds[1], 3); + close(data->pipe_fds[1]); + execlp("/usr/sbin/in.telnetd", "in.telnetd", "-L", + "/usr/lib/uml/port-helper", NULL); + shutdown(3, SHUT_RDWR); + shutdown(data->pipe_fds[0], SHUT_RDWR); + data->err = errno; + exit(1); + } +} + +static int rcv_fd(int fd, struct port_connect_data *data) +{ + int new, n; + char buf[CMSG_SPACE(sizeof(new))]; + struct msghdr msg; + struct cmsghdr *cmsg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + msg.msg_flags = 0; + + n = recvmsg(fd, &msg, 0); + if(n < 0){ + printk("rcv_fd : recvmsg failed - errno = %d\n", errno); + return(-1); + } + + cmsg = CMSG_FIRSTHDR(&msg); + if(cmsg == NULL){ + printk("rcv_fd didn't receive anything, error = %d\n", + data->err); + return(-1); + } + if((cmsg->cmsg_level != SOL_SOCKET) || + (cmsg->cmsg_type != SCM_RIGHTS)){ + printk("rcv_fd didn't receive a descriptor\n"); + return(-1); + } + + new = ((int *) CMSG_DATA(cmsg))[0]; + return(new); +} + +int port_connection(int fd) +{ + int new, fds[2]; + struct port_connect_data data; + + if((new = accept(fd, NULL, 0)) < 0) return(-errno); + + if(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) < 0) return(-errno); + + data = ((struct port_connect_data) + { sock_fd : new, + pipe_fds : { fds[0], fds[1] } }); + + tracing_cb(port_connect_tramp, &data); + return(rcv_fd(fds[0], &data)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/port.h b/arch/um/drivers/port.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/port.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PORT_H__ +#define __PORT_H__ + +extern void *port_data(int port); +extern int port_wait(void *data); +extern void port_kern_close(void *d); +extern int port_connection(int fd); +extern int port_listen_fd(int port); +extern void port_read(int fd, void *data); +extern void port_kern_free(void *d); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/port_kern.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/list.h" +#include "linux/slab.h" +#include "linux/irq.h" +#include "linux/spinlock.h" +#include "asm/semaphore.h" +#include "asm/errno.h" +#include "kern_util.h" +#include "kern.h" +#include "irq_user.h" +#include "port.h" + +struct port_list { + struct list_head list; + struct semaphore sem; + int port; + int fd; + spinlock_t lock; + struct list_head connections; +}; + +struct port_dev { + struct port_list *port; + int fd; +}; + +struct connection { + struct list_head list; + int fd; +}; + +struct list_head ports = LIST_HEAD_INIT(ports); + +static void port_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct port_list *port = data; + struct connection *conn; + int fd; + + reactivate_fd(port->fd); + fd = port_connection(port->fd); + if(fd < 0){ + printk("port_connection returned %d\n", -fd); + return; + } + conn = kmalloc(sizeof(*conn), GFP_ATOMIC); + if(conn == NULL){ + printk("port_interrupt : failed to allocate connection\n"); + close(fd); + return; + } + *conn = ((struct connection) + { list : LIST_HEAD_INIT(conn->list), + fd : fd }); + list_add(&conn->list, &port->connections); + up(&port->sem); +} + +void *port_data(int port_num) +{ + struct list_head *ele; + struct port_list *port; + struct port_dev *dev; + int fd; + + list_for_each(ele, &ports){ + port = list_entry(ele, struct port_list, list); + if(port->port == port_num) goto found; + } + port = kmalloc(sizeof(struct port_list), GFP_KERNEL); + if(port == NULL){ + printk(KERN_ERR "Allocation of port list failed\n"); + return(NULL); + } + + fd = port_listen_fd(port_num); + if(fd < 0){ + printk(KERN_ERR "binding to port %d failed, errno = %d\n", + port_num, -fd); + return(NULL); + } + if(um_request_irq(ACCEPT_IRQ, fd, port_interrupt, + SA_INTERRUPT | SA_SHIRQ, "port", port)){ + printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num); + return(NULL); + } + + *port = ((struct port_list) + { list : LIST_HEAD_INIT(port->list), + sem : __SEMAPHORE_INITIALIZER(port->sem, 0), + lock : SPIN_LOCK_UNLOCKED, + port : port_num, + fd : fd, + connections : LIST_HEAD_INIT(port->connections) }); + list_add(&port->list, &ports); + + found: + dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); + if(dev == NULL){ + printk(KERN_ERR "Allocation of port device entry failed\n"); + return(NULL); + } + + *dev = ((struct port_dev) + { port : port, + fd : -1 }); + return(dev); +} + +int port_wait(void *data) +{ + struct port_dev *dev = data; + struct connection *conn; + + if(down_interruptible(&dev->port->sem)) return(-ERESTARTSYS); + spin_lock(&dev->port->lock); + conn = list_entry(dev->port->connections.next, struct connection, + list); + list_del(&conn->list); + spin_unlock(&dev->port->lock); + + dev->fd = conn->fd; + kfree(conn); + + return(dev->fd); +} + +void port_kern_free(void *d) +{ + struct port_dev *dev = d; + + kfree(dev); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/pty.c b/arch/um/drivers/pty.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/pty.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "chan_user.h" +#include "user.h" +#include "user_util.h" + +struct pty_chan { + void (*announce)(char *dev_name, int dev); + int dev; + int raw; + struct termios tt; +}; + +void *pty_chan_init(char *str, int device, struct chan_opts *opts) +{ + struct pty_chan *data; + + if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + *data = ((struct pty_chan) { announce : opts->announce, + dev : device, + raw : opts->raw }); + return(data); +} + +int pts_open(int input, int output, void *d) +{ + struct pty_chan *data = d; + int fd; + + if((fd = get_pty()) < 0){ + printk("open_pts : Failed to open pts\n"); + return(-errno); + } + if(data->raw){ + tcgetattr(fd, &data->tt); + raw(fd, 0); + } + if(data->announce) (*data->announce)(ptsname(fd), data->dev); + return(fd); +} + +int pty_open(int input, int output, void *d) +{ + struct pty_chan *data = d; + int fd; + char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx"; + + fd = getmaster(dev); + if(fd < 0) return(-errno); + if(data->raw) raw(fd, 0); + if(data->announce) (*data->announce)(dev, data->dev); + return(fd); +} + +int pty_console_write(int fd, const char *buf, int n, void *d) +{ + struct pty_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops pty_ops = { + init: pty_chan_init, + open: pty_open, + close: generic_close, + read: generic_read, + write: generic_write, + console_write: pty_console_write, + window_size: generic_window_size, + free: generic_free, +}; + +struct chan_ops pts_ops = { + init: pty_chan_init, + open: pts_open, + close: generic_close, + read: generic_read, + write: generic_write, + console_write: pty_console_write, + window_size: generic_window_size, + free: generic_free, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/slip.h b/arch/um/drivers/slip.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/slip.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,34 @@ +#ifndef __UM_SLIP_H +#define __UM_SLIP_H + +#define BUF_SIZE 1500 + +struct slip_data { + void *dev; + char name[sizeof("slnnnnn\0")]; + char *addr; + char *gate_addr; + int slave; + char buf[2 * BUF_SIZE]; + int pos; + int esc; +}; + +extern struct net_user_info slip_user_info; + +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri); +extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/slip_kern.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,106 @@ +#include "linux/kernel.h" +#include "linux/stddef.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/if_arp.h" +#include "net_kern.h" +#include "net_user.h" +#include "kern.h" +#include "slip.h" +#include "slip_kern.h" + +struct slip_data slip_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + addr: NULL, + gate_addr: NULL, + slave: -1, + buf: { 0 }, + pos: 0, + esc: 0, + } +}; + +struct net_device umn_dev; + +struct net_device *slip_init(int private_size, int index) +{ + struct uml_net_private *private; + struct slip_data *spri; + + private = kmalloc(private_size, GFP_KERNEL); + if(private == NULL) return(NULL); + umn_dev.priv = private; + spri = (struct slip_data *) private->user; + *spri = slip_priv[index]; + strncpy(umn_dev.name, "umn", IFNAMSIZ); + umn_dev.init = NULL; + umn_dev.hard_header_len = 0; + umn_dev.addr_len = 4; + umn_dev.type = ARPHRD_ETHER; + umn_dev.tx_queue_len = 256; + umn_dev.flags = IFF_NOARP; + if(register_netdev(&umn_dev)) + printk(KERN_ERR "Couldn't initialize umn\n"); + printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr); + + return(&umn_dev); +} + +static int slip_set_mac(struct sockaddr *hwaddr, void *data) +{ + return(0); +} + +static unsigned short slip_protocol(struct sk_buff *skbuff) +{ + return(htons(ETH_P_IP)); +} + +static int slip_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + (struct slip_data *) &lp->user)); +} + +static int slip_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_write(fd, (*skb)->data, (*skb)->len, + (struct slip_data *) &lp->user)); +} + +struct net_kern_info slip_kern_info = { + init: slip_init, + protocol: slip_protocol, + set_mac: slip_set_mac, + read: slip_read, + write: slip_write, +}; + +static int slip_count = 0; + +void slip_setup(char *str, struct uml_net *dev) +{ + int n = slip_count; + + dev->user = &slip_user_info; + dev->kern = &slip_kern_info; + dev->private_size = sizeof(struct slip_data); + dev->transport_index = slip_count++; + if(*str != ',') return; + str++; + if(str[0] != '\0') slip_priv[n].gate_addr = str; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/slip_kern.h b/arch/um/drivers/slip_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/slip_kern.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,8 @@ +#ifndef __UM_SLIP_KERN_H +#define __UM_SLIP_KERN_H + +#include "net_kern.h" + +extern void slip_setup(char *arg, struct uml_net *dev); + +#endif diff -Nru a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/slip_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "net_user.h" +#include "slip.h" + +void slip_user_init(void *data, void *dev) +{ + struct slip_data *pri = data; + + pri->dev = dev; +} + +static int set_up_tty(int fd) +{ + int i; + struct termios tios; + + if (tcgetattr(fd, &tios) < 0) { + printk("could not get initial terminal attributes\n"); + return(-1); + } + + tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + for (i = 0; i < NCCS; i++) + tios.c_cc[i] = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + cfsetospeed(&tios, B38400); + cfsetispeed(&tios, B38400); + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + printk("failed to set terminal attributes\n"); + return(-1); + } + return(0); +} + +struct slip_tramp_data { + int fd; + char **args; + int err; + char *output; +}; + +void slip_tramp(void *arg) +{ + struct slip_tramp_data *data = arg; + char **argv = data->args; + int status, pid, fds[2]; + + data->err = 0; + data->output = NULL; + if(pipe(fds) != 0){ + perror("slip_tramp : pipe failed"); + data->err = EINVAL; + return; + } + if((pid = fork()) == 0){ + if(data->fd != -1) dup2(data->fd, 0); + dup2(fds[1], 1); + close(fds[0]); + execvp(argv[0], argv); + exit(errno); + } + else if(pid < 0) data->err = errno; + else { + close(fds[1]); + read_output(fds[0], &data->output); + if(waitpid(pid, &status, 0) < 0) data->err = errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ + printk("'%s' didn't exit with status 0\n", argv[0]); + data->err = EINVAL; + } + } +} + +static int slip_open(void *data) +{ + struct slip_data *pri = data; + struct slip_tramp_data slip_data; + char version_buf[sizeof("nnnnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, + NULL }; + int sfd, mfd, disc, sencap; + + if((mfd = get_pty()) < 0){ + printk("umn : Failed to open pty\n"); + return(-1); + } + if((sfd = open(ptsname(mfd), O_RDWR)) < 0){ + printk("Couldn't open tty for slip line\n"); + return(-1); + } + if(set_up_tty(sfd)) return(-1); + pri->slave = sfd; + pri->pos = 0; + pri->esc = 0; + if(pri->gate_addr != NULL){ + sprintf(version_buf, "%d", UML_NET_VERSION); + slip_data.fd = sfd; + strcpy(gate_buf, pri->gate_addr); + slip_data.args = argv; + tracing_cb(slip_tramp, &slip_data); + if(slip_data.output != NULL){ + printk("%s", slip_data.output); + kfree(slip_data.output); + } + if(slip_data.err != 0){ + printk("slip_tramp failed - errno = %d\n", + slip_data.err); + return(-slip_data.err); + } + if(ioctl(pri->slave, SIOCGIFNAME, pri->name) < 0){ + printk("SIOCGIFNAME failed, errno = %d\n", errno); + return(-errno); + } + iter_addresses(pri->dev, open_addr, pri->name); + } + else { + disc = N_SLIP; + if(ioctl(sfd, TIOCSETD, &disc) < 0){ + printk("Failed to set slip line discipline - " + "errno = %d\n", errno); + return(-errno); + } + sencap = 0; + if(ioctl(sfd, SIOCSIFENCAP, &sencap) < 0){ + printk("Failed to sett slip encapsulation - " + "errno = %d\n", errno); + return(-errno); + } + } + return(mfd); +} + +static void slip_close(int fd, void *data) +{ + struct slip_data *pri = data; + struct slip_tramp_data slip_data; + char version_buf[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, + NULL }; + + if(pri->gate_addr != NULL) + iter_addresses(pri->dev, close_addr, pri->name); + + sprintf(version_buf, "%d", UML_NET_VERSION); + slip_data.fd = -1; + slip_data.args = argv; + tracing_cb(slip_tramp, &slip_data); + if(slip_data.output != NULL){ + printk("%s", slip_data.output); + kfree(slip_data.output); + } + if(slip_data.err != 0) + printk("slip_tramp failed - errno = %d\n", slip_data.err); + close(fd); + close(pri->slave); + pri->slave = -1; +} + +/* SLIP protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +static int slip_unesc(struct slip_data *sl, unsigned char c) +{ + int ret; + + switch(c){ + case END: + sl->esc = 0; + ret = sl->pos; + sl->pos = 0; + return(ret); + case ESC: + sl->esc = 1; + return(0); + case ESC_ESC: + if(sl->esc){ + sl->esc = 0; + c = ESC; + } + break; + case ESC_END: + if(sl->esc){ + sl->esc = 0; + c = END; + } + break; + } + sl->buf[sl->pos++] = c; + return(0); +} + +int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) +{ + int i, n, size, start; + + n = net_read(fd, &pri->buf[pri->pos], sizeof(pri->buf) - pri->pos); + if(n <= 0) return(n); + + start = pri->pos; + for(i = 0; i < n; i++){ + size = slip_unesc(pri, pri->buf[start + i]); + if(size){ + memcpy(buf, pri->buf, size); + return(size); + } + } + return(0); +} + +static int slip_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the SLIP protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return (ptr - d); +} + +int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) +{ + int actual, n; + + actual = slip_esc(buf, pri->buf, len); + n = net_write(fd, pri->buf, actual); + if(n < 0) return(n); + else return(len); +} + +static int slip_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +static void slip_add_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if(pri->slave == -1) return; + open_addr(addr, netmask, pri->name); +} + +static void slip_del_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if(pri->slave == -1) return; + close_addr(addr, netmask, pri->name); +} + +struct net_user_info slip_user_info = { + init: slip_user_init, + open: slip_open, + close: slip_close, + set_mtu: slip_set_mtu, + add_address: slip_add_addr, + delete_address: slip_del_addr, + max_packet: BUF_SIZE +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/ssl.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/fs.h" +#include "linux/tty.h" +#include "linux/tty_driver.h" +#include "linux/major.h" +#include "linux/mm.h" +#include "linux/init.h" +#include "linux/devfs_fs_kernel.h" +#include "asm/termbits.h" +#include "asm/irq.h" +#include "line.h" +#include "ssl.h" +#include "chan_kern.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "init.h" +#include "2_5compat.h" + +static int ssl_version = 1; + +static struct tty_driver ssl_driver; + +static int ssl_refcount = 0; + +#define NR_PORTS 64 + +static struct tty_struct *ssl_table[NR_PORTS]; +static struct termios *ssl_termios[NR_PORTS]; +static struct termios *ssl_termios_locked[NR_PORTS]; + +void ssl_announce(char *dev_name, int dev) +{ + printk(KERN_INFO "Serial line %d assigned device '%s'\n", dev, + dev_name); +} + +static struct chan_opts opts = { + announce: ssl_announce, + xterm_title: "Serial Line #%d", + raw: 1, +}; + +static struct line serial_lines[NR_PORTS] = + { [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN) }; + +static int setup_ssl_irq(int fd, int input, int output, void *data) +{ + return(um_request_irq(SSL_IRQ, fd, line_interrupt, + SA_INTERRUPT | SA_SHIRQ, "ssl", data)); +} + +int ssl_open(struct tty_struct *tty, struct file *filp) +{ + int line; + + line = minor(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + return(line_open(serial_lines, line, tty, setup_ssl_irq, &opts)); +} + +static void ssl_close(struct tty_struct *tty, struct file * filp) +{ + line_close(serial_lines, minor(tty->device) - tty->driver.minor_start); +} + +static int ssl_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + line = minor(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + return(write_chan(&serial_lines[line].chan_list, buf, count)); +} + +static void ssl_put_char(struct tty_struct *tty, unsigned char ch) +{ + int line; + + line = minor(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + write_chan(&serial_lines[line].chan_list, &ch, sizeof(ch)); +} + +static void ssl_flush_chars(struct tty_struct *tty) +{ + return; +} + +static int ssl_write_room(struct tty_struct *tty) +{ + return(16384); +} + +static int ssl_chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +static void ssl_flush_buffer(struct tty_struct *tty) +{ + return; +} + +static int ssl_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + ret = 0; + switch(cmd){ + case TCGETS: + case TCSETS: + case TCFLSH: + case TCSETSF: + case TCSETSW: + case TCGETA: + ret = -ENOIOCTLCMD; + break; + default: + printk(KERN_ERR + "Unimplemented ioctl in ssl_ioctl : 0x%x\n", cmd); + ret = -ENOIOCTLCMD; + break; + } + return(ret); +} + +static void ssl_throttle(struct tty_struct * tty) +{ + printk(KERN_ERR "Someone should implement ssl_throttle\n"); +} + +static void ssl_unthrottle(struct tty_struct * tty) +{ + printk(KERN_ERR "Someone should implement ssl_unthrottle\n"); +} + +static void ssl_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ +} + +static void ssl_stop(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_stop\n"); +} + +static void ssl_start(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_start\n"); +} + +void ssl_hangup(struct tty_struct *tty) +{ +} + +static struct winch_lines winch = { + list : LIST_HEAD_INIT(winch.list), + lines : serial_lines, + nlines : sizeof(serial_lines)/sizeof(serial_lines[0]) +}; + +int ssl_init(void) +{ + int i, err; + + printk(KERN_INFO "Initializing software serial port version %d\n", + ssl_version); + + /* Initialize the tty_driver structure */ + + memset(&ssl_driver, 0, sizeof(struct tty_driver)); + ssl_driver.magic = TTY_DRIVER_MAGIC; + ssl_driver.name = "tts/%d"; + ssl_driver.major = TTYAUX_MAJOR; + ssl_driver.minor_start = 64; + ssl_driver.num = NR_PORTS; + ssl_driver.type = TTY_DRIVER_TYPE_SERIAL; + ssl_driver.subtype = 0; + ssl_driver.init_termios = tty_std_termios; + ssl_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + ssl_driver.flags = TTY_DRIVER_REAL_RAW; + ssl_driver.refcount = &ssl_refcount; + ssl_driver.table = ssl_table; + ssl_driver.termios = ssl_termios; + ssl_driver.termios_locked = ssl_termios_locked; + + ssl_driver.open = ssl_open; + ssl_driver.close = ssl_close; + ssl_driver.write = ssl_write; + ssl_driver.put_char = ssl_put_char; + ssl_driver.flush_chars = ssl_flush_chars; + ssl_driver.write_room = ssl_write_room; + ssl_driver.chars_in_buffer = ssl_chars_in_buffer; + ssl_driver.flush_buffer = ssl_flush_buffer; + ssl_driver.ioctl = ssl_ioctl; + ssl_driver.throttle = ssl_throttle; + ssl_driver.unthrottle = ssl_unthrottle; + ssl_driver.set_termios = ssl_set_termios; + ssl_driver.stop = ssl_stop; + ssl_driver.start = ssl_start; + ssl_driver.hangup = ssl_hangup; + if (tty_register_driver(&ssl_driver)) + panic("Couldn't register ssl driver\n"); + + err = devfs_mk_symlink(NULL, "serial", 0, "tts", NULL, NULL); + if(err) printk("Symlink creation from /dev/serial to /dev/tts " + "returned %d\n", err); + for(i = 0; i < sizeof(serial_lines)/sizeof(serial_lines[0]); i++){ + INIT_LIST_HEAD(&serial_lines[i].chan_list); + sema_init(&serial_lines[i].sem, 1); + } + + register_winch(&winch); + return(0); +} + +__initcall(ssl_init); + +static int ssl_chan_setup(char *str) +{ + line_setup(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0]), + str); + return(1); +} + +__setup("ssl", ssl_chan_setup); +__channel_help(ssl_chan_setup, "ssl"); + +static void ssl_exit(void) +{ + int i; + + for(i=0;idevice) - tty->driver.minor_start; + ret = open_console(line, tty); + chan_window_size(&vts[line].chan_list, &tty->winsize.ws_row, + &tty->winsize.ws_col); + return(ret); +} + +static void con_close(struct tty_struct * tty, struct file * filp) +{ + line_close(vts, minor(tty->device) - tty->driver.minor_start); +} + +static int con_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + if(in_interrupt() && tty->stopped) return 0; + while(tty->stopped) schedule(); + + line = minor(tty->device) - tty->driver.minor_start; + return(write_chan(&vts[line].chan_list, buf, count)); +} + +static int write_room(struct tty_struct *tty) +{ + return(1024); +} + +static void set_termios(struct tty_struct *tty, struct termios * old) +{ +} + +static int chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +static struct winch_lines winch = { + list : LIST_HEAD_INIT(winch.list), + lines : vts, + nlines : sizeof(vts)/sizeof(vts[0]) +}; + +int stdio_init(void) +{ + int i, err; + + printk(KERN_INFO "Initializing stdio console driver\n"); + memset(&console_driver, 0, sizeof(struct tty_driver)); + console_driver.magic = TTY_DRIVER_MAGIC; + console_driver.driver_name = "stdio console"; + console_driver.name = "vc/%d"; + console_driver.major = TTY_MAJOR; + console_driver.minor_start = 0; + console_driver.num = 8; + console_driver.type = TTY_DRIVER_TYPE_CONSOLE; + console_driver.subtype = SYSTEM_TYPE_CONSOLE; + console_driver.init_termios = tty_std_termios; + console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + console_driver.refcount = &console_refcount; + console_driver.table = console_table; + console_driver.termios = console_termios; + console_driver.termios_locked = console_termios_locked; + + console_driver.open = con_open; + console_driver.close = con_close; + console_driver.write = con_write; + console_driver.put_char = NULL; + console_driver.flush_chars = NULL; + console_driver.write_room = write_room; + console_driver.chars_in_buffer = chars_in_buffer; + console_driver.flush_buffer = NULL; + console_driver.ioctl = NULL; + console_driver.throttle = NULL; + console_driver.unthrottle = NULL; + console_driver.send_xchar = NULL; + console_driver.set_termios = set_termios; + console_driver.stop = NULL; + console_driver.start = NULL; + console_driver.hangup = NULL; + console_driver.break_ctl = NULL; + console_driver.wait_until_sent = NULL; + console_driver.read_proc = NULL; + if (tty_register_driver(&console_driver)) + panic("Couldn't register console driver\n"); + + err = devfs_mk_symlink(NULL, "ttys", 0, "vc", NULL, NULL); + if(err) printk("Symlink creation from /dev/ttys to /dev/vc " + "returned %d\n", err); + for(i=0;iindex].chan_list, string, len); +} + +static kdev_t console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index); +} + +static int console_setup(struct console *co, char *options) +{ + return(0); +} + +static struct console stdiocons = { + "tty", + console_write, + NULL, + console_device, + NULL, + NULL, + console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +void stdio_console_init(void) +{ + INIT_LIST_HEAD(&vts[0].chan_list); + list_add(&init_console_chan.list, &vts[0].chan_list); + register_console(&stdiocons); +} + +static int console_chan_setup(char *str) +{ + line_setup(vts, sizeof(vts)/sizeof(vts[0]), str); + return(1); +} + +__setup("con", console_chan_setup); +__channel_help(console_chan_setup, "con"); + +static void console_exit(void) +{ + int i; + + line_close(vts, 0); + for(i=0;i +#include +#include +#include +#include +#include "chan_user.h" +#include "user_util.h" +#include "user.h" + +struct tty_chan { + char *dev; + int raw; + struct termios tt; +}; + +void *tty_chan_init(char *str, int device, struct chan_opts *opts) +{ + struct tty_chan *data; + + if(*str != ':'){ + printk("tty_init : channel type 'tty' must specify " + "a device\n"); + return(NULL); + } + str++; + + if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + *data = ((struct tty_chan) { dev : str, + raw : opts->raw }); + + return(data); +} + +int tty_open(int input, int output, void *d) +{ + struct tty_chan *data = d; + int fd, mode; + + if(input && output) mode = O_RDWR; + else if(input) mode = O_RDONLY; + else mode = O_WRONLY; + + fd = open(data->dev, mode); + if(fd < 0) return(-errno); + if(data->raw){ + tcgetattr(fd, &data->tt); + raw(fd, 0); + } + return(fd); +} + +int tty_console_write(int fd, const char *buf, int n, void *d) +{ + struct tty_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops tty_ops = { + init: tty_chan_init, + open: tty_open, + close: generic_close, + read: generic_read, + write: generic_write, + console_write: tty_console_write, + window_size: generic_window_size, + free: generic_free, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/tuntap.h b/arch/um/drivers/tuntap.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/tuntap.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_TUNTAP_H +#define __UM_TUNTAP_H + +#include "net_user.h" + +struct tuntap_data { + char *dev_name; + char *gate_addr; + int fd; + void *dev; + unsigned char hw_addr[ETH_ADDR_LEN]; + int hw_setup; +}; + +extern struct net_user_info tuntap_user_info; + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/tuntap_kern.c b/arch/um/drivers/tuntap_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/tuntap_kern.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "linux/skbuff.h" +#include "asm/errno.h" +#include "net_kern.h" +#include "net_user.h" +#include "tuntap.h" + +struct tuntap_setup { + char *dev_name; + unsigned char hw_addr[ETH_ALEN]; + int hw_setup; + char *gate_addr; +}; + +struct tuntap_setup tuntap_priv[MAX_UML_NETDEV] = { + [ 0 ... MAX_UML_NETDEV - 1 ] = + { + dev_name: NULL, + hw_addr: { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + hw_setup: 0, + gate_addr: NULL, + } +}; + +struct net_device *tuntap_init(int private_size, int index) +{ + struct net_device *dev; + struct uml_net_private *pri; + struct tuntap_data *tpri; + + dev = init_etherdev(NULL, private_size); + if(dev == NULL) return(NULL); + pri = dev->priv; + tpri = (struct tuntap_data *) pri->user; + tpri->dev_name = tuntap_priv[index].dev_name; + tpri->gate_addr = tuntap_priv[index].gate_addr; + memcpy(dev->dev_addr, tuntap_priv[index].hw_addr, ETH_ALEN); + memcpy(tpri->hw_addr, tuntap_priv[index].hw_addr, + sizeof(tpri->hw_addr)); + printk("TUN/TAP backend - "); + if(tpri->gate_addr != NULL) + printk("IP = %s", tpri->gate_addr); + tpri->hw_setup = tuntap_priv[index].hw_setup; + if(tpri->hw_setup) + printk(" ether = %x:%x:%x:%x:%x:%x", + tpri->hw_addr[0], tpri->hw_addr[1], tpri->hw_addr[2], + tpri->hw_addr[3], tpri->hw_addr[4], tpri->hw_addr[5]); + printk("\n"); + tpri->fd = -1; + return(dev); +} + +static unsigned short tuntap_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +static int tuntap_set_mac(struct sockaddr *addr, void *data) +{ + struct tuntap_data *pri = data; + struct sockaddr *hwaddr = addr; + + memcpy(pri->hw_addr, hwaddr->sa_data, ETH_ALEN); + + return 0; +} + +static int tuntap_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(net_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER)); +} + +static int tuntap_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(net_write(fd, (*skb)->data, (*skb)->len)); +} + +struct net_kern_info tuntap_kern_info = { + init: tuntap_init, + protocol: tuntap_protocol, + set_mac: tuntap_set_mac, + read: tuntap_read, + write: tuntap_write, +}; + +static int tuntap_count = 0; + +void tuntap_setup(char *str, struct uml_net *dev) +{ + struct tuntap_setup *pri; + + dev->user = &tuntap_user_info; + dev->kern = &tuntap_kern_info; + dev->private_size = sizeof(struct tuntap_data); + pri = &tuntap_priv[tuntap_count]; + dev->transport_index = tuntap_count++; + tap_setup_common(str, "tuntap", &pri->dev_name, pri->hw_addr, + &pri->hw_setup, &pri->gate_addr); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/tuntap_kern.h b/arch/um/drivers/tuntap_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/tuntap_kern.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_TUNTAP_KERN_H +#define __UM_TUNTAP_KERN_H + +#include "net_kern.h" + +extern void tuntap_setup(char *arg, struct uml_net *dev); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/tuntap_user.c b/arch/um/drivers/tuntap_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/tuntap_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_user.h" +#include "tuntap.h" +#include "kern_util.h" +#include "user.h" + +#define MAX_PACKET ETH_MAX_PACKET + +void tuntap_user_init(void *data, void *dev) +{ + struct tuntap_data *pri = data; + + pri->dev = dev; +} + +struct tuntap_open_data { + char *name; + char *gate; + int data_fd; + int remote; + int me; + int err; + char *buffer; + int len; + int used; +}; + +static void tuntap_open_tramp(void *arg) +{ + struct tuntap_open_data *data = arg; + char version_buf[sizeof("nnnnn\0")]; + char *args[] = { "uml_net", version_buf, "tuntap", "up", data->gate, + NULL }; + char buf[CMSG_SPACE(sizeof(data->data_fd))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + int pid, n; + + sprintf(version_buf, "%d", UML_NET_VERSION); + data->err = 0; + if((pid = fork()) == 0){ + dup2(data->remote, 1); + close(data->me); + execvp(args[0], args); + printk("Exec of '%s' failed - errno = %d\n", args[0], errno); + exit(1); + } + else if(pid < 0) data->err = errno; + close(data->remote); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + if(data->buffer != NULL){ + iov = ((struct iovec) { data->buffer, data->len }); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + } + else { + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + } + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + msg.msg_flags = 0; + n = recvmsg(data->me, &msg, 0); + data->used = n; + if(n < 0){ + printk("tuntap_open_tramp : recvmsg failed - errno = %d\n", + errno); + data->err = errno; + return; + } + waitpid(pid, NULL, 0); + + cmsg = CMSG_FIRSTHDR(&msg); + if(cmsg == NULL){ + printk("tuntap_open_tramp : didn't receive a message\n"); + data->err = EINVAL; + return; + } + if((cmsg->cmsg_level != SOL_SOCKET) || + (cmsg->cmsg_type != SCM_RIGHTS)){ + printk("tuntap_open_tramp : didn't receive a descriptor\n"); + data->err = EINVAL; + return; + } + data->data_fd = ((int *) CMSG_DATA(cmsg))[0]; +} + +static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct tuntap_data *pri = data; + + if(pri->fd == -1) return; + open_addr(addr, netmask, pri->dev_name); +} + +static void tuntap_del_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct tuntap_data *pri = data; + + if(pri->fd == -1) return; + close_addr(addr, netmask, pri->dev_name); +} + +static int tuntap_open(void *data) +{ + struct tuntap_data *pri = data; + struct tuntap_open_data tap_data; + char *output; + int err, fds[2]; + + err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr); + if(err) return(err); + + if(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) < 0){ + printk("data socketpair failed - errno = %d\n", errno); + return(-errno); + } + + tap_data.me = fds[0]; + tap_data.remote = fds[1]; + tap_data.data_fd = -1; + tap_data.gate = pri->gate_addr; + tap_data.buffer = get_output_buffer(&tap_data.len); + if(tap_data.buffer != NULL) tap_data.len--; + tap_data.used = 0; + + tracing_cb(tuntap_open_tramp, &tap_data); + output = tap_data.buffer; + if(tap_data.err == 0){ + pri->dev_name = uml_strdup(tap_data.buffer); + output += IFNAMSIZ; + printk(output); + free_output_buffer(tap_data.buffer); + } + else { + printk(output); + free_output_buffer(tap_data.buffer); + printk("tuntap_open_tramp failed - errno = %d\n", + tap_data.err); + return(-tap_data.err); + } + close(fds[0]); + + pri->fd = tap_data.data_fd; + iter_addresses(pri->dev, open_addr, pri->dev_name); + return(tap_data.data_fd); +} + +static void tuntap_close(int fd, void *data) +{ + struct tuntap_data *pri = data; + + iter_addresses(pri->dev, close_addr, pri->dev_name); + close(fd); +} + +static int tuntap_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info tuntap_user_info = { + init: tuntap_user_init, + open: tuntap_open, + close: tuntap_close, + set_mtu: tuntap_set_mtu, + add_address: tuntap_add_addr, + delete_address: tuntap_del_addr, + max_packet: MAX_PACKET +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/ubd.c b/arch/um/drivers/ubd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/ubd.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,846 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "ubd_user.h" +#define MAJOR_NR UBD_MAJOR +#include "linux/config.h" +#include "linux/blk.h" +#include "linux/blkdev.h" +#include "linux/hdreg.h" +#include "linux/init.h" +#include "linux/devfs_fs_kernel.h" +#include "linux/cdrom.h" +#include "linux/proc_fs.h" +#include "linux/ctype.h" +#include "linux/capability.h" +#include "linux/mm.h" +#include "linux/vmalloc.h" +#include "asm/segment.h" +#include "asm/uaccess.h" +#include "asm/irq.h" +#include "asm/types.h" +#include "user_util.h" +#include "mem_user.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole_kern.h" +#include "init.h" +#include "irq_user.h" +#include "2_5compat.h" + +extern __u64 file_size(char *file); + +static int ubd_open(struct inode * inode, struct file * filp); +static int ubd_release(struct inode * inode, struct file * file); +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); +static request_queue_t *ubd_get_queue(kdev_t device); + +#define MAX_DEV (8) + +static int blk_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int hardsect_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = 0 }; + +static struct block_device_operations ubd_blops = { + open: ubd_open, + release: ubd_release, + ioctl: ubd_ioctl, +}; + +static struct hd_struct ubd_part[MAX_DEV] = +{ [ 0 ... MAX_DEV - 1 ] = { 0, 0, 0 } }; + +static request_queue_t *ubd_queue; + +static int fake_major = 0; + +static struct gendisk ubd_gendisk = { + MAJOR_NR, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +static struct gendisk fake_gendisk = { + 0, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +#ifdef CONFIG_BLK_DEV_UBD_SYNC +#define OPEN_FLAGS O_RDWR | O_SYNC +#else +#define OPEN_FLAGS O_RDWR +#endif + +struct cow { + char *file; + int fd; + unsigned long *bitmap; + unsigned long bitmap_len; + int bitmap_offset; + int data_offset; +}; + +struct ubd { + char *file; + int is_dir; + int count; + int fd; + __u64 size; + int boot_openflags; + int openflags; + devfs_handle_t real; + devfs_handle_t fake; + struct cow cow; +}; + +#define DEFAULT_COW { \ + file: NULL, \ + fd: -1, \ + bitmap: NULL, \ + bitmap_offset: 0, \ + data_offset: 0, \ +} + +#define DEFAULT_UBD { \ + file: NULL, \ + is_dir: 0, \ + count: 0, \ + fd: -1, \ + size: -1, \ + boot_openflags: OPEN_FLAGS, \ + openflags: OPEN_FLAGS, \ + real: NULL, \ + fake: NULL, \ + cow: DEFAULT_COW, \ +} + +struct ubd ubd_dev[MAX_DEV] = { +{ + file: "root_fs", + is_dir: 0, + count: 0, + fd: -1, + size: 0, + boot_openflags: OPEN_FLAGS, + openflags: OPEN_FLAGS, + real: NULL, + fake: NULL, + cow: DEFAULT_COW, +}, +[ 1 ... MAX_DEV - 1 ] = DEFAULT_UBD +}; + +static struct hd_driveid ubd_id = { + cyls: 0, + heads: 128, + sectors: 32, +}; + +static int fake_ide = 0; +static struct proc_dir_entry *proc_ide_root = NULL; +static struct proc_dir_entry *proc_ide = NULL; + +static void make_proc_ide(void) +{ + proc_ide_root = proc_mkdir("ide", 0); + proc_ide = proc_mkdir("ide0", proc_ide_root); +} + +static int proc_ide_read_media(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len; + + strcpy(page, "disk\n"); + len = strlen("disk\n"); + len -= off; + if (len < count){ + *eof = 1; + if (len <= 0) return 0; + } + else len = count; + *start = page + off; + return len; + +} + +static void make_ide_entries(char *dev_name) +{ + struct proc_dir_entry *dir, *ent; + char name[64]; + + if(!fake_ide) return; + if(proc_ide_root == NULL) make_proc_ide(); + dir = proc_mkdir(dev_name, proc_ide); + ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); + if(!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_ide_read_media; + ent->write_proc = NULL; + sprintf(name,"ide0/%s", dev_name); + proc_symlink(dev_name, proc_ide_root, name); +} + +static int fake_ide_setup(char *str) +{ + fake_ide = 1; + return(1); +} + +__setup("fake_ide", fake_ide_setup); +__uml_help(fake_ide_setup, +"fake_ide\n" +" Create ide0 entries that map onto ubd devices.\n\n" +); + +static int ubd_setup_common(char *str, int *index_out) +{ + char *backing_file; + int n, sync, perm = O_RDWR; + + if(index_out) *index_out = -1; + n = *str++; + if(n == '='){ + char *end; + int major; + + if(!strcmp(str, "sync")){ + sync = 1; + return(0); + } + major = simple_strtoul(str, &end, 0); + if(*end != '\0'){ + printk(KERN_ERR + "ubd_setup : didn't parse major number\n"); + return(1); + } + fake_gendisk.major = major; + fake_major = major; + printk(KERN_INFO "Setting extra ubd major number to %d\n", + major); + return(0); + } + if(n < '0'){ + printk(KERN_ERR "ubd_setup : index out of range\n"); + return(1); + } + n -= '0'; + if(n >= MAX_DEV){ + printk(KERN_ERR "ubd_setup : index out of range\n"); + return(1); + } + if(index_out) *index_out = n; + sync = ubd_dev[n].boot_openflags & O_SYNC; + if (*str == 'r') { + perm = O_RDONLY; + str++; + } + if (*str == 's') { + sync = O_SYNC; + str++; + } + if(*str++ != '='){ + printk(KERN_ERR "ubd_setup : Expected '='\n"); + return(1); + } + backing_file = strchr(str, ','); + if(backing_file){ + *backing_file = '\0'; + backing_file++; + } + ubd_dev[n].file = str; + ubd_dev[n].cow.file = backing_file; + ubd_dev[n].boot_openflags = perm | sync; + return(0); +} + +static int ubd_setup(char *str) +{ + ubd_setup_common(str, NULL); + return(1); +} + +__setup("ubd", ubd_setup); +__uml_help(ubd_setup, +"ubd=\n" +" This is used to associate a device with a file in the underlying\n" +" filesystem. Usually, there is a filesystem in the file, but \n" +" that's not required. Swap devices containing swap files can be\n" +" specified like this. Also, a file which doesn't contain a\n" +" filesystem can have its contents read in the virtual \n" +" machine by running dd on the device. n must be in the range\n" +" 0 to 7. Appending an 'r' to the number will cause that device\n" +" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n" +" an 's' (has to be _after_ 'r', if there is one) will cause data\n" +" to be written to disk on the host immediately.\n\n" +); + +static int fakehd(char *str) +{ + printk(KERN_INFO + "fakehd : Changing ubd_gendisk.major_name to \"hd\".\n"); + ubd_gendisk.major_name = "hd"; + return(1); +} + +__setup("fakehd", fakehd); +__uml_help(fakehd, +"fakehd\n" +" Change the ubd device name to \"hd\".\n\n" +); + +static void do_ubd_request(request_queue_t * q); + +int thread_fd = -1; + +int intr_count = 0; + +extern int errno; + +#ifdef notdef +#ifdef CONFIG_SMP +#error end_request needs some locking +#endif +#endif + +static void ubd_finish(int error) +{ + int nsect; + + if(error){ + end_request(0); + return; + } + nsect = CURRENT->current_nr_sectors; + CURRENT->sector += nsect; + CURRENT->buffer += nsect << 9; + CURRENT->errors = 0; + CURRENT->nr_sectors -= nsect; + CURRENT->current_nr_sectors = 0; + end_request(1); +} + +static void ubd_handler(void) +{ + struct io_thread_req req; + + DEVICE_INTR = NULL; + intr_count++; + if(read_ubd_fs(thread_fd, &req, sizeof(req)) != sizeof(req)){ + printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " + "errno = %d\n", getpid(), errno); + end_request(0); + return; + } + + if((req.offset != ((__u64) (CURRENT->sector)) << 9) || + (req.length != (CURRENT->current_nr_sectors) << 9)) + panic("I/O op mismatch"); + + ubd_finish(req.error); + reactivate_fd(thread_fd); + do_ubd_request(ubd_queue); +} + +static void ubd_intr(int irq, void *dev, struct pt_regs *unused) +{ + ubd_handler(); +} + +static int io_pid = -1; + +void kill_io_thread(void) +{ + if(io_pid != -1) kill(io_pid, SIGKILL); +} + +__uml_exitcall(kill_io_thread); + +int sync = 0; + +devfs_handle_t ubd_dir_handle; +devfs_handle_t ubd_fake_dir_handle; + +static int ubd_add(int n) +{ + char name[sizeof("nnnnnn\0")], dev_name[sizeof("ubd0x")]; + + if(ubd_dev[n].file == NULL) return(-1); + sprintf(name, "%d", n); + ubd_dev[n].real = devfs_register(ubd_dir_handle, name, + DEVFS_FL_DEFAULT, MAJOR_NR, n, + S_IFBLK | S_IRUSR | S_IWUSR | + S_IRGRP |S_IWGRP, + &ubd_blops, NULL); + if(fake_major != 0){ + ubd_dev[n].fake = devfs_register(ubd_fake_dir_handle, name, + DEVFS_FL_DEFAULT, fake_major, + n, S_IFBLK | S_IRUSR | + S_IWUSR | S_IRGRP | S_IWGRP, + &ubd_blops, NULL); + } + if(!strcmp(ubd_gendisk.major_name, "ubd")){ + sprintf(dev_name, "%s%d", ubd_gendisk.major_name, n); + } + else sprintf(dev_name, "%s%c", ubd_gendisk.major_name, + n + 'a'); + make_ide_entries(dev_name); + return(0); +} + +static int ubd_config(char *str) +{ + int n, err; + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "ubd_config failed to strdup string\n"); + return(1); + } + err = ubd_setup_common(str, &n); + if(err){ + kfree(str); + return(-1); + } + if(n != -1) ubd_add(n); + return(0); +} + +static int ubd_remove(char *str) +{ + int n; + + if(!isdigit(*str)) return(-1); + n = *str - '0'; + if(ubd_dev[n].file == NULL) return(0); + if(ubd_dev[n].count > 0) return(-1); + if(ubd_dev[n].real != NULL) devfs_unregister(ubd_dev[n].real); + if(ubd_dev[n].fake != NULL) devfs_unregister(ubd_dev[n].fake); + ubd_dev[n] = ((struct ubd) DEFAULT_UBD); + return(0); +} + +static struct mc_device ubd_mc = { + name: "ubd", + config: ubd_config, + remove: ubd_remove, +}; + +int ubd_mc_init(void) +{ + mconsole_register_dev(&ubd_mc); + return(0); +} + +__initcall(ubd_mc_init); + +int ubd_init(void) +{ + unsigned long stack; + int i, err; + + ubd_dir_handle = devfs_mk_dir (NULL, "ubd", NULL); + if (devfs_register_blkdev(MAJOR_NR, "ubd", &ubd_blops)) { + printk(KERN_ERR "ubd: unable to get major %d\n", MAJOR_NR); + return -1; + } + ubd_queue = BLK_DEFAULT_QUEUE(MAJOR_NR); + blk_init_queue(ubd_queue, DEVICE_REQUEST); + elevator_init(&ubd_queue->elevator, ELEVATOR_NOOP); + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[MAJOR_NR] = blk_sizes; + blk_size[MAJOR_NR] = sizes; + hardsect_size[MAJOR_NR] = hardsect_sizes; + add_gendisk(&ubd_gendisk); + if (fake_major != 0){ + char name[sizeof("ubd_nnn\0")]; + + snprintf(name, sizeof(name), "ubd_%d", fake_major); + ubd_fake_dir_handle = devfs_mk_dir(NULL, name, NULL); + if(devfs_register_blkdev(fake_major, "ubd", &ubd_blops)) { + printk(KERN_ERR "ubd: unable to get major %d\n", + fake_major); + return -1; + } + blk_dev[fake_major].queue = ubd_get_queue; + read_ahead[fake_major] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[fake_major] = blk_sizes; + hardsect_size[fake_major] = hardsect_sizes; + add_gendisk(&fake_gendisk); + } + for(i=0;ifd); + if(dev->cow.file != NULL) { + close_fd(dev->cow.fd); + vfree(dev->cow.bitmap); + dev->cow.bitmap = NULL; + } +} + +static int ubd_open_dev(struct ubd *dev) +{ + char *backing_file; + int err, flags, n; + + dev->fd = open_ubd_file(dev->file, &dev->openflags, &backing_file, + &dev->cow.bitmap_offset, &dev->cow.bitmap_len, + &dev->cow.data_offset); + + if((dev->fd == -ENOENT) && (dev->cow.file != NULL)){ + printk(KERN_INFO "Creating \"%s\" as COW file for \"%s\"\n", + dev->file, dev->cow.file); + n = dev - ubd_dev; + dev->fd = create_cow_file(dev->file, dev->cow.file, 1 << 9, + &dev->cow.bitmap_offset, + &dev->cow.bitmap_len, + &dev->cow.data_offset); + if(dev->fd < 0){ + printk(KERN_ERR "Creation of COW file \"%s\" failed, " + "errno = %d\n", dev->file, -dev->fd); + } + } + + if(dev->fd < 0) return(dev->fd); + + if(dev->cow.file == NULL) dev->cow.file = backing_file; + if((backing_file != NULL) && strcmp(dev->cow.file, backing_file)){ + printk(KERN_WARNING "Backing file mismatch - \"%s\" " + "requested, \"%s\" specified in COW header of \"%s\"\n", + dev->cow.file, backing_file, dev->file); + printk(KERN_WARNING "Using \"%s\"\n", backing_file); + dev->cow.file = backing_file; + } + + if(dev->cow.file != NULL){ + err = -ENOMEM; + dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len); + if(dev->cow.bitmap == NULL) goto error; + flush_tlb_kernel_vm(); + + err = read_cow_bitmap(dev->fd, dev->cow.bitmap, + dev->cow.bitmap_offset, + dev->cow.bitmap_len); + if(err) goto error; + + flags = O_RDONLY; + err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, + NULL); + if(err < 0) goto error; + dev->cow.fd = err; + } + return(0); + error: + close_fd(dev->fd); + return(err); +} + +static int ubd_open(struct inode * inode, struct file * filp) +{ + char *file; + int n; + + n = DEVICE_NR(inode->i_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(ubd_is_dir(ubd_dev[n].file)){ + ubd_dev[n].is_dir = 1; + return(0); + } + if(ubd_dev[n].count == 0){ + ubd_dev[n].openflags = ubd_dev[n].boot_openflags; + /* XXX This error is wrong when errno isn't stored in + * ubd_dev[n].fd + */ + if(ubd_open_dev(&ubd_dev[n]) < 0){ + printk(KERN_ERR "ubd%d: Can't open \"%s\": " + "errno = %d\n", n, ubd_dev[n].file, + -ubd_dev[n].fd); + } + if(ubd_dev[n].fd < 0) + return -ENODEV; + file = ubd_dev[n].cow.file ? ubd_dev[n].cow.file : + ubd_dev[n].file; + ubd_dev[n].size = file_size(file); + if(ubd_dev[n].size < 0) return(ubd_dev[n].size); + ubd_part[n].start_sect = 0; + ubd_part[n].nr_sects = ubd_dev[n].size / blk_sizes[n]; + sizes[n] = ubd_dev[n].size / BLOCK_SIZE; + } + ubd_dev[n].count++; + if ((filp->f_mode & FMODE_WRITE) && + ((ubd_dev[n].openflags & ~O_SYNC) == O_RDONLY)){ + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); + return -EROFS; + } + return(0); +} + +static int ubd_release(struct inode * inode, struct file * file) +{ + int n; + + n = DEVICE_NR(inode->i_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(--ubd_dev[n].count == 0) ubd_close(&ubd_dev[n]); + return(0); +} + +int cow_read = 0; +int cow_write = 0; + +void cowify_req(struct io_thread_req *req, struct ubd *dev) +{ + int i, update_bitmap, sector = req->offset >> 9; + + if(req->length > (sizeof(req->sector_mask) * 8) << 9) + panic("Operation too long"); + if(req->op == UBD_READ) { + for(i = 0; i < req->length >> 9; i++){ + if(ubd_test_bit(sector + i, dev->cow.bitmap)){ + ubd_set_bit(i, &req->sector_mask); + cow_read++; + } + } + } + else { + update_bitmap = 0; + for(i = 0; i < req->length >> 9; i++){ + cow_write++; + ubd_set_bit(i, &req->sector_mask); + if(!ubd_test_bit(sector + i, dev->cow.bitmap)) + update_bitmap = 1; + ubd_set_bit(sector + i, dev->cow.bitmap); + } + if(update_bitmap){ + req->cow_offset = sector / (sizeof(unsigned long) * 8); + req->bitmap_words[0] = + dev->cow.bitmap[req->cow_offset]; + req->bitmap_words[1] = + dev->cow.bitmap[req->cow_offset + 1]; + req->cow_offset *= sizeof(unsigned long); + req->cow_offset += dev->cow.bitmap_offset; + } + } +} + +static void do_one_request(request_queue_t *q) +{ + struct request *req; + struct io_thread_req io_req; + int block, nsect, dev, n, err; + + if(DEVICE_INTR) return; + if(list_empty(&q->queue_head)) return; + req = blkdev_entry_next_request(&q->queue_head); + if(req->rq_status == RQ_INACTIVE) return; + if((req->bh) && !buffer_locked(req->bh)) + panic(DEVICE_NAME ": block not locked"); + + block = req->sector; + nsect = req->current_nr_sectors; + dev = minor(req->rq_dev); + if(ubd_dev[dev].is_dir){ + strcpy(req->buffer, "HOSTFS:"); + strcat(req->buffer, ubd_dev[dev].file); + end_request(1); + return; + } + if((req->cmd == WRITE) && + ((ubd_dev[dev].openflags & O_ACCMODE) == O_RDONLY)){ + printk("Write attempted on readonly ubd device %d\n", + dev); + end_request(0); + return; + } + + io_req.op = (req->cmd == READ) ? UBD_READ : UBD_WRITE; + io_req.fds[0] = (ubd_dev[dev].cow.file != NULL) ? + ubd_dev[dev].cow.fd : ubd_dev[dev].fd; + io_req.fds[1] = ubd_dev[dev].fd; + io_req.offsets[0] = 0; + io_req.offsets[1] = ubd_dev[dev].cow.data_offset; + io_req.offset = ((__u64) block) << 9; + io_req.length = nsect << 9; + io_req.buffer = req->buffer; + io_req.sectorsize = 1 << 9; + io_req.sector_mask = 0; + io_req.cow_offset = -1; + io_req.error = 0; + + if(ubd_dev[dev].cow.file != NULL) cowify_req(&io_req, &ubd_dev[dev]); + + if(thread_fd == -1){ + err = do_io(&io_req); + ubd_finish(err); + } + else { + SET_INTR(ubd_handler); + n = write_ubd_fs(thread_fd, (char *) &io_req, sizeof(io_req)); + if(n != sizeof(io_req)) + printk("write to io thread failed - returned %d, " + "errno %d\n", n, errno); + } +} + +static request_queue_t *ubd_get_queue(kdev_t device) +{ + return(ubd_queue); +} + +static void do_ubd_request(request_queue_t *q) +{ + if(thread_fd == -1){ + while(!QUEUE_EMPTY) do_one_request(q); + } + else do_one_request(q); +} + +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct hd_geometry *loc = (struct hd_geometry *) arg; + int dev, err; + + if(!inode) return -EINVAL; + dev = DEVICE_NR(inode->i_rdev); + if (dev > MAX_DEV) + return -EINVAL; + switch (cmd) { + struct hd_geometry g; + struct cdrom_volctrl volume; + case HDIO_GETGEO: + if (!loc) return -EINVAL; + g.heads = 128; + g.sectors = 32; + g.cylinders = ubd_dev[dev].size / (128 * 32); + g.start = 2; + return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; + case BLKRASET: + if(!capable(CAP_SYS_ADMIN)) return -EACCES; + if(arg > 0xff) return -EINVAL; + read_ahead[major(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + put_user(ubd_dev[dev].size >> 9, (long *) arg); + return 0; + case BLKFLSBUF: + if(!capable(CAP_SYS_ADMIN)) return -EACCES; + return 0; + + case BLKRRPART: /* Re-read partition tables */ + return 0; /* revalidate_hddisk(inode->i_rdev, 1); */ + + case HDIO_SET_UNMASKINTR: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if ((arg > 1) || (minor(inode->i_rdev) & 0x3F)) + return -EINVAL; + return 0; + + case HDIO_GET_UNMASKINTR: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_GET_MULTCOUNT: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_SET_MULTCOUNT: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (minor(inode->i_rdev) & 0x3F) return -EINVAL; + return 0; + + case HDIO_GET_IDENTITY: + ubd_id.cyls = ubd_dev[dev].size / (128 * 32); + if (copy_to_user((char *) arg, (char *) &ubd_id, + sizeof(ubd_id))) + return -EFAULT; + return 0; + + case CDROMVOLREAD: + if(copy_from_user(&volume, (char *) arg, sizeof(volume))) + return -EFAULT; + volume.channel0 = 255; + volume.channel1 = 255; + volume.channel2 = 255; + volume.channel3 = 255; + if(copy_to_user((char *) arg, &volume, sizeof(volume))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/ubd_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asm/types.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "ubd_user.h" + +#include +#include +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(x) (x) +# define htonll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define ntohll(x) bswap_64(x) +# define htonll(x) bswap_64(x) +#else +#error "__BYTE_ORDER not defined" +#endif + +extern void panic(char *fmt, ...); + +#define PATH_LEN_V1 256 + +struct cow_header_v1 { + int magic; + int version; + char backing_file[PATH_LEN_V1]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +#define PATH_LEN_V2 MAXPATHLEN + +struct cow_header_v2 { + unsigned long magic; + unsigned long version; + char backing_file[PATH_LEN_V2]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +union cow_header { + struct cow_header_v1 v1; + struct cow_header_v2 v2; +}; + +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ +#define COW_VERSION 2 + +static void sizes(__u64 size, int sectorsize, int bitmap_offset, + unsigned long *bitmap_len_out, int *data_offset_out) +{ + *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = (*data_offset_out + sectorsize - 1) / sectorsize; + *data_offset_out *= sectorsize; +} + +static int read_cow_header(int fd, int *magic_out, char **backing_file_out, + time_t *mtime_out, __u64 *size_out, + int *sectorsize_out, int *bitmap_offset_out) +{ + union cow_header *header; + char *file; + int err, n; + unsigned long version, magic; + + header = um_kmalloc(sizeof(*header)); + if(header == NULL){ + printk("read_cow_header - Failed to allocate header\n"); + return(-ENOMEM); + } + err = -EINVAL; + n = read(fd, header, sizeof(*header)); + if(n < offsetof(typeof(header->v1), backing_file)){ + printk("read_cow_header - short header\n"); + goto out; + } + + err = 0; + magic = header->v1.magic; + if(magic == COW_MAGIC) { + version = header->v1.version; + } + else if(magic == ntohl(COW_MAGIC)){ + version = ntohl(header->v1.version); + } + else goto out; + + *magic_out = COW_MAGIC; + + err = -EINVAL; + if(version == 1){ + if(n < sizeof(header->v1)){ + printk("read_cow_header - failed to read V1 header\n"); + goto out; + } + *mtime_out = header->v1.mtime; + *size_out = header->v1.size; + *sectorsize_out = header->v1.sectorsize; + *bitmap_offset_out = sizeof(header->v1); + file = header->v1.backing_file; + } + else if(version == 2){ + if(n < sizeof(header->v2)){ + printk("read_cow_header - failed to read V2 header\n"); + goto out; + } + *mtime_out = ntohl(header->v2.mtime); + *size_out = ntohll(header->v2.size); + *sectorsize_out = ntohl(header->v2.sectorsize); + *bitmap_offset_out = sizeof(header->v2); + file = header->v2.backing_file; + } + else { + printk("read_cow_header - invalid COW version\n"); + goto out; + } + err = -ENOMEM; + *backing_file_out = uml_strdup(file); + if(*backing_file_out == NULL){ + printk("read_cow_header - failed to allocate backing file\n"); + goto out; + } + err = 0; + out: + kfree(header); + return(err); +} + +int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_offset_out, unsigned long *bitmap_len_out, + int *data_offset_out) +{ + struct stat64 buf; + time_t mtime; + __u64 size; + int fd, err, sectorsize, magic, mode = 0644; + + if(backing_file_out != NULL) *backing_file_out = NULL; + if((fd = open64(file, *openflags, mode)) < 0){ + if(((*openflags & O_ACCMODE) != O_RDWR) || + ((errno != EROFS) && (errno != EACCES))) return(-errno); + *openflags &= ~O_ACCMODE; + *openflags |= O_RDONLY; + if((fd = open64(file, *openflags, mode)) < 0) return(-errno); + } + if(backing_file_out == NULL) return(fd); + + err = read_cow_header(fd, &magic, backing_file_out, &mtime, &size, + §orsize, bitmap_offset_out); + if(err) goto error; + + if(magic != COW_MAGIC){ + *backing_file_out = NULL; + return(fd); + } + + err = stat64(*backing_file_out, &buf); + if(err) goto error; + + err = -EINVAL; + if(buf.st_size != size){ + printk("Size mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_size, size); + goto error; + } + if(buf.st_mtime != mtime){ + printk("mtime mismatch (%ld vs %ld) of COW header vs backing " + "file\n", buf.st_mtime, mtime); + goto error; + } + + sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out, + data_offset_out); + + return(fd); + error: + close(fd); + return(err); +} + +int read_cow_bitmap(int fd, void *buf, int offset, int len) +{ + int err; + + err = lseek64(fd, offset, SEEK_SET); + if(err != offset) return(-errno); + err = read(fd, buf, len); + if(err < 0) return(-errno); + return(0); +} + +static int absolutize(char *to, int size, char *from) +{ + char save_cwd[256], *slash; + int remaining; + + if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { + printk("absolutize : unable to get cwd - errno = %d\n", errno); + return(-1); + } + slash = strrchr(from, '/'); + if(slash != NULL){ + *slash = '\0'; + if(chdir(from)){ + *slash = '/'; + printk("absolutize : Can't cd to '%s' - errno = %d\n", + from, errno); + return(-1); + } + *slash = '/'; + if(getcwd(to, size) == NULL){ + printk("absolutize : unable to get cwd of '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + remaining = size - strlen(to); + if(strlen(slash) + 1 > remaining){ + printk("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcat(to, slash); + } + else { + if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ + printk("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcpy(to, save_cwd); + strcat(to, "/"); + strcat(to, from); + } + chdir(save_cwd); + return(0); +} + +int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_offset_out, unsigned long *bitmap_len_out, + int *data_offset_out) +{ + struct cow_header_v2 *header; + struct stat64 buf; + __u64 blocks; + long zero; + int err, fd, i, flags; + __u64 size; + + flags = O_RDWR | O_CREAT; + fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL); + if(fd < 0) return(fd); + + err = -ENOMEM; + header = um_kmalloc(sizeof(*header)); + if(header == NULL){ + printk("Failed to allocate COW V2 header\n"); + goto out_close; + } + header->magic = htonl(COW_MAGIC); + header->version = htonl(COW_VERSION); + + err = -EINVAL; + if(strlen(backing_file) > sizeof(header->backing_file) - 1){ + printk("Backing file name \"%s\" is too long - names are " + "limited to %d characters\n", backing_file, + sizeof(header->backing_file) - 1); + goto out_free; + } + + if(absolutize(header->backing_file, sizeof(header->backing_file), + backing_file)) + goto out_free; + + err = stat64(header->backing_file, &buf); + if(err < 0) goto out_free; + + header->mtime = htonl(buf.st_mtime); + header->size = htonll(buf.st_size); + header->sectorsize = htonl(sectorsize); + size = buf.st_size; + + err = write(fd, header, sizeof(*header)); + if(err != sizeof(*header)) goto out_free; + + blocks = (size + sectorsize - 1) / sectorsize; + blocks = (blocks + sizeof(long) * 8 - 1) / (sizeof(long) * 8); + zero = 0; + for(i = 0; i < blocks; i++){ + err = write(fd, &zero, sizeof(zero)); + if(err != sizeof(zero)) goto out_free; + } + + sizes(size, sectorsize, sizeof(struct cow_header_v2), + bitmap_len_out, data_offset_out); + *bitmap_offset_out = sizeof(struct cow_header_v2); + + kfree(header); + return(fd); + + out_free: + kfree(header); + out_close: + close(fd); + return(err); +} + +int read_ubd_fs(int fd, void *buffer, int len) +{ + return(read(fd, buffer, len)); +} + +int write_ubd_fs(int fd, char *buffer, int len) +{ + return(write(fd, buffer, len)); +} + +int ubd_is_dir(char *file) +{ + struct stat64 buf; + + if(stat64(file, &buf) < 0) return(0); + return(S_ISDIR(buf.st_mode)); +} + +int do_io(struct io_thread_req *req) +{ + char *buf; + unsigned long len; + int n, nsectors, start, end, bit; + __u64 off; + nsectors = req->length / req->sectorsize; + start = 0; + do { + bit = ubd_test_bit(start, &req->sector_mask); + end = start; + while((end < nsectors) && + (ubd_test_bit(end, &req->sector_mask) == bit)) + end++; + + if(end != nsectors) + printk("end != nsectors\n"); + off = req->offset + req->offsets[bit]; + len = (end - start) * req->sectorsize; + buf = &req->buffer[start * req->sectorsize]; + + if(lseek64(req->fds[bit], off, SEEK_SET) != off){ + printk("do_io - lseek failed : errno = %d\n", + errno); + return(-1); + } + if(req->op == UBD_READ){ + n = 0; + do { + buf = &buf[n]; + len -= n; + n = read(req->fds[bit], buf, len); + if (n < 0) { + printk("do_io - read returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + } while((n < len) && (n != 0)); + if (n < len) memset(&buf[n], 0, len - n); + } + else { + n = write(req->fds[bit], buf, len); + if(n != len){ + printk("do_io - write returned %d : " + "errno = %d fd = %d\n", n, + errno, req->fds[bit]); + return(-1); + } + } + + start = end; + } while(start < nsectors); + + if(req->cow_offset != -1){ + if(lseek64(req->fds[1], req->cow_offset, SEEK_SET) != + req->cow_offset){ + printk("do_io - bitmap lseek failed : errno = %d\n", + errno); + return(-1); + } + n = write(req->fds[1], &req->bitmap_words, + sizeof(req->bitmap_words)); + if(n != sizeof(req->bitmap_words)){ + printk("do_io - bitmap update returned %d : " + "errno = %d fd = %d\n", n, errno, req->fds[1]); + return(-1); + } + } + return(0); +} + +int kernel_fd = -1; + +int io_count = 0; + +int io_thread(void *arg) +{ + struct io_thread_req req; + int n; + + signal(SIGWINCH, SIG_IGN); + while(1){ + n = read(kernel_fd, &req, sizeof(req)); + if(n < 0) printk("io_thread - read returned %d, errno = %d\n", + n, errno); + else if(n < sizeof(req)){ + printk("io_thread - short read : length = %d\n", n); + continue; + } + io_count++; + if(do_io(&req)) req.error = 1; + n = write(kernel_fd, &req, sizeof(req)); + if(n != sizeof(req)) + printk("io_thread - write failed, errno = %d\n", + errno); + } +} + +int start_io_thread(unsigned long sp, int *fd_out) +{ + int pid, fds[2]; + + if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0){ + printk("start_io_thread - socketpair failed, errno = %d\n", + errno); + return(-1); + } + kernel_fd = fds[0]; + *fd_out = fds[1]; + + pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD, + NULL); + if(pid < 0){ + printk("start_io_thread - clone failed : errno = %d\n", errno); + return(-errno); + } + return(pid); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/drivers/xterm.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "kern_util.h" +#include "user_util.h" +#include "chan_user.h" +#include "user.h" + +struct xterm_chan { + int pid; + int fd; + char *title; + int device; + int raw; + struct termios tt; +}; + +void *xterm_init(char *str, int device, struct chan_opts *opts) +{ + struct xterm_chan *data; + + if((data = malloc(sizeof(*data))) == NULL) return(NULL); + *data = ((struct xterm_chan) { pid : -1, + device : device, + title : opts->xterm_title, + raw : opts->raw }); + return(data); +} + +struct xterm_info { + char tty[2]; + int fd; + int slave; + int console_num; + int *pid_out; + char *title; +}; + +static void xterm_tramp(void *arg) +{ + struct xterm_info *info; + int pid; + char title[256], flag[sizeof("Sxxnn\0")], c; + + info = arg; + sprintf(flag, "-S%c%c%d", info->tty[0], info->tty[1], info->fd); + sprintf(title, info->title, info->console_num); + if((pid = fork()) != 0) *info->pid_out = pid; + else { + execlp("xterm", "xterm", flag, "-T", title, NULL); + printk("execlp of xterm failed - errno = %d\n", errno); + close(info->fd); + exit(1); + } + close(info->fd); + while((read(info->slave, &c, sizeof(c)) == sizeof(c)) && (c != '\n')) ; +} + +int xterm_open(int input, int output, void *d) +{ + struct xterm_chan *data = d; + struct xterm_info info; + int master, slave; + char dev[] = "/dev/ptyXX"; + + master = getmaster(dev); + if(master == -1){ + printk("No unused host ptys found\n"); + return(-ENODEV); + } + dev[strlen("/dev/")] = 't'; + slave = open(dev, O_RDWR); + if(slave == -1) return(-errno); + tcgetattr(slave, &data->tt); + raw(slave, 0); + info.tty[0] = dev[strlen("/dev/pty")]; + info.tty[1] = dev[strlen("/dev/ptyX")]; + info.fd = master; + info.slave = slave; + info.console_num = data->device; + info.pid_out = &data->pid; + info.title = data->title; + tracing_cb(xterm_tramp, &info); + tcsetattr(slave, TCSADRAIN, &data->tt); + if(data->raw) raw(slave, 0); + data->fd = slave; + return(slave); +} + +void xterm_close(int fd, void *d) +{ + struct xterm_chan *data = d; + + if(data->pid != -1) kill(data->pid, SIGKILL); + close(fd); +} + +void xterm_free(void *d) +{ + free(d); +} + +int xterm_console_write(int fd, const char *buf, int n, void *d) +{ + struct xterm_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops xterm_ops = { + init: xterm_init, + open: xterm_open, + close: xterm_close, + read: generic_read, + write: generic_write, + console_write: xterm_console_write, + window_size: generic_window_size, + free: xterm_free, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/fs/Makefile b/arch/um/fs/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/fs/Makefile Wed Feb 13 20:03:56 2002 @@ -0,0 +1,16 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +O_TARGET := fs.o + +subdir-$(CONFIG_HOSTFS) = hostfs + +MOD_SUB_DIRS := $(subdir-m) +SUB_DIRS := $(subdir-y) + +obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) +obj-m += $(join $(subdir-m),$(subdir-m:%=/%.o)) + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/fs/hostfs/Makefile b/arch/um/fs/hostfs/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/fs/hostfs/Makefile Wed Feb 13 20:04:02 2002 @@ -0,0 +1,31 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +# struct stat64 changed the inode field name between 2.2 and 2.4 from st_ino +# to __st_ino. It stayed in the same place, so as long as the correct name +# is used, hostfs compiled on 2.2 should work on 2.4 and vice versa. + +STAT64_INO_FIELD := $(shell grep -q __st_ino /usr/include/bits/stat.h && \ + echo __)st_ino + +USER_CFLAGS := $(USER_CFLAGS) -DSTAT64_INO_FIELD=$(STAT64_INO_FIELD) + +O_TARGET := +obj-y = +obj-m = + +CFLAGS_hostfs_kern.o := $(CFLAGS) +CFLAGS_hostfs_user.o := $(USER_CFLAGS) + +ifneq ($(CONFIG_HOSTFS), n) + O_TARGET := hostfs.o +endif + +obj-y += hostfs_kern.o hostfs_user.o +obj-m += $(O_TARGET) + +override CFLAGS = + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/fs/hostfs/hostfs.h b/arch/um/fs/hostfs/hostfs.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/fs/hostfs/hostfs.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,74 @@ +#ifndef __UM_FS_HOSTFS +#define __UM_FS_HOSTFS + +#define HOSTFS_FILE 1 +#define HOSTFS_DIR 2 +#define HOSTFS_SYMLINK 3 +#define HOSTFS_CHARDEV 4 +#define HOSTFS_BLOCDEV 5 +#define HOSTFS_FIFO 6 +#define HOSTFS_SOCK 7 + +/* These are exactly the same definitions as in fs.h, but the names are + * changed so that this file can be included in both kernel and user files. + */ + +#define HOSTFS_ATTR_MODE 1 +#define HOSTFS_ATTR_UID 2 +#define HOSTFS_ATTR_GID 4 +#define HOSTFS_ATTR_SIZE 8 +#define HOSTFS_ATTR_ATIME 16 +#define HOSTFS_ATTR_MTIME 32 +#define HOSTFS_ATTR_CTIME 64 +#define HOSTFS_ATTR_ATIME_SET 128 +#define HOSTFS_ATTR_MTIME_SET 256 +#define HOSTFS_ATTR_FORCE 512 /* Not a change, but a change it */ +#define HOSTFS_ATTR_ATTR_FLAG 1024 + +struct hostfs_iattr { + unsigned int ia_valid; + mode_t ia_mode; + uid_t ia_uid; + gid_t ia_gid; + loff_t ia_size; + time_t ia_atime; + time_t ia_mtime; + time_t ia_ctime; + unsigned int ia_attr_flags; +}; + +extern int stat_file(const char *path, int *dev_out, unsigned long long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, + int *gid_out, unsigned long long *size_out, + unsigned long *atime_out, unsigned long *mtime_out, + unsigned long *ctime_out, int *blksize_out, + unsigned long long *blocks_out); +extern int access_file(char *path, int r, int w, int x); +extern int open_file(char *path, int r, int w); +extern int file_type(const char *path, int *rdev); +extern void *open_dir(char *path, int *err_out); +extern char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out); +extern void close_file(void *stream); +extern void close_dir(void *stream); +extern int read_file(int fd, unsigned long long *offset, char *buf, int len); +extern int write_file(int fd, unsigned long long *offset, const char *buf, + int len); +extern int lseek_file(int fd, long long offset, int whence); +extern int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox); +extern int set_attr(const char *file, struct hostfs_iattr *attrs); +extern int make_symlink(const char *from, const char *to); +extern int unlink_file(const char *file); +extern int do_mkdir(const char *file, int mode); +extern int do_rmdir(const char *file); +extern int do_mknod(const char *file, int mode, int dev); +extern int link_file(const char *from, const char *to); +extern int do_readlink(char *file, char *buf, int size); +extern int rename_file(char *from, char *to); +extern int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, long long *files_out, + long long *ffree_out, void *fsid_out, int fsid_size, + long *namelen_out, long *spare_out); + +#endif diff -Nru a/arch/um/fs/hostfs/hostfs_kern.c b/arch/um/fs/hostfs/hostfs_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/fs/hostfs/hostfs_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "kern.h" +#include "user_util.h" +#include "2_5compat.h" + +#define file_hostfs_i(file) (&(file)->f_dentry->d_inode->u.hostfs_i) + +int hostfs_d_delete(struct dentry *dentry) +{ + return(1); +} + +struct dentry_operations hostfs_dentry_ops = { + d_delete: hostfs_d_delete, +}; + +static char *root_ino = "/"; + +#define HOSTFS_SUPER_MAGIC 0x00c0ffee + +static struct inode_operations hostfs_iops; +static struct address_space_operations hostfs_link_aops; + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *root, *name; + int len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + len += parent->d_name.len + 1; + parent = parent->d_parent; + } + + root = parent->d_inode->u.hostfs_i.host_filename; + len += strlen(root); + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + len -= parent->d_name.len + 1; + name[len] = '/'; + strncpy(&name[len + 1], parent->d_name.name, + parent->d_name.len); + parent = parent->d_parent; + } + strncpy(name, root, strlen(root)); + return(name); +} + +static char *inode_name(struct inode *ino, int extra) +{ + struct dentry *dentry; + + dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); + return(dentry_name(dentry, extra)); +} + +static int read_name(struct inode *ino, char *name) +{ + /* The non-int inode fields are copied into ints by stat_file and + * then copied into the inode because passing the actual pointers + * in and having them treated as int * breaks on big-endian machines + */ + int err; + int i_dev, i_mode, i_nlink, i_blksize; + unsigned long long i_size; + unsigned long long i_ino; + unsigned long long i_blocks; + err = stat_file(name, &i_dev, &i_ino, &i_mode, &i_nlink, + &ino->i_uid, &ino->i_gid, &i_size, &ino->i_atime, + &ino->i_mtime, &ino->i_ctime, &i_blksize, &i_blocks); + if(err) return(err); + ino->i_ino = i_ino; + ino->i_dev = i_dev; + ino->i_mode = i_mode; + ino->i_nlink = i_nlink; + ino->i_size = i_size; + ino->i_blksize = i_blksize; + ino->i_blocks = i_blocks; + if(kdev_same(ino->i_sb->s_dev, ROOT_DEV) && (ino->i_uid == getuid())) + ino->i_uid = 0; + return(0); +} + +static int read_inode(struct inode *ino) +{ + char *name; + int err; + + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = read_name(ino, name); + kfree(name); + return(err); +} + +void hostfs_delete_inode(struct inode *ino) +{ + if(ino->u.hostfs_i.host_filename) kfree(ino->u.hostfs_i.host_filename); + ino->u.hostfs_i.host_filename = NULL; + if(ino->u.hostfs_i.fd != -1) close_file(&ino->u.hostfs_i.fd); + ino->u.hostfs_i.mode = 0; + clear_inode(ino); +} + +int hostfs_statfs(struct super_block *sb, struct statfs *sf) +{ + /* do_statfs uses struct statfs64 internally, but the linux kernel + * struct statfs still has 32-bit versions for most of these fields, + * so we convert them here + */ + int err; + long long f_blocks; + long long f_bfree; + long long f_bavail; + long long f_files; + long long f_ffree; + + err = do_statfs(sb->s_root->d_inode->u.hostfs_i.host_filename, + &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, + &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), + &sf->f_namelen, sf->f_spare); + if(err) return(err); + sf->f_blocks = f_blocks; + sf->f_bfree = f_bfree; + sf->f_bavail = f_bavail; + sf->f_files = f_files; + sf->f_ffree = f_ffree; + sf->f_type = HOSTFS_SUPER_MAGIC; + return(0); +} + +static struct super_operations hostfs_sbops = { + put_inode: force_delete, + delete_inode: hostfs_delete_inode, + statfs: hostfs_statfs, +}; + +int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + void *dir; + char *name; + unsigned long long next, ino; + int error, len; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + dir = open_dir(name, &error); + kfree(name); + if(dir == NULL) return(-error); + next = file->f_pos; + while((name = read_dir(dir, &next, &ino, &len)) != NULL){ + error = (*filldir)(ent, name, len, file->f_pos, + ino, DT_UNKNOWN); + if(error) break; + file->f_pos = next; + } + close_dir(dir); + return(0); +} + +unsigned int hostfs_poll(struct file *file, struct poll_table_struct *table) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_ioctl(struct inode *ino, struct file *file, unsigned int code, + unsigned long data) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_file_open(struct inode *ino, struct file *file) +{ + char *name; + int mode = 0, r = 0, w = 0, fd; + + mode = file->f_mode & (FMODE_READ | FMODE_WRITE); + if((mode & ino->u.hostfs_i.mode) == mode) return(0); + + if(ino->u.hostfs_i.fd != -1){ + close_file(&ino->u.hostfs_i.fd); + ino->u.hostfs_i.fd = -1; + } + ino->u.hostfs_i.mode |= mode; + if(ino->u.hostfs_i.mode & FMODE_READ) r = 1; + if(ino->u.hostfs_i.mode & FMODE_WRITE) w = 1; + if(w) r = 1; + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + fd = open_file(name, r, w); + kfree(name); + if(fd < 0) return(fd); + file_hostfs_i(file)->fd = fd; + return(0); +} + +int hostfs_dir_open(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_dir_release(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + return(0); +} + +int hostfs_fasync(int fd, struct file *file, int on) +{ + not_implemented(); + return(-EINVAL); +} + +static struct file_operations hostfs_file_fops = { + owner: NULL, + read: generic_file_read, + write: generic_file_write, + poll: hostfs_poll, + mmap: generic_file_mmap, + open: hostfs_file_open, + release: NULL, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +static struct file_operations hostfs_dir_fops = { + owner: NULL, + readdir: hostfs_readdir, + poll: hostfs_poll, + ioctl: hostfs_ioctl, + open: hostfs_dir_open, + release: hostfs_dir_release, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +int hostfs_writepage(struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + unsigned long long base; + int count = PAGE_CACHE_SIZE; + int end_index = inode->i_size >> PAGE_CACHE_SHIFT; + int err; + + if (page->index >= end_index) + count = inode->i_size & (PAGE_CACHE_SIZE-1); + + buffer = kmap(page); + base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; + + err = write_file(inode->u.hostfs_i.fd, &base, buffer, count); + if(err != count){ + ClearPageUptodate(page); + goto out; + } + + if (base > inode->i_size) + inode->i_size = base; + + if (PageError(page)) + ClearPageError(page); + + out: + kunmap(page); + + UnlockPage(page); + return err; +} + +int hostfs_readpage(struct file *file, struct page *page) +{ + char *buffer; + long long start; + int err = 0; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + err = read_file(file_hostfs_i(file)->fd, &start, buffer, + PAGE_CACHE_SIZE); + if(err < 0) goto out; + + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + out: + kunmap(page); + UnlockPage(page); + return(err); +} + +int hostfs_prepare_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + char *buffer; + long long start; + int err; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + if(from != 0){ + err = read_file(file_hostfs_i(file)->fd, &start, buffer, + from); + if(err < 0) goto out; + } + if(to != PAGE_CACHE_SIZE){ + start += to; + err = read_file(file_hostfs_i(file)->fd, &start, buffer + to, + PAGE_CACHE_SIZE - to); + if(err < 0) goto out; + } + err = 0; + out: + kunmap(page); + return(err); +} + +int hostfs_commit_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + long long start; + int err = 0; + + start = (long long) (page->index << PAGE_CACHE_SHIFT) + from; + buffer = kmap(page); + err = write_file(file_hostfs_i(file)->fd, &start, buffer + from, + to - from); + if(err > 0) err = 0; + if(!err && (start > inode->i_size)) + inode->i_size = start; + + kunmap(page); + return(err); +} + +static struct address_space_operations hostfs_aops = { + writepage: hostfs_writepage, + readpage: hostfs_readpage, + prepare_write: hostfs_prepare_write, + commit_write: hostfs_commit_write +}; + +static struct inode *get_inode(struct super_block *sb, struct dentry *dentry, + int *error) +{ + struct inode *inode; + char *name; + int type, err = 0, rdev; + + inode = get_empty_inode(); + if(inode == NULL) return(NULL); + inode->u.hostfs_i.host_filename = NULL; + inode->u.hostfs_i.fd = -1; + inode->u.hostfs_i.mode = 0; + if(error) *error = 0; + insert_inode_hash(inode); + if(dentry){ + name = dentry_name(dentry, 0); + if(name == NULL){ + err = -ENOMEM; + goto out; + } + type = file_type(name, &rdev); + kfree(name); + } + else type = HOSTFS_DIR; + inode->i_sb = sb; + + if(type == HOSTFS_SYMLINK) + inode->i_op = &page_symlink_inode_operations; + else inode->i_op = &hostfs_iops; + + if(type == HOSTFS_DIR) inode->i_fop = &hostfs_dir_fops; + else inode->i_fop = &hostfs_file_fops; + + if(type == HOSTFS_SYMLINK) inode->i_mapping->a_ops = &hostfs_link_aops; + else inode->i_mapping->a_ops = &hostfs_aops; + + switch (type) { + case HOSTFS_CHARDEV: + init_special_inode(inode, S_IFCHR, rdev); + break; + case HOSTFS_BLOCDEV: + init_special_inode(inode, S_IFBLK, rdev); + break; + case HOSTFS_FIFO: + init_special_inode(inode, S_IFIFO, 0); + break; + case HOSTFS_SOCK: + init_special_inode(inode, S_IFSOCK, 0); + break; + } + + return(inode); + out: + iput(inode); + if(error) *error = err; + return(NULL); +} + +int hostfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(dir->i_sb, dentry, &error); + if(error) return(error); + name = dentry_name(dentry, 0); + if(name == NULL){ + iput(inode); + return(-ENOMEM); + } + error = file_create(name, + mode | S_IRUSR, mode | S_IWUSR, mode | S_IXUSR, + mode | S_IRGRP, mode | S_IWGRP, mode | S_IXGRP, + mode | S_IROTH, mode | S_IWOTH, mode | S_IXOTH); + if(!error) error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + return(error); + } + d_instantiate(dentry, inode); + return(0); +} + +struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(ino->i_sb, dentry, &error); + if(error != 0) return(ERR_PTR(error)); + name = dentry_name(dentry, 0); + if(name == NULL) return(ERR_PTR(-ENOMEM)); + error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + if(error == -ENOENT) inode = NULL; + else return(ERR_PTR(error)); + } + d_add(dentry, inode); + dentry->d_op = &hostfs_dentry_ops; + return(NULL); +} + +static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) +{ + char *file; + int len; + + file = inode_name(ino, dentry->d_name.len + 1); + if(file == NULL) return(NULL); + strcat(file, "/"); + len = strlen(file); + strncat(file, dentry->d_name.name, dentry->d_name.len); + file[len + dentry->d_name.len] = '\0'; + return(file); +} + +int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(ino, from)) == NULL) + return(-ENOMEM); + to_name = dentry_name(to, 0); + if(to_name == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = link_file(to_name, from_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +int hostfs_unlink(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = unlink_file(file); + kfree(file); + return(err); +} + +int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = make_symlink(file, to); + kfree(file); + return(err); +} + +int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mkdir(file, mode); + kfree(file); + return(err); +} + +int hostfs_rmdir(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_rmdir(file); + kfree(file); + return(err); +} + +int hostfs_mknod(struct inode *ino, struct dentry *dentry, int mode, int dev) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mknod(file, mode, dev); + kfree(file); + return(err); +} + +int hostfs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(from_ino, from)) == NULL) + return(-ENOMEM); + if((to_name = inode_dentry_name(to_ino, to)) == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = rename_file(from_name, to_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +void hostfs_truncate(struct inode *ino) +{ + not_implemented(); +} + +int hostfs_permission(struct inode *ino, int desired) +{ + char *name; + int r = 0, w = 0, x = 0, err; + + if(desired & MAY_READ) r = 1; + if(desired & MAY_WRITE) w = 1; + if(desired & MAY_EXEC) x = 1; + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = access_file(name, r, w, x); + kfree(name); + if(!err) err = vfs_permission(ino, desired); + return(err); +} + +int hostfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct hostfs_iattr attrs; + char *name; + int err; + + attrs.ia_valid = 0; + if(attr->ia_valid & ATTR_MODE){ + attrs.ia_valid |= HOSTFS_ATTR_MODE; + attrs.ia_mode = attr->ia_mode; + } + if(attr->ia_valid & ATTR_UID){ + attrs.ia_valid |= HOSTFS_ATTR_UID; + attrs.ia_uid = attr->ia_uid; + } + if(attr->ia_valid & ATTR_GID){ + attrs.ia_valid |= HOSTFS_ATTR_GID; + attrs.ia_gid = attr->ia_gid; + } + if(attr->ia_valid & ATTR_SIZE){ + attrs.ia_valid |= HOSTFS_ATTR_SIZE; + attrs.ia_size = attr->ia_size; + } + if(attr->ia_valid & ATTR_ATIME){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME; + attrs.ia_atime = attr->ia_atime; + } + if(attr->ia_valid & ATTR_MTIME){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME; + attrs.ia_mtime = attr->ia_mtime; + } + if(attr->ia_valid & ATTR_CTIME){ + attrs.ia_valid |= HOSTFS_ATTR_CTIME; + attrs.ia_ctime = attr->ia_ctime; + } + if(attr->ia_valid & ATTR_ATIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; + } + if(attr->ia_valid & ATTR_MTIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; + } + name = dentry_name(dentry, 0); + if(name == NULL) return(-ENOMEM); + err = set_attr(name, &attrs); + kfree(name); + return(err); +} + +int hostfs_getattr(struct dentry *dentry, struct iattr *attr) +{ + not_implemented(); + return(-EINVAL); +} + +static struct inode_operations hostfs_iops = { + create: hostfs_create, + lookup: hostfs_lookup, + link: hostfs_link, + unlink: hostfs_unlink, + symlink: hostfs_symlink, + mkdir: hostfs_mkdir, + rmdir: hostfs_rmdir, + mknod: hostfs_mknod, + rename: hostfs_rename, + truncate: hostfs_truncate, + permission: hostfs_permission, + setattr: hostfs_setattr, + getattr: hostfs_getattr, +}; + +int hostfs_link_readpage(struct file *file, struct page *page) +{ + char *buffer, *name; + long long start; + int err; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + name = inode_name(page->mapping->host, 0); + if(name == NULL) return(-ENOMEM); + err = do_readlink(name, buffer, PAGE_CACHE_SIZE); + kfree(name); + if(err == 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + } + kunmap(page); + UnlockPage(page); + return(err); +} + +static struct address_space_operations hostfs_link_aops = { + readpage: hostfs_link_readpage, +}; + +static struct super_block *hostfs_read_super_common(struct super_block *sb, + char *data) +{ + struct inode * root_inode; + char *name; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = HOSTFS_SUPER_MAGIC; + sb->s_op = &hostfs_sbops; + if((data == NULL) || (*((char *) data) == '\0')) data = root_ino; + name = kmalloc(strlen(data) + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + strcpy(name, data); + root_inode = get_inode(sb, NULL, NULL); + if(root_inode == NULL){ + kfree(name); + return(NULL); + } + root_inode->u.hostfs_i.host_filename = name; + sb->s_root = d_alloc_root(root_inode); + if(read_inode(root_inode)){ + iput(root_inode); + return(NULL); + } + return(sb); +} + +struct super_block *hostfs_read_super(struct super_block *sb, void *data, + int silent) +{ + return(hostfs_read_super_common(sb, data)); +} + +struct super_block *hostfs_root_read_super(struct super_block *sb, void *data, + int silent) +{ + struct buffer_head * bh; + struct super_block *ret = NULL; + kdev_t dev = sb->s_dev; + int blocksize = get_hardsect_size(dev); + + if(blocksize == 0) blocksize = BLOCK_SIZE; + set_blocksize (dev, blocksize); + if(!(bh = bread (dev, 0, blocksize))) return NULL; + if(strncmp(bh->b_data, "HOSTFS:", strlen("HOSTFS:"))) goto out; + ret = hostfs_read_super_common(sb, bh->b_data + strlen("HOSTFS:")); + out: + brelse (bh); + return(ret); +} + +DECLARE_FSTYPE(hostfs_type, "hostfs", hostfs_read_super, 0); +DECLARE_FSTYPE_DEV(hostfs_root_type, "root-hostfs", hostfs_root_read_super); + +static int __init init_hostfs(void) +{ + return(register_filesystem(&hostfs_type) || + register_filesystem(&hostfs_root_type)); +} + +static void __exit exit_hostfs(void) +{ + unregister_filesystem(&hostfs_type); + unregister_filesystem(&hostfs_root_type); +} + +module_init(init_hostfs) +module_exit(exit_hostfs) + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/fs/hostfs/hostfs_user.c b/arch/um/fs/hostfs/hostfs_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/fs/hostfs/hostfs_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "user.h" + +int stat_file(const char *path, int *dev_out, unsigned long long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, int *gid_out, + unsigned long long *size_out, unsigned long *atime_out, + unsigned long *mtime_out, unsigned long *ctime_out, + int *blksize_out, unsigned long long *blocks_out) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) + return(-errno); + if(dev_out != NULL) *dev_out = buf.st_dev; + + /* See the Makefile for why STAT64_INO_FIELD is passed in + * by the build + */ + if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD; + if(mode_out != NULL) *mode_out = buf.st_mode; + if(nlink_out != NULL) *nlink_out = buf.st_nlink; + if(uid_out != NULL) *uid_out = buf.st_uid; + if(gid_out != NULL) *gid_out = buf.st_gid; + if(size_out != NULL) *size_out = buf.st_size; + if(atime_out != NULL) *atime_out = buf.st_atime; + if(mtime_out != NULL) *mtime_out = buf.st_mtime; + if(ctime_out != NULL) *ctime_out = buf.st_ctime; + if(blksize_out != NULL) *blksize_out = buf.st_blksize; + if(blocks_out != NULL) *blocks_out = buf.st_blocks; + return(0); +} + +int file_type(const char *path, int *rdev) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) return(-errno); + *rdev = buf.st_rdev; + if(S_ISDIR(buf.st_mode)) return(HOSTFS_DIR); + else if(S_ISLNK(buf.st_mode)) return(HOSTFS_SYMLINK); + else if(S_ISCHR(buf.st_mode)) return(HOSTFS_CHARDEV); + else if(S_ISBLK(buf.st_mode)) return(HOSTFS_BLOCDEV); + else if(S_ISFIFO(buf.st_mode))return(HOSTFS_FIFO); + else if(S_ISSOCK(buf.st_mode))return(HOSTFS_SOCK); + + else return(HOSTFS_FILE); +} + +int access_file(char *path, int r, int w, int x) +{ + int mode = 0; + + if(r) mode = R_OK; + if(w) mode |= W_OK; + if(x) mode |= X_OK; + if(access(path, mode) != 0) return(-errno); + else return(0); +} + +int open_file(char *path, int r, int w) +{ + int mode = 0, fd; + + if(r && !w) mode = O_RDONLY; + else if(!r && w) mode = O_WRONLY; + else if(r && w) mode = O_RDWR; + else panic("Impossible mode in open_file"); + fd = open64(path, mode); + if(fd < 0) return(-errno); + else return(fd); +} + +void *open_dir(char *path, int *err_out) +{ + DIR *dir; + + dir = opendir(path); + *err_out = errno; + if(dir == NULL) return(NULL); + return(dir); +} + +char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out) +{ + DIR *dir = stream; + struct dirent *ent; + + seekdir(dir, *pos); + ent = readdir(dir); + if(ent == NULL) return(NULL); + *len_out = strlen(ent->d_name); + *ino_out = ent->d_ino; + *pos = telldir(dir); + return(ent->d_name); +} + +int read_file(int fd, unsigned long long *offset, char *buf, int len) +{ + int n; + + n = pread64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int write_file(int fd, unsigned long long *offset, const char *buf, int len) +{ + int n; + + n = pwrite64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int lseek_file(int fd, long long offset, int whence) +{ + int ret; + + ret = lseek64(fd, offset, whence); + if(ret < 0) return(-errno); + return(0); +} + +void close_file(void *stream) +{ + close(*((int *) stream)); +} + +void close_dir(void *stream) +{ + closedir(stream); +} + +int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox) +{ + int mode, fd; + + mode = 0; + mode |= ur ? S_IRUSR : 0; + mode |= uw ? S_IWUSR : 0; + mode |= ux ? S_IXUSR : 0; + mode |= gr ? S_IRGRP : 0; + mode |= gw ? S_IWGRP : 0; + mode |= gx ? S_IXGRP : 0; + mode |= or ? S_IROTH : 0; + mode |= ow ? S_IWOTH : 0; + mode |= ox ? S_IXOTH : 0; + fd = open64(name, O_CREAT, mode); + if(fd < 0) return(-errno); + close(fd); + return(0); +} + +int set_attr(const char *file, struct hostfs_iattr *attrs) +{ + struct utimbuf buf; + int err, ma; + + if(attrs->ia_valid & HOSTFS_ATTR_MODE){ + if(chmod(file, attrs->ia_mode) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_UID){ + if(chown(file, attrs->ia_uid, -1)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_GID){ + if(chown(file, -1, attrs->ia_gid)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ + if(truncate(file, attrs->ia_size)) return(-errno); + } + ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ + buf.actime = attrs->ia_atime; + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + else { + if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &buf.modtime, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.actime = attrs->ia_atime; + if(utime(file, &buf) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &buf.actime, NULL, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + } + if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ; + if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &attrs->ia_atime, &attrs->ia_mtime, + NULL, NULL, NULL); + if(err != 0) return(err); + } + return(0); +} + +int make_symlink(const char *from, const char *to) +{ + int err; + + err = symlink(to, from); + if(err) return(-errno); + return(0); +} + +int unlink_file(const char *file) +{ + int err; + + err = unlink(file); + if(err) return(-errno); + return(0); +} + +int do_mkdir(const char *file, int mode) +{ + int err; + + err = mkdir(file, mode); + if(err) return(-errno); + return(0); +} + +int do_rmdir(const char *file) +{ + int err; + + err = rmdir(file); + if(err) return(-errno); + return(0); +} + +int do_mknod(const char *file, int mode, int dev) +{ + int err; + + err = mknod(file, mode, dev); + if(err) return(-errno); + return(0); +} + +int link_file(const char *to, const char *from) +{ + int err; + + err = link(to, from); + if(err) return(-errno); + return(0); +} + +int do_readlink(char *file, char *buf, int size) +{ + int err; + + err = readlink(file, buf, size); + if(err < 0) return(-errno); + if(err < size) buf[err] = '\0'; + return(0); +} + +int rename_file(char *from, char *to) +{ + int err; + + err = rename(from, to); + if(err < 0) return(-errno); + return(0); +} + +int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, + long long *files_out, long long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out) +{ + struct statfs64 buf; + int err; + + err = statfs64(root, &buf); + if(err < 0) return(-errno); + *bsize_out = buf.f_bsize; + *blocks_out = buf.f_blocks; + *bfree_out = buf.f_bfree; + *bavail_out = buf.f_bavail; + *files_out = buf.f_files; + *ffree_out = buf.f_ffree; + memcpy(fsid_out, &buf.f_fsid, + sizeof(buf.f_fsid) > fsid_size ? fsid_size : + sizeof(buf.f_fsid)); + *namelen_out = buf.f_namelen; + spare_out[0] = buf.f_spare[0]; + spare_out[1] = buf.f_spare[1]; + spare_out[2] = buf.f_spare[2]; + spare_out[3] = buf.f_spare[3]; + spare_out[4] = buf.f_spare[4]; + spare_out[5] = buf.f_spare[5]; + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/2_5compat.h b/arch/um/include/2_5compat.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/2_5compat.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __2_5_COMPAT_H__ +#define __2_5_COMPAT_H__ + +#if PATCHLEVEL == 4 + +#define major(dev) MAJOR(dev) +#define minor(dev) MINOR(dev) +#define kdev_val(dev1, dev2) ((dev1) == (dev2)) + +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/chan_kern.h b/arch/um/include/chan_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/chan_kern.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __CHAN_KERN_H__ +#define __CHAN_KERN_H__ + +#include "linux/tty.h" +#include "linux/list.h" +#include "chan_user.h" + +struct chan { + struct list_head list; + unsigned int primary:1; + unsigned int input:1; + unsigned int output:1; + int opened; + int fd; + enum chan_init_pri pri; + struct chan_ops *ops; + void *data; +}; + +extern void chan_interrupt(struct list_head *chans, struct tty_struct *tty); +extern int parse_chan_pair(char *str, struct list_head *chans, int pri, + int device, struct chan_opts *opts); +extern int open_chan(struct list_head *chans); +extern int write_chan(struct list_head *chans, const char *buf, int len); +extern int console_write_chan(struct list_head *chans, const char *buf, + int len); +extern void close_chan(struct list_head *chans); +extern void enable_chan(struct list_head *chans, + int (*irq_setup)(int fd, int input, int output, + void *data), + void *data); +extern void disable_chan(struct list_head *chans); +extern int chan_window_size(struct list_head *chans, + unsigned short *rows_out, + unsigned short *cols_out); +extern int chan_out_fd(struct list_head *chans); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/chan_user.h b/arch/um/include/chan_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/chan_user.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __CHAN_USER_H__ +#define __CHAN_USER_H__ + +#include "init.h" + +struct chan_opts { + void (*announce)(char *dev_name, int dev); + char *xterm_title; + int raw; +}; + +enum chan_init_pri { INIT_STATIC, INIT_ALL, INIT_ONE }; + +struct chan_ops { + void *(*init)(char *, int, struct chan_opts *); + int (*open)(int, int, void *); + void (*close)(int, void *); + int (*read)(int, void *); + int (*write)(int, const char *, int, void *); + int (*console_write)(int, const char *, int, void *); + int (*window_size)(int, void *, unsigned short *, unsigned short *); + void (*free)(void *); +}; + +extern struct chan_ops pty_ops, pts_ops, tty_ops, xterm_ops, fd_ops, + port_ops; + +extern void generic_close(int fd, void *unused); +extern int generic_read(int fd, void *unused); +extern int generic_write(int fd, const char *buf, int n, void *unused); +extern int generic_console_write(int fd, const char *buf, int n, void *state); +extern int generic_window_size(int fd, void *unused, unsigned short *rows_out, + unsigned short *cols_out); +extern void generic_free(void *data); +extern int getmaster(char *line); +extern void run_winch_handlers(void); + +#define __channel_help(fn, prefix) \ +__uml_help(fn, prefix "[0-9]*=\n" \ +" Attach a console or serial line to a host channel. See\n" \ +" http://user-mode-linux.sourceforge.net/input.html for a complete\n" \ +" description of this switch.\n\n" \ +); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/debug.h b/arch/um/include/debug.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/debug.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) and + * Lars Brinkhoff. + * Licensed under the GPL + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +extern void debugger_proxy(int status, pid_t pid); +extern void child_proxy(pid_t pid, int status); +extern void init_proxy (pid_t pid, int waiting, int status); +extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd); +extern void fake_child_exit(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/hostaudio.h b/arch/um/include/hostaudio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/hostaudio.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2002 Steve Schmidtke + * Licensed under the GPL + */ + +#ifndef HOSTAUDIO_H +#define HOSTAUDIO_H + +#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp" +#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer" + +struct hostaudio_state { + int fd; +}; + +struct hostmixer_state { + int fd; +}; + +/* UML user-side protoypes */ +extern ssize_t hostaudio_read_user(struct hostaudio_state *state, char *buffer, + size_t count, loff_t *ppos); +extern ssize_t hostaudio_write_user(struct hostaudio_state *state, + const char *buffer, size_t count, + loff_t *ppos); +extern int hostaudio_ioctl_user(struct hostaudio_state *state, + unsigned int cmd, unsigned long arg); +extern int hostaudio_open_user(struct hostaudio_state *state, int r, int w, + char *dsp); +extern int hostaudio_release_user(struct hostaudio_state *state); +extern int hostmixer_ioctl_mixdev_user(struct hostmixer_state *state, + unsigned int cmd, unsigned long arg); +extern int hostmixer_open_mixdev_user(struct hostmixer_state *state, int r, + int w, char *mixer); +extern int hostmixer_release_mixdev_user(struct hostmixer_state *state); + +#endif /* HOSTAUDIO_H */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/init.h b/arch/um/include/init.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/init.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,106 @@ +#ifndef _LINUX_UML_INIT_H +#define _LINUX_UML_INIT_H + +/* These macros are used to mark some functions or + * initialized data (doesn't apply to uninitialized data) + * as `initialization' functions. The kernel can take this + * as hint that the function is used only during the initialization + * phase and free up used memory resources after + * + * Usage: + * For functions: + * + * You should add __init immediately before the function name, like: + * + * static void __init initme(int x, int y) + * { + * extern int z; z = x * y; + * } + * + * If the function has a prototype somewhere, you can also add + * __init between closing brace of the prototype and semicolon: + * + * extern int initialize_foobar_device(int, int, int) __init; + * + * For initialized data: + * You should insert __initdata between the variable name and equal + * sign followed by value, e.g.: + * + * static int init_variable __initdata = 0; + * static char linux_logo[] __initdata = { 0x32, 0x36, ... }; + * + * Don't forget to initialize data not at file scope, i.e. within a function, + * as gcc otherwise puts the data into the bss section and not into the init + * section. + * + * Also note, that this data cannot be "const". + */ + +#ifndef _LINUX_INIT_H +typedef int (*initcall_t)(void); +typedef void (*exitcall_t)(void); + +#define __init __attribute__ ((__section__ (".text.init"))) +#define __exit __attribute__ ((unused, __section__(".text.exit"))) +#define __initdata __attribute__ ((__section__ (".data.init"))) + +#endif +struct uml_param { + const char *str; + int (*setup_func)(char *, int *); +}; + +extern initcall_t __uml_initcall_start, __uml_initcall_end; +extern initcall_t __uml_postsetup_start, __uml_postsetup_end; +extern const char *__uml_help_start, *__uml_help_end; + +#define __uml_initcall(fn) \ + static initcall_t __uml_initcall_##fn __uml_init_call = fn + +#define __uml_exitcall(fn) \ + static exitcall_t __uml_exitcall_##fn __uml_exit_call = fn + +extern struct uml_param __uml_setup_start, __uml_setup_end; + +#define __uml_postsetup(fn) \ + static initcall_t __uml_postsetup_##fn __uml_postsetup_call = fn + +#define __non_empty_string(dummyname,string) \ + struct __uml_non_empty_string_struct_##dummyname \ + { \ + char _string[sizeof(string)-2]; \ + } + +#define __uml_setup(str, fn, help...) \ + __non_empty_string(fn ##_setup, str); \ + __uml_help(fn, help); \ + static char __uml_setup_str_##fn[] __initdata = str; \ + static struct uml_param __uml_setup_##fn __uml_init_setup = { __uml_setup_str_##fn, fn } + +#define __uml_help(fn, help...) \ + __non_empty_string(fn ##__help, help); \ + static char __uml_help_str_##fn[] __initdata = help; \ + static const char *__uml_help_##fn __uml_setup_help = __uml_help_str_##fn + +/* + * Mark functions and data as being only used at initialization + * or exit time. + */ +#define __uml_init_setup __attribute__ ((unused,__section__ (".uml.setup.init"))) +#define __uml_setup_help __attribute__ ((unused,__section__ (".uml.help.init"))) +#define __uml_init_call __attribute__ ((unused,__section__ (".uml.initcall.init"))) +#define __uml_postsetup_call __attribute__ ((unused,__section__ (".uml.postsetup.init"))) +#define __uml_exit_call __attribute__ ((unused,__section__ (".uml.exitcall.exit"))) + +#endif /* _LINUX_UML_INIT_H */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/initrd.h b/arch/um/include/initrd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/initrd.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __INITRD_USER_H__ +#define __INITRD_USER_H__ + +extern int load_initrd(char *filename, void *buf, int size); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/irq_user.h b/arch/um/include/irq_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/irq_user.h Wed Feb 13 20:03:58 2002 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __IRQ_USER_H__ +#define __IRQ_USER_H__ + +extern void sigio_handler(int sig, void *sc, int usermode); +extern int activate_fd(int irq, int fd, void *dev_id); +extern void free_irq_by_dev(void *dev_id); +extern void free_irq_by_fd(int fd); +extern void reactivate_fd(int fd); +extern void forward_interrupts(int pid); +extern void init_irq_signals(int on_sigstack); +extern void forward_ipi(int fd, int pid); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/kern.h b/arch/um/include/kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/kern.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_H__ +#define __KERN_H__ + +/* These are all user-mode things which are convenient to call directly + * from kernel code and for which writing a wrapper is too much of a pain. + * The regular include files can't be included because this file is included + * only into kernel code, and user-space includes conflict with kernel + * includes. + */ + +extern int errno; + +extern int getpid(void); +extern int clone(int (*proc)(void *), void *sp, int flags, void *data); +extern int sleep(int); +extern int printf(char *fmt, ...); +extern char *strerror(int errnum); +extern char *ptsname(int __fd); +extern int munmap(void *, int); +extern void *sbrk(int increment); +extern void *malloc(int size); +extern void perror(char *err); +extern int kill(int pid, int sig); +extern int getuid(void); +extern int pause(void); +extern int write(int, const void *, int); +extern int exit(int); +extern int close(int); +extern int read(unsigned int, char *, int); +extern int pipe(int *); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/kern_util.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_UTIL_H__ +#define __KERN_UTIL_H__ + +#include "sysdep/ptrace.h" + +extern int ncpus; +extern char *linux_prog; +extern char *gdb_init; +extern int kmalloc_ok; + +#define ROUND_DOWN(addr) ((void *)(((unsigned long) addr) & PAGE_MASK)) +#define ROUND_UP(addr) ROUND_DOWN(((unsigned long) addr) + PAGE_SIZE - 1) + +extern int kernel_fork(unsigned long flags, int (*fn)(void *), void * arg); +extern unsigned long stack_sp(unsigned long page); +extern int kernel_thread_proc(void *data); +extern long execute_syscall(struct sys_pt_regs regs); +extern void syscall_segv(int sig); +extern int current_pid(void); +extern void set_init_pid(int pid); +extern unsigned long alloc_stack(void); +extern int do_signal(unsigned long *error, int *again_out); +extern int is_stack_fault(unsigned long sp); +extern unsigned long segv(unsigned long address, unsigned long ip, + int is_write, int is_user); +extern int set_user_thread(void *task, int on, int restore_regs, + int protect_mem); +extern void syscall_ready(void); +extern void set_tracing(void *t, int tracing); +extern int is_tracing(void *task); +extern int segv_syscall(void); +extern void ret_from_sys_call(void); +extern void kern_finish_exec(void *task, int new_pid, unsigned long stack); +extern int page_size(void); +extern int page_mask(void); +extern int need_finish_fork(void); +extern int do_proc_op(void *t, int proc_id); +extern void free_stack(unsigned long stack); +extern void add_input_request(int op, void (*proc)(int), void *arg); +extern int sys_execve(char *file, char **argv, char **env); +extern char *current_cmd(void); +extern void timer_handler(int sig, void *sc, int usermode); +extern int set_signals(int enable); +extern void force_sigbus(void); +extern int pid_to_processor_id(int pid); +extern void block_signals(void); +extern void unblock_signals(void); +extern void deliver_signals(void *t); +extern void lock_syscall(void); +extern void unlock_syscall(void); +extern void lock_trap(void); +extern void unlock_trap(void); +extern void lock_pid(void); +extern void unlock_pid(void); +extern void cpu_idle(void); +extern void finish_fork(void); +extern void paging_init(void); +extern unsigned long um_virt_to_phys(void *t, unsigned long addr); +extern void init_flush_vm(void); +extern void *process_state(void *t); +extern struct sys_pt_regs *syscall_state(void *t, void **stack_out, + int *size_out); +extern void syscall_trace(void); +extern int hz(void); +extern int switching_modes(void *t); +extern void idle_timer(void); +extern unsigned int do_IRQ(int irq, int user_mode); +extern int external_pid(void *t); +extern int pid_to_processor_id(int pid); +extern void boot_timer_handler(int sig); +extern void interrupt_end(void); +extern void tracing_reboot(void); +extern void tracing_halt(void); +extern void tracing_cb(void (*proc)(void *), void *arg); +extern void debugger_signal(int status, int pid); +extern void child_signal(int pid, int status); +extern int init_ptrace_proxy(int idle_pid, int startup, int stop); +extern void check_stack_overflow(void *ptr); +extern void relay_signal(int sig, void *sc, int usermode); +extern int singlestepping(void *t); +extern void not_implemented(void); +extern void setup_kernel_stack(void); +extern void set_syscall_regs(void *t); +extern void finish_fork_handler(int sig); +extern int user_context(unsigned long sp); +extern void timer_irq(int user_mode); +extern void set_repeat_syscall(void *task, int again); +extern int get_repeat_syscall(void *t); +extern void force_flush_all(void); +extern void unprotect_stack(unsigned long stack); +extern void kern_start_exec(int new_pid); +extern void do_exitcalls(void); +extern void do_uml_exitcalls(void); +extern int get_restore_regs(void *t); +extern int attach_debugger(int idle_pid, int pid, int stop); +extern void *round_up(unsigned long addr); +extern void *round_down(unsigned long addr); +extern void bad_segv(unsigned long address, unsigned long ip, int is_write); +extern int config_gdb(char *str); +extern int remove_gdb(void); +extern char *uml_strdup(char *string); +extern void unprotect_kernel_mem(void); +extern void protect_kernel_mem(void); +extern unsigned long get_kmem_end(void); +extern void set_kmem_end(unsigned long); +extern void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, + char **mask_out, struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_out_mask); +extern void set_task_sizes(int arg); +extern void uml_cleanup(void); +extern int pid_to_processor_id(int pid); +extern void set_current(void *t); +extern void lock_signalled_task(void *t); +extern void IPI_handler(int cpu); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/line.h b/arch/um/include/line.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/line.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __LINE_H__ +#define __LINE_H__ + +#include "linux/list.h" +#include "asm/semaphore.h" +#include "chan_user.h" + +struct line { + char *init_str; + int init_pri; + struct list_head chan_list; + int count; + struct tty_struct *tty; + struct semaphore sem; + int initialized; +}; + +#define LINE_INIT(str) \ + { init_str : str, \ + init_pri : INIT_STATIC, \ + chan_list : { }, \ + count : 0, \ + tty : NULL, \ + sem : { } } + +struct winch_lines { + struct list_head list; + struct line *lines; + int nlines; +}; + +extern void line_interrupt(int irq, void *data, struct pt_regs *unused); +extern void line_close(struct line *lines, int n); +extern int line_open(struct line *lines, int n, struct tty_struct *tty, + int (*setup_irq)(int fd, int input, int output, + void *data), + struct chan_opts *opts); +extern void line_setup(struct line *lines, int num, char *init); +extern void run_winch_handlers(void); +extern void register_winch(struct winch_lines *lines); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/mconsole.h b/arch/um/include/mconsole.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/mconsole.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_H__ +#define __MCONSOLE_H__ + +#define MCONSOLE_MAGIC (0xcafebabe) +#define MCONSOLE_MAX_DATA (512) +#define MCONSOLE_VERSION (1) + +struct mconsole_request { + unsigned long magic; + int version; + int len; + char data[MCONSOLE_MAX_DATA]; +}; + +struct mconsole_reply { + int err; + int more; + int len; + char data[MCONSOLE_MAX_DATA]; +}; + +struct mc_request; + +struct mconsole_command +{ + char *command; + void (*handler)(struct mc_request *req); + int as_interrupt; +}; + +struct mc_request +{ + int len; + int as_interrupt; + + int originating_fd; + int originlen; + unsigned char origin[128]; /* sockaddr_un */ + + struct mconsole_request request; + struct mconsole_command *cmd; +}; + +extern char mconsole_socket_name[]; + +extern int mconsole_unlink_socket(void); +extern int mconsole_reply(struct mc_request *req, char *reply, int err, + int more); +extern void mconsole_version(struct mc_request *req); +extern void mconsole_help(struct mc_request *req); +extern void mconsole_halt(struct mc_request *req); +extern void mconsole_reboot(struct mc_request *req); +extern void mconsole_config(struct mc_request *req); +extern void mconsole_remove(struct mc_request *req); +extern void mconsole_sysrq(struct mc_request *req); +extern void mconsole_cad(struct mc_request *req); +extern int mconsole_create_listening_socket(void); +extern int mconsole_get_request(int fd, struct mc_request *req); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/mconsole_kern.h b/arch/um/include/mconsole_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/mconsole_kern.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_KERN_H__ +#define __MCONSOLE_KERN_H__ + +#include "linux/config.h" +#include "linux/list.h" +#include "mconsole.h" + +struct mconsole_entry { + struct list_head list; + struct mc_request request; +}; + +struct mc_device { + struct list_head list; + char *name; + int (*config)(char *); + int (*remove)(char *); +}; + +#ifdef CONFIG_MCONSOLE + +extern void mconsole_register_dev(struct mc_device *new); + +#else + +static inline void mconsole_register_dev(struct mc_device *new) +{ +} + +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/mem_user.h b/arch/um/include/mem_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/mem_user.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,61 @@ +/* + * arch/um/include/mem_user.h + * + * BRIEF MODULE DESCRIPTION + * user side memory interface for support IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEM_USER_H +#define _MEM_USER_H + +#define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1)) + +extern unsigned long host_task_size; +extern unsigned long task_size; + +extern int init_mem_user(void); +extern int create_mem_file(unsigned long len); +extern void setup_range(int fd, char *driver, unsigned long start, + unsigned long usable, unsigned long total); +extern void map(unsigned long virt, void *p, unsigned long len, + int r, int w, int x); +extern int parse_iomem(char *str, int *add); +extern void setup_memory(void); +extern unsigned long find_iomem(char *driver, unsigned long *len_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/process.h b/arch/um/include/process.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/process.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +#include + +extern void sig_handler(int sig, struct sigcontext sc); +extern void irq_handler(int sig, struct sigcontext sc); +extern void alarm_handler(int sig, struct sigcontext sc); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sigcontext.h b/arch/um/include/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sigcontext.h Wed Feb 13 20:03:59 2002 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UML_SIGCONTEXT_H__ +#define __UML_SIGCONTEXT_H__ + +extern void fill_in_sigcontext(void *sc, struct sys_pt_regs *regs, + unsigned long cr2, int err); +extern void fill_in_regs(struct sys_pt_regs *regs, void *sc_ptr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/signal_kern.h b/arch/um/include/signal_kern.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/signal_kern.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_KERN_H__ +#define __SIGNAL_KERN_H__ + +#include "sysdep/ptrace.h" + +extern void signal_deliverer(int sig); +extern struct sys_pt_regs *signal_state(void *t); +extern int probe_stack(unsigned long sp, int delta); +extern int have_signals(void *t); +extern void *signal_context(void *t, unsigned long *cr2_out, int *err_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/signal_user.h b/arch/um/include/signal_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/signal_user.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_USER_H__ +#define __SIGNAL_USER_H__ + +extern int signal_stack_size; + +extern int change_sig(int signal, int on); +extern void signal_handler(void *task, unsigned long handler, int sig, + void *pinfo); +extern void set_sigstack(void *stack, int size); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out); +extern void setup_signal_stack(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-i386/ptrace.h b/arch/um/include/sysdep-i386/ptrace.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-i386/ptrace.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_I386_PTRACE_H +#define __SYSDEP_I386_PTRACE_H + +#define UM_MAX_REG (17) +#define UM_MAX_FP_REG (27) + +#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(long)) + +struct sys_pt_regs { + unsigned long regs[UM_MAX_REG]; +}; + +#define EMPTY_REGS { { [ 0 ... UM_MAX_REG - 1 ] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_IP(r) UM_REG(r, EIP) +#define UM_SP(r) UM_REG(r, UESP) +#define UM_ELF_ZERO(r) UM_REG(r, EDX) + +#define UM_SYSCALL_RET(r) UM_REG(r, EAX) +#define UM_SYSCALL_NR(r) UM_REG(r, ORIG_EAX) + +#define UM_SYSCALL_ARG1(r) UM_REG(r, EBX) +#define UM_SYSCALL_ARG2(r) UM_REG(r, ECX) +#define UM_SYSCALL_ARG3(r) UM_REG(r, EDX) +#define UM_SYSCALL_ARG4(r) UM_REG(r, ESI) +#define UM_SYSCALL_ARG5(r) UM_REG(r, EDI) +#define UM_SYSCALL_ARG6(r) UM_REG(r, EBP) + +#define UM_IP_OFFSET (EIP * sizeof(long)) +#define UM_SP_OFFSET (UESP * sizeof(long)) +#define UM_ELF_ZERO_OFFSET (EDX * sizeof(long)) + +#define UM_SYSCALL_RET_OFFSET (EAX * sizeof(long)) +#define UM_SYSCALL_NR_OFFSET (ORIG_EAX * sizeof(long)) + +#define UM_SYSCALL_ARG1_OFFSET (EBX * sizeof(long)) +#define UM_SYSCALL_ARG2_OFFSET (ECX * sizeof(long)) +#define UM_SYSCALL_ARG3_OFFSET (EDX * sizeof(long)) +#define UM_SYSCALL_ARG4_OFFSET (ESI * sizeof(long)) +#define UM_SYSCALL_ARG5_OFFSET (EDI * sizeof(long)) +#define UM_SYSCALL_ARG6_OFFSET (EBP * sizeof(long)) + +#define UM_SET_SYSCALL_RETURN(r, result) UM_REG(r, EAX) = (result) + +#define UM_FIX_EXEC_STACK(sp) do ; while(0) + +#define UM_HAVE_GETREGS +#define UM_HAVE_GETFPREGS +#define UM_HAVE_SETREGS +#define UM_HAVE_SETFPREGS + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-i386/sigcontext.h b/arch/um/include/sysdep-i386/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-i386/sigcontext.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_I386_H +#define __SYS_SIGCONTEXT_I386_H + +#define UM_ALLOCATE_SC(name) struct sigcontext name +#define SC_FAULT_ADDR(sc) ((sc)->cr2) +#define SC_FAULT_WRITE(sc) (((sc)->err) & 2) +#define SC_IP(sc) ((sc)->eip) +#define SC_SP(sc) ((sc)->esp_at_signal) + +/* These are General Protection, and Page Fault */ +#define SEGV_IS_FIXABLE(sc) (((sc)->trapno == 13) || ((sc)->trapno == 14)) +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-i386/syscalls.h b/arch/um/include/sysdep-i386/syscalls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-i386/syscalls.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/unistd.h" + +typedef long syscall_handler_t(struct sys_pt_regs regs); + +#define EXECUTE_SYSCALL(syscall, regs) (*sys_call_table[syscall])(regs) + +extern syscall_handler_t sys_modify_ldt; +extern syscall_handler_t old_mmap_i386; +extern syscall_handler_t old_select; +extern syscall_handler_t sys_ni_syscall; + +#define ARCH_SYSCALLS \ + [ __NR_mmap ] = old_mmap_i386, \ + [ __NR_select ] = old_select, \ + [ __NR_vm86old ] = sys_ni_syscall, \ + [ __NR_modify_ldt ] = sys_modify_ldt, \ + [ __NR_lchown32 ] = sys_lchown, \ + [ __NR_getuid32 ] = sys_getuid, \ + [ __NR_getgid32 ] = sys_getgid, \ + [ __NR_geteuid32 ] = sys_geteuid, \ + [ __NR_getegid32 ] = sys_getegid, \ + [ __NR_setreuid32 ] = sys_setreuid, \ + [ __NR_setregid32 ] = sys_setregid, \ + [ __NR_getgroups32 ] = sys_getgroups, \ + [ __NR_setgroups32 ] = sys_setgroups, \ + [ __NR_fchown32 ] = sys_fchown, \ + [ __NR_setresuid32 ] = sys_setresuid, \ + [ __NR_getresuid32 ] = sys_getresuid, \ + [ __NR_setresgid32 ] = sys_setresgid, \ + [ __NR_getresgid32 ] = sys_getresgid, \ + [ __NR_chown32 ] = sys_chown, \ + [ __NR_setuid32 ] = sys_setuid, \ + [ __NR_setgid32 ] = sys_setgid, \ + [ __NR_setfsuid32 ] = sys_setfsuid, \ + [ __NR_setfsgid32 ] = sys_setfsgid, \ + [ __NR_pivot_root ] = sys_pivot_root, \ + [ __NR_mincore ] = sys_mincore, \ + [ __NR_madvise ] = sys_madvise, \ + [ 222 ] = sys_ni_syscall, + +/* 222 doesn't yet have a name in include/asm-i386/unistd.h */ + +#define LAST_ARCH_SYSCALL 222 + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ia64/ptrace.h b/arch/um/include/sysdep-ia64/ptrace.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ia64/ptrace.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_PTRACE_H +#define __SYSDEP_IA64_PTRACE_H + +struct sys_pt_regs { + int foo; +}; + +#define EMPTY_REGS { 0 } + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ia64/sigcontext.h b/arch/um/include/sysdep-ia64/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ia64/sigcontext.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SIGCONTEXT_H +#define __SYSDEP_IA64_SIGCONTEXT_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ia64/syscalls.h b/arch/um/include/sysdep-ia64/syscalls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ia64/syscalls.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SYSCALLS_H +#define __SYSDEP_IA64_SYSCALLS_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ppc/ptrace.h b/arch/um/include/sysdep-ppc/ptrace.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ppc/ptrace.h Wed Feb 13 20:03:58 2002 @@ -0,0 +1,104 @@ +/* + * Licensed under the GPL + */ + +#ifndef __SYS_PTRACE_PPC_H +#define __SYS_PTRACE_PPC_H + +#include "linux/config.h" +#include "linux/types.h" + +/* the following taken from */ + +#ifdef CONFIG_PPC64 +#define PPC_REG unsigned long /*long*/ +#else +#define PPC_REG unsigned long +#endif +struct sys_pt_regs_s { + PPC_REG gpr[32]; + PPC_REG nip; + PPC_REG msr; + PPC_REG orig_gpr3; /* Used for restarting system calls */ + PPC_REG ctr; + PPC_REG link; + PPC_REG xer; + PPC_REG ccr; + PPC_REG mq; /* 601 only (not used at present) */ + /* Used on APUS to hold IPL value. */ + PPC_REG trap; /* Reason for being here */ + PPC_REG dar; /* Fault registers */ + PPC_REG dsisr; + PPC_REG result; /* Result of a system call */ +}; + +#define NUM_REGS (sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)) + +struct sys_pt_regs { + PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)]; +}; + +#define UM_MAX_REG (PT_FPR0) +#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(PPC_REG)) + +#define EMPTY_REGS { { [ 0 ... NUM_REGS - 1] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_SYSCALL_RET(r) UM_REG(r, PT_R3) +#define UM_SP(r) UM_REG(r, PT_R1) +#define UM_IP(r) UM_REG(r, PT_NIP) +#define UM_ELF_ZERO(r) UM_REG(r, PT_FPSCR) +#define UM_SYSCALL_NR(r) UM_REG(r, PT_R0) +#define UM_SYSCALL_ARG1(r) UM_REG(r, PT_ORIG_R3) +#define UM_SYSCALL_ARG2(r) UM_REG(r, PT_R4) +#define UM_SYSCALL_ARG3(r) UM_REG(r, PT_R5) +#define UM_SYSCALL_ARG4(r) UM_REG(r, PT_R6) +#define UM_SYSCALL_ARG5(r) UM_REG(r, PT_R7) +#define UM_SYSCALL_ARG6(r) UM_REG(r, PT_R8) + +#define UM_SYSCALL_NR_OFFSET (PT_R0 * sizeof(PPC_REG)) +#define UM_SYSCALL_RET_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG1_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG2_OFFSET (PT_R4 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG3_OFFSET (PT_R5 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG4_OFFSET (PT_R6 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG5_OFFSET (PT_R7 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG6_OFFSET (PT_R8 * sizeof(PPC_REG)) +#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG)) +#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG)) +#define UM_ELF_ZERO_OFFSET (PT_R3 * sizeof(PPC_REG)) + +#define UM_SET_SYSCALL_RETURN(_regs, result) \ +do { \ + if (result < 0) { \ + (_regs)->regs[PT_CCR] |= 0x10000000; \ + UM_SYSCALL_RET((_regs)) = -result; \ + } else { \ + UM_SYSCALL_RET((_regs)) = result; \ + } \ +} while(0) + +extern void shove_aux_table(unsigned long sp); +#define UM_FIX_EXEC_STACK(sp) shove_aux_table(sp); + +/* These aren't actually defined. The undefs are just to make sure + * everyone's clear on the concept. + */ +#undef UML_HAVE_GETREGS +#undef UML_HAVE_GETFPREGS +#undef UML_HAVE_SETREGS +#undef UML_HAVE_SETFPREGS + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ppc/sigcontext.h b/arch/um/include/sysdep-ppc/sigcontext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ppc/sigcontext.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_PPC_H +#define __SYS_SIGCONTEXT_PPC_H + +#define UM_ALLOCATE_SC(name) \ + struct sys_pt_regs name##_regs; \ + struct sigcontext name; \ + name.regs = &name##_regs + +#define DSISR_WRITE 0x02000000 + +#define SC_FAULT_ADDR(sc) ({ \ + struct sigcontext_struct *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = _sc->regs->dar; \ + break; \ + case 0x400: \ + /* instruction exception */ \ + retval = _sc->regs->nip; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_FAULT_WRITE(sc) ({ \ + struct sigcontext_struct *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = !!(_sc->regs->dsisr & DSISR_WRITE); \ + break; \ + case 0x400: \ + /* instruction exception: not a write */ \ + retval = 0; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_IP(sc) ((sc)->regs->nip) +#define SC_SP(sc) ((sc)->regs->gpr[1]) +#define SEGV_IS_FIXABLE(sc) (1) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysdep-ppc/syscalls.h b/arch/um/include/sysdep-ppc/syscalls.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysdep-ppc/syscalls.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +typedef long syscall_handler_t(unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6); + +#define EXECUTE_SYSCALL(syscall, regs) \ + (*sys_call_table[syscall])(UM_SYSCALL_ARG1(®s), \ + UM_SYSCALL_ARG2(®s), \ + UM_SYSCALL_ARG3(®s), \ + UM_SYSCALL_ARG4(®s), \ + UM_SYSCALL_ARG5(®s), \ + UM_SYSCALL_ARG6(®s)) + +extern syscall_handler_t sys_mincore; +extern syscall_handler_t sys_madvise; + +/* old_mmap needs the correct prototype since syscall_kern.c includes + * this file. + */ +int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset); + +#define ARCH_SYSCALLS \ + [ __NR_modify_ldt ] = sys_ni_syscall, \ + [ __NR_pciconfig_read ] = sys_ni_syscall, \ + [ __NR_pciconfig_write ] = sys_ni_syscall, \ + [ __NR_pciconfig_iobase ] = sys_ni_syscall, \ + [ __NR_pivot_root ] = sys_ni_syscall, \ + [ __NR_multiplexer ] = sys_ni_syscall, \ + [ __NR_mmap ] = old_mmap, \ + [ __NR_madvise ] = sys_madvise, \ + [ __NR_mincore ] = sys_mincore, + +#define LAST_ARCH_SYSCALL __NR_mincore + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/sysrq.h b/arch/um/include/sysrq.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/sysrq.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,6 @@ +#ifndef __UM_SYSRQ_H +#define __UM_SYSRQ_H + +extern void show_trace(unsigned long *stack); + +#endif diff -Nru a/arch/um/include/ubd_user.h b/arch/um/include/ubd_user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/ubd_user.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#ifndef __UM_UBD_USER_H +#define __UM_UBD_USER_H + +enum ubd_req { UBD_READ, UBD_WRITE }; + +struct io_thread_req { + enum ubd_req op; + int fds[2]; + unsigned long offsets[2]; + unsigned long long offset; + unsigned long length; + char *buffer; + int sectorsize; + unsigned long sector_mask; + unsigned long cow_offset; + unsigned long bitmap_words[2]; + int error; +}; + +extern int open_ubd_file(char *file, int *openflags, char **backing_file_out, + int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out); +extern int create_cow_file(char *cow_file, char *backing_file, int sectorsize, + int *bitmap_offset_out, + unsigned long *bitmap_len_out, + int *data_offset_out); +extern int read_cow_bitmap(int fd, void *buf, int offset, int len); +extern int read_ubd_fs(int fd, void *buffer, int len); +extern int write_ubd_fs(int fd, char *buffer, int len); +extern int start_io_thread(unsigned long sp, int *fds_out); +extern int do_io(struct io_thread_req *req); +extern int ubd_is_dir(char *file); + +static inline int ubd_test_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + return((data[n] & (1 << off)) != 0); +} + +static inline void ubd_set_bit(int bit, unsigned long *data) +{ + int bits, n, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + data[n] |= (1 << off); +} + + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/umid.h b/arch/um/include/umid.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/umid.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +extern int umid_file_name(char *name, char *buf, int len); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/umn.h b/arch/um/include/umn.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/umn.h Wed Feb 13 20:03:58 2002 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UMN_H +#define __UMN_H + +extern int open_umn_tty(int *slave_out, int *slipno_out); +extern void close_umn_tty(int master, int slave); +extern int umn_send_packet(int fd, void *data, int len); +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern void slip_unesc(unsigned char s); +extern void umn_read(int fd); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/user.h b/arch/um/include/user.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/user.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_H__ +#define __USER_H__ + +extern void panic(char *fmt, ...); +extern int printk(char *fmt, ...); +extern void schedule(void); +extern void *um_kmalloc(int size); +extern void kfree(void *ptr); +extern int in_aton(char *str); +extern int open_gdb_chan(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/include/user_util.h b/arch/um/include/user_util.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/include/user_util.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_UTIL_H__ +#define __USER_UTIL_H__ + +#include "sysdep/ptrace.h" + +extern int grantpt(int __fd); +extern int unlockpt(int __fd); +extern char *ptsname(int __fd); + +enum { OP_NONE, OP_EXEC, OP_THREAD, OP_FORK, OP_TRACE_ON, OP_TRACE_OFF, + OP_REBOOT, OP_HALT, OP_CB }; + +struct cpu_task { + int pid; + void *task; +}; + +extern struct cpu_task cpu_tasks[]; + +extern unsigned long low_physmem; +extern unsigned long high_physmem; +extern unsigned long uml_physmem; +extern unsigned long end_vm; +extern unsigned long start_vm; + +extern int tracing_pid; +extern int honeypot; + +extern char host_info[]; + +extern char saved_command_line[]; +extern char command_line[]; + +extern int gdb_pid; + +extern char *tempdir; + +extern void *open_maps(void); +extern int read_map(void *fd, unsigned long *start_out, + unsigned long *end_out, char *r_out, char *w_out, + char *x_out, char *p_out); +extern void close_maps(void *fd); +extern unsigned long get_brk(void); +extern void stop(void); +extern int proc_start_thread(unsigned long ip, unsigned long sp); +extern void stack_protections(unsigned long address); +extern void task_protections(unsigned long address); +extern void abandon_proc_space(int (*proc)(void *), unsigned long sp); +extern int signals(int (*init_proc)(void *), void *sp); +extern int unmap(unsigned long address, unsigned long len); +extern void protect(unsigned long addr, unsigned long len, int r, int w, + int x); +extern void stop_pid(int pid); +extern void kill_pid(int pid); +extern void usr1_pid(int pid); +extern void cont_pid(int pid); +extern int __personality(int); +extern int syscall_handler(void *unused); +extern int do_syscall(void *task, int pid); +extern int wait_for_stop(int pid, int sig, int cont_type); +extern void *add_signal_handler(int sig, void (*handler)(int)); +extern void signal_init(void); +extern void finish_exec(int old_pid, int new_pid, struct sys_pt_regs *regs); +extern int start_fork_tramp(void *arg, unsigned long temp_stack, int clone_vm, + int (*tramp)(void *)); +extern void trace_myself(void); +extern void timer(void); +extern void get_profile_timer(void); +extern void disable_profile_timer(void); +extern void set_timers(int set_signal); +extern int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags); +extern int input_loop(void); +extern void continue_execing_proc(int pid); +extern int linux_main(int argc, char **argv); +extern void remap_data(void *segment_start, void *segment_end); +extern void set_cmdline(char *cmd); +extern void input_cb(void (*proc)(void *), void *arg, int arg_len); +extern void setup_input(void); +extern int exit_kernel(int pid, void *task); +extern int get_pty(void); +extern void save_signal_state(int *sig_ptr); +extern void *um_kmalloc(int size); +extern int raw(int fd, int complain); +extern int switcheroo(int fd, int prot, void *from, void *to, int size); +extern void idle_sleep(int secs); +extern int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out); +extern void setup_machinename(char *machine_out); +extern void setup_hostinfo(void); +extern void add_arg(char *cmd_line, char *arg); +extern void init_new_thread(void *sig_stack, void (*usr1_handler)(int)); +extern void start_exec(int old_pid, int new_pid, int *error, + struct sys_pt_regs *regs); +extern void attach_process(int pid); +extern void calc_sigframe_size(void); +extern int fork_tramp(void *sig_stack); +extern void do_exec(int old_pid, int new_pid); +extern void tracer_panic(char *msg, ...); +extern void close_fd(int); +extern int make_tempfile(const char *template, char **tempname, int do_unlink); +extern char *get_umid(void); +extern int ptrace_getregs(long pid, struct sys_pt_regs *regs_out); +extern int ptrace_setregs(long pid, struct sys_pt_regs *regs_in); +extern void do_longjmp(void *p); +extern void term_handler(int sig); +extern void suspend_new_thread(int fd); +extern int detach(int pid, int sig); +extern int attach(int pid); +extern void kill_child_dead(int pid); +extern int cont(int pid); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/Makefile Wed Feb 13 20:03:57 2002 @@ -0,0 +1,61 @@ +OBJ = um.o + +OBJS = process.o exec_kern.o exec_user.o init_task.o irq.o \ + irq_user.o mem.o mem_user.o ptrace.o reboot.o resource.o \ + setup.o signal_user.o smp.o syscall_kern.o syscall_user.o \ + sysrq.o sys_call_table.o time.o time_kern.o tlb.o trap_kern.o \ + trap_user.o uaccess_user.o um_arch.o umid.o user_util.o + +ifeq ($(CONFIG_BLK_DEV_INITRD), y) + OBJS += initrd_kern.o initrd_user.o +endif + +# user_syms.o not included here because Rules.make has its own ideas about +# building anything in export-objs + +USER_OBJS = $(filter %_user.o,$(OBJS)) process.o time.o umid.o user_util.o + +export-objs = ksyms.o process_kern.o signal_kern.o user_syms.o + +UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS)) +UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS)) + +ifeq ($(CONFIG_MODULES), y) + DMODULES = -D__CONFIG_MODULES__ +endif + +ifeq ($(CONFIG_MODVERSIONS), y) + DMODVERSIONS = -D__CONFIG_MODVERSIONS__ +endif + +ifeq ($(CONFIG_GPROF), y) + OBJS += gprof_syms.o + export-objs += gprof_syms.o +endif + +CFLAGS_user_syms.o = -D__AUTOCONF_INCLUDED__ $(DMODULES) $(DMODVERSIONS) -I- \ + -I../include + +all: $(OBJ) unmap_fin.o + +$(USER_OBJS) : %.o: %.c + $(CC) $(CFLAGS_$@) $(USER_CFLAGS) -c -o $@ $< + +unmap.o: unmap.c + $(CC) $(UNMAP_CFLAGS) -c -o $@ $< + +unmap_fin.o : unmap.o + ld -r -o $@ $< -lc -L/usr/lib + +$(OBJ): $(OBJS) $(export-objs) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +clean: + rm -f $(OBJS) $(export-objs) + +modules: + +fastdep: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/exec_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "irq_user.h" + +static void check_vma(unsigned long addr) +{ + struct vm_area_struct *vma; + + vma = find_vma(current->mm, addr); + if((vma == NULL) || (vma->vm_start > addr)) force_sigbus(); +} + +/* See comment above fork_tramp for why sigstop is defined and used like + * this + */ + +static int sigstop = SIGSTOP; + +static int exec_tramp(void *sig_stack) +{ + int sig = sigstop; + + block_signals(); + init_new_thread(sig_stack, NULL); + kill(getpid(), sig); + return(0); +} + +void flush_thread(void) +{ + unsigned long stack; + int new_pid; + + stack = alloc_stack(); + new_pid = start_fork_tramp((void *) current->thread.kernel_stack, + stack, 0, exec_tramp); + if(new_pid < 0){ + printk(KERN_ERR + "flush_thread : new thread failed, errno = %d\n", + errno); + do_exit(SIGKILL); + } + + if (current->cpu == 0) + forward_interrupts(new_pid); + current->thread.request.op = OP_EXEC; + current->thread.request.u.exec.pid = new_pid; + unprotect_stack((unsigned long) current); + usr1_pid(getpid()); + + current->thread.extern_pid = new_pid; + cpu_tasks[current->cpu].pid = new_pid; + free_page(stack); + protect(uml_physmem, high_physmem - uml_physmem, 1, 1, 0); + task_protections((unsigned long) current); + force_flush_all(); + unblock_signals(); +} + +void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) +{ + check_vma(current->mm->brk - 1); + check_vma(eip); + set_fs(USER_DS); + flush_tlb_mm(current->mm); + if(UM_SP(¤t->thread.process_regs) == 0) + current->thread.process_regs = current->thread.syscall_regs; + UM_IP(¤t->thread.process_regs) = eip; + UM_SP(¤t->thread.process_regs) = esp; + UM_ELF_ZERO(¤t->thread.process_regs) = 0; + UM_FIX_EXEC_STACK(esp); +} + +static int execve1(char *file, char **argv, char **env) +{ + int error; + + error = do_execve(file, argv, env, NULL); + if (error == 0){ + current->ptrace &= ~PT_DTRACE; + set_cmdline(current_cmd()); + } + return(error); +} + +int um_execve(char *file, char **argv, char **env) +{ + if(execve1(file, argv, env) == 0) set_user_thread(current, 1, 1, 1); + return(-1); +} + +int sys_execve(char *file, char **argv, char **env) +{ + int error; + char *filename; + + lock_kernel(); + filename = getname((char *) file); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; + error = execve1(filename, argv, env); + putname(filename); + out: + unlock_kernel(); + return(error); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/exec_user.c b/arch/um/kernel/exec_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/exec_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +void do_exec(int old_pid, int new_pid) +{ + struct sys_pt_regs regs; + + if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0) || + (waitpid(new_pid, 0, WUNTRACED) < 0)) + tracer_panic("do_exec failed to attach proc"); + + if(ptrace_getregs(old_pid, ®s) < 0) + tracer_panic("do_exec failed to get registers"); + + kill(old_pid, SIGKILL); + + if((ptrace_setregs(new_pid, ®s) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) + tracer_panic("do_exec failed to start new proc"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/gprof_syms.c b/arch/um/kernel/gprof_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/gprof_syms.c Wed Feb 13 20:04:00 2002 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" + +extern void mcount(void); +EXPORT_SYMBOL(mcount); + +extern void __bb_init_func(void *); +EXPORT_SYMBOL(__bb_init_func); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/init_task.c b/arch/um/kernel/init_task.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/init_task.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "linux/sched.h" +#include "asm/uaccess.h" +#include "asm/pgtable.h" +#include "user_util.h" + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union task_union init_task_union +__attribute__((__section__(".data.init_task"))) = +{ INIT_TASK(init_task_union.task) }; + +struct task_struct *alloc_task_struct(void){ + struct task_struct *task; + + task = (struct task_struct *) __get_free_pages(GFP_KERNEL,2); + if(task == NULL) return(NULL); + return(task); +} + +void unprotect_stack(unsigned long stack) +{ + protect(stack, 4 * PAGE_SIZE, 1, 1, 0); +} + +void free_task_struct(struct task_struct *task) +{ + /* free_pages decrements the page counter and only actually frees + * the pages if they are now not accessed by anything. + */ + free_pages((unsigned long) task, 2); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/initrd_kern.c b/arch/um/kernel/initrd_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/initrd_kern.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/blk.h" +#include "asm/types.h" +#include "user_util.h" +#include "initrd.h" +#include "init.h" + +extern __u64 file_size(char *file); + +static char *initrd __initdata = NULL; + +static int __init read_initrd(void) +{ + void *area; + int size; + + if(initrd == NULL) return 0; + size = file_size(initrd); + if(size < 0) return 0; + area = alloc_bootmem(size); + if(area == NULL) return 0; + if(load_initrd(initrd, area, size) == -1) return 0; + initrd_start = (unsigned long) area; + initrd_end = initrd_start + size; + return 0; +} + +__uml_postsetup(read_initrd); + +static int __init uml_initrd_setup(char *line, int *add) +{ + initrd = line; + return 0; +} + +__uml_setup("initrd=", uml_initrd_setup, +"initrd=\n" +" This is used to boot UML from an initrd image. The argument is the\n" +" name of the file containing the image\n" +); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/initrd_user.c b/arch/um/kernel/initrd_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/initrd_user.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include + +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "initrd.h" + +int load_initrd(char *filename, void *buf, int size) +{ + int fd, n; + + if((fd = open(filename, O_RDONLY)) == -1){ + printk("Opening '%s' failed - errno = %d\n", filename, errno); + return(-1); + } + if((n = read(fd, buf, size)) != size){ + printk("Read of %d bytes from '%s' returned %d, errno = %d\n", + size, filename, n, errno); + return(-1); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/irq.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/smp.h" +#include "linux/irq.h" +#include "linux/kernel_stat.h" +#include "linux/interrupt.h" +#include "linux/random.h" +#include "linux/slab.h" +#include "linux/file.h" +#include "linux/proc_fs.h" +#include "linux/init.h" +#include "linux/seq_file.h" +#include "asm/irq.h" +#include "asm/hw_irq.h" +#include "asm/hardirq.h" +#include "asm/atomic.h" +#include "asm/signal.h" +#include "asm/system.h" +#include "asm/errno.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "irq_user.h" + +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk(KERN_ERR "unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. + */ + ack_APIC_irq(); +#endif +#endif +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +volatile unsigned long irq_err_count; + +/* + * Generic, controller-independent functions: + */ + +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + for (j=0; jtypename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + p += sprintf(p, "\n"); +#ifdef notdef +#if CONFIG_SMP + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif +#endif + p += sprintf(p, "ERR: %10lu\n", irq_err_count); + return p - buf; +} + + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, + struct irqaction * action) +{ + int status; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * 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; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * 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); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * 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; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk(KERN_ERR "enable_irq() unbalanced from %p\n", + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +unsigned int do_IRQ(int irq, int user_mode) +{ + /* + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + struct pt_regs regs; + unsigned int status; + + regs.user_mode = user_mode; + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, ®s, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_pending(cpu)) + do_softirq(); + 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, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk(KERN_ERR "Bad boy: %s (at 0x%x) called us " + "without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +int um_request_irq(unsigned int irq, int fd, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id) +{ + int retval; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if(retval) return(retval); + return(activate_fd(irq, fd, dev_id)); +} + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~IRQ_DISABLED; + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/** + * 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; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + free_irq_by_dev(dev_id); + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + kfree(action); + return; + } + printk(KERN_ERR "Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +#if CONFIG_SMP + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + +unsigned long probe_irq_on(void) +{ + return(0); +} + +int probe_irq_off(unsigned long val) +{ + return(0); +} + +static unsigned int startup_SIGIO_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGIO_irq(unsigned int irq) +{ +} + +static void enable_SIGIO_irq(unsigned int irq) +{ +} + +static void disable_SIGIO_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGIO(unsigned int irq) +{ +} + +static void end_SIGIO_irq(unsigned int irq) +{ +} + +static unsigned int startup_SIGVTALRM_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void enable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void disable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGVTALRM(unsigned int irq) +{ +} + +static void end_SIGVTALRM_irq(unsigned int irq) +{ +} + +static struct hw_interrupt_type SIGIO_irq_type = { + "SIGIO", + startup_SIGIO_irq, + shutdown_SIGIO_irq, + enable_SIGIO_irq, + disable_SIGIO_irq, + mask_and_ack_SIGIO, + end_SIGIO_irq, + NULL +}; + +static struct hw_interrupt_type SIGVTALRM_irq_type = { + "SIGVTALRM", + startup_SIGVTALRM_irq, + shutdown_SIGVTALRM_irq, + enable_SIGVTALRM_irq, + disable_SIGVTALRM_irq, + mask_and_ack_SIGVTALRM, + end_SIGVTALRM_irq, + NULL +}; + +void __init init_IRQ(void) +{ + int i; + + irq_desc[TIMER_IRQ].status = IRQ_DISABLED; + irq_desc[TIMER_IRQ].action = 0; + irq_desc[TIMER_IRQ].depth = 1; + irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; + enable_irq(TIMER_IRQ); + for(i=1;idepth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } +} + +void irq_rate_check(struct pt_regs *regs) +{ + unsigned long flags; + irq_desc_t *desc; + int i; + + __save_flags(flags); + __cli(); + for (i = 0; i < NR_IRQS; i++) { + struct irqaction * action; + + desc = irq_desc + i; + desc->irq_contexts = 0; + desc->total_contexts = 0; + /* + * quick lockless test. + */ + if (!(desc->status & IRQ_MITIGATED)) + continue; + /* + * From this point on we re-execute pending IRQ + * handlers. This is necessery for IRQ controllers + * that are neither capable of recording pending + * interrupts, nor are capable of injecting IRQs + * artificially via hw_resend_irq(). So we are + * restarting them by hand - fortunately we are in + * an IRQ context already. + */ + action = NULL; + spin_lock(&desc->lock); + if (desc->status & IRQ_MITIGATED) { + desc->status &= ~IRQ_MITIGATED; + if (!(desc->status & IRQ_INPROGRESS) && + (desc->depth == 1)) { + action = desc->action; + desc->status |= IRQ_INPROGRESS; + } + } + if (action) { + for (;;) { + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + handle_IRQ_event(i, regs, action); + spin_lock(&desc->lock); + if (!(desc->status & IRQ_PENDING)) + break; + } + desc->status &= ~IRQ_INPROGRESS; + } + desc->status |= IRQ_REPLAY; + __enable_irq(desc, i); + spin_unlock(&desc->lock); + } + __restore_flags(flags); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/irq_user.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" + +struct irq_fd { + struct irq_fd *next; + void *id; + int fd; + int irq; + int pid; + int events; + int current_events; +}; + +static struct irq_fd *active_fds = NULL; +static struct irq_fd **last_irq_ptr = &active_fds; + +static struct pollfd *pollfds = NULL; +static int pollfds_num = 0; +static int pollfds_size = 0; + +extern int io_count, intr_count; + +void sigio_handler(int sig, void *sc, int usermode) +{ + struct irq_fd *irq_fd, *next; + int i, n; + +#ifdef CONFIG_SMP + IPI_handler(hard_smp_processor_id()); + if (hard_smp_processor_id() != 0) return; +#endif + while(1){ + if((n = poll(pollfds, pollfds_num, 0)) < 0){ + if(errno == EINTR) continue; + printk("sigio_handler : poll returned %d, " + "errno = %d\n", n, errno); + break; + } + if(n == 0) break; + + irq_fd = active_fds; + for(i = 0; i < pollfds_num; i++){ + if(pollfds[i].revents != 0){ + irq_fd->current_events = pollfds[i].revents; + pollfds[i].events = 0; + } + irq_fd = irq_fd->next; + } + + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){ + /* This mysterious assignment protects us against + * the irq handler freeing the irq from under us. + */ + next = irq_fd->next; + if(irq_fd->current_events != 0){ + irq_fd->current_events = 0; + do_IRQ(irq_fd->irq, usermode); + } + } + } +} + +static int prepare_fd_async(int fd, int pid) +{ + int retval; + + if((retval = fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK)) < 0){ + printk("Failed to set O_ASYNC and O_NONBLOCK on fd # %d, " + "errno = %d\n", fd, errno); + return(-retval); + } + + if(((retval = fcntl(fd, F_SETSIG, SIGIO)) < 0) || + ((retval = fcntl(fd, F_SETOWN, pid)) < 0)){ + printk("Failed to fcntl F_SETOWN (or F_SETSIG) " + "fd %d to pid %d, errno = %d\n", fd, pid, errno); + return(-retval); + } + + return(0); +} + +int activate_ipi(int fd, int pid) +{ + return prepare_fd_async(fd, pid); +} + +int activate_fd(int irq, int fd, void *dev_id) +{ + struct irq_fd *new_fd; + int pid, retval, events = POLLIN | POLLPRI; + + for(new_fd = active_fds;new_fd;new_fd = new_fd->next){ + if(new_fd->fd == fd){ + printk("Registering fd %d twice\n", fd); + printk("Irqs : %d, %d\n", new_fd->irq, irq); + printk("Ids : 0x%x, 0x%x\n", new_fd->id, dev_id); + return(-EIO); + } + } + pid = cpu_tasks[0].pid; + if ((retval = prepare_fd_async(fd, pid)) != 0) + return(retval); + new_fd = um_kmalloc(sizeof(*new_fd)); + if(new_fd == NULL) return(-ENOMEM); + pollfds_num++; + if(pollfds_num > pollfds_size){ + struct pollfd *tmp_pfd; + + tmp_pfd = um_kmalloc(pollfds_num * sizeof(pollfds[0])); + if(tmp_pfd == NULL){ + pollfds_num--; + return(-ENOMEM); + } + if(pollfds != NULL){ + memcpy(tmp_pfd, pollfds, + sizeof(pollfds[0]) * pollfds_size); + kfree(pollfds); + } + pollfds = tmp_pfd; + pollfds_size = pollfds_num; + } + *new_fd = ((struct irq_fd) { next : NULL, + id : dev_id, + fd : fd, + irq : irq, + pid : pid, + events : events, + current_events: 0 } ); + + *last_irq_ptr = new_fd; + last_irq_ptr = &new_fd->next; + + pollfds[pollfds_num - 1].fd = fd; + pollfds[pollfds_num - 1].events = events; + pollfds[pollfds_num - 1].revents = 0; + return(0); +} + +static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) +{ + struct irq_fd **prev; + int i = 0; + + prev = &active_fds; + while(*prev != NULL){ + if((*test)(*prev, arg)){ + struct irq_fd *old_fd = *prev; + if(pollfds[i].fd != (*prev)->fd){ + printk("free_irq_fd - mismatch between " + "active_fds and pollfds, fd %d vs %d\n", + (*prev)->fd, pollfds[i].fd); + return; + } + memcpy(&pollfds[i], &pollfds[i + 1], + (pollfds_num - i - 1) * sizeof(pollfds[0])); + pollfds_num--; + if(last_irq_ptr == &old_fd->next) + last_irq_ptr = prev; + *prev = (*prev)->next; + kfree(old_fd); + continue; + } + prev = &(*prev)->next; + i++; + } +} + +static int same_dev(struct irq_fd *irq, void *dev) +{ + return(irq->id == dev); +} + +void free_irq_by_dev(void *dev) +{ + free_irq_by_cb(same_dev, dev); +} + +static int same_fd(struct irq_fd *irq, void *fd) +{ + return(irq->fd == *((int *) fd)); +} + +void free_irq_by_fd(int fd) +{ + free_irq_by_cb(same_fd, &fd); +} + +static struct irq_fd *find_irq_by_fd(int fd, int *index_out) +{ + struct irq_fd *irq; + int i = 0; + + for(irq=active_fds; irq != NULL; irq = irq->next){ + if(irq->fd == fd) break; + i++; + } + if(irq == NULL){ + printk("find_irq_by_fd doesn't have descriptor %d\n", fd); + return(NULL); + } + if(pollfds[i].fd != fd){ + printk("find_irq_by_fd - mismatch between active_fds and " + "pollfds, fd %d vs %d, need %d\n", irq->fd, + pollfds[i].fd, fd); + return(NULL); + } + *index_out = i; + return(irq); +} + +void reactivate_fd(int fd) +{ + struct irq_fd *irq; + int i; + + irq = find_irq_by_fd(fd, &i); + if(irq == NULL) return; + pollfds[i].events = irq->events; +} + +void forward_ipi(int fd, int pid) +{ + if(fcntl(fd, F_SETOWN, pid) < 0) + printk("forward_ipi: F_SETOWN failed, errno = %d\n", errno); +} + +void forward_interrupts(int pid) +{ + struct irq_fd *irq; + + for(irq=active_fds;irq != NULL;irq = irq->next){ + if(fcntl(irq->fd, F_SETOWN, pid) < 0){ + int save_errno = errno; + if(fcntl(irq->fd, F_GETOWN, 0) != pid){ + /* XXX Just remove the irq rather than + * print out an infinite stream of these + */ + printk("Failed to forward %d to pid %d, " + "errno = %d\n", irq->fd, pid, + save_errno); + } + } + irq->pid = pid; + } +} + +void init_irq_signals(int on_sigstack) +{ + int flags; + + flags = on_sigstack ? SA_ONSTACK : 0; + set_handler(SIGVTALRM, (__sighandler_t) alarm_handler, + flags | SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, + SIGWINCH, -1); + set_handler(SIGIO, (__sighandler_t) irq_handler, flags | SA_RESTART, + SIGUSR1, SIGIO, SIGWINCH, -1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/ksyms.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,29 @@ +#include "linux/module.h" +#include "linux/string.h" +#include "asm/current.h" +#include "asm/delay.h" +#include "asm/processor.h" +#include "asm/unistd.h" +#include "asm/pgalloc.h" +#include "kern_util.h" +#include "user_util.h" + +EXPORT_SYMBOL(stop); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(uml_physmem); +EXPORT_SYMBOL(set_signals); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(sys_waitpid); +EXPORT_SYMBOL(task_size); +EXPORT_SYMBOL(__do_copy_from_user); +EXPORT_SYMBOL(__do_strncpy_from_user); +EXPORT_SYMBOL(flush_tlb_range); +EXPORT_SYMBOL(__do_clear_user); +EXPORT_SYMBOL(honeypot); +EXPORT_SYMBOL(host_task_size); + +/* This is here because UML expands open to sys_open, not to a system + * call instruction. + */ +EXPORT_SYMBOL(sys_open); diff -Nru a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/mem.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/types.h" +#include "linux/mm.h" +#include "linux/fs.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/swap.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/bitops.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "kern.h" +#include "init.h" + +unsigned long high_physmem; + +unsigned long low_physmem; + +unsigned long vm_start; + +unsigned long vm_end; + +pgd_t swapper_pg_dir[1024]; + +unsigned long *empty_zero_page = NULL; + +unsigned long *empty_bad_page = NULL; + +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + +static unsigned long totalram_pages = 0; + +extern char __init_begin, __init_end; +extern long physmem_size; + +int kmalloc_ok = 0; + +void mem_init(void) +{ + max_mapnr = num_physpages = max_low_pfn; + + /* clear the zero-page */ + memset((void *) empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + printk(KERN_INFO "Memory: %luk available\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); + kmalloc_ok = 1; +} + +void paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES]; + int i; + + empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + for(i=0;i> PAGE_SHIFT) - + (uml_physmem >> PAGE_SHIFT) - zones_size[0]; + free_area_init(zones_size); +} + +static int meminfo_22 = 0; + +static int meminfo_compat(char *str) +{ + meminfo_22 = 1; + return(1); +} + +__setup("22_meminfo", meminfo_compat); + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; + if(meminfo_22){ + val->freeram <<= PAGE_SHIFT; + val->bufferram <<= PAGE_SHIFT; + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + } +} + +pte_t __bad_page(void) +{ + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte((struct page *) empty_bad_page, + PAGE_SHARED)); +} + +/* This can't do anything because nothing in the kernel image can be freed + * since it's not in kernel physical memory. + */ + +void free_initmem(void) +{ +} + +#ifdef CONFIG_BLK_DEV_INITRD + +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldk freed\n", + (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} + +#endif + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if (pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed++; + } + if (pmd_quicklist) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if (pte_quicklist) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } + } while(pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i, total = 0, reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageHighMem(mem_map+i)) + highmem++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (page_count(mem_map+i)) + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n", total); + printk("%d pages of HIGHMEM\n",highmem); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +} + +unsigned long kmem_top = 0; + +unsigned long get_kmem_end(void) +{ + if(kmem_top == 0) kmem_top = host_task_size - ABOVE_KMEM; + return(kmem_top); +} + +void set_kmem_end(unsigned long new) +{ + kmem_top = new; +} + +static int __init uml_mem_setup(char *line, int *add) +{ + char *retptr; + physmem_size = memparse(line,&retptr); + return 0; +} +__uml_setup("mem=",uml_mem_setup, +"mem=\n" +" This controls how much \"physical\" memory the kernel allocates\n" +" for the system. The size is specified as a number followed by\n" +" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" +" This is not related to the amount of memory in the physical\n" +" machine. It can be more, and the excess, if it's ever used, will\n" +" just be swapped out.\n Example: mem=64M\n\n" +); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/mem_user.c b/arch/um/kernel/mem_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/mem_user.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,240 @@ +/* + * arch/um/kernel/mem_user.c + * + * BRIEF MODULE DESCRIPTION + * user side memory routines for supporting IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kern_util.h" +#include "user.h" +#include "user_util.h" +#include "init.h" + +struct mem_region { + struct mem_region *next; + char *driver; + unsigned long start; + unsigned long usable; + unsigned long total; + int fd; +}; + +struct mem_region physmem_region; + +struct mem_region *mem_list = &physmem_region; + +#define TEMPNAME_TEMPLATE "vm_file-XXXXXX" + +int create_mem_file(unsigned long len) +{ + int fd; + char zero; + + fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); + if (fchmod(fd, 0777) < 0){ + perror("fchmod"); + exit(1); + } + if(lseek(fd, len, SEEK_SET) < 0){ + perror("lseek"); + exit(1); + } + zero = 0; + if(write(fd, &zero, 1) != 1){ + perror("write"); + exit(1); + } + if(fcntl(fd, F_SETFD, 1) != 0) + perror("Setting FD_CLOEXEC failed"); + return(fd); +} + +void setup_range(int fd, char *driver, unsigned long start, + unsigned long usable, unsigned long total) +{ + struct mem_region *region, *next; + + if(fd == -1){ + fd = create_mem_file(usable); + region = &physmem_region; + next = physmem_region.next; + } + else { + region = malloc(sizeof(*region)); + if(region == NULL){ + perror("Allocating iomem struct"); + exit(1); + } + next = physmem_region.next; + } + *region = ((struct mem_region) { next, driver, start, usable, + total, fd } ); + if(region != &physmem_region) physmem_region.next = region; +} + +void setup_memory(void) +{ + struct mem_region *region; + void *loc; + unsigned long start; + int page; + + start = -1; + region = mem_list; + page = page_size(); + while(region){ + if(region->start != -1) start = region->start; + else region->start = start; + loc = mmap((void *) region->start, region->usable, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + region->fd, 0); + if(loc != (void *) region->start){ + perror("Mapping memory"); + exit(1); + } + start += region->total; + start = (start + page - 1) & ~(page - 1); + region = region->next; + } +} + +static int __init parse_iomem(char *str, int *add) +{ + struct stat buf; + char *file, *driver; + int fd; + + driver = str; + file = strchr(str,','); + if(file == NULL){ + printk(__FUNCTION__ " failed to parse iomem\n"); + return 1; + } + *file = '\0'; + file++; + fd = open(file, O_RDWR); + if(fd < 0){ + perror("Couldn't open io file"); + return 1; + } + if(fstat(fd, &buf) < 0) { + perror(__FUNCTION__ "fstat - cannot fstat file"); + exit(1); + } + setup_range(fd, driver, -1, buf.st_size, buf.st_size); + return 0; +} +__uml_setup("iomem=",parse_iomem, +"iomem=,\n" +" Configure as a named IO memory region named .\n\n" +); + +#ifdef notdef +int logging = 0; +int logging_fd = -1; + +int logging_line = 0; +char logging_buf[256]; + +void log(char *fmt, ...) +{ + va_list ap; + struct timeval tv; + + if(logging == 0) return; + if(logging_fd == -1) + logging_fd = open("log", O_RDWR | O_CREAT | O_TRUNC, 0644); + gettimeofday(&tv, NULL); + sprintf(logging_buf, "%d\t %u.%u ", logging_line++, tv.tv_sec, + tv.tv_usec); + va_start(ap, fmt); + vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap); + va_end(ap); + write(logging_fd, logging_buf, strlen(logging_buf)); +} +#endif + +void map(unsigned long virt, void *p, unsigned long len, + int r, int w, int x) +{ + struct mem_region *region; + unsigned long phys = (unsigned long) p; + void *loc; + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + for(region = mem_list; region ; region = region->next) { + if((phys < region->start) || + (phys >= region->start + region->usable)) + continue; + phys -= region->start; + loc = mmap((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, + region->fd, phys); + if(loc != (void *) virt){ + panic("Error mapping a page - errno = %d", errno); + } + return; + } + panic("No physical or IO memory region for address 0x%x\n", phys); +} + +unsigned long find_iomem(char *driver, unsigned long *len_out) +{ + struct mem_region *region; + + for(region = mem_list; region ; region = region->next) { + if((region->driver != NULL) && + !strcmp(region->driver, driver)){ + *len_out = region->usable; + return(region->start); + } + } + *len_out = 0; + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/mprot.h b/arch/um/kernel/mprot.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/mprot.h Wed Feb 13 20:03:58 2002 @@ -0,0 +1,6 @@ +#ifndef __MPROT_H__ +#define __MPROT_H__ + +extern void no_access(unsigned long addr, unsigned int len); + +#endif diff -Nru a/arch/um/kernel/process.c b/arch/um/kernel/process.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/process.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PROFILING +#include +#endif +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "sysdep/ptrace.h" +#include "sysdep/sigcontext.h" +#include "irq_user.h" + +void stop_pid(int pid) +{ + kill(pid, SIGSTOP); +} + +void kill_pid(int pid) +{ + kill(pid, SIGKILL); +} + +void usr1_pid(int pid) +{ + kill(pid, SIGUSR1); +} + +void cont_pid(int pid) +{ + kill(pid, SIGCONT); +} + +void init_new_thread(void *sig_stack, void (*usr1_handler)(int)) +{ + int flags = 0; + + if(sig_stack != NULL){ + set_sigstack(sig_stack, 2 * page_size()); + flags = SA_ONSTACK; + } + set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGILL, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags, + SIGUSR1, SIGIO, SIGWINCH, -1); + if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + set_timers(1); /* XXX A bit of a race here */ + init_irq_signals(sig_stack != NULL); +} + +struct tramp { + int (*tramp)(void *); + void *tramp_data; + unsigned long temp_stack; + int flags; + int pid; +}; + +/* See above for why sigkill is here */ + +int sigkill = SIGKILL; + +int outer_tramp(void *arg) +{ + struct tramp *t; + int sig = sigkill; + + t = arg; + t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, + t->flags, t->tramp_data); + if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT); + kill(getpid(), sig); + exit(0); +} + +int start_fork_tramp(void *thread_arg, unsigned long temp_stack, int clone_vm, + int (*tramp)(void *)) +{ + struct tramp arg; + unsigned long sp; + int new_pid, flags, status, err; + + /* The trampoline will run on the temporary stack */ + sp = stack_sp(temp_stack); + + flags = CLONE_FILES | SIGCHLD; + if(clone_vm) flags |= CLONE_VM; + + arg.tramp = tramp; + arg.tramp_data = thread_arg; + arg.temp_stack = temp_stack; + arg.flags = flags; + + /* Start the process and wait for it to stop itself */ + new_pid = clone(outer_tramp, (void *) sp, flags, &arg); + if(new_pid < 0) return(-errno); + while((err = waitpid(new_pid, &status, 0) < 0) && (errno == EINTR)) ; + if(err < 0) panic("Waiting for outer trampoline failed - errno = %d", + errno); + if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) + panic("outer trampoline didn't exit with SIGKILL"); + + return(arg.pid); +} + +void trace_myself(void) +{ + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace failed in trace_myself"); +} + +int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out) +{ + void *stack; + unsigned long sp; + int pid, n; + + if((stack = mmap(NULL, page_size(), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){ + perror("get_one_stack : couldn't mmap stack"); + exit(1); + } + sp = stack_sp((unsigned long) stack); + pid = clone(proc, (void *) sp, SIGCHLD | CLONE_VM, arg); + if(pid < 0){ + perror("get_one_stack : couldn't start thread"); + exit(1); + } + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + perror("get_one_stack : couldn't attach to child"); + exit(1); + } + if(ptrace_getregs(pid, regs_out) < 0){ + perror("get_one_stack : couldn't get registers"); + exit(1); + } + kill(pid, SIGKILL); + kill(pid, SIGCONT); + waitpid(pid, NULL, 0); + n = page_size() - (UM_SP(regs_out) & ~page_mask()); + *stack_out = stack; + + return(n); +} + +void attach_process(int pid) +{ + if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)) + tracer_panic("OP_FORK failed to attach pid"); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) + tracer_panic("OP_FORK failed to continue process"); +} + +int signal_frame_size; +static void *local_addr; + +static void frame_size(int sig) +{ + int n; + + signal_frame_size = (unsigned long) local_addr - (unsigned long) &n; +} + +void calc_sigframe_size(void) +{ + int n; + + signal(SIGUSR1, frame_size); + local_addr = &n; + usr1_pid(getpid()); +} + +void tracer_panic(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vprintf(format, ap); + printf("\n"); + while(1) sleep(10); +} + +void suspend_new_thread(int fd) +{ + char c; + + kill(getpid(), SIGSTOP); + + if(read(fd, &c, sizeof(c)) != sizeof(c)) + panic("read failed in suspend_new_thread"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/process_kern.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/mm.h" +#include "linux/slab.h" +#include "linux/utsname.h" +#include "linux/fs.h" +#include "linux/utime.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "asm/unistd.h" +#include "asm/mman.h" +#include "asm/segment.h" +#include "asm/stat.h" +#include "asm/pgtable.h" +#include "asm/processor.h" +#include "asm/pgalloc.h" +#include "asm/spinlock.h" +#include "asm/uaccess.h" +#include "asm/user.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "init.h" +#include "irq_user.h" + +struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; + +static struct task_struct *get_task(int pid, int require) +{ + struct task_struct *task, *ret; + + ret = NULL; + read_lock(&tasklist_lock); + for_each_task(task){ + if(task->pid == pid){ + ret = task; + break; + } + } + read_unlock(&tasklist_lock); + if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); + return(ret); +} + +int external_pid(void *t) +{ + struct task_struct *task = t ? t : current; + + return(task->thread.extern_pid); +} + +int pid_to_processor_id(int pid) +{ + int i; + + for(i = 0; i < smp_num_cpus; i++){ + if(cpu_tasks[i].pid == pid) return(i); + } + tracer_panic("Couldn't find pid %d in cpu_tasks", pid); + return(-1); +} + +void free_stack(unsigned long stack) +{ + free_page(stack); +} + +void set_init_pid(int pid) +{ + init_task.thread.extern_pid = pid; + if(pipe(init_task.thread.switch_pipe) < 0) + panic("Can't create switch pipe for init_task"); +} + +int set_user_thread(void *t, int on, int restore_regs, int protect_mem) +{ + struct task_struct *task; + int ret; + + task = t ? t : current; + if(on == task->thread.tracing) return(on); + ret = task->thread.tracing; + task->thread.request.op = on ? OP_TRACE_ON : OP_TRACE_OFF; + task->thread.request.u.tracing.restore_regs = restore_regs; + if(on && protect_mem) protect_kernel_mem(); + usr1_pid(getpid()); + return(ret); +} + +void set_tracing(void *task, int tracing) +{ + ((struct task_struct *) task)->thread.tracing = tracing; +} + +int is_tracing(void *t) +{ + return (((struct task_struct *) t)->thread.tracing); +} + +#ifdef CONFIG_SMP +extern void schedule_tail(struct task_struct *prev); +#else +#define schedule_tail(prev) do { ; } while(0) +#endif + +static int new_thread_proc(void *t) +{ + struct task_struct *task; + int (*fn)(void *), pid; + void *arg; + + task = t; + trace_myself(); + init_new_thread(NULL, NULL); + pid = getpid(); + fn = task->thread.request.u.thread.proc; + arg = task->thread.request.u.thread.arg; + task->thread.extern_pid = pid; + + suspend_new_thread(task->thread.switch_pipe[0]); + + set_cmdline("(kernel thread)"); + force_flush_all(); + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); + current->thread.prev_sched = NULL; + (*fn)(arg); + do_exit(0); + return(0); +} + +unsigned long alloc_stack(void) +{ + unsigned long page; + + if((page = __get_free_page(GFP_KERNEL)) == 0) + panic("Couldn't allocate new stack"); + stack_protections(page); + return(page); +} + +extern int inited_cpus; + +static int start_kernel_thread(struct task_struct *task, int (*fn)(void *), + void *arg, int cpu) +{ + int extern_pid; + unsigned long sp; + + sp = ((unsigned long) task) + 4 * PAGE_SIZE - sizeof(void *); + task->thread.request.u.thread.proc = fn; + task->thread.request.u.thread.arg = arg; + task->thread.extern_pid = -1; + extern_pid = clone_and_wait(new_thread_proc, task, (void *) sp, + CLONE_FILES | SIGCHLD); + if(task->thread.extern_pid == -1) + tracer_panic("task didn't set its pid"); + atomic_inc(&init_mm.mm_count); + task->mm = &init_mm; + task->active_mm = &init_mm; +#ifdef CONFIG_SMP + if(cpu != NO_PROC_ID){ + unsigned char c; + + cpu_tasks[cpu].pid = extern_pid; + cpu_tasks[cpu].task = task; + inited_cpus++; + init_tasks[cpu] = task; + task->processor = cpu; + write(task->thread.switch_pipe[1], &c, sizeof(c)); + } +#endif + return(extern_pid); +} + +int kernel_thread1(int (*fn)(void *), void * arg, unsigned long flags, + int cpu, int *extern_pid_out) +{ + struct task_struct *new_task; + int pid, extern_pid; + + pid = do_fork(CLONE_VM | flags, 0, NULL, 0); + if(pid < 0) panic("do_fork failed in kernel_thread"); + new_task = get_task(pid, 1); + current->thread.request.op = OP_THREAD; + current->thread.request.u.thread.proc = fn; + current->thread.request.u.thread.arg = arg; + current->thread.request.u.thread.flags = flags; + current->thread.request.u.thread.new_task = new_task; + current->thread.request.u.thread.cpu = cpu; + usr1_pid(getpid()); + extern_pid = current->thread.request.u.thread.new_pid; + if(extern_pid < 0){ + printk(KERN_ERR "Kernel thread failed : errno = %d\n", + -extern_pid); + return(extern_pid); + } + if(extern_pid_out != NULL) *extern_pid_out = extern_pid; + current->thread.prev_sched = NULL; + return(pid); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + return(kernel_thread1(fn, arg, flags, -1, NULL)); +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, unsigned cpu) +{ + if (prev != next) + clear_bit(cpu, &prev->cpu_vm_mask); + set_bit(cpu, &next->cpu_vm_mask); +} + +void set_current(void *t) +{ + struct task_struct *task = t; + + cpu_tasks[task->cpu] = + ((struct cpu_task) { task->thread.extern_pid, task }); +} + +void *_switch_to(void *prev, void *next) +{ + struct task_struct *from, *to; + int vtalrm, alrm; + char c; + + from = prev; + to = next; + + to->thread.prev_sched = from; + + if(from->cpu == 0) forward_interrupts(to->thread.extern_pid); + forward_ipi(cpu_data[from->cpu].ipi_pipe[0], + to->thread.extern_pid); + block_signals(); + + vtalrm = change_sig(SIGVTALRM, 0); + alrm = change_sig(SIGALRM, 0); + + c = 0; + set_current(to); + if(write(to->thread.switch_pipe[1], &c, sizeof(c)) != sizeof(c)) + panic("write of switch_pipe failed, errno = %d", errno); + + if(from->state == TASK_ZOMBIE) kill_pid(getpid()); + if(read(from->thread.switch_pipe[0], &c, sizeof(c)) != sizeof(c)) + panic("read of switch_pipe failed, errno = %d", errno); + + change_sig(SIGVTALRM, vtalrm); + change_sig(SIGALRM, alrm); + + flush_tlb_all(); + unblock_signals(); + + return(current->thread.prev_sched); +} + +void ret_from_sys_call(void) +{ + if(current->need_resched) schedule(); + if(current->sigpending != 0) do_signal(NULL, NULL); +} + +void release_thread(struct task_struct *task) +{ + close(task->thread.switch_pipe[0]); + close(task->thread.switch_pipe[1]); + kill_pid(task->thread.extern_pid); +} + +void exit_thread(void) +{ + unprotect_stack((unsigned long) current); +} + +char *generic_stack = NULL; +char *generic_stack_mask = NULL; +int generic_stack_size = 0; +struct sys_pt_regs generic_regs; +struct sys_pt_regs generic_regs_mask; + +void set_syscall_regs(void *t) +{ + struct sys_pt_regs *regs; + int i; + struct task_struct *task = t; + + regs = &task->thread.syscall_regs; + for(i = 0; i < sizeof(regs->regs)/sizeof(regs->regs[0]); i++){ + task->thread.syscall_regs.regs[i] = generic_regs.regs[i]; + if(generic_regs_mask.regs[i]) + task->thread.syscall_regs.regs[i] += + task->thread.kernel_stack + 2 * PAGE_SIZE; + } +} + +/* This sigusr1 business works around a bug in gcc's -pg support. + * Normally a procedure's mcount call comes after esp has been copied to + * ebp and the new frame is constructed. With procedures with no locals, + * the mcount comes before, as the first thing that the procedure does. + * When that procedure is main for a thread, ebp comes in as NULL. So, + * when mcount dereferences it, it segfaults. So, UML works around this + * by adding a non-optimizable local to the various trampolines, fork_tramp + * and outer_tramp below, and exec_tramp. + */ + +static int sigusr1 = SIGUSR1; + +int fork_tramp(void *stack) +{ + int sig = sigusr1; + + block_signals(); + init_new_thread(stack, finish_fork_handler); + + kill(getpid(), sig); + return(0); +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, + unsigned long stack_top, struct task_struct * p, + struct pt_regs *regs) +{ + int new_pid, clone_vm; + unsigned long stack; + + p->thread = (struct thread_struct) INIT_THREAD; + p->thread.kernel_stack = (unsigned long) p + 2 * PAGE_SIZE; + p->thread.tracing = current->thread.forking; + set_syscall_regs(p); + if(pipe(p->thread.switch_pipe) < 0) + panic("copy_thread : pipe failed"); + if(current->thread.forking){ + stack = alloc_stack(); + clone_vm = (p->mm == current->mm); + p->thread.temp_stack = stack; + new_pid = start_fork_tramp((void *) p->thread.kernel_stack, + stack, clone_vm, fork_tramp); + if(new_pid < 0){ + printk(KERN_ERR "copy_thread : clone failed - " + "errno = %d\n", -new_pid); + return(new_pid); + } + + p->thread.tracing = 0; + p->thread.process_regs = current->thread.process_regs; + UM_SET_SYSCALL_RETURN(&p->thread.process_regs, 0); + if(sp != 0) UM_SP(&p->thread.process_regs) = sp; + p->thread.extern_pid = new_pid; + + current->thread.request.op = OP_FORK; + current->thread.request.u.fork.pid = new_pid; + usr1_pid(getpid()); + } + current->need_resched = 1; + return(0); +} + +void tracing_reboot(void) +{ + current->thread.request.op = OP_REBOOT; + usr1_pid(getpid()); +} + +void tracing_halt(void) +{ + current->thread.request.op = OP_HALT; + usr1_pid(getpid()); +} + +void tracing_cb(void (*proc)(void *), void *arg) +{ + if(getpid() == tracing_pid){ + (*proc)(arg); + } + else { + current->thread.request.op = OP_CB; + current->thread.request.u.cb.proc = proc; + current->thread.request.u.cb.arg = arg; + usr1_pid(getpid()); + } +} + +int do_proc_op(void *t, int proc_id) +{ + struct task_struct *task; + struct thread_struct *thread; + int op, pid; + + task = t; + thread = &task->thread; + op = thread->request.op; + switch(op){ + case OP_NONE: + case OP_TRACE_ON: + case OP_TRACE_OFF: + break; + case OP_EXEC: + do_exec(thread->extern_pid, thread->request.u.exec.pid); + break; + case OP_THREAD: + pid = start_kernel_thread(thread->request.u.thread.new_task, + thread->request.u.thread.proc, + thread->request.u.thread.arg, + thread->request.u.thread.cpu); + thread->request.u.thread.new_pid = pid; + break; + case OP_FORK: + attach_process(thread->request.u.fork.pid); + break; + case OP_CB: + (*thread->request.u.cb.proc)(thread->request.u.cb.arg); + break; + case OP_REBOOT: + case OP_HALT: + break; + default: + tracer_panic("Bad op in do_proc_op"); + break; + } + thread->request.op = OP_NONE; + return(op); +} + +unsigned long stack_sp(unsigned long page) +{ + return(page + PAGE_SIZE - sizeof(void *)); +} + +int current_pid(void) +{ + return(current->pid); +} + +void cpu_idle(void) +{ + if (current->cpu == 0) idle_timer(); + + atomic_inc(&init_mm.mm_count); + current->mm = &init_mm; + current->active_mm = &init_mm; + + while(1){ + /* endless idle loop with no priority at all */ + set_user_nice(current, 20); + + /* + * although we are an idle CPU, we do not want to + * get into the scheduler unnecessarily. + */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } + idle_sleep(10); + } +} + +int page_size(void) +{ + return(PAGE_SIZE); +} + +int page_mask(void) +{ + return(PAGE_MASK); +} + +unsigned long um_virt_to_phys(void *t, unsigned long addr) +{ + struct task_struct *task; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + task = t; + if(task->mm == NULL) return(0xffffffff); + pgd = pgd_offset(task->mm, addr); + pmd = pmd_offset(pgd, addr); + if(!pmd_present(*pmd)) return(0xffffffff); + pte = pte_offset(pmd, addr); + if(!pte_present(*pte)) return(0xffffffff); + return((pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); +} + +char *current_cmd(void) +{ +#ifdef CONFIG_SMP + return("(Unknown)"); +#else + unsigned long addr; + + if((addr = um_virt_to_phys(current, + current->mm->arg_start)) == 0xffffffff) + return("(Unknown)"); + else return((char *) addr); +#endif +} + +void force_sigbus(void) +{ + printk(KERN_ERR "Killing pid %d because of a lack of memory\n", + current->pid); + lock_kernel(); + sigaddset(¤t->pending.signal, SIGBUS); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(SIGBUS | 0x80); +} + +void finish_fork_handler(int sig) +{ + suspend_new_thread(current->thread.switch_pipe[0]); + + force_flush_all(); + if(current->mm != current->p_pptr->mm) + protect(uml_physmem, high_physmem - uml_physmem, 1, 1, 0); + task_protections((unsigned long) current); + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); + current->thread.prev_sched = NULL; + + free_page(current->thread.temp_stack); + change_sig(SIGUSR1, 1); + unblock_signals(); + set_user_thread(current, 1, 1, 1); +} + +void *process_state(void *t) +{ + struct task_struct *task = t ? t : current; + + return(&task->thread.process_regs); +} + +struct sys_pt_regs *syscall_state(void *t, void **stack_out, int *size_out) +{ + struct task_struct *task; + + task = t; + *stack_out = generic_stack; + *size_out = generic_stack_size; + return(&task->thread.syscall_regs); +} + +int get_repeat_syscall(void *t) +{ + struct task_struct *task = t; + + return(task->thread.repeat_syscall); +} + +void set_repeat_syscall(void *t, int again) +{ + struct task_struct *task; + + task = t ? t : current; + task->thread.repeat_syscall = again; +} + +void dump_thread(struct pt_regs *regs, struct user *u) +{ +} + +int switching_modes(void *t) +{ + struct task_struct *task; + + task = t; + return(task->thread.request.op == OP_TRACE_OFF); +} + +void enable_hlt(void) +{ + panic("enable_hlt"); +} + +void disable_hlt(void) +{ + panic("disable_hlt"); +} + +extern int signal_frame_size; + +void interrupt_end(void) +{ + if(current->need_resched) schedule(); + do_signal(NULL, NULL); +} + +void *um_kmalloc(int size) +{ + return(kmalloc(size, GFP_KERNEL)); +} + +unsigned long get_fault_addr(void) +{ + return((unsigned long) current->thread.fault_addr); +} + +EXPORT_SYMBOL(get_fault_addr); + +int singlestepping(void *t) +{ + struct task_struct *task; + int ret; + + task = (struct task_struct *) t; + ret = (task->ptrace & PT_DTRACE); + task->ptrace &= ~PT_DTRACE; + return(ret); +} + +void not_implemented(void) +{ + printk(KERN_DEBUG "Something isn't implemented in here\n"); +} + +EXPORT_SYMBOL(not_implemented); + +void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, char **mask_out, + struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_mask_out) +{ + char *mask; + void *new; + unsigned long s1, s2, n1, n2, off1, off2, i; + + s1 = (unsigned long) stack1; + s2 = (unsigned long) stack2; + new = malloc(size); + mask = malloc(size); + if((new == NULL) || (mask == NULL)){ + perror("diff_stacks : allocating new stack and mask"); + exit(1); + } + memset(mask, 0, size); + for(i = PAGE_SIZE - size; i < PAGE_SIZE - sizeof(void *); i++){ + /* This is horribly word-length- and byte-order-dependent */ + n1 = stack1[i] | (stack1[i + 1] << 8) | + (stack1[i + 2] << 16) | stack1[i + 3] << 24; + n2 = stack2[i] | (stack2[i + 1] << 8) | + (stack2[i + 2] << 16) | stack2[i + 3] << 24; + + /* Internal pointers have to be different on different + * stacks, and they have to point to the stack page. If not, + * then keep looking. + */ + if((n1 == n2) || ((n1 & PAGE_MASK) != s1) || + ((n2 & PAGE_MASK) != s2)) continue; + + /* The offsets have to be the same and they have to point + * within the used area of the stack. + */ + off1 = n1 & ~PAGE_MASK; + off2 = n2 & ~PAGE_MASK; + if((off1 == off2) && (off1 > PAGE_SIZE - size)){ + *((unsigned long *) &stack1[i]) -= s1 + PAGE_SIZE; + mask[i - (PAGE_SIZE - size)] = 1; + i += sizeof(unsigned long); + } + } + memcpy((char *) new, &stack1[PAGE_SIZE - size], size); + for(i = 0; i < sizeof(regs1->regs)/sizeof(regs1->regs[0]); i++){ + regs_out->regs[i] = regs1->regs[i]; + regs_mask_out->regs[i] = 0; + if((regs1->regs[i] == regs2->regs[i]) || + ((regs1->regs[i] & PAGE_MASK) != s1) || + ((regs2->regs[i] & PAGE_MASK) != s2)) continue; + off1 = regs1->regs[i] & ~PAGE_MASK; + off2 = regs2->regs[i] & ~PAGE_MASK; + if((off1 == off2) && (off1 >= PAGE_SIZE - size)){ + regs_out->regs[i] = regs1->regs[i] - s1 - PAGE_SIZE; + regs_mask_out->regs[i] = 1; + } + } + *mask_out = mask; + return(new); +} + +void setup_kernel_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *stack1, *stack2; + int size; + + size = get_one_stack(syscall_handler, NULL, &stack1, ®s1); + if(get_one_stack(syscall_handler, NULL, &stack2, ®s2) != size){ + printf("setup_kernel_stack : differing stack sizes\n"); + exit(1); + } + + generic_stack = diff_stacks(size, stack1, ®s1, stack2, ®s2, + &generic_stack_mask, &generic_regs, + &generic_regs_mask); + generic_stack_size = size; + + if((munmap(stack1, PAGE_SIZE) < 0) || + (munmap(stack2, PAGE_SIZE) < 0)){ + perror("setup_kernel_stack : unmapping temp stacks"); + exit(1); + } +} + +int user_context(unsigned long sp) +{ + return((sp & (PAGE_MASK << 1)) != current->thread.kernel_stack); +} + +int get_restore_regs(void *t) +{ + struct task_struct *task = t; + + return(task->thread.request.u.tracing.restore_regs); +} + +extern void remove_umid_dir(void); +__uml_exitcall(remove_umid_dir); + +extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end; + +void do_uml_exitcalls(void) +{ + exitcall_t *call; + + call = &__uml_exitcall_end; + while (--call >= &__uml_exitcall_begin) + (*call)(); +} + +extern exitcall_t __exitcall_begin, __exitcall_end; + +void do_exitcalls(void) +{ + exitcall_t *call; + + call = &__exitcall_end; + while (--call >= &__exitcall_begin) + (*call)(); + do_uml_exitcalls(); +} + +void *round_up(unsigned long addr) +{ + return(ROUND_UP(addr)); +} + +void *round_down(unsigned long addr) +{ + return(ROUND_DOWN(addr)); +} + +char *uml_strdup(char *string) +{ + char *new; + + new = kmalloc(strlen(string) + 1, GFP_KERNEL); + if(new == NULL) return(NULL); + strcpy(new, string); + return(new); +} + +static int jail = 0; + +static int __init jail_setup(char *line, int *add) +{ + jail = 1; + *add = 1; + return(0); +} + +__uml_setup("jail", jail_setup, +"jail\n" +" Enables the protection of kernel memory from processes\n\n" +); + +void unprotect_kernel_mem(void) +{ + unsigned long start_stack, end_stack; + + if(!jail) return; + if(current == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; + protect(uml_physmem, start_stack - uml_physmem, 1, 1, 1); + protect(end_stack, high_physmem - end_stack, 1, 1, 1); +} + +void protect_kernel_mem(void) +{ + unsigned long start_stack, end_stack; + + if(!jail) return; + if(current == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; + protect(uml_physmem, start_stack - uml_physmem, 1, 0, 1); + protect(end_stack, high_physmem - end_stack, 1, 0, 1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- diff -Nru a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/ptrace.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/errno.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "kern_util.h" + +/* + * Called by kernel/ptrace.c when detaching.. + */ +void ptrace_disable(struct task_struct *child) +{ +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int i, ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + ret = -EIO; + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + tmp = 0; /* Default return condition */ + if(addr < UM_MAX_REG_OFFSET){ + tmp = getreg(child, addr); + ret = put_user(tmp,(unsigned long *) data); + } + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = -EIO; + if (access_process_vm(child, addr, &data, sizeof(data), + 1) != sizeof(data)) + break; + ret = 0; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + if (addr < UM_MAX_REG_OFFSET) { + ret = putreg(child, addr, data); + break; + } + + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + child->ptrace |= PT_DTRACE; + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + +#ifdef PTRACE_GETREGS + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + UM_MAX_REG_OFFSET)) { + ret = -EIO; + break; + } + for ( i = 0; i < UM_MAX_REG_OFFSET; i += sizeof(long) ) { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_SETREGS + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp = 0; + if (!access_ok(VERIFY_READ, (unsigned *)data, + UM_MAX_REG_OFFSET)) { + ret = -EIO; + break; + } + for ( i = 0; i < UM_MAX_REG_OFFSET; i += sizeof(long) ) { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_GETFPREGS + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + ret = -EIO; + break; + } +#endif +#ifdef PTRACE_SETFPREGS + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = -EIO; + break; + } +#endif + default: + ret = -EIO; + break; + } + out_tsk: + free_task_struct(child); + out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/reboot.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static void kill_off_processes(void) +{ + struct task_struct *p; + int me; + + me = getpid(); + for_each_task(p){ + if(p->thread.extern_pid != me) kill_pid(p->thread.extern_pid); + } + if(init_task.thread.extern_pid != me) + kill_pid(init_task.thread.extern_pid); +} + +void uml_cleanup(void) +{ + kill_off_processes(); + do_uml_exitcalls(); +} + +void machine_restart(char * __unused) +{ + kill_off_processes(); + do_exitcalls(); + tracing_reboot(); + kill_pid(getpid()); +} + +void machine_power_off(void) +{ + kill_off_processes(); + do_exitcalls(); + tracing_halt(); + kill_pid(getpid()); +} + +void machine_halt(void) +{ + machine_power_off(); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/resource.c b/arch/um/kernel/resource.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/resource.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/pci.h" + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/setup.c b/arch/um/kernel/setup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/setup.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/processor.h" + +struct cpuinfo_um boot_cpu_data = { 0, 0, 0, 0 }; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/signal_kern.c b/arch/um/kernel/signal_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/signal_kern.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/stddef.h" +#include "linux/sys.h" +#include "linux/sched.h" +#include "linux/wait.h" +#include "linux/kernel.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/slab.h" +#include "asm/signal.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "kern.h" + +EXPORT_SYMBOL(block_signals); +EXPORT_SYMBOL(unblock_signals); + +struct sys_pt_regs *signal_state(void *t) +{ + struct task_struct *task = t; + + return(&task->thread.altstack_regs); +} + +int probe_stack(unsigned long sp, int delta) +{ + int n; + + if((get_user(n, (int *) sp) != 0) || + (put_user(n, (int *) sp) != 0) || + (get_user(n, (int *) (sp - delta)) != 0) || + (put_user(n, (int *) (sp - delta)) != 0)) + return(-EFAULT); + return(0); +} + +int have_signals(void *t) +{ + struct task_struct *task = t; + int ret; + + ret = (task->thread.signal.state == SIGNAL_PENDING); + task->thread.signal.state = SIGNAL_NONE; + return(ret); +} + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +/* + * OK, we're invoking a handler + */ +static int handle_signal(unsigned long signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + unsigned long *error, int *again_out) +{ + struct signal_context *context; + __sighandler_t handler; + unsigned long sp; + sigset_t save; + int frame_size; + + if(again_out) *again_out = 0; + if((error != NULL) && (*error != 0)){ + switch (*error) { + case -ERESTARTNOHAND: + *error = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + *error = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + if(again_out) *again_out = 1; + break; + } + } + handler = ka->sa.sa_handler; + save = *oldset; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,signr); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + + sp = UM_SP(¤t->thread.process_regs); + + if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + frame_size = 4 * sizeof(void *) + sizeof(*context) + + 4 * sizeof(void *) + signal_stack_size; + + if(probe_stack(sp - sizeof(void *), frame_size) < 0){ + if(signr == SIGSEGV){ + struct k_sigaction *ka; + + ka = ¤t->sig->action[SIGSEGV - 1]; + ka->sa.sa_handler = SIG_DFL; + } + force_sig(SIGSEGV, current); + return(1); + } + + current->thread.signal.signal = signr; + current->thread.signal.handler = (unsigned long) handler; + current->thread.signal.state = SIGNAL_PENDING; + if (ka->sa.sa_flags & SA_SIGINFO) + current->thread.signal.info = *info; + else current->thread.signal.info.si_signo = 0; + + sp -= 4 * sizeof(void *) + sizeof(*context); + context = (struct signal_context *) sp; + if(error != NULL) + UM_SET_SYSCALL_RETURN(¤t->thread.process_regs, *error); + + copy_to_user(&context->regs, ¤t->thread.process_regs, + sizeof(current->thread.process_regs)); + copy_to_user(&context->repeat, ¤t->thread.repeat_syscall, + sizeof(current->thread.repeat_syscall)); + copy_to_user(&context->sigs, &save, sizeof(save)); + copy_to_user(¤t->thread.signal_context, &context, + sizeof(context)); + + sp -= 4 * sizeof(void *); + setup_stack(sp, ¤t->thread.altstack_regs); + + return(0); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ + +static int kern_do_signal(sigset_t *oldset, unsigned long *error, + int *again_out) +{ + siginfo_t info; + struct k_sigaction *ka; + int err; + + if (!oldset) + oldset = ¤t->blocked; + + if(again_out) *again_out = 0; + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: { + struct signal_struct *sig; + current->state = TASK_STOPPED; + current->exit_code = signr; + sig = current->p_pptr->sig; + if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + } + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, + (struct pt_regs *) + ¤t->thread.process_regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + err = handle_signal(signr, ka, &info, oldset, error, + again_out); + if(!err) return(1); + } + return(0); +} + +int do_signal(unsigned long *error, int *again_out) +{ + return(kern_do_signal(NULL, error, again_out)); +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + /* This is required because handle_signal expects + * the return value to be already set. *sigsuspend are + * different from everything else because they call + * kern_do_signal directly, rather than letting it be + * called from ret_from_syscall + */ + UM_SYSCALL_RET(¤t->thread.process_regs) = -EINTR; + if (kern_do_signal(&saveset, NULL, NULL)) + return(-EINTR); + } +} + +int sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + UM_SYSCALL_RET(¤t->thread.process_regs) = -EINTR; + if (kern_do_signal(&saveset, NULL, NULL)) + return(-EINTR); + } +} + +struct task_struct *signalled_task; +static spinlock_t signalled_task_lock = SPIN_LOCK_UNLOCKED; + +void lock_signalled_task(void *t) +{ + struct task_struct *task; + + task = t; + spin_lock(&signalled_task_lock); + signalled_task = t; +} + +void signal_deliverer(int sig) +{ + struct task_struct *task; + unsigned long handler; + int signal; + siginfo_t info, *pinfo; + + stop_pid(getpid()); + task = signalled_task; + spin_unlock(&signalled_task_lock); + signal = task->thread.signal.signal; + handler = task->thread.signal.handler; + if(task->thread.signal.info.si_signo == 0) + pinfo = NULL; + else { + info = task->thread.signal.info; + pinfo = &info; + } + signal_handler(task, handler, signal, pinfo); +} + +int sys_sigreturn(struct sys_pt_regs regs) +{ + struct signal_context *context = current->thread.signal_context; + + sigdelsetmask(&context->sigs, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = context->sigs; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + current->thread.process_regs = context->regs; + current->thread.repeat_syscall = context->repeat; + current->thread.signal_context = context->prev; + return(UM_SYSCALL_RET(¤t->thread.process_regs)); +} + +void *signal_context(void *t, unsigned long *cr2_out, int *err_out) +{ + struct task_struct *task = t ? t : current; + + if(cr2_out) *cr2_out = task->thread.cr2; + if(err_out) *err_out = task->thread.err; + return(&task->thread.signal_context->regs); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/signal_user.c b/arch/um/kernel/signal_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/signal_user.c Wed Feb 13 20:04:00 2002 @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "signal_user.h" +#include "signal_kern.h" +#include "sysdep/sigcontext.h" +#include "sigcontext.h" + +extern int timer_on; + +char *signal_stack = NULL; +char *signal_stack_mask = NULL; +int signal_stack_size = 0; +struct sys_pt_regs signal_regs; +struct sys_pt_regs signal_regs_mask; + +static int setup_signal_tramp(void *stack) +{ + set_sigstack(stack, page_size()); + set_handler(SIGUSR1, signal_deliverer, SA_ONSTACK, -1); + usr1_pid(getpid()); + return(0); +} + +void setup_signal_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *sigstack1, *sigstack2, *stack1, *stack2; + int size; + + sigstack1 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + sigstack2 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if((sigstack1 == MAP_FAILED) || (sigstack2 == MAP_FAILED)){ + perror("setup_signal_stack : couldn't mmap stacks"); + exit(1); + } + size = get_one_stack(setup_signal_tramp, sigstack1, &stack1, ®s1); + if(get_one_stack(setup_signal_tramp, sigstack2, &stack2, + ®s2) != size){ + printf("setup_signal_stack : differing stack sizes\n"); + exit(1); + } + + signal_stack = diff_stacks(size, sigstack1, ®s1, sigstack2, ®s2, + &signal_stack_mask, &signal_regs, + &signal_regs_mask); + signal_stack_size = size; + + if((munmap(sigstack1, page_size()) < 0) || + (munmap(sigstack2, page_size()) < 0) || + (munmap(stack1, page_size()) < 0) || + (munmap(stack2, page_size()) < 0)){ + perror("setup_signal_stack : unmapping temp stacks"); + exit(1); + } +} + +void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out) +{ + char *frame; + unsigned long val; + int i, n; + + n = sizeof(signal_regs.regs)/sizeof(signal_regs.regs[0]); + for(i = 0; i < n; i++){ + regs_out->regs[i] = signal_regs.regs[i]; + if(signal_regs_mask.regs[i]) + regs_out->regs[i] += stack_top; + } + + frame = (char *) (stack_top - signal_stack_size); + for(i = 0; i < signal_stack_size;){ + if(signal_stack_mask[i] == 0){ + frame[i] = signal_stack[i]; + i++; + } + else { + val = *((unsigned long *) &signal_stack[i]); + val += stack_top; + *((unsigned long *) &frame[i]) = val; + i += sizeof(unsigned long); + } + } +} + +void set_sigstack(void *sig_stack, int size) +{ + stack_t stack; + + stack.ss_sp = (__ptr_t) sig_stack; + stack.ss_flags = 0; + stack.ss_size = size - sizeof(void *); + if(sigaltstack(&stack, NULL) != 0) + panic("sigaltstack failed"); +} + +void set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + int mask; + + va_start(ap, flags); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + while((mask = va_arg(ap, int)) != -1){ + sigaddset(&action.sa_mask, mask); + } + action.sa_flags = flags; + action.sa_restorer = NULL; + if(sigaction(sig, &action, NULL) < 0) + panic("sigaction failed"); +} + +int change_sig(int signal, int on) +{ + sigset_t sigset, old; + + sigemptyset(&sigset); + sigaddset(&sigset, signal); + sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old); + return(sigismember(&old, signal)); +} + +void signal_handler(void *task, unsigned long h, int sig, void *pinfo) +{ + void (*handler)(int, struct sigcontext); + void (*info_handler)(int, void *); + void *regs; + unsigned long cr2; + int err; + UM_ALLOCATE_SC(sc); + + regs = signal_context(task, &cr2, &err); + if(pinfo == NULL){ + fill_in_sigcontext(&sc, regs, cr2, err); + handler = (void (*)(int, struct sigcontext)) h; + (*handler)(sig, sc); + } + else { + info_handler = (void (*)(int, void *)) h; + (*info_handler)(sig, pinfo); + } +} + +static void change_signals(int type) +{ + sigset_t mask; + + sigemptyset(&mask); + if(type == SIG_BLOCK) timer_on = 0; + else { + timer_on = 1; + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + sigaddset(&mask, SIGIO); + sigaddset(&mask, SIGWINCH); + sigaddset(&mask, SIGPROF); + if(sigprocmask(type, &mask, NULL) < 0) + panic("Failed to change signal mask - errno = %d", errno); +} + +void block_signals(void) +{ + change_signals(SIG_BLOCK); +} + +void unblock_signals(void) +{ + change_signals(SIG_UNBLOCK); +} + +#define SIGIO_BIT 0 +#define SIGVTALRM_BIT 1 + +static int enable_mask(sigset_t *mask) +{ + int sigs; + + sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT; + sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT; + sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT; + if(timer_on) sigs |= 1 << SIGVTALRM_BIT; + return(sigs); +} + +int set_signals(int enable) +{ + sigset_t mask; + int ret; + + sigemptyset(&mask); + if(enable & (1 << SIGIO_BIT)) sigaddset(&mask, SIGIO); + if(enable & (1 << SIGVTALRM_BIT)){ + timer_on = 1; + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) + panic("Failed to enable signals"); + ret = enable_mask(&mask); + sigemptyset(&mask); + if((enable & (1 << SIGIO_BIT)) == 0) sigaddset(&mask, SIGIO); + if((enable & (1 << SIGVTALRM_BIT)) == 0){ + timer_on = 0; + } + if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + panic("Failed to block signals"); + return(ret); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/smp.c Wed Feb 13 20:03:59 2002 @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" + + +#ifdef CONFIG_SMP + +#include "linux/sched.h" +#include "linux/threads.h" +#include "linux/interrupt.h" +#include "asm/smp.h" +#include "asm/processor.h" +#include "asm/spinlock.h" +#include "asm/softirq.h" +#include "asm/hardirq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +/* Total count of live CPUs */ +int smp_num_cpus = 1; + +/* The 'big kernel lock' */ +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_um cpu_data[NR_CPUS]; + +/* CPU online map */ +unsigned long cpu_online_map; + +spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED; + +atomic_t global_bh_count; + +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile long global_irq_lock; + +/* Set when the idlers are all forked */ +int smp_threads_ready = 0; +int num_reschedules_sent = 0; + +void smp_send_reschedule(int cpu) +{ + write(cpu_data[cpu].ipi_pipe[1], "R", 1); + num_reschedules_sent++; +} + +static void show(char * str) +{ + int cpu = smp_processor_id(); + + printk(KERN_INFO "\n%s, CPU %d:\n", str, cpu); +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +void smp_send_stop(void) +{ + printk(KERN_INFO "Stopping all CPUs\n"); +} + + +static atomic_t smp_commenced = ATOMIC_INIT(0); +static volatile unsigned long smp_callin_map = 0; + +void smp_commence(void) +{ + printk("All CPUs are go!\n"); + + wmb(); + atomic_set(&smp_commenced, 1); +} + +static int idle_proc(void *unused) +{ + int cpu; + + set_current(current); + del_from_runqueue(current); + unhash_process(current); + + cpu = current->processor; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, cpu_data[cpu].ipi_pipe) < 0) + panic("CPU#%d failed to create IPI pipe", cpu); + + activate_ipi(cpu_data[cpu].ipi_pipe[0], current->thread.extern_pid); + + wmb(); + if (test_and_set_bit(current->processor, &smp_callin_map)) { + printk("huh, CPU#%d already present??\n", current->processor); + BUG(); + } + + while (!atomic_read(&smp_commenced)) + cpu_relax(); + + init_idle(); + cpu_idle(); + return(0); +} + +void smp_boot_cpus(void) +{ + set_bit(0, &cpu_online_map); + set_bit(0, &smp_callin_map); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, cpu_data[0].ipi_pipe) < 0) + panic("CPU#0 failed to create IPI pipe"); + activate_ipi(cpu_data[0].ipi_pipe[0], current->thread.extern_pid); + + if(ncpus < 1){ + printk(KERN_INFO "ncpus set to 1\n"); + ncpus = 1; + } + else if(ncpus > NR_CPUS){ + printk(KERN_INFO + "ncpus can't be greater than NR_CPUS, set to %d\n", + NR_CPUS); + ncpus = NR_CPUS; + } + + if(ncpus > 1){ + int i, pid; + + printk(KERN_INFO "Starting up other processors:\n"); + for(i=1;ineed_resched = 1; + break; + + default: + printk("CPU#%d received unknown IPI [%c]!\n", cpu, c); + break; + } + } +} + +int inited_cpus = 1; + +int hard_smp_processor_id(void) +{ + return(pid_to_processor_id(getpid())); +} + +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; +static atomic_t scf_started; +static atomic_t scf_finished; +static void (*func)(void *info); +static void *info; + +void smp_call_function_slave(int cpu) +{ + atomic_inc(&scf_started); + (*func)(info); + atomic_inc(&scf_finished); +} + +int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, + int wait) +{ + int cpus = smp_num_cpus - 1; + int i; + + if (!cpus) + return 0; + + spin_lock_bh(&call_lock); + atomic_set(&scf_started, 0); + atomic_set(&scf_finished, 0); + func = _func; + info = _info; + + for (i=0;iprocessor && test_bit(i, &cpu_online_map)) + write(cpu_data[i].ipi_pipe[1], "C", 1); + + while (atomic_read(&scf_started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&scf_finished) != cpus) + barrier(); + + spin_unlock_bh(&call_lock); + return 0; +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/sys_call_table.c b/arch/um/kernel/sys_call_table.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/sys_call_table.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/unistd.h" +#include "asm/signal.h" +#include "linux/sys.h" +#include "sysdep/syscalls.h" +#include "kern_util.h" + +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_exit; +extern syscall_handler_t sys_fork; +extern syscall_handler_t sys_read; +extern syscall_handler_t sys_write; +extern syscall_handler_t sys_creat; +extern syscall_handler_t sys_link; +extern syscall_handler_t sys_unlink; +extern syscall_handler_t sys_chdir; +extern syscall_handler_t sys_mknod; +extern syscall_handler_t sys_chmod; +extern syscall_handler_t sys_lchown16; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_stat; +extern syscall_handler_t sys_lseek; +extern syscall_handler_t sys_getpid; +extern syscall_handler_t sys_oldumount; +extern syscall_handler_t sys_setuid16; +extern syscall_handler_t sys_getuid16; +extern syscall_handler_t sys_ptrace; +extern syscall_handler_t sys_alarm; +extern syscall_handler_t sys_fstat; +extern syscall_handler_t sys_pause; +extern syscall_handler_t sys_utime; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_access; +extern syscall_handler_t sys_nice; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_sync; +extern syscall_handler_t sys_kill; +extern syscall_handler_t sys_rename; +extern syscall_handler_t sys_mkdir; +extern syscall_handler_t sys_rmdir; +extern syscall_handler_t sys_pipe; +extern syscall_handler_t sys_times; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_brk; +extern syscall_handler_t sys_setgid16; +extern syscall_handler_t sys_getgid16; +extern syscall_handler_t sys_signal; +extern syscall_handler_t sys_geteuid16; +extern syscall_handler_t sys_getegid16; +extern syscall_handler_t sys_acct; +extern syscall_handler_t sys_umount; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_ioctl; +extern syscall_handler_t sys_fcntl; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_setpgid; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_olduname; +extern syscall_handler_t sys_umask; +extern syscall_handler_t sys_chroot; +extern syscall_handler_t sys_ustat; +extern syscall_handler_t sys_dup2; +extern syscall_handler_t sys_getppid; +extern syscall_handler_t sys_getpgrp; +extern syscall_handler_t sys_sigaction; +extern syscall_handler_t sys_sgetmask; +extern syscall_handler_t sys_ssetmask; +extern syscall_handler_t sys_setreuid16; +extern syscall_handler_t sys_setregid16; +extern syscall_handler_t sys_sigsuspend; +extern syscall_handler_t sys_sigpending; +extern syscall_handler_t sys_sethostname; +extern syscall_handler_t sys_setrlimit; +extern syscall_handler_t sys_old_getrlimit; +extern syscall_handler_t sys_getrusage; +extern syscall_handler_t sys_gettimeofday; +extern syscall_handler_t sys_settimeofday; +extern syscall_handler_t sys_getgroups16; +extern syscall_handler_t sys_setgroups16; +extern syscall_handler_t sys_symlink; +extern syscall_handler_t sys_lstat; +extern syscall_handler_t sys_readlink; +extern syscall_handler_t sys_uselib; +extern syscall_handler_t sys_swapon; +extern syscall_handler_t sys_reboot; +extern syscall_handler_t old_readdir; +extern syscall_handler_t sys_munmap; +extern syscall_handler_t sys_truncate; +extern syscall_handler_t sys_ftruncate; +extern syscall_handler_t sys_fchmod; +extern syscall_handler_t sys_fchown16; +extern syscall_handler_t sys_getpriority; +extern syscall_handler_t sys_setpriority; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_statfs; +extern syscall_handler_t sys_fstatfs; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_socketcall; +extern syscall_handler_t sys_syslog; +extern syscall_handler_t sys_setitimer; +extern syscall_handler_t sys_getitimer; +extern syscall_handler_t sys_newstat; +extern syscall_handler_t sys_newlstat; +extern syscall_handler_t sys_newfstat; +extern syscall_handler_t sys_uname; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_vhangup; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_swapoff; +extern syscall_handler_t sys_sysinfo; +extern syscall_handler_t sys_ipc; +extern syscall_handler_t sys_fsync; +extern syscall_handler_t sys_sigreturn; +extern syscall_handler_t sys_clone; +extern syscall_handler_t sys_setdomainname; +extern syscall_handler_t sys_newuname; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_adjtimex; +extern syscall_handler_t sys_mprotect; +extern syscall_handler_t sys_sigprocmask; +extern syscall_handler_t sys_create_module; +extern syscall_handler_t sys_init_module; +extern syscall_handler_t sys_delete_module; +extern syscall_handler_t sys_get_kernel_syms; +extern syscall_handler_t sys_quotactl; +extern syscall_handler_t sys_getpgid; +extern syscall_handler_t sys_fchdir; +extern syscall_handler_t sys_bdflush; +extern syscall_handler_t sys_sysfs; +extern syscall_handler_t sys_personality; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_setfsuid16; +extern syscall_handler_t sys_setfsgid16; +extern syscall_handler_t sys_llseek; +extern syscall_handler_t sys_getdents; +extern syscall_handler_t sys_flock; +extern syscall_handler_t sys_msync; +extern syscall_handler_t sys_readv; +extern syscall_handler_t sys_writev; +extern syscall_handler_t sys_getsid; +extern syscall_handler_t sys_fdatasync; +extern syscall_handler_t sys_sysctl; +extern syscall_handler_t sys_mlock; +extern syscall_handler_t sys_munlock; +extern syscall_handler_t sys_mlockall; +extern syscall_handler_t sys_munlockall; +extern syscall_handler_t sys_sched_setparam; +extern syscall_handler_t sys_sched_getparam; +extern syscall_handler_t sys_sched_setscheduler; +extern syscall_handler_t sys_sched_getscheduler; +extern long sys_sched_yield(void); +extern syscall_handler_t sys_sched_get_priority_max; +extern syscall_handler_t sys_sched_get_priority_min; +extern syscall_handler_t sys_sched_rr_get_interval; +extern syscall_handler_t sys_nanosleep; +extern syscall_handler_t sys_mremap; +extern syscall_handler_t sys_setresuid16; +extern syscall_handler_t sys_getresuid16; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_query_module; +extern syscall_handler_t sys_poll; +extern syscall_handler_t sys_nfsservctl; +extern syscall_handler_t sys_setresgid16; +extern syscall_handler_t sys_getresgid16; +extern syscall_handler_t sys_prctl; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_rt_sigaction; +extern syscall_handler_t sys_rt_sigprocmask; +extern syscall_handler_t sys_rt_sigpending; +extern syscall_handler_t sys_rt_sigtimedwait; +extern syscall_handler_t sys_rt_sigqueueinfo; +extern syscall_handler_t sys_rt_sigsuspend; +extern syscall_handler_t sys_pread; +extern syscall_handler_t sys_pwrite; +extern syscall_handler_t sys_chown16; +extern syscall_handler_t sys_getcwd; +extern syscall_handler_t sys_capget; +extern syscall_handler_t sys_capset; +extern syscall_handler_t sys_sigaltstack; +extern syscall_handler_t sys_sendfile; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_vfork; +extern syscall_handler_t sys_getrlimit; +extern syscall_handler_t sys_mmap2; +extern syscall_handler_t sys_truncate64; +extern syscall_handler_t sys_ftruncate64; +extern syscall_handler_t sys_stat64; +extern syscall_handler_t sys_lstat64; +extern syscall_handler_t sys_fstat64; +extern syscall_handler_t sys_lchown; +extern syscall_handler_t sys_getuid; +extern syscall_handler_t sys_getgid; +extern syscall_handler_t sys_geteuid; +extern syscall_handler_t sys_getegid; +extern syscall_handler_t sys_setreuid; +extern syscall_handler_t sys_setregid; +extern syscall_handler_t sys_getgroups; +extern syscall_handler_t sys_setgroups; +extern syscall_handler_t sys_fchown; +extern syscall_handler_t sys_setresuid; +extern syscall_handler_t sys_getresuid; +extern syscall_handler_t sys_setresgid; +extern syscall_handler_t sys_getresgid; +extern syscall_handler_t sys_chown; +extern syscall_handler_t sys_setuid; +extern syscall_handler_t sys_setgid; +extern syscall_handler_t sys_setfsuid; +extern syscall_handler_t sys_setfsgid; +extern syscall_handler_t sys_pivot_root; +extern syscall_handler_t sys_mincore; +extern syscall_handler_t sys_madvise; +extern syscall_handler_t sys_fcntl64; +extern syscall_handler_t sys_getdents64; +extern syscall_handler_t sys_gettid; +extern syscall_handler_t sys_readahead; + +extern syscall_handler_t um_mount; +extern syscall_handler_t um_time; +extern syscall_handler_t um_stime; + +#define LAST_GENERIC_SYSCALL __NR_readahead + +#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL +#define LAST_SYSCALL LAST_GENERIC_SYSCALL +#else +#define LAST_SYSCALL LAST_ARCH_SYSCALL +#endif + +syscall_handler_t *sys_call_table[] = { + [ 0 ] = sys_ni_syscall, + [ __NR_exit ] = sys_exit, + [ __NR_fork ] = sys_fork, + [ __NR_read ] = sys_read, + [ __NR_write ] = sys_write, + + /* These three are declared differently in asm/unistd.h */ + [ __NR_open ] = (syscall_handler_t *) sys_open, + [ __NR_close ] = (syscall_handler_t *) sys_close, + [ __NR_waitpid ] = (syscall_handler_t *) sys_waitpid, + [ __NR_creat ] = sys_creat, + [ __NR_link ] = sys_link, + [ __NR_unlink ] = sys_unlink, + + /* declared differently in kern_util.h */ + [ __NR_execve ] = (syscall_handler_t *) sys_execve, + [ __NR_chdir ] = sys_chdir, + [ __NR_time ] = um_time, + [ __NR_mknod ] = sys_mknod, + [ __NR_chmod ] = sys_chmod, + [ __NR_lchown ] = sys_lchown16, + [ __NR_break ] = sys_ni_syscall, + [ __NR_oldstat ] = sys_stat, + [ __NR_lseek ] = sys_lseek, + [ __NR_getpid ] = sys_getpid, + [ __NR_mount ] = um_mount, + [ __NR_umount ] = sys_oldumount, + [ __NR_setuid ] = sys_setuid16, + [ __NR_getuid ] = sys_getuid16, + [ __NR_stime ] = um_stime, + [ __NR_ptrace ] = sys_ptrace, + [ __NR_alarm ] = sys_alarm, + [ __NR_oldfstat ] = sys_fstat, + [ __NR_pause ] = sys_pause, + [ __NR_utime ] = sys_utime, + [ __NR_stty ] = sys_ni_syscall, + [ __NR_gtty ] = sys_ni_syscall, + [ __NR_access ] = sys_access, + [ __NR_nice ] = sys_nice, + [ __NR_ftime ] = sys_ni_syscall, + [ __NR_sync ] = sys_sync, + [ __NR_kill ] = sys_kill, + [ __NR_rename ] = sys_rename, + [ __NR_mkdir ] = sys_mkdir, + [ __NR_rmdir ] = sys_rmdir, + + /* Declared differently in asm/unistd.h */ + [ __NR_dup ] = (syscall_handler_t *) sys_dup, + [ __NR_pipe ] = sys_pipe, + [ __NR_times ] = sys_times, + [ __NR_prof ] = sys_ni_syscall, + [ __NR_brk ] = sys_brk, + [ __NR_setgid ] = sys_setgid16, + [ __NR_getgid ] = sys_getgid16, + [ __NR_signal ] = sys_signal, + [ __NR_geteuid ] = sys_geteuid16, + [ __NR_getegid ] = sys_getegid16, + [ __NR_acct ] = sys_acct, + [ __NR_umount2 ] = sys_umount, + [ __NR_lock ] = sys_ni_syscall, + [ __NR_ioctl ] = sys_ioctl, + [ __NR_fcntl ] = sys_fcntl, + [ __NR_mpx ] = sys_ni_syscall, + [ __NR_setpgid ] = sys_setpgid, + [ __NR_ulimit ] = sys_ni_syscall, + [ __NR_oldolduname ] = sys_olduname, + [ __NR_umask ] = sys_umask, + [ __NR_chroot ] = sys_chroot, + [ __NR_ustat ] = sys_ustat, + [ __NR_dup2 ] = sys_dup2, + [ __NR_getppid ] = sys_getppid, + [ __NR_getpgrp ] = sys_getpgrp, + [ __NR_setsid ] = (syscall_handler_t *) sys_setsid, + [ __NR_sigaction ] = sys_sigaction, + [ __NR_sgetmask ] = sys_sgetmask, + [ __NR_ssetmask ] = sys_ssetmask, + [ __NR_setreuid ] = sys_setreuid16, + [ __NR_setregid ] = sys_setregid16, + [ __NR_sigsuspend ] = sys_sigsuspend, + [ __NR_sigpending ] = sys_sigpending, + [ __NR_sethostname ] = sys_sethostname, + [ __NR_setrlimit ] = sys_setrlimit, + [ __NR_getrlimit ] = sys_old_getrlimit, + [ __NR_getrusage ] = sys_getrusage, + [ __NR_gettimeofday ] = sys_gettimeofday, + [ __NR_settimeofday ] = sys_settimeofday, + [ __NR_getgroups ] = sys_getgroups16, + [ __NR_setgroups ] = sys_setgroups16, + [ __NR_symlink ] = sys_symlink, + [ __NR_oldlstat ] = sys_lstat, + [ __NR_readlink ] = sys_readlink, + [ __NR_uselib ] = sys_uselib, + [ __NR_swapon ] = sys_swapon, + [ __NR_reboot ] = sys_reboot, + [ __NR_readdir ] = old_readdir, + [ __NR_munmap ] = sys_munmap, + [ __NR_truncate ] = sys_truncate, + [ __NR_ftruncate ] = sys_ftruncate, + [ __NR_fchmod ] = sys_fchmod, + [ __NR_fchown ] = sys_fchown16, + [ __NR_getpriority ] = sys_getpriority, + [ __NR_setpriority ] = sys_setpriority, + [ __NR_profil ] = sys_ni_syscall, + [ __NR_statfs ] = sys_statfs, + [ __NR_fstatfs ] = sys_fstatfs, + [ __NR_ioperm ] = sys_ni_syscall, + [ __NR_socketcall ] = sys_socketcall, + [ __NR_syslog ] = sys_syslog, + [ __NR_setitimer ] = sys_setitimer, + [ __NR_getitimer ] = sys_getitimer, + [ __NR_stat ] = sys_newstat, + [ __NR_lstat ] = sys_newlstat, + [ __NR_fstat ] = sys_newfstat, + [ __NR_olduname ] = sys_uname, + [ __NR_iopl ] = sys_ni_syscall, + [ __NR_vhangup ] = sys_vhangup, + [ __NR_idle ] = sys_ni_syscall, + [ __NR_wait4 ] = (syscall_handler_t *) sys_wait4, + [ __NR_swapoff ] = sys_swapoff, + [ __NR_sysinfo ] = sys_sysinfo, + [ __NR_ipc ] = sys_ipc, + [ __NR_fsync ] = sys_fsync, + [ __NR_sigreturn ] = sys_sigreturn, + [ __NR_clone ] = sys_clone, + [ __NR_setdomainname ] = sys_setdomainname, + [ __NR_uname ] = sys_newuname, + [ __NR_adjtimex ] = sys_adjtimex, + [ __NR_mprotect ] = sys_mprotect, + [ __NR_sigprocmask ] = sys_sigprocmask, + [ __NR_create_module ] = sys_create_module, + [ __NR_init_module ] = sys_init_module, + [ __NR_delete_module ] = sys_delete_module, + [ __NR_get_kernel_syms ] = sys_get_kernel_syms, + [ __NR_quotactl ] = sys_quotactl, + [ __NR_getpgid ] = sys_getpgid, + [ __NR_fchdir ] = sys_fchdir, + [ __NR_bdflush ] = sys_bdflush, + [ __NR_sysfs ] = sys_sysfs, + [ __NR_personality ] = sys_personality, + [ __NR_afs_syscall ] = sys_ni_syscall, + [ __NR_setfsuid ] = sys_setfsuid16, + [ __NR_setfsgid ] = sys_setfsgid16, + [ __NR__llseek ] = sys_llseek, + [ __NR_getdents ] = sys_getdents, + [ __NR__newselect ] = (syscall_handler_t *) sys_select, + [ __NR_flock ] = sys_flock, + [ __NR_msync ] = sys_msync, + [ __NR_readv ] = sys_readv, + [ __NR_writev ] = sys_writev, + [ __NR_getsid ] = sys_getsid, + [ __NR_fdatasync ] = sys_fdatasync, + [ __NR__sysctl ] = sys_sysctl, + [ __NR_mlock ] = sys_mlock, + [ __NR_munlock ] = sys_munlock, + [ __NR_mlockall ] = sys_mlockall, + [ __NR_munlockall ] = sys_munlockall, + [ __NR_sched_setparam ] = sys_sched_setparam, + [ __NR_sched_getparam ] = sys_sched_getparam, + [ __NR_sched_setscheduler ] = sys_sched_setscheduler, + [ __NR_sched_getscheduler ] = sys_sched_getscheduler, + [ __NR_sched_yield ] = sys_sched_yield, + [ __NR_sched_get_priority_max ] = sys_sched_get_priority_max, + [ __NR_sched_get_priority_min ] = sys_sched_get_priority_min, + [ __NR_sched_rr_get_interval ] = sys_sched_rr_get_interval, + [ __NR_nanosleep ] = sys_nanosleep, + [ __NR_mremap ] = sys_mremap, + [ __NR_setresuid ] = sys_setresuid16, + [ __NR_getresuid ] = sys_getresuid16, + [ __NR_vm86 ] = sys_ni_syscall, + [ __NR_query_module ] = sys_query_module, + [ __NR_poll ] = sys_poll, + [ __NR_nfsservctl ] = sys_nfsservctl, + [ __NR_setresgid ] = sys_setresgid16, + [ __NR_getresgid ] = sys_getresgid16, + [ __NR_prctl ] = sys_prctl, + [ __NR_rt_sigreturn ] = sys_ni_syscall, + [ __NR_rt_sigaction ] = sys_rt_sigaction, + [ __NR_rt_sigprocmask ] = sys_rt_sigprocmask, + [ __NR_rt_sigpending ] = sys_rt_sigpending, + [ __NR_rt_sigtimedwait ] = sys_rt_sigtimedwait, + [ __NR_rt_sigqueueinfo ] = sys_rt_sigqueueinfo, + [ __NR_rt_sigsuspend ] = sys_rt_sigsuspend, + [ __NR_pread ] = sys_pread, + [ __NR_pwrite ] = sys_pwrite, + [ __NR_chown ] = sys_chown16, + [ __NR_getcwd ] = sys_getcwd, + [ __NR_capget ] = sys_capget, + [ __NR_capset ] = sys_capset, + [ __NR_sigaltstack ] = sys_sigaltstack, + [ __NR_sendfile ] = sys_sendfile, + [ __NR_getpmsg ] = sys_ni_syscall, + [ __NR_putpmsg ] = sys_ni_syscall, + [ __NR_vfork ] = sys_vfork, + [ __NR_ugetrlimit ] = sys_getrlimit, + [ __NR_mmap2 ] = sys_mmap2, + [ __NR_truncate64 ] = sys_truncate64, + [ __NR_ftruncate64 ] = sys_ftruncate64, + [ __NR_stat64 ] = sys_stat64, + [ __NR_lstat64 ] = sys_lstat64, + [ __NR_fstat64 ] = sys_fstat64, + [ __NR_fcntl64 ] = sys_fcntl64, + [ __NR_getdents64 ] = sys_getdents64, + [ __NR_security ] = sys_ni_syscall, + [ __NR_gettid ] = sys_gettid, + [ __NR_readahead ] = sys_readahead, + ARCH_SYSCALLS + [ LAST_SYSCALL + 1 ... NR_syscalls ] = + (syscall_handler_t *) sys_ni_syscall +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/syscall_kern.c b/arch/um/kernel/syscall_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/syscall_kern.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/file.h" +#include "linux/smp_lock.h" +#include "linux/mm.h" +#include "linux/utsname.h" +#include "linux/msg.h" +#include "linux/shm.h" +#include "linux/sys.h" +#include "linux/unistd.h" +#include "linux/slab.h" +#include "asm/mman.h" +#include "asm/uaccess.h" +#include "asm/ipc.h" +#include "kern_util.h" +#include "user_util.h" +#include "sysdep/syscalls.h" + +long um_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void * data) +{ + if(type == NULL) type = ""; + return(sys_mount(dev_name, dir_name, type, new_flags, data)); +} + +long sys_fork(void) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_clone(unsigned long clone_flags, unsigned long newsp) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(clone_flags, newsp, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_vfork(void) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); + out: + return error; +} + +long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset) +{ + int err = -EINVAL; + if (offset & ~PAGE_MASK) + goto out; + + err = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + out: + return err; +} +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +int sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + panic("msgrcv with version != 0"); + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + } +} + +int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname, + __OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename, + __OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release, + __OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version, + __OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine, + __OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +int sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return(do_sigaltstack(uss, uoss, + UM_SP(¤t->thread.process_regs))); +} + +int nsyscalls = 0; + +extern syscall_handler_t *sys_call_table[]; + +long execute_syscall(struct sys_pt_regs regs) +{ + long res; + int syscall; + + current->thread.nsyscalls++; + nsyscalls++; + syscall = UM_SYSCALL_NR(®s); + if(syscall == -1) + panic("syscall thread activated without a system call"); + if((syscall >= NR_syscalls) || (syscall < 0)) + return(-ENOSYS); + + set_fs(KERNEL_DS); + res = EXECUTE_SYSCALL(syscall, regs); + set_fs(USER_DS); + + return(res); +} + +spinlock_t syscall_lock = SPIN_LOCK_UNLOCKED; + +void lock_syscall(void) +{ + spin_lock(&syscall_lock); +} + +void unlock_syscall(void) +{ + spin_unlock(&syscall_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/syscall_user.c b/arch/um/kernel/syscall_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/syscall_user.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +/* XXX FIXME : Ensure that SIGIO and SIGVTALRM can't happen immediately + * after setting up syscall stack + * block SIGVTALRM in any code that's under wait_for_stop + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "signal_kern.h" +#include "sysdep/ptrace.h" + +/* XXX Bogus */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 + +struct { + int syscall; + int pid; + int result; + struct timeval start; + struct timeval end; +} syscall_record[1024]; + +int syscall_index = 0; + +extern int timer_ready; +extern int signal_frame_size; + +int syscall_handler(void *unused) +{ + struct sys_pt_regs *regs; + long result; + int index, syscall, again; + + kill(getpid(), SIGSTOP); + unprotect_kernel_mem(); + timer_ready = 1; + syscall_trace(); + lock_syscall(); + if(syscall_index == 1024) syscall_index = 0; + index = syscall_index; + syscall_index++; + unlock_syscall(); + regs = process_state(NULL); + syscall = UM_SYSCALL_NR(regs); + syscall_record[index].syscall = UM_SYSCALL_NR(regs); + syscall_record[index].pid = current_pid(); + syscall_record[index].result = 0xdeadbeef; + gettimeofday(&syscall_record[index].start, NULL); + result = execute_syscall(*regs); + again = 0; + if((result == -ERESTARTNOHAND) || (result == -ERESTARTSYS) || + (result == -ERESTARTNOINTR)) + do_signal(&result, &again); + UM_SET_SYSCALL_RETURN(regs, result); + set_repeat_syscall(NULL, again); + syscall_trace(); + syscall_record[index].result = result; + gettimeofday(&syscall_record[index].end, NULL); + ret_from_sys_call(); + + /* XXX + * This is a race, set_user_thread has to be called with signals off + */ + timer_ready = 0; + set_user_thread(NULL, 1, 1, 1); + return(0); +} + +int exit_kernel(int pid, void *task) +{ + void *stack; + struct sys_pt_regs *regs; + int tracing, again, n, restore; + + tracing = 1; + again = get_repeat_syscall(task); + set_repeat_syscall(task, 0); + restore = get_restore_regs(task); + regs = process_state(task); + if(restore){ + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't restore registers"); + } + if(have_signals(task)){ + if(!restore) ptrace_getregs(pid, regs); + regs = signal_state(task); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't set alternate stack state"); + lock_signalled_task(task); + } + else if(again){ + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't restart system call"); + memcpy((void *) UM_SP(regs), stack, n); + tracing = 0; + } + return(tracing); +} + +extern unsigned long _stext, _etext; + +int do_syscall(void *task, int pid) +{ + void *stack; + struct sys_pt_regs *regs, proc_regs; + int syscall, n; + + if(ptrace_getregs(pid, &proc_regs) < 0) + tracer_panic("Couldn't read registers"); + + syscall = UM_SYSCALL_NR(&proc_regs); + if(syscall < 1) return(0); + + if((syscall != __NR_sigreturn) && + ((unsigned long *) UM_IP(&proc_regs) >= &_stext) && + ((unsigned long *) UM_IP(&proc_regs) <= &_etext)) + tracer_panic("I'm tracing myself and I can't get out"); + + regs = process_state(task); + *regs = proc_regs; + set_tracing(task, 0); + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't set system call state"); + memcpy((void *) UM_SP(regs), stack, n); + + if((ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, + __NR_getpid) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)){ + printk("Failed to change syscall number to __NR_getpid\n"); + if(errno == EIO){ + printk("You probably didn't apply the ptrace patch " + "to your hosting kernel\n"); + } + else printk("errno = %d\n", errno); + tracer_panic("do_syscall : Couldn't force getpid"); + } + return(1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/sysrq.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/kernel.h" +#include "linux/module.h" +#include "asm/page.h" +#include "asm/processor.h" +#include "sysrq.h" + +extern int _stext, _etext; + + /* + * If the address is either in the .text section of the + * kernel, or in the vmalloc'ed module regions, it *may* + * be the address of a calling routine + */ + +#ifdef CONFIG_MODULES + +extern struct module *module_list; +extern struct module kernel_module; + +static inline int kernel_text_address(unsigned long addr) +{ + int retval = 0; + struct module *mod; + + if (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext) + return 1; + + for (mod = module_list; mod != &kernel_module; mod = mod->next) { + /* mod_bound tests for addr being inside the vmalloc'ed + * module area. Of course it'd be better to test only + * for the .text subset... */ + if (mod_bound(addr, 0, mod)) { + retval = 1; + break; + } + } + + return retval; +} + +#else + +static inline int kernel_text_address(unsigned long addr) +{ + return (addr >= (unsigned long) &_stext && + addr <= (unsigned long) &_etext); +} + +#endif + +void show_trace(unsigned long * stack) +{ + int i; + unsigned long addr; + + if (!stack) + stack = (unsigned long*) &stack; + + printk("Call Trace: "); + i = 1; + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + if (kernel_text_address(addr)) { + if (i && ((i % 6) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } + printk("\n"); +} + +void show_trace_task(struct task_struct *tsk) +{ + unsigned long esp = UM_SP(&tsk->thread.process_regs); + + /* User space on another CPU? */ + if ((esp ^ (unsigned long)tsk) & (PAGE_MASK<<1)) + return; + show_trace((unsigned long *)esp); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/time.c b/arch/um/kernel/time.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/time.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#define _GNU_SOURCE /* to get timeradd and timersub */ + +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" + +extern struct timeval xtime; + +void timer_handler(int sig, void *sc, int usermode) +{ + timer_irq(usermode); +} + +void timer(void) +{ + gettimeofday(&xtime, NULL); +} + +static struct itimerval profile_interval; + +void get_profile_timer(void) +{ + getitimer(ITIMER_PROF, &profile_interval); + profile_interval.it_value = profile_interval.it_interval; +} + +void disable_profile_timer(void) +{ + struct itimerval interval = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); + setitimer(ITIMER_PROF, &interval, NULL); +} + +static void set_interval(int timer_type) +{ + struct itimerval interval; + + interval.it_interval.tv_sec = 0; + interval.it_interval.tv_usec = 1000000/hz(); + interval.it_value.tv_sec = 0; + interval.it_value.tv_usec = 1000000/hz(); + if(setitimer(timer_type, &interval, NULL) == -1) + panic("setitimer failed - errno = %d\n", errno); +} + +void idle_timer(void) +{ + if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) + panic("Couldn't unset SIGVTALRM handler"); + set_handler(SIGALRM, (__sighandler_t) alarm_handler, + SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, -1); + set_interval(ITIMER_REAL); +} + +void time_init(void) +{ + if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); +} + +void set_timers(int set_signal) +{ + if(set_signal){ + if(signal(SIGVTALRM, + (__sighandler_t) alarm_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); + } + if(setitimer(ITIMER_PROF, &profile_interval, NULL) == -1) + panic("setitimer ITIMER_PROF failed - errno = %d\n", errno); +} + +struct timeval local_offset = { 0, 0 }; + +void do_gettimeofday(struct timeval *tv) +{ + gettimeofday(tv, NULL); + timeradd(tv, &local_offset, tv); +} + +void do_settimeofday(struct timeval *tv) +{ + struct timeval now; + + gettimeofday(&now, NULL); + timersub(tv, &now, &local_offset); +} + +void idle_sleep(int secs) +{ + struct timespec ts; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + nanosleep(&ts, &ts); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/time_kern.c b/arch/um/kernel/time_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/time_kern.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/unistd.h" +#include "linux/stddef.h" +#include "linux/spinlock.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/init.h" +#include "linux/delay.h" +#include "asm/param.h" +#include "asm/current.h" +#include "kern_util.h" +#include "user_util.h" + +extern rwlock_t xtime_lock; + +int hz(void) +{ + return(HZ); +} + +int timer_irq_inited = 0; + +int timer_on = 0; +int timer_ready = 0; +int missed_ticks = 0; + +void timer_irq(int user_mode) +{ + int ticks = missed_ticks; + + if(!timer_irq_inited) return; + missed_ticks = 0; + while(ticks--) do_IRQ(TIMER_IRQ, user_mode); +} + +void boot_timer_handler(int sig) +{ + struct pt_regs regs; + + regs.user_mode = 0; + do_timer(®s); +} + + + +void um_timer(int irq, void *dev, struct pt_regs *regs) +{ + do_timer(regs); + write_lock(&xtime_lock); + timer(); + write_unlock(&xtime_lock); +} + +long um_time(int * tloc) +{ + struct timeval now; + + do_gettimeofday(&now); + if (tloc) { + if (put_user(now.tv_sec,tloc)) + now.tv_sec = -EFAULT; + } + return now.tv_sec; +} + +long um_stime(int * tptr) +{ + int value; + struct timeval new; + + if (get_user(value, tptr)) + return -EFAULT; + new.tv_sec = value; + new.tv_usec = 0; + do_settimeofday(&new); + return 0; +} + +void __delay(um_udelay_t time) +{ + int i; + + for(i=0;ithread.extern_pid != -1) && + (current->thread.extern_pid != getpid())) + panic("fix_range fixing wrong address space, current = 0x%p", + current); + if(mm == NULL) return; + for(addr=start_addr;addr TASK_SIZE, which is + * only true in the honeypot case. + */ + addr = STACK_TOP - ABOVE_KMEM; + continue; + } + npgd = pgd_offset(mm, addr); + npmd = pmd_offset(npgd, addr); + if(pmd_present(*npmd)){ + npte = pte_offset(npmd, addr); + r = pte_read(*npte); + w = pte_write(*npte); + x = pte_exec(*npte); + if(!pte_dirty(*npte)) w = 0; + if(!pte_young(*npte)){ + r = 0; + w = 0; + } + if(force || pte_newpage(*npte)){ + if(munmap((void *) addr, PAGE_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + if(pte_present(*npte)) + map(addr, pte_address(*npte), + PAGE_SIZE, r, w, x); + } + else if(pte_newprot(*npte)) + protect(addr, PAGE_SIZE, r, w, x); + *npte = pte_mkuptodate(*npte); + addr += PAGE_SIZE; + } + else { + if(force || pmd_newpage(*npmd)){ + if(munmap((void *) addr, PMD_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + } + addr += PMD_SIZE; + } + } +} + +atomic_t vmchange_seq = ATOMIC_INIT(1); + +static void flush_kernel_vm_range(unsigned long start, unsigned long end, + int update_seq) +{ + struct mm_struct *mm; + pgd_t *npgd; + pmd_t *npmd; + pte_t *npte; + unsigned long addr; + int r, w, x, updated = 0; + + mm = &init_mm; + for(addr = start_vm; addr < end_vm;){ + npgd = pgd_offset(mm, addr); + npmd = pmd_offset(npgd, addr); + if(pmd_present(*npmd)){ + unsigned long mask = PAGE_MASK | _PAGE_NEWPAGE | + _PAGE_NEWPROT; + npte = pte_offset(npmd, addr); + + if((pte_val(*npte) & ~mask) == + pgprot_val(PAGE_KERNEL)){ + r = 1; + w = 1; + x = 1; + } + else { + r = 1; + w = 0; + x = 1; + } + if(!pte_present(*npte) || pte_newpage(*npte)){ + updated = 1; + if(munmap((void *) addr, PAGE_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + if(pte_present(*npte)) + map(addr, pte_address(*npte), + PAGE_SIZE, r, w, x); + } + else if(pte_newprot(*npte)){ + updated = 1; + protect(addr, PAGE_SIZE, r, w, x); + } + addr += PAGE_SIZE; + + } + else { + if(pmd_newpage(*npmd)){ + updated = 1; + if(munmap((void *) addr, PMD_SIZE) < 0) + panic("munmap failed, errno = %d\n", + errno); + } + addr += PMD_SIZE; + } + } + if(updated && update_seq) atomic_inc(&vmchange_seq); +} + +void flush_tlb_kernel_vm(void) +{ + flush_kernel_vm_range(start_vm, end_vm, 1); +} + +void flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + if(mm != current->mm) return; + + /* Assumes that the range start ... end is entirely within + * either process memory or kernel vm + */ + if((start >= start_vm) && (start < end_vm)) + flush_kernel_vm_range(start, end, 1); + else fix_range(mm, start, end, 0); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + unsigned long seq; + + if(mm != current->mm) return; + + fix_range(mm, 0, STACK_TOP, 0); + + seq = atomic_read(&vmchange_seq); + if(current->thread.vm_seq == seq) return; + current->thread.vm_seq = seq; + flush_kernel_vm_range(start_vm, end_vm, 0); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) +{ + address &= PAGE_MASK; + flush_tlb_range(vma->vm_mm, address, address + PAGE_SIZE); +} + +void flush_tlb_all(void) +{ + flush_tlb_mm(current->mm); +} + +void force_flush_all(void) +{ + fix_range(current->mm, 0, STACK_TOP, 1); + flush_kernel_vm_range(start_vm, end_vm, 0); +} + +pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) +{ + return(pgd_offset(mm, address)); +} + +pmd_t *pmd_offset_proc(pgd_t *pgd, unsigned long address) +{ + return(pmd_offset(pgd, address)); +} + +pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) +{ + return(pte_offset(pmd, address)); +} + +pte_t *addr_pte(struct task_struct *task, unsigned long addr) +{ + return(pte_offset(pmd_offset(pgd_offset(task->mm, addr), addr), addr)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/trap_kern.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/spinlock.h" +#include "linux/config.h" +#include "linux/init.h" +#include "asm/semaphore.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/a.out.h" +#include "asm/current.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "chan_kern.h" +#include "debug.h" +#include "mconsole_kern.h" + +extern int nsyscalls; + +unsigned long segv(unsigned long address, unsigned long ip, int is_write, + int is_user) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + struct siginfo si; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long page; + int ok; + + if((address >= start_vm) && (address < end_vm)){ + flush_tlb_kernel_vm(); + return(0); + } + if(mm == NULL) panic("Segfault with no mm"); + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + ok = 1; + if(!vma) ok = 0; + else if(is_write && !(vma->vm_flags & VM_WRITE)) ok = 0; + else if(vma->vm_start > address){ + if((vma->vm_flags & VM_STACK_FLAGS) != VM_STACK_FLAGS) ok = 0; + else if(expand_stack(vma, address)) ok = 0; + } + if(!ok) goto bad; + page = address & PAGE_MASK; + if(page == (unsigned long) current + PAGE_SIZE) + panic("Kernel stack overflow"); + pgd = pgd_offset(mm, page); + pmd = pmd_offset(pgd, page); + do { + survive: + switch (handle_mm_fault(mm, vma, address, is_write)) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + default: + if (current->pid == 1) { + __set_task_state(current, TASK_RUNNING); + yield(); + goto survive; + } + /* Fall through to bad area case */ + case 0: + goto bad; + } + pte = pte_offset(pmd, page); + } while(!pte_present(*pte)); + *pte = pte_mkyoung(*pte); + if(pte_write(*pte)) *pte = pte_mkdirty(*pte); + flush_tlb_page(vma, page); + up_read(&mm->mmap_sem); + return(0); + bad: + if (current->thread.fault_catcher != NULL) { + current->thread.fault_addr = (void *) address; + up_read(&mm->mmap_sem); + do_longjmp(current->thread.fault_catcher); + } + else if(current->thread.fault_addr != NULL){ + panic("fault_addr set but no fault catcher"); + } + if(!is_user) + panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", + address, ip); + si.si_signo = SIGSEGV; + si.si_code = SEGV_ACCERR; + si.si_addr = (void *) address; + current->thread.cr2 = address; + current->thread.err = is_write; + force_sig_info(SIGSEGV, &si, current); + up_read(&mm->mmap_sem); + return(0); +} + +void bad_segv(unsigned long address, unsigned long ip, int is_write) +{ + struct siginfo si; + + printk(KERN_ERR "Unfixable SEGV in '%s' (pid %d) at 0x%lx " + "(ip 0x%lx)\n", current->comm, current->pid, address, ip); + si.si_signo = SIGSEGV; + si.si_code = SEGV_ACCERR; + si.si_addr = (void *) address; + current->thread.cr2 = address; + current->thread.err = is_write; + force_sig_info(SIGSEGV, &si, current); +} + +void relay_signal(int sig, void *sc, int usermode) +{ + force_sig(sig, current); +} + +void trap_init(void) +{ +} + +spinlock_t trap_lock = SPIN_LOCK_UNLOCKED; + +void lock_trap(void) +{ + spin_lock(&trap_lock); +} + +void unlock_trap(void) +{ + spin_unlock(&trap_lock); +} + +extern int debugger_pid; +extern int debugger_fd; + +#ifdef CONFIG_PT_PROXY + +void debugger_signal(int status, pid_t pid) +{ + debugger_proxy(status, pid); +} + +void child_signal(pid_t pid, int status) +{ + child_proxy(pid, status); +} + +static void gdb_announce(char *dev_name, int dev) +{ + printf("gdb assigned device '%s'\n", dev_name); +} + +static struct chan_opts opts = { + announce : gdb_announce, + xterm_title : "UML kernel debugger", + raw : 0 +}; + +static void *xterm_data; +static int xterm_fd; + +extern void *xterm_init(char *, int, struct chan_opts *); +extern int xterm_open(int, int, void *); +extern void xterm_close(int, void *); + +int open_gdb_chan(void) +{ + xterm_data = xterm_init("", 0, &opts); + xterm_fd = xterm_open(1, 1, xterm_data); + return(xterm_fd); +} + +static void exit_debugger_cb(void *unused) +{ + if(debugger_pid != -1){ + if(gdb_pid != -1){ + fake_child_exit(); + gdb_pid = -1; + } + else kill_child_dead(debugger_pid); + debugger_pid = -1; + } + if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data); +} + +static void exit_debugger(void) +{ + tracing_cb(exit_debugger_cb, NULL); +} + +__uml_exitcall(exit_debugger); + +struct gdb_data { + char *str; + int err; +}; + +static void config_gdb_cb(void *arg) +{ + struct gdb_data *data = arg; + struct task_struct *task; + int pid; + + data->err = -1; + if(debugger_pid != -1) exit_debugger_cb(NULL); + if(!strncmp(data->str, "pid,", strlen("pid,"))){ + data->str += strlen("pid,"); + pid = simple_strtoul(data->str, NULL, 0); + task = cpu_tasks[0].task; + debugger_pid = attach_debugger(task->thread.extern_pid, + pid, 0); + if(debugger_pid != -1){ + data->err = 0; + gdb_pid = pid; + } + return; + } + data->err = 0; + debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); + init_proxy(debugger_pid, 0, 0); +} + +int gdb_config(char *str) +{ + struct gdb_data data; + + if(*str++ != '=') return(-1); + data.str = str; + tracing_cb(config_gdb_cb, &data); + return(data.err); +} + +void remove_gdb_cb(void *unused) +{ + exit_debugger_cb(NULL); +} + +int gdb_remove(char *unused) +{ + tracing_cb(remove_gdb_cb, NULL); + return(0); +} + +#ifdef CONFIG_MCONSOLE + +static struct mc_device gdb_mc = { + name: "gdb", + config: gdb_config, + remove: gdb_remove, +}; + +int gdb_mc_init(void) +{ + mconsole_register_dev(&gdb_mc); + return(0); +} + +__initcall(gdb_mc_init); + +#endif + +void signal_usr1(int sig) +{ + if(debugger_pid != -1){ + printk(KERN_ERR "The debugger is already running\n"); + return; + } + debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); + init_proxy(debugger_pid, 0, 0); +} + +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + int pid, status; + + pid = start_debugger(linux_prog, startup, stop, &debugger_fd); + status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + if(pid < 0){ + cont(idle_pid); + return(-1); + } + init_proxy(pid, 1, status); + return(pid); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ + int status = 0; + + if(attach(pid) < 0){ + printf("Failed to attach pid %d, errno = %d\n", pid, errno); + return(-1); + } + if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + init_proxy(pid, 1, status); + return(pid); +} + +#ifdef notdef /* Put this back in when it does something useful */ +static int __init uml_gdb_init_setup(char *line, int *add) +{ + gdb_init = line; + return 0; +} + +__uml_setup("gdb=", uml_gdb_init_setup, +"gdb=\n\n" +); +#endif + +static int __init uml_gdb_pid_setup(char *line, int *add) +{ + gdb_pid = simple_strtoul(line, NULL, 0); + return 0; +} + +__uml_setup("gdb-pid=", uml_gdb_pid_setup, +"gdb-pid=\n" +" gdb-pid is used to attach an external debugger to UML. This may be\n" +" an already-running gdb or a debugger-like process like strace.\n\n" +); + +#else + +void debugger_signal(int status, pid_t pid){ } +void child_signal(pid_t pid, int status){ } +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + printk(KERN_ERR "debug requested when CONFIG_PT_PROXY is off\n"); + wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + cont(idle_pid); + return(-1); +} + +void signal_usr1(int sig) +{ + printk(KERN_ERR "debug requested when CONFIG_PT_PROXY is off\n"); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ + printk(KERN_ERR "attach_debugger called when CONFIG_PT_PROXY " + "is off\n"); + return(-1); +} + +int config_gdb(char *str) +{ + return(-1); +} + +int remove_gdb(void) +{ + return(-1); +} + +#endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/trap_user.c b/arch/um/kernel/trap_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/trap_user.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "signal_user.h" +#include "mem_user.h" +#include "user.h" +#include "process.h" +#include "sigcontext.h" +#include "sysdep/sigcontext.h" +#include "init.h" +#include "chan_user.h" +#include "irq_user.h" + +static void signal_segv(int sig) +{ + write(2, "Seg fault in signals\n", strlen("Seg fault in signals\n")); + for(;;) ; +} + +int detach(int pid, int sig) +{ + return(ptrace(PTRACE_DETACH, pid, 0, sig)); +} + +int attach(int pid) +{ + return(ptrace(PTRACE_ATTACH, pid, 0, 0)); +} + +int cont(int pid) +{ + return(ptrace(PTRACE_CONT, pid, 0, 0)); +} + +void kill_child_dead(int pid) +{ + kill(pid, SIGKILL); + kill(pid, SIGCONT); + while(waitpid(pid, NULL, 0) > 0) kill(pid, SIGCONT); +} + +int debug = 0; +int debug_stop = 1; + +int honeypot = 0; + +static int signal_tramp(void *arg) +{ + int (*proc)(void *); + + if(honeypot && munmap((void *) (host_task_size - 0x10000000), + 0x10000000)) + panic("Unmapping stack failed"); + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace PTRACE_TRACEME failed"); + signal(SIGUSR1, SIG_IGN); + signal(SIGSEGV, (__sighandler_t) sig_handler); + set_timers(0); + set_cmdline("(idle thread)"); + set_init_pid(getpid()); + proc = arg; + return((*proc)(NULL)); +} + +static void last_ditch_exit(int sig) +{ + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + uml_cleanup(); + exit(1); +} + +#ifdef CONFIG_SMP +#error need to make these arrays +#endif + +int debugger_pid = -1; +int debugger_fd = -1; +int gdb_pid = -1; + +struct { + unsigned long address; + int is_write; + int pid; + unsigned long sp; + int is_user; +} segfault_record[1024]; + +int segfault_index = 0; + +struct { + int pid; + int signal; + unsigned long addr; + struct timeval time; +} signal_record[1024]; + +int signal_index = 0; +int nsignals = 0; +int debug_trace = 0; +extern int io_nsignals, io_count, intr_count; + +extern void signal_usr1(int sig); + +int tracing_pid = -1; + +int signals(int (*init_proc)(void *), void *sp) +{ + void *task = NULL; + unsigned long eip = 0; + int status, pid = 0, sig, cont_type, tracing = 0, op = 0; + int last_index, proc_id; + + setup_kernel_stack(); + setup_signal_stack(); + calc_sigframe_size(); + signal(SIGPIPE, SIG_IGN); + tracing_pid = getpid(); + printk("tracing thread pid = %d\n", tracing_pid); + pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); + signal(SIGSEGV, signal_segv); + signal(SIGUSR1, signal_usr1); + set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); + set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); + set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); + if(debug){ + if(gdb_pid != -1) + debugger_pid = attach_debugger(pid, gdb_pid, 1); + else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop); + } + set_cmdline("(tracing thread)"); + if(debug_trace){ + printk("Tracing thread pausing to be attached\n"); + stop(); + } + while(1){ + if((pid = waitpid(-1, &status, WUNTRACED)) <= 0){ + if(errno != ECHILD){ + printk("wait failed - errno = %d\n", errno); + } + continue; + } + if(pid == debugger_pid){ + if(WIFEXITED(status) || WIFSIGNALED(status)) + debugger_pid = -1; + + /* XXX Figure out how to deal with gdb and SMP */ + debugger_signal(status, cpu_tasks[0].pid); + continue; + } + nsignals++; + if(WIFEXITED(status)) ; +#ifdef notdef + { + printk("Child %d exited with status %d\n", pid, + WEXITSTATUS(status)); + } +#endif + else if(WIFSIGNALED(status)){ + sig = WTERMSIG(status); + if(sig != 9){ + printk("Child %d exited with signal %d\n", pid, + sig); + } + } + else if(WIFSTOPPED(status)){ + sig = WSTOPSIG(status); + if(signal_index == 1024){ + signal_index = 0; + last_index = 1023; + } + else last_index = signal_index - 1; + if(((sig == SIGPROF) || (sig == SIGVTALRM) || + (sig == SIGALRM)) && + (signal_record[last_index].signal == sig) && + (signal_record[last_index].pid == pid)) + signal_index = last_index; + signal_record[signal_index].pid = pid; + gettimeofday(&signal_record[signal_index].time, NULL); + eip = ptrace(PTRACE_PEEKUSER, pid, UM_IP_OFFSET, 0); + signal_record[signal_index].addr = eip; + signal_record[signal_index++].signal = sig; + + proc_id = pid_to_processor_id(pid); + task = cpu_tasks[proc_id].task; + + tracing = is_tracing(task); + switch(sig){ + case SIGUSR1: + sig = 0; + op = do_proc_op(task, proc_id); + switch(op){ + case OP_EXEC: + continue; + case OP_TRACE_ON: + tracing = exit_kernel(pid, task); + break; + case OP_TRACE_OFF: + tracing = 0; + break; + case OP_REBOOT: + case OP_HALT: + kmalloc_ok = 0; + ptrace(PTRACE_KILL, pid, 0, 0); + return(op == OP_REBOOT); + case OP_NONE: + printk("Detaching pid %d\n", pid); + detach(pid, SIGSTOP); + continue; + default: + break; + } + break; + case SIGSTOP: + if(debugger_pid != -1) + child_signal(pid, status); + continue; + case SIGTRAP: + sig = 0; + if(switching_modes(task)) tracing = 0; + else { + if(!tracing && (debugger_pid != -1)) + child_signal(pid, status); + else if(!do_syscall(task, pid)){ + tracing = 0; + sig = SIGTRAP; + break; + } + continue; + } + break; + case SIGCONT: + break; + case SIGSEGV: + case SIGIO: + case SIGALRM: + case SIGVTALRM: + case SIGFPE: + case SIGBUS: + case SIGILL: + if(!tracing && (debugger_pid != -1)){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + case SIGPROF: + if(tracing) sig = 0; + break; + case SIGCHLD: + sig = 0; + break; + case SIGWINCH: + if(pid != external_pid(task)){ + ptrace(PTRACE_CONT, pid, 0, sig); + continue; + } + if(!tracing && (debugger_pid != -1)){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + case SIGHUP: + if(pid != external_pid(task)) continue; + sig = 0; + break; + default: + if(debugger_pid != -1){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + } + set_tracing(task, tracing); + if(tracing != 0){ + if(singlestepping(task)) + cont_type = PTRACE_SINGLESTEP; + else cont_type = PTRACE_SYSCALL; + } + else cont_type = PTRACE_CONT; + + if(ptrace(cont_type, pid, 0, sig) != 0){ + tracer_panic("ptrace failed to continue " + "process - errno = %d\n", + errno); + } + } + } + return(0); +} + +static int __init uml_debugtrace_setup(char *line, int *add) +{ + debug_trace = 1; + return 0; +} +__uml_setup("debugtrace=", uml_debugtrace_setup, +"debugtrace\n" +" Causes the tracing thread to pause until it is attached by a\n" +" debugger and continued. This is mostly for debugging crashes\n" +" early during boot, and should be pretty much obsoleted by\n" +" the debug switch.\n\n" +); + +static int __init uml_honeypot_setup(char *line, int *add) +{ + honeypot = 1; + return 0; +} +__uml_setup("honeypot", uml_honeypot_setup, +"honeypot\n" +" This makes UML put process stacks in the same location as they are\n" +" on the host, allowing expoits such as stack smashes to work against\n" +" UML.\n\n" +); + +int nsegfaults = 0; + +void segv_handler(int sig, void *sc, int usermode) +{ + struct sigcontext_struct *context = sc; + int index; + + if(usermode && !SEGV_IS_FIXABLE(context)){ + bad_segv(SC_FAULT_ADDR(context), SC_IP(context), + SC_FAULT_WRITE(context)); + return; + } + lock_trap(); + index = segfault_index++; + if(segfault_index == 1024) segfault_index = 0; + unlock_trap(); + nsegfaults++; + segfault_record[index].address = SC_FAULT_ADDR(context); + segfault_record[index].pid = getpid(); + segfault_record[index].is_write = SC_FAULT_WRITE(context); + segfault_record[index].sp = SC_SP(context); + segfault_record[index].is_user = usermode; + segv(SC_FAULT_ADDR(context), SC_IP(context), SC_FAULT_WRITE(context), + usermode); +} + +static void winch_handler(int sig, void *sc, int usermode) +{ + run_winch_handlers(); +} + +extern int timer_ready, timer_on; + +static void (*handlers[])(int, void *, int) = { + [ SIGTRAP ] relay_signal, + [ SIGFPE ] relay_signal, + [ SIGILL ] relay_signal, + [ SIGBUS ] relay_signal, + [ SIGSEGV] segv_handler, + [ SIGIO ] sigio_handler, + [ SIGVTALRM ] timer_handler, + [ SIGALRM ] timer_handler, + [ SIGWINCH ] winch_handler, +}; + +void irq_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno, save_timer = timer_on; + + unprotect_kernel_mem(); + timer_on = 0; + user_mode = user_context(SC_SP(&sc)); + if(user_mode){ + fill_in_regs(process_state(NULL), &sc); + timer_ready = 1; + } + change_sig(SIGUSR1, 1); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode) interrupt_end(); + block_signals(); + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 1, 0); + errno = save_errno; + if(user_mode) timer_ready = 0; + timer_on = save_timer; + if(user_mode) protect_kernel_mem(); +} + +void sig_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno, save_timer = timer_on; + + unprotect_kernel_mem(); + timer_on = 0; + user_mode = user_context(SC_SP(&sc)); + if(user_mode){ + fill_in_regs(process_state(NULL), &sc); + timer_ready = 1; + } + change_sig(SIGUSR1, 1); + unblock_signals(); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode) interrupt_end(); + block_signals(); + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 1, 0); + errno = save_errno; + if(user_mode) timer_ready = 0; + timer_on = save_timer; + if(user_mode) protect_kernel_mem(); +} + +extern int timer_irq_inited, missed_ticks; + +void alarm_handler(int sig, struct sigcontext sc) +{ + int user_mode; + + if(!timer_irq_inited) return; + missed_ticks++; + user_mode = user_context(SC_SP(&sc)); + if(!user_mode && !timer_ready) return; + if(!timer_on) return; + irq_handler(sig, sc); + timer_ready = 1; +} + +void do_longjmp(void *p) +{ + jmp_buf *jbuf = (jmp_buf *) p; + + longjmp(*jbuf, 1); +} + +static int __init uml_debug_setup(char *line, int *add) +{ + debug = 1; + if(!strcmp(line, "=go")){ + debug_stop = 0; + *add = 0; + } + return 0; +} + +__uml_setup("debug",uml_debug_setup, +"debug\n" +" Starts up the kernel under the control of gdb. See the \n" +" kernel debugging tutorial and the debugging session pages\n" +" at http://user-mode-linux.sourceforge.net/ for more information.\n\n" +); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/uaccess_user.c b/arch/um/kernel/uaccess_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/uaccess_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) + * Licensed under the GPL + */ + +#include +#include +#include "user_util.h" + +static unsigned long __do_user_copy(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher, + void (*op)(void *to, const void *from, + int n), int *faulted_out) +{ + unsigned long *faddrp = (unsigned long *) fault_addr, ret; + + jmp_buf jbuf; + *fault_catcher = &jbuf; + if(setjmp(jbuf) == 0){ + (*op)(to, from, n); + ret = 0; + *faulted_out = 0; + } + else { + ret = *faddrp; + *faulted_out = 1; + } + *fault_addr = NULL; + *fault_catcher = NULL; + return ret; +} + +static void __do_copy(void *to, const void *from, int n) +{ + memcpy(to, from, n); +} + +int __do_copy_from_user(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, + __do_copy, &faulted); + if(!faulted) return(0); + else return(n - (fault - (unsigned long) from)); +} + + +int __do_copy_to_user(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, + __do_copy, &faulted); + if(!faulted) return(0); + else return(n - (fault - (unsigned long) to)); +} + +static void __do_strncpy(void *dst, const void *src, int count) +{ + strncpy(dst, src, count); +} + +int __do_strncpy_from_user(char *dst, const char *src, unsigned long count, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher, + __do_strncpy, &faulted); + if(!faulted) return(strlen(dst)); + else return(-1); +} + +static void __do_clear(void *to, const void *from, int n) +{ + memset(to, 0, n); +} + +int __do_clear_user(void *mem, unsigned long len, + void **fault_addr, void **fault_catcher) +{ + unsigned long fault; + int faulted; + + fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher, + __do_clear, &faulted); + if(!faulted) return(0); + else return(len - (fault - (unsigned long) mem)); +} + +int __do_strnlen_user(const char *str, unsigned long n, + void **fault_addr, void **fault_catcher) +{ + int ret; + unsigned long *faddrp = (unsigned long *)fault_addr; + jmp_buf jbuf; + + *fault_catcher = &jbuf; + if(setjmp(jbuf) == 0){ + ret = strlen(str) + 1; + } + else { + ret = *faddrp - (unsigned long) str; + } + *fault_addr = NULL; + *fault_catcher = NULL; + return ret; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/um_arch.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/types.h" +#include "linux/tty.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/spinlock.h" +#include "linux/utsname.h" +#include +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/ptrace.h" +#include "asm/elf.h" +#include "asm/user.h" +#include "asm/delay.h" +#include "ubd_user.h" +#include "asm/current.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mprot.h" +#include "mem_user.h" +#include "umid.h" +#include "initrd.h" +#include "init.h" + +unsigned long _stext; + +#define DEFAULT_COMMAND_LINE "root=/dev/ubd0" + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) +{ + return(0); +} + +unsigned long thread_saved_pc(struct thread_struct *thread) +{ + panic("Someone should implement thread_saved_pc"); + return(0); +} + +/* + * get_cpuinfo - Get information on one CPU for use by procfs. + * + * Prints info on the next CPU into buffer. Beware, doesn't check for + * buffer overflow. Current implementation of procfs assumes that the + * resulting data is <= 1K. + * + * Args: + * buffer -- you guessed it, the data buffer + * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. + * + * Returns number of bytes written to buffer. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + int index; + + index = (struct cpuinfo_um *)v - cpu_data; +#ifdef CONFIG_SMP + if (!(cpu_online_map & (1 << index))) + return 0; +#endif + + seq_printf(m, "processor\t: user-mode\n"); + seq_printf(m, "bogomips\t: %lu.%02lu\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); + seq_printf(m, "host\t\t: %s\n", host_info); + + return(0); +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; + +pte_t * __bad_pagetable(void) +{ + panic("Someone should implement __bad_pagetable"); + return(NULL); +} + +extern void start_kernel(void); + +extern int debug; +extern int debug_stop; + +static int start_kernel_proc(void *unused) +{ + int pid; + + block_signals(); + pid = getpid(); + + cpu_tasks[0].pid = pid; + cpu_tasks[0].task = current; +#ifdef CONFIG_SMP + cpu_online_map = 1; +#endif + if(debug) stop_pid(pid); + start_kernel(); + return(0); +} + +extern unsigned long high_physmem; + +#ifdef CONFIG_HOST_2G_2G +#define START 0x60000000 +#else +#define START 0xa0000000 +#endif + +unsigned long host_task_size; +unsigned long task_size; + +void set_task_sizes(int arg) +{ + /* Round up to the nearest 4M */ + host_task_size = ROUND_4M((unsigned long) &arg); + task_size = START; +} + +unsigned long uml_physmem; + +unsigned long start_vm; +unsigned long end_vm; + +int ncpus = 1; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +static char *argv1_begin = NULL; +static char *argv1_end = NULL; + +static int have_root __initdata = 0; +long physmem_size = 32 * 1024 * 1024; + +void set_cmdline(char *cmd) +{ + if(honeypot) return; + strcpy(argv1_begin, "["); + strncat(argv1_begin, cmd, argv1_end - argv1_begin - strlen("[]")); + strcat(argv1_begin, "]"); + memset(argv1_begin + strlen(argv1_begin), '\0', + argv1_end - argv1_begin - strlen(argv1_begin)); +} + +static char *usage_string = +"User Mode Linux v%s\n" +" available at http://user-mode-linux.sourceforge.net/\n\n"; + +static int __init uml_version_setup(char *line, int *add) +{ + printf("%s\n", system_utsname.release); + exit(0); +} + +__uml_setup("--version", uml_version_setup, +"--version\n" +" Prints the version number of the kernel\n\n" +); + +static int __init uml_root_setup(char *line, int *add) +{ + have_root = 1; + return 0; +} +__uml_setup("root=", uml_root_setup, +"root=\n" +" This is actually used by the generic kernel in exactly the same\n" +" way as in any other kernel. If you configure a number of block\n" +" devices and want to boot off something other than ubd0, you \n" +" would use something like:\n" +" root=/dev/ubd5\n\n" +); + +#ifdef CONFIG_SMP +static int __init uml_ncpus_setup(char *line, int *add) +{ + if (!sscanf(line, "%d", &ncpus)) { + printk("Couldn't parse [%s]\n", line); + return -1; + } + + return 0; +} + +__uml_setup("ncpus=", uml_ncpus_setup, +"ncpus=<# of desired CPUs>\n" +" This tells an SMP kernel how many virtual processors to start.\n" +" Currently, this has no effect because SMP isn't enabled.\n\n" +); +#endif + +static int __init Usage(char *line, int *add) +{ + const char **p; + + printf(usage_string, system_utsname.release); + p = &__uml_help_start; + while (p < &__uml_help_end) { + printf("%s", *p); + p++; + } + exit(0); +} + +__uml_setup("--help", Usage, +"--help\n" +" Prints this message\n\n" +); + +static int __init uml_checksetup(char *line, int *add) +{ + struct uml_param *p; + + p = &__uml_setup_start; + while(p < &__uml_setup_end) { + int n; + + n = strlen(p->str); + if(!strncmp(line, p->str, n)){ + if (p->setup_func(line + n, add)) return 1; + } + p++; + } + return 0; +} + +static void __init uml_postsetup(void) +{ + initcall_t *p; + + p = &__uml_postsetup_start; + while(p < &__uml_postsetup_end){ + (*p)(); + p++; + } + return; +} + +extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; +extern int debug_trace; + +int linux_main(int argc, char **argv) +{ + unsigned long start_pfn, end_pfn, bootmap_size; + unsigned long virtmem_size; + unsigned int i, add; + void *sp; + void *brk_start; + + remap_data(ROUND_DOWN(&_stext), ROUND_UP(&_etext)); + remap_data(ROUND_DOWN(&_sdata), ROUND_UP(&_edata)); + brk_start = sbrk(0); + remap_data(ROUND_DOWN(&__bss_start), ROUND_UP(brk_start)); + + /* Start physical memory at least 4M after the current brk */ + uml_physmem = ROUND_4M(brk_start) + (1 << 22); + + for (i = 1; i < argc; i++){ + if((i == 1) && (argv[i][0] == ' ')) continue; + add = 1; + uml_checksetup(argv[i], &add); + if(add) add_arg(saved_command_line, argv[i]); + } + if(have_root == 0) add_arg(saved_command_line, DEFAULT_COMMAND_LINE); + + setup_machinename(system_utsname.machine); + + argv1_begin = argv[1]; + argv1_end = &argv[1][strlen(argv[1])]; + + /* Kernel vm starts after physical memory and is either the size + * of physical memory or the remaining space left in the kernel + * area of the address space, whichever is smaller. + */ + start_vm = uml_physmem + physmem_size + VMALLOC_OFFSET; + if(start_vm >= get_kmem_end()) + panic("Physical memory too large to allow any kernel " + "virtual memory"); + + virtmem_size = physmem_size; + if(physmem_size > get_kmem_end() - start_vm) + virtmem_size = get_kmem_end() - start_vm; + end_vm = start_vm + virtmem_size; + + if(virtmem_size < physmem_size) + printk(KERN_INFO "Kernel virtual memory size shrunk to %ld " + "bytes\n", virtmem_size); + + setup_range(-1, NULL, uml_physmem, physmem_size, + physmem_size + VMALLOC_OFFSET + virtmem_size); + setup_memory(); + high_physmem = uml_physmem + physmem_size; + + start_pfn = PFN_UP(__pa(uml_physmem)); + end_pfn = PFN_DOWN(__pa(high_physmem)); + bootmap_size = init_bootmem(start_pfn, end_pfn - start_pfn); + free_bootmem(__pa(uml_physmem) + bootmap_size, + high_physmem - uml_physmem - bootmap_size); + uml_postsetup(); + + init_task.thread.kernel_stack = (unsigned long) &init_task + + 2 * PAGE_SIZE; + + task_protections((unsigned long) &init_task); + sp = (void *) init_task.thread.kernel_stack + 2 * PAGE_SIZE - + sizeof(unsigned long); + return(signals(start_kernel_proc, sp)); +} + +void setup_arch(char **cmdline_p) +{ + paging_init(); + strcpy(command_line, saved_command_line); + *cmdline_p = command_line; + setup_hostinfo(); +} + +void check_bugs(void) +{ +} + +spinlock_t pid_lock = SPIN_LOCK_UNLOCKED; + +void lock_pid(void) +{ + spin_lock(&pid_lock); +} + +void unlock_pid(void) +{ + spin_unlock(&pid_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/umid.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "umid.h" +#include "init.h" + +#define UMID_LEN 64 +#define UML_DIR "/tmp/uml/" + +static char umid[UMID_LEN] = { 0 }; +static char *uml_dir = UML_DIR; + +static int umid_inited = 0; + +static int make_umid(void); + +static int __init set_umid(char *name, int *add) +{ + if(umid_inited){ + printk("Unique machine name can't be set twice\n"); + return(-1); + } + + if(strlen(name) > UMID_LEN - 1) + printk("Unique machine name is being truncated to %s " + "characters\n", UMID_LEN); + strncpy(umid, name, UMID_LEN - 1); + umid[UMID_LEN - 1] = '\0'; + + umid_inited = 1; + return 0; +} + +__uml_setup("umid=", set_umid, +"umid=\n" +" This is used to assign a unique identity to this UML machine\n" +" This is used for naming the pid file and management console socket\n\n" +); + +int __init umid_file_name(char *name, char *buf, int len) +{ + int n; + + if(!umid_inited && make_umid()) return(-1); + + n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1; + if(n > len){ + printk("umid_file_name : buffer too short\n"); + return(-1); + } + + sprintf(buf, "%s%s/%s", uml_dir, umid, name); + return(0); +} + +extern int tracing_pid; + +static int __init create_pid_file(void) +{ + char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; + char pid[sizeof("nnnnn\0")]; + int fd; + + if(umid_file_name("pid", file, sizeof(file))) return 0; + + if((fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0){ + printk("Open of machine pid file \"%s\" failed - " + "errno = %d\n", file, errno); + return 0; + } + + sprintf(pid, "%d\n", (tracing_pid == -1) ? getpid() : tracing_pid); + if(write(fd, pid, strlen(pid)) != strlen(pid)) + printk("Write of pid file failed - errno = %d\n", errno); + close(fd); + return 0; +} + +static int actually_do_remove(char *dir) +{ + DIR *directory; + struct dirent *ent; + int len; + char file[256]; + + if((directory = opendir(dir)) == NULL){ + printk("actually_do_remove : couldn't open directory '%s', " + "errno = %d\n", dir, errno); + return(1); + } + while((ent = readdir(directory)) != NULL){ + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; + if(len > sizeof(file)){ + printk("Not deleting '%s' from '%s' - name too long\n", + ent->d_name, dir); + continue; + } + sprintf(file, "%s/%s", dir, ent->d_name); + if(unlink(file) < 0){ + printk("actually_do_remove : couldn't remove '%s' " + "from '%s', errno = %d\n", ent->d_name, dir, + errno); + return(1); + } + } + if(rmdir(dir) < 0){ + printk("actually_do_remove : couldn't rmdir '%s', " + "errno = %d\n", dir, errno); + return(1); + } + return(0); +} + +void remove_umid_dir(void) +{ + char dir[strlen(uml_dir) + UMID_LEN + 1]; + if(!umid_inited) return; + + sprintf(dir, "%s%s", uml_dir, umid); + actually_do_remove(dir); +} + +char *get_umid(void) +{ + return(umid); +} + +int not_dead_yet(char *dir) +{ + char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; + char pid[sizeof("nnnnn\0")], *end; + int dead, fd, p; + + sprintf(file, "%s/pid", dir); + dead = 0; + if((fd = open(file, O_RDONLY)) < 0){ + if(errno != ENOENT){ + printk("not_dead_yet : couldn't open pid file '%s', " + "errno = %d\n", file, errno); + return(1); + } + dead = 1; + } + if(fd > 0){ + if(read(fd, pid, sizeof(pid)) < 0){ + printk("not_dead_yet : couldn't read pid file '%s', " + "errno = %d\n", file, errno); + return(1); + } + p = strtoul(pid, &end, 0); + if(end == pid){ + printk("not_dead_yet : couldn't parse pid file '%s', " + "errno = %d\n", file, errno); + dead = 1; + } + if(((kill(p, 0) < 0) && (errno == ESRCH)) || + (p == tracing_pid)) + dead = 1; + } + if(!dead) return(1); + return(actually_do_remove(dir)); + return(0); +} + +static int __init set_uml_dir(char *name, int *add) +{ + if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){ + uml_dir = malloc(strlen(name) + 1); + if(uml_dir == NULL){ + printk("Failed to malloc uml_dir - error = %d\n", + errno); + uml_dir = name; + return(0); + } + sprintf(uml_dir, "%s/", name); + } + else uml_dir = name; + return 0; +} + +static int __init make_uml_dir(void) +{ + if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){ + printk("Failed to mkdir %s - errno = %i\n", uml_dir, errno); + return(-1); + } + return 0; +} + +static int __init make_umid(void) +{ + int fd, err; + char tmp[strlen(uml_dir) + UMID_LEN + 1]; + + strcpy(tmp, uml_dir); + + if(*umid == 0){ + strcat(tmp, "XXXXXX"); + fd = mkstemp(tmp); + if(fd < 0){ + printk("set_umid - mkstemp failed, errno = %d\n", + errno); + return(1); + } + + close(fd); + /* There's a nice tiny little race between this unlink and + * the mkdir below. It'd be nice if there were a mkstemp + * for directories. + */ + unlink(tmp); + strcpy(umid, &tmp[strlen(uml_dir)]); + } + + sprintf(tmp, "%s%s", uml_dir, umid); + + if((err = mkdir(tmp, 0777)) < 0){ + if(errno == EEXIST){ + if(not_dead_yet(tmp)){ + printk("umid '%s' is in use\n", umid); + return(-1); + } + err = mkdir(tmp, 0777); + } + } + if(err < 0){ + printk("Failed to create %s - errno = %d\n", umid, errno); + return(-1); + } + + return(0); +} + +__uml_setup("uml_dir=", set_uml_dir, +"uml_dir=\n" +" The location to place the pid and umid files.\n\n" +); + +__uml_postsetup(make_uml_dir); +__uml_postsetup(make_umid); +__uml_postsetup(create_pid_file); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/unmap.c b/arch/um/kernel/unmap.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/unmap.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "user.h" + +int switcheroo(int fd, int prot, void *from, void *to, int size) +{ + if(munmap(to, size) < 0){ + return(-1); + } + if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){ + return(-1); + } + if(munmap(from, size) < 0){ + return(-1); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/kernel/user_syms.c b/arch/um/kernel/user_syms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/user_syms.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "mem_user.h" + +/* XXX All the __CONFIG_* stuff is broken because this file can't include + * config.h + */ + +/* Had to steal this from linux/module.h because that file can't be included + * since this includes various user-level headers. + */ + +struct module_symbol +{ + unsigned long value; + const char *name; +}; + +/* Indirect stringification. */ + +#define __MODULE_STRING_1(x) #x +#define __MODULE_STRING(x) __MODULE_STRING_1(x) + +#if !defined(__AUTOCONF_INCLUDED__) + +#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module +#define EXPORT_SYMBOL(var) error config_must_be_included_before_module +#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module + +#elif !defined(__CONFIG_MODULES__) + +#define __EXPORT_SYMBOL(sym,str) +#define EXPORT_SYMBOL(var) +#define EXPORT_SYMBOL_NOVERS(var) + +#else + +#define __EXPORT_SYMBOL(sym, str) \ +const char __kstrtab_##sym[] \ +__attribute__((section(".kstrtab"))) = str; \ +const struct module_symbol __ksymtab_##sym \ +__attribute__((section("__ksymtab"))) = \ +{ (unsigned long)&sym, __kstrtab_##sym } + +#if defined(__MODVERSIONS__) || !defined(__CONFIG_MODVERSIONS__) +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) +#else +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) +#endif + +#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) + +#endif + +EXPORT_SYMBOL(__errno_location); + +EXPORT_SYMBOL(access); +EXPORT_SYMBOL(open); +EXPORT_SYMBOL(open64); +EXPORT_SYMBOL(close); +EXPORT_SYMBOL(read); +EXPORT_SYMBOL(write); +EXPORT_SYMBOL(__xstat); +EXPORT_SYMBOL(__lxstat); +EXPORT_SYMBOL(__lxstat64); +EXPORT_SYMBOL(lseek); +EXPORT_SYMBOL(lseek64); +EXPORT_SYMBOL(chown); +EXPORT_SYMBOL(truncate); +EXPORT_SYMBOL(utime); +EXPORT_SYMBOL(chmod); +EXPORT_SYMBOL(rename); +EXPORT_SYMBOL(__xmknod); + +EXPORT_SYMBOL(symlink); +EXPORT_SYMBOL(link); +EXPORT_SYMBOL(unlink); +EXPORT_SYMBOL(readlink); + +EXPORT_SYMBOL(mkdir); +EXPORT_SYMBOL(rmdir); +EXPORT_SYMBOL(opendir); +EXPORT_SYMBOL(readdir); +EXPORT_SYMBOL(closedir); +EXPORT_SYMBOL(seekdir); +EXPORT_SYMBOL(telldir); + +EXPORT_SYMBOL(ioctl); + +extern ssize_t pread64 (int __fd, void *__buf, size_t __nbytes, + __off64_t __offset); +extern ssize_t pwrite64 (int __fd, __const void *__buf, size_t __n, + __off64_t __offset); +EXPORT_SYMBOL(pread64); +EXPORT_SYMBOL(pwrite64); + +EXPORT_SYMBOL(statfs); +EXPORT_SYMBOL(statfs64); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(getuid); + +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(find_iomem); diff -Nru a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/kernel/user_util.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asm/types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "mem_user.h" +#include "init.h" + +#define COMMAND_LINE_SIZE _POSIX_ARG_MAX + +char saved_command_line[COMMAND_LINE_SIZE] = { 0 }; +char command_line[COMMAND_LINE_SIZE] = { 0 }; + +void add_arg(char *cmd_line, char *arg) +{ + if (strlen(cmd_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { + printf("add_arg: Too much command line!\n"); + exit(1); + } + if(strlen(cmd_line) > 0) strcat(cmd_line, " "); + strcat(cmd_line, arg); +} + +void *open_maps(void) +{ + void *maps; + + maps = fopen("/proc/self/maps", "r"); + if(!maps){ + perror("open_maps"); + exit(1); + } + return(maps); +} + +int read_map(void *maps, unsigned long *start_out, unsigned long *end_out, + char *r_out, char *w_out, char *x_out, char *p_out) +{ + unsigned int inode; + unsigned long long offset; + unsigned long start, end, major, minor; + int ret; + char r, w, x, p; + + ret = fscanf(maps, "%lx-%lx %c%c%c%c %Lx %lx:%lx %d%*[^\n]", &start, + &end, &r, &w, &x, &p, &offset, &major, &minor, + &inode); + if (ret == EOF) return 0; + else if (ret != 10){ + perror("Scanning a map line"); + exit(1); + } + if(start_out != NULL) *start_out = start; + if(end_out != NULL) *end_out = end; + if(r_out != NULL) *r_out = r; + if(w_out != NULL) *w_out = w; + if(x_out != NULL) *x_out = x; + if(p_out != NULL) *p_out = p; + return(1); +} + +void close_maps(void *maps) +{ + fclose(maps); +} + +void remap_data(void *segment_start, void *segment_end) +{ + void *addr; + unsigned long start, end, size; + void *maps; + int data, prot; + char r, w, x; + + maps= open_maps(); + while(read_map(maps, &start, &end, &r, &w, &x, NULL) != 0){ + if(((unsigned long) segment_start >= start) && + ((unsigned long) segment_end <= end)){ + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + size = (unsigned long) segment_end - + (unsigned long) segment_start; + data = create_mem_file(size); + if((addr = mmap(NULL, size, PROT_WRITE | PROT_READ, + MAP_SHARED, data, 0)) < 0){ + perror("mapping new data segment"); + exit(1); + } + memcpy(addr, segment_start, size); + if(switcheroo(data, prot, addr, segment_start, + size) < 0){ + printf("switcheroo failed\n"); + exit(1); + } + close_maps(maps); + return; + } + } + fprintf(stderr, "remap_data couldn't find data segment 0x%lx - 0x%lx " + "in /proc/self/maps\n", (unsigned long) segment_start, + (unsigned long) segment_end); + exit(1); +} + +__u64 file_size(char *file) +{ + struct stat64 buf; + + if(stat64(file, &buf) == -1){ + printk("Couldn't stat \"%s\" : errno = %d\n", file, errno); + return(-errno); + } + if(S_ISBLK(buf.st_mode)){ + long long size; + int fd; + + if((fd = open64(file, O_RDONLY)) < 0){ + printk("Couldn't open \"%s\", errno = %d\n", file, + errno); + return(-errno); + } + if(ioctl(fd, BLKGETSIZE, &size) < 0){ + printk("Couldn't get the block size of \"%s\", " + "errno = %d\n", file, errno); + close(fd); + return(-errno); + } + size *= 512; + close(fd); + return(size); + } + return(buf.st_size); +} + +void stop(void) +{ + while(1) sleep(1000000); +} + +void stack_protections(unsigned long address) +{ + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + + if(mprotect((void *) address, page_size(), prot) < 0) + panic("protecting stack failed, errno = %d", errno); +} + +void task_protections(unsigned long address) +{ + unsigned long guard = address + page_size(); + unsigned long stack = guard + page_size(); + int prot = 0; + + if(mprotect((void *) stack, page_size(), prot) < 0) + panic("protecting guard page failed, errno = %d", errno); + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + if(mprotect((void *) stack, 2 * page_size(), prot) < 0) + panic("protecting stack failed, errno = %d", errno); +} + +void protect(unsigned long addr, unsigned long len, int r, int w, int x) +{ + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + if(mprotect((void *) addr, len, prot) == -1) + panic("protect failed, errno = %d", errno); +} + +int wait_for_stop(int pid, int sig, int cont_type) +{ + int status, ret; + + while(1){ + if(((ret = waitpid(pid, &status, WUNTRACED)) < 0) || + !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){ + if(ret < 0){ + if(errno == EINTR) continue; + printk("wait failed, errno = %d\n", + errno); + } + else if(WIFEXITED(status)) + printk("process exited with status %d\n", + WEXITSTATUS(status)); + else if(WIFSIGNALED(status)) + printk("process exited with signal %d\n", + WTERMSIG(status)); + else if((WSTOPSIG(status) == SIGVTALRM) || + (WSTOPSIG(status) == SIGALRM) || + (WSTOPSIG(status) == SIGIO) || + (WSTOPSIG(status) == SIGPROF) || + (WSTOPSIG(status) == SIGCHLD) || + (WSTOPSIG(status) == SIGWINCH) || + (WSTOPSIG(status) == SIGINT)){ + ptrace(cont_type, pid, 0, WSTOPSIG(status)); + continue; + } + else printk("process stopped with signal %d\n", + WSTOPSIG(status)); + panic("wait_for_stop failed to wait for %d to stop " + "with %d\n", pid, sig); + } + return(status); + } +} + +int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags) +{ + int pid; + + pid = clone(fn, sp, flags, arg); + if(pid < 0) return(-1); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + ptrace(PTRACE_CONT, pid, 0, 0); + return(pid); +} + +struct grantpt_info { + int fd; + int res; + int err; +}; + +static void grantpt_cb(void *arg) +{ + struct grantpt_info *info = arg; + + info->res = grantpt(info->fd); + info->err = errno; +} + +int get_pty(void) +{ + struct grantpt_info info; + int fd; + + if((fd = open("/dev/ptmx", O_RDWR)) < 0){ + printk("get_pty : Couldn't open /dev/ptmx - errno = %d\n", + errno); + return(-1); + } + info.fd = fd; + tracing_cb(grantpt_cb, &info); + if(info.res < 0){ + printk("get_pty : Couldn't grant pty - errno = %d\n", + info.err); + return(-1); + } + if(unlockpt(fd) < 0){ + printk("get_pty : Couldn't unlock pty - errno = %d\n", errno); + return(-1); + } + return(fd); +} + +int raw(int fd, int complain) +{ + struct termios tt; + int err; + + tcgetattr(fd, &tt); + cfmakeraw(&tt); + err = tcsetattr(fd, TCSANOW, &tt); + if((err < 0) && complain){ + printk("tcsetattr failed, errno = %d\n", errno); + return(-errno); + } + return(0); +} + +void setup_machinename(char *machine_out) +{ + struct utsname host; + + uname(&host); + strcpy(machine_out, host.machine); +} + +char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1]; + +void setup_hostinfo(void) +{ + struct utsname host; + + uname(&host); + sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename, + host.release, host.version, host.machine); +} + +void close_fd(int fd) +{ + close(fd); +} + +char *tempdir = NULL; + +static void __init find_tempdir(void) +{ + char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; + int i; + char *dir = NULL; + + if(tempdir != NULL) return; /* We've already been called */ + for(i = 0; dirs[i]; i++){ + dir = getenv(dirs[i]); + if(dir != NULL) break; + } + if(dir == NULL) dir = "/tmp"; + else if(*dir == '\0') dir = NULL; + if(dir != NULL) { + tempdir = malloc(strlen(dir) + 2); + if(tempdir == NULL){ + fprintf(stderr, "Failed to malloc tempdir, " + "errno = %d\n", errno); + return; + } + strcpy(tempdir, dir); + strcat(tempdir, "/"); + } +} + +int make_tempfile(const char *template, char **out_tempname, int do_unlink) +{ + char tempname[MAXPATHLEN]; + int fd; + + find_tempdir(); + if (*template != '/') + strcpy(tempname, tempdir); + else + *tempname = 0; + strcat(tempname, template); + if((fd = mkstemp(tempname)) < 0){ + fprintf(stderr, "open - cannot create %s: %s\n", tempname, + strerror(errno)); + return -1; + } + if(do_unlink && (unlink(tempname) < 0)){ + perror("unlink"); + return -1; + } + if(out_tempname){ + if((*out_tempname = strdup(tempname)) == NULL){ + perror("strdup"); + return -1; + } + } + return(fd); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/link.ld.in b/arch/um/link.ld.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/link.ld.in Wed Feb 13 20:03:56 2002 @@ -0,0 +1,129 @@ +OUTPUT_FORMAT("elf32-ELF_SUBARCH") +OUTPUT_ARCH(ELF_SUBARCH) +ENTRY(_start) + +SECTIONS +{ + . = START() + SIZEOF_HEADERS; + + . = ALIGN(4096); + _bar = .; + .thread_private : { + __start_thread_private = .; + errno = .; + . += 4; + arch/um/kernel/unmap_fin.o (.data) + __end_thread_private = .; + } + _foo = .; + . = ALIGN(4096); + .remap : { arch/um/kernel/unmap_fin.o (.text) } + + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .text.init : { *(.text.init) } + . = ALIGN(4096); + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + _etext = .; + PROVIDE (etext = .); + + . = ALIGN(4096); + PROVIDE (_sdata = .); + __uml_setup_start = .; + .uml.setup.init : { *(.uml.setup.init) } + __uml_setup_end = .; + __uml_help_start = .; + .uml.help.init : { *(.uml.help.init) } + __uml_help_end = .; + __uml_postsetup_start = .; + .uml.postsetup.init : { *(.uml.postsetup.init) } + __uml_postsetup_end = .; + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + __uml_initcall_start = .; + .uml.initcall.init : { *(.uml.initcall.init) } + __uml_initcall_end = .; + __init_end = .; + __exitcall_begin = .; + .exitcall : { *(.exitcall.exit) } + __exitcall_end = .; + __uml_exitcall_begin = .; + .uml.exitcall : { *(.uml.exitcall.exit) } + __uml_exitcall_end = .; + + .data.init : { *(.data.init) } + .data : + { + . = ALIGN(16384); /* init_task */ + *(.data.init_task) + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(0x1000); + .sbss : + { + __bss_start = .; + PROVIDE(_bss_start = .); + *(.sbss) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff -Nru a/arch/um/main.c b/arch/um/main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/main.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "user.h" +#include "init.h" + +unsigned long stacksizelim; + +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +char padding[THREAD_NAME_LEN] = { [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' }; + +static void set_stklim(void) +{ + struct rlimit lim; + + if(getrlimit(RLIMIT_STACK, &lim) < 0){ + perror("getrlimit"); + exit(1); + } + if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ + lim.rlim_cur = STACKSIZE; + if(setrlimit(RLIMIT_STACK, &lim) < 0){ + perror("setrlimit"); + exit(1); + } + } + stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); +} + +static __init void do_uml_initcalls(void) +{ + initcall_t *call; + + call = &__uml_initcall_start; + while (call < &__uml_initcall_end){; + (*call)(); + call++; + } +} +int main(int argc, char **argv, char **envp) +{ + int ret, i; + char **new_argv; + + /* Allocate memory for thread command lines */ + if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + new_argv = malloc((argc + 2) * sizeof(char*)); + if(!new_argv) { + perror("Allocating extended argv"); + exit(1); + } + + new_argv[0] = argv[0]; + new_argv[1] = padding; + + for(i = 2; i <= argc; i++) + new_argv[i] = argv[i - 1]; + new_argv[argc + 1] = NULL; + +#ifdef PROFILING + disable_profile_timer(); +#endif + execvp(new_argv[0], new_argv); + perror("execing with extended args"); + exit(1); + } + + linux_prog = argv[0]; + + set_stklim(); + set_task_sizes(0); + + if((new_argv = malloc((argc + 1) * sizeof(char *))) == NULL){ + perror("Mallocing argv"); + exit(1); + } + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +#include "user_util.h" +#include "user.h" + +/* + * Handle debugger trap, i.e. syscall. + */ + +void debugger_syscall (debugger_state *debugger, pid_t child) +{ + long arg1, arg2, arg3, arg4, arg5, result; + int syscall; + + syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, + &arg5); + + switch(syscall){ + case __NR_execve: + /* execve never returns */ + debugger->handle_trace = debugger_syscall; + break; + + case __NR_ptrace: + if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; + result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child); + syscall_cancel(debugger->pid, result); + debugger->handle_trace = debugger_syscall; + return; + + case __NR_waitpid: + case __NR_wait4: + debugger->wait_status_ptr = (int *) arg2; + debugger->wait_options = arg3; + if(debugger->debugee->event){ + syscall_continue(debugger->pid); + wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL); + proxy_wait_return(debugger, -1); + return; + } + else if(debugger->wait_options & WNOHANG){ + syscall_cancel(debugger->pid, 0); + debugger->handle_trace = debugger_syscall; + return; + } + else { + syscall_pause(debugger->pid); + debugger->handle_trace = proxy_wait_return; + debugger->waiting = 1; + } + break; + + case __NR_kill: + if(arg1 == debugger->debugee->pid){ + result = kill(child, arg2); + syscall_cancel(debugger->pid, result); + debugger->handle_trace = debugger_syscall; + return; + } + else debugger->handle_trace = debugger_normal_return; + break; + + default: + debugger->handle_trace = debugger_normal_return; + } + + syscall_continue (debugger->pid); +} + +void debugger_normal_return(debugger_state *debugger, pid_t unused) +{ + debugger->handle_trace = debugger_syscall; + syscall_continue(debugger->pid); +} + +void debugger_cancelled_return(debugger_state *debugger, int result) +{ + debugger->handle_trace = debugger_syscall; + syscall_set_result(debugger->pid, result); + syscall_continue(debugger->pid); +} + +#ifdef CONFIG_SMP +#error need to make these arrays +#endif + +static debugger_state debugger; +static debugee_state debugee; + +void init_proxy (pid_t debugger_pid, int stopped, int status) +{ + debugger.pid = debugger_pid; + debugger.handle_trace = debugger_syscall; + debugger.debugee = &debugee; + debugger.waiting = 0; + + debugee.pid = 0; + debugee.traced = 0; + debugee.stopped = stopped; + debugee.event = 0; + debugee.zombie = 0; + debugee.died = 0; + debugee.wait_status = status; +} + +void debugger_proxy(int status, int pid) +{ + if(WIFSTOPPED(status)){ + if (WSTOPSIG (status) == SIGTRAP) + (*debugger.handle_trace)(&debugger, pid); + else ptrace(PTRACE_SYSCALL, debugger.pid, 0, WSTOPSIG(status)); + } + else if(WIFEXITED(status)){ + panic("debugger (pid %d) exited with status %d", + debugger.pid, WEXITSTATUS(status)); + } + else if(WIFSIGNALED(status)){ + panic("debugger (pid %d) exited with signal %d", + debugger.pid, WTERMSIG(status)); + } + else { + panic("proxy got unknown status (0x%x) on debugger " + "(pid %d)", status, debugger.pid); + } +} + +void child_proxy(pid_t pid, int status) +{ + debugee.event = 1; + debugee.wait_status = status; + + if(WIFSTOPPED(status)){ + debugee.stopped = 1; + kill(debugger.pid, SIGCHLD); + } + else if(WIFEXITED(status) || WIFSIGNALED(status)){ + debugee.zombie = 1; + kill(debugger.pid, SIGCHLD); + } + else panic("proxy got unknown status (0x%x) on child (pid %d)", + status, pid); +} + +void fake_child_exit(void) +{ + int status, pid; + + child_proxy(1, W_EXITCODE(0, 0)); + while(debugger.waiting == 1){ + pid = waitpid(debugger.pid, &status, WUNTRACED); + if(pid != debugger.pid){ + printk("fake_child_exit - waitpid failed, " + "errno = %d\n", errno); + return; + } + debugger_proxy(status, debugger.pid); + } + pid = waitpid(debugger.pid, &status, WUNTRACED); + if(pid != debugger.pid){ + printk("fake_child_exit - waitpid failed, " + "errno = %d\n", errno); + return; + } + if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0) + printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n", + errno); +} + +char gdb_init_string[] = +"att 1 +b panic +b stop +handle SIGWINCH nostop noprint pass +handle SIGSEGV nostop noprint pass +"; + +int start_debugger(char *prog, int startup, int stop, int *fd_out) +{ + int slave, child; + + slave = open_gdb_chan(); + if((child = fork()) == 0){ + char *tempname = NULL; + int fd; + + if(setsid() < 0) perror("setsid"); + if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || + (dup2(slave, 2) < 0)){ + printk("start_debugger : dup2 failed, errno = %d\n", + errno); + exit(1); + } + if(ioctl(0, TIOCSCTTY, 0) < 0){ + printk("start_debugger : TIOCSCTTY failed, " + "errno = %d\n", errno); + exit(1); + } + if(tcsetpgrp (1, getpid()) < 0){ + printk("start_debugger : tcsetpgrp failed, " + "errno = %d\n", errno); +#ifdef notdef + exit(1); +#endif + } + if((fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0)) < 0){ + printk("start_debugger : make_tempfile failed, errno = %d\n", + errno); + exit(1); + } + write(fd, gdb_init_string, sizeof(gdb_init_string) - 1); + if(startup){ + if(stop){ + write(fd, "b start_kernel\n", + strlen("b start_kernel\n")); + } + write(fd, "c\n", strlen("c\n")); + } + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ + printk("start_debugger : PTRACE_TRACEME failed, " + "errno = %d\n", errno); + exit(1); + } + execlp("gdb", "gdb", "--command", tempname, prog, NULL); + printk("start_debugger : exec of gdb failed, errno = %d\n", + errno); + } + if(child < 0){ + printk("start_debugger : fork for gdb failed, errno = %d\n", + errno); + return(-1); + } + *fd_out = slave; + return(child); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/ptproxy.h b/arch/um/ptproxy/ptproxy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/ptproxy.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,57 @@ +/********************************************************************** +ptproxy.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#ifndef __PTPROXY_H +#define __PTPROXY_H + +#include + +typedef struct debugger debugger_state; +typedef struct debugee debugee_state; + +struct debugger +{ + pid_t pid; + int wait_options; + int *wait_status_ptr; + unsigned int waiting : 1; + void (*handle_trace) (debugger_state *, pid_t); + + debugee_state *debugee; +}; + +struct debugee +{ + pid_t pid; + int wait_status; + unsigned died : 1; + unsigned event : 1; + unsigned stopped : 1; + unsigned trace_singlestep : 1; + unsigned trace_syscall : 1; + unsigned traced : 1; + unsigned zombie : 1; +}; + +extern void debugger_syscall(debugger_state *debugger, pid_t pid); +extern void debugger_normal_return (debugger_state *debugger, pid_t unused); + +extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t); +extern void debugger_cancelled_return(debugger_state *debugger, int result); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/ptrace.c b/arch/um/ptproxy/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/ptrace.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,176 @@ +/********************************************************************** +ptrace.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +Jeff Dike (jdike@karaya.com) : Modified for integration into uml +**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" +#include "debug.h" +#include "user_util.h" + +long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2, + long arg3, long arg4, pid_t child) +{ + long result; + int status; + + if(debugger->debugee->died) return(-ESRCH); + + switch(arg1){ + case PTRACE_ATTACH: + if(debugger->debugee->traced) return(-EPERM); + + debugger->debugee->pid = arg2; + debugger->debugee->traced = 1; + if(debugger->debugee->stopped) + child_proxy(child, W_STOPCODE(SIGSTOP)); + else kill(child, SIGSTOP); + return(0); + + case PTRACE_CONT: + return(ptrace(PTRACE_CONT, child, arg3, arg4)); + + case PTRACE_DETACH: + if(!debugger->debugee->traced) return(-EPERM); + + debugger->debugee->traced = 0; + kill(child, SIGCONT); + return(0); + +#ifdef UM_HAVE_GETFPREGS + case PTRACE_GETFPREGS: + { + long regs[UM_MAX_FP_REG]; + int i, result; + + result = ptrace(PTRACE_GETFPREGS, child, 0, regs); + if(result == -1) return(-errno); + + for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) + ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, + regs[i]); + return(result); + } +#endif + +#ifdef UM_HAVE_GETREGS + case PTRACE_GETREGS: + { + long regs[UM_MAX_REG]; + int i, result; + + result = ptrace(PTRACE_GETREGS, child, 0, regs); + if(result == -1) return(-errno); + + for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) + ptrace (PTRACE_POKEDATA, debugger->pid, + arg4 + 4 * i, regs[i]); + return(result); + } + break; +#endif + + case PTRACE_KILL: + result = ptrace(PTRACE_KILL, child, arg3, arg4); + if(result == -1) return(-errno); + + return(result); + + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + case PTRACE_PEEKUSER: + /* The value being read out could be -1, so we have to + * check errno to see if there's an error, and zero it + * beforehand so we're not faked out by an old error + */ + + errno = 0; + result = ptrace(arg1, child, arg3, 0); + if((result == -1) && (errno != 0)) return(-errno); + + result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result); + if(result == -1) return(-errno); + + return(result); + + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSER: + result = ptrace(arg1, child, arg3, arg4); + if(result == -1) return(-errno); + + return(result); + +#ifdef UM_HAVE_SETFPREGS + case PTRACE_SETFPREGS: + { + long regs[UM_MAX_FP_REG]; + int i; + + for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) + regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + result = ptrace(PTRACE_SETFPREGS, child, 0, regs); + if(result == -1) return(-errno); + + return(result); + } +#endif + +#ifdef UM_HAVE_SETREGS + case PTRACE_SETREGS: + { + long regs[UM_MAX_REG]; + int i; + + for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) + regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + result = ptrace(PTRACE_SETREGS, child, 0, regs); + if(result == -1) return(-errno); + + return(result); + } +#endif + + case PTRACE_SINGLESTEP: + result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4); + if(result == -1) return(-errno); + + status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP); + child_proxy(child, status); + return(result); + + case PTRACE_SYSCALL: + result = ptrace(PTRACE_SYSCALL, child, arg3, arg4); + if(result == -1) return(-errno); + + return(result); + + case PTRACE_TRACEME: + default: + return(-EINVAL); + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/sysdep.c b/arch/um/ptproxy/sysdep.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/sysdep.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,71 @@ +/********************************************************************** +sysdep.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sysdep/ptrace.h" +#include "user_util.h" +#include "user.h" + +int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4, + long *arg5) +{ + *arg1 = ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG1_OFFSET, 0); + *arg2 = ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG2_OFFSET, 0); + *arg3 = ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG3_OFFSET, 0); + *arg4 = ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG4_OFFSET, 0); + *arg5 = ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG5_OFFSET, 0); + return(ptrace(PTRACE_PEEKUSER, pid, UM_SYSCALL_NR_OFFSET, 0)); +} + +void syscall_cancel(pid_t pid, int result) +{ + if((ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, + __NR_getpid) < 0) || + (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) || + (wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL) < 0) || + (ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_RET_OFFSET, result) < 0) || + (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)) + printk("ptproxy: couldn't cancel syscall: errno = %d\n", + errno); +} + +void syscall_set_result(pid_t pid, long result) +{ + ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_RET_OFFSET, result); +} + +void syscall_continue(pid_t pid) +{ + ptrace(PTRACE_SYSCALL, pid, 0, 0); +} + +int syscall_pause(pid_t pid) +{ + if(ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, __NR_pause) < 0){ + printk("syscall_change - ptrace failed, errno = %d\n", errno); + return(-1); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/sysdep.h b/arch/um/ptproxy/sysdep.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/sysdep.h Wed Feb 13 20:04:00 2002 @@ -0,0 +1,25 @@ +/********************************************************************** +sysdep.h + +Copyright (C) 1999 Lars Brinkhoff. +Copyright (C) 2001 Jeff Dike (jdike@karaya.com) +See the file COPYING for licensing terms and conditions. +**********************************************************************/ + +extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, + long *arg4, long *arg5); +extern void syscall_cancel (pid_t pid, long result); +extern void syscall_set_result (pid_t pid, long result); +extern void syscall_continue (pid_t pid); +extern int syscall_pause(pid_t pid); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/wait.c b/arch/um/ptproxy/wait.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/wait.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,56 @@ +/********************************************************************** +wait.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +**********************************************************************/ + +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +#include +#include "sysdep/ptrace.h" + +void proxy_wait_return (struct debugger *debugger, pid_t unused) +{ + debugger->waiting = 0; + + if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){ + debugger_cancelled_return(debugger, -ECHILD); + return; + } + + if(debugger->debugee->zombie && debugger->debugee->event) + debugger->debugee->died = 1; + + if(debugger->debugee->event){ + debugger->debugee->event = 0; + ptrace(PTRACE_POKEDATA, debugger->pid, + debugger->wait_status_ptr, + debugger->debugee->wait_status); + /* if (wait4) + ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ + debugger_cancelled_return(debugger, debugger->debugee->pid); + return; + } + + /* pause will return -EINTR, which happens to be right for wait */ + debugger_normal_return(debugger, -1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/ptproxy/wait.h b/arch/um/ptproxy/wait.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/ptproxy/wait.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,8 @@ +/********************************************************************** +wait.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +extern void proxy_wait_return (struct debugger *debugger, pid_t unused); diff -Nru a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/Makefile Wed Feb 13 20:04:02 2002 @@ -0,0 +1,49 @@ +OBJ = sys.o + +OBJS = checksum.o ldt.o old-checksum.o ptrace.o ptrace_user.o semaphore.o \ + sigcontext.o syscalls.o sysrq.o +export-objs = ksyms.o + +SYMLINKS = semaphore.c old-checksum.c checksum.S + +all: $(OBJ) + +$(OBJ): $(OBJS) $(export-objs) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +sigcontext.o: sigcontext.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ldt.o: ldt.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ptrace_user.o: ptrace_user.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +checksum.S old-checksum.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/lib/$@ $@ + +semaphore.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/kernel/$@ $@ + +clean: + rm -f $(OBJS) $(export-objs) + +fastdep: + +archmrproper: + rm -f $(SYMLINKS) + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/sys-i386/ksyms.c b/arch/um/sys-i386/ksyms.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/ksyms.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,16 @@ +#include "linux/module.h" +#include "linux/in6.h" +#include "linux/rwsem.h" +#include "asm/byteorder.h" +#include "asm/semaphore.h" +#include "asm/uaccess.h" +#include "asm/checksum.h" +#include "asm/errno.h" + +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__down_failed_trylock); +EXPORT_SYMBOL(__up_wakeup); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy_generic); diff -Nru a/arch/um/sys-i386/ldt.c b/arch/um/sys-i386/ldt.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/ldt.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +extern int modify_ldt(int func, void *ptr, unsigned long bytecount); + +int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + return modify_ldt(func, ptr, bytecount); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/ptrace.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "asm/ptrace.h" + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x00044dd5 + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + switch (regno >> 2) { + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[FS] = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[GS] = value; + return 0; + case DS: + case ES: + if (value && (value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case SS: + case CS: + if ((value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case EFL: + value &= FLAG_MASK; + value |= child->thread.process_regs.regs[EFL]; + break; + } + child->thread.process_regs.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + switch (regno >> 2) { + case FS: + case GS: + case DS: + case ES: + case SS: + case CS: + retval = 0xffff; + /* fall through */ + default: + retval &= child->thread.process_regs.regs[regno >> 2]; + } + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/ptrace_user.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,25 @@ +#include +#include +#include +#include "sysdep/ptrace.h" + +int ptrace_getregs(long pid, struct sys_pt_regs *regs_out) +{ + return(ptrace(PTRACE_GETREGS, pid, 0, regs_out)); +} + +int ptrace_setregs(long pid, struct sys_pt_regs *regs) +{ + return(ptrace(PTRACE_SETREGS, pid, 0, regs)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-i386/sigcontext.c b/arch/um/sys-i386/sigcontext.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/sigcontext.c Wed Feb 13 20:03:59 2002 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include "sysdep/ptrace.h" + +void fill_in_sigcontext(void *sc_ptr, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ + struct sigcontext *sc; + + sc = sc_ptr; + sc->ebx = regs->regs[EBX]; + sc->ecx = regs->regs[ECX]; + sc->edx = regs->regs[EDX]; + sc->esi = regs->regs[ESI]; + sc->edi = regs->regs[EDI]; + sc->ebp = regs->regs[EBP]; + sc->eax = regs->regs[EAX]; + sc->ds = regs->regs[DS]; + sc->es = regs->regs[ES]; + sc->fs = regs->regs[FS]; + sc->gs = regs->regs[GS]; + sc->eip = regs->regs[EIP]; + sc->cs = regs->regs[CS]; + sc->eflags = regs->regs[EFL]; + sc->esp_at_signal = regs->regs[UESP]; + sc->ss = regs->regs[SS]; + sc->err = err; + sc->cr2 = cr2; +} + +void fill_in_regs(struct sys_pt_regs *regs, void *sc_ptr) +{ + struct sigcontext *sc; + + sc = sc_ptr; + regs->regs[EBX] = sc->ebx; + regs->regs[ECX] = sc->ecx; + regs->regs[EDX] = sc->edx; + regs->regs[ESI] = sc->esi; + regs->regs[EDI] = sc->edi; + regs->regs[EBP] = sc->ebp; + regs->regs[EAX] = sc->eax; + regs->regs[DS] = sc->ds; + regs->regs[ES] = sc->es; + regs->regs[FS] = sc->fs; + regs->regs[GS] = sc->gs; + regs->regs[EIP] = sc->eip; + regs->regs[CS] = sc->cs; + regs->regs[EFL] = sc->eflags; + regs->regs[UESP] = sc->esp_at_signal; + regs->regs[SS] = sc->ss; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-i386/syscalls.c b/arch/um/sys-i386/syscalls.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/syscalls.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/mman.h" +#include "asm/uaccess.h" +#include "asm/unistd.h" + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +extern int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset); + +int old_mmap_i386(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int err = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + err = old_mmap(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); + out: + return err; +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-i386/sysrq.c b/arch/um/sys-i386/sysrq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-i386/sysrq.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,23 @@ +#include "linux/kernel.h" +#include "linux/smp.h" +#include "linux/sched.h" +#include "asm/ptrace.h" +#include "sysrq.h" + +void show_regs(struct pt_regs_subarch *regs) +{ + printk("\n"); + printk("EIP: %04x:[<%08lx>] CPU: %d %s",0xffff & regs->xcs, regs->eip, + smp_processor_id(), print_tainted()); + if (regs->xcs & 3) + printk(" ESP: %04x:%08lx",0xffff & regs->xss, regs->esp); + printk(" EFLAGS: %08lx\n %s\n", regs->eflags, print_tainted()); + printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", + regs->eax, regs->ebx, regs->ecx, regs->edx); + printk("ESI: %08lx EDI: %08lx EBP: %08lx", + regs->esi, regs->edi, regs->ebp); + printk(" DS: %04x ES: %04x\n", + 0xffff & regs->xds, 0xffff & regs->xes); + + show_trace(®s->esp); +} diff -Nru a/arch/um/sys-ia64/Makefile b/arch/um/sys-ia64/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ia64/Makefile Wed Feb 13 20:03:56 2002 @@ -0,0 +1,26 @@ +OBJ = sys.o + +OBJS = + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ +clean: + rm -f $(OBJS) + +fastdep: + +archmrproper: + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/sys-ppc/Makefile b/arch/um/sys-ppc/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/Makefile Wed Feb 13 20:03:57 2002 @@ -0,0 +1,78 @@ +OBJ = sys.o + +.S.o: + $(CC) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + +OBJS = ptrace.o sigcontext.o semaphore.o checksum.o miscthings.o misc.o \ + ptrace_user.o sysrq.o + +EXTRA_AFLAGS := -DCONFIG_ALL_PPC -I. -I$(TOPDIR)/arch/ppc/kernel + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +ptrace_user.o: ptrace_user.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +sigcontext.o: sigcontext.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +semaphore.c: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +checksum.S: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/lib/$@ $@ + +mk_defs.c: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +ppc_defs.head: + rm -f $@ + ln -s $(TOPDIR)/arch/ppc/kernel/$@ $@ + +ppc_defs.h: mk_defs.c ppc_defs.head \ + $(TOPDIR)/include/asm-ppc/mmu.h \ + $(TOPDIR)/include/asm-ppc/processor.h \ + $(TOPDIR)/include/asm-ppc/pgtable.h \ + $(TOPDIR)/include/asm-ppc/ptrace.h +# $(CC) $(CFLAGS) -S mk_defs.c + cp ppc_defs.head ppc_defs.h +# for bk, this way we can write to the file even if it's not checked out + echo '#define THREAD 608' >> ppc_defs.h + echo '#define PT_REGS 8' >> ppc_defs.h + echo '#define CLONE_VM 256' >> ppc_defs.h +# chmod u+w ppc_defs.h +# grep '^#define' mk_defs.s >> ppc_defs.h +# rm mk_defs.s + +# the asm link is horrible, and breaks the other targets. This is also +# not going to work with parallel makes. + +checksum.o: checksum.S + rm -f asm + ln -s $(TOPDIR)/include/asm-ppc asm + $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + rm -f asm + +misc.o: misc.S ppc_defs.h + rm -f asm + ln -s $(TOPDIR)/include/asm-ppc asm + $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o + rm -f asm + +clean: + rm -f $(OBJS) + rm -f ppc_defs.h + rm -f checksum.S semaphore.c mk_defs.c + +fastdep: + +modules: + +include $(TOPDIR)/Rules.make diff -Nru a/arch/um/sys-ppc/misc.S b/arch/um/sys-ppc/misc.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/misc.S Wed Feb 13 20:04:00 2002 @@ -0,0 +1,116 @@ +/* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * A couple of functions stolen from arch/ppc/kernel/misc.S for UML + * by Chris Emerson. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include "ppc_asm.h" + +#if defined(CONFIG_4xx) || defined(CONFIG_8xx) +#define CACHE_LINE_SIZE 16 +#define LG_CACHE_LINE_SIZE 4 +#define MAX_COPY_PREFETCH 1 +#elif !defined(CONFIG_PPC64BRIDGE) +#define CACHE_LINE_SIZE 32 +#define LG_CACHE_LINE_SIZE 5 +#define MAX_COPY_PREFETCH 4 +#else +#define CACHE_LINE_SIZE 128 +#define LG_CACHE_LINE_SIZE 7 +#define MAX_COPY_PREFETCH 1 +#endif /* CONFIG_4xx || CONFIG_8xx */ + + .text + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +#ifdef CONFIG_8xx + li r4, 0 +1: stw r4, 0(r3) + stw r4, 4(r3) + stw r4, 8(r3) + stw r4, 12(r3) +#else +1: dcbz 0,r3 +#endif + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_16_BYTES \ + lwz r6,4(r4); \ + lwz r7,8(r4); \ + lwz r8,12(r4); \ + lwzu r9,16(r4); \ + stw r6,4(r3); \ + stw r7,8(r3); \ + stw r8,12(r3); \ + stwu r9,16(r3) + +_GLOBAL(copy_page) + addi r3,r3,-4 + addi r4,r4,-4 + li r5,4 + +#ifndef CONFIG_8xx +#if MAX_COPY_PREFETCH > 1 + li r0,MAX_COPY_PREFETCH + li r11,4 + mtctr r0 +11: dcbt r11,r4 + addi r11,r11,CACHE_LINE_SIZE + bdnz 11b +#else /* MAX_COPY_PREFETCH == 1 */ + dcbt r5,r4 + li r11,CACHE_LINE_SIZE+4 +#endif /* MAX_COPY_PREFETCH */ +#endif /* CONFIG_8xx */ + + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: +#ifndef CONFIG_8xx + dcbt r11,r4 + dcbz r5,r3 +#endif + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 1b + blr diff -Nru a/arch/um/sys-ppc/miscthings.c b/arch/um/sys-ppc/miscthings.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/miscthings.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,56 @@ +#include "linux/threads.h" +#include "linux/stddef.h" // for NULL +#include "linux/elf.h" // for AT_NULL + +/* unsigned int local_bh_count[NR_CPUS]; */ +unsigned long isa_io_base = 0; + +/* The following function nicked from arch/ppc/kernel/process.c and + * adapted slightly */ +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + argc = *(int *)sp; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + p = *(char **)sp; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + e = *(unsigned long *)sp; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + e = *(unsigned long *)sp; + *(unsigned long *)(sp + offset) = e; + } while (sp > aux_start); + } +} +/* END stuff taken from arch/ppc/kernel/process.c */ + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-ppc/ptrace.c b/arch/um/sys-ppc/ptrace.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/ptrace.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,28 @@ +#include "linux/sched.h" +#include "asm/ptrace.h" + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + child->thread.process_regs.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + retval &= child->thread.process_regs.regs[regno >> 2]; + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-ppc/ptrace_user.c b/arch/um/sys-ppc/ptrace_user.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/ptrace_user.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,40 @@ +#include +#include +#include +#include "sysdep/ptrace.h" + +int ptrace_getregs(long pid, struct sys_pt_regs *regs_out) +{ + int i; + for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { + errno = 0; + regs_out->regs[i] = ptrace(PTRACE_PEEKUSER, pid, i*4, 0); + if (errno) { + return -errno; + } + } + return 0; +} + +int ptrace_setregs(long pid, struct sys_pt_regs *regs_in) +{ + int i; + for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { + if (i != 34 /* FIXME: PT_ORIG_R3 */ && i <= PT_MQ) { + if (ptrace(PTRACE_POKEUSER, pid, i*4, regs_in->regs[i]) < 0) { + return -errno; + } + } + } + return 0; +} +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-ppc/sigcontext.c b/arch/um/sys-ppc/sigcontext.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/sigcontext.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,53 @@ +#include "asm/ptrace.h" +#include "asm/sigcontext.h" +#include "sysdep/ptrace.h" +#include "user_util.h" + +void fill_in_sigcontext(void *scontext, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ + struct sigcontext_struct *sc = scontext; +#if 0 + int i; + // general purpose regs + for (i=0; i<32; ++i) { + sc->regs->gpr[i] = regs->regs[PT_R0 + i]; + } + sc->regs->nip = regs->regs[PT_NIP]; + sc->regs->msr = regs->regs[PT_MSR]; + sc->regs->orig_gpr3 = regs->regs[PT_ORIG_R3]; + sc->regs->ctr = regs->regs[PT_CTR]; + sc->regs->link = regs->regs[PT_LNK]; + sc->regs->xer = regs->regs[PT_XER]; + sc->regs->ccr = regs->regs[PT_CCR]; + sc->regs->mq = regs->regs[PT_MQ]; + sc->regs->trap = err; + sc->regs->dar = cr2; +#endif + /* This is a bit of a hack; there's some confusion with the + * various definitions of [sys_]pt_regs, and everything isn't + * quite coming together quite right. */ + memcpy(sc->regs, regs, sizeof(struct sys_pt_regs)); + /*(sc->regs) = *regs; */ +} + +void fill_in_regs(struct sys_pt_regs *regs, void *sc_ptr) +{ + struct sigcontext_struct *sc; + + sc = sc_ptr; + + // FIXME: need to investigate what's going on with struct pt_regs etc. + *regs = *(sc->regs); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/arch/um/sys-ppc/sysrq.c b/arch/um/sys-ppc/sysrq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/um/sys-ppc/sysrq.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/smp.h" +#include "asm/ptrace.h" +#include "sysrq.h" + +void show_regs(struct pt_regs_subarch *regs) +{ + printk("\n"); + printk("show_regs(): insert regs here.\n"); +#if 0 + printk("\n"); + printk("EIP: %04x:[<%08lx>] CPU: %d",0xffff & regs->xcs, regs->eip, + smp_processor_id()); + if (regs->xcs & 3) + printk(" ESP: %04x:%08lx",0xffff & regs->xss, regs->esp); + printk(" EFLAGS: %08lx\n", regs->eflags); + printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", + regs->eax, regs->ebx, regs->ecx, regs->edx); + printk("ESI: %08lx EDI: %08lx EBP: %08lx", + regs->esi, regs->edi, regs->ebp); + printk(" DS: %04x ES: %04x\n", + 0xffff & regs->xds, 0xffff & regs->xes); +#endif + + show_trace(®s->gpr[1]); +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile Wed Feb 13 20:03:41 2002 +++ b/drivers/Makefile Wed Feb 13 20:03:41 2002 @@ -8,7 +8,7 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ - fc4 net/hamradio i2c acpi bluetooth + fc4 net/hamradio i2c acpi bluetooth sensors subdir-y := parport char block net sound misc media cdrom hotplug subdir-m := $(subdir-y) @@ -43,6 +43,7 @@ # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set -- ch subdir-$(CONFIG_HAMRADIO) += net/hamradio subdir-$(CONFIG_I2C) += i2c +subdir-$(CONFIG_SENSORS) += sensors subdir-$(CONFIG_ACPI) += acpi subdir-$(CONFIG_BLUEZ) += bluetooth diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c --- a/drivers/block/DAC960.c Wed Feb 13 20:03:56 2002 +++ b/drivers/block/DAC960.c Wed Feb 13 20:03:56 2002 @@ -7013,3 +7013,5 @@ module_init(DAC960_Initialize); module_exit(DAC960_Finalize); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/block/elevator.c b/drivers/block/elevator.c --- a/drivers/block/elevator.c Wed Feb 13 20:03:41 2002 +++ b/drivers/block/elevator.c Wed Feb 13 20:03:41 2002 @@ -80,30 +80,38 @@ struct buffer_head *bh, int rw, int max_sectors) { - struct list_head *entry = &q->queue_head; - unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE; - + struct list_head *entry; + unsigned int count = bh->b_size >> 9; + unsigned int ret = ELEVATOR_NO_MERGE; + int merge_only = 0; + const int max_bomb_segments = q->elevator.max_bomb_segments; + + entry = &q->queue_head; while ((entry = entry->prev) != head) { struct request *__rq = blkdev_entry_to_request(entry); - /* - * simply "aging" of requests in queue - */ - if (__rq->elevator_sequence-- <= 0) - break; - + if (__rq->elevator_sequence-- <= 0) { + /* + * OK, we've exceeded someone's latency limit. + * But we still continue to look for merges, + * because they're so much better than seeks. + */ + merge_only = 1; + } if (__rq->waiting) continue; if (__rq->rq_dev != bh->b_rdev) continue; - if (!*req && bh_rq_in_between(bh, __rq, &q->queue_head)) + if (!*req && !merge_only && + bh_rq_in_between(bh, __rq, &q->queue_head)) { *req = __rq; + } if (__rq->cmd != rw) continue; if (__rq->nr_sectors + count > max_sectors) continue; if (__rq->elevator_sequence < count) - break; + merge_only = 1; if (__rq->sector + __rq->nr_sectors == bh->b_rsector) { ret = ELEVATOR_BACK_MERGE; *req = __rq; @@ -116,6 +124,56 @@ } } + /* + * If we failed to merge a read anywhere in the request + * queue, we really don't want to place it at the end + * of the list, behind lots of writes. So place it near + * the front. + * + * We don't want to place it in front of _all_ writes: that + * would create lots of seeking, and isn't tunable. + * We try to avoid promoting this read in front of existing + * reads. + * + * max_bomb_sectors becomes the maximum number of write + * requests which we allow to remain in place in front of + * a newly introduced read. We weight things a little bit, + * so large writes are more expensive than small ones, but it's + * requests which count, not sectors. + */ + if (max_bomb_segments && rw == READ && ret == ELEVATOR_NO_MERGE) { + int cur_latency = 0; + struct request * const cur_request = *req; + + entry = head->next; + while (entry != &q->queue_head) { + struct request *__rq; + + if (entry == &q->queue_head) + BUG(); + if (entry == q->queue_head.next && + q->head_active && !q->plugged) + BUG(); + __rq = blkdev_entry_to_request(entry); + + if (__rq == cur_request) { + /* + * This is where the old algorithm placed it. + * There's no point pushing it further back, + * so leave it here, in sorted order. + */ + break; + } + if (__rq->cmd == WRITE) { + cur_latency += 1 + __rq->nr_sectors / 64; + if (cur_latency >= max_bomb_segments) { + *req = __rq; + break; + } + } + entry = entry->next; + } + } return ret; } @@ -188,7 +246,7 @@ output.queue_ID = elevator->queue_ID; output.read_latency = elevator->read_latency; output.write_latency = elevator->write_latency; - output.max_bomb_segments = 0; + output.max_bomb_segments = elevator->max_bomb_segments; if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t))) return -EFAULT; @@ -207,9 +265,12 @@ return -EINVAL; if (input.write_latency < 0) return -EINVAL; + if (input.max_bomb_segments < 0) + return -EINVAL; elevator->read_latency = input.read_latency; elevator->write_latency = input.write_latency; + elevator->max_bomb_segments = input.max_bomb_segments; return 0; } diff -Nru a/drivers/block/floppy.c b/drivers/block/floppy.c --- a/drivers/block/floppy.c Wed Feb 13 20:03:35 2002 +++ b/drivers/block/floppy.c Wed Feb 13 20:03:35 2002 @@ -129,6 +129,12 @@ * floppy controller (lingering task on list after module is gone... boom.) */ +/* + * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range + * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix + * requires many non-obvious changes in arch dependent code. + */ + #define FLOPPY_SANITY_CHECK #undef FLOPPY_SILENT_DCL_CLEAR @@ -4229,7 +4235,7 @@ FDCS->rawcmd = 2; if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){ /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address, 6); + release_region(FDCS->address+2, 4); release_region(FDCS->address+7, 1); FDCS->address = -1; FDCS->version = FDC_NONE; @@ -4239,7 +4245,7 @@ FDCS->version = get_fdc_version(); if (FDCS->version == FDC_NONE){ /* free ioports reserved by floppy_grab_irq_and_dma() */ - release_region(FDCS->address, 6); + release_region(FDCS->address+2, 4); release_region(FDCS->address+7, 1); FDCS->address = -1; continue; @@ -4318,11 +4324,11 @@ for (fdc=0; fdc< N_FDC; fdc++){ if (FDCS->address != -1){ - if (!request_region(FDCS->address, 6, "floppy")) { - DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address); + if (!request_region(FDCS->address+2, 4, "floppy")) { + DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + 2); goto cleanup1; } - if (!request_region(FDCS->address + 7, 1, "floppy DIR")) { + if (!request_region(FDCS->address+7, 1, "floppy DIR")) { DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + 7); goto cleanup2; } @@ -4350,12 +4356,12 @@ irqdma_allocated = 1; return 0; cleanup2: - release_region(FDCS->address, 6); + release_region(FDCS->address + 2, 4); cleanup1: fd_free_irq(); fd_free_dma(); while(--fdc >= 0) { - release_region(FDCS->address, 6); + release_region(FDCS->address + 2, 4); release_region(FDCS->address + 7, 1); } MOD_DEC_USE_COUNT; @@ -4422,7 +4428,7 @@ old_fdc = fdc; for (fdc = 0; fdc < N_FDC; fdc++) if (FDCS->address != -1) { - release_region(FDCS->address, 6); + release_region(FDCS->address+2, 4); release_region(FDCS->address+7, 1); } fdc = old_fdc; diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c --- a/drivers/block/ll_rw_blk.c Wed Feb 13 20:03:35 2002 +++ b/drivers/block/ll_rw_blk.c Wed Feb 13 20:03:35 2002 @@ -118,10 +118,22 @@ int * max_sectors[MAX_BLKDEV]; /* - * How many reqeusts do we allocate per queue, - * and how many do we "batch" on freeing them? + * blkdev_varyio indicates if variable size IO can be done on a device. + * + * Currently used for doing variable size IO on RAW devices. + */ +char * blkdev_varyio[MAX_BLKDEV]; + +/* + * The total number of requests in each queue. */ -static int queue_nr_requests, batch_requests; +static int queue_nr_requests; + +/* + * low- and high-water marks for the queues. + */ +static int batch_requests_low; +static int batch_requests_high; static inline int get_max_sectors(kdev_t dev) { @@ -352,7 +364,8 @@ q->rq[i&1].count++; } - init_waitqueue_head(&q->wait_for_request); + init_waitqueue_head(&q->wait_for_requests[0]); + init_waitqueue_head(&q->wait_for_requests[1]); spin_lock_init(&q->queue_lock); } @@ -418,9 +431,9 @@ #define blkdev_free_rq(list) list_entry((list)->next, struct request, queue); /* * Get a free request. io_request_lock must be held and interrupts - * disabled on the way in. + * disabled on the way in. Returns NULL if there are no free requests. */ -static inline struct request *get_request(request_queue_t *q, int rw) +static struct request *get_request(request_queue_t *q, int rw) { struct request *rq = NULL; struct request_list *rl = q->rq + rw; @@ -438,38 +451,102 @@ } /* - * No available requests for this queue, unplug the device. + * Here's the request allocation design: + * + * 1: Blocking on request exhaustion is a key part of I/O throttling. + * + * 2: We want to be `fair' to all requesters. We must avoid starvation, and + * attempt to ensure that all requesters sleep for a similar duration. Hence + * no stealing requests when there are other processes waiting. + * + * 3: We also wish to support `batching' of requests. So when a process is + * woken, we want to allow it to allocate a decent number of requests + * before it blocks again, so they can be nicely merged (this only really + * matters if the process happens to be adding requests near the head of + * the queue). + * + * 4: We want to avoid scheduling storms. This isn't really important, because + * the system will be I/O bound anyway. But it's easy. + * + * There is tension between requirements 2 and 3. Once a task has woken, + * we don't want to allow it to sleep as soon as it takes its second request. + * But we don't want currently-running tasks to steal all the requests + * from the sleepers. We handle this with high- and low- water marks and + * with the assumption that request taking is much, much faster than + * request freeing. + * + * So here's what we do: + * + * a) A READA requester fails if free_requests < batch_requests_high + * + * We don't want READA requests to prevent sleepers from ever + * waking. + * + * When a process wants a new request: + * + * b) If someone else is waiting on requests and free_requests < low_water, + * the requester sleeps in FIFO manner. + * + * c) If low_water < free_requests < high_water, the caller is immediately + * granted a new request. + * + * d) If nobody is waiting on requests, the caller gets given a request, + * if there are any available. Otherwise the caller sleeps. + * + * When a request is released: + * + * e) If free_requests < low_water, do nothing. + * + * f) If free_requests > high_water, wake up a single waiter. + * + * The net effect is that when a process is woken at the high-water mark, + * it will be able to take approximately (high-water - low-water) requests + * before blocking again (at the tail of the queue). + * + * This all assumes that the rate of taking requests is much, much higher + * than the rate of releasing them. Which is very true. + * + * -akpm, Feb 2002. */ +#undef REQTIMING static struct request *__get_request_wait(request_queue_t *q, int rw) { register struct request *rq; DECLARE_WAITQUEUE(wait, current); +#ifdef REQTIMING + struct timeval tv1, tv2; + unsigned long long t1, t2; + unsigned long t; + do_gettimeofday(&tv1); +#endif + generic_unplug_device(q); - add_wait_queue(&q->wait_for_request, &wait); + add_wait_queue_exclusive(&q->wait_for_requests[rw], &wait); do { set_current_state(TASK_UNINTERRUPTIBLE); - if (q->rq[rw].count < batch_requests) + if (q->rq[rw].count < batch_requests_low) schedule(); spin_lock_irq(&io_request_lock); rq = get_request(q,rw); spin_unlock_irq(&io_request_lock); } while (rq == NULL); - remove_wait_queue(&q->wait_for_request, &wait); + remove_wait_queue(&q->wait_for_requests[rw], &wait); current->state = TASK_RUNNING; - return rq; -} - -static inline struct request *get_request_wait(request_queue_t *q, int rw) -{ - register struct request *rq; - spin_lock_irq(&io_request_lock); - rq = get_request(q, rw); - spin_unlock_irq(&io_request_lock); - if (rq) - return rq; - return __get_request_wait(q, rw); +#ifdef REQTIMING + do_gettimeofday(&tv2); + t1 = tv1.tv_sec; + t1 *= 1000000; + t1 += tv1.tv_usec; + t2 = tv2.tv_sec; + t2 *= 1000000; + t2 += tv2.tv_usec; + t = t2 - t1; + printk("%s[%d] wakes after %ldms\n", current->comm, + current->pid, t / 1000); +#endif + return rq; } /* RO fail safe mechanism */ @@ -546,7 +623,7 @@ /* * Must be called with io_request_lock held and interrupts disabled */ -inline void blkdev_release_request(struct request *req) +void blkdev_release_request(struct request *req) { request_queue_t *q = req->q; int rw = req->cmd; @@ -560,8 +637,9 @@ */ if (q) { list_add(&req->queue, &q->rq[rw].free); - if (++q->rq[rw].count >= batch_requests && waitqueue_active(&q->wait_for_request)) - wake_up(&q->wait_for_request); + if (++q->rq[rw].count >= batch_requests_high && + waitqueue_active(&q->wait_for_requests[rw])) + wake_up(&q->wait_for_requests[rw]); } } @@ -742,22 +820,41 @@ BUG(); } - /* - * Grab a free request from the freelist - if that is empty, check - * if we are doing read ahead and abort instead of blocking for - * a free slot. - */ get_rq: if (freereq) { req = freereq; freereq = NULL; - } else if ((req = get_request(q, rw)) == NULL) { - spin_unlock_irq(&io_request_lock); - if (rw_ahead) - goto end_io; - - freereq = __get_request_wait(q, rw); - goto again; + } else { + /* + * See description above __get_request_wait() + */ + if (rw_ahead) { + if (q->rq[rw].count < batch_requests_high) { + spin_unlock_irq(&io_request_lock); + goto end_io; + } + req = get_request(q, rw); + if (req == NULL) + BUG(); + } else { + if (waitqueue_active(&q->wait_for_requests[rw])) { + if (q->rq[rw].count < batch_requests_low) { + spin_unlock_irq(&io_request_lock); + freereq = __get_request_wait(q, rw); + goto again; + } + req = get_request(q, rw); + if (req == NULL) + BUG(); + } else { + req = get_request(q, rw); + if (req == NULL) { + spin_unlock_irq(&io_request_lock); + freereq = __get_request_wait(q, rw); + goto again; + } + } + } } /* fill up the request-info, and add it to the queue */ @@ -919,6 +1016,38 @@ } } +/* + * submit_bh_blknr() - same as submit_bh() except that b_rsector is + * set to b_blocknr. Used for RAW VARY. + */ +void submit_bh_blknr(int rw, struct buffer_head * bh) +{ + int count = bh->b_size >> 9; + + if (!test_bit(BH_Lock, &bh->b_state)) + BUG(); + + set_bit(BH_Req, &bh->b_state); + + /* + * First step, 'identity mapping' - RAID or LVM might + * further remap this. + */ + bh->b_rdev = bh->b_dev; + bh->b_rsector = bh->b_blocknr; + + generic_make_request(rw, bh); + + switch (rw) { + case WRITE: + kstat.pgpgout += count; + break; + default: + kstat.pgpgin += count; + break; + } +} + /** * ll_rw_block: low-level access to block devices * @rw: whether to %READ or %WRITE or maybe %READA (readahead) @@ -1095,7 +1224,7 @@ int __init blk_dev_init(void) { struct blk_dev_struct *dev; - int total_ram; + int total_ram; /* kilobytes */ request_cachep = kmem_cache_create("blkdev_requests", sizeof(struct request), @@ -1117,15 +1246,19 @@ * Free request slots per queue. * (Half for reads, half for writes) */ - queue_nr_requests = 64; - if (total_ram > MB(32)) - queue_nr_requests = 128; + queue_nr_requests = (total_ram >> 9) & ~15; /* One per half-megabyte */ + if (queue_nr_requests < 32) + queue_nr_requests = 32; + if (queue_nr_requests > 1024) + queue_nr_requests = 1024; /* * Batch frees according to queue length */ - batch_requests = queue_nr_requests/4; - printk("block: %d slots per queue, batch=%d\n", queue_nr_requests, batch_requests); + batch_requests_high = queue_nr_requests / 4; + batch_requests_low = batch_requests_high / 2; + printk("block: %d slots per queue, batch_low=%d, batch_high=%d\n", + queue_nr_requests, batch_requests_low, batch_requests_high); #ifdef CONFIG_AMIGA_Z2RAM z2_init(); diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c Wed Feb 13 20:03:30 2002 +++ b/drivers/block/loop.c Wed Feb 13 20:03:30 2002 @@ -492,8 +492,10 @@ * rbh locked at this point, noone else should clear * the dirty flag */ - if (rw == WRITE) + if (rw == WRITE) { set_bit(BH_Dirty, &rbh->b_state); + set_bit(BH_Fake, &rbh->b_state); + } loop_add_bh(lo, rbh); return 0; } @@ -569,9 +571,6 @@ sigfillset(¤t->blocked); flush_signals(current); spin_unlock_irq(¤t->sigmask_lock); - - current->policy = SCHED_OTHER; - current->nice = -20; spin_lock_irq(&lo->lo_lock); lo->lo_state = Lo_bound; diff -Nru a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c --- a/drivers/cdrom/cdrom.c Wed Feb 13 20:03:35 2002 +++ b/drivers/cdrom/cdrom.c Wed Feb 13 20:03:35 2002 @@ -1910,6 +1910,107 @@ return ret; } +/* + * CDROM audio read, with DMA support. Added in 2.4.18-pre4, akpm. + * + * Initially, we try to perform multiframe bus-mastering. If the IDE + * layer experiences a DMA error, we fall back to single-frame DMA. + * If the IDE layer again detects a DMA error, we fall back to multiframe + * PIO. + * + * We do not want to disable drive-level DMA at any stage, because + * some devices can perform non-packet DMA quite happily, but appear + * to not be able to perform packet DMA correctly. + * + * If the drive is not using_dma, we never attempt packet DMA. + */ +static int cdda_read_audio(int cmd, + struct cdrom_device_info *cdi, + struct cdrom_generic_command *cgc, + struct cdrom_read_audio *ra) +{ + int lba; + unsigned frames_todo; + int ret; + void *xferbuf = 0; + unsigned nr_local_frames; + char *useraddr; + + ret = -EINVAL; + if (ra->addr_format == CDROM_MSF) { + lba = msf_to_lba(ra->addr.msf.minute, + ra->addr.msf.second, + ra->addr.msf.frame); + } else if (ra->addr_format == CDROM_LBA) { + lba = ra->addr.lba; + } else { + goto out; + } + + if (lba < 0 || ra->nframes <= 0) + goto out; + + /* + * We can't sensibly support more that 64k because we later + * use a buffer_head to map the temp buffer. And b_count is + * unisgned short. + */ + nr_local_frames = ra->nframes; + if (nr_local_frames * CD_FRAMESIZE_RAW > 32768) + nr_local_frames = 32768 / CD_FRAMESIZE_RAW; + + if (cdi->dma_mode == CDROM_DMA_SINGLE) + nr_local_frames = 1; + + do { + xferbuf = kmalloc(CD_FRAMESIZE_RAW * nr_local_frames, GFP_KERNEL); + } while (!xferbuf && nr_local_frames--); + ret = -ENOMEM; + if (!xferbuf) + goto out; + + cgc->buffer = xferbuf; + cgc->data_direction = CGC_DATA_READ; + if (cdi->dma_mode != CDROM_DMA_NONE) + cgc->do_dma = 1; + frames_todo = ra->nframes; + useraddr = ra->buf; +retry: + while (frames_todo) { + unsigned frames_now = min(frames_todo, nr_local_frames); + + cgc->dma_error = 0; + ret = cdrom_read_block(cdi, cgc, lba, frames_now, 1, CD_FRAMESIZE_RAW); + if (ret) { + /* + * Here we implement DMA size fallback + */ + if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_MULTI) { + printk(KERN_WARNING "CDROM: falling back to " + "single frame DMA\n"); + cdi->dma_mode = CDROM_DMA_SINGLE; + nr_local_frames = 1; + goto retry; + } else if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_SINGLE) { + printk(KERN_WARNING "CDROM: disabled DMA\n"); + cdi->dma_mode = CDROM_DMA_NONE; + goto retry; + } + goto out; + } + ret = -EFAULT; + if (copy_to_user(useraddr, cgc->buffer, CD_FRAMESIZE_RAW * frames_now)) + goto out; + useraddr += CD_FRAMESIZE_RAW * frames_now; + frames_todo -= frames_now; + lba += frames_now; + } + ret = 0; +out: + kfree(xferbuf); + return ret; +} + static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg) { @@ -1973,57 +2074,9 @@ } case CDROMREADAUDIO: { struct cdrom_read_audio ra; - int lba, nr; IOCTL_IN(arg, struct cdrom_read_audio, ra); - - if (ra.addr_format == CDROM_MSF) - lba = msf_to_lba(ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); - else if (ra.addr_format == CDROM_LBA) - lba = ra.addr.lba; - else - return -EINVAL; - - /* FIXME: we need upper bound checking, too!! */ - if (lba < 0 || ra.nframes <= 0) - return -EINVAL; - - /* - * start with will ra.nframes size, back down if alloc fails - */ - nr = ra.nframes; - do { - cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL); - if (cgc.buffer) - break; - - nr >>= 1; - } while (nr); - - if (!nr) - return -ENOMEM; - - if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) { - kfree(cgc.buffer); - return -EFAULT; - } - cgc.data_direction = CGC_DATA_READ; - while (ra.nframes > 0) { - if (nr > ra.nframes) - nr = ra.nframes; - - ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW); - if (ret) - break; - __copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW*nr); - ra.buf += CD_FRAMESIZE_RAW * nr; - ra.nframes -= nr; - lba += nr; - } - kfree(cgc.buffer); - return ret; + return cdda_read_audio(cmd, cdi, &cgc, &ra); } case CDROMSUBCHNL: { struct cdrom_subchnl q; diff -Nru a/drivers/char/Config.in b/drivers/char/Config.in --- a/drivers/char/Config.in Wed Feb 13 20:03:39 2002 +++ b/drivers/char/Config.in Wed Feb 13 20:03:39 2002 @@ -107,6 +107,8 @@ source drivers/i2c/Config.in +source drivers/sensors/Config.in + mainmenu_option next_comment comment 'Mice' tristate 'Bus Mouse Support' CONFIG_BUSMOUSE @@ -183,7 +185,9 @@ tristate 'NetWinder flash support' CONFIG_NWFLASH fi +dep_tristate 'AMD 768 Random Number Generator support' CONFIG_AMD_RNG $CONFIG_PCI dep_tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG $CONFIG_PCI +dep_tristate 'AMD 762/768 native power management' CONFIG_AMD_PM768 $CONFIG_PCI tristate '/dev/nvram support' CONFIG_NVRAM tristate 'Enhanced Real Time Clock Support' CONFIG_RTC if [ "$CONFIG_IA64" = "y" ]; then diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile --- a/drivers/char/Makefile Wed Feb 13 20:03:50 2002 +++ b/drivers/char/Makefile Wed Feb 13 20:03:50 2002 @@ -72,6 +72,12 @@ endif endif +ifeq ($(ARCH),um) + KEYMAP = + KEYBD = + CONSOLE = +endif + ifeq ($(ARCH),sh) KEYMAP = KEYBD = @@ -196,6 +202,8 @@ obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_INTEL_RNG) += i810_rng.o +obj-$(CONFIG_AMD_RNG) += amd768_rng.o +obj-$(CONFIG_AMD_PM768) += amd768_pm.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o diff -Nru a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c --- a/drivers/char/agp/agpgart_be.c Wed Feb 13 20:03:35 2002 +++ b/drivers/char/agp/agpgart_be.c Wed Feb 13 20:03:35 2002 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -830,7 +831,7 @@ page = virt_to_page(pt); atomic_dec(&page->count); clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + wake_up_page(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -2835,7 +2836,7 @@ page = virt_to_page(pt); atomic_dec(&page->count); clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + wake_up_page(page); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } diff -Nru a/drivers/char/amd768_pm.c b/drivers/char/amd768_pm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/amd768_pm.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,151 @@ +/* + * Native power management driver for the AMD 760MPx series + * + * (c) Copyright 2002, Red Hat Inc, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u32 pmbase; /* PMxx I/O base */ +static struct pci_dev *amd762; + +/* + * amd768pm_init_one - look for and attempt to init PM + */ +static int __init amd768pm_init_one (struct pci_dev *dev) +{ + u32 reg; + u8 rnen; + int i; + + amd762 = pci_find_device(0x1022, 0x700C, NULL); + if(amd762 == NULL) + return -ENODEV; + + pci_read_config_dword(amd762, 0x70, ®); + if(!(reg & (1<<18))) + { + printk(KERN_INFO "AMD768_pm: enabling self refresh.\n"); + reg |= (1<<18); /* Enable self refresh */ + pci_write_config_dword(amd762, 0x70, reg); + } + + pci_read_config_dword(amd762, 0x58, ®); + if(reg&(1<<19)) + { + printk(KERN_INFO "AMD768_pm: DRAM refresh enabled.\n"); + reg &= ~(1<<19); /* Disable to allow self refresh modes */ + pci_write_config_dword(amd762, 0x58, reg); + } + + for(i=0; i 2) + { + printk(KERN_ERR "Only single and dual processor AMD762/768 is supported.\n"); + return -ENODEV; + } + pci_for_each_dev(pdev) { + if (pci_match_device (amd768pm_pci_tbl, pdev) != NULL) + goto match; + } + + return -ENODEV; + +match: + rc = amd768pm_init_one (pdev); + if (rc) + return rc; + return 0; +} + + +/* + * amd768pm_cleanup - shutdown the AMD pm module + */ +static void __exit amd768pm_cleanup (void) +{ +} + + +module_init (amd768pm_init); +module_exit (amd768pm_cleanup); diff -Nru a/drivers/char/amd768_rng.c b/drivers/char/amd768_rng.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/amd768_rng.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,295 @@ +/* + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc + + derived from + + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik + Copyright 2000,2001 Philipp Rumpf + + Please read Documentation/i810_rng.txt for details on use. + + ---------------------------------------------------------- + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * core module and version information + */ +#define RNG_VERSION "0.1.0" +#define RNG_MODULE_NAME "amd768_rng" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + + +/* + * debugging macros + */ +#undef RNG_DEBUG /* define to enable copious debugging info */ + +#ifdef RNG_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define RNG_MISCDEV_MINOR 183 /* official */ + +/* + * various RNG status variables. they are globals + * as we only support a single RNG device + */ + +static u32 pmbase; /* PMxx I/O base */ +static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ + + +/* + * inlined helper functions for accessing RNG registers + */ + +static inline int rng_data_present (void) +{ + return inl(pmbase+0xF4) & 1; +} + + +static inline int rng_data_read (void) +{ + return inl(pmbase+0xF0); +} + +static int rng_dev_open (struct inode *inode, struct file *filp) +{ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + + /* wait for device to become free */ + if (filp->f_flags & O_NONBLOCK) { + if (down_trylock (&rng_open_sem)) + return -EAGAIN; + } else { + if (down_interruptible (&rng_open_sem)) + return -ERESTARTSYS; + } + return 0; +} + + +static int rng_dev_release (struct inode *inode, struct file *filp) +{ + up(&rng_open_sem); + return 0; +} + + +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp) +{ + static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; + int have_data; + u32 data = 0; + ssize_t ret = 0; + + while (size) { + spin_lock(&rng_lock); + + have_data = 0; + if (rng_data_present()) { + data = rng_data_read(); + have_data = 4; + } + + spin_unlock (&rng_lock); + + while (have_data > 0) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(current->need_resched) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + else + udelay(200); /* FIXME: We could poll for 250uS ?? */ + + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + + +static struct file_operations rng_chrdev_ops = { + owner: THIS_MODULE, + open: rng_dev_open, + release: rng_dev_release, + read: rng_dev_read, +}; + + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + + +/* + * rng_init_one - look for and attempt to init a single RNG + */ +static int __init rng_init_one (struct pci_dev *dev) +{ + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + rc = misc_register (&rng_miscdev); + if (rc) { + printk (KERN_ERR PFX "cannot register misc device\n"); + DPRINTK ("EXIT, returning %d\n", rc); + goto err_out; + } + + pci_read_config_dword(dev, 0x58, &pmbase); + + pmbase&=0x0000FF00; + + if(pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + DPRINTK ("EXIT, returning %d\n", rc); + goto err_out_free_miscdev; + } + + pci_read_config_byte(dev, 0x40, &rnen); + rnen|=(1<<7); /* RNG on */ + pci_write_config_byte(dev, 0x40, rnen); + + pci_read_config_byte(dev, 0x41, &rnen); + rnen|=(1<<7); /* PMIO enable */ + pci_write_config_byte(dev, 0x41, rnen); + + printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_miscdev: + misc_deregister (&rng_miscdev); +err_out: + return rc; +} + + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id rng_pci_tbl[] __initdata = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); + + +MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); +MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); + + +/* + * rng_init - initialize RNG module + */ +static int __init rng_init (void) +{ + int rc; + struct pci_dev *pdev; + + DPRINTK ("ENTER\n"); + + init_MUTEX (&rng_open_sem); + + pci_for_each_dev(pdev) { + if (pci_match_device (rng_pci_tbl, pdev) != NULL) + goto match; + } + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + rc = rng_init_one (pdev); + if (rc) + return rc; + + printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/* + * rng_init - shutdown RNG module + */ +static void __exit rng_cleanup (void) +{ + DPRINTK ("ENTER\n"); + misc_deregister (&rng_miscdev); + DPRINTK ("EXIT\n"); +} + + +module_init (rng_init); +module_exit (rng_cleanup); diff -Nru a/drivers/char/applicom.c b/drivers/char/applicom.c --- a/drivers/char/applicom.c Wed Feb 13 20:03:49 2002 +++ b/drivers/char/applicom.c Wed Feb 13 20:03:49 2002 @@ -36,7 +36,7 @@ #if LINUX_VERSION_CODE < 0x20300 /* These probably want adding to */ -#define init_waitqueue_head(x) do { *(x) = NULL; } while (0); +#define init_waitqueue_head(x) do { *(x) = NULL; } while (0) #define PCI_BASE_ADDRESS(dev) (dev->base_address[0]) #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x #define __setup(x,y) /* */ diff -Nru a/drivers/char/drm/Config.in b/drivers/char/drm/Config.in --- a/drivers/char/drm/Config.in Wed Feb 13 20:03:48 2002 +++ b/drivers/char/drm/Config.in Wed Feb 13 20:03:48 2002 @@ -10,5 +10,6 @@ tristate ' ATI Rage 128' CONFIG_DRM_R128 dep_tristate ' ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP dep_tristate ' Intel I810' CONFIG_DRM_I810 $CONFIG_AGP +dep_tristate ' Intel 830M' CONFIG_DRM_I830 $CONFIG_AGP dep_tristate ' Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP dep_tristate ' SiS' CONFIG_DRM_SIS $CONFIG_AGP diff -Nru a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile --- a/drivers/char/drm/Makefile Wed Feb 13 20:03:56 2002 +++ b/drivers/char/drm/Makefile Wed Feb 13 20:03:56 2002 @@ -3,13 +3,15 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. O_TARGET := drm.o -list-multi := gamma.o tdfx.o r128.o mga.o i810.o radeon.o ffb.o sis.o +list-multi := gamma.o tdfx.o r128.o mga.o i810.o i830.o radeon.o ffb.o sis.o gamma-objs := gamma_drv.o gamma_dma.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o i810-objs := i810_drv.o i810_dma.o +i830-objs := i830_drv.o i830_dma.o + radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o ffb-objs := ffb_drv.o ffb_context.o sis-objs := sis_drv.o sis_ds.o sis_mm.o @@ -20,6 +22,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon.o obj-$(CONFIG_DRM_MGA) += mga.o obj-$(CONFIG_DRM_I810) += i810.o +obj-$(CONFIG_DRM_I830) += i830.o obj-$(CONFIG_DRM_FFB) += ffb.o obj-$(CONFIG_DRM_SIS) += sis.o @@ -36,6 +39,9 @@ i810.o: $(i810-objs) $(lib) $(LD) -r -o $@ $(i810-objs) $(lib) + +i830.o: $(i830-objs) $(lib) + $(LD) -r -o $@ $(i830-objs) $(lib) r128.o: $(r128-objs) $(lib) $(LD) -r -o $@ $(r128-objs) $(lib) diff -Nru a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h --- a/drivers/char/drm/drm.h Wed Feb 13 20:03:40 2002 +++ b/drivers/char/drm/drm.h Wed Feb 13 20:03:40 2002 @@ -104,9 +104,8 @@ #include "i810_drm.h" #include "r128_drm.h" #include "radeon_drm.h" -#if defined(CONFIG_DRM_SIS) || defined(CONFIG_DRM_SIS_MODULE) #include "sis_drm.h" -#endif +#include "i830_drm.h" typedef struct drm_version { int version_major; /* Major version */ @@ -449,6 +448,12 @@ #define DRM_IOCTL_I810_SWAP DRM_IO( 0x46) #define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t) #define DRM_IOCTL_I810_DOCOPY DRM_IO( 0x48) +#define DRM_IOCTL_I810_OV0INFO DRM_IOR( 0x49, drm_i810_overlay_t) +#define DRM_IOCTL_I810_FSTATUS DRM_IO ( 0x4a) +#define DRM_IOCTL_I810_OV0FLIP DRM_IO ( 0x4b) +#define DRM_IOCTL_I810_MC DRM_IOW( 0x4c, drm_i810_mc_t) +#define DRM_IOCTL_I810_RSTATUS DRM_IO ( 0x4d ) + /* Rage 128 specific ioctls */ #define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t) @@ -483,7 +488,6 @@ #define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(0x4d, drm_radeon_indirect_t) #define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(0x4e, drm_radeon_texture_t) -#if defined(CONFIG_DRM_SIS) || defined(CONFIG_DRM_SIS_MODULE) /* SiS specific ioctls */ #define SIS_IOCTL_FB_ALLOC DRM_IOWR(0x44, drm_sis_mem_t) #define SIS_IOCTL_FB_FREE DRM_IOW( 0x45, drm_sis_mem_t) @@ -493,6 +497,16 @@ #define SIS_IOCTL_FLIP DRM_IOW( 0x48, drm_sis_flip_t) #define SIS_IOCTL_FLIP_INIT DRM_IO( 0x49) #define SIS_IOCTL_FLIP_FINAL DRM_IO( 0x50) -#endif + +/* I830 specific ioctls */ +#define DRM_IOCTL_I830_INIT DRM_IOW( 0x40, drm_i830_init_t) +#define DRM_IOCTL_I830_VERTEX DRM_IOW( 0x41, drm_i830_vertex_t) +#define DRM_IOCTL_I830_CLEAR DRM_IOW( 0x42, drm_i830_clear_t) +#define DRM_IOCTL_I830_FLUSH DRM_IO ( 0x43) +#define DRM_IOCTL_I830_GETAGE DRM_IO ( 0x44) +#define DRM_IOCTL_I830_GETBUF DRM_IOWR(0x45, drm_i830_dma_t) +#define DRM_IOCTL_I830_SWAP DRM_IO ( 0x46) +#define DRM_IOCTL_I830_COPY DRM_IOW( 0x47, drm_i830_copy_t) +#define DRM_IOCTL_I830_DOCOPY DRM_IO ( 0x48) #endif diff -Nru a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h --- a/drivers/char/drm/drmP.h Wed Feb 13 20:03:37 2002 +++ b/drivers/char/drm/drmP.h Wed Feb 13 20:03:37 2002 @@ -66,13 +66,8 @@ #include #include #endif -#if LINUX_VERSION_CODE >= 0x020100 /* KERNEL_VERSION(2,1,0) */ #include #include -#endif -#if LINUX_VERSION_CODE < 0x020400 -#include "compat-pre24.h" -#endif #include #include "drm.h" @@ -81,12 +76,6 @@ #define page_to_bus(page) ((unsigned int)(virt_to_bus(page_address(page)))) #endif -/* We just use virt_to_bus for pci_map_single on older kernels */ -#if LINUX_VERSION_CODE < 0x020400 -#define pci_map_single(hwdev, ptr, size, direction) virt_to_bus(ptr) -#define pci_unmap_single(hwdev, dma_addr, size, direction) -#endif - /* DRM template customization defaults */ #ifndef __HAVE_AGP @@ -160,180 +149,7 @@ #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) - /* Backward compatibility section */ - /* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */ -#ifndef _PAGE_PWT -#define _PAGE_PWT _PAGE_WT -#endif - /* Wait queue declarations changed in 2.3.1 */ -#ifndef DECLARE_WAITQUEUE -#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL } -typedef struct wait_queue *wait_queue_head_t; -#define init_waitqueue_head(q) *q = NULL; -#endif - - /* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */ -#ifndef _PAGE_PSE -#define _PAGE_PSE _PAGE_4M -#endif - - /* vm_offset changed to vm_pgoff in 2.3.25 */ -#if LINUX_VERSION_CODE < 0x020319 -#define VM_OFFSET(vma) ((vma)->vm_offset) -#else #define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) -#endif - - /* *_nopage return values defined in 2.3.26 */ -#ifndef NOPAGE_SIGBUS -#define NOPAGE_SIGBUS 0 -#endif -#ifndef NOPAGE_OOM -#define NOPAGE_OOM 0 -#endif - - /* module_init/module_exit added in 2.3.13 */ -#ifndef module_init -#define module_init(x) int init_module(void) { return x(); } -#endif -#ifndef module_exit -#define module_exit(x) void cleanup_module(void) { x(); } -#endif - - /* Generic cmpxchg added in 2.3.x */ -#ifndef __HAVE_ARCH_CMPXCHG - /* Include this here so that driver can be - used with older kernels. */ -#if defined(__alpha__) -static __inline__ unsigned long -__cmpxchg_u32(volatile int *m, int old, int new) -{ - unsigned long prev, cmp; - - __asm__ __volatile__( - "1: ldl_l %0,%5\n" - " cmpeq %0,%3,%1\n" - " beq %1,2f\n" - " mov %4,%1\n" - " stl_c %1,%2\n" - " beq %1,3f\n" - "2: mb\n" - ".subsection 2\n" - "3: br 1b\n" - ".previous" - : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m) - : "memory" ); - - return prev; -} - -static __inline__ unsigned long -__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) -{ - unsigned long prev, cmp; - - __asm__ __volatile__( - "1: ldq_l %0,%5\n" - " cmpeq %0,%3,%1\n" - " beq %1,2f\n" - " mov %4,%1\n" - " stq_c %1,%2\n" - " beq %1,3f\n" - "2: mb\n" - ".subsection 2\n" - "3: br 1b\n" - ".previous" - : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m) - : "memory" ); - - return prev; -} - -static __inline__ unsigned long -__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) -{ - switch (size) { - case 4: - return __cmpxchg_u32(ptr, old, new); - case 8: - return __cmpxchg_u64(ptr, old, new); - } - return old; -} -#define cmpxchg(ptr,o,n) \ - ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, sizeof(*(ptr))); \ - }) - -#elif __i386__ -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) -{ - unsigned long prev; - switch (size) { - case 1: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 2: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 4: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - } - return old; -} - -#elif defined(__powerpc__) -extern void __cmpxchg_called_with_bad_pointer(void); -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) -{ - unsigned long prev; - - switch (size) { - case 4: - __asm__ __volatile__( - "sync;" - "0: lwarx %0,0,%1 ;" - " cmpl 0,%0,%3;" - " bne 1f;" - " stwcx. %2,0,%1;" - " bne- 0b;" - "1: " - "sync;" - : "=&r"(prev) - : "r"(ptr), "r"(new), "r"(old) - : "cr0", "memory"); - return prev; - } - __cmpxchg_called_with_bad_pointer(); - return old; -} - -#endif /* i386, powerpc & alpha */ - -#ifndef __alpha__ -#define cmpxchg(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ - (unsigned long)(n),sizeof(*(ptr)))) -#endif - -#endif /* !__HAVE_ARCH_CMPXCHG */ /* Macros to make printk easier */ #define DRM_ERROR(fmt, arg...) \ @@ -778,34 +594,18 @@ struct poll_table_struct *wait); /* Mapping support (drm_vm.h) */ -#if LINUX_VERSION_CODE < 0x020317 -extern unsigned long DRM(vm_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused); -extern unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused); -extern unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused); -extern unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused); -#else - /* Return type changed in 2.3.23 */ extern struct page *DRM(vm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); -#endif + int write_access); extern void DRM(vm_open)(struct vm_area_struct *vma); extern void DRM(vm_close)(struct vm_area_struct *vma); extern void DRM(vm_shm_close)(struct vm_area_struct *vma); diff -Nru a/drivers/char/drm/drm_agpsupport.h b/drivers/char/drm/drm_agpsupport.h --- a/drivers/char/drm/drm_agpsupport.h Wed Feb 13 20:03:29 2002 +++ b/drivers/char/drm/drm_agpsupport.h Wed Feb 13 20:03:29 2002 @@ -35,12 +35,8 @@ #if __REALLY_HAVE_AGP -#if LINUX_VERSION_CODE < 0x020400 -#include "agpsupport-pre24.h" -#else #define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") #define DRM_AGP_PUT inter_module_put("drm_agp") -#endif static const drm_agp_t *drm_agp = NULL; @@ -271,24 +267,24 @@ case INTEL_GX: head->chipset = "Intel 440GX"; break; case INTEL_I810: head->chipset = "Intel i810"; break; -#if LINUX_VERSION_CODE >= 0x020400 case INTEL_I815: head->chipset = "Intel i815"; break; +#if LINUX_VERSION_CODE >= 0x020415 case INTEL_I820: head->chipset = "Intel i820"; break; +#endif case INTEL_I840: head->chipset = "Intel i840"; break; +#if LINUX_VERSION_CODE >= 0x020415 case INTEL_I845: head->chipset = "Intel i845"; break; - case INTEL_I850: head->chipset = "Intel i850"; break; #endif + case INTEL_I850: head->chipset = "Intel i850"; break; case VIA_GENERIC: head->chipset = "VIA"; break; case VIA_VP3: head->chipset = "VIA VP3"; break; case VIA_MVP3: head->chipset = "VIA MVP3"; break; -#if LINUX_VERSION_CODE >= 0x020400 case VIA_MVP4: head->chipset = "VIA MVP4"; break; case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; break; case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; break; -#endif case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; break; diff -Nru a/drivers/char/drm/drm_bufs.h b/drivers/char/drm/drm_bufs.h --- a/drivers/char/drm/drm_bufs.h Wed Feb 13 20:03:40 2002 +++ b/drivers/char/drm/drm_bufs.h Wed Feb 13 20:03:40 2002 @@ -229,11 +229,7 @@ DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { -#if LINUX_VERSION_CODE >= 0x020300 if (pt->vma->vm_private_data == map) found_maps++; -#else - if (pt->vma->vm_pte == map) found_maps++; -#endif } if(!found_maps) { diff -Nru a/drivers/char/drm/drm_drv.h b/drivers/char/drm/drm_drv.h --- a/drivers/char/drm/drm_drv.h Wed Feb 13 20:03:42 2002 +++ b/drivers/char/drm/drm_drv.h Wed Feb 13 20:03:42 2002 @@ -113,7 +113,6 @@ #define DRIVER_IOCTLS #endif #ifndef DRIVER_FOPS -#if LINUX_VERSION_CODE >= 0x020400 #define DRIVER_FOPS \ static struct file_operations DRM(fops) = { \ owner: THIS_MODULE, \ @@ -126,19 +125,6 @@ fasync: DRM(fasync), \ poll: DRM(poll), \ } -#else -#define DRIVER_FOPS \ -static struct file_operations DRM(fops) = { \ - open: DRM(open), \ - flush: DRM(flush), \ - release: DRM(release), \ - ioctl: DRM(ioctl), \ - mmap: DRM(mmap), \ - read: DRM(read), \ - fasync: DRM(fasync), \ - poll: DRM(poll), \ -} -#endif #endif @@ -234,7 +220,9 @@ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_PARM( drm_opts, "s" ); +#ifdef MODULE_LICENSE MODULE_LICENSE("GPL and additional rights"); +#endif static int DRM(setup)( drm_device_t *dev ) { @@ -732,9 +720,6 @@ retcode = DRM(open_helper)( inode, filp, dev ); if ( !retcode ) { -#if LINUX_VERSION_CODE < 0x020333 - MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ -#endif atomic_inc( &dev->counts[_DRM_STAT_OPENS] ); spin_lock( &dev->count_lock ); if ( !dev->open_count++ ) { @@ -853,9 +838,6 @@ * End inline drm_release */ -#if LINUX_VERSION_CODE < 0x020333 - MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ -#endif atomic_inc( &dev->counts[_DRM_STAT_CLOSES] ); spin_lock( &dev->count_lock ); if ( !--dev->open_count ) { @@ -1047,25 +1029,6 @@ atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] ); -#if __HAVE_KERNEL_CTX_SWITCH - /* We no longer really hold it, but if we are the next - * agent to request it then we should just be able to - * take it immediately and not eat the ioctl. - */ - dev->lock.pid = 0; - { - __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; - unsigned int old, new, prev, ctx; - - ctx = lock.context; - do { - old = *plock; - new = ctx; - prev = cmpxchg(plock, old, new); - } while (prev != old); - } - wake_up_interruptible(&dev->lock.lock_queue); -#else DRM(lock_transfer)( dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT ); #if __HAVE_DMA_SCHEDULE @@ -1080,7 +1043,6 @@ DRM_ERROR( "\n" ); } } -#endif /* !__HAVE_KERNEL_CTX_SWITCH */ unblock_all_signals(); return 0; diff -Nru a/drivers/char/drm/drm_fops.h b/drivers/char/drm/drm_fops.h --- a/drivers/char/drm/drm_fops.h Wed Feb 13 20:03:30 2002 +++ b/drivers/char/drm/drm_fops.h Wed Feb 13 20:03:30 2002 @@ -125,21 +125,31 @@ int avail; int send; int cur; + DECLARE_WAITQUEUE(wait, current); DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + add_wait_queue(&dev->buf_readers, &wait); + set_current_state(TASK_INTERRUPTIBLE); while (dev->buf_rp == dev->buf_wp) { DRM_DEBUG(" sleeping\n"); if (filp->f_flags & O_NONBLOCK) { + remove_wait_queue(&dev->buf_readers, &wait); + set_current_state(TASK_RUNNING); return -EAGAIN; } - interruptible_sleep_on(&dev->buf_readers); + schedule(); /* wait for dev->buf_readers */ if (signal_pending(current)) { DRM_DEBUG(" interrupted\n"); + remove_wait_queue(&dev->buf_readers, &wait); + set_current_state(TASK_RUNNING); return -ERESTARTSYS; } DRM_DEBUG(" awake\n"); + set_current_state(TASK_INTERRUPTIBLE); } + remove_wait_queue(&dev->buf_readers, &wait); + set_current_state(TASK_RUNNING); left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; avail = DRM_BSZ - left; @@ -191,24 +201,8 @@ send -= count; } -#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS) - /* The extra parameter to kill_fasync was added in 2.3.21, and is - _not_ present in _stock_ 2.2.14 and 2.2.15. However, some - distributions patch 2.2.x kernels to add this parameter. The - Makefile.linux attempts to detect this addition and defines - KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */ - if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); -#else - - /* Parameter added in 2.3.21. */ -#if LINUX_VERSION_CODE < 0x020400 - if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); -#else - /* Type of first parameter changed in - Linux 2.4.0-test2... */ if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); -#endif -#endif + DRM_DEBUG("waking\n"); wake_up_interruptible(&dev->buf_readers); return 0; diff -Nru a/drivers/char/drm/drm_memory.h b/drivers/char/drm/drm_memory.h --- a/drivers/char/drm/drm_memory.h Wed Feb 13 20:03:37 2002 +++ b/drivers/char/drm/drm_memory.h Wed Feb 13 20:03:37 2002 @@ -85,12 +85,7 @@ } si_meminfo(&si); -#if LINUX_VERSION_CODE < 0x020317 - /* Changed to page count in 2.3.23 */ - DRM(ram_available) = si.totalram >> PAGE_SHIFT; -#else DRM(ram_available) = si.totalram; -#endif DRM(ram_used) = 0; } @@ -257,12 +252,7 @@ for (addr = address, sz = bytes; sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { -#if LINUX_VERSION_CODE >= 0x020400 - /* Argument type changed in 2.4.0-test6/pre8 */ mem_map_reserve(virt_to_page(addr)); -#else - mem_map_reserve(MAP_NR(addr)); -#endif } return address; @@ -283,12 +273,7 @@ for (addr = address, sz = bytes; sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { -#if LINUX_VERSION_CODE >= 0x020400 - /* Argument type changed in 2.4.0-test6/pre8 */ mem_map_unreserve(virt_to_page(addr)); -#else - mem_map_unreserve(MAP_NR(addr)); -#endif } free_pages(address, order); } diff -Nru a/drivers/char/drm/drm_stub.h b/drivers/char/drm/drm_stub.h --- a/drivers/char/drm/drm_stub.h Wed Feb 13 20:03:56 2002 +++ b/drivers/char/drm/drm_stub.h Wed Feb 13 20:03:56 2002 @@ -31,10 +31,6 @@ #define __NO_VERSION__ #include "drmP.h" -#if LINUX_VERSION_CODE < 0x020400 -#include "stubsupport-pre24.h" -#endif - #define DRM_STUB_MAXCARDS 16 /* Enough for one machine */ static struct drm_stub_list { @@ -70,9 +66,7 @@ } static struct file_operations DRM(stub_fops) = { -#if LINUX_VERSION_CODE >= 0x020400 owner: THIS_MODULE, -#endif open: DRM(stub_open) }; diff -Nru a/drivers/char/drm/drm_vm.h b/drivers/char/drm/drm_vm.h --- a/drivers/char/drm/drm_vm.h Wed Feb 13 20:03:54 2002 +++ b/drivers/char/drm/drm_vm.h Wed Feb 13 20:03:54 2002 @@ -56,16 +56,9 @@ close: DRM(vm_close), }; -#if LINUX_VERSION_CODE < 0x020317 -unsigned long DRM(vm_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused) -#else - /* Return type changed in 2.3.23 */ struct page *DRM(vm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused) -#endif + int write_access) { #if __REALLY_HAVE_AGP drm_file_t *priv = vma->vm_file->private_data; @@ -122,11 +115,7 @@ DRM_DEBUG("baddr = 0x%lx page = 0x%p, offset = 0x%lx\n", baddr, __va(agpmem->memory->memory[offset]), offset); -#if LINUX_VERSION_CODE < 0x020317 - return page_address(page); -#else return page; -#endif } vm_nopage_error: #endif /* __REALLY_HAVE_AGP */ @@ -134,22 +123,11 @@ return NOPAGE_SIGBUS; /* Disallow mremap */ } -#if LINUX_VERSION_CODE < 0x020317 -unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused) -#else - /* Return type changed in 2.3.23 */ struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused) -#endif + int write_access) { -#if LINUX_VERSION_CODE >= 0x020300 drm_map_t *map = (drm_map_t *)vma->vm_private_data; -#else - drm_map_t *map = (drm_map_t *)vma->vm_pte; -#endif unsigned long offset; unsigned long i; pgd_t *pgd; @@ -175,12 +153,8 @@ page = pte_page(*pte); get_page(page); - DRM_DEBUG("shm_nopage 0x%lx\n", address); -#if LINUX_VERSION_CODE < 0x020317 - return page_address(page); -#else + DRM_DEBUG("0x%08lx => 0x%08x\n", address, page_to_bus(page)); return page; -#endif } /* Special close routine which deletes map information if we are the last @@ -199,25 +173,14 @@ DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); -#if LINUX_VERSION_CODE < 0x020333 - MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ -#endif atomic_dec(&dev->vma_count); -#if LINUX_VERSION_CODE >= 0x020300 map = vma->vm_private_data; -#else - map = vma->vm_pte; -#endif down(&dev->struct_sem); for (pt = dev->vmalist, prev = NULL; pt; pt = next) { next = pt->next; -#if LINUX_VERSION_CODE >= 0x020300 if (pt->vma->vm_private_data == map) found_maps++; -#else - if (pt->vma->vm_pte == map) found_maps++; -#endif if (pt->vma == vma) { if (prev) { prev->next = pt->next; @@ -270,16 +233,9 @@ up(&dev->struct_sem); } -#if LINUX_VERSION_CODE < 0x020317 -unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused) -#else - /* Return type changed in 2.3.23 */ struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused) -#endif + int write_access) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; @@ -299,30 +255,16 @@ get_page(page); - DRM_DEBUG("dma_nopage 0x%lx (page %lu)\n", address, page_nr); -#if LINUX_VERSION_CODE < 0x020317 - return page_address(page); -#else + DRM_DEBUG("0x%08lx (page %lu) => 0x%08x\n", address, page_nr, + page_to_bus(page)); return page; -#endif } -#if LINUX_VERSION_CODE < 0x020317 -unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, - unsigned long address, - int unused) -#else - /* Return type changed in 2.3.23 */ struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused) -#endif + int write_access) { -#if LINUX_VERSION_CODE >= 0x020300 drm_map_t *map = (drm_map_t *)vma->vm_private_data; -#else - drm_map_t *map = (drm_map_t *)vma->vm_pte; -#endif drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->dev; drm_sg_mem_t *entry = dev->sg; @@ -342,11 +284,7 @@ page = entry->pagelist[page_offset]; get_page(page); -#if LINUX_VERSION_CODE < 0x020317 - return page_address(page); -#else return page; -#endif } void DRM(vm_open)(struct vm_area_struct *vma) @@ -358,10 +296,6 @@ DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); atomic_inc(&dev->vma_count); -#if LINUX_VERSION_CODE < 0x020333 - /* The map can exist after the fd is closed. */ - MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ -#endif vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS); if (vma_entry) { @@ -382,9 +316,6 @@ DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); -#if LINUX_VERSION_CODE < 0x020333 - MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ -#endif atomic_dec(&dev->vma_count); down(&dev->struct_sem); @@ -423,13 +354,13 @@ unlock_kernel(); vma->vm_ops = &DRM(vm_dma_ops); - vma->vm_flags |= VM_RESERVED; /* Don't swap */ -#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ - /* In Linux 2.2.3 and above, this is - handled in do_mmap() in mm/mmap.c. */ - ++filp->f_count; +#if LINUX_VERSION_CODE <= 0x020414 + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ +#else + vma->vm_flags |= VM_RESERVED; /* Don't swap */ #endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ DRM(vm_open)(vma); return 0; @@ -531,17 +462,10 @@ vma->vm_flags |= VM_IO; /* not in core dump */ } offset = DRIVER_GET_REG_OFS(); -#ifdef __sparc__ - if (io_remap_page_range(vma->vm_start, - VM_OFFSET(vma) + offset, - vma->vm_end - vma->vm_start, - vma->vm_page_prot, 0)) -#else if (remap_page_range(vma->vm_start, VM_OFFSET(vma) + offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) -#endif return -EAGAIN; DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," " offset = 0x%lx\n", @@ -551,34 +475,33 @@ break; case _DRM_SHM: vma->vm_ops = &DRM(vm_shm_ops); -#if LINUX_VERSION_CODE >= 0x020300 vma->vm_private_data = (void *)map; -#else - vma->vm_pte = (unsigned long)map; -#endif /* Don't let this area swap. Change when DRM_KERNEL advisory is supported. */ +#if LINUX_VERSION_CODE <= 0x020414 + vma->vm_flags |= VM_LOCKED; +#else vma->vm_flags |= VM_RESERVED; +#endif break; case _DRM_SCATTER_GATHER: vma->vm_ops = &DRM(vm_sg_ops); -#if LINUX_VERSION_CODE >= 0x020300 vma->vm_private_data = (void *)map; +#if LINUX_VERSION_CODE <= 0x020414 + vma->vm_flags |= VM_LOCKED; #else - vma->vm_pte = (unsigned long)map; + vma->vm_flags |= VM_RESERVED; #endif - vma->vm_flags |= VM_RESERVED; break; default: return -EINVAL; /* This should never happen. */ } +#if LINUX_VERSION_CODE <= 0x020414 + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ +#else vma->vm_flags |= VM_RESERVED; /* Don't swap */ - -#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ - /* In Linux 2.2.3 and above, this is - handled in do_mmap() in mm/mmap.c. */ - ++filp->f_count; #endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ DRM(vm_open)(vma); return 0; diff -Nru a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c --- a/drivers/char/drm/i810_dma.c Wed Feb 13 20:03:47 2002 +++ b/drivers/char/drm/i810_dma.c Wed Feb 13 20:03:47 2002 @@ -35,6 +35,7 @@ #include "drmP.h" #include "i810_drv.h" #include /* For task queue support */ +#include /* in case we don't have a 2.3.99-pre6 kernel or later: */ #ifndef VM_DONTCOPY @@ -73,7 +74,7 @@ *(volatile unsigned int *)(virt + outring) = n; \ outring += 4; \ outring &= ringmask; \ -} while (0); +} while (0) static inline void i810_print_status_page(drm_device_t *dev) { @@ -86,6 +87,7 @@ DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]); DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]); DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]); + DRM_DEBUG( "hw_status: Last Render: %x\n", temp[4]); DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]); for(i = 6; i < dma->buf_count + 6; i++) { DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]); @@ -227,14 +229,9 @@ #else down_write( ¤t->mm->mmap_sem ); #endif -#if LINUX_VERSION_CODE < 0x020399 - retcode = do_munmap((unsigned long)buf_priv->virtual, - (size_t) buf->total); -#else retcode = do_munmap(current->mm, (unsigned long)buf_priv->virtual, (size_t) buf->total); -#endif #if LINUX_VERSION_CODE <= 0x020402 up( ¤t->mm->mmap_sem ); #else @@ -294,14 +291,14 @@ static void i810_free_page(drm_device_t *dev, unsigned long page) { - if(page == 0UL) - return; + if (page) { + struct page *p = virt_to_page(page); - atomic_dec(&virt_to_page(page)->count); - clear_bit(PG_locked, &virt_to_page(page)->flags); - wake_up(&virt_to_page(page)->wait); - free_page(page); - return; + atomic_dec(&p->count); + clear_bit(PG_locked, &p->flags); + wake_up_page(p); + free_page(page); + } } static int i810_dma_cleanup(drm_device_t *dev) @@ -476,6 +473,9 @@ dev_priv->back_offset = init->back_offset; dev_priv->depth_offset = init->depth_offset; + dev_priv->overlay_offset = init->overlay_offset; + dev_priv->overlay_physical = init->overlay_physical; + dev_priv->front_di1 = init->front_offset | init->pitch_bits; dev_priv->back_di1 = init->back_offset | init->pitch_bits; dev_priv->zi1 = init->depth_offset | init->pitch_bits; @@ -1264,3 +1264,156 @@ if(VM_DONTCOPY == 0) return 1; return 0; } + +static void i810_dma_dispatch_mc(drm_device_t *dev, drm_buf_t *buf, int used, + unsigned int last_render) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned long address = (unsigned long)buf->bus_address; + unsigned long start = address - dev->agp->base; + int u; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_HARDWARE); + if(u != I810_BUF_CLIENT) { + DRM_DEBUG("MC found buffer that isn't mine!\n"); + } + + if (used > 4*1024) + used = 0; + + sarea_priv->dirty = 0x7f; + + DRM_DEBUG("dispatch mc addr 0x%lx, used 0x%x\n", + address, used); + + dev_priv->counter++; + DRM_DEBUG("dispatch counter : %ld\n", dev_priv->counter); + DRM_DEBUG("i810_dma_dispatch_mc\n"); + DRM_DEBUG("start : %lx\n", start); + DRM_DEBUG("used : %d\n", used); + DRM_DEBUG("start + used - 4 : %ld\n", start + used - 4); + + if (buf_priv->currently_mapped == I810_BUF_MAPPED) { + if (used & 4) { + *(u32 *)((u32)buf_priv->virtual + used) = 0; + used += 4; + } + + i810_unmap_buffer(buf); + } + BEGIN_LP_RING(4); + OUT_RING( CMD_OP_BATCH_BUFFER ); + OUT_RING( start | BB1_PROTECTED ); + OUT_RING( start + used - 4 ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + + BEGIN_LP_RING(8); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( buf_priv->my_use_idx ); + OUT_RING( I810_BUF_FREE ); + OUT_RING( 0 ); + + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 16 ); + OUT_RING( last_render ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); +} + +int i810_dma_mc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + drm_i810_mc_t mc; + + if (copy_from_user(&mc, (drm_i810_mc_t *)arg, sizeof(mc))) + return -EFAULT; + + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma_mc called without lock held\n"); + return -EINVAL; + } + + i810_dma_dispatch_mc(dev, dma->buflist[mc.idx], mc.used, + mc.last_render ); + + atomic_add(mc.used, &dev->counts[_DRM_STAT_SECONDARY]); + atomic_inc(&dev->counts[_DRM_STAT_DMA]); + sarea_priv->last_enqueue = dev_priv->counter-1; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + +int i810_rstatus(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + + return (int)(((u32 *)(dev_priv->hw_status_page))[4]); +} + +int i810_ov0_info(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + drm_i810_overlay_t data; + + data.offset = dev_priv->overlay_offset; + data.physical = dev_priv->overlay_physical; + copy_to_user((drm_i810_overlay_t *)arg,&data,sizeof(data)); + return 0; +} + +int i810_fstatus(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_fstatus called without lock held\n"); + return -EINVAL; + } + return I810_READ(0x30008); +} + +int i810_ov0_flip(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_ov0_flip called without lock held\n"); + return -EINVAL; + } + + //Tell the overlay to update + I810_WRITE(0x30000,dev_priv->overlay_physical | 0x80000000); + + return 0; +} + + diff -Nru a/drivers/char/drm/i810_drm.h b/drivers/char/drm/i810_drm.h --- a/drivers/char/drm/i810_drm.h Wed Feb 13 20:03:29 2002 +++ b/drivers/char/drm/i810_drm.h Wed Feb 13 20:03:29 2002 @@ -112,6 +112,8 @@ unsigned int front_offset; unsigned int back_offset; unsigned int depth_offset; + unsigned int overlay_offset; + unsigned int overlay_physical; unsigned int w; unsigned int h; unsigned int pitch; @@ -195,5 +197,19 @@ int request_size; int granted; } drm_i810_dma_t; + +typedef struct _drm_i810_overlay_t { + unsigned int offset; /* Address of the Overlay Regs */ + unsigned int physical; +} drm_i810_overlay_t; + +typedef struct _drm_i810_mc { + int idx; /* buffer index */ + int used; /* nr bytes in use */ + int num_blocks; /* number of GFXBlocks */ + int *length; /* List of lengths for GFXBlocks (FUTURE)*/ + unsigned int last_render; /* Last Render Request */ +} drm_i810_mc_t; + #endif /* _I810_DRM_H_ */ diff -Nru a/drivers/char/drm/i810_drv.c b/drivers/char/drm/i810_drv.c --- a/drivers/char/drm/i810_drv.c Wed Feb 13 20:03:50 2002 +++ b/drivers/char/drm/i810_drv.c Wed Feb 13 20:03:50 2002 @@ -39,10 +39,10 @@ #define DRIVER_NAME "i810" #define DRIVER_DESC "Intel i810" -#define DRIVER_DATE "20010616" +#define DRIVER_DATE "20010920" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 1 +#define DRIVER_MINOR 2 #define DRIVER_PATCHLEVEL 0 #define DRIVER_IOCTLS \ @@ -54,7 +54,12 @@ [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0INFO)] = { i810_ov0_info, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_FSTATUS)] = { i810_fstatus, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0FLIP)] = { i810_ov0_flip, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_MC)] = { i810_dma_mc, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_RSTATUS)] = { i810_rstatus, 1, 0 } #define __HAVE_COUNTERS 4 diff -Nru a/drivers/char/drm/i810_drv.h b/drivers/char/drm/i810_drv.h --- a/drivers/char/drm/i810_drv.h Wed Feb 13 20:03:50 2002 +++ b/drivers/char/drm/i810_drv.h Wed Feb 13 20:03:50 2002 @@ -73,6 +73,8 @@ int back_offset; int depth_offset; + int overlay_offset; + int overlay_physical; int w, h; int pitch; } drm_i810_private_t; @@ -93,6 +95,18 @@ unsigned int cmd, unsigned long arg); extern int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +extern int i810_rstatus(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_ov0_info(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_fstatus(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_ov0_flip(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_dma_mc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + extern void i810_dma_quiescent(drm_device_t *dev); diff -Nru a/drivers/char/drm/i830.h b/drivers/char/drm/i830.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/drm/i830.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,116 @@ +/* i830.h -- Intel I830 DRM template customization -*- linux-c -*- + * Created: Thu Feb 15 00:01:12 2001 by gareth@valinux.com + * + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + */ + +#ifndef __I830_H__ +#define __I830_H__ + +/* This remains constant for all DRM template files. + */ +#define DRM(x) i830_##x + +/* General customization: + */ +#define __HAVE_AGP 1 +#define __MUST_HAVE_AGP 1 +#define __HAVE_MTRR 1 +#define __HAVE_CTX_BITMAP 1 + +/* Driver customization: + */ +#define __HAVE_RELEASE 1 +#define DRIVER_RELEASE() do { \ + i830_reclaim_buffers( dev, priv->pid ); \ +} while (0) + +/* DMA customization: + */ +#define __HAVE_DMA 1 +#define __HAVE_DMA_QUEUE 1 +#define __HAVE_DMA_WAITLIST 1 +#define __HAVE_DMA_RECLAIM 1 + +#define __HAVE_DMA_QUIESCENT 1 +#define DRIVER_DMA_QUIESCENT() do { \ + i830_dma_quiescent( dev ); \ +} while (0) + +#define __HAVE_DMA_IRQ 1 +#define __HAVE_DMA_IRQ_BH 1 +#define __HAVE_SHARED_IRQ 1 +#define DRIVER_PREINSTALL() do { \ + drm_i830_private_t *dev_priv = \ + (drm_i830_private_t *)dev->dev_private; \ + u16 tmp; \ + tmp = I830_READ16( I830REG_HWSTAM ); \ + tmp = tmp & 0x6000; \ + I830_WRITE16( I830REG_HWSTAM, tmp ); \ + \ + tmp = I830_READ16( I830REG_INT_MASK_R ); \ + tmp = tmp & 0x6000; /* Unmask interrupts */ \ + I830_WRITE16( I830REG_INT_MASK_R, tmp ); \ + tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ + tmp = tmp & 0x6000; /* Disable all interrupts */ \ + I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ +} while (0) + +#define DRIVER_POSTINSTALL() do { \ + drm_i830_private_t *dev_priv = \ + (drm_i830_private_t *)dev->dev_private; \ + u16 tmp; \ + tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ + tmp = tmp & 0x6000; \ + tmp = tmp | 0x0003; /* Enable bp & user interrupts */ \ + I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ +} while (0) + +#define DRIVER_UNINSTALL() do { \ + drm_i830_private_t *dev_priv = \ + (drm_i830_private_t *)dev->dev_private; \ + u16 tmp; \ + if ( dev_priv ) { \ + tmp = I830_READ16( I830REG_INT_IDENTITY_R ); \ + tmp = tmp & ~(0x6000); /* Clear all interrupts */ \ + if ( tmp != 0 ) \ + I830_WRITE16( I830REG_INT_IDENTITY_R, tmp ); \ + \ + tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ + tmp = tmp & 0x6000; /* Disable all interrupts */ \ + I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ + } \ +} while (0) + +/* Buffer customization: + */ + +#define DRIVER_BUF_PRIV_T drm_i830_buf_priv_t + +#define DRIVER_AGP_BUFFERS_MAP( dev ) \ + ((drm_i830_private_t *)((dev)->dev_private))->buffer_map + +#endif diff -Nru a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/drm/i830_dma.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,1418 @@ +/* i830_dma.c -- DMA support for the I830 -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * Keith Whitwell + * Abraham vd Merwe + * + */ + +#define __NO_VERSION__ +#include "i830.h" +#include "drmP.h" +#include "i830_drv.h" +#include /* For task queue support */ +#include +/* in case we don't have a 2.3.99-pre6 kernel or later: */ +#ifndef VM_DONTCOPY +#define VM_DONTCOPY 0 +#endif + +#define I830_BUF_FREE 2 +#define I830_BUF_CLIENT 1 +#define I830_BUF_HARDWARE 0 + +#define I830_BUF_UNMAPPED 0 +#define I830_BUF_MAPPED 1 + +#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; + + +#define DO_IDLE_WORKAROUND() \ +do { \ + int _head; \ + int _tail; \ + int _i; \ + do { \ + _head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; \ + _tail = I830_READ(LP_RING + RING_TAIL) & TAIL_ADDR; \ + udelay(1); \ + } while(_head != _tail); \ +} while(0) + +#define I830_SYNC_WORKAROUND 0 + +#define BEGIN_LP_RING(n) do { \ + if (I830_VERBOSE) \ + DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ + n, __FUNCTION__); \ + if (I830_SYNC_WORKAROUND) \ + DO_IDLE_WORKAROUND(); \ + if (dev_priv->ring.space < n*4) \ + i830_wait_ring(dev, n*4); \ + dev_priv->ring.space -= n*4; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ +} while (0) + +#define ADVANCE_LP_RING() do { \ + if (I830_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ + dev_priv->ring.tail = outring; \ + I830_WRITE(LP_RING + RING_TAIL, outring); \ +} while(0) + +#define OUT_RING(n) do { \ + if (I830_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ +} while (0); + +static inline void i830_print_status_page(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_i830_private_t *dev_priv = dev->dev_private; + u32 *temp = (u32 *)dev_priv->hw_status_page; + int i; + + DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]); + DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]); + DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]); + DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]); + DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]); + for(i = 9; i < dma->buf_count + 9; i++) { + DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 9, temp[i]); + } +} + +static drm_buf_t *i830_freelist_get(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i; + int used; + + /* Linear search might not be the best solution */ + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I830_BUF_FREE, + I830_BUF_CLIENT); + if(used == I830_BUF_FREE) { + return buf; + } + } + return NULL; +} + +/* This should only be called if the buffer is not sent to the hardware + * yet, the hardware updates in use for us once its on the ring buffer. + */ + +static int i830_freelist_put(drm_device_t *dev, drm_buf_t *buf) +{ + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + int used; + + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, I830_BUF_FREE); + if(used != I830_BUF_CLIENT) { + DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx); + return -EINVAL; + } + + return 0; +} + +static struct file_operations i830_buffer_fops = { + open: DRM(open), + flush: DRM(flush), + release: DRM(release), + ioctl: DRM(ioctl), + mmap: i830_mmap_buffers, + read: DRM(read), + fasync: DRM(fasync), + poll: DRM(poll), +}; + +int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_i830_private_t *dev_priv; + drm_buf_t *buf; + drm_i830_buf_priv_t *buf_priv; + + lock_kernel(); + dev = priv->dev; + dev_priv = dev->dev_private; + buf = dev_priv->mmap_buffer; + buf_priv = buf->dev_private; + + vma->vm_flags |= (VM_IO | VM_DONTCOPY); + vma->vm_file = filp; + + buf_priv->currently_mapped = I830_BUF_MAPPED; + unlock_kernel(); + + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) return -EAGAIN; + return 0; +} + +static int i830_map_buffer(drm_buf_t *buf, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + drm_i830_private_t *dev_priv = dev->dev_private; + struct file_operations *old_fops; + int retcode = 0; + + if(buf_priv->currently_mapped == I830_BUF_MAPPED) return -EINVAL; + + if(VM_DONTCOPY != 0) { +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif + old_fops = filp->f_op; + filp->f_op = &i830_buffer_fops; + dev_priv->mmap_buffer = buf; + buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, + PROT_READ|PROT_WRITE, + MAP_SHARED, + buf->bus_address); + dev_priv->mmap_buffer = NULL; + filp->f_op = old_fops; + if ((unsigned long)buf_priv->virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed int)buf_priv->virtual; + buf_priv->virtual = 0; + } +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } else { + buf_priv->virtual = buf_priv->kernel_virtual; + buf_priv->currently_mapped = I830_BUF_MAPPED; + } + return retcode; +} + +static int i830_unmap_buffer(drm_buf_t *buf) +{ + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + int retcode = 0; + + if(VM_DONTCOPY != 0) { + if(buf_priv->currently_mapped != I830_BUF_MAPPED) + return -EINVAL; +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif +#if LINUX_VERSION_CODE < 0x020399 + retcode = do_munmap((unsigned long)buf_priv->virtual, + (size_t) buf->total); +#else + retcode = do_munmap(current->mm, + (unsigned long)buf_priv->virtual, + (size_t) buf->total); +#endif +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } + buf_priv->currently_mapped = I830_BUF_UNMAPPED; + buf_priv->virtual = 0; + + return retcode; +} + +static int i830_dma_get_buffer(drm_device_t *dev, drm_i830_dma_t *d, + struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_buf_t *buf; + drm_i830_buf_priv_t *buf_priv; + int retcode = 0; + + buf = i830_freelist_get(dev); + if (!buf) { + retcode = -ENOMEM; + DRM_DEBUG("retcode=%d\n", retcode); + return retcode; + } + + retcode = i830_map_buffer(buf, filp); + if(retcode) { + i830_freelist_put(dev, buf); + DRM_DEBUG("mapbuf failed, retcode %d\n", retcode); + return retcode; + } + buf->pid = priv->pid; + buf_priv = buf->dev_private; + d->granted = 1; + d->request_idx = buf->idx; + d->request_size = buf->total; + d->virtual = buf_priv->virtual; + + return retcode; +} + +static unsigned long i830_alloc_page(drm_device_t *dev) +{ + unsigned long address; + + address = __get_free_page(GFP_KERNEL); + if(address == 0UL) + return 0; + + atomic_inc(&virt_to_page(address)->count); + set_bit(PG_locked, &virt_to_page(address)->flags); + + return address; +} + +static void i830_free_page(drm_device_t *dev, unsigned long page) +{ + if(page == 0UL) + return; + + atomic_dec(&virt_to_page(page)->count); + clear_bit(PG_locked, &virt_to_page(page)->flags); + wake_up_page(virt_to_page(page)); + free_page(page); + return; +} + +static int i830_dma_cleanup(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + if(dev->dev_private) { + int i; + drm_i830_private_t *dev_priv = + (drm_i830_private_t *) dev->dev_private; + + if(dev_priv->ring.virtual_start) { + DRM(ioremapfree)((void *) dev_priv->ring.virtual_start, + dev_priv->ring.Size); + } + if(dev_priv->hw_status_page != 0UL) { + i830_free_page(dev, dev_priv->hw_status_page); + /* Need to rewrite hardware status page */ + I830_WRITE(0x02080, 0x1ffff000); + } + DRM(free)(dev->dev_private, sizeof(drm_i830_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + DRM(ioremapfree)(buf_priv->kernel_virtual, buf->total); + } + } + return 0; +} + +static int i830_wait_ring(drm_device_t *dev, int n) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_ring_buffer_t *ring = &(dev_priv->ring); + int iters = 0; + unsigned long end; + unsigned int last_head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + + end = jiffies + (HZ*3); + while (ring->space < n) { + int i; + + ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; + + if (ring->head != last_head) { + end = jiffies + (HZ*3); + last_head = ring->head; + } + + iters++; + if(time_before(end,jiffies)) { + DRM_ERROR("space: %d wanted %d\n", ring->space, n); + DRM_ERROR("lockup\n"); + goto out_wait_ring; + } + + udelay(1); + } + +out_wait_ring: + return iters; +} + +static void i830_kernel_lost_context(drm_device_t *dev) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_ring_buffer_t *ring = &(dev_priv->ring); + + ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->tail = I830_READ(LP_RING + RING_TAIL); + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; +} + +static int i830_freelist_init(drm_device_t *dev, drm_i830_private_t *dev_priv) +{ + drm_device_dma_t *dma = dev->dma; + int my_idx = 36; + u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx); + int i; + + if(dma->buf_count > 1019) { + /* Not enough space in the status page for the freelist */ + return -EINVAL; + } + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + + buf_priv->in_use = hw_status++; + buf_priv->my_use_idx = my_idx; + my_idx += 4; + + *buf_priv->in_use = I830_BUF_FREE; + + buf_priv->kernel_virtual = DRM(ioremap)(buf->bus_address, + buf->total); + } + return 0; +} + +static int i830_dma_initialize(drm_device_t *dev, + drm_i830_private_t *dev_priv, + drm_i830_init_t *init) +{ + struct list_head *list; + + memset(dev_priv, 0, sizeof(drm_i830_private_t)); + + list_for_each(list, &dev->maplist->head) { + drm_map_list_t *r_list = (drm_map_list_t *)list; + if( r_list->map && + r_list->map->type == _DRM_SHM && + r_list->map->flags & _DRM_CONTAINS_LOCK ) { + dev_priv->sarea_map = r_list->map; + break; + } + } + + if(!dev_priv->sarea_map) { + dev->dev_private = (void *)dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("can not find sarea!\n"); + return -EINVAL; + } + DRM_FIND_MAP( dev_priv->mmio_map, init->mmio_offset ); + if(!dev_priv->mmio_map) { + dev->dev_private = (void *)dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("can not find mmio map!\n"); + return -EINVAL; + } + DRM_FIND_MAP( dev_priv->buffer_map, init->buffers_offset ); + if(!dev_priv->buffer_map) { + dev->dev_private = (void *)dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("can not find dma buffer map!\n"); + return -EINVAL; + } + + dev_priv->sarea_priv = (drm_i830_sarea_t *) + ((u8 *)dev_priv->sarea_map->handle + + init->sarea_priv_offset); + + atomic_set(&dev_priv->flush_done, 0); + init_waitqueue_head(&dev_priv->flush_queue); + + dev_priv->ring.Start = init->ring_start; + dev_priv->ring.End = init->ring_end; + dev_priv->ring.Size = init->ring_size; + + dev_priv->ring.virtual_start = DRM(ioremap)(dev->agp->base + + init->ring_start, + init->ring_size); + + if (dev_priv->ring.virtual_start == NULL) { + dev->dev_private = (void *) dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return -ENOMEM; + } + + dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; + + dev_priv->w = init->w; + dev_priv->h = init->h; + dev_priv->pitch = init->pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->depth_offset = init->depth_offset; + + dev_priv->front_di1 = init->front_offset | init->pitch_bits; + dev_priv->back_di1 = init->back_offset | init->pitch_bits; + dev_priv->zi1 = init->depth_offset | init->pitch_bits; + + dev_priv->cpp = init->cpp; + /* We are using seperate values as placeholders for mechanisms for + * private backbuffer/depthbuffer usage. + */ + + dev_priv->back_pitch = init->back_pitch; + dev_priv->depth_pitch = init->depth_pitch; + + /* Program Hardware Status Page */ + dev_priv->hw_status_page = i830_alloc_page(dev); + if(dev_priv->hw_status_page == 0UL) { + dev->dev_private = (void *)dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("Can not allocate hardware status page\n"); + return -ENOMEM; + } + memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); + DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); + + I830_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page)); + DRM_DEBUG("Enabled hardware status page\n"); + + /* Now we need to init our freelist */ + if(i830_freelist_init(dev, dev_priv) != 0) { + dev->dev_private = (void *)dev_priv; + i830_dma_cleanup(dev); + DRM_ERROR("Not enough space in the status page for" + " the freelist\n"); + return -ENOMEM; + } + dev->dev_private = (void *)dev_priv; + + return 0; +} + +int i830_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv; + drm_i830_init_t init; + int retcode = 0; + + if (copy_from_user(&init, (drm_i830_init_t *)arg, sizeof(init))) + return -EFAULT; + + switch(init.func) { + case I830_INIT_DMA: + dev_priv = DRM(alloc)(sizeof(drm_i830_private_t), + DRM_MEM_DRIVER); + if(dev_priv == NULL) return -ENOMEM; + retcode = i830_dma_initialize(dev, dev_priv, &init); + break; + case I830_CLEANUP_DMA: + retcode = i830_dma_cleanup(dev); + break; + default: + retcode = -EINVAL; + break; + } + + return retcode; +} + +/* Most efficient way to verify state for the i830 is as it is + * emitted. Non-conformant state is silently dropped. + * + * Use 'volatile' & local var tmp to force the emitted values to be + * identical to the verified ones. + */ +static void i830EmitContextVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I830_CTX_SETUP_SIZE ); + for ( i = 0 ; i < I830_CTX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + +#if 0 + if ((tmp & (7<<29)) == (3<<29) && + (tmp & (0x1f<<24)) < (0x1d<<24)) { + OUT_RING( tmp ); + j++; + } else { + printk("Skipping %d\n", i); + } +#else + OUT_RING( tmp ); + j++; +#endif + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + +static void i830EmitTexVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I830_TEX_SETUP_SIZE ); + + OUT_RING( GFX_OP_MAP_INFO ); + OUT_RING( code[I830_TEXREG_MI1] ); + OUT_RING( code[I830_TEXREG_MI2] ); + OUT_RING( code[I830_TEXREG_MI3] ); + OUT_RING( code[I830_TEXREG_MI4] ); + OUT_RING( code[I830_TEXREG_MI5] ); + + for ( i = 6 ; i < I830_TEX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + OUT_RING( tmp ); + j++; + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + +static void i830EmitTexBlendVerified( drm_device_t *dev, + volatile unsigned int *code, + volatile unsigned int num) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( num ); + + for ( i = 0 ; i < num ; i++ ) { + tmp = code[i]; + OUT_RING( tmp ); + j++; + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + +static void i830EmitTexPalette( drm_device_t *dev, + unsigned int *palette, + int number, + int is_shared ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + + BEGIN_LP_RING( 258 ); + + if(is_shared == 1) { + OUT_RING(CMD_OP_MAP_PALETTE_LOAD | + MAP_PALETTE_NUM(0) | + MAP_PALETTE_BOTH); + } else { + OUT_RING(CMD_OP_MAP_PALETTE_LOAD | MAP_PALETTE_NUM(number)); + } + for(i = 0; i < 256; i++) { + OUT_RING(palette[i]); + } + OUT_RING(0); +} + +/* Need to do some additional checking when setting the dest buffer. + */ +static void i830EmitDestVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I830_DEST_SETUP_SIZE + 6 ); + + tmp = code[I830_DESTREG_CBUFADDR]; + if (tmp == dev_priv->front_di1) { + /* Don't use fence when front buffer rendering */ + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( BUF_3D_ID_COLOR_BACK | + BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) ); + OUT_RING( tmp ); + + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( BUF_3D_ID_DEPTH | + BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp)); + OUT_RING( dev_priv->zi1 ); + } else if(tmp == dev_priv->back_di1) { + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( BUF_3D_ID_COLOR_BACK | + BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) | + BUF_3D_USE_FENCE); + OUT_RING( tmp ); + + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( BUF_3D_ID_DEPTH | BUF_3D_USE_FENCE | + BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp)); + OUT_RING( dev_priv->zi1 ); + } else { + DRM_DEBUG("bad di1 %x (allow %x or %x)\n", + tmp, dev_priv->front_di1, dev_priv->back_di1); + } + + /* invarient: + */ + + + OUT_RING( GFX_OP_DESTBUFFER_VARS ); + OUT_RING( code[I830_DESTREG_DV1] ); + + OUT_RING( GFX_OP_DRAWRECT_INFO ); + OUT_RING( code[I830_DESTREG_DR1] ); + OUT_RING( code[I830_DESTREG_DR2] ); + OUT_RING( code[I830_DESTREG_DR3] ); + OUT_RING( code[I830_DESTREG_DR4] ); + + /* Need to verify this */ + tmp = code[I830_DESTREG_SENABLE]; + if((tmp & ~0x3) == GFX_OP_SCISSOR_ENABLE) { + OUT_RING( tmp ); + } else { + DRM_DEBUG("bad scissor enable\n"); + OUT_RING( 0 ); + } + + OUT_RING( code[I830_DESTREG_SENABLE] ); + + OUT_RING( GFX_OP_SCISSOR_RECT ); + OUT_RING( code[I830_DESTREG_SR1] ); + OUT_RING( code[I830_DESTREG_SR2] ); + + ADVANCE_LP_RING(); +} + +static void i830EmitState( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + if (dirty & I830_UPLOAD_BUFFERS) { + i830EmitDestVerified( dev, sarea_priv->BufferState ); + sarea_priv->dirty &= ~I830_UPLOAD_BUFFERS; + } + + if (dirty & I830_UPLOAD_CTX) { + i830EmitContextVerified( dev, sarea_priv->ContextState ); + sarea_priv->dirty &= ~I830_UPLOAD_CTX; + } + + if (dirty & I830_UPLOAD_TEX0) { + i830EmitTexVerified( dev, sarea_priv->TexState[0] ); + sarea_priv->dirty &= ~I830_UPLOAD_TEX0; + } + + if (dirty & I830_UPLOAD_TEX1) { + i830EmitTexVerified( dev, sarea_priv->TexState[1] ); + sarea_priv->dirty &= ~I830_UPLOAD_TEX1; + } + + if (dirty & I830_UPLOAD_TEXBLEND0) { + i830EmitTexBlendVerified( dev, sarea_priv->TexBlendState[0], + sarea_priv->TexBlendStateWordsUsed[0]); + sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND0; + } + + if (dirty & I830_UPLOAD_TEXBLEND1) { + i830EmitTexBlendVerified( dev, sarea_priv->TexBlendState[1], + sarea_priv->TexBlendStateWordsUsed[1]); + sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND1; + } + + if (dirty & I830_UPLOAD_TEX_PALETTE_SHARED) { + i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1); + } else { + if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) { + i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0); + } + if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) { + i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1); + } + } +} + +static void i830_dma_dispatch_clear( drm_device_t *dev, int flags, + unsigned int clear_color, + unsigned int clear_zval, + unsigned int clear_depthmask) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = dev_priv->cpp; + int i; + unsigned int BR13, CMD, D_CMD; + RING_LOCALS; + + i830_kernel_lost_context(dev); + + switch(cpp) { + case 2: + BR13 = (0xF0 << 16) | (pitch * cpp) | (1<<24); + D_CMD = CMD = XY_COLOR_BLT_CMD; + break; + case 4: + BR13 = (0xF0 << 16) | (pitch * cpp) | (1<<24) | (1<<25); + CMD = (XY_COLOR_BLT_CMD | XY_COLOR_BLT_WRITE_ALPHA | + XY_COLOR_BLT_WRITE_RGB); + D_CMD = XY_COLOR_BLT_CMD; + if(clear_depthmask & 0x00ffffff) + D_CMD |= XY_COLOR_BLT_WRITE_RGB; + if(clear_depthmask & 0xff000000) + D_CMD |= XY_COLOR_BLT_WRITE_ALPHA; + break; + default: + BR13 = (0xF0 << 16) | (pitch * cpp) | (1<<24); + D_CMD = CMD = XY_COLOR_BLT_CMD; + break; + } + + if (nbox > I830_NR_SAREA_CLIPRECTS) + nbox = I830_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox ; i++, pbox++) { + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + if ( flags & I830_FRONT ) { + DRM_DEBUG("clear front\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( CMD ); + OUT_RING( BR13 ); + OUT_RING( (pbox->y1 << 16) | pbox->x1 ); + OUT_RING( (pbox->y2 << 16) | pbox->x2 ); + OUT_RING( 0 ); + OUT_RING( clear_color ); + ADVANCE_LP_RING(); + } + + if ( flags & I830_BACK ) { + DRM_DEBUG("clear back\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( CMD ); + OUT_RING( BR13 ); + OUT_RING( (pbox->y1 << 16) | pbox->x1 ); + OUT_RING( (pbox->y2 << 16) | pbox->x2 ); + OUT_RING( dev_priv->back_offset ); + OUT_RING( clear_color ); + ADVANCE_LP_RING(); + } + + if ( flags & I830_DEPTH ) { + DRM_DEBUG("clear depth\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( D_CMD ); + OUT_RING( BR13 ); + OUT_RING( (pbox->y1 << 16) | pbox->x1 ); + OUT_RING( (pbox->y2 << 16) | pbox->x2 ); + OUT_RING( dev_priv->depth_offset ); + OUT_RING( clear_zval ); + ADVANCE_LP_RING(); + } + } +} + +static void i830_dma_dispatch_swap( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = dev_priv->cpp; + int ofs = dev_priv->back_offset; + int i; + unsigned int CMD, BR13; + RING_LOCALS; + + DRM_DEBUG("swapbuffers\n"); + + switch(cpp) { + case 2: + BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24); + CMD = XY_SRC_COPY_BLT_CMD; + break; + case 4: + BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24) | (1<<25); + CMD = (XY_SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB); + break; + default: + BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24); + CMD = XY_SRC_COPY_BLT_CMD; + break; + } + + i830_kernel_lost_context(dev); + + if (nbox > I830_NR_SAREA_CLIPRECTS) + nbox = I830_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox; i++, pbox++) + { + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", + pbox->x1, pbox->y1, + pbox->x2, pbox->y2); + + BEGIN_LP_RING( 8 ); + OUT_RING( CMD ); + OUT_RING( BR13 ); + + OUT_RING( (pbox->y1 << 16) | + pbox->x1 ); + OUT_RING( (pbox->y2 << 16) | + pbox->x2 ); + + OUT_RING( 0 /* front ofs always zero */ ); + OUT_RING( (pbox->y1 << 16) | + pbox->x1 ); + + OUT_RING( BR13 & 0xffff ); + OUT_RING( ofs ); + + ADVANCE_LP_RING(); + } +} + + +static void i830_dma_dispatch_vertex(drm_device_t *dev, + drm_buf_t *buf, + int discard, + int used) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_clip_rect_t *box = sarea_priv->boxes; + int nbox = sarea_priv->nbox; + unsigned long address = (unsigned long)buf->bus_address; + unsigned long start = address - dev->agp->base; + int i = 0, u; + RING_LOCALS; + + i830_kernel_lost_context(dev); + + if (nbox > I830_NR_SAREA_CLIPRECTS) + nbox = I830_NR_SAREA_CLIPRECTS; + + if (discard) { + u = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, + I830_BUF_HARDWARE); + if(u != I830_BUF_CLIENT) { + DRM_DEBUG("xxxx 2\n"); + } + } + + if (used > 4*1024) + used = 0; + + if (sarea_priv->dirty) + i830EmitState( dev ); + + DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", + address, used, nbox); + + dev_priv->counter++; + DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); + DRM_DEBUG( "i830_dma_dispatch\n"); + DRM_DEBUG( "start : %lx\n", start); + DRM_DEBUG( "used : %d\n", used); + DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4); + + if (buf_priv->currently_mapped == I830_BUF_MAPPED) { + *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | + sarea_priv->vertex_prim | + ((used/4)-2)); + + if (used & 4) { + *(u32 *)((u32)buf_priv->virtual + used) = 0; + used += 4; + } + + i830_unmap_buffer(buf); + } + + if (used) { + do { + if (i < nbox) { + BEGIN_LP_RING(6); + OUT_RING( GFX_OP_DRAWRECT_INFO ); + OUT_RING( sarea_priv->BufferState[I830_DESTREG_DR1] ); + OUT_RING( box[i].x1 | (box[i].y1<<16) ); + OUT_RING( box[i].x2 | (box[i].y2<<16) ); + OUT_RING( sarea_priv->BufferState[I830_DESTREG_DR4] ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + + BEGIN_LP_RING(4); + + OUT_RING( MI_BATCH_BUFFER ); + OUT_RING( start | MI_BATCH_NON_SECURE ); + OUT_RING( start + used - 4 ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + } while (++i < nbox); + } + + BEGIN_LP_RING(10); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 20 ); + OUT_RING( dev_priv->counter ); + OUT_RING( 0 ); + + if (discard) { + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( buf_priv->my_use_idx ); + OUT_RING( I830_BUF_FREE ); + OUT_RING( 0 ); + } + + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); +} + +/* Interrupts are only for flushing */ +void i830_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u16 temp; + + temp = I830_READ16(I830REG_INT_IDENTITY_R); + temp = temp & ~(0x6000); + if(temp != 0) I830_WRITE16(I830REG_INT_IDENTITY_R, + temp); /* Clear all interrupts */ + else + return; + + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void DRM(dma_immediate_bh)(void *device) +{ + drm_device_t *dev = (drm_device_t *) device; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + + atomic_set(&dev_priv->flush_done, 1); + wake_up_interruptible(&dev_priv->flush_queue); +} + +static inline void i830_dma_emit_flush(drm_device_t *dev) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i830_kernel_lost_context(dev); + + BEGIN_LP_RING(2); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + + i830_wait_ring( dev, dev_priv->ring.Size - 8 ); + atomic_set(&dev_priv->flush_done, 1); + wake_up_interruptible(&dev_priv->flush_queue); +} + +static inline void i830_dma_quiescent_emit(drm_device_t *dev) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i830_kernel_lost_context(dev); + + BEGIN_LP_RING(4); + OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + + i830_wait_ring( dev, dev_priv->ring.Size - 8 ); + atomic_set(&dev_priv->flush_done, 1); + wake_up_interruptible(&dev_priv->flush_queue); +} + +void i830_dma_quiescent(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + unsigned long end; + + if(dev_priv == NULL) { + return; + } + atomic_set(&dev_priv->flush_done, 0); + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + + for (;;) { + current->state = TASK_INTERRUPTIBLE; + i830_dma_quiescent_emit(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if(time_before(end, jiffies)) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + return; +} + +static int i830_flush_queue(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + drm_device_dma_t *dma = dev->dma; + unsigned long end; + int i, ret = 0; + + if(dev_priv == NULL) { + return 0; + } + atomic_set(&dev_priv->flush_done, 0); + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + i830_dma_emit_flush(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if(time_before(end, jiffies)) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + + int used = cmpxchg(buf_priv->in_use, I830_BUF_HARDWARE, + I830_BUF_FREE); + + if (used == I830_BUF_HARDWARE) + DRM_DEBUG("reclaimed from HARDWARE\n"); + if (used == I830_BUF_CLIENT) + DRM_DEBUG("still on client HARDWARE\n"); + } + + return ret; +} + +/* Must be called with the lock held */ +void i830_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + if (!dev->dev_private) return; + if (!dma->buflist) return; + + i830_flush_queue(dev); + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i830_buf_priv_t *buf_priv = buf->dev_private; + + if (buf->pid == pid && buf_priv) { + int used = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, + I830_BUF_FREE); + + if (used == I830_BUF_CLIENT) + DRM_DEBUG("reclaimed from client\n"); + if(buf_priv->currently_mapped == I830_BUF_MAPPED) + buf_priv->currently_mapped = I830_BUF_UNMAPPED; + } + } +} + +int i830_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i830_flush_ioctl\n"); + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_flush_ioctl called without lock held\n"); + return -EINVAL; + } + + i830_flush_queue(dev); + return 0; +} + +int i830_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) + dev_priv->sarea_priv; + drm_i830_vertex_t vertex; + + if (copy_from_user(&vertex, (drm_i830_vertex_t *)arg, sizeof(vertex))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_dma_vertex called without lock held\n"); + return -EINVAL; + } + + DRM_DEBUG("i830 dma vertex, idx %d used %d discard %d\n", + vertex.idx, vertex.used, vertex.discard); + + if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL; + + i830_dma_dispatch_vertex( dev, + dma->buflist[ vertex.idx ], + vertex.discard, vertex.used ); + + sarea_priv->last_enqueue = dev_priv->counter-1; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + +int i830_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_clear_t clear; + + if (copy_from_user(&clear, (drm_i830_clear_t *)arg, sizeof(clear))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_clear_bufs called without lock held\n"); + return -EINVAL; + } + + /* GH: Someone's doing nasty things... */ + if (!dev->dev_private) { + return -EINVAL; + } + + i830_dma_dispatch_clear( dev, clear.flags, + clear.clear_color, + clear.clear_depth, + clear.clear_depthmask); + return 0; +} + +int i830_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i830_swap_bufs\n"); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_swap_buf called without lock held\n"); + return -EINVAL; + } + + i830_dma_dispatch_swap( dev ); + return 0; +} + +int i830_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) + dev_priv->sarea_priv; + + sarea_priv->last_dispatch = (int) hw_status[5]; + return 0; +} + +int i830_getbuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_i830_dma_t d; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) + dev_priv->sarea_priv; + + DRM_DEBUG("getbuf\n"); + if (copy_from_user(&d, (drm_i830_dma_t *)arg, sizeof(d))) + return -EFAULT; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_dma called without lock held\n"); + return -EINVAL; + } + + d.granted = 0; + + retcode = i830_dma_get_buffer(dev, &d, filp); + + DRM_DEBUG("i830_dma: %d returning %d, granted = %d\n", + current->pid, retcode, d.granted); + + if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d))) + return -EFAULT; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return retcode; +} + +int i830_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_copy_t d; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) + dev_priv->sarea_priv; + drm_buf_t *buf; + drm_i830_buf_priv_t *buf_priv; + drm_device_dma_t *dma = dev->dma; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_dma called without lock held\n"); + return -EINVAL; + } + + if (copy_from_user(&d, (drm_i830_copy_t *)arg, sizeof(d))) + return -EFAULT; + + if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL; + buf = dma->buflist[ d.idx ]; + buf_priv = buf->dev_private; + if (buf_priv->currently_mapped != I830_BUF_MAPPED) return -EPERM; + + if(d.used < 0 || d.used > buf->total) return -EINVAL; + + if (copy_from_user(buf_priv->virtual, d.address, d.used)) + return -EFAULT; + + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + +int i830_docopy(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + if(VM_DONTCOPY == 0) return 1; + return 0; +} diff -Nru a/drivers/char/drm/i830_drm.h b/drivers/char/drm/i830_drm.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/drm/i830_drm.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,238 @@ +#ifndef _I830_DRM_H_ +#define _I830_DRM_H_ + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _I830_DEFINES_ +#define _I830_DEFINES_ + +#define I830_DMA_BUF_ORDER 12 +#define I830_DMA_BUF_SZ (1< + * Jeff Hartmann + * Gareth Hughes + * Abraham vd Merwe + */ + +#include +#include "i830.h" +#include "drmP.h" +#include "i830_drv.h" + +#define DRIVER_AUTHOR "VA Linux Systems Inc." + +#define DRIVER_NAME "i830" +#define DRIVER_DESC "Intel 830M" +#define DRIVER_DATE "20011004" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 0 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_INIT)] = { i830_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_VERTEX)] = { i830_dma_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_CLEAR)] = { i830_clear_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_FLUSH)] = { i830_flush_ioctl, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_GETAGE)] = { i830_getage, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_GETBUF)] = { i830_getbuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_SWAP)] = { i830_swap_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_COPY)] = { i830_copybuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_DOCOPY)] = { i830_docopy, 1, 0 }, + +#define __HAVE_COUNTERS 4 +#define __HAVE_COUNTER6 _DRM_STAT_IRQ +#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY +#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY +#define __HAVE_COUNTER9 _DRM_STAT_DMA + + +#include "drm_agpsupport.h" +#include "drm_auth.h" +#include "drm_bufs.h" +#include "drm_context.h" +#include "drm_dma.h" +#include "drm_drawable.h" +#include "drm_drv.h" + +#ifndef MODULE +/* DRM(options) is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +/* JH- We have to hand expand the string ourselves because of the cpp. If + * anyone can think of a way that we can fit into the __setup macro without + * changing it, then please send the solution my way. + */ +static int __init i830_options( char *str ) +{ + DRM(parse_options)( str ); + return 1; +} + +__setup( DRIVER_NAME "=", i830_options ); +#endif + +#include "drm_fops.h" +#include "drm_init.h" +#include "drm_ioctl.h" +#include "drm_lock.h" +#include "drm_lists.h" +#include "drm_memory.h" +#include "drm_proc.h" +#include "drm_vm.h" +#include "drm_stub.h" diff -Nru a/drivers/char/drm/i830_drv.h b/drivers/char/drm/i830_drv.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/drm/i830_drv.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,213 @@ +/* i830_drv.h -- Private header for the I830 driver -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#ifndef _I830_DRV_H_ +#define _I830_DRV_H_ + +typedef struct drm_i830_buf_priv { + u32 *in_use; + int my_use_idx; + int currently_mapped; + void *virtual; + void *kernel_virtual; + int map_count; + struct vm_area_struct *vma; +} drm_i830_buf_priv_t; + +typedef struct _drm_i830_ring_buffer{ + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; +} drm_i830_ring_buffer_t; + +typedef struct drm_i830_private { + drm_map_t *sarea_map; + drm_map_t *buffer_map; + drm_map_t *mmio_map; + + drm_i830_sarea_t *sarea_priv; + drm_i830_ring_buffer_t ring; + + unsigned long hw_status_page; + unsigned long counter; + + atomic_t flush_done; + wait_queue_head_t flush_queue; /* Processes waiting until flush */ + drm_buf_t *mmap_buffer; + + u32 front_di1, back_di1, zi1; + + int back_offset; + int depth_offset; + int w, h; + int pitch; + int back_pitch; + int depth_pitch; + unsigned int cpp; +} drm_i830_private_t; + + /* i830_dma.c */ +extern int i830_dma_schedule(drm_device_t *dev, int locked); +extern int i830_getbuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i830_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i830_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern void i830_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int i830_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +extern int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma); +extern int i830_copybuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i830_docopy(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern void i830_dma_quiescent(drm_device_t *dev); + +extern int i830_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i830_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i830_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +#define I830_VERBOSE 0 + +#define I830_BASE(reg) ((unsigned long) \ + dev_priv->mmio_map->handle) +#define I830_ADDR(reg) (I830_BASE(reg) + reg) +#define I830_DEREF(reg) *(__volatile__ int *)I830_ADDR(reg) +#define I830_READ(reg) I830_DEREF(reg) +#define I830_WRITE(reg,val) do { I830_DEREF(reg) = val; } while (0) +#define I830_DEREF16(reg) *(__volatile__ u16 *)I830_ADDR(reg) +#define I830_READ16(reg) I830_DEREF16(reg) +#define I830_WRITE16(reg,val) do { I830_DEREF16(reg) = val; } while (0) + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + +#define INST_PARSER_CLIENT 0x00000000 +#define INST_OP_FLUSH 0x02000000 +#define INST_FLUSH_MAP_CACHE 0x00000001 + + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I830REG_HWSTAM 0x02098 +#define I830REG_INT_IDENTITY_R 0x020a4 +#define I830REG_INT_MASK_R 0x020a8 +#define I830REG_INT_ENABLE_R 0x020a0 + +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x00FFFFF8 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x000FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + +#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define SC_UPDATE_SCISSOR (0x1<<1) +#define SC_ENABLE_MASK (0x1<<0) +#define SC_ENABLE (0x1<<0) + +#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) +#define SCI_YMIN_MASK (0xffff<<16) +#define SCI_XMIN_MASK (0xffff<<0) +#define SCI_YMAX_MASK (0xffff<<16) +#define SCI_XMAX_MASK (0xffff<<0) + +#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1) +#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4) +#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) +#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_PRIMITIVE ((0x3<<29)|(0x1f<<24)) + +#define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) + + +#define BR00_BITBLT_CLIENT 0x40000000 +#define BR00_OP_COLOR_BLT 0x10000000 +#define BR00_OP_SRC_COPY_BLT 0x10C00000 +#define BR13_SOLID_PATTERN 0x80000000 + +#define BUF_3D_ID_COLOR_BACK (0x3<<24) +#define BUF_3D_ID_DEPTH (0x7<<24) +#define BUF_3D_USE_FENCE (1<<23) +#define BUF_3D_PITCH(x) (((x)/4)<<2) + +#define CMD_OP_MAP_PALETTE_LOAD ((3<<29)|(0x1d<<24)|(0x82<<16)|255) +#define MAP_PALETTE_NUM(x) ((x<<8) & (1<<8)) +#define MAP_PALETTE_BOTH (1<<11) + +#define XY_COLOR_BLT_CMD ((2<<29)|(0x50<<22)|0x4) +#define XY_COLOR_BLT_WRITE_ALPHA (1<<21) +#define XY_COLOR_BLT_WRITE_RGB (1<<20) + +#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) +#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) +#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) + +#define MI_BATCH_BUFFER ((0x30<<23)|1) +#define MI_BATCH_NON_SECURE (1) + + +#endif + diff -Nru a/drivers/char/drm/picker.c b/drivers/char/drm/picker.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/drm/picker.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,30 @@ + +#include +#include + +#ifndef CONFIG_SMP +#define CONFIG_SMP 0 +#endif + +#ifndef CONFIG_MODULES +#define CONFIG_MODULES 0 +#endif + +#ifndef CONFIG_MODVERSIONS +#define CONFIG_MODVERSIONS 0 +#endif + +#ifndef CONFIG_AGP_MODULE +#define CONFIG_AGP_MODULE 0 +#endif + +#ifndef CONFIG_AGP +#define CONFIG_AGP 0 +#endif + +SMP = CONFIG_SMP +MODULES = CONFIG_MODULES +MODVERSIONS = CONFIG_MODVERSIONS +AGP = CONFIG_AGP +AGP_MODULE = CONFIG_AGP_MODULE +RELEASE = UTS_RELEASE diff -Nru a/drivers/char/drm/r128_drv.c b/drivers/char/drm/r128_drv.c --- a/drivers/char/drm/r128_drv.c Wed Feb 13 20:03:42 2002 +++ b/drivers/char/drm/r128_drv.c Wed Feb 13 20:03:42 2002 @@ -39,11 +39,11 @@ #define DRIVER_NAME "r128" #define DRIVER_DESC "ATI Rage 128" -#define DRIVER_DATE "20010405" +#define DRIVER_DATE "20010917" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 6 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 0 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ diff -Nru a/drivers/char/drm/r128_state.c b/drivers/char/drm/r128_state.c --- a/drivers/char/drm/r128_state.c Wed Feb 13 20:03:36 2002 +++ b/drivers/char/drm/r128_state.c Wed Feb 13 20:03:36 2002 @@ -1519,10 +1519,75 @@ { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_indirect_t indirect; +#if 0 + RING_LOCALS; +#endif LOCK_TEST_WITH_RETURN( dev ); - /* Indirect buffer firing is not supported at this time. + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + if ( copy_from_user( &indirect, (drm_r128_indirect_t *)arg, + sizeof(indirect) ) ) + return -EFAULT; + + DRM_DEBUG( "indirect: idx=%d s=%d e=%d d=%d\n", + indirect.idx, indirect.start, + indirect.end, indirect.discard ); + + if ( indirect.idx < 0 || indirect.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + indirect.idx, dma->buf_count - 1 ); + return -EINVAL; + } + + buf = dma->buflist[indirect.idx]; + buf_priv = buf->dev_private; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", indirect.idx ); + return -EINVAL; + } + + if ( indirect.start < buf->used ) { + DRM_ERROR( "reusing indirect: start=0x%x actual=0x%x\n", + indirect.start, buf->used ); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN( dev_priv ); + VB_AGE_TEST_WITH_RETURN( dev_priv ); + + buf->used = indirect.end; + buf_priv->discard = indirect.discard; + +#if 0 + /* Wait for the 3D stream to idle before the indirect buffer + * containing 2D acceleration commands is processed. */ - return -EINVAL; + BEGIN_RING( 2 ); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); +#endif + + /* Dispatch the indirect buffer full of commands from the + * X server. This is insecure and is thus only available to + * privileged clients. + */ + r128_cce_dispatch_indirect( dev, buf, indirect.start, indirect.end ); + + return 0; } diff -Nru a/drivers/char/drm/sis_ds.c b/drivers/char/drm/sis_ds.c --- a/drivers/char/drm/sis_ds.c Wed Feb 13 20:03:41 2002 +++ b/drivers/char/drm/sis_ds.c Wed Feb 13 20:03:41 2002 @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include diff -Nru a/drivers/char/drm-4.0/ffb_drv.c b/drivers/char/drm-4.0/ffb_drv.c --- a/drivers/char/drm-4.0/ffb_drv.c Wed Feb 13 20:03:34 2002 +++ b/drivers/char/drm-4.0/ffb_drv.c Wed Feb 13 20:03:34 2002 @@ -710,8 +710,7 @@ /* Contention */ atomic_inc(&dev->total_sleeps); current->state = TASK_INTERRUPTIBLE; - current->policy |= SCHED_YIELD; - schedule(); + yield(); if (signal_pending(current)) { ret = -ERESTARTSYS; break; diff -Nru a/drivers/char/drm-4.0/i810_dma.c b/drivers/char/drm-4.0/i810_dma.c --- a/drivers/char/drm-4.0/i810_dma.c Wed Feb 13 20:03:34 2002 +++ b/drivers/char/drm-4.0/i810_dma.c Wed Feb 13 20:03:34 2002 @@ -83,7 +83,7 @@ *(volatile unsigned int *)(virt + outring) = n; \ outring += 4; \ outring &= ringmask; \ -} while (0); +} while (0) static inline void i810_print_status_page(drm_device_t *dev) { @@ -293,7 +293,7 @@ atomic_dec(&virt_to_page(page)->count); clear_bit(PG_locked, &virt_to_page(page)->flags); - wake_up(&virt_to_page(page)->wait); + wake_up_page(page); free_page(page); return; } diff -Nru a/drivers/char/drm-4.0/tdfx_drv.c b/drivers/char/drm-4.0/tdfx_drv.c --- a/drivers/char/drm-4.0/tdfx_drv.c Wed Feb 13 20:03:35 2002 +++ b/drivers/char/drm-4.0/tdfx_drv.c Wed Feb 13 20:03:35 2002 @@ -554,7 +554,6 @@ lock.context, current->pid, j, dev->lock.lock_time, jiffies); current->state = TASK_INTERRUPTIBLE; - current->policy |= SCHED_YIELD; schedule_timeout(DRM_LOCK_SLICE-j); DRM_DEBUG("jiffies=%d\n", jiffies); } @@ -578,10 +577,7 @@ /* Contention */ atomic_inc(&dev->total_sleeps); -#if 1 - current->policy |= SCHED_YIELD; -#endif - schedule(); + yield(); if (signal_pending(current)) { ret = -ERESTARTSYS; break; @@ -604,8 +600,7 @@ when dev->last_context == lock.context NOTE WE HOLD THE LOCK THROUGHOUT THIS TIME! */ - current->policy |= SCHED_YIELD; - schedule(); + yield(); current->state = TASK_RUNNING; remove_wait_queue(&dev->context_wait, &entry); if (signal_pending(current)) { diff -Nru a/drivers/char/mem.c b/drivers/char/mem.c --- a/drivers/char/mem.c Wed Feb 13 20:03:55 2002 +++ b/drivers/char/mem.c Wed Feb 13 20:03:55 2002 @@ -26,6 +26,9 @@ #include #include +#ifdef CONFIG_SENSORS +extern void sensors_init_all(void); +#endif #ifdef CONFIG_I2C extern int i2c_init_all(void); #endif @@ -400,7 +403,7 @@ if (count > size) count = size; - zap_page_range(mm, addr, count); + zap_page_range(mm, addr, count, ZPR_NORMAL); zeromap_page_range(addr, count, PAGE_COPY); size -= count; @@ -672,6 +675,10 @@ #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR) tapechar_init(); #endif +#ifdef CONFIG_SENSORS + sensors_init_all(); +#endif + return 0; } diff -Nru a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c --- a/drivers/char/mwave/mwavedd.c Wed Feb 13 20:03:42 2002 +++ b/drivers/char/mwave/mwavedd.c Wed Feb 13 20:03:42 2002 @@ -279,7 +279,6 @@ pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - current->nice = -20; /* boost to provide priority timing */ #else current->priority = 0x28; /* boost to provide priority timing */ #endif diff -Nru a/drivers/char/mxser.c b/drivers/char/mxser.c --- a/drivers/char/mxser.c Wed Feb 13 20:03:35 2002 +++ b/drivers/char/mxser.c Wed Feb 13 20:03:35 2002 @@ -32,6 +32,9 @@ * version : 1.2 * * Fixes for C104H/PCI by Tim Hockin + * Added support for: C102, CI-132, CI-134, CP-132, CP-114, CT-114 cards + * by Damian Wrobel + * */ #include @@ -62,7 +65,7 @@ #include #include -#define MXSER_VERSION "1.2" +#define MXSER_VERSION "1.2.1" #define MXSERMAJOR 174 #define MXSERCUMAJOR 175 @@ -115,10 +118,22 @@ #ifndef PCI_DEVICE_ID_C104 #define PCI_DEVICE_ID_C104 0x1040 #endif +#ifndef PCI_DEVICE_ID_CP132 +#define PCI_DEVICE_ID_CP132 0x1320 +#endif +#ifndef PCI_DEVICE_ID_CP114 +#define PCI_DEVICE_ID_CP114 0x1141 +#endif +#ifndef PCI_DEVICE_ID_CT114 +#define PCI_DEVICE_ID_CT114 0x1140 +#endif #define C168_ASIC_ID 1 #define C104_ASIC_ID 2 +#define CI134_ASIC_ID 3 +#define CI132_ASIC_ID 4 #define CI104J_ASIC_ID 5 +#define C102_ASIC_ID 0xB enum { MXSER_BOARD_C168_ISA = 0, @@ -126,6 +141,12 @@ MXSER_BOARD_CI104J, MXSER_BOARD_C168_PCI, MXSER_BOARD_C104_PCI, + MXSER_BOARD_C102_ISA, + MXSER_BOARD_CI132, + MXSER_BOARD_CI134, + MXSER_BOARD_CP132_PCI, + MXSER_BOARD_CP114_PCI, + MXSER_BOARD_CT114_PCI }; static char *mxser_brdname[] = @@ -135,6 +156,12 @@ "CI-104J series", "C168H/PCI series", "C104H/PCI series", + "C102 series", + "CI-132 series", + "CI-134 series", + "CP-132 series", + "CP-114 series", + "CT-114 series" }; static int mxser_numports[] = @@ -144,6 +171,12 @@ 4, 8, 4, + 2, + 2, + 4, + 2, + 4, + 4 }; /* @@ -164,6 +197,12 @@ MXSER_BOARD_C168_PCI }, { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C104_PCI }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + MXSER_BOARD_CP132_PCI }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + MXSER_BOARD_CP114_PCI }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CT114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + MXSER_BOARD_CT114_PCI }, { 0 } }; MODULE_DEVICE_TABLE(pci, mxser_pcibrds); @@ -2304,6 +2343,12 @@ hwconf->board_type = MXSER_BOARD_C168_ISA; else if (id == C104_ASIC_ID) hwconf->board_type = MXSER_BOARD_C104_ISA; + else if (id == C102_ASIC_ID) + hwconf->board_type = MXSER_BOARD_C102_ISA; + else if (id == CI132_ASIC_ID) + hwconf->board_type = MXSER_BOARD_CI132; + else if (id == CI134_ASIC_ID) + hwconf->board_type = MXSER_BOARD_CI134; else if (id == CI104J_ASIC_ID) hwconf->board_type = MXSER_BOARD_CI104J; else @@ -2415,7 +2460,8 @@ (void) inb(port); restore_flags(flags); id = inb(port + 1) & 0x1F; - if ((id != C168_ASIC_ID) && (id != C104_ASIC_ID) && (id != CI104J_ASIC_ID)) + if ((id != C168_ASIC_ID) && (id != C104_ASIC_ID) && (id != CI104J_ASIC_ID) && + (id != C102_ASIC_ID) && (id != CI132_ASIC_ID) && (id != CI134_ASIC_ID)) return (-1); for (i = 0, j = 0; i < 4; i++) { n = inb(port + 2); diff -Nru a/drivers/char/pcwd.c b/drivers/char/pcwd.c --- a/drivers/char/pcwd.c Wed Feb 13 20:03:34 2002 +++ b/drivers/char/pcwd.c Wed Feb 13 20:03:34 2002 @@ -40,6 +40,8 @@ * fairly useless proc entry. * 990610 removed said useless proc code for the merge * 000403 Removed last traces of proc code. + * 020210 Backported 2.5 open_allowed changes, and got rid of a useless + * variable */ #include @@ -100,7 +102,8 @@ #define WD_SRLY2 0x80 /* Software external relay triggered */ static int current_readport, revision, temp_panic; -static int is_open, initial_status, supports_temp, mode_debug; +static atomic_t open_allowed = ATOMIC_INIT(1); +static int initial_status, supports_temp, mode_debug; static spinlock_t io_lock; /* @@ -237,7 +240,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int i, cdat, rv; + int cdat, rv; static struct watchdog_info ident= { WDIOF_OVERHEAT|WDIOF_CARDRESET, @@ -250,8 +253,9 @@ return -ENOTTY; case WDIOC_GETSUPPORT: - i = copy_to_user((void*)arg, &ident, sizeof(ident)); - return i ? -EFAULT : 0; + if(copy_to_user((void*)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; case WDIOC_GETSTATUS: spin_lock(&io_lock); @@ -402,8 +406,10 @@ switch (MINOR(ino->i_rdev)) { case WATCHDOG_MINOR: - if (is_open) + if (!atomic_dec_and_test(&open_allowed)){ + atomic_inc(&open_allowed); return -EBUSY; + } MOD_INC_USE_COUNT; /* Enable the port */ if (revision == PCWD_REVISION_C) @@ -412,7 +418,6 @@ outb_p(0x00, current_readport + 3); spin_unlock(&io_lock); } - is_open = 1; return(0); case TEMP_MINOR: return(0); @@ -452,8 +457,6 @@ { if (MINOR(ino->i_rdev)==WATCHDOG_MINOR) { - lock_kernel(); - is_open = 0; #ifndef CONFIG_WATCHDOG_NOWAYOUT /* Disable the board */ if (revision == PCWD_REVISION_C) { @@ -463,7 +466,7 @@ spin_unlock(&io_lock); } #endif - unlock_kernel(); + atomic_inc(&open_allowed); } return 0; } @@ -574,7 +577,6 @@ printk("pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); /* Initial variables */ - is_open = 0; supports_temp = 0; mode_debug = 0; temp_panic = 0; diff -Nru a/drivers/char/raw.c b/drivers/char/raw.c --- a/drivers/char/raw.c Wed Feb 13 20:03:33 2002 +++ b/drivers/char/raw.c Wed Feb 13 20:03:33 2002 @@ -23,6 +23,7 @@ struct block_device *binding; int inuse, sector_size, sector_bits; struct semaphore mutex; + int can_do_vary; } raw_device_data_t; static raw_device_data_t raw_devices[256]; @@ -117,6 +118,8 @@ if (raw_devices[minor].inuse++) goto out; + raw_devices[minor].can_do_vary = + get_blkdev_varyio(MAJOR(rdev), MINOR(rdev)); /* * Don't interfere with mounted devices: we cannot safely set * the blocksize on a device which is already mounted. @@ -126,6 +129,7 @@ if (is_mounted(rdev)) { if (blksize_size[MAJOR(rdev)]) sector_size = blksize_size[MAJOR(rdev)][MINOR(rdev)]; + raw_devices[minor].can_do_vary = 0; } else { if (hardsect_size[MAJOR(rdev)]) sector_size = hardsect_size[MAJOR(rdev)][MINOR(rdev)]; @@ -133,6 +137,7 @@ set_blocksize(rdev, sector_size); raw_devices[minor].sector_size = sector_size; + filp->f_iobuf->dovary = raw_devices[minor].can_do_vary; for (sector_bits = 0; !(sector_size & 1); ) sector_size>>=1, sector_bits++; @@ -301,6 +306,7 @@ if (err) goto out; new_iobuf = 1; + iobuf->dovary = raw_devices[minor].can_do_vary; } dev = to_kdev_t(raw_devices[minor].binding->bd_dev); diff -Nru a/drivers/char/serial.c b/drivers/char/serial.c --- a/drivers/char/serial.c Wed Feb 13 20:03:44 2002 +++ b/drivers/char/serial.c Wed Feb 13 20:03:44 2002 @@ -57,6 +57,11 @@ * 10/00: add in optional software flow control for serial console. * Kanoj Sarcar (Modified by Theodore Ts'o) * + * 02/02: Fix for AMD Elan bug in transmit irq routine, by + * Christer Weinigel , + * Robert Schwebel , + * Juergen Beisert , + * Theodore Ts'o */ static char *serial_version = "5.05c"; @@ -198,6 +203,7 @@ #include #include #include +#include #if (LINUX_VERSION_CODE >= 131343) #include #endif @@ -801,7 +807,7 @@ */ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) { - int status; + int status, iir; struct async_struct * info; int pass_counter = 0; struct async_struct *end_mark = 0; @@ -826,7 +832,7 @@ do { if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) { if (!end_mark) end_mark = info; goto next; @@ -845,7 +851,9 @@ if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); - if (status & UART_LSR_THRE) + if ((status & UART_LSR_THRE) || + /* for buggy ELAN processors */ + ((iir & UART_IIR_ID) == UART_IIR_THRI)) transmit_chars(info, 0); next: @@ -879,7 +887,7 @@ */ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) { - int status; + int status, iir; int pass_counter = 0; struct async_struct * info; #ifdef CONFIG_SERIAL_MULTIPORT @@ -901,6 +909,7 @@ first_multi = inb(multi->port_monitor); #endif + iir = serial_in(info, UART_IIR); do { status = serial_inp(info, UART_LSR); #ifdef SERIAL_DEBUG_INTR @@ -909,18 +918,21 @@ if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); - if (status & UART_LSR_THRE) + if ((status & UART_LSR_THRE) || + /* For buggy ELAN processors */ + ((iir & UART_IIR_ID) == UART_IIR_THRI)) transmit_chars(info, 0); if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 +#if SERIAL_DEBUG_INTR printk("rs_single loop break.\n"); #endif break; } + iir = serial_in(info, UART_IIR); #ifdef SERIAL_DEBUG_INTR - printk("IIR = %x...", serial_in(info, UART_IIR)); + printk("IIR = %x...", iir); #endif - } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + } while ((iir & UART_IIR_NO_INT) == 0); info->last_active = jiffies; #ifdef CONFIG_SERIAL_MULTIPORT if (multi->port_monitor) @@ -1210,7 +1222,7 @@ if (!page) return -ENOMEM; - save_flags(flags); cli(); + spin_lock_irqsave( &info->irq_spinlock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); @@ -1448,11 +1460,11 @@ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore( &info->irq_spinlock, flags); return 0; errout: - restore_flags(flags); + spin_unlock_irqrestore( &info->irq_spinlock, flags); return retval; } @@ -1476,7 +1488,7 @@ state->irq); #endif - save_flags(flags); cli(); /* Disable interrupts */ + spin_lock_irqsave( &info->irq_spinlock, flags); /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq @@ -1484,41 +1496,6 @@ */ wake_up_interruptible(&info->delta_msr_wait); - /* - * First unlink the serial port from the IRQ chain... - */ - if (info->next_port) - info->next_port->prev_port = info->prev_port; - if (info->prev_port) - info->prev_port->next_port = info->next_port; - else - IRQ_ports[state->irq] = info->next_port; - figure_IRQ_timeout(state->irq); - - /* - * Free the IRQ, if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - free_irq(state->irq, &IRQ_ports[state->irq]); - retval = request_irq(state->irq, rs_interrupt_single, - SA_SHIRQ, "serial", - &IRQ_ports[state->irq]); - - if (retval) - printk("serial shutdown: request_irq: error %d" - " Couldn't reacquire IRQ.\n", retval); - } else - free_irq(state->irq, &IRQ_ports[state->irq]); - } - - if (info->xmit.buf) { - unsigned long pg = (unsigned long) info->xmit.buf; - info->xmit.buf = 0; - free_page(pg); - } - info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ #ifdef CONFIG_SERIAL_MANY_PORTS @@ -1575,7 +1552,43 @@ serial_outp(info, UART_IER, UART_IERX_SLEEP); } info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + /* + * Free the IRQ, if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + spin_unlock_irqrestore( &info->irq_spinlock, flags); } #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ @@ -3119,6 +3132,7 @@ info->tqueue.routine = do_softint; info->tqueue.data = info; info->state = sstate; + spin_lock_init(&info->irq_spinlock); if (sstate->info) { kfree(info); *ret_info = sstate->info; @@ -3643,6 +3657,7 @@ info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; save_flags(flags); cli(); @@ -3895,7 +3910,14 @@ case 6: /* BAR 4*/ case 7: base_idx=idx-2; /* BAR 5*/ } - + + /* AFAVLAB uses a different mixture of BARs and offsets */ + /* Not that ugly ;) -- HW */ + if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { + base_idx = 4; + offset = (idx - 4) * 8; + } + /* Some Titan cards are also a little weird */ if (dev->vendor == PCI_VENDOR_ID_TITAN && (dev->device == PCI_DEVICE_ID_TITAN_400L || @@ -4242,6 +4264,7 @@ pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, + pbn_b0_bt_8_115200, pbn_b0_bt_1_460800, pbn_b0_bt_2_460800, @@ -4320,6 +4343,7 @@ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ @@ -4841,6 +4865,11 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_115200 }, + /* AFAVLAB serial card, from Harald Welte */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + /* EKF addition for i960 Boards form EKF with serial port */ { PCI_VENDOR_ID_INTEL, 0x1960, 0xE4BF, PCI_ANY_ID, 0, 0, @@ -5619,6 +5648,7 @@ info->io_type = req->io_type; info->iomem_base = req->iomem_base; info->iomem_reg_shift = req->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; } autoconfig(state); if (state->type == PORT_UNKNOWN) { @@ -5955,6 +5985,7 @@ info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; quot = state->baud_base / baud; cval = cflag & (CSIZE | CSTOPB); #if defined(__powerpc__) || defined(__alpha__) diff -Nru a/drivers/char/tty_io.c b/drivers/char/tty_io.c --- a/drivers/char/tty_io.c Wed Feb 13 20:03:55 2002 +++ b/drivers/char/tty_io.c Wed Feb 13 20:03:55 2002 @@ -727,6 +727,7 @@ ret = -ERESTARTSYS; if (signal_pending(current)) break; + debug_lock_break(551); if (current->need_resched) schedule(); } diff -Nru a/drivers/i2c/Config.in b/drivers/i2c/Config.in --- a/drivers/i2c/Config.in Wed Feb 13 20:03:50 2002 +++ b/drivers/i2c/Config.in Wed Feb 13 20:03:50 2002 @@ -44,6 +44,23 @@ fi # This is needed for automatic patch generation: sensors code starts here + bool 'I2C mainboard interfaces' CONFIG_I2C_MAINBOARD + if [ "$CONFIG_I2C_MAINBOARD" = "y" ]; then + tristate ' Acer Labs ALI 1535' CONFIG_I2C_ALI1535 + tristate ' Acer Labs ALI 1533 and 1543C' CONFIG_I2C_ALI15X3 + dep_tristate ' Apple Hydra Mac I/O' CONFIG_I2C_HYDRA $CONFIG_I2C_ALGOBIT + tristate ' AMD 756/766' CONFIG_I2C_AMD756 + dep_tristate ' DEC Tsunami I2C interface' CONFIG_I2C_TSUNAMI $CONFIG_I2C_ALGOBIT + tristate ' Intel 82801AA, 82801AB and 82801BA' CONFIG_I2C_I801 + dep_tristate ' Intel i810AA, i810AB and i815' CONFIG_I2C_I810 $CONFIG_I2C_ALGOBIT + tristate ' Intel 82371AB PIIX4(E), ServerWorks OSB4/CSB5' CONFIG_I2C_PIIX4 + tristate ' SiS 5595' CONFIG_I2C_SIS5595 + dep_tristate ' VIA Technologies, Inc. VT82C586B' CONFIG_I2C_VIA $CONFIG_I2C_ALGOBIT + tristate ' VIA Technologies, Inc. VT596A/B' CONFIG_I2C_VIAPRO + dep_tristate ' Voodoo3 I2C interface' CONFIG_I2C_VOODOO3 $CONFIG_I2C_ALGOBIT + tristate ' Pseudo ISA adapter (for some hardware sensors)' CONFIG_I2C_ISA + fi + # This is needed for automatic patch generation: sensors code ends here dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C diff -Nru a/drivers/i2c/Makefile b/drivers/i2c/Makefile --- a/drivers/i2c/Makefile Wed Feb 13 20:03:30 2002 +++ b/drivers/i2c/Makefile Wed Feb 13 20:03:30 2002 @@ -21,6 +21,19 @@ obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o # This is needed for automatic patch generation: sensors code starts here +obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o +obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o +obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o +obj-$(CONFIG_I2C_I801) += i2c-i801.o +obj-$(CONFIG_I2C_I810) += i2c-i810.o +obj-$(CONFIG_I2C_ISA) += i2c-isa.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o +obj-$(CONFIG_I2C_TSUNAMI) += i2c-tsunami.o +obj-$(CONFIG_I2C_VIA) += i2c-via.o +obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o +obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o # This is needed for automatic patch generation: sensors code ends here include $(TOPDIR)/Rules.make diff -Nru a/drivers/i2c/i2c-ali1535.c b/drivers/i2c/i2c-ali1535.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-ali1535.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,691 @@ +/* + i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2000 Frodo Looijaard , + Philip Edelbrock , + Mark D. Studebaker , + Dan Eaton and + Stephen Rousset + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1535 South Bridge. + + The M1535 is a South bridge for portable systems. + It is very similar to the M15x3 South bridges also produced + by Acer Labs Inc. Some of the registers within the part + have moved and some have been redefined slightly. Additionally, + the sequencing of the SMBus transactions has been modified + to be more consistent with the sequencing recommended by + the manufacturer and observed through testing. These + changes are reflected in this driver and can be identified + by comparing this driver to the i2c-ali15x3 driver. + For an overview of these chips see http://www.acerlabs.com + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + + This driver does not use interrupts. +*/ + + +/* Note: we assume there can only be one ALI1535, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef DECLARE_MUTEX +#define DECLARE_MUTEX(name) struct semaphore name = MUTEX +#endif /* def DECLARE_MUTEX */ + +/* ALI1535 SMBus address offsets */ +#define SMBHSTSTS (0 + ali1535_smba) +#define SMBHSTTYP (1 + ali1535_smba) +#define SMBHSTPORT (2 + ali1535_smba) +#define SMBHSTCMD (7 + ali1535_smba) +#define SMBHSTADD (3 + ali1535_smba) +#define SMBHSTDAT0 (4 + ali1535_smba) +#define SMBHSTDAT1 (5 + ali1535_smba) +#define SMBBLKDAT (6 + ali1535_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBREV 0x008 +#define SMBCFG 0x0D1 +#define SMBBA 0x0E2 +#define SMBHSTCFG 0x0F0 +#define SMBCLK 0x0F2 + +/* Other settings */ +#define MAX_TIMEOUT 500 /* times 1/100 sec */ +#define ALI1535_SMB_IOSIZE 32 + +/* +*/ +#define ALI1535_SMB_DEFAULTBASE 0x8040 + +/* ALI1535 address lock bits */ +#define ALI1535_LOCK 0x06 < dwe > + +/* ALI1535 command constants */ +#define ALI1535_QUICK 0x00 +#define ALI1535_BYTE 0x10 +#define ALI1535_BYTE_DATA 0x20 +#define ALI1535_WORD_DATA 0x30 +#define ALI1535_BLOCK_DATA 0x40 +#define ALI1535_I2C_READ 0x60 + +#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ + /* I2C read */ +#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ +#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define ALI1535_KILL 0x04 /* Kill Command (write) */ +#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ + +#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ + /* of 10-bit address in I2C */ + /* Read Command */ + +/* ALI1535 status register bits */ +#define ALI1535_STS_IDLE 0x04 +#define ALI1535_STS_BUSY 0x08 /* host busy */ +#define ALI1535_STS_DONE 0x10 /* transaction complete */ +#define ALI1535_STS_DEV 0x20 /* device error */ +#define ALI1535_STS_BUSERR 0x40 /* bus error */ +#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ +#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ + +#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ + +/* ALI1535 device address register bits */ +#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ + /* Address field */ + /* -> Write = 0 */ + /* -> Read = 1 */ +#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_ali1535_init(void); +static int __init ali1535_cleanup(void); +static int ali1535_setup(void); +static s32 ali1535_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void ali1535_do_pause(unsigned int amount); +static int ali1535_transaction(void); +static void ali1535_inc(struct i2c_adapter *adapter); +static void ali1535_dec(struct i2c_adapter *adapter); +static u32 ali1535_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-i2c SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ ali1535_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ ali1535_func, +}; + +static struct i2c_adapter ali1535_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535, + &smbus_algorithm, + NULL, + ali1535_inc, + ali1535_dec, + NULL, + NULL, +}; + +static int __initdata ali1535_initialized; +static unsigned short ali1535_smba = 0; +DECLARE_MUTEX(i2c_ali1535_sem); + + +/* Detect whether a ALI1535 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int ali1535_setup(void) +{ + int error_return = 0; + unsigned char temp; + + struct pci_dev *ALI1535_dev; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-ali1535.o: Error: No PCI-bus found!\n"); + error_return = -ENODEV; + goto END; + } + + /* Look for the ALI1535, M7101 device */ + ALI1535_dev = NULL; + ALI1535_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, + ALI1535_dev); + + if (ALI1535_dev == NULL) { + printk("i2c-ali1535.o: Error: Can't detect ali1535!\n"); + error_return = -ENODEV; + goto END; + } + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba); + ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); + if (ali1535_smba == 0) { + printk + ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n"); + error_return = -ENODEV; + } + + if (error_return == -ENODEV) + goto END; + + if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) { + printk + ("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n", + ali1535_smba); + error_return = -ENODEV; + } + + if (error_return == -ENODEV) + goto END; + + /* check if whole device is enabled */ + pci_read_config_byte(ALI1535_dev, SMBCFG, &temp); + if ((temp & ALI1535_SMBIO_EN) == 0) { + printk + ("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n"); + error_return = -ENODEV; + goto END; + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk + ("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n"); + error_return = -ENODEV; + goto END; + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali1535_smba, ALI1535_SMB_IOSIZE, "ali1535-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI1535_dev, SMBREV, &temp); + printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void ali1535_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali1535_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali1535.o: Transaction (pre): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI1535_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an KILL command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali1535.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI1535_KILL, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + if (temp & ALI1535_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali1535.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI1535_T_OUT, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali1535.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI1535_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTPORT); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali1535_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali1535.o: SMBus Timeout!\n"); + } + + if (temp & ALI1535_STS_FAIL) { + result = -1; +#ifdef DEBUG + printk("i2c-ali1535.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI1535_STS_BUSERR) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali1535.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI1535_STS_DEV) { + result = -1; + printk("i2c-ali1535.o: Error: device error\n"); + } + +/* + check to see if the "command complete" indication is set + */ + if (!(temp & ALI1535_STS_DONE)) { + result = -1; + printk("i2c-ali1535.o: Error: command never completed\n"); + } +#ifdef DEBUG + printk + ("i2c-ali1535.o: Transaction (post): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + +/* + take consequent actions for error conditions + */ + if (!(temp & ALI1535_STS_DONE)) { + /* issue "kill" to reset host controller */ + outb_p(ALI1535_KILL,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } + else if (temp & ALI1535_STS_ERR) { + /* issue "timeout" to reset all devices on bus */ + outb_p(ALI1535_T_OUT,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } + + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 ali1535_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + s32 result = 0; + + down(&i2c_ali1535_sem); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); + timeout++) { + ali1535_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali1535.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + +/* clear status register (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali1535.o: I2C_SMBUS_PROC_CALL not supported!\n"); + result = -1; + goto EXIT; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_QUICK; + outb_p(size, SMBHSTTYP); /* output command */ + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE; + outb_p(size, SMBHSTTYP); /* output command */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_WORD_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BLOCK_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + break; + } + + if (ali1535_transaction()) /* Error in transaction */ + { + result = -1; + goto EXIT; + } + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) + { + result = 0; + goto EXIT; + } + + switch (size) { + case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI1535_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } +EXIT: + up(&i2c_ali1535_sem); + return result; +} + +void ali1535_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void ali1535_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 ali1535_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_ali1535_init(void) +{ + int res; + printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (ali1535_initialized) { + printk + ("i2c-ali1535.o: Oops, ali1535_init called a second time!\n"); + return -EBUSY; + } +#endif + ali1535_initialized = 0; + if ((res = ali1535_setup())) { + printk + ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n"); + ali1535_cleanup(); + return res; + } + ali1535_initialized++; + sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x", + ali1535_smba); + if ((res = i2c_add_adapter(&ali1535_adapter))) { + printk + ("i2c-ali1535.o: Adapter registration failed, module not inserted.\n"); + ali1535_cleanup(); + return res; + } + ali1535_initialized++; + printk + ("i2c-ali1535.o: ALI1535 SMBus Controller detected and initialized\n"); + return 0; +} + +int __init ali1535_cleanup(void) +{ + int res; + if (ali1535_initialized >= 2) { + if ((res = i2c_del_adapter(&ali1535_adapter))) { + printk + ("i2c-ali1535.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + ali1535_initialized--; + } + if (ali1535_initialized >= 1) { + release_region(ali1535_smba, ALI1535_SMB_IOSIZE); + ali1535_initialized--; + } + return 0; +} + +#ifdef RLX +EXPORT_SYMBOL(ali1535_smba); +EXPORT_SYMBOL(ali1535_access); +EXPORT_SYMBOL(i2c_ali1535_sem); +#else +EXPORT_NO_SYMBOLS; +#endif + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , + Mark D. Studebaker and Dan Eaton "); +MODULE_DESCRIPTION("ALI1535 SMBus driver"); + +int init_module(void) +{ + return i2c_ali1535_init(); +} + +int cleanup_module(void) +{ + return ali1535_cleanup(); +} + +#endif /* MODULE */ + diff -Nru a/drivers/i2c/i2c-ali15x3.c b/drivers/i2c/i2c-ali15x3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-ali15x3.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,650 @@ +/* + ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard and + Philip Edelbrock and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_ali15x3_init(void); +static int __init ali15x3_cleanup(void); +static int ali15x3_setup(void); +static s32 ali15x3_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void ali15x3_do_pause(unsigned int amount); +static int ali15x3_transaction(void); +static void ali15x3_inc(struct i2c_adapter *adapter); +static void ali15x3_dec(struct i2c_adapter *adapter); +static u32 ali15x3_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ ali15x3_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + &smbus_algorithm, + NULL, + ali15x3_inc, + ali15x3_dec, + NULL, + NULL, +}; + +static int __initdata ali15x3_initialized; +static unsigned short ali15x3_smba = 0; +static int locked=0; + +/* Detect whether a ALI15X3 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int ali15x3_setup(void) +{ + u16 a; + unsigned char temp; + + struct pci_dev *ALI15X3_dev; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-ali15x3.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the ALI15X3, M7101 device */ + ALI15X3_dev = NULL; + ALI15X3_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, ALI15X3_dev); + if (ALI15X3_dev == NULL) { + printk("i2c-ali15x3.o: Error: Can't detect ali15x3!\n"); + return -ENODEV; + } + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. +*/ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-ali15x3.o: forcing ISA address 0x%04X\n", ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(ALI15X3_dev, SMBBA, &a)) + return -ENODEV; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + printk("i2c-ali15x3.o: force address failed - not supported?\n"); + return -ENODEV; + } + } +/* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba); +#endif /* DEBUG */ + + return 0; +} + + +/* Internally used pause function */ +void ali15x3_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali15x3_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI15X3_ABORT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + if (temp & ALI15X3_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali15x3.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali15x3.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali15x3.o: SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -1; +#ifdef DEBUG + printk("i2c-ali15x3.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't + do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI15X3_STS_COLL) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -1; + printk("i2c-ali15x3.o: Error: device error\n"); + } +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + +/* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + if (ali15x3_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } + return 0; +} + +void ali15x3_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void ali15x3_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_ali15x3_init(void) +{ + int res; + printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (ali15x3_initialized) { + printk + ("i2c-ali15x3.o: Oops, ali15x3_init called a second time!\n"); + return -EBUSY; + } +#endif + ali15x3_initialized = 0; + if ((res = ali15x3_setup())) { + printk + ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); + ali15x3_cleanup(); + return res; + } + ali15x3_initialized++; + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", + ali15x3_smba); + if ((res = i2c_add_adapter(&ali15x3_adapter))) { + printk + ("i2c-ali15x3.o: Adapter registration failed, module not inserted.\n"); + ali15x3_cleanup(); + return res; + } + ali15x3_initialized++; + printk + ("i2c-ali15x3.o: ALI15X3 SMBus Controller detected and initialized\n"); + return 0; +} + +int __init ali15x3_cleanup(void) +{ + int res; + if (ali15x3_initialized >= 2) { + if ((res = i2c_del_adapter(&ali15x3_adapter))) { + printk + ("i2c-ali15x3.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + ali15x3_initialized--; + } + if (ali15x3_initialized >= 1) { + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); + ali15x3_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); + +int init_module(void) +{ + return i2c_ali15x3_init(); +} + +int cleanup_module(void) +{ + return ali15x3_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-amd756.c b/drivers/i2c/i2c-amd756.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-amd756.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,508 @@ +/* + amd756.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 1999 Merlin Hughes + + Shamelessly ripped from i2c-piix4.c: + + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports AMD756 and AMD766. + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_AMD_756 +#define PCI_DEVICE_ID_AMD_756 0x740B +#endif +#ifndef PCI_DEVICE_ID_AMD_766 +#define PCI_DEVICE_ID_AMD_766 0x7413 +#endif + +static int supported[] = {PCI_DEVICE_ID_AMD_756, + PCI_DEVICE_ID_AMD_766, + 0 }; + +/* AMD756 SMBus address offsets */ +#define SMB_ADDR_OFFSET 0xE0 +#define SMB_IOSIZE 16 +#define SMB_GLOBAL_STATUS (0x0 + amd756_smba) +#define SMB_GLOBAL_ENABLE (0x2 + amd756_smba) +#define SMB_HOST_ADDRESS (0x4 + amd756_smba) +#define SMB_HOST_DATA (0x6 + amd756_smba) +#define SMB_HOST_COMMAND (0x8 + amd756_smba) +#define SMB_HOST_BLOCK_DATA (0x9 + amd756_smba) +#define SMB_HAS_DATA (0xA + amd756_smba) +#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_smba) +#define SMB_HAS_HOST_ADDRESS (0xE + amd756_smba) +#define SMB_SNOOP_ADDRESS (0xF + amd756_smba) + +/* PCI Address Constants */ + +/* address of I/O space */ +#define SMBBA 0x058 /* mh */ + +/* general configuration */ +#define SMBGCFG 0x041 /* mh */ + +/* silicon revision code */ +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 100 + +/* AMD756 constants */ +#define AMD756_QUICK 0x00 +#define AMD756_BYTE 0x01 +#define AMD756_BYTE_DATA 0x02 +#define AMD756_WORD_DATA 0x03 +#define AMD756_PROCESS_CALL 0x04 +#define AMD756_BLOCK_DATA 0x05 + +/* insmod parameters */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_amd756_init(void); +static int __init amd756_cleanup(void); +static int amd756_setup(void); +static s32 amd756_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void amd756_do_pause(unsigned int amount); +static int amd756_transaction(void); +static void amd756_inc(struct i2c_adapter *adapter); +static void amd756_dec(struct i2c_adapter *adapter); +static u32 amd756_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ amd756_access, + /* slave;_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ amd756_func, +}; + +static struct i2c_adapter amd756_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756, + &smbus_algorithm, + NULL, + amd756_inc, + amd756_dec, + NULL, + NULL, +}; + +static int __initdata amd756_initialized; +static unsigned short amd756_smba = 0; + +/* Detect whether a AMD756 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int amd756_setup(void) +{ + unsigned char temp; + int *num = supported; + struct pci_dev *AMD756_dev = NULL; + + if (pci_present() == 0) { + printk("i2c-amd756.o: Error: No PCI-bus found!\n"); + return(-ENODEV); + } + + /* Look for the AMD756, function 3 */ + /* Note: we keep on searching until we have found 'function 3' */ + do { + if((AMD756_dev = pci_find_device(PCI_VENDOR_ID_AMD, + *num, AMD756_dev))) { + if(PCI_FUNC(AMD756_dev->devfn) != 3) + continue; + break; + } + num++; + } while (*num != 0); + + if (AMD756_dev == NULL) { + printk + ("i2c-amd756.o: Error: Can't detect AMD756, function 3!\n"); + return(-ENODEV); + } + + + pci_read_config_byte(AMD756_dev, SMBGCFG, &temp); + if ((temp & 128) == 0) { + printk + ("i2c-amd756.o: Error: SMBus controller I/O not enabled!\n"); + return(-ENODEV); + } + + /* Determine the address of the SMBus areas */ + /* Technically it is a dword but... */ + pci_read_config_word(AMD756_dev, SMBBA, &amd756_smba); + amd756_smba &= 0xff00; + amd756_smba += SMB_ADDR_OFFSET; + + if (check_region(amd756_smba, SMB_IOSIZE)) { + printk + ("i2c-amd756.o: SMB region 0x%x already in use!\n", + amd756_smba); + return(-ENODEV); + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(amd756_smba, SMB_IOSIZE, "amd756-smbus"); + +#ifdef DEBUG + pci_read_config_byte(AMD756_dev, SMBREV, &temp); + printk("i2c-amd756.o: SMBREV = 0x%X\n", temp); + printk("i2c-amd756.o: AMD756_smba = 0x%X\n", amd756_smba); +#endif /* DEBUG */ + + return 0; +} + +/* + SMBUS event = I/O 28-29 bit 11 + see E0 for the status bits and enabled in E2 + +*/ + +/* Internally used pause function */ +void amd756_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +#define GS_ABRT_STS (1 << 0) +#define GS_COL_STS (1 << 1) +#define GS_PRERR_STS (1 << 2) +#define GS_HST_STS (1 << 3) +#define GS_HCYC_STS (1 << 4) +#define GS_TO_STS (1 << 5) +#define GS_SMB_STS (1 << 11) + +#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ + GS_HCYC_STS | GS_TO_STS ) + +#define GE_CYC_TYPE_MASK (7) +#define GE_HOST_STC (1 << 3) + +int amd756_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-amd756.o: Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { +#ifdef DEBUG + printk + ("i2c-amd756.o: SMBus busy (%04x). Waiting... \n", temp); +#endif + do { + amd756_do_pause(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & (GS_HST_STS | GS_SMB_STS)) && + (timeout++ < MAX_TIMEOUT)); + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-amd756.o: Busy wait timeout! (%04x)\n", + temp); + return(-1); + } + timeout = 0; + } + + /* start the transaction by setting the start bit */ + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); + + /* We will always wait for a fraction of a second! */ + do { + amd756_do_pause(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-amd756.o: Completion timeout!\n"); + return(-1); + } + + if (temp & GS_PRERR_STS) { + result = -1; +#ifdef DEBUG + printk("i2c-amd756.o: SMBus Protocol error (no response)!\n"); +#endif + } + + if (temp & GS_COL_STS) { + result = -1; + printk("i2c-amd756.o: SMBus collision!\n"); + /* TODO: Clear Collision Status with a 1 */ + } + + if (temp & GS_TO_STS) { + result = -1; +#ifdef DEBUG + printk("i2c-amd756.o: SMBus protocol timeout!\n"); +#endif + } +#ifdef DEBUG + if (temp & GS_HCYC_STS) { + printk("i2c-amd756.o: SMBus protocol success!\n"); + } +#endif + + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + +#ifdef DEBUG + if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { + printk + ("i2c-amd756.o: Failed reset at end of transaction (%04x)\n", + temp); + } + printk + ("i2c-amd756.o: Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); +#endif + + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 amd756_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + /** TODO: Should I supporte the 10-bit transfers? */ + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-amd756.o: I2C_SMBUS_PROC_CALL not supported!\n"); + /* TODO: Well... It is supported, I'm just not sure what to do here... */ + return -1; + case I2C_SMBUS_QUICK: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + size = AMD756_QUICK; + break; + case I2C_SMBUS_BYTE: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + /* TODO: Why only during write? */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMB_HOST_COMMAND); + size = AMD756_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->byte, SMB_HOST_DATA); + size = AMD756_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ + size = AMD756_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outw_p(len, SMB_HOST_DATA); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], + SMB_HOST_BLOCK_DATA); + } + size = AMD756_BLOCK_DATA; + break; + } + + /* How about enabling interrupts... */ + outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); + + if (amd756_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) + return 0; + + + switch (size) { + case AMD756_BYTE: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_BYTE_DATA: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_WORD_DATA: + data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ + break; + case AMD756_BLOCK_DATA: + data->block[0] = inw_p(SMB_HOST_DATA & 63); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); + break; + } + + return 0; +} + +void amd756_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void amd756_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 amd756_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL; +} + +int __init i2c_amd756_init(void) +{ + int res; + printk("i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (amd756_initialized) { + printk + ("i2c-amd756.o: Oops, amd756_init called a second time!\n"); + return -EBUSY; + } +#endif + amd756_initialized = 0; + if ((res = amd756_setup())) { + printk + ("i2c-amd756.o: AMD756/766 not detected, module not inserted.\n"); + amd756_cleanup(); + return res; + } + amd756_initialized++; + sprintf(amd756_adapter.name, "SMBus AMD7X6 adapter at %04x", + amd756_smba); + if ((res = i2c_add_adapter(&amd756_adapter))) { + printk + ("i2c-amd756.o: Adapter registration failed, module not inserted.\n"); + amd756_cleanup(); + return res; + } + amd756_initialized++; + printk("i2c-amd756.o: AMD756/766 bus detected and initialized\n"); + return 0; +} + +int __init amd756_cleanup(void) +{ + int res; + if (amd756_initialized >= 2) { + if ((res = i2c_del_adapter(&amd756_adapter))) { + printk + ("i2c-amd756.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + amd756_initialized--; + } + if (amd756_initialized >= 1) { + release_region(amd756_smba, SMB_IOSIZE); + amd756_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Merlin Hughes "); +MODULE_DESCRIPTION("AMD756/766 SMBus driver"); + +int init_module(void) +{ + return i2c_amd756_init(); +} + +int cleanup_module(void) +{ + return amd756_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c Wed Feb 13 20:03:35 2002 +++ b/drivers/i2c/i2c-core.c Wed Feb 13 20:03:35 2002 @@ -1315,6 +1315,45 @@ #endif /* This is needed for automatic patch generation: sensors code starts here */ +#ifdef CONFIG_I2C_ALI1535 + extern int i2c_ali1535_init(void); +#endif +#ifdef CONFIG_I2C_ALI15X3 + extern int i2c_ali15x3_init(void); +#endif +#ifdef CONFIG_I2C_AMD756 + extern int i2c_amd756_init(void); +#endif +#ifdef CONFIG_I2C_HYDRA + extern int i2c_hydra_init(void); +#endif +#ifdef CONFIG_I2C_I801 + extern int i2c_i801_init(void); +#endif +#ifdef CONFIG_I2C_I810 + extern int i2c_i810_init(void); +#endif +#ifdef CONFIG_I2C_ISA + extern int i2c_isa_init(void); +#endif +#ifdef CONFIG_I2C_PIIX4 + extern int i2c_piix4_init(void); +#endif +#ifdef CONFIG_I2C_SIS5595 + extern int i2c_sis5595_init(void); +#endif +#ifdef CONFIG_I2C_TSUNAMI + extern int i2c_tsunami_init(void); +#endif +#ifdef CONFIG_I2C_VIA + extern int i2c_via_init(void); +#endif +#ifdef CONFIG_I2C_VIAPRO + extern int i2c_vt596_init(void); +#endif +#ifdef CONFIG_I2C_VOODOO3 + extern int i2c_voodoo3_init(void); +#endif /* This is needed for automatic patch generation: sensors code ends here */ int __init i2c_init_all(void) @@ -1360,6 +1399,45 @@ sensors_init(); #endif /* This is needed for automatic patch generation: sensors code starts here */ +#ifdef CONFIG_I2C_ALI1535 + i2c_ali1535_init(); +#endif +#ifdef CONFIG_I2C_ALI15X3 + i2c_ali15x3_init(); +#endif +#ifdef CONFIG_I2C_AMD756 + i2c_amd756_init(); +#endif +#ifdef CONFIG_I2C_HYDRA + i2c_hydra_init(); +#endif +#ifdef CONFIG_I2C_I801 + i2c_i801_init(); +#endif +#ifdef CONFIG_I2C_I810 + i2c_i810_init(); +#endif +#ifdef CONFIG_I2C_PIIX4 + i2c_piix4_init(); +#endif +#ifdef CONFIG_I2C_SIS5595 + i2c_sis5595_init(); +#endif +#ifdef CONFIG_I2C_TSUNAMI + i2c_tsunami_init(); +#endif +#ifdef CONFIG_I2C_VIA + i2c_via_init(); +#endif +#ifdef CONFIG_I2C_VIAPRO + i2c_vt596_init(); +#endif +#ifdef CONFIG_I2C_VOODOO3 + i2c_voodoo3_init(); +#endif +#ifdef CONFIG_I2C_ISA + i2c_isa_init(); +#endif /* This is needed for automatic patch generation: sensors code ends here */ return 0; diff -Nru a/drivers/i2c/i2c-hydra.c b/drivers/i2c/i2c-hydra.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-hydra.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,206 @@ +/* + i2c-hydra.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + i2c Support for the Apple `Hydra' Mac I/O + + Copyright (c) 1999 Geert Uytterhoeven + + Based on i2c Support for Via Technologies 82C586B South Bridge + Copyright (c) 1998, 1999 Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI device */ +#define VENDOR PCI_VENDOR_ID_APPLE +#define DEVICE PCI_DEVICE_ID_APPLE_HYDRA + +#define HYDRA_CACHE_PD 0x00000030 + +#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */ +#define HYDRA_CPD_PD1 0x00000002 +#define HYDRA_CPD_PD2 0x00000004 +#define HYDRA_CPD_PD3 0x00000008 + +#define HYDRA_SCLK HYDRA_CPD_PD0 +#define HYDRA_SDAT HYDRA_CPD_PD1 +#define HYDRA_SCLK_OE 0x00000010 +#define HYDRA_SDAT_OE 0x00000020 + +static unsigned long hydra_base; + +static inline void pdregw(u32 val) +{ + writel(val, hydra_base + HYDRA_CACHE_PD); +} + +static inline u32 pdregr(void) +{ + u32 val = readl(hydra_base + HYDRA_CACHE_PD); + return val; +} + +static void bit_hydra_setscl(void *data, int state) +{ + u32 val = pdregr(); + if (state) + val &= ~HYDRA_SCLK_OE; + else { + val &= ~HYDRA_SCLK; + val |= HYDRA_SCLK_OE; + } + pdregw(val); +} + +static void bit_hydra_setsda(void *data, int state) +{ + u32 val = pdregr(); + if (state) + val &= ~HYDRA_SDAT_OE; + else { + val &= ~HYDRA_SDAT; + val |= HYDRA_SDAT_OE; + } + pdregw(val); +} + +static int bit_hydra_getscl(void *data) +{ + return (pdregr() & HYDRA_SCLK) != 0; +} + +static int bit_hydra_getsda(void *data) +{ + return (pdregr() & HYDRA_SDAT) != 0; +} + +static void bit_hydra_inc(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_hydra_dec(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data bit_hydra_data = { + NULL, + bit_hydra_setsda, + bit_hydra_setscl, + bit_hydra_getsda, + bit_hydra_getscl, + 5, 5, 100, /*waits, timeout */ +}; + +static struct i2c_adapter bit_hydra_ops = { + "Hydra i2c", + I2C_HW_B_HYDRA, + NULL, + &bit_hydra_data, + bit_hydra_inc, + bit_hydra_dec, + NULL, + NULL, +}; + + +static int find_hydra(void) +{ + struct pci_dev *dev; + unsigned int base_addr; + + if (!pci_present()) + return -ENODEV; + + dev = pci_find_device(VENDOR, DEVICE, NULL); + if (!dev) { + printk("Hydra not found\n"); + return -ENODEV; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + base_addr = dev->resource[0].start; +#else + base_addr = dev->base_address[0]; +#endif + hydra_base = (unsigned long) ioremap(base_addr, 0x100); + + return 0; +} + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_hydra_init(void) +{ + if (find_hydra() < 0) { + printk("Error while reading PCI configuration\n"); + return -ENODEV; + } + + pdregw(0); /* clear SCLK_OE and SDAT_OE */ + + if (i2c_bit_add_bus(&bit_hydra_ops) == 0) { + printk("Hydra i2c: Module succesfully loaded\n"); + return 0; + } else { + iounmap((void *) hydra_base); + printk + ("Hydra i2c: Algo-bit error, couldn't register bus\n"); + return -ENODEV; + } +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); + +int init_module(void) +{ + return i2c_hydra_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_hydra_ops); + if (hydra_base) { + pdregw(0); /* clear SCLK_OE and SDAT_OE */ + iounmap((void *) hydra_base); + } +} +#endif diff -Nru a/drivers/i2c/i2c-i801.c b/drivers/i2c/i2c-i801.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-i801.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,710 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , and Mark D. Studebaker + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + SUPPORTED DEVICES PCI ID + 82801AA 2413 + 82801AB 2423 + 82801BA 2443 + 82801CA/CAM 2483 + + This driver supports several versions of Intel's I/O Controller Hubs (ICH). + For SMBus support, they are similar to the PIIX4 and are part + of Intel's '810' and other chipsets. + See the doc/busses/i2c-i801 file for details. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801AA_3 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801AB_3 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801BA_2 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#endif +#define PCI_DEVICE_ID_INTEL_82801CA_SMBUS 0x2483 + +static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3, + PCI_DEVICE_ID_INTEL_82801AB_3, + PCI_DEVICE_ID_INTEL_82801BA_2, + PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + 0 }; + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) + +/* PCI Address Constants */ +#define SMBBA 0x020 +#define SMBHSTCFG 0x040 +#define SMBREV 0x008 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + I801. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the I801. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the I801 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the I801 at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_i801_init(void); +static int __init i801_cleanup(void); +static int i801_setup(void); +static s32 i801_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void i801_do_pause(unsigned int amount); +static int i801_transaction(void); +static int i801_block_transaction(union i2c_smbus_data *data, + char read_write, int i2c_enable); +static void i801_inc(struct i2c_adapter *adapter); +static void i801_dec(struct i2c_adapter *adapter); +static u32 i801_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_xfer */ i801_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ i801_func, +}; + +static struct i2c_adapter i801_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + &smbus_algorithm, + NULL, + i801_inc, + i801_dec, + NULL, + NULL, +}; + +static int __initdata i801_initialized; +static unsigned short i801_smba = 0; +static struct pci_dev *I801_dev = NULL; + + +/* Detect whether a I801 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int i801_setup(void) +{ + int error_return = 0; + int *num = supported; + unsigned char temp; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-i801.o: Error: No PCI-bus found!\n"); + error_return = -ENODEV; + goto END; + } + + /* Look for each chip */ + /* Note: we keep on searching until we have found 'function 3' */ + I801_dev = NULL; + do { + if((I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + *num, I801_dev))) { + if(PCI_FUNC(I801_dev->devfn) != 3) + continue; + break; + } + num++; + } while (*num != 0); + + if (I801_dev == NULL) { + printk + ("i2c-i801.o: Error: Can't detect I801, function 3!\n"); + error_return = -ENODEV; + goto END; + } + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + } + + if (check_region(i801_smba, 8)) { + printk + ("i2c-i801.o: I801_smb region 0x%x already in use!\n", + i801_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the I801 first. */ + if (force_addr) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(I801_dev, SMBBA, i801_smba); + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); + printk + ("i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(I801_dev, SMBHSTCFG, + temp | 1); + printk + ("i2c-i801.o: WARNING: I801 SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + ("SMBUS: Error: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* note: we assumed that the BIOS picked SMBus or I2C Bus timing + appropriately (bit 2 in SMBHSTCFG) */ + /* Everything is happy, let's grab the memory and set things up. */ + request_region(i801_smba, 8, "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + ("i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + ("i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk("i2c-i801.o: SMBREV = 0x%X\n", temp); + printk("i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk("i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-i801.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + i801_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + ("i2c-i801.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: no response!\n"); +#endif + } + + if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + ("i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* All-inclusive block transaction function */ +int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int i2c_enable) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout = 0; + unsigned char hostc, errmask; + + if (i2c_enable) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + printk("i2c-i801.o: " + "I2C_SMBUS_I2C_BLOCK_READ not supported!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + outb_p(data->block[1], SMBBLKDAT); + } else { + len = 32; /* max for reads */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; + + outb_p((smbcmd & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT); + +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb_p(SMBHSTSTS); + if (i == 1) { + /* Erronenous conditions before transaction: + * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + errmask=0x9f; + } else { + /* Erronenous conditions during transaction: + * Failed, Bus_Err, Dev_Err, Intr */ + errmask=0x1e; + } + if (temp & errmask) { +#ifdef DEBUG + printk + ("i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + ("i2c-i801.o: Reset failed! (%02x)\n", + temp); + result = -1; + goto END; + } + if (i != 1) { + result = -1; /* if die in middle of block transaction, fail */ + goto END; + } + } + + /* start the transaction by setting bit 6 */ + if (i==1) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ( + (((i >= len) && (temp & 0x01)) + || ((i < len) && !(temp & 0x80))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + ("i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk + ("i2c-i801.o: Bus collision! SMBus may be locked until next hard" + " reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: no response!\n"); +#endif + } + + if (i == 1 && read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1) + len = 1; + if (len > 32) + len = 32; + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT); + if ((temp & 0x9e) != 0x00) + outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ + + temp = inb_p(SMBHSTSTS); + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + ("i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) { + goto END; + } + } + result = 0; +END: + if (i2c_enable) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk("i2c-i801.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + /* Block transactions are very different from piix4 block + and from the other i801 transactions. Handle in the + i801_block_transaction() routine. */ + return i801_block_transaction(data, read_write, + size==I2C_SMBUS_I2C_BLOCK_DATA); + } + + /* 'size' is really the transaction type */ + outb_p((size & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (i801_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == I801_QUICK)) + return 0; + + + switch (size) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + } + return 0; +} + +void i801_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i801_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +u32 i801_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK; +} + +int __init i2c_i801_init(void) +{ + int res; + printk("i2c-i801.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (i801_initialized) { + printk + ("i2c-i801.o: Oops, i801_init called a second time!\n"); + return -EBUSY; + } +#endif + i801_initialized = 0; + if ((res = i801_setup())) { + printk + ("i2c-i801.o: I801 not detected, module not inserted.\n"); + i801_cleanup(); + return res; + } + i801_initialized++; + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", + i801_smba); + if ((res = i2c_add_adapter(&i801_adapter))) { + printk + ("i2c-i801.o: Adapter registration failed, module not inserted.\n"); + i801_cleanup(); + return res; + } + i801_initialized++; + printk("i2c-i801.o: I801 bus detected and initialized\n"); + return 0; +} + +int __init i801_cleanup(void) +{ + int res; + if (i801_initialized >= 2) { + if ((res = i2c_del_adapter(&i801_adapter))) { + printk + ("i2c-i801.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + i801_initialized--; + } + if (i801_initialized >= 1) { + release_region(i801_smba, 8); + i801_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I801 SMBus driver"); + +int init_module(void) +{ + return i2c_i801_init(); +} + +int cleanup_module(void) +{ + return i801_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-i810.c b/drivers/i2c/i2c-i810.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-i810.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,348 @@ +/* + i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999, 2000 Frodo Looijaard , + Philip Edelbrock , + Ralph Metzler , and + Mark D. Studebaker + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This interfaces to the I810/I815 to provide access to + the DDC Bus and the I2C Bus. + + SUPPORTED DEVICES PCI ID + i810AA 7121 + i810AB 7123 + i815 1132 +*/ + + +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI defines */ +#ifndef PCI_DEVICE_ID_INTEL_82810_IG1 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82810_IG3 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82815_2 +#define PCI_DEVICE_ID_INTEL_82815_2 0x1132 +#endif + +static int i810_supported[] = {PCI_DEVICE_ID_INTEL_82810_IG1, + PCI_DEVICE_ID_INTEL_82810_IG3, + PCI_DEVICE_ID_INTEL_82815_2, + 0 }; + +/* GPIO register locations */ +#define I810_IOCONTROL_OFFSET 0x5000 +#define I810_HVSYNC 0x00 /* not used */ +#define I810_GPIOA 0x10 +#define I810_GPIOB 0x14 + +/* bit locations in the registers */ +#define SCL_DIR_MASK 0x0001 +#define SCL_DIR 0x0002 +#define SCL_VAL_MASK 0x0004 +#define SCL_VAL_OUT 0x0008 +#define SCL_VAL_IN 0x0010 +#define SDA_DIR_MASK 0x0100 +#define SDA_DIR 0x0200 +#define SDA_VAL_MASK 0x0400 +#define SDA_VAL_OUT 0x0800 +#define SDA_VAL_IN 0x1000 + +/* initialization states */ +#define INIT1 0x1 +#define INIT2 0x2 +#define INIT3 0x4 + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT 50 + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_i810_init(void); +static int __init i810i2c_cleanup(void); +static int i810i2c_setup(void); +static void config_i810(struct pci_dev *dev); +static void i810_inc(struct i2c_adapter *adapter); +static void i810_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static int __initdata i810i2c_initialized; +static unsigned char *mem; + +static inline void outlong(unsigned int dat, int off) +{ + *((unsigned int *) (mem + off)) = dat; +} + +static inline unsigned int readlong(int off) +{ + return *((unsigned int *) (mem + off)); +} + +/* The i810 GPIO registers have individual masks for each bit + so we never have to read before writing. Nice. */ + +static void bit_i810i2c_setscl(void *data, int val) +{ + outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, + I810_GPIOB); +} + +static void bit_i810i2c_setsda(void *data, int val) +{ + outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, + I810_GPIOB); +} + +/* The GPIO pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. Following guidance in the 815 + prog. ref. guide, we do a "dummy write" of 0 to the register before + reading which forces the input value to be latched. We presume this + applies to the 810 as well. This is necessary to get + i2c_algo_bit bit_test=1 to pass. */ + +static int bit_i810i2c_getscl(void *data) +{ + outlong(0, I810_GPIOB); + return (0 != (readlong(I810_GPIOB) & SCL_VAL_IN)); +} + +static int bit_i810i2c_getsda(void *data) +{ + outlong(0, I810_GPIOB); + return (0 != (readlong(I810_GPIOB) & SDA_VAL_IN)); +} + +static void bit_i810ddc_setscl(void *data, int val) +{ + outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, + I810_GPIOA); +} + +static void bit_i810ddc_setsda(void *data, int val) +{ + outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, + I810_GPIOA); +} + +static int bit_i810ddc_getscl(void *data) +{ + outlong(0, I810_GPIOA); + return (0 != (readlong(I810_GPIOA) & SCL_VAL_IN)); +} + +static int bit_i810ddc_getsda(void *data) +{ + outlong(0, I810_GPIOA); + return (0 != (readlong(I810_GPIOA) & SDA_VAL_IN)); +} + +static struct i2c_algo_bit_data i810_i2c_bit_data = { + NULL, + bit_i810i2c_setsda, + bit_i810i2c_setscl, + bit_i810i2c_getsda, + bit_i810i2c_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter i810_i2c_adapter = { + "I810/I815 I2C Adapter", + I2C_HW_B_I810, + NULL, + &i810_i2c_bit_data, + i810_inc, + i810_dec, + NULL, + NULL, +}; + +static struct i2c_algo_bit_data i810_ddc_bit_data = { + NULL, + bit_i810ddc_setsda, + bit_i810ddc_setscl, + bit_i810ddc_getsda, + bit_i810ddc_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter i810_ddc_adapter = { + "I810/I815 DDC Adapter", + I2C_HW_B_I810, + NULL, + &i810_ddc_bit_data, + i810_inc, + i810_dec, + NULL, + NULL, +}; + + +/* Configures the chip */ +void config_i810(struct pci_dev *dev) +{ + unsigned long cadr; + + /* map I810 memory */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + cadr = dev->resource[1].start; +#else + cadr = dev->base_address[1]; +#endif + cadr += I810_IOCONTROL_OFFSET; + cadr &= PCI_BASE_ADDRESS_MEM_MASK; + mem = ioremap_nocache(cadr, 0x1000); + bit_i810i2c_setscl(NULL, 1); + bit_i810i2c_setsda(NULL, 1); + bit_i810ddc_setscl(NULL, 1); + bit_i810ddc_setsda(NULL, 1); +} + +/* Detect whether a supported device can be found, + and initialize it */ +static int i810i2c_setup(void) +{ + struct pci_dev *dev = NULL; + int *num = i810_supported; + + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, + *num++, dev))) { + config_i810(dev); + printk("i2c-i810.o: i810/i815 found.\n"); + return 0; + } + } while (*num != 0); + + return -ENODEV; +} + + +void i810_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i810_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_i810_init(void) +{ + int res; + printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE); + + i810i2c_initialized = 0; + if ((res = i810i2c_setup())) { + printk + ("i2c-i810.o: i810/i815 not detected, module not inserted.\n"); + i810i2c_cleanup(); + return res; + } + if ((res = i2c_bit_add_bus(&i810_i2c_adapter))) { + printk("i2c-i810.o: I2C adapter registration failed\n"); + } else { + printk("i2c-i810.o: I810/I815 I2C bus initialized\n"); + i810i2c_initialized |= INIT2; + } + if ((res = i2c_bit_add_bus(&i810_ddc_adapter))) { + printk("i2c-i810.o: DDC adapter registration failed\n"); + } else { + printk("i2c-i810.o: I810/I815 DDC bus initialized\n"); + i810i2c_initialized |= INIT3; + } + if(!(i810i2c_initialized & (INIT2 | INIT3))) { + printk("i2c-i810.o: Both registrations failed, module not inserted\n"); + i810i2c_cleanup(); + return res; + } + return 0; +} + +int __init i810i2c_cleanup(void) +{ + int res; + + iounmap(mem); + if (i810i2c_initialized & INIT3) { + if ((res = i2c_bit_del_bus(&i810_ddc_adapter))) { + printk + ("i2c-i810.o: i2c_del_adapter failed, module not removed\n"); + return res; + } + } + if (i810i2c_initialized & INIT2) { + if ((res = i2c_bit_del_bus(&i810_i2c_adapter))) { + printk + ("i2c-i810.o: i2c_del_adapter failed, module not removed\n"); + return res; + } + } + i810i2c_initialized = 0; + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); + + +int init_module(void) +{ + return i2c_i810_init(); +} + +int cleanup_module(void) +{ + return i810i2c_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-isa.c b/drivers/i2c/i2c-isa.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-isa.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,157 @@ +/* + i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This implements an i2c algorithm/adapter for ISA bus. Not that this is + on first sight very useful; almost no functionality is preserved. + Except that it makes writing drivers for chips which can be on both + the SMBus and the ISA bus very much easier. See lm78.c for an example + of this. */ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +static void isa_inc_use(struct i2c_adapter *adapter); +static void isa_dec_use(struct i2c_adapter *adapter); +static u32 isa_func(struct i2c_adapter *adapter); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_isa_init(void); +static int __init isa_cleanup(void); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* This is the actual algorithm we define */ +static struct i2c_algorithm isa_algorithm = { + /* name */ "ISA bus algorithm", + /* id */ I2C_ALGO_ISA, + /* master_xfer */ NULL, + /* smbus_access */ NULL, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ &isa_func, +}; + +/* There can only be one... */ +static struct i2c_adapter isa_adapter = { + /* name */ "ISA main adapter", + /* id */ I2C_ALGO_ISA | I2C_HW_ISA, + /* algorithm */ &isa_algorithm, + /* algo_data */ NULL, + /* inc_use */ &isa_inc_use, + /* dec_use */ &isa_dec_use, + /* data */ NULL, + /* Other fields not initialized */ +}; + +/* Used in isa_init/cleanup */ +static int __initdata isa_initialized; + +void isa_inc_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void isa_dec_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* We can't do a thing... */ +static u32 isa_func(struct i2c_adapter *adapter) +{ + return 0; +} + +int __init i2c_isa_init(void) +{ + int res; + printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG + if (isa_initialized) { + printk + ("i2c-isa.o: Oops, isa_init called a second time!\n"); + return -EBUSY; + } +#endif + isa_initialized = 0; + if ((res = i2c_add_adapter(&isa_adapter))) { + printk("i2c-isa.o: Adapter registration failed, " + "module i2c-isa.o is not inserted\n."); + isa_cleanup(); + return res; + } + isa_initialized++; + printk("i2c-isa.o: ISA bus access for i2c modules initialized.\n"); + return 0; +} + +int __init isa_cleanup(void) +{ + int res; + if (isa_initialized >= 1) { + if ((res = i2c_del_adapter(&isa_adapter))) { + printk + ("i2c-isa.o: Adapter deregistration failed, module not removed.\n"); + return res; + } else + isa_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("ISA bus access through i2c"); + +int init_module(void) +{ + return i2c_isa_init(); +} + +int cleanup_module(void) +{ + return isa_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-piix4.c b/drivers/i2c/i2c-piix4.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-piix4.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,570 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* Note: We assume all devices are identical + to the Intel PIIX4; we only mention it during detection. */ + +#ifndef PCI_DEVICE_ID_SERVERWORKS_OSB4 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#endif + +#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB5 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#endif + +#ifndef PCI_VENDOR_ID_SERVERWORKS +#define PCI_VENDOR_ID_SERVERWORKS 0x01166 +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82443MX_3 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#endif + +#ifndef PCI_VENDOR_ID_EFAR +#define PCI_VENDOR_ID_EFAR 0x1055 +#endif + +#ifndef PCI_DEVICE_ID_EFAR_SLC90E66_3 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 +#endif + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +static struct sd supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, 3, "PIIX4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, 0, "OSB4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, 0, "CSB5"}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, 3, "440MX"}, + {PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3, 0, "Victory66"}, + {0, 0, 0, NULL} +}; + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_piix4_init(void); +static int __init piix4_cleanup(void); +static int piix4_setup(void); +static s32 piix4_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void piix4_do_pause(unsigned int amount); +static int piix4_transaction(void); +static void piix4_inc(struct i2c_adapter *adapter); +static void piix4_dec(struct i2c_adapter *adapter); +static u32 piix4_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ piix4_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + &smbus_algorithm, + NULL, + piix4_inc, + piix4_dec, + NULL, + NULL, +}; + +static int __initdata piix4_initialized; +static unsigned short piix4_smba = 0; + +/* Detect whether a PIIX4 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int piix4_setup(void) +{ + int error_return = 0; + unsigned char temp; + struct sd *num = supported; + struct pci_dev *PIIX4_dev = NULL; + + if (pci_present() == 0) { + error_return = -ENODEV; + goto END; + } + + /* Look for a supported device/function */ + do { + if((PIIX4_dev = pci_find_device(num->mfr, num->dev, + PIIX4_dev))) { + if(PCI_FUNC(PIIX4_dev->devfn) != num->fn) + continue; + break; + } + PIIX4_dev = NULL; + num++; + } while (num->mfr); + + if (PIIX4_dev == NULL) { + printk + ("i2c-piix4.o: Error: Can't detect PIIX4 or compatible device!\n"); + error_return = -ENODEV; + goto END; + } + printk("i2c-piix4.o: Found %s device\n", num->name); + + +/* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + } + + if (check_region(piix4_smba, 8)) { + printk + ("i2c-piix4.o: SMB region 0x%x already in use!\n", + piix4_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + printk + ("i2c-piix4.o: WARNING: SMBus interface set to new " + "address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + printk + ("i2c-piix4.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + ("SMBUS: Error: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(piix4_smba, 8, "piix4-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk + ("i2c-piix4.o: Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk + ("i2c-piix4.o: Using Interrupt SMI# for SMBus.\n"); + else + printk + ("i2c-piix4.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + printk("i2c-piix4.o: SMBREV = 0x%X\n", temp); + printk("i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void piix4_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-piix4.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-piix4.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-piix4.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + do { + piix4_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + +#ifdef DEBUG + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-piix4.o: SMBus Timeout!\n"); + result = -1; + } +#endif + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-piix4.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + ("i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-piix4.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + +#ifdef DEBUG + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + printk + ("i2c-piix4.o: Failed reset at end of transaction (%02x)\n", + temp); + } + printk + ("i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (piix4_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +void piix4_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void piix4_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_piix4_init(void) +{ + int res; + printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE); + if (piix4_initialized) { + printk + ("i2c-piix4.o: Oops, piix4_init called a second time!\n"); + return -EBUSY; + } + piix4_initialized = 0; + if ((res = piix4_setup())) { + printk + ("i2c-piix4.o: Device not detected, module not inserted.\n"); + piix4_cleanup(); + return res; + } + piix4_initialized++; + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", + piix4_smba); + if ((res = i2c_add_adapter(&piix4_adapter))) { + printk + ("i2c-piix4.o: Adapter registration failed, module not inserted.\n"); + piix4_cleanup(); + return res; + } + piix4_initialized++; + printk("i2c-piix4.o: SMBus detected and initialized\n"); + return 0; +} + +int __init piix4_cleanup(void) +{ + int res; + if (piix4_initialized >= 2) { + if ((res = i2c_del_adapter(&piix4_adapter))) { + printk + ("i2c-piix4.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + piix4_initialized--; + } + if (piix4_initialized >= 1) { + release_region(piix4_smba, 8); + piix4_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); + +int init_module(void) +{ + return i2c_piix4_init(); +} + +int cleanup_module(void) +{ + return piix4_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-sis5595.c b/drivers/i2c/i2c-sis5595.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-sis5595.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,548 @@ +/* + sis5595.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Note: we assume there can only be one SIS5595 with one SMBus interface */ + +/* + Note: all have mfr. ID 0x1039. + SUPPORTED PCI ID + 5595 0008 + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 730 0008 0730 +*/ + +/* TO DO: + * Add Block Transfers (ugly, but supported by the adapter) + * Add adapter resets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_SI_540 +#define PCI_DEVICE_ID_SI_540 0x0540 +#endif +#ifndef PCI_DEVICE_ID_SI_550 +#define PCI_DEVICE_ID_SI_550 0x0550 +#endif +#ifndef PCI_DEVICE_ID_SI_630 +#define PCI_DEVICE_ID_SI_630 0x0630 +#endif +#ifndef PCI_DEVICE_ID_SI_730 +#define PCI_DEVICE_ID_SI_730 0x0730 +#endif +#ifndef PCI_DEVICE_ID_SI_5598 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#endif + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but + that ID shows up in other chips so we + use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0 }; + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* SIS5595 SMBus registers */ +#define SMB_STS_LO 0x00 +#define SMB_STS_HI 0x01 +#define SMB_CTL_LO 0x02 +#define SMB_CTL_HI 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCNT 0x06 +#define SMB_CNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_HAA 0x13 + +/* PCI Address Constants */ +#define SMB_INDEX 0x38 +#define SMB_DAT 0x39 +#define SIS5595_ENABLE_REG 0x40 +#define ACPI_BASE 0x90 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS5595 constants */ +#define SIS5595_QUICK 0x00 +#define SIS5595_BYTE 0x02 +#define SIS5595_BYTE_DATA 0x04 +#define SIS5595_WORD_DATA 0x06 +#define SIS5595_PROC_CALL 0x08 +#define SIS5595_BLOCK_DATA 0x0A + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_sis5595_init(void); +static int __init sis5595_cleanup(void); +static int sis5595_setup(void); +static s32 sis5595_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void sis5595_do_pause(unsigned int amount); +static int sis5595_transaction(void); +static void sis5595_inc(struct i2c_adapter *adapter); +static void sis5595_dec(struct i2c_adapter *adapter); +static u32 sis5595_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ sis5595_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ sis5595_func, +}; + +static struct i2c_adapter sis5595_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595, + &smbus_algorithm, + NULL, + sis5595_inc, + sis5595_dec, + NULL, + NULL, +}; + +static int __initdata sis5595_initialized; +static unsigned short sis5595_base = 0; + +static u8 sis5595_read(u8 reg) +{ + outb(reg, sis5595_base + SMB_INDEX); + return inb(sis5595_base + SMB_DAT); +} + +static void sis5595_write(u8 reg, u8 data) +{ + outb(reg, sis5595_base + SMB_INDEX); + outb(data, sis5595_base + SMB_DAT); +} + + +/* Detect whether a SIS5595 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int sis5595_setup(void) +{ + u16 a; + u8 val; + struct pci_dev *SIS5595_dev; + int *i; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-sis5595.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the SIS5595 */ + SIS5595_dev = NULL; + if (!(SIS5595_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_503, + SIS5595_dev))) { + printk("i2c-sis5595.o: Error: Can't detect SIS5595!\n"); + return -ENODEV; + } + + /* Look for imposters */ + for(i = blacklist; *i != 0; i++) { + if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { + printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); + return -ENODEV; + } + } + +/* Determine the address of the SMBus areas */ + pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); + if(sis5595_base == 0 && force_addr == 0) { + printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); +#ifdef DEBUG + printk("ACPI Base address: %04x\n", sis5595_base); +#endif + /* NB: We grab just the two SMBus registers here, but this may still + * interfere with ACPI :-( */ + if (check_region(sis5595_base + SMB_INDEX, 2)) { + printk + ("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n", + sis5595_base + SMB_INDEX, + sis5595_base + SMB_INDEX + 1); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)) + return -ENODEV; + if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { + /* doesn't work for some chips! */ + printk("i2c-sis5595.o: force address failed - not supported?\n"); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { + printk("sis5595.o: enabling ACPI\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, + val | 0x80)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { /* doesn't work for some chips? */ + printk("sis5595.o: ACPI enable failed - not supported?\n"); + return -ENODEV; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus"); + return(0); +} + + +/* Internally used pause function */ +void sis5595_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int sis5595_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { +#ifdef DEBUG + printk("i2c-sis5595.o: SMBus busy (%04x). Resetting... \n", + temp); +#endif + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + if ( + (temp = + sis5595_read(SMB_STS_LO) + + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { +#ifdef DEBUG + printk("i2c-sis5595.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-sis5595.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 4 */ + sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); + + /* We will always wait for a fraction of a second! */ + do { + sis5595_do_pause(1); + temp = sis5595_read(SMB_STS_LO); + } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-sis5595.o: SMBus Timeout!\n"); +#endif + result = -1; + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-sis5595.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x20) { + result = -1; + printk + ("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n" + "reset (or not...)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + } + + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { + +#ifdef DEBUG + printk + ("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 sis5595_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + switch (size) { + case I2C_SMBUS_QUICK: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS5595_QUICK; + break; + case I2C_SMBUS_BYTE: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_CMD, command); + size = SIS5595_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_BYTE, data->byte); + size = SIS5595_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis5595_write(SMB_BYTE, data->word & 0xff); + sis5595_write(SMB_BYTE + 1, + (data->word & 0xff00) >> 8); + } + size = + (size == + I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : + SIS5595_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + printk("sis5595.o: Block data not yet implemented!\n"); + return -1; + break; + } + + sis5595_write(SMB_CTL_LO, ((size & 0x0E))); + + if (sis5595_transaction()) /* Error in transaction */ + return -1; + + if ((size != SIS5595_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) + return 0; + + + switch (size) { + case SIS5595_BYTE: /* Where is the result put? I assume here it is in + SMB_DATA but it might just as well be in the + SMB_CMD. No clue in the docs */ + case SIS5595_BYTE_DATA: + data->byte = sis5595_read(SMB_BYTE); + break; + case SIS5595_WORD_DATA: + case SIS5595_PROC_CALL: + data->word = + sis5595_read(SMB_BYTE) + + (sis5595_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +void sis5595_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void sis5595_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 sis5595_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL; +} + +int __init i2c_sis5595_init(void) +{ + int res; + printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (sis5595_initialized) { + printk + ("i2c-sis5595.o: Oops, sis5595_init called a second time!\n"); + return -EBUSY; + } +#endif + sis5595_initialized = 0; + if ((res = sis5595_setup())) { + printk + ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", + sis5595_base + SMB_INDEX); + if ((res = i2c_add_adapter(&sis5595_adapter))) { + printk + ("i2c-sis5595.o: Adapter registration failed, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + printk("i2c-sis5595.o: SIS5595 bus detected and initialized\n"); + return 0; +} + +int __init sis5595_cleanup(void) +{ + int res; + if (sis5595_initialized >= 2) { + if ((res = i2c_del_adapter(&sis5595_adapter))) { + printk + ("i2c-sis5595.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + sis5595_initialized--; + } + if (sis5595_initialized >= 1) { + release_region(sis5595_base + SMB_INDEX, 2); + sis5595_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("SIS5595 SMBus driver"); + +int init_module(void) +{ + return i2c_sis5595_init(); +} + +int cleanup_module(void) +{ + return sis5595_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-tsunami.c b/drivers/i2c/i2c-tsunami.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-tsunami.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,201 @@ +/* + i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Oleg Vdovikin + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets + to gain access to the on-board I2C devices. + + For more information refer to Compaq's + "Tsunami/Typhoon 21272 Chipset Hardware Reference Manual" + Order Number: DS-0025-TE +*/ + +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* Memory Presence Detect Register (MPD-RW) bits + with except of reserved RAZ bits */ + +#define MPD_DR 0x8 /* Data receive - RO */ +#define MPD_CKR 0x4 /* Clock receive - RO */ +#define MPD_DS 0x2 /* Data send - Must be a 1 to receive - WO */ +#define MPD_CKS 0x1 /* Clock send - WO */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_tsunami_init(void); +static int __init i2c_tsunami_cleanup(void); +static void i2c_tsunami_inc(struct i2c_adapter *adapter); +static void i2c_tsunami_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +extern inline void writempd(unsigned long value) +{ + TSUNAMI_cchip->mpd.csr = value; + mb(); +} + +extern inline unsigned long readmpd(void) +{ + return TSUNAMI_cchip->mpd.csr; +} + +static void bit_tsunami_setscl(void *data, int val) +{ + /* read currently setted bits to modify them */ + unsigned long bits = readmpd() >> 2; /* assume output == input */ + + if (val) + bits |= MPD_CKS; + else + bits &= ~MPD_CKS; + + writempd(bits); +} + +static void bit_tsunami_setsda(void *data, int val) +{ + /* read currently setted bits to modify them */ + unsigned long bits = readmpd() >> 2; /* assume output == input */ + + if (val) + bits |= MPD_DS; + else + bits &= ~MPD_DS; + + writempd(bits); +} + +/* The MPD pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. */ + +static int bit_tsunami_getscl(void *data) +{ + return (0 != (readmpd() & MPD_CKR)); +} + +static int bit_tsunami_getsda(void *data) +{ + return (0 != (readmpd() & MPD_DR)); +} + +static struct i2c_algo_bit_data tsunami_i2c_bit_data = { + NULL, + bit_tsunami_setsda, + bit_tsunami_setscl, + bit_tsunami_getsda, + bit_tsunami_getscl, + 10, 10, 50 /* delays/timeout */ +}; + +static struct i2c_adapter tsunami_i2c_adapter = { + "I2C Tsunami/Typhoon adapter", + I2C_HW_B_TSUNA, + NULL, + &tsunami_i2c_bit_data, + i2c_tsunami_inc, + i2c_tsunami_dec, + NULL, + NULL, +}; + +void i2c_tsunami_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i2c_tsunami_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_tsunami_init(void) +{ + int res; + printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE); + + if (hwrpb->sys_type != ST_DEC_TSUNAMI) { + printk("i2c-tsunami.o: not Tsunami based system (%d), module not inserted.\n", hwrpb->sys_type); + return -ENXIO; + } else { + printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", &TSUNAMI_cchip->mpd); + } + + if ((res = i2c_bit_add_bus(&tsunami_i2c_adapter))) { + printk("i2c-tsunami.o: I2C adapter registration failed\n"); + } else { + printk("i2c-tsunami.o: I2C bus initialized\n"); + } + + return res; +} + +int __init i2c_tsunami_cleanup(void) +{ + int res; + + if ((res = i2c_bit_del_bus(&tsunami_i2c_adapter))) { + printk("i2c-tsunami.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Oleg I. Vdovikin "); +MODULE_DESCRIPTION("Tsunami I2C/SMBus driver"); + +int init_module(void) +{ + return i2c_tsunami_init(); +} + +int cleanup_module(void) +{ + return i2c_tsunami_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-via.c b/drivers/i2c/i2c-via.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-via.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,226 @@ +/* + i2c-via.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + i2c Support for Via Technologies 82C586B South Bridge + + Copyright (c) 1998, 1999 Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI device */ +#define VENDOR PCI_VENDOR_ID_VIA +#define DEVICE PCI_DEVICE_ID_VIA_82C586_3 + +/* Power management registers */ + +#define PM_CFG_REVID 0x08 /* silicon revision code */ +#define PM_CFG_IOBASE0 0x20 +#define PM_CFG_IOBASE1 0x48 + +#define I2C_DIR (pm_io_base+0x40) +#define I2C_OUT (pm_io_base+0x42) +#define I2C_IN (pm_io_base+0x44) +#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 +#define IOTEXT "via-i2c" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) x /* silicon revision, io addresses */ +#define DEB2(x) x /* line status */ +#define DEBE(x) /* */ + +/* ----- local functions ---------------------------------------------- */ + +static u16 pm_io_base; + +/* + It does not appear from the datasheet that the GPIO pins are + open drain. So a we set a low value by setting the direction to + output and a high value by setting the direction to input and + relying on the required I2C pullup. The data value is initialized + to 0 in i2c_via_init() and never changed. +*/ + +static void bit_via_setscl(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, + I2C_DIR); +} + +static void bit_via_setsda(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, + I2C_DIR); +} + +static int bit_via_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL)); +} + +static int bit_via_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA)); +} + +static void bit_via_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void bit_via_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data bit_data = { + NULL, + bit_via_setsda, + bit_via_setscl, + bit_via_getsda, + bit_via_getscl, + 5, 5, 100, /*waits, timeout */ +}; + +static struct i2c_adapter bit_via_ops = { + "VIA i2c", + I2C_HW_B_VIA, + NULL, + &bit_data, + bit_via_inc, + bit_via_dec, + NULL, + NULL, +}; + + +/* When exactly was the new pci interface introduced? */ +static int find_via(void) +{ + struct pci_dev *s_bridge; + u16 base; + u8 rev; + + if (!pci_present()) + return -ENODEV; + + s_bridge = pci_find_device(VENDOR, DEVICE, NULL); + + if (!s_bridge) { + printk("i2c-via.o: vt82c586b not found\n"); + return -ENODEV; + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, PM_CFG_REVID, &rev)) + return -ENODEV; + + switch (rev) { + case 0x00: + base = PM_CFG_IOBASE0; + break; + case 0x01: + case 0x10: + base = PM_CFG_IOBASE1; + break; + + default: + base = PM_CFG_IOBASE1; + /* later revision */ + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, base, &pm_io_base)) + return -ENODEV; + + pm_io_base &= (0xff << 8); + return 0; +} + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_via_init(void) +{ + printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE); + if (find_via() < 0) { + printk("i2c-via.o: Error while reading PCI configuration\n"); + return -ENODEV; + } + + if (check_region(I2C_DIR, IOSPACE) < 0) { + printk("i2c-via.o: IO 0x%x-0x%x already in use\n", + I2C_DIR, I2C_DIR + IOSPACE); + return -EBUSY; + } else { + request_region(I2C_DIR, IOSPACE, IOTEXT); + outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); + outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); + } + + if (i2c_bit_add_bus(&bit_via_ops) == 0) { + printk("i2c-via.o: Module succesfully loaded\n"); + return 0; + } else { + release_region(I2C_DIR, IOSPACE); + printk + ("i2c-via.o: Algo-bit error, couldn't register bus\n"); + return -ENODEV; + } +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Kyösti Mälkki "); +MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); + +int init_module(void) +{ + return i2c_via_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_via_ops); + release_region(I2C_DIR, IOSPACE); +} +#endif diff -Nru a/drivers/i2c/i2c-viapro.c b/drivers/i2c/i2c-viapro.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-viapro.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,566 @@ +/* + i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , Kyösti Mälkki , + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports Via devices: + 82C596A/B (0x3050) + 82C596B (0x3051) + 82C686A/B + 8233 + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_VIA_82C596_3 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#endif +#ifndef PCI_DEVICE_ID_VIA_82C596B_3 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#endif +#ifndef PCI_DEVICE_ID_VIA_82C686_4 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#endif +#ifndef PCI_DEVICE_ID_VIA_8233_0 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#endif + +#define SMBBA1 0x90 +#define SMBBA2 0x80 +#define SMBBA3 0xD0 + +struct sd { + const unsigned short dev; + const unsigned char base; + const unsigned char hstcfg; + const char *name; +}; + +static struct sd supported[] = { + {PCI_DEVICE_ID_VIA_82C596_3, SMBBA1, 0xD2, "VT82C596A/B"}, + {PCI_DEVICE_ID_VIA_82C596B_3, SMBBA1, 0xD2, "VT82C596B"}, + {PCI_DEVICE_ID_VIA_82C686_4, SMBBA1, 0xD2, "VT82C686A/B"}, + {PCI_DEVICE_ID_VIA_8233_0, SMBBA3, 0xD2, "VT8233"}, + {0, 0, 0, NULL} +}; + +static struct sd *num = supported; + +/* SMBus address offsets */ +#define SMBHSTSTS (0 + vt596_smba) +#define SMBHSLVSTS (1 + vt596_smba) +#define SMBHSTCNT (2 + vt596_smba) +#define SMBHSTCMD (3 + vt596_smba) +#define SMBHSTADD (4 + vt596_smba) +#define SMBHSTDAT0 (5 + vt596_smba) +#define SMBHSTDAT1 (6 + vt596_smba) +#define SMBBLKDAT (7 + vt596_smba) +#define SMBSLVCNT (8 + vt596_smba) +#define SMBSHDWCMD (9 + vt596_smba) +#define SMBSLVEVT (0xA + vt596_smba) +#define SMBSLVDAT (0xC + vt596_smba) + +/* PCI Address Constants */ + +/* SMBus data in configuration space can be found in two places, + We try to select the better one*/ + +static unsigned short smb_cf_hstcfg; + +#define SMBHSTCFG (smb_cf_hstcfg) +#define SMBSLVC (SMBHSTCFG+1) +#define SMBSHDW1 (SMBHSTCFG+2) +#define SMBSHDW2 (SMBHSTCFG+3) +#define SMBREV (SMBHSTCFG+4) + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* VT82C596 constants */ +#define VT596_QUICK 0x00 +#define VT596_BYTE 0x04 +#define VT596_BYTE_DATA 0x08 +#define VT596_WORD_DATA 0x0C +#define VT596_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + VT596. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the VT596 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the SMBus at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_vt596_init(void); +static int __init vt596_cleanup(void); +static int vt596_setup(void); +static s32 vt596_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void vt596_do_pause(unsigned int amount); +static int vt596_transaction(void); +static void vt596_inc(struct i2c_adapter *adapter); +static void vt596_dec(struct i2c_adapter *adapter); +static u32 vt596_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ vt596_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ vt596_func, +}; + +static struct i2c_adapter vt596_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2, + &smbus_algorithm, + NULL, + vt596_inc, + vt596_dec, + NULL, + NULL, +}; + +static int __initdata vt596_initialized; +static unsigned short vt596_smba = 0; + + +/* Detect whether a compatible device can be found, and initialize it. */ +int vt596_setup(void) +{ + unsigned char temp; + + struct pci_dev *VT596_dev = NULL; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) + return(-ENODEV); + + /* Look for a supported device/function */ + do { + if((VT596_dev = pci_find_device(PCI_VENDOR_ID_VIA, num->dev, + VT596_dev))) + break; + } while ((++num)->dev); + + if (VT596_dev == NULL) + return(-ENODEV); + printk("i2c-viapro.o: Found Via %s device\n", num->name); + +/* Determine the address of the SMBus areas */ + smb_cf_hstcfg = num->hstcfg; + if (force_addr) { + vt596_smba = force_addr; + force = 0; + } else { + if ((pci_read_config_word(VT596_dev, num->base, &vt596_smba)) + || !(vt596_smba & 0x1)) { + /* try 2nd address and config reg. for 596 */ + if((num->dev == PCI_DEVICE_ID_VIA_82C596_3) && + (!pci_read_config_word(VT596_dev, SMBBA2, &vt596_smba)) && + (vt596_smba & 0x1)) { + smb_cf_hstcfg = 0x84; + } else { + /* no matches at all */ + printk("i2c-viapro.o: Cannot configure SMBus " + "I/O Base address\n"); + return(-ENODEV); + } + } + } + vt596_smba &= 0xfff0; + + if (check_region(vt596_smba, 8)) { + printk("i2c-viapro.o: SMB region 0x%x already in use!\n", + vt596_smba); + return(-ENODEV); + } + + pci_read_config_byte(VT596_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the VT596 first. */ + if (force_addr) { + pci_write_config_byte(VT596_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(VT596_dev, num->base, vt596_smba); + pci_write_config_byte(VT596_dev, SMBHSTCFG, temp | 0x01); + printk + ("i2c-viapro.o: WARNING: SMBus interface set to new " + "address 0x%04x!\n", vt596_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(VT596_dev, SMBHSTCFG, + temp | 1); + printk + ("i2c-viapro.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + ("SMBUS: Error: Host SMBus controller not enabled!\n"); + return(-ENODEV); + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(vt596_smba, 8, "via2-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk("i2c-viapro.o: using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk("i2c-viapro.o: using Interrupt SMI# for SMBus.\n"); + else + printk + ("i2c-viapro.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(VT596_dev, SMBREV, &temp); + printk("i2c-viapro.o: SMBREV = 0x%X\n", temp); + printk("i2c-viapro.o: VT596_smba = 0x%X\n", vt596_smba); +#endif /* DEBUG */ + + return(0); +} + + +/* Internally used pause function */ +void vt596_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int vt596_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-viapro.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-viapro.o: SMBus busy (0x%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-viapro.o: Failed! (0x%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-viapro.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! + I don't know if VIA needs this, Intel did */ + do { + vt596_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-viapro.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-viapro.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + ("i2c-viapro.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-viapro.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk + ("i2c-viapro.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-viapro.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 vt596_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-viapro.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = VT596_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = VT596_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = VT596_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = VT596_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = VT596_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (vt596_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) + return 0; + + + switch (size) { + case VT596_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case VT596_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +void vt596_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void vt596_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 vt596_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_vt596_init(void) +{ + int res; + printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (vt596_initialized) { + printk + ("i2c-viapro.o: Oops, vt596_init called a second time!\n"); + return -EBUSY; + } +#endif + vt596_initialized = 0; + if ((res = vt596_setup())) { + printk + ("i2c-viapro.o: Can't detect vt82c596 or compatible device, module not inserted.\n"); + vt596_cleanup(); + return res; + } + vt596_initialized++; + sprintf(vt596_adapter.name, "SMBus Via Pro adapter at %04x", + vt596_smba); + if ((res = i2c_add_adapter(&vt596_adapter))) { + printk + ("i2c-viapro.o: Adapter registration failed, module not inserted.\n"); + vt596_cleanup(); + return res; + } + vt596_initialized++; + printk("i2c-viapro.o: Via Pro SMBus detected and initialized\n"); + return 0; +} + +int __init vt596_cleanup(void) +{ + int res; + if (vt596_initialized >= 2) { + if ((res = i2c_del_adapter(&vt596_adapter))) { + printk + ("i2c-viapro.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + vt596_initialized--; + } + if (vt596_initialized >= 1) { + release_region(vt596_smba, 8); + vt596_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("vt82c596 SMBus driver"); + + +int init_module(void) +{ + return i2c_vt596_init(); +} + +int cleanup_module(void) +{ + return vt596_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/i2c/i2c-voodoo3.c b/drivers/i2c/i2c-voodoo3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-voodoo3.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,362 @@ +/* + voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + Philip Edelbrock , + Ralph Metzler , and + Mark D. Studebaker + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This interfaces to the I2C bus of the Voodoo3 to gain access to + the BT869 and possibly other I2C devices. */ + +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* 3DFX defines */ +#ifndef PCI_DEVICE_ID_3DFX_VOODOO3 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05 +#endif +#ifndef PCI_DEVICE_ID_3DFX_BANSHEE +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x03 +#endif + +/* the only registers we use */ +#define REG 0x78 +#define REG2 0x70 + +/* bit locations in the register */ +#define DDC_ENAB 0x00040000 +#define DDC_SCL_OUT 0x00080000 +#define DDC_SDA_OUT 0x00100000 +#define DDC_SCL_IN 0x00200000 +#define DDC_SDA_IN 0x00400000 +#define I2C_ENAB 0x00800000 +#define I2C_SCL_OUT 0x01000000 +#define I2C_SDA_OUT 0x02000000 +#define I2C_SCL_IN 0x04000000 +#define I2C_SDA_IN 0x08000000 + +/* initialization states */ +#define INIT2 0x2 +#define INIT3 0x4 + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT 50 + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_voodoo3_init(void); +static int __init voodoo3_cleanup(void); +static int voodoo3_setup(void); +static void config_v3(struct pci_dev *dev); +static void voodoo3_inc(struct i2c_adapter *adapter); +static void voodoo3_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + + +static int __initdata voodoo3_initialized; +static unsigned char *mem; + +extern inline void outlong(unsigned int dat) +{ + *((unsigned int *) (mem + REG)) = dat; +} + +extern inline unsigned int readlong(void) +{ + return *((unsigned int *) (mem + REG)); +} + +/* The voo GPIO registers don't have individual masks for each bit + so we always have to read before writing. */ + +static void bit_vooi2c_setscl(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SCL_OUT; + else + r &= ~I2C_SCL_OUT; + outlong(r); +} + +static void bit_vooi2c_setsda(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SDA_OUT; + else + r &= ~I2C_SDA_OUT; + outlong(r); +} + +/* The GPIO pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. */ + +static int bit_vooi2c_getscl(void *data) +{ + return (0 != (readlong() & I2C_SCL_IN)); +} + +static int bit_vooi2c_getsda(void *data) +{ + return (0 != (readlong() & I2C_SDA_IN)); +} + +static void bit_vooddc_setscl(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= DDC_SCL_OUT; + else + r &= ~DDC_SCL_OUT; + outlong(r); +} + +static void bit_vooddc_setsda(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= DDC_SDA_OUT; + else + r &= ~DDC_SDA_OUT; + outlong(r); +} + +static int bit_vooddc_getscl(void *data) +{ + return (0 != (readlong() & DDC_SCL_IN)); +} + +static int bit_vooddc_getsda(void *data) +{ + return (0 != (readlong() & DDC_SDA_IN)); +} + +static struct i2c_algo_bit_data voo_i2c_bit_data = { + NULL, + bit_vooi2c_setsda, + bit_vooi2c_setscl, + bit_vooi2c_getsda, + bit_vooi2c_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter voodoo3_i2c_adapter = { + "I2C Voodoo3/Banshee adapter", + I2C_HW_B_VOO, + NULL, + &voo_i2c_bit_data, + voodoo3_inc, + voodoo3_dec, + NULL, + NULL, +}; + +static struct i2c_algo_bit_data voo_ddc_bit_data = { + NULL, + bit_vooddc_setsda, + bit_vooddc_setscl, + bit_vooddc_getsda, + bit_vooddc_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter voodoo3_ddc_adapter = { + "DDC Voodoo3/Banshee adapter", + I2C_HW_B_VOO, + NULL, + &voo_ddc_bit_data, + voodoo3_inc, + voodoo3_dec, + NULL, + NULL, +}; + +/* Configures the chip */ + +void config_v3(struct pci_dev *dev) +{ + unsigned int cadr; + + /* map Voodoo3 memory */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + cadr = dev->resource[0].start; +#else + cadr = dev->base_address[0]; +#endif + cadr &= PCI_BASE_ADDRESS_MEM_MASK; + mem = ioremap_nocache(cadr, 0x1000); + + *((unsigned int *) (mem + REG2)) = 0x8160; + *((unsigned int *) (mem + REG)) = 0xcffc0020; + printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%p\n", mem); +} + +/* Detect whether a Voodoo3 or a Banshee can be found, + and initialize it. */ +static int voodoo3_setup(void) +{ + struct pci_dev *dev; + int v3_num; + + v3_num = 0; + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX, + PCI_DEVICE_ID_3DFX_VOODOO3, + dev))) { + if (!v3_num) + config_v3(dev); + v3_num++; + } + } while (dev); + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX, + PCI_DEVICE_ID_3DFX_BANSHEE, + dev))) { + if (!v3_num) + config_v3(dev); + v3_num++; + } + } while (dev); + + if (v3_num > 0) { + printk("i2c-voodoo3: %d Banshee/Voodoo3 found.\n", v3_num); + if (v3_num > 1) + printk("i2c-voodoo3: warning: only 1 supported.\n"); + return 0; + } else { + printk("i2c-voodoo3: No Voodoo3 found.\n"); + return -ENODEV; + } +} + +void voodoo3_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void voodoo3_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_voodoo3_init(void) +{ + int res; + printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE); + voodoo3_initialized = 0; + if ((res = voodoo3_setup())) { + printk + ("i2c-voodoo3.o: Voodoo3 not detected, module not inserted.\n"); + voodoo3_cleanup(); + return res; + } + if ((res = i2c_bit_add_bus(&voodoo3_i2c_adapter))) { + printk("i2c-voodoo3.o: I2C adapter registration failed\n"); + } else { + printk("i2c-voodoo3.o: I2C bus initialized\n"); + voodoo3_initialized |= INIT2; + } + if ((res = i2c_bit_add_bus(&voodoo3_ddc_adapter))) { + printk("i2c-voodoo3.o: DDC adapter registration failed\n"); + } else { + printk("i2c-voodoo3.o: DDC bus initialized\n"); + voodoo3_initialized |= INIT3; + } + if(!(voodoo3_initialized & (INIT2 | INIT3))) { + printk("i2c-voodoo3.o: Both registrations failed, module not inserted\n"); + voodoo3_cleanup(); + return res; + } + return 0; +} + +int __init voodoo3_cleanup(void) +{ + int res; + + iounmap(mem); + if (voodoo3_initialized & INIT3) { + if ((res = i2c_bit_del_bus(&voodoo3_ddc_adapter))) { + printk + ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + } + if (voodoo3_initialized & INIT2) { + if ((res = i2c_bit_del_bus(&voodoo3_i2c_adapter))) { + printk + ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); +MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); + + +int init_module(void) +{ + return i2c_voodoo3_init(); +} + +int cleanup_module(void) +{ + return voodoo3_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in --- a/drivers/ide/Config.in Wed Feb 13 20:03:48 2002 +++ b/drivers/ide/Config.in Wed Feb 13 20:03:48 2002 @@ -14,6 +14,7 @@ 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_mbool ' Auto-Geometry Resizing support' CONFIG_IDEDISK_STROKE $CONFIG_BLK_DEV_IDEDISK define_bool CONFIG_BLK_DEV_IDEDISK_VENDOR n dep_mbool ' Fujitsu Vendor Specific' CONFIG_BLK_DEV_IDEDISK_FUJITSU $CONFIG_BLK_DEV_IDEDISK_VENDOR @@ -32,6 +33,9 @@ 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 $CONFIG_SCSI + bool ' IDE Taskfile Access' CONFIG_IDE_TASK_IOCTL + bool ' IDE Taskfile IO' CONFIG_IDE_TASKFILE_IO + comment 'IDE chipset support/bugfixes' if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86 @@ -43,14 +47,17 @@ 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 ' Asynchronous DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_ADMA $CONFIG_BLK_DEV_IDEDMA_PCI - define_bool CONFIG_BLK_DEV_ADMA $CONFIG_BLK_DEV_IDEDMA_PCI bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD + dep_bool ' Force enable legacy 2.0.X HOSTS to use DMA' CONFIG_BLK_DEV_IDEDMA_FORCED $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL -# dep_bool ' Attempt to HACK around Chipsets that TIMEOUT (WIP)' CONFIG_BLK_DEV_IDEDMA_TIMEOUT $CONFIG_IDEDMA_PCI_WIP + dep_bool ' Attempt to HACK around Chipsets that TIMEOUT (WIP)' CONFIG_BLK_DEV_IDEDMA_TIMEOUT $CONFIG_IDEDMA_PCI_WIP dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP +# dep_bool ' Asynchronous DMA support (WIP) (EXPERIMENTAL)' CONFIG_BLK_DEV_ADMA $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_IDEDMA_PCI_WIP + define_bool CONFIG_BLK_DEV_ADMA $CONFIG_BLK_DEV_IDEDMA_PCI +# dep_bool ' Tag Command Queue DMA support (WIP) (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDMA_TCQ $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_IDEDMA_PCI_WIP dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_mbool ' AEC62XX Tuning support' CONFIG_AEC62XX_TUNING $CONFIG_BLK_DEV_AEC62XX @@ -59,6 +66,7 @@ dep_bool ' AMD Viper support' CONFIG_BLK_DEV_AMD74XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_mbool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD74XX_OVERRIDE $CONFIG_BLK_DEV_AMD74XX $CONFIG_IDEDMA_PCI_WIP dep_bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' CMD680 chipset tuning support' CONFIG_BLK_DEV_CMD680 $CONFIG_BLK_DEV_CMD64X dep_bool ' CY82C693 chipset support' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDEDMA_PCI 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 @@ -74,7 +82,8 @@ fi dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL - dep_bool ' PROMISE PDC202{46|62|65|67|68} support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' Pacific Digital A-DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC_ADMA $CONFIG_BLK_DEV_ADMA $CONFIG_IDEDMA_PCI_WIP + dep_bool ' PROMISE PDC202{46|62|65|67|68|69|70} support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX dep_bool ' Special FastTrak Feature' CONFIG_PDC202XX_FORCE $CONFIG_BLK_DEV_PDC202XX dep_bool ' ServerWorks OSB4/CSB5 chipsets support' CONFIG_BLK_DEV_SVWKS $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86 @@ -84,8 +93,6 @@ dep_bool ' VIA82CXXX chipset support' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDEDMA_PCI fi -# dep_mbool ' Pacific Digital A-DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC_ADMA $CONFIG_BLK_DEV_ADMA $CONFIG_IDEDMA_PCI_WIP - if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 fi @@ -149,6 +156,8 @@ bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672 fi fi + + bool ' Use the NOOP Elevator (WARNING)' CONFIG_BLK_DEV_ELEVATOR_NOOP 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 @@ -173,6 +182,7 @@ else define_bool CONFIG_DMA_NONPCI n fi + if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \ "$CONFIG_BLK_DEV_AEC62XX" = "y" -o \ "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \ diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile --- a/drivers/ide/Makefile Wed Feb 13 20:03:34 2002 +++ b/drivers/ide/Makefile Wed Feb 13 20:03:34 2002 @@ -10,7 +10,7 @@ O_TARGET := idedriver.o -export-objs := ide.o ide-features.o ataraid.o +export-objs := ide.o ide-features.o ide-probe.o ide-taskfile.o ataraid.o list-multi := ide-mod.o ide-probe-mod.o obj-y := @@ -24,6 +24,7 @@ obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o + obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o ide-obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o @@ -69,13 +70,13 @@ # The virtualised raid layers MUST come after the ide itself or bad stuff # will happen. -obj-$(CONFIG_BLK_DEV_ATARAID) += ataraid.o +obj-$(CONFIG_BLK_DEV_ATARAID) += ataraid.o obj-$(CONFIG_BLK_DEV_ATARAID_PDC) += pdcraid.o obj-$(CONFIG_BLK_DEV_ATARAID_HPT) += hptraid.o ide-obj-$(CONFIG_PROC_FS) += ide-proc.o -ide-mod-objs := ide.o ide-features.o $(ide-obj-y) +ide-mod-objs := ide.o ide-features.o ide-taskfile.o $(ide-obj-y) ide-probe-mod-objs := ide-probe.o ide-geometry.o include $(TOPDIR)/Rules.make diff -Nru a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c --- a/drivers/ide/alim15x3.c Wed Feb 13 20:03:34 2002 +++ b/drivers/ide/alim15x3.c Wed Feb 13 20:03:34 2002 @@ -453,7 +453,7 @@ } dma_func = ide_dma_off_quietly; if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x003F) { /* Force if Capable UltraDMA */ dma_func = config_chipset_for_dma(drive, can_ultra_dma); if ((id->field_valid & 2) && diff -Nru a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c --- a/drivers/ide/amd74xx.c Wed Feb 13 20:03:30 2002 +++ b/drivers/ide/amd74xx.c Wed Feb 13 20:03:30 2002 @@ -75,7 +75,8 @@ { unsigned int class_rev; - if (dev->device == PCI_DEVICE_ID_AMD_VIPER_7411) + if ((dev->device == PCI_DEVICE_ID_AMD_VIPER_7411) || + (dev->device == PCI_DEVICE_ID_AMD_VIPER_7441)) return 0; pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); @@ -122,8 +123,8 @@ pci_read_config_byte(dev, 0x4c, &pio_timing); #ifdef DEBUG - printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ", - drive->name, ultra_timing, dma_pio_timing, pio_timing); + printk("%s:%d: Speed 0x%02x UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n", + drive->name, drive->dn, speed, ultra_timing, dma_pio_timing, pio_timing); #endif ultra_timing &= ~0xC7; @@ -131,22 +132,19 @@ pio_timing &= ~(0x03 << drive->dn); #ifdef DEBUG - printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ", - ultra_timing, dma_pio_timing, pio_timing); + printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n", + drive->name, ultra_timing, dma_pio_timing, pio_timing); #endif switch(speed) { #ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_7: + case XFER_UDMA_6: + speed = XFER_UDMA_5; case XFER_UDMA_5: -#undef __CAN_MODE_5 -#ifdef __CAN_MODE_5 ultra_timing |= 0x46; dma_pio_timing |= 0x20; break; -#else - printk("%s: setting to mode 4, driver problems in mode 5.\n", drive->name); - speed = XFER_UDMA_4; -#endif /* __CAN_MODE_5 */ case XFER_UDMA_4: ultra_timing |= 0x45; dma_pio_timing |= 0x20; @@ -164,7 +162,7 @@ dma_pio_timing |= 0x20; break; case XFER_UDMA_0: - ultra_timing |= 0x42; + ultra_timing |= 0x42; dma_pio_timing |= 0x20; break; case XFER_MW_DMA_2: @@ -222,8 +220,8 @@ pci_write_config_byte(dev, 0x4c, pio_timing); #ifdef DEBUG - printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n", - ultra_timing, dma_pio_timing, pio_timing); + printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n", + drive->name, ultra_timing, dma_pio_timing, pio_timing); #endif #ifdef CONFIG_BLK_DEV_IDEDMA @@ -303,11 +301,15 @@ struct pci_dev *dev = hwif->pci_dev; struct hd_driveid *id = drive->id; byte udma_66 = eighty_ninty_three(drive); - byte udma_100 = (dev->device==PCI_DEVICE_ID_AMD_VIPER_7411) ? 1 : 0; + byte udma_100 = ((dev->device==PCI_DEVICE_ID_AMD_VIPER_7411)|| + (dev->device==PCI_DEVICE_ID_AMD_VIPER_7441)) ? 1 : 0; byte speed = 0x00; int rval; - if ((id->dma_ultra & 0x0020) && (udma_66)&& (udma_100)) { + if (udma_100) + udma_66 = eighty_ninty_three(drive); + + if ((id->dma_ultra & 0x0020) && (udma_66) && (udma_100)) { speed = XFER_UDMA_5; } else if ((id->dma_ultra & 0x0010) && (udma_66)) { speed = XFER_UDMA_4; @@ -331,7 +333,7 @@ (void) amd74xx_tune_chipset(drive, speed); - rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on : + rval = (int)( ((id->dma_ultra >> 11) & 7) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : ((id->dma_mword >> 8) & 7) ? ide_dma_on : ide_dma_off_quietly); @@ -352,7 +354,7 @@ } dma_func = ide_dma_off_quietly; if (id->field_valid & 4) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x003F) { /* Force if Capable UltraDMA */ dma_func = config_chipset_for_dma(drive); if ((id->field_valid & 2) && @@ -442,17 +444,43 @@ unsigned int __init ata66_amd74xx (ide_hwif_t *hwif) { + struct pci_dev *dev = hwif->pci_dev; + byte cable_80_pin[2] = { 0, 0 }; + byte ata66 = 0; + byte tmpbyte; + + /* + * Ultra66 cable detection (from Host View) + * 7411, 7441, 0x42, bit0: primary, bit2: secondary 80 pin + */ + pci_read_config_byte(dev, 0x42, &tmpbyte); + + /* + * 0x42, bit0 is 1 => primary channel + * has 80-pin (from host view) + */ + if (tmpbyte & 0x01) cable_80_pin[0] = 1; + + /* + * 0x42, bit2 is 1 => secondary channel + * has 80-pin (from host view) + */ + if (tmpbyte & 0x04) cable_80_pin[1] = 1; + + switch(dev->device) { + case PCI_DEVICE_ID_AMD_VIPER_7441: + case PCI_DEVICE_ID_AMD_VIPER_7411: + ata66 = (hwif->channel) ? + cable_80_pin[1] : + cable_80_pin[0]; + default: + break; + } #ifdef CONFIG_AMD74XX_OVERRIDE - byte ata66 = 1; + return(1); #else - byte ata66 = 0; + return (unsigned int) ata66; #endif /* CONFIG_AMD74XX_OVERRIDE */ - -#if 0 - pci_read_config_byte(hwif->pci_dev, 0x48, &ata66); - return ((ata66 & 0x02) ? 0 : 1); -#endif - return ata66; } void __init ide_init_amd74xx (ide_hwif_t *hwif) diff -Nru a/drivers/ide/ataraid.c b/drivers/ide/ataraid.c --- a/drivers/ide/ataraid.c Wed Feb 13 20:03:51 2002 +++ b/drivers/ide/ataraid.c Wed Feb 13 20:03:51 2002 @@ -123,8 +123,7 @@ ptr=kmalloc(sizeof(struct buffer_head),GFP_NOIO); if (!ptr) { __set_current_state(TASK_RUNNING); - current->policy |= SCHED_YIELD; - schedule(); + yield(); } } return ptr; @@ -139,8 +138,7 @@ ptr=kmalloc(sizeof(struct ataraid_bh_private),GFP_NOIO); if (!ptr) { __set_current_state(TASK_RUNNING); - current->policy |= SCHED_YIELD; - schedule(); + yield(); } } return ptr; diff -Nru a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c --- a/drivers/ide/cmd64x.c Wed Feb 13 20:03:48 2002 +++ b/drivers/ide/cmd64x.c Wed Feb 13 20:03:48 2002 @@ -86,6 +86,7 @@ #include static int cmd64x_get_info(char *, char **, off_t, int); +static int cmd680_get_info(char *, char **, off_t, int); extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */ extern char *ide_media_verbose(ide_drive_t *); static struct pci_dev *bmide_dev; @@ -180,24 +181,21 @@ return p-buffer; /* => must be less than 4k! */ } -#if 0 -static char * cmd64x_chipset_data (char *buf, struct pci_dev *dev) -{ - char *p = buf; - p += sprintf(p, "thingy stuff\n"); - return (char *)p; -} -static int __init cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) +static int cmd680_get_info (char *buffer, char **addr, off_t offset, int count) { char *p = buffer; - p = cmd64x_chipset_data(buffer, bmide_dev); - return p-buffer; /* hoping it is less than 4K... */ + p += sprintf(p, "\n CMD680 Chipset.\n"); + p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); + p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "PIO Mode: %s %s %s %s\n", + "?", "?", "?", "?"); + return p-buffer; /* => must be less than 4k! */ } -#endif #endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */ byte cmd64x_proc = 0; +byte cmd680_proc = 0; /* * Registers and masks for easy access by drive index: @@ -345,10 +343,58 @@ setup_count, active_count, recovery_count); } -static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +static byte cmd680_taskfile_timing (ide_hwif_t *hwif) { - byte speed= 0x00; - byte set_pio= ide_get_best_pio_mode(drive, 4, 5, NULL); + struct pci_dev *dev = hwif->pci_dev; + byte addr_mask = (hwif->channel) ? 0xB2 : 0xA2; + unsigned short timing; + + pci_read_config_word(dev, addr_mask, &timing); + + switch (timing) { + case 0x10c1: return 4; + case 0x10c3: return 3; + case 0x1281: return 2; + case 0x2283: return 1; + case 0x328a: + default: return 0; + } +} + +static void cmd680_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte drive_pci; + unsigned short speedt; + + switch (drive->dn) { + case 0: drive_pci = 0xA4; break; + case 1: drive_pci = 0xA6; break; + case 2: drive_pci = 0xB4; break; + case 3: drive_pci = 0xB6; break; + default: return; + } + + pci_read_config_word(dev, drive_pci, &speedt); + + /* cheat for now and use the docs */ +// switch(cmd680_taskfile_timing(hwif)) { + switch(mode_wanted) { + case 4: speedt = 0x10c1; break; + case 3: speedt = 0x10C3; break; + case 2: speedt = 0x1104; break; + case 1: speedt = 0x2283; break; + case 0: + default: speedt = 0x328A; break; + } + pci_write_config_word(dev, drive_pci, speedt); +} + +static void config_cmd64x_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + byte speed = 0x00; + byte set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); cmd64x_tuneproc(drive, set_pio); speed = XFER_PIO_0 + set_pio; @@ -356,6 +402,41 @@ (void) ide_config_drive_speed(drive, speed); } +static void config_cmd680_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 unit = (drive->select.b.unit & 0x01); + u8 addr_mask = (hwif->channel) ? 0x84 : 0x80; + u8 speed = 0x00; + u8 mode_pci = 0x00; + u8 channel_timings = cmd680_taskfile_timing(hwif); + u8 set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + + pci_read_config_byte(dev, addr_mask, &mode_pci); + mode_pci &= ~((unit) ? 0x30 : 0x03); + + /* WARNING PIO timing mess is going to happen b/w devices, argh */ + if ((channel_timings != set_pio) && (set_pio > channel_timings)) + set_pio = channel_timings; + + cmd680_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + if (set_speed) { + (void) ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + } +} + +static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + if (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_CMD_680) { + config_cmd680_chipset_for_pio(drive, set_speed); + } else { + config_cmd64x_chipset_for_pio(drive, set_speed); + } +} + static int cmd64x_tune_chipset (ide_drive_t *drive, byte speed) { #ifdef CONFIG_BLK_DEV_IDEDMA @@ -363,7 +444,7 @@ struct pci_dev *dev = hwif->pci_dev; int err = 0; - byte unit = (drive->select.b.unit & 0x01); + u8 unit = (drive->select.b.unit & 0x01); u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0; u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0; u8 regU = 0; @@ -424,8 +505,123 @@ return err; } +static int cmd680_tune_chipset (ide_drive_t *drive, byte speed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 addr_mask = (hwif->channel) ? 0x84 : 0x80; + u8 unit = (drive->select.b.unit & 0x01); + u8 dma_pci = 0; + u8 udma_pci = 0; + u8 mode_pci = 0; + u8 scsc = 0; + u16 ultra = 0; + u16 multi = 0; + int err = 0; + + pci_read_config_byte(dev, addr_mask, &mode_pci); + pci_read_config_byte(dev, 0x8A, &scsc); + + switch (drive->dn) { + case 0: dma_pci = 0xA8; udma_pci = 0xAC; break; + case 1: dma_pci = 0xAA; udma_pci = 0xAE; break; + case 2: dma_pci = 0xB8; udma_pci = 0xBC; break; + case 3: dma_pci = 0xBA; udma_pci = 0xBE; break; + default: return 1; + } + + pci_read_config_byte(dev, addr_mask, &mode_pci); + mode_pci &= ~((unit) ? 0x30 : 0x03); + pci_read_config_word(dev, dma_pci, &multi); + pci_read_config_word(dev, udma_pci, &ultra); + + if ((speed == XFER_UDMA_6) && (scsc & 0x30) == 0x00) { + pci_write_config_byte(dev, 0x8A, scsc|0x01); + pci_read_config_byte(dev, 0x8A, &scsc); + } + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_6: + if ((scsc & 0x30) == 0x00) + goto speed_break; + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= 0x01; + break; +speed_break : + speed = XFER_UDMA_5; + case XFER_UDMA_5: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x01 : 0x02); + break; + case XFER_UDMA_4: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x02 : 0x03); + break; + case XFER_UDMA_3: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x04 : 0x05); + break; + case XFER_UDMA_2: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x05 : 0x07); + break; + case XFER_UDMA_1: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x07 : 0x0B); + break; + case XFER_UDMA_0: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x0C : 0x0F); + break; + case XFER_MW_DMA_2: + multi = 0x10C1; + break; + case XFER_MW_DMA_1: + multi = 0x10C2; + break; + case XFER_MW_DMA_0: + multi = 0x2208; + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: cmd680_tuneproc(drive, 4); break; + case XFER_PIO_3: cmd680_tuneproc(drive, 3); break; + case XFER_PIO_2: cmd680_tuneproc(drive, 2); break; + case XFER_PIO_1: cmd680_tuneproc(drive, 1); break; + case XFER_PIO_0: cmd680_tuneproc(drive, 0); break; + default: + return 1; + } + + + if (speed >= XFER_MW_DMA_0) + config_cmd680_chipset_for_pio(drive, 0); + + if (speed >= XFER_UDMA_0) + mode_pci |= ((unit) ? 0x30 : 0x03); + else if (speed >= XFER_MW_DMA_0) + mode_pci |= ((unit) ? 0x20 : 0x02); + else + mode_pci |= ((unit) ? 0x10 : 0x01); + + pci_write_config_byte(dev, addr_mask, mode_pci); + pci_write_config_word(dev, dma_pci, multi); + pci_write_config_word(dev, udma_pci, ultra); + + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + return err; +} + #ifdef CONFIG_BLK_DEV_IDEDMA -static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66) +static int config_cmd64x_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66) { struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); @@ -510,6 +706,55 @@ return rval; } +static int config_cmd680_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte udma_66 = eighty_ninty_three(drive); + byte speed = 0x00; + byte set_pio = 0x00; + int rval; + + if ((id->dma_ultra & 0x0040) && (udma_66)) speed = XFER_UDMA_6; + else if ((id->dma_ultra & 0x0020) && (udma_66)) speed = XFER_UDMA_5; + else if ((id->dma_ultra & 0x0010) && (udma_66)) speed = XFER_UDMA_4; + else if ((id->dma_ultra & 0x0008) && (udma_66)) speed = XFER_UDMA_3; + else if (id->dma_ultra & 0x0004) speed = XFER_UDMA_2; + else if (id->dma_ultra & 0x0002) speed = XFER_UDMA_1; + else if (id->dma_ultra & 0x0001) speed = XFER_UDMA_0; + else if (id->dma_mword & 0x0004) speed = XFER_MW_DMA_2; + else if (id->dma_mword & 0x0002) speed = XFER_MW_DMA_1; + else if (id->dma_mword & 0x0001) speed = XFER_MW_DMA_0; + else { + set_pio = 1; + } + + if (!drive->init_speed) + drive->init_speed = speed; + + config_chipset_for_pio(drive, set_pio); + + if (set_pio) + return ((int) ide_dma_off_quietly); + + if (cmd680_tune_chipset(drive, speed)) + return ((int) ide_dma_off); + + rval = (int)( ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); + return rval; +} + +static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66) +{ + if (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_CMD_680) + return (config_cmd680_chipset_for_dma(drive)); + return (config_cmd64x_chipset_for_dma(drive, rev, ultra_66)); +} + static int cmd64x_config_drive_for_dma (ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -519,12 +764,15 @@ byte can_ultra_33 = 0; byte can_ultra_66 = 0; byte can_ultra_100 = 0; + byte can_ultra_133 = 0; ide_dma_action_t dma_func = ide_dma_on; pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; switch(dev->device) { + case PCI_DEVICE_ID_CMD_680: + can_ultra_133 = 1; case PCI_DEVICE_ID_CMD_649: can_ultra_100 = 1; case PCI_DEVICE_ID_CMD_648: @@ -550,7 +798,7 @@ } dma_func = ide_dma_off_quietly; if ((id->field_valid & 4) && (can_ultra_33)) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x007F) { /* Force if Capable UltraDMA */ dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66); if ((id->field_valid & 2) && @@ -586,6 +834,18 @@ return HWIF(drive)->dmaproc(dma_func, drive); } +static int cmd680_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return cmd64x_config_drive_for_dma(drive); + default: + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} + static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { byte dma_stat = 0; @@ -663,7 +923,78 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ -unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name) +static int cmd680_busproc (ide_drive_t * drive, int state) +{ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + u8 addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + u32 stat_config = 0; + + pci_read_config_dword(hwif->pci_dev, addr_mask, &stat_config); + + if (!hwif) + return -EINVAL; + + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + default: + return 0; + } + hwif->bus_state = state; +#endif + return 0; +} + +void cmd680_reset (ide_drive_t *drive) +{ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + u8 addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + byte reset = 0; + + pci_read_config_byte(hwif->pci_dev, addr_mask, &reset); + pci_write_config_byte(hwif->pci_dev, addr_mask, reset|0x03); +#endif +} + +unsigned int cmd680_pci_init (struct pci_dev *dev, const char *name) +{ + u8 tmpbyte = 0; + pci_write_config_byte(dev, 0x80, 0x00); + pci_write_config_byte(dev, 0x84, 0x00); + pci_read_config_byte(dev, 0x8A, &tmpbyte); + pci_write_config_byte(dev, 0x8A, tmpbyte|0x01); + pci_write_config_word(dev, 0xA2, 0x328A); + pci_write_config_dword(dev, 0xA4, 0x328A); + pci_write_config_dword(dev, 0xA8, 0x4392); + pci_write_config_dword(dev, 0xAC, 0x4009); + pci_write_config_word(dev, 0xB2, 0x328A); + pci_write_config_dword(dev, 0xB4, 0x328A); + pci_write_config_dword(dev, 0xB8, 0x4392); + pci_write_config_dword(dev, 0xBC, 0x4009); + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) + if (!cmd64x_proc) { + cmd64x_proc = 1; + bmide_dev = dev; + cmd64x_display_info = &cmd680_get_info; + } +#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +unsigned int cmd64x_pci_init (struct pci_dev *dev, const char *name) { unsigned char mrdmode; unsigned int class_rev; @@ -752,7 +1083,23 @@ return 0; } -unsigned int __init ata66_cmd64x (ide_hwif_t *hwif) +unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name) +{ + if (dev->device == PCI_DEVICE_ID_CMD_680) + return cmd680_pci_init (dev, name); + return cmd64x_pci_init (dev, name); +} + +unsigned int cmd680_ata66 (ide_hwif_t *hwif) +{ + byte ata66 = 0; + byte addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + + pci_read_config_byte(hwif->pci_dev, addr_mask, &ata66); + return (ata66 & 0x01) ? 1 : 0; +} + +unsigned int cmd64x_ata66 (ide_hwif_t *hwif) { byte ata66 = 0; byte mask = (hwif->channel) ? 0x02 : 0x01; @@ -761,6 +1108,14 @@ return (ata66 & mask) ? 1 : 0; } +unsigned int __init ata66_cmd64x (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->device == PCI_DEVICE_ID_CMD_680) + return cmd680_ata66(hwif); + return cmd64x_ata66(hwif); +} + void __init ide_init_cmd64x (ide_hwif_t *hwif) { struct pci_dev *dev = hwif->pci_dev; @@ -769,8 +1124,6 @@ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; - hwif->tuneproc = &cmd64x_tuneproc; - hwif->speedproc = &cmd64x_tune_chipset; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; @@ -779,10 +1132,19 @@ #ifdef CONFIG_BLK_DEV_IDEDMA switch(dev->device) { + case PCI_DEVICE_ID_CMD_680: + hwif->busproc = &cmd680_busproc; + hwif->dmaproc = &cmd680_dmaproc; + hwif->resetproc = &cmd680_reset; + hwif->speedproc = &cmd680_tune_chipset; + hwif->tuneproc = &cmd680_tuneproc; + break; case PCI_DEVICE_ID_CMD_649: case PCI_DEVICE_ID_CMD_648: case PCI_DEVICE_ID_CMD_643: - hwif->dmaproc = &cmd64x_dmaproc; + hwif->dmaproc = &cmd64x_dmaproc; + hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; break; case PCI_DEVICE_ID_CMD_646: hwif->chipset = ide_cmd646; @@ -791,6 +1153,8 @@ } else { hwif->dmaproc = &cmd64x_dmaproc; } + hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; break; default: break; diff -Nru a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c --- a/drivers/ide/hpt366.c Wed Feb 13 20:03:35 2002 +++ b/drivers/ide/hpt366.c Wed Feb 13 20:03:35 2002 @@ -1,7 +1,8 @@ /* - * linux/drivers/ide/hpt366.c Version 0.18 June. 9, 2000 + * linux/drivers/ide/hpt366.c Version 0.22 20 Sep 2001 * * Copyright (C) 1999-2000 Andre Hedrick + * Portions Copyright (C) 2001 Sun Microsystems, Inc. * May be copied or modified under the terms of the GNU General Public License * * Thanks to HighPoint Technologies for their assistance, and hardware. @@ -11,6 +12,34 @@ * * Note that final HPT370 support was done by force extraction of GPL. * + * - add function for getting/setting power status of drive + * - the HPT370's state machine can get confused. reset it before each dma + * xfer to prevent that from happening. + * - reset state engine whenever we get an error. + * - check for busmaster state at end of dma. + * - use new highpoint timings. + * - detect bus speed using highpoint register. + * - use pll if we don't have a clock table. added a 66MHz table that's + * just 2x the 33MHz table. + * - removed turnaround. NOTE: we never want to switch between pll and + * pci clocks as the chip can glitch in those cases. the highpoint + * approved workaround slows everything down too much to be useful. in + * addition, we would have to serialize access to each chip. + * Adrian Sun + * + * add drive timings for 66MHz PCI bus, + * fix ATA Cable signal detection, fix incorrect /proc info + * add /proc display for per-drive PIO/DMA/UDMA mode and + * per-channel ATA-33/66 Cable detect. + * Duncan Laurie + * + * fixup /proc output for multiple controllers + * Tim Hockin + * + * On hpt366: + * Reset the hpt366 on error, reset on dma + * Fix disabling Fast Interrupt hpt366. + * Mike Waychison */ #include @@ -28,6 +57,7 @@ #include #include +#include #include #include @@ -35,6 +65,11 @@ #define DISPLAY_HPT366_TIMINGS +/* various tuning parameters */ +#define HPT_RESET_STATE_ENGINE +/*#define HPT_DELAY_INTERRUPT*/ +/*#define HPT_SERIALIZE_IO*/ + #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) #include #include @@ -106,146 +141,302 @@ struct chipset_bus_clock_list_entry { byte xfer_speed; - unsigned int chipset_settings_write; - unsigned int chipset_settings_read; + unsigned int chipset_settings; }; +/* key for bus clock timings + * bit + * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file + * register access. + * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file + * register access. + * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer. + * during task file register access. + * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA + * xfer. + * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task + * register access. + * 28 UDMA enable + * 29 DMA enable + * 30 PIO_MST enable. if set, the chip is in bus master mode during + * PIO. + * 31 FIFO enable. + */ struct chipset_bus_clock_list_entry forty_base [] = { - { XFER_UDMA_4, 0x900fd943, 0x900fd943 }, - { XFER_UDMA_3, 0x900ad943, 0x900ad943 }, - { XFER_UDMA_2, 0x900bd943, 0x900bd943 }, - { XFER_UDMA_1, 0x9008d943, 0x9008d943 }, - { XFER_UDMA_0, 0x9008d943, 0x9008d943 }, - - { XFER_MW_DMA_2, 0xa008d943, 0xa008d943 }, - { XFER_MW_DMA_1, 0xa010d955, 0xa010d955 }, - { XFER_MW_DMA_0, 0xa010d9fc, 0xa010d9fc }, - - { XFER_PIO_4, 0xc008d963, 0xc008d963 }, - { XFER_PIO_3, 0xc010d974, 0xc010d974 }, - { XFER_PIO_2, 0xc010d997, 0xc010d997 }, - { XFER_PIO_1, 0xc010d9c7, 0xc010d9c7 }, - { XFER_PIO_0, 0xc018d9d9, 0xc018d9d9 }, - { 0, 0x0120d9d9, 0x0120d9d9 } + { XFER_UDMA_4, 0x900fd943 }, + { XFER_UDMA_3, 0x900ad943 }, + { XFER_UDMA_2, 0x900bd943 }, + { XFER_UDMA_1, 0x9008d943 }, + { XFER_UDMA_0, 0x9008d943 }, + + { XFER_MW_DMA_2, 0xa008d943 }, + { XFER_MW_DMA_1, 0xa010d955 }, + { XFER_MW_DMA_0, 0xa010d9fc }, + + { XFER_PIO_4, 0xc008d963 }, + { XFER_PIO_3, 0xc010d974 }, + { XFER_PIO_2, 0xc010d997 }, + { XFER_PIO_1, 0xc010d9c7 }, + { XFER_PIO_0, 0xc018d9d9 }, + { 0, 0x0120d9d9 } }; struct chipset_bus_clock_list_entry thirty_three_base [] = { - { XFER_UDMA_4, 0x90c9a731, 0x90c9a731 }, - { XFER_UDMA_3, 0x90cfa731, 0x90cfa731 }, - { XFER_UDMA_2, 0x90caa731, 0x90caa731 }, - { XFER_UDMA_1, 0x90cba731, 0x90cba731 }, - { XFER_UDMA_0, 0x90c8a731, 0x90c8a731 }, - - { XFER_MW_DMA_2, 0xa0c8a731, 0xa0c8a731 }, - { XFER_MW_DMA_1, 0xa0c8a732, 0xa0c8a732 }, /* 0xa0c8a733 */ - { XFER_MW_DMA_0, 0xa0c8a797, 0xa0c8a797 }, - - { XFER_PIO_4, 0xc0c8a731, 0xc0c8a731 }, - { XFER_PIO_3, 0xc0c8a742, 0xc0c8a742 }, - { XFER_PIO_2, 0xc0d0a753, 0xc0d0a753 }, - { XFER_PIO_1, 0xc0d0a7a3, 0xc0d0a7a3 }, /* 0xc0d0a793 */ - { XFER_PIO_0, 0xc0d0a7aa, 0xc0d0a7aa }, /* 0xc0d0a7a7 */ - { 0, 0x0120a7a7, 0x0120a7a7 } + { XFER_UDMA_4, 0x90c9a731 }, + { XFER_UDMA_3, 0x90cfa731 }, + { XFER_UDMA_2, 0x90caa731 }, + { XFER_UDMA_1, 0x90cba731 }, + { XFER_UDMA_0, 0x90c8a731 }, + + { XFER_MW_DMA_2, 0xa0c8a731 }, + { XFER_MW_DMA_1, 0xa0c8a732 }, /* 0xa0c8a733 */ + { XFER_MW_DMA_0, 0xa0c8a797 }, + + { XFER_PIO_4, 0xc0c8a731 }, + { XFER_PIO_3, 0xc0c8a742 }, + { XFER_PIO_2, 0xc0d0a753 }, + { XFER_PIO_1, 0xc0d0a7a3 }, /* 0xc0d0a793 */ + { XFER_PIO_0, 0xc0d0a7aa }, /* 0xc0d0a7a7 */ + { 0, 0x0120a7a7 } }; struct chipset_bus_clock_list_entry twenty_five_base [] = { - { XFER_UDMA_4, 0x90c98521, 0x90c98521 }, - { XFER_UDMA_3, 0x90cf8521, 0x90cf8521 }, - { XFER_UDMA_2, 0x90cf8521, 0x90cf8521 }, - { XFER_UDMA_1, 0x90cb8521, 0x90cb8521 }, - { XFER_UDMA_0, 0x90cb8521, 0x90cb8521 }, - - { XFER_MW_DMA_2, 0xa0ca8521, 0xa0ca8521 }, - { XFER_MW_DMA_1, 0xa0ca8532, 0xa0ca8532 }, - { XFER_MW_DMA_0, 0xa0ca8575, 0xa0ca8575 }, - - { XFER_PIO_4, 0xc0ca8521, 0xc0ca8521 }, - { XFER_PIO_3, 0xc0ca8532, 0xc0ca8532 }, - { XFER_PIO_2, 0xc0ca8542, 0xc0ca8542 }, - { XFER_PIO_1, 0xc0d08572, 0xc0d08572 }, - { XFER_PIO_0, 0xc0d08585, 0xc0d08585 }, - { 0, 0x01208585, 0x01208585 } + { XFER_UDMA_4, 0x90c98521 }, + { XFER_UDMA_3, 0x90cf8521 }, + { XFER_UDMA_2, 0x90cf8521 }, + { XFER_UDMA_1, 0x90cb8521 }, + { XFER_UDMA_0, 0x90cb8521 }, + + { XFER_MW_DMA_2, 0xa0ca8521 }, + { XFER_MW_DMA_1, 0xa0ca8532 }, + { XFER_MW_DMA_0, 0xa0ca8575 }, + + { XFER_PIO_4, 0xc0ca8521 }, + { XFER_PIO_3, 0xc0ca8532 }, + { XFER_PIO_2, 0xc0ca8542 }, + { XFER_PIO_1, 0xc0d08572 }, + { XFER_PIO_0, 0xc0d08585 }, + { 0, 0x01208585 } }; +#if 1 +/* these are the current (4 sep 2001) timings from highpoint */ struct chipset_bus_clock_list_entry thirty_three_base_hpt370[] = { - { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, - { XFER_UDMA_4, 0x16454e31, 0x16454e31 }, - { XFER_UDMA_3, 0x166d4e31, 0x166d4e31 }, - { XFER_UDMA_2, 0x16494e31, 0x16494e31 }, - { XFER_UDMA_1, 0x164d4e31, 0x164d4e31 }, - { XFER_UDMA_0, 0x16514e31, 0x16514e31 }, - - { XFER_MW_DMA_2, 0x26514e21, 0x26514e21 }, - { XFER_MW_DMA_1, 0x26514e33, 0x26514e33 }, - { XFER_MW_DMA_0, 0x26514e97, 0x26514e97 }, - - { XFER_PIO_4, 0x06514e21, 0x06514e21 }, - { XFER_PIO_3, 0x06514e22, 0x06514e22 }, - { XFER_PIO_2, 0x06514e33, 0x06514e33 }, - { XFER_PIO_1, 0x06914e43, 0x06914e43 }, - { XFER_PIO_0, 0x06914e57, 0x06914e57 }, - { 0, 0x06514e57, 0x06514e57 } + { XFER_UDMA_5, 0x12446231 }, + { XFER_UDMA_4, 0x12446231 }, + { XFER_UDMA_3, 0x126c6231 }, + { XFER_UDMA_2, 0x12486231 }, + { XFER_UDMA_1, 0x124c6233 }, + { XFER_UDMA_0, 0x12506297 }, + + { XFER_MW_DMA_2, 0x22406c31 }, + { XFER_MW_DMA_1, 0x22406c33 }, + { XFER_MW_DMA_0, 0x22406c97 }, + + { XFER_PIO_4, 0x06414e31 }, + { XFER_PIO_3, 0x06414e42 }, + { XFER_PIO_2, 0x06414e53 }, + { XFER_PIO_1, 0x06814e93 }, + { XFER_PIO_0, 0x06814ea7 }, + { 0, 0x06814ea7 } +}; + +/* 2x 33MHz timings */ +struct chipset_bus_clock_list_entry sixty_six_base_hpt370[] = { + { XFER_UDMA_5, 0x1488e673 }, + { XFER_UDMA_4, 0x1488e673 }, + { XFER_UDMA_3, 0x1498e673 }, + { XFER_UDMA_2, 0x1490e673 }, + { XFER_UDMA_1, 0x1498e677 }, + { XFER_UDMA_0, 0x14a0e73f }, + + { XFER_MW_DMA_2, 0x2480fa73 }, + { XFER_MW_DMA_1, 0x2480fa77 }, + { XFER_MW_DMA_0, 0x2480fb3f }, + + { XFER_PIO_4, 0x0c82be73 }, + { XFER_PIO_3, 0x0c82be95 }, + { XFER_PIO_2, 0x0c82beb7 }, + { XFER_PIO_1, 0x0d02bf37 }, + { XFER_PIO_0, 0x0d02bf5f }, + { 0, 0x0d02bf5f } +}; +#else +/* from highpoint documentation. these are old values */ +struct chipset_bus_clock_list_entry thirty_three_base_hpt370[] = { + { XFER_UDMA_5, 0x16454e31 }, + { XFER_UDMA_4, 0x16454e31 }, + { XFER_UDMA_3, 0x166d4e31 }, + { XFER_UDMA_2, 0x16494e31 }, + { XFER_UDMA_1, 0x164d4e31 }, + { XFER_UDMA_0, 0x16514e31 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; + +struct chipset_bus_clock_list_entry sixty_six_base_hpt370[] = { + { XFER_UDMA_5, 0x14846231 }, + { XFER_UDMA_4, 0x14886231 }, + { XFER_UDMA_3, 0x148c6231 }, + { XFER_UDMA_2, 0x148c6231 }, + { XFER_UDMA_1, 0x14906231 }, + { XFER_UDMA_0, 0x14986231 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; +#endif + +struct chipset_bus_clock_list_entry fifty_base_hpt370[] = { + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x0ac1f48a } }; #define HPT366_DEBUG_DRIVE_INFO 0 #define HPT370_ALLOW_ATA100_5 1 #define HPT366_ALLOW_ATA66_4 1 #define HPT366_ALLOW_ATA66_3 1 +#define HPT366_MAX_DEVS 8 + +#define F_LOW_PCI_33 0x23 +#define F_LOW_PCI_40 0x29 +#define F_LOW_PCI_50 0x2d +#define F_LOW_PCI_66 0x42 + +static struct pci_dev *hpt_devs[HPT366_MAX_DEVS]; +static int n_hpt_devs; + +static unsigned int pci_rev_check_hpt3xx(struct pci_dev *dev); +static unsigned int pci_rev2_check_hpt3xx(struct pci_dev *dev); +byte hpt366_proc = 0; +byte hpt363_shared_irq; +byte hpt363_shared_pin; +extern char *ide_xfer_verbose (byte xfer_rate); #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) static int hpt366_get_info(char *, char **, off_t, int); extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */ extern char *ide_media_verbose(ide_drive_t *); -static struct pci_dev *bmide_dev; -static struct pci_dev *bmide2_dev; static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count) { - char *p = buffer; - u32 bibma = bmide_dev->resource[4].start; - u32 bibma2 = bmide2_dev->resource[4].start; - char *chipset_names[] = {"HPT366", "HPT366", "HPT368", "HPT370", "HPT370A"}; - u8 c0 = 0, c1 = 0; - u32 class_rev; - - pci_read_config_dword(bmide_dev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xff; - - /* - * at that point bibma+0x2 et bibma+0xa are byte registers - * to investigate: - */ - c0 = inb_p((unsigned short)bibma + 0x02); - if (bmide2_dev) - c1 = inb_p((unsigned short)bibma2 + 0x02); - - p += sprintf(p, "\n %s Chipset.\n", chipset_names[class_rev]); - p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); - p += sprintf(p, " %sabled %sabled\n", - (c0&0x80) ? "dis" : " en", - (c1&0x80) ? "dis" : " en"); - p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n"); - p += sprintf(p, "DMA enabled: %s %s %s %s\n", - (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ", - (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " ); - - p += sprintf(p, "UDMA\n"); - p += sprintf(p, "DMA\n"); - p += sprintf(p, "PIO\n"); + char *p = buffer; + char *chipset_nums[] = {"366", "366", "368", "370", "370A"}; + int i; + + p += sprintf(p, "\n " + "HighPoint HPT366/368/370\n"); + for (i = 0; i < n_hpt_devs; i++) { + struct pci_dev *dev = hpt_devs[i]; + unsigned long iobase = dev->resource[4].start; + u32 class_rev; + u8 c0, c1; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + p += sprintf(p, "\nController: %d\n", i); + p += sprintf(p, "Chipset: HPT%s\n", chipset_nums[class_rev]); + p += sprintf(p, "--------------- Primary Channel " + "--------------- Secondary Channel " + "--------------\n"); + + /* get the bus master status registers */ + c0 = inb_p(iobase + 0x2); + c1 = inb_p(iobase + 0xa); + p += sprintf(p, "Enabled: %s" + " %s\n", + (c0 & 0x80) ? "no" : "yes", + (c1 & 0x80) ? "no" : "yes"); + + if (pci_rev_check_hpt3xx(dev)) { + u8 cbl; + cbl = inb_p(iobase + 0x7b); + outb_p(cbl | 1, iobase + 0x7b); + outb_p(cbl & ~1, iobase + 0x7b); + cbl = inb_p(iobase + 0x7a); + p += sprintf(p, "Cable: ATA-%d" + " ATA-%d\n", + (cbl & 0x02) ? 33 : 66, + (cbl & 0x01) ? 33 : 66); + p += sprintf(p, "\n"); + } + p += sprintf(p, "--------------- drive0 --------- drive1 " + "------- drive0 ---------- drive1 -------\n"); + p += sprintf(p, "DMA capable: %s %s" + " %s %s\n", + (c0 & 0x20) ? "yes" : "no ", + (c0 & 0x40) ? "yes" : "no ", + (c1 & 0x20) ? "yes" : "no ", + (c1 & 0x40) ? "yes" : "no "); + + { + u8 c2, c3; + /* older revs don't have these registers mapped + * into io space */ + pci_read_config_byte(dev, 0x43, &c0); + pci_read_config_byte(dev, 0x47, &c1); + pci_read_config_byte(dev, 0x4b, &c2); + pci_read_config_byte(dev, 0x4f, &c3); + + p += sprintf(p, "Mode: %s %s" + " %s %s\n", + (c0 & 0x10) ? "UDMA" : (c0 & 0x20) ? "DMA " : + (c0 & 0x80) ? "PIO " : "off ", + (c1 & 0x10) ? "UDMA" : (c1 & 0x20) ? "DMA " : + (c1 & 0x80) ? "PIO " : "off ", + (c2 & 0x10) ? "UDMA" : (c2 & 0x20) ? "DMA " : + (c2 & 0x80) ? "PIO " : "off ", + (c3 & 0x10) ? "UDMA" : (c3 & 0x20) ? "DMA " : + (c3 & 0x80) ? "PIO " : "off "); + } + } + p += sprintf(p, "\n"); + return p-buffer;/* => must be less than 4k! */ } #endif /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */ -byte hpt366_proc = 0; - -extern char *ide_xfer_verbose (byte xfer_rate); -byte hpt363_shared_irq; -byte hpt363_shared_pin; - static unsigned int pci_rev_check_hpt3xx (struct pci_dev *dev) { unsigned int class_rev; @@ -282,16 +473,16 @@ return 0; } -static unsigned int pci_bus_clock_list (byte speed, int direction, struct chipset_bus_clock_list_entry * chipset_table) +static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { for ( ; chipset_table->xfer_speed ; chipset_table++) if (chipset_table->xfer_speed == speed) { - return (direction) ? chipset_table->chipset_settings_write : chipset_table->chipset_settings_read; + return chipset_table->chipset_settings; } - return (direction) ? chipset_table->chipset_settings_write : chipset_table->chipset_settings_read; + return chipset_table->chipset_settings; } -static void hpt366_tune_chipset (ide_drive_t *drive, byte speed, int direction) +static void hpt366_tune_chipset (ide_drive_t *drive, byte speed) { byte regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; byte regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; @@ -304,7 +495,7 @@ byte drive_fast = 0; /* - * Disable the "fast interrupt" prediction. + * Disable the "fast interrupt" prediction. */ pci_read_config_byte(HWIF(drive)->pci_dev, regfast, &drive_fast); if (drive_fast & 0x02) @@ -314,16 +505,22 @@ /* detect bus speed by looking at control reg timing: */ switch((reg1 >> 8) & 7) { case 5: - reg2 = pci_bus_clock_list(speed, direction, forty_base); + reg2 = pci_bus_clock_list(speed, forty_base); break; case 9: - reg2 = pci_bus_clock_list(speed, direction, twenty_five_base); + reg2 = pci_bus_clock_list(speed, twenty_five_base); break; default: case 7: - reg2 = pci_bus_clock_list(speed, direction, thirty_three_base); + reg2 = pci_bus_clock_list(speed, thirty_three_base); break; } +#if 0 + /* this is a nice idea ... */ + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + dev->sysdata); +#endif /* * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later) */ @@ -337,40 +534,47 @@ pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2); } -static void hpt370_tune_chipset (ide_drive_t *drive, byte speed, int direction) +static void hpt370_tune_chipset (ide_drive_t *drive, byte speed) { byte regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; - byte reg5bh = (speed != XFER_UDMA_5) ? 0x22 : (direction) ? 0x20 : 0x22; - unsigned int list_conf = pci_bus_clock_list(speed, direction, thirty_three_base_hpt370); + unsigned int list_conf = 0; unsigned int drive_conf = 0; unsigned int conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; - byte drive_pci = 0; - byte drive_fast = 0; + byte drive_pci = 0x40 + (drive->dn * 4); + byte new_fast, drive_fast = 0; + struct pci_dev *dev = HWIF(drive)->pci_dev; - switch (drive->dn) { - case 0: drive_pci = 0x40; break; - case 1: drive_pci = 0x44; break; - case 2: drive_pci = 0x48; break; - case 3: drive_pci = 0x4c; break; - default: return; - } /* * Disable the "fast interrupt" prediction. + * don't holdoff on interrupts. (== 0x01 despite what the docs say) */ - pci_read_config_byte(HWIF(drive)->pci_dev, regfast, &drive_fast); - if (drive_fast & 0x80) - pci_write_config_byte(HWIF(drive)->pci_dev, regfast, drive_fast & ~0x80); + pci_read_config_byte(dev, regfast, &drive_fast); + new_fast = drive_fast; + if (new_fast & 0x02) + new_fast &= ~0x02; + +#ifdef HPT_DELAY_INTERRUPT + if (new_fast & 0x01) + new_fast &= ~0x01; +#else + if ((new_fast & 0x01) == 0) + new_fast |= 0x01; +#endif + if (new_fast != drive_fast) + pci_write_config_byte(HWIF(drive)->pci_dev, regfast, new_fast); - pci_read_config_dword(HWIF(drive)->pci_dev, drive_pci, &drive_conf); - pci_write_config_byte(HWIF(drive)->pci_dev, 0x5b, reg5bh); + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + dev->sysdata); + pci_read_config_dword(dev, drive_pci, &drive_conf); list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); - /* - * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later) - */ - list_conf &= ~0x80000000; + + if (speed < XFER_MW_DMA_0) { + list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ + } - pci_write_config_dword(HWIF(drive)->pci_dev, drive_pci, list_conf); + pci_write_config_dword(dev, drive_pci, list_conf); } static int hpt3xx_tune_chipset (ide_drive_t *drive, byte speed) @@ -382,9 +586,9 @@ drive->init_speed = speed; if (pci_rev_check_hpt3xx(HWIF(drive)->pci_dev)) { - hpt370_tune_chipset(drive, speed, 0); + hpt370_tune_chipset(drive, speed); } else { - hpt366_tune_chipset(drive, speed, 0); + hpt366_tune_chipset(drive, speed); } drive->current_speed = speed; return ((int) ide_config_drive_speed(drive, speed)); @@ -541,13 +745,6 @@ } } -void hpt370_rw_proc (ide_drive_t *drive, ide_dma_action_t func) -{ - if ((func != ide_dma_write) || (func != ide_dma_read)) - return; - hpt370_tune_chipset(drive, drive->current_speed, (func == ide_dma_write)); -} - static int config_drive_xfer_rate (ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -624,6 +821,13 @@ reg50h, reg52h, reg5ah); if (reg5ah & 0x10) pci_write_config_byte(HWIF(drive)->pci_dev, 0x5a, reg5ah & ~0x10); + /* fall through to a reset */ +#if 0 + case ide_dma_begin: + case ide_dma_end: + /* reset the chips state over and over.. */ + pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, 0x13); +#endif break; case ide_dma_timeout: default: @@ -634,9 +838,52 @@ int hpt370_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte regstate = hwif->channel ? 0x54 : 0x50; + byte reginfo = hwif->channel ? 0x56 : 0x52; + byte dma_stat; + switch (func) { case ide_dma_check: return config_drive_xfer_rate(drive); + case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = inb(dma_base+2); + return (dma_stat & 4) == 4; /* return 1 if INTR asserted */ + + case ide_dma_end: + dma_stat = inb(dma_base + 2); + if (dma_stat & 0x01) { + udelay(20); /* wait a little */ + dma_stat = inb(dma_base + 2); + } + if ((dma_stat & 0x01) == 0) + break; + + func = ide_dma_timeout; + /* fallthrough */ + + case ide_dma_timeout: + case ide_dma_lostirq: + pci_read_config_byte(hwif->pci_dev, reginfo, + &dma_stat); + printk("%s: %d bytes in FIFO\n", drive->name, + dma_stat); + pci_write_config_byte(hwif->pci_dev, regstate, 0x37); + udelay(10); + dma_stat = inb(dma_base); + outb(dma_stat & ~0x1, dma_base); /* stop dma */ + dma_stat = inb(dma_base + 2); + outb(dma_stat | 0x6, dma_base+2); /* clear errors */ + /* fallthrough */ + +#ifdef HPT_RESET_STATE_ENGINE + case ide_dma_begin: +#endif + pci_write_config_byte(hwif->pci_dev, regstate, 0x37); + udelay(10); + break; + default: break; } @@ -644,6 +891,210 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ +void hpt3xx_reset (ide_drive_t *drive) +{ +#if 0 + unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); + byte reset = (HWIF(drive)->channel) ? 0x80 : 0x40; + byte reg59h = 0; + + pci_read_config_byte(HWIF(drive)->pci_dev, 0x59, ®59h); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h|reset); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h); +#endif +} + +static int hpt3xx_tristate (ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte reset = (hwif->channel) ? 0x80 : 0x40; + byte state_reg = (hwif->channel) ? 0x57 : 0x53; + byte reg59h = 0; + byte regXXh = 0; + + if (!hwif) + return -EINVAL; + +// hwif->bus_state = state; + + pci_read_config_byte(dev, 0x59, ®59h); + pci_read_config_byte(dev, state_reg, ®XXh); + + if (state) { + (void) ide_do_reset(drive); + pci_write_config_byte(dev, state_reg, regXXh|0x80); + pci_write_config_byte(dev, 0x59, reg59h|reset); + } else { + pci_write_config_byte(dev, 0x59, reg59h & ~(reset)); + pci_write_config_byte(dev, state_reg, regXXh & ~(0x80)); + (void) ide_do_reset(drive); + } + return 0; +} + +/* + * set/get power state for a drive. + * turning the power off does the following things: + * 1) soft-reset the drive + * 2) tri-states the ide bus + * + * when we turn things back on, we need to re-initialize things. + */ +#define TRISTATE_BIT 0x8000 +static int hpt370_busproc(ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + byte tristate, resetmask, bus_reg; + u16 tri_reg; + + if (!hwif) + return -EINVAL; + + hwif->bus_state = state; + + if (hwif->channel) { + /* secondary channel */ + tristate = 0x56; + resetmask = 0x80; + } else { + /* primary channel */ + tristate = 0x52; + resetmask = 0x40; + } + + /* grab status */ + pci_read_config_word(hwif->pci_dev, tristate, &tri_reg); + pci_read_config_byte(hwif->pci_dev, 0x59, &bus_reg); + + /* set the state. we don't set it if we don't need to do so. + * make sure that the drive knows that it has failed if it's off */ + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + if ((bus_reg & resetmask) == 0) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg &= ~resetmask; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) == 0 && (bus_reg & resetmask)) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg |= resetmask; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) && (bus_reg & resetmask)) + return 0; + tri_reg |= TRISTATE_BIT; + bus_reg |= resetmask; + break; + } + pci_write_config_byte(hwif->pci_dev, 0x59, bus_reg); + pci_write_config_word(hwif->pci_dev, tristate, tri_reg); + + return 0; +} + +static void __init init_hpt370(struct pci_dev *dev) +{ + int adjust, i; + u16 freq; + u32 pll; + byte reg5bh; + + /* + * default to pci clock. make sure MA15/16 are set to output + * to prevent drives having problems with 40-pin cables. + */ + pci_write_config_byte(dev, 0x5b, 0x23); + + /* + * set up the PLL. we need to adjust it so that it's stable. + * freq = Tpll * 192 / Tpci + */ + pci_read_config_word(dev, 0x78, &freq); + freq &= 0x1FF; + if (freq < 0x9c) { + pll = F_LOW_PCI_33; + dev->sysdata = (void *) thirty_three_base_hpt370; + printk("HPT370: using 33MHz PCI clock\n"); + } else if (freq < 0xb0) { + pll = F_LOW_PCI_40; + } else if (freq < 0xc8) { + pll = F_LOW_PCI_50; + dev->sysdata = (void *) fifty_base_hpt370; + printk("HPT370: using 50MHz PCI clock\n"); + } else { + pll = F_LOW_PCI_66; + dev->sysdata = (void *) sixty_six_base_hpt370; + printk("HPT370: using 66MHz PCI clock\n"); + } + + /* + * only try the pll if we don't have a table for the clock + * speed that we're running at. NOTE: the internal PLL will + * result in slow reads when using a 33MHz PCI clock. we also + * don't like to use the PLL because it will cause glitches + * on PRST/SRST when the HPT state engine gets reset. + */ + if (dev->sysdata) + goto init_hpt370_done; + + /* + * adjust PLL based upon PCI clock, enable it, and wait for + * stabilization. + */ + adjust = 0; + freq = (pll < F_LOW_PCI_50) ? 2 : 4; + while (adjust++ < 6) { + pci_write_config_dword(dev, 0x5c, (freq + pll) << 16 | + pll | 0x100); + + /* wait for clock stabilization */ + for (i = 0; i < 0x50000; i++) { + pci_read_config_byte(dev, 0x5b, ®5bh); + if (reg5bh & 0x80) { + /* spin looking for the clock to destabilize */ + for (i = 0; i < 0x1000; ++i) { + pci_read_config_byte(dev, 0x5b, + ®5bh); + if ((reg5bh & 0x80) == 0) + goto pll_recal; + } + pci_read_config_dword(dev, 0x5c, &pll); + pci_write_config_dword(dev, 0x5c, + pll & ~0x100); + pci_write_config_byte(dev, 0x5b, 0x21); + dev->sysdata = (void *) fifty_base_hpt370; + printk("HPT370: using 50MHz internal PLL\n"); + goto init_hpt370_done; + } + } +pll_recal: + if (adjust & 1) + pll -= (adjust >> 1); + else + pll += (adjust >> 1); + } + +init_hpt370_done: + /* reset state engine */ + pci_write_config_byte(dev, 0x50, 0x37); + pci_write_config_byte(dev, 0x54, 0x37); + udelay(100); +} + unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name) { byte test = 0; @@ -652,14 +1103,8 @@ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test); - -#if 0 - if (test != 0x08) - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x08); -#else if (test != (L1_CACHE_BYTES / 4)) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4)); -#endif pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test); if (test != 0x78) @@ -673,17 +1118,18 @@ if (test != 0x08) pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); + if (pci_rev_check_hpt3xx(dev)) { + init_hpt370(dev); + hpt_devs[n_hpt_devs++] = dev; + } else { + hpt_devs[n_hpt_devs++] = dev; + } + #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) if (!hpt366_proc) { hpt366_proc = 1; - bmide_dev = dev; - if (pci_rev_check_hpt3xx(dev)) - bmide2_dev = dev; hpt366_display_info = &hpt366_get_info; } - if ((hpt366_proc) && ((dev->devfn - bmide_dev->devfn) == 1)) { - bmide2_dev = dev; - } #endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */ return dev->irq; @@ -691,38 +1137,58 @@ unsigned int __init ata66_hpt366 (ide_hwif_t *hwif) { - byte ata66 = 0; + byte ata66 = 0; + byte regmask = (hwif->channel) ? 0x01 : 0x02; pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66); #ifdef DEBUG printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n", - ata66, (ata66 & 0x02) ? "33" : "66", + ata66, (ata66 & regmask) ? "33" : "66", PCI_FUNC(hwif->pci_dev->devfn)); #endif /* DEBUG */ - return ((ata66 & 0x02) ? 0 : 1); + return ((ata66 & regmask) ? 0 : 1); } void __init ide_init_hpt366 (ide_hwif_t *hwif) { + int hpt_rev; + hwif->tuneproc = &hpt3xx_tune_drive; hwif->speedproc = &hpt3xx_tune_chipset; hwif->quirkproc = &hpt3xx_quirkproc; hwif->intrproc = &hpt3xx_intrproc; hwif->maskproc = &hpt3xx_maskproc; +#ifdef HPT_SERIALIZE_IO + /* serialize access to this device */ + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; +#endif + + hpt_rev = pci_rev_check_hpt3xx(hwif->pci_dev); + if (hpt_rev) { + /* set up ioctl for power status. note: power affects both + * drives on each channel */ + hwif->busproc = &hpt370_busproc; + } + if (pci_rev2_check_hpt3xx(hwif->pci_dev)) { /* do nothing now but will split device types */ + hwif->resetproc = &hpt3xx_reset; +/* + * don't do until we can parse out the cobalt box argh ... + * hwif->busproc = &hpt3xx_tristate; + */ } #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { - if (pci_rev_check_hpt3xx(hwif->pci_dev)) { + if (hpt_rev) { byte reg5ah = 0; pci_read_config_byte(hwif->pci_dev, 0x5a, ®5ah); if (reg5ah & 0x10) /* interrupt force enable */ pci_write_config_byte(hwif->pci_dev, 0x5a, reg5ah & ~0x10); hwif->dmaproc = &hpt370_dmaproc; - hwif->rwproc = &hpt370_rw_proc; } else { hwif->dmaproc = &hpt366_dmaproc; } diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- a/drivers/ide/ide-cd.c Wed Feb 13 20:03:50 2002 +++ b/drivers/ide/ide-cd.c Wed Feb 13 20:03:50 2002 @@ -1312,9 +1312,26 @@ int ireason, len, stat, thislen; struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; + struct cdrom_info *info = drive->driver_data; + int dma = info->dma; + int dma_error; ide_startstop_t startstop; /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) { + /* + * We don't disable drive DMA for packet DMA errors. + * It's handled in cdda_read_audio() + */ + /* HWIF(drive)->dmaproc(ide_dma_off, drive); */ + pc->stat = 2; /* 2 -> DMA error */ + printk(KERN_ERR "CDROM packet DMA error\n"); + } + } + + /* Check for errors. */ if (cdrom_decode_status (&startstop, drive, 0, &stat)) return startstop; @@ -1322,6 +1339,14 @@ ireason = IN_BYTE (IDE_NSECTOR_REG); len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG); + if (dma) { + /* + * If DMA succeeded, we have all the data + */ + pc->buffer += pc->buflen; + pc->buflen = 0; + } + /* If DRQ is clear, the command has completed. Complain if we still have data left to transfer. */ if ((stat & DRQ_STAT) == 0) { @@ -1424,7 +1449,11 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; struct cdrom_info *info = drive->driver_data; - info->dma = 0; + if (rq->bh) { + info->dma = 1; + } else { + info->dma = 0; + } info->cmd = 0; pc->stat = 0; len = pc->buflen; @@ -1447,6 +1476,13 @@ } while (sleep); } +/* + * end_buffer_io_sync() is not exported + */ +static void cdrom_end_buffer_io_sync(struct buffer_head *bh, int uptodate) +{ +} + static int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc) { @@ -1459,7 +1495,25 @@ /* Start of retry loop. */ do { + struct buffer_head bh; + ide_init_drive_cmd (&req); + + if (pc->do_dma) { + /* Hack up a buffer_head for IDE DMA's use */ + memset(&bh, 0, sizeof(bh)); + bh.b_size = pc->buflen; + bh.b_data = pc->buffer; + bh.b_state = (1 << BH_Lock) | (1 << BH_Mapped) | + (1 << BH_Req); + bh.b_end_io = cdrom_end_buffer_io_sync; +#if 0 /* Needed by end_buffer_io_sync, but not cdrom_end_buffer_io_sync */ + atomic_set(&bh.b_count, 1); + init_waitqueue_head(&bh.b_wait); +#endif + req.bh = &bh; + } + req.cmd = PACKET_COMMAND; req.buffer = (char *)pc; ide_do_drive_cmd (drive, &req, ide_wait); @@ -2200,7 +2254,12 @@ pc.quiet = cgc->quiet; pc.timeout = cgc->timeout; pc.sense = cgc->sense; - return cgc->stat = cdrom_queue_packet_command(drive, &pc); + if (cgc->do_dma && drive->using_dma) + pc.do_dma = 1; + cgc->stat = cdrom_queue_packet_command(drive, &pc); + if (pc.stat == 2) /* DMA error: fall back to lower mode */ + cgc->dma_error = 1; + return cgc->stat; } static @@ -2961,11 +3020,7 @@ return 0; } -static -int ide_cdrom_reinit (ide_drive_t *drive) -{ - return 0; -} +int ide_cdrom_reinit (ide_drive_t *drive); static ide_driver_t ide_cdrom_driver = { name: "ide-cdrom", @@ -2975,6 +3030,8 @@ supports_dma: 1, supports_dsc_overlap: 1, cleanup: ide_cdrom_cleanup, + standby: NULL, + flushcache: NULL, do_request: ide_do_rw_cdrom, end_request: NULL, ioctl: ide_cdrom_ioctl, @@ -2986,7 +3043,9 @@ capacity: ide_cdrom_capacity, special: NULL, proc: NULL, - driver_reinit: ide_cdrom_reinit, + reinit: ide_cdrom_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, }; int ide_cdrom_init(void); @@ -3002,6 +3061,39 @@ MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); + +int ide_cdrom_reinit (ide_drive_t *drive) +{ + struct cdrom_info *info; + int failed = 0; + + MOD_INC_USE_COUNT; + info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); + if (info == NULL) { + printk ("%s: Can't allocate a cdrom structure\n", drive->name); + return 1; + } + if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) { + printk ("%s: Failed to register the driver with ide.c\n", drive->name); + kfree (info); + return 1; + } + memset (info, 0, sizeof (struct cdrom_info)); + drive->driver_data = info; + DRIVER(drive)->busy++; + if (ide_cdrom_setup (drive)) { + DRIVER(drive)->busy--; + if (ide_cdrom_cleanup (drive)) + printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name); + return 1; + } + DRIVER(drive)->busy--; + failed--; + + ide_register_module(&ide_cdrom_module); + MOD_DEC_USE_COUNT; + return 0; +} static void __exit ide_cdrom_exit(void) { diff -Nru a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h --- a/drivers/ide/ide-cd.h Wed Feb 13 20:03:49 2002 +++ b/drivers/ide/ide-cd.h Wed Feb 13 20:03:49 2002 @@ -38,7 +38,9 @@ /************************************************************************/ #define SECTOR_BITS 9 +#ifndef SECTOR_SIZE #define SECTOR_SIZE (1 << SECTOR_BITS) +#endif #define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS) #define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) #define SECTORS_BUFFER (SECTOR_BUFFER_SIZE >> SECTOR_BITS) @@ -109,6 +111,7 @@ int quiet; int timeout; struct request_sense *sense; + int do_dma; unsigned char c[12]; }; diff -Nru a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c --- a/drivers/ide/ide-cs.c Wed Feb 13 20:03:46 2002 +++ b/drivers/ide/ide-cs.c Wed Feb 13 20:03:46 2002 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -226,6 +227,15 @@ #define CFG_CHECK(fn, args...) \ if (CardServices(fn, args) != 0) goto next_entry +int idecs_register (int arg1, int arg2, int irq) +{ + hw_regs_t hw; + ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL); + hw.irq = irq; + hw.chipset = ide_pci; /* this enables IRQ sharing w/ PCI irqs */ + return ide_register_hw(&hw, NULL); +} + void ide_config(dev_link_t *link) { client_handle_t handle = link->handle; @@ -329,10 +339,14 @@ /* retry registration in case device is still spinning up */ for (i = 0; i < 10; i++) { - hd = ide_register(io_base, ctl_base, link->irq.AssignedIRQ); + if (ctl_base) + outb(0x02, ctl_base); /* Set nIEN = disable device interrupts */ + hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ); if (hd >= 0) break; if (link->io.NumPorts1 == 0x20) { - hd = ide_register(io_base+0x10, ctl_base+0x10, + if (ctl_base) + outb(0x02, ctl_base+0x10); + hd = idecs_register(io_base+0x10, ctl_base+0x10, link->irq.AssignedIRQ); if (hd >= 0) { io_base += 0x10; ctl_base += 0x10; diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c --- a/drivers/ide/ide-disk.c Wed Feb 13 20:03:29 2002 +++ b/drivers/ide/ide-disk.c Wed Feb 13 20:03:29 2002 @@ -27,9 +27,11 @@ * Version 1.09 added increment of rq->sector in ide_multwrite * added UDMA 3/4 reporting * Version 1.10 request queue changes, Ultra DMA 100 + * Version 1.11 added 48-bit lba + * Version 1.12 adding taskfile io access method */ -#define IDEDISK_VERSION "1.10" +#define IDEDISK_VERSION "1.12" #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -59,6 +61,14 @@ #define IS_PDC4030_DRIVE (0) /* auto-NULLs out pdc4030 code */ #endif +#ifdef CONFIG_IDE_TASKFILE_IO +# undef __TASKFILE__IO /* define __TASKFILE__IO */ +#else /* CONFIG_IDE_TASKFILE_IO */ +# undef __TASKFILE__IO +#endif /* CONFIG_IDE_TASKFILE_IO */ + +#ifndef __TASKFILE__IO + static void idedisk_bswap_data (void *buffer, int wcount) { u16 *p = buffer; @@ -86,6 +96,8 @@ ide_output_data(drive, buffer, wcount); } +#endif /* __TASKFILE__IO */ + /* * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity" * value for this drive (from its reported identification information). @@ -99,6 +111,11 @@ { unsigned long lba_sects, chs_sects, head, tail; + if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) { + printk("48-bit Drive: %llu \n", id->lba_capacity_2); + return 1; + } + /* * The ATA spec tells large drives to return * C/H/S = 16383/16/63 independent of their size. @@ -131,6 +148,8 @@ return 0; /* lba_capacity value may be bad */ } +#ifndef __TASKFILE__IO + /* * read_intr() is the handler for disk read/multread interrupts */ @@ -313,53 +332,218 @@ } return ide_error(drive, "multwrite_intr", stat); } +#endif /* __TASKFILE__IO */ + +#ifdef __TASKFILE__IO + +static ide_startstop_t chs_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block); +static ide_startstop_t lba_28_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block); +static ide_startstop_t lba_48_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long long block); /* - * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. + * do_rw_disk() issues READ and WRITE commands to a disk, + * using LBA if supported, or CHS otherwise, to address sectors. + * It also takes care of issuing special DRIVE_CMDs. */ -static ide_startstop_t set_multmode_intr (ide_drive_t *drive) +static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { - byte stat; + if (rq->cmd == READ) + goto good_command; + if (rq->cmd == WRITE) + goto good_command; - if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) { - drive->mult_count = drive->mult_req; - } else { - drive->mult_req = drive->mult_count = 0; - drive->special.b.recalibrate = 1; - (void) ide_dump_status(drive, "set_multmode", stat); - } + printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd); + ide_end_request(0, HWGROUP(drive)); return ide_stopped; + +good_command: + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (IS_PDC4030_DRIVE) { + extern ide_startstop_t promise_rw_disk(ide_drive_t *, struct request *, unsigned long); + return promise_rw_disk(drive, rq, block); + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ + + if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing)) /* 48-bit LBA */ + return lba_48_rw_disk(drive, rq, (unsigned long long) block); + if (drive->select.b.lba) /* 28-bit LBA */ + return lba_28_rw_disk(drive, rq, (unsigned long) block); + + /* 28-bit CHS : DIE DIE DIE piece of legacy crap!!! */ + return chs_rw_disk(drive, rq, (unsigned long) block); } -/* - * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. - */ -static ide_startstop_t set_geometry_intr (ide_drive_t *drive) +static task_ioreg_t get_command (ide_drive_t *drive, int cmd) { - byte stat; + int lba48bit = (drive->id->cfs_enable_2 & 0x0400) ? 1 : 0; - if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) - return ide_stopped; +#if 1 + lba48bit = drive->addressing; +#endif - if (stat & (ERR_STAT|DRQ_STAT)) - return ide_error(drive, "set_geometry_intr", stat); + if ((cmd == READ) && (drive->using_dma)) + return (lba48bit) ? WIN_READDMA_EXT : WIN_READDMA; + else if ((cmd == READ) && (drive->mult_count)) + return (lba48bit) ? WIN_MULTREAD_EXT : WIN_MULTREAD; + else if (cmd == READ) + return (lba48bit) ? WIN_READ_EXT : WIN_READ; + else if ((cmd == WRITE) && (drive->using_dma)) + return (lba48bit) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA; + else if ((cmd == WRITE) && (drive->mult_count)) + return (lba48bit) ? WIN_MULTWRITE_EXT : WIN_MULTWRITE; + else if (cmd == WRITE) + return (lba48bit) ? WIN_WRITE_EXT : WIN_WRITE; + else + return WIN_NOP; +} + +static ide_startstop_t chs_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + ide_task_t args; + + task_ioreg_t command = get_command(drive, rq->cmd); + unsigned int track = (block / drive->sect); + unsigned int sect = (block % drive->sect) + 1; + unsigned int head = (track % drive->head); + unsigned int cyl = (track / drive->head); + + memset(&taskfile, 0, sizeof(task_struct_t)); + memset(&hobfile, 0, sizeof(hob_struct_t)); + + taskfile.sector_count = (rq->nr_sectors==256)?0x00:rq->nr_sectors; + taskfile.sector_number = sect; + taskfile.low_cylinder = cyl; + taskfile.high_cylinder = (cyl>>8); + taskfile.device_head = head; + taskfile.device_head |= drive->select.all; + taskfile.command = command; + +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq->cmd==READ) ? "read" : "writ"); + if (lba) printk("LBAsect=%lld, ", block); + else printk("CHS=%d/%d/%d, ", cyl, head, sect); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif + + memcpy(args.tfRegister, &taskfile, sizeof(struct hd_drive_task_hdr)); + memcpy(args.hobRegister, &hobfile, sizeof(struct hd_drive_hob_hdr)); + args.command_type = ide_cmd_type_parser(&args); + args.prehandler = ide_pre_handler_parser(&taskfile, &hobfile); + args.handler = ide_handler_parser(&taskfile, &hobfile); + args.posthandler = NULL; + args.rq = (struct request *) rq; + args.block = block; + rq->special = NULL; + rq->special = (ide_task_t *)&args; + + return do_rw_taskfile(drive, &args); +} + +static ide_startstop_t lba_28_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + ide_task_t args; + + task_ioreg_t command = get_command(drive, rq->cmd); + + memset(&taskfile, 0, sizeof(task_struct_t)); + memset(&hobfile, 0, sizeof(hob_struct_t)); + + taskfile.sector_count = (rq->nr_sectors==256)?0x00:rq->nr_sectors; + taskfile.sector_number = block; + taskfile.low_cylinder = (block>>=8); + taskfile.high_cylinder = (block>>=8); + taskfile.device_head = ((block>>8)&0x0f); + taskfile.device_head |= drive->select.all; + taskfile.command = command; + + +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq->cmd==READ) ? "read" : "writ"); + if (lba) printk("LBAsect=%lld, ", block); + else printk("CHS=%d/%d/%d, ", cyl, head, sect); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif - ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL); - return ide_started; + memcpy(args.tfRegister, &taskfile, sizeof(struct hd_drive_task_hdr)); + memcpy(args.hobRegister, &hobfile, sizeof(struct hd_drive_hob_hdr)); + args.command_type = ide_cmd_type_parser(&args); + args.prehandler = ide_pre_handler_parser(&taskfile, &hobfile); + args.handler = ide_handler_parser(&taskfile, &hobfile); + args.posthandler = NULL; + args.rq = (struct request *) rq; + args.block = block; + rq->special = NULL; + rq->special = (ide_task_t *)&args; + + return do_rw_taskfile(drive, &args); } /* - * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. + * 268435455 == 137439 MB or 28bit limit + * 320173056 == 163929 MB or 48bit addressing + * 1073741822 == 549756 MB or 48bit addressing fake drive */ -static ide_startstop_t recal_intr (ide_drive_t *drive) + +static ide_startstop_t lba_48_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long long block) { - byte stat = GET_STAT(); + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + ide_task_t args; + + task_ioreg_t command = get_command(drive, rq->cmd); + + memset(&taskfile, 0, sizeof(task_struct_t)); + memset(&hobfile, 0, sizeof(hob_struct_t)); + + taskfile.sector_count = rq->nr_sectors; + hobfile.sector_count = (rq->nr_sectors>>8); + + if (rq->nr_sectors == 65536) { + taskfile.sector_count = 0x00; + hobfile.sector_count = 0x00; + } + + taskfile.sector_number = block; /* low lba */ + taskfile.low_cylinder = (block>>=8); /* mid lba */ + taskfile.high_cylinder = (block>>=8); /* hi lba */ + hobfile.sector_number = (block>>=8); /* low lba */ + hobfile.low_cylinder = (block>>=8); /* mid lba */ + hobfile.high_cylinder = (block>>=8); /* hi lba */ + taskfile.device_head = drive->select.all; + hobfile.device_head = taskfile.device_head; + hobfile.control = (drive->ctl|0x80); + taskfile.command = command; - if (!OK_STAT(stat,READY_STAT,BAD_STAT)) - return ide_error(drive, "recal_intr", stat); - return ide_stopped; +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq->cmd==READ) ? "read" : "writ"); + if (lba) printk("LBAsect=%lld, ", block); + else printk("CHS=%d/%d/%d, ", cyl, head, sect); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif + + memcpy(args.tfRegister, &taskfile, sizeof(struct hd_drive_task_hdr)); + memcpy(args.hobRegister, &hobfile, sizeof(struct hd_drive_hob_hdr)); + args.command_type = ide_cmd_type_parser(&args); + args.prehandler = ide_pre_handler_parser(&taskfile, &hobfile); + args.handler = ide_handler_parser(&taskfile, &hobfile); + args.posthandler = NULL; + args.rq = (struct request *) rq; + args.block = block; + rq->special = NULL; + rq->special = (ide_task_t *)&args; + + return do_rw_taskfile(drive, &args); } +#else /* !__TASKFILE__IO */ /* * do_rw_disk() issues READ and WRITE commands to a disk, * using LBA if supported, or CHS otherwise, to address sectors. @@ -369,22 +553,70 @@ { if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl,IDE_CONTROL_REG); - OUT_BYTE(0x00, IDE_FEATURE_REG); - OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG); + #ifdef CONFIG_BLK_DEV_PDC4030 if (drive->select.b.lba || IS_PDC4030_DRIVE) { #else /* !CONFIG_BLK_DEV_PDC4030 */ if (drive->select.b.lba) { #endif /* CONFIG_BLK_DEV_PDC4030 */ + + if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing)) { + task_ioreg_t tasklets[10]; + + tasklets[0] = 0; + tasklets[1] = 0; + tasklets[2] = rq->nr_sectors; + tasklets[3] = (rq->nr_sectors>>8); + if (rq->nr_sectors == 65536) { + tasklets[2] = 0x00; + tasklets[3] = 0x00; + } + tasklets[4] = (task_ioreg_t) block; + tasklets[5] = (task_ioreg_t) (block>>8); + tasklets[6] = (task_ioreg_t) (block>>16); + tasklets[7] = (task_ioreg_t) (block>>24); + tasklets[8] = (task_ioreg_t) 0; + tasklets[9] = (task_ioreg_t) 0; +// tasklets[8] = (task_ioreg_t) (block>>32); +// tasklets[9] = (task_ioreg_t) (block>>40); #ifdef DEBUG - printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", - drive->name, (rq->cmd==READ)?"read":"writ", - block, rq->nr_sectors, (unsigned long) rq->buffer); -#endif - OUT_BYTE(block,IDE_SECTOR_REG); - OUT_BYTE(block>>=8,IDE_LCYL_REG); - OUT_BYTE(block>>=8,IDE_HCYL_REG); - OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); + printk("%s: %sing: LBAsect=%lu, sectors=%ld, buffer=0x%08lx, LBAsect=0x%012lx\n", + drive->name, + (rq->cmd==READ)?"read":"writ", + block, + rq->nr_sectors, + (unsigned long) rq->buffer, + block); + printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n", + drive->name, tasklets[3], tasklets[2], + tasklets[9], tasklets[8], tasklets[7], + tasklets[6], tasklets[5], tasklets[4]); +#endif + OUT_BYTE(tasklets[1], IDE_FEATURE_REG); + OUT_BYTE(tasklets[3], IDE_NSECTOR_REG); + OUT_BYTE(tasklets[7], IDE_SECTOR_REG); + OUT_BYTE(tasklets[8], IDE_LCYL_REG); + OUT_BYTE(tasklets[9], IDE_HCYL_REG); + + OUT_BYTE(tasklets[0], IDE_FEATURE_REG); + OUT_BYTE(tasklets[2], IDE_NSECTOR_REG); + OUT_BYTE(tasklets[4], IDE_SECTOR_REG); + OUT_BYTE(tasklets[5], IDE_LCYL_REG); + OUT_BYTE(tasklets[6], IDE_HCYL_REG); + OUT_BYTE(0x00|drive->select.all,IDE_SELECT_REG); + } else { +#ifdef DEBUG + printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", + drive->name, (rq->cmd==READ)?"read":"writ", + block, rq->nr_sectors, (unsigned long) rq->buffer); +#endif + OUT_BYTE(0x00, IDE_FEATURE_REG); + OUT_BYTE((rq->nr_sectors==256)?0x00:rq->nr_sectors,IDE_NSECTOR_REG); + OUT_BYTE(block,IDE_SECTOR_REG); + OUT_BYTE(block>>=8,IDE_LCYL_REG); + OUT_BYTE(block>>=8,IDE_HCYL_REG); + OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); + } } else { unsigned int sect,head,cyl,track; track = block / drive->sect; @@ -392,6 +624,9 @@ OUT_BYTE(sect,IDE_SECTOR_REG); head = track % drive->head; cyl = track / drive->head; + + OUT_BYTE(0x00, IDE_FEATURE_REG); + OUT_BYTE((rq->nr_sectors==256)?0x00:rq->nr_sectors,IDE_NSECTOR_REG); OUT_BYTE(cyl,IDE_LCYL_REG); OUT_BYTE(cyl>>8,IDE_HCYL_REG); OUT_BYTE(head|drive->select.all,IDE_SELECT_REG); @@ -413,7 +648,11 @@ return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ ide_set_handler(drive, &read_intr, WAIT_CMD, NULL); - OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); + if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing)) { + OUT_BYTE(drive->mult_count ? WIN_MULTREAD_EXT : WIN_READ_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); + } return ide_started; } if (rq->cmd == WRITE) { @@ -422,7 +661,11 @@ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ - OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); + if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing)) { + OUT_BYTE(drive->mult_count ? WIN_MULTWRITE_EXT : WIN_WRITE_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); + } if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); @@ -432,17 +675,17 @@ __cli(); /* local CPU only */ if (drive->mult_count) { ide_hwgroup_t *hwgroup = HWGROUP(drive); - /* - * Ugh.. this part looks ugly because we MUST set up - * the interrupt handler before outputting the first block - * of data to be written. If we hit an error (corrupted buffer list) - * in ide_multwrite(), then we need to remove the handler/timer - * before returning. Fortunately, this NEVER happens (right?). - * - * Except when you get an error it seems... - */ + /* + * Ugh.. this part looks ugly because we MUST set up + * the interrupt handler before outputting the first block + * of data to be written. If we hit an error (corrupted buffer list) + * in ide_multwrite(), then we need to remove the handler/timer + * before returning. Fortunately, this NEVER happens (right?). + * + * Except when you get an error it seems... + */ hwgroup->wrq = *rq; /* scratchpad */ - ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL); + ide_set_handler(drive, &multwrite_intr, WAIT_CMD, NULL); if (ide_multwrite(drive, drive->mult_count)) { unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); @@ -462,29 +705,47 @@ return ide_stopped; } +#endif /* __TASKFILE__IO */ + static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive) { MOD_INC_USE_COUNT; if (drive->removable && drive->usage == 1) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.command = WIN_DOORLOCK; check_disk_change(inode->i_rdev); /* * Ignore the return code from door_lock, * since the open() has already succeeded, * and the door_lock is irrelevant at this point. */ - if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL)) + if (drive->doorlocking && ide_wait_taskfile(drive, &taskfile, &hobfile, NULL)) drive->doorlocking = 0; } return 0; } +static int do_idedisk_flushcache(ide_drive_t *drive); + static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive) { if (drive->removable && !drive->usage) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.command = WIN_DOORUNLOCK; invalidate_bdev(inode->i_bdev, 0); - if (drive->doorlocking && ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL)) + if (drive->doorlocking && ide_wait_taskfile(drive, &taskfile, &hobfile, NULL)) drive->doorlocking = 0; } + if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) + if (do_idedisk_flushcache(drive)) + printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", + drive->name); MOD_DEC_USE_COUNT; } @@ -501,27 +762,234 @@ } /* + * Queries for true maximum capacity of the drive. + * Returns maximum LBA address (> 0) of the drive, 0 if failed. + */ +static unsigned long idedisk_read_native_max_address(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long addr = 0; + + if (!(drive->id->command_set_1 & 0x0400) && + !(drive->id->cfs_enable_2 & 0x0100)) + return addr; + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX; + args.handler = task_no_data_intr; + + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + } + addr++; /* since the return value is (maxlba - 1), we add 1 */ + return addr; +} + +static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long long addr = 0; + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX_EXT; + args.handler = task_no_data_intr; + + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = ((args.hobRegister[IDE_HCYL_OFFSET_HOB])<<16) | + ((args.hobRegister[IDE_LCYL_OFFSET_HOB])<<8) | + (args.hobRegister[IDE_SECTOR_OFFSET_HOB]); + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr = ((__u64)high << 24) | low; + } + addr++; /* since the return value is (maxlba - 1), we add 1 */ + return addr; +} + +#ifdef CONFIG_IDEDISK_STROKE +/* + * Sets maximum virtual LBA address of the drive. + * Returns new maximum virtual LBA address (> 0) or 0 on failure. + */ +static unsigned long idedisk_set_max_address(ide_drive_t *drive, unsigned long addr_req) +{ + ide_task_t args; + unsigned long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >> 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >> 16) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = ((addr_req >> 24) & 0x0f) | 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX; + args.handler = task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, read new maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr_set = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + } + addr_set++; + return addr_set; +} + +static unsigned long long idedisk_set_max_address_ext(ide_drive_t *drive, unsigned long long addr_req) +{ + ide_task_t args; + unsigned long long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX_EXT; + args.hobRegister[IDE_SECTOR_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_LCYL_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_HCYL_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_SELECT_OFFSET_HOB] = 0x40; + args.hobRegister[IDE_CONTROL_OFFSET_HOB]= (drive->ctl|0x80); + args.handler = task_no_data_intr; + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = ((args.hobRegister[IDE_HCYL_OFFSET_HOB])<<16) | + ((args.hobRegister[IDE_LCYL_OFFSET_HOB])<<8) | + (args.hobRegister[IDE_SECTOR_OFFSET_HOB]); + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr_set = ((__u64)high << 24) | low; + } + return addr_set; +} + +/* + * Tests if the drive supports Host Protected Area feature. + * Returns true if supported, false otherwise. + */ +static inline int idedisk_supports_host_protected_area(ide_drive_t *drive) +{ + int flag = (drive->id->cfs_enable_1 & 0x0400) ? 1 : 0; + printk("%s: host protected area => %d\n", drive->name, flag); + return flag; +} + +#endif /* CONFIG_IDEDISK_STROKE */ + +/* * Compute drive->capacity, the full capacity of the drive * Called with drive->id != NULL. + * + * To compute capacity, this uses either of + * + * 1. CHS value set by user (whatever user sets will be trusted) + * 2. LBA value from target drive (require new ATA feature) + * 3. LBA value from system BIOS (new one is OK, old one may break) + * 4. CHS value from system BIOS (traditional style) + * + * in above order (i.e., if value of higher priority is available, + * reset will be ignored). */ static void init_idedisk_capacity (ide_drive_t *drive) { struct hd_driveid *id = drive->id; unsigned long capacity = drive->cyl * drive->head * drive->sect; + unsigned long set_max = idedisk_read_native_max_address(drive); + unsigned long long capacity_2 = capacity; + unsigned long long set_max_ext; + drive->capacity48 = 0; drive->select.b.lba = 0; + if (id->cfs_enable_2 & 0x0400) { + capacity_2 = id->lba_capacity_2; + drive->cyl = (unsigned int) capacity_2 / (drive->head * drive->sect); + drive->head = drive->bios_head = 255; + drive->sect = drive->bios_sect = 63; + drive->select.b.lba = 1; + set_max_ext = idedisk_read_native_max_address_ext(drive); + if (set_max_ext > capacity_2) { +#ifdef CONFIG_IDEDISK_STROKE + set_max_ext = idedisk_read_native_max_address_ext(drive); + set_max_ext = idedisk_set_max_address_ext(drive, set_max_ext); + if (set_max_ext) { + drive->capacity48 = capacity_2 = set_max_ext; + drive->cyl = (unsigned int) set_max_ext / (drive->head * drive->sect); + drive->select.b.lba = 1; + drive->id->lba_capacity_2 = capacity_2; + } +#else /* !CONFIG_IDEDISK_STROKE */ + printk("%s: setmax_ext LBA %llu, native %llu\n", + drive->name, set_max_ext, capacity_2); +#endif /* CONFIG_IDEDISK_STROKE */ + } + drive->bios_cyl = drive->cyl; + drive->capacity48 = capacity_2; + drive->capacity = (unsigned long) capacity_2; + return; /* Determine capacity, and use LBA if the drive properly supports it */ - if ((id->capability & 2) && lba_capacity_is_ok(id)) { + } else if ((id->capability & 2) && lba_capacity_is_ok(id)) { capacity = id->lba_capacity; drive->cyl = capacity / (drive->head * drive->sect); drive->select.b.lba = 1; } + + if (set_max > capacity) { +#ifdef CONFIG_IDEDISK_STROKE + set_max = idedisk_read_native_max_address(drive); + set_max = idedisk_set_max_address(drive, set_max); + if (set_max) { + drive->capacity = capacity = set_max; + drive->cyl = set_max / (drive->head * drive->sect); + drive->select.b.lba = 1; + drive->id->lba_capacity = capacity; + } +#else /* !CONFIG_IDEDISK_STROKE */ + printk("%s: setmax LBA %lu, native %lu\n", + drive->name, set_max, capacity); +#endif /* CONFIG_IDEDISK_STROKE */ + } + drive->capacity = capacity; + + if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) { + drive->capacity48 = id->lba_capacity_2; + drive->head = 255; + drive->sect = 63; + drive->cyl = (unsigned long)(drive->capacity48) / (drive->head * drive->sect); + } } -static unsigned long idedisk_capacity (ide_drive_t *drive) +static unsigned long idedisk_capacity (ide_drive_t *drive) { + if (drive->id->cfs_enable_2 & 0x0400) + return (drive->capacity48 - drive->sect0); return (drive->capacity - drive->sect0); } @@ -530,23 +998,48 @@ special_t *s = &drive->special; if (s->b.set_geometry) { - s->b.set_geometry = 0; - OUT_BYTE(drive->sect,IDE_SECTOR_REG); - OUT_BYTE(drive->cyl,IDE_LCYL_REG); - OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG); - OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG); - if (!IS_PDC4030_DRIVE) - ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr); + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + ide_handler_t *handler = NULL; + + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + + s->b.set_geometry = 0; + taskfile.sector_number = drive->sect; + taskfile.low_cylinder = drive->cyl; + taskfile.high_cylinder = drive->cyl>>8; + taskfile.device_head = ((drive->head-1)|drive->select.all)&0xBF; + if (!IS_PDC4030_DRIVE) { + taskfile.sector_count = drive->sect; + taskfile.command = WIN_SPECIFY; + handler = ide_handler_parser(&taskfile, &hobfile); + } + do_taskfile(drive, &taskfile, &hobfile, handler); } else if (s->b.recalibrate) { s->b.recalibrate = 0; - if (!IS_PDC4030_DRIVE) - ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr); + if (!IS_PDC4030_DRIVE) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.sector_count = drive->sect; + taskfile.command = WIN_RESTORE; + do_taskfile(drive, &taskfile, &hobfile, ide_handler_parser(&taskfile, &hobfile)); + } } else if (s->b.set_multmode) { s->b.set_multmode = 0; if (drive->id && drive->mult_req > drive->id->max_multsect) drive->mult_req = drive->id->max_multsect; - if (!IS_PDC4030_DRIVE) - ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr); + if (!IS_PDC4030_DRIVE) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.sector_count = drive->mult_req; + taskfile.command = WIN_SETMULT; + do_taskfile(drive, &taskfile, &hobfile, ide_handler_parser(&taskfile, &hobfile)); + } } else if (s->all) { int special = s->all; s->all = 0; @@ -558,9 +1051,11 @@ static void idedisk_pre_reset (ide_drive_t *drive) { + int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1; + drive->special.all = 0; - drive->special.b.set_geometry = 1; - drive->special.b.recalibrate = 1; + drive->special.b.set_geometry = legacy; + drive->special.b.recalibrate = legacy; if (OK_TO_RESET_CONTROLLER) drive->mult_count = 0; if (!drive->keep_settings && !drive->using_dma) @@ -573,19 +1068,45 @@ static int smart_enable(ide_drive_t *drive) { - return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL); + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.feature = SMART_ENABLE; + taskfile.low_cylinder = SMART_LCYL_PASS; + taskfile.high_cylinder = SMART_HCYL_PASS; + taskfile.command = WIN_SMART; + return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); } static int get_smart_values(ide_drive_t *drive, byte *buf) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.feature = SMART_READ_VALUES; + taskfile.sector_count = 0x01; + taskfile.low_cylinder = SMART_LCYL_PASS; + taskfile.high_cylinder = SMART_HCYL_PASS; + taskfile.command = WIN_SMART; (void) smart_enable(drive); - return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf); + return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } static int get_smart_thresholds(ide_drive_t *drive, byte *buf) { + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.feature = SMART_READ_THRESHOLDS; + taskfile.sector_count = 0x01; + taskfile.low_cylinder = SMART_LCYL_PASS; + taskfile.high_cylinder = SMART_HCYL_PASS; + taskfile.command = WIN_SMART; (void) smart_enable(drive); - return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf); + return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } static int proc_idedisk_read_cache @@ -609,7 +1130,7 @@ int len = 0, i = 0; if (!get_smart_thresholds(drive, page)) { - unsigned short *val = ((unsigned short *)page) + 2; + unsigned short *val = (unsigned short *) page; char *out = ((char *)val) + (SECTOR_WORDS * 4); page = out; do { @@ -628,7 +1149,7 @@ int len = 0, i = 0; if (!get_smart_values(drive, page)) { - unsigned short *val = ((unsigned short *)page) + 2; + unsigned short *val = (unsigned short *) page; char *out = ((char *)val) + (SECTOR_WORDS * 4); page = out; do { @@ -656,14 +1177,31 @@ static int set_multcount(ide_drive_t *drive, int arg) { +#ifdef __TASKFILE__IO + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + + if (drive->special.b.set_multmode) + return -EBUSY; + + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.sector_count = drive->mult_req; + taskfile.command = WIN_SETMULT; + drive->mult_req = arg; + drive->special.b.set_multmode = 1; + ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); +#else /* !__TASKFILE__IO */ struct request rq; if (drive->special.b.set_multmode) return -EBUSY; ide_init_drive_cmd (&rq); + rq.cmd = IDE_DRIVE_CMD; drive->mult_req = arg; drive->special.b.set_multmode = 1; (void) ide_do_drive_cmd (drive, &rq, ide_wait); +#endif /* __TASKFILE__IO */ return (drive->mult_count == arg) ? 0 : -EIO; } @@ -677,6 +1215,79 @@ return 0; } +static int write_cache (ide_drive_t *drive, int arg) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.feature = (arg) ? SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE; + taskfile.command = WIN_SETFEATURES; + + if (!(drive->id->cfs_enable_2 & 0x3000)) + return 1; + + (void) ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); + drive->wcache = arg; + return 0; +} + +static int do_idedisk_standby (ide_drive_t *drive) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + taskfile.command = WIN_STANDBYNOW1; + return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); +} + +static int do_idedisk_flushcache (ide_drive_t *drive) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + if (drive->id->cfs_enable_2 & 0x2400) { + taskfile.command = WIN_FLUSH_CACHE_EXT; + } else { + taskfile.command = WIN_FLUSH_CACHE; + } + return ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); +} + +static int set_acoustic (ide_drive_t *drive, int arg) +{ + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + + taskfile.feature = (arg)?SETFEATURES_EN_AAM:SETFEATURES_DIS_AAM; + taskfile.sector_count = arg; + + taskfile.command = WIN_SETFEATURES; + (void) ide_wait_taskfile(drive, &taskfile, &hobfile, NULL); + drive->acoustic = arg; + return 0; +} + +static int probe_lba_addressing (ide_drive_t *drive, int arg) +{ + drive->addressing = 0; + + if (!(drive->id->cfs_enable_2 & 0x0400)) + return -EIO; + + drive->addressing = arg; + return 0; +} + +static int set_lba_addressing (ide_drive_t *drive, int arg) +{ + return (probe_lba_addressing(drive, arg)); +} + static void idedisk_add_settings(ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -686,15 +1297,18 @@ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "address", SETTING_RW, HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, TYPE_INTA, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); - ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 2, &drive->mult_count, set_multcount); + ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 1, &read_ahead[major], NULL); ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, 4096, PAGE_SIZE, 1024, &max_readahead[major][minor], NULL); - ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); + ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 1, &max_sectors[major][minor], NULL); ide_add_setting(drive, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); - ide_add_setting(drive, "failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->failures, NULL); - ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); + ide_add_setting(drive, "wcache", SETTING_RW, HDIO_GET_WCACHE, HDIO_SET_WCACHE, TYPE_BYTE, 0, 1, 1, 1, &drive->wcache, write_cache); + ide_add_setting(drive, "acoustic", SETTING_RW, HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, TYPE_BYTE, 0, 254, 1, 1, &drive->acoustic, set_acoustic); + ide_add_setting(drive, "failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->failures, NULL); + ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); } static void idedisk_setup (ide_drive_t *drive) @@ -764,7 +1378,6 @@ if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && (!drive->forced_geom) && drive->bios_sect && drive->bios_head) drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; - printk (KERN_INFO "%s: %ld sectors", drive->name, capacity); /* Give size in megabytes (MB), not mebibytes (MiB). */ @@ -796,21 +1409,25 @@ drive->mult_req = id->max_multsect; if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect)) drive->special.b.set_multmode = 1; -#endif +#endif /* CONFIG_IDEDISK_MULTI_MODE */ } drive->no_io_32bit = id->dword_io ? 1 : 0; -} - -static int idedisk_reinit (ide_drive_t *drive) -{ - return 0; + if (drive->id->cfs_enable_2 & 0x3000) + write_cache(drive, (id->cfs_enable_2 & 0x3000)); + (void) probe_lba_addressing(drive, 1); } static int idedisk_cleanup (ide_drive_t *drive) { + if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) + if (do_idedisk_flushcache(drive)) + printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", + drive->name); return ide_unregister_subdriver(drive); } +int idedisk_reinit(ide_drive_t *drive); + /* * IDE subdriver functions, registered with ide.c */ @@ -822,6 +1439,8 @@ supports_dma: 1, supports_dsc_overlap: 0, cleanup: idedisk_cleanup, + standby: do_idedisk_standby, + flushcache: do_idedisk_flushcache, do_request: do_rw_disk, end_request: NULL, ioctl: NULL, @@ -833,7 +1452,9 @@ capacity: idedisk_capacity, special: idedisk_special, proc: idedisk_proc, - driver_reinit: idedisk_reinit, + reinit: idedisk_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, }; int idedisk_init (void); @@ -846,6 +1467,32 @@ MODULE_DESCRIPTION("ATA DISK Driver"); +int idedisk_reinit (ide_drive_t *drive) +{ + int failed = 0; + + MOD_INC_USE_COUNT; + + if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); + return 1; + } + DRIVER(drive)->busy++; + idedisk_setup(drive); + if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { + printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); + (void) idedisk_cleanup(drive); + DRIVER(drive)->busy--; + return 1; + } + DRIVER(drive)->busy--; + failed--; + + ide_register_module(&idedisk_module); + MOD_DEC_USE_COUNT; + return 0; +} + static void __exit idedisk_exit (void) { ide_drive_t *drive; @@ -877,12 +1524,15 @@ printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); continue; } + DRIVER(drive)->busy++; idedisk_setup(drive); if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); (void) idedisk_cleanup(drive); + DRIVER(drive)->busy--; continue; } + DRIVER(drive)->busy--; failed--; } ide_register_module(&idedisk_module); diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c --- a/drivers/ide/ide-dma.c Wed Feb 13 20:03:37 2002 +++ b/drivers/ide/ide-dma.c Wed Feb 13 20:03:37 2002 @@ -123,6 +123,7 @@ { "WDC AC11000H" , "ALL" }, { "WDC AC22100H" , "ALL" }, + { "WDC AC31000H" , "ALL" }, { "WDC AC32500H" , "ALL" }, { "WDC AC33100H" , "ALL" }, { "WDC AC31600H" , "ALL" }, @@ -282,6 +283,37 @@ return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); } +static int ide_raw_build_sglist (ide_hwif_t *hwif, struct request *rq) +{ + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + ide_task_t *args = rq->special; + unsigned char *virt_addr = rq->buffer; + int sector_count = rq->nr_sectors; + +// if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_WRITEDMA) || +// (args->tfRegister[IDE_COMMAND_OFFSET] == WIN_WRITEDMA_EXT)) + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + else + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + + if (sector_count > 128) { + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; + + return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); +} + /* * ide_build_dmatable() prepares a dma request. * Returns 0 if all went okay, returns 1 otherwise. @@ -299,7 +331,10 @@ int i; struct scatterlist *sg; - HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq); + if (HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) + HWIF(drive)->sg_nents = i = ide_raw_build_sglist(HWIF(drive), HWGROUP(drive)->rq); + else + HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq); if (!i) return 0; @@ -429,7 +464,14 @@ struct hd_driveid *id = drive->id; if ((id->field_valid & 4) && (eighty_ninty_three(drive)) && - (id->dma_ultra & (id->dma_ultra >> 11) & 7)) { + (id->dma_ultra & (id->dma_ultra >> 14) & 3)) { + if ((id->dma_ultra >> 15) & 1) { + printk(", UDMA(mode 7)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(133)"); /* UDMA BIOS-enabled! */ + } + } else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) && + (id->dma_ultra & (id->dma_ultra >> 11) & 7)) { if ((id->dma_ultra >> 13) & 1) { printk(", UDMA(100)"); /* UDMA BIOS-enabled! */ } else if ((id->dma_ultra >> 12) & 1) { @@ -456,14 +498,24 @@ static int config_drive_for_dma (ide_drive_t *drive) { + int config_allows_dma = 1; struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); - if (id && (id->capability & 1) && hwif->autodma) { +#ifdef CONFIG_IDEDMA_ONLYDISK + if (drive->media != ide_disk) + config_allows_dma = 0; +#endif + + if (id && (id->capability & 1) && hwif->autodma && config_allows_dma) { /* Consult the list of known "bad" drives */ if (ide_dmaproc(ide_dma_bad_drive, drive)) return hwif->dmaproc(ide_dma_off, drive); + /* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */ + if ((id->field_valid & 4) && (eighty_ninty_three(drive))) + if ((id->dma_ultra & (id->dma_ultra >> 14) & 2)) + return hwif->dmaproc(ide_dma_on, drive); /* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */ if ((id->field_valid & 4) && (eighty_ninty_three(drive))) if ((id->dma_ultra & (id->dma_ultra >> 11) & 7)) @@ -495,7 +547,7 @@ printk("%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); #endif /* DEBUG */ -#if 1 +#if 0 HWGROUP(drive)->expiry = NULL; /* one free ride for now */ #endif @@ -550,7 +602,7 @@ */ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { -// ide_hwgroup_t *hwgroup = HWGROUP(drive); +// ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); unsigned long dma_base = hwif->dma_base; byte unit = (drive->select.b.unit & 0x01); @@ -582,11 +634,20 @@ if (drive->media != ide_disk) return 0; #ifdef CONFIG_BLK_DEV_IDEDMA_TIMEOUT - ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */ + ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); /* issue cmd to drive */ #else /* !CONFIG_BLK_DEV_IDEDMA_TIMEOUT */ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */ #endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */ - OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) && + (drive->addressing == 1)) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing) { + OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + } + return HWIF(drive)->dmaproc(ide_dma_begin, drive); case ide_dma_begin: /* Note that this is done *after* the cmd has * been issued to the drive, as per the BM-IDE spec. @@ -604,7 +665,7 @@ return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; /* verify good DMA status */ case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ dma_stat = inb(dma_base+2); -#if 0 /* do not set unless you know what you are doing */ +#if 0 /* do not set unless you know what you are doing */ if (dma_stat & 4) { byte stat = GET_STAT(); outb(dma_base+2, dma_stat & 0xE4); @@ -649,6 +710,8 @@ GET_STAT(), dma_stat); return restart_request(drive); // BUG: return types do not match!! +//#else +// return HWGROUP(drive)->handler(drive); #endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */ case ide_dma_retune: case ide_dma_lostirq: diff -Nru a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c --- a/drivers/ide/ide-features.c Wed Feb 13 20:03:35 2002 +++ b/drivers/ide/ide-features.c Wed Feb 13 20:03:35 2002 @@ -127,30 +127,14 @@ case XFER_UDMA_3: return XFER_UDMA_2; case XFER_UDMA_2: return XFER_UDMA_1; case XFER_UDMA_1: return XFER_UDMA_0; + /* + * OOPS we do not goto non Ultra DMA modes + * without iCRC's available we force + * the system to PIO and make the user + * invoke the ATA-1 ATA-2 DMA modes. + */ case XFER_UDMA_0: - if (drive->id->dma_mword & 0x0004) return XFER_MW_DMA_2; - else if (drive->id->dma_mword & 0x0002) return XFER_MW_DMA_1; - else if (drive->id->dma_mword & 0x0001) return XFER_MW_DMA_0; - else return XFER_PIO_4; - case XFER_MW_DMA_2: return XFER_MW_DMA_1; - case XFER_MW_DMA_1: return XFER_MW_DMA_0; - case XFER_MW_DMA_0: - if (drive->id->dma_1word & 0x0004) return XFER_SW_DMA_2; - else if (drive->id->dma_1word & 0x0002) return XFER_SW_DMA_1; - else if (drive->id->dma_1word & 0x0001) return XFER_SW_DMA_0; - else return XFER_PIO_4; - case XFER_SW_DMA_2: return XFER_SW_DMA_1; - case XFER_SW_DMA_1: return XFER_SW_DMA_0; - case XFER_SW_DMA_0: - { - return XFER_PIO_4; - } - case XFER_PIO_4: return XFER_PIO_3; - case XFER_PIO_3: return XFER_PIO_2; - case XFER_PIO_2: return XFER_PIO_1; - case XFER_PIO_1: return XFER_PIO_0; - case XFER_PIO_0: - default: return XFER_PIO_SLOW; + default: return XFER_PIO_4; } } @@ -216,11 +200,11 @@ * in combination with the device (usually a disk) properly detect * and acknowledge each end of the ribbon. */ -int ide_ata66_check (ide_drive_t *drive, byte cmd, byte nsect, byte feature) +int ide_ata66_check (ide_drive_t *drive, ide_task_t *args) { - if ((cmd == WIN_SETFEATURES) && - (nsect > XFER_UDMA_2) && - (feature == SETFEATURES_XFER)) { + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) { if (!HWIF(drive)->udma_four) { printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", HWIF(drive)->name); return 1; @@ -243,11 +227,11 @@ * 1 : Safe to update drive->id DMA registers. * 0 : OOPs not allowed. */ -int set_transfer (ide_drive_t *drive, byte cmd, byte nsect, byte feature) +int set_transfer (ide_drive_t *drive, ide_task_t *args) { - if ((cmd == WIN_SETFEATURES) && - (nsect >= XFER_SW_DMA_0) && - (feature == SETFEATURES_XFER) && + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) && (drive->id->dma_ultra || drive->id->dma_mword || drive->id->dma_1word)) @@ -389,3 +373,4 @@ EXPORT_SYMBOL(set_transfer); EXPORT_SYMBOL(eighty_ninty_three); EXPORT_SYMBOL(ide_config_drive_speed); + diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c --- a/drivers/ide/ide-floppy.c Wed Feb 13 20:03:40 2002 +++ b/drivers/ide/ide-floppy.c Wed Feb 13 20:03:40 2002 @@ -71,9 +71,14 @@ * including set_bit patch from Rusty Russel * Ver 0.97 Jul 22 01 Merge 0.91-0.96 onto 0.9.sv for ac series * Ver 0.97.sv Aug 3 01 Backported from 2.4.7-ac3 + * Ver 0.98 Oct 26 01 Split idefloppy_transfer_pc into two pieces to + * fix a lost interrupt problem. It appears the busy + * bit was being deasserted by my IOMEGA ATAPI ZIP 100 + * drive before the drive was actually ready. + * Ver 0.98a Oct 29 01 Expose delay value so we can play. */ -#define IDEFLOPPY_VERSION "0.97.sv" +#define IDEFLOPPY_VERSION "0.98a" #include #include @@ -276,6 +281,7 @@ * Last error information */ byte sense_key, asc, ascq; + byte ticks; /* delay this long before sending packet command */ int progress_indication; /* @@ -289,6 +295,8 @@ unsigned long flags; /* Status/Action flags */ } idefloppy_floppy_t; +#define IDEFLOPPY_TICKS_DELAY 3 /* default delay for ZIP 100 */ + /* * Floppy flag bits values. */ @@ -297,7 +305,7 @@ #define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */ #define IDEFLOPPY_FORMAT_IN_PROGRESS 3 /* Format in progress */ #define IDEFLOPPY_CLIK_DRIVE 4 /* Avoid commands not supported in Clik drive */ - +#define IDEFLOPPY_ZIP_DRIVE 5 /* Requires BH algorithm for packets */ /* * ATAPI floppy drive packet commands @@ -1001,6 +1009,11 @@ return ide_started; } +/* + * This is the original routine that did the packet transfer. + * It fails at high speeds on the Iomega ZIP drive, so there's a slower version + * for that drive below. The algorithm is chosen based on drive type + */ static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) { ide_startstop_t startstop; @@ -1021,6 +1034,56 @@ return ide_started; } + +/* + * What we have here is a classic case of a top half / bottom half + * interrupt service routine. In interrupt mode, the device sends + * an interrupt to signal it's ready to receive a packet. However, + * we need to delay about 2-3 ticks before issuing the packet or we + * gets in trouble. + * + * So, follow carefully. transfer_pc1 is called as an interrupt (or + * directly). In either case, when the device says it's ready for a + * packet, we schedule the packet transfer to occur about 2-3 ticks + * later in transfer_pc2. + */ +static int idefloppy_transfer_pc2 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + return IDEFLOPPY_WAIT_CMD; /* Timeout for the packet command */ +} + +static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + ide_startstop_t startstop; + idefloppy_ireason_reg_t ireason; + + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n"); + return startstop; + } + ireason.all=IN_BYTE (IDE_IREASON_REG); + if (!ireason.b.cod || ireason.b.io) { + printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n"); + return ide_do_reset (drive); + } + /* + * The following delay solves a problem with ATAPI Zip 100 drives where the + * Busy flag was apparently being deasserted before the unit was ready to + * receive data. This was happening on a 1200 MHz Athlon system. 10/26/01 + * 25msec is too short, 40 and 50msec work well. idefloppy_pc_intr will + * not be actually used until after the packet is moved in about 50 msec. + */ + ide_set_handler (drive, + &idefloppy_pc_intr, /* service routine for packet command */ + floppy->ticks, /* wait this long before "failing" */ + &idefloppy_transfer_pc2); /* fail == transfer_pc2 */ + return ide_started; +} + /* * Issue a packet command */ @@ -1029,6 +1092,7 @@ idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_bcount_reg_t bcount; int dma_ok = 0; + ide_handler_t *pkt_xfer_routine; #if IDEFLOPPY_DEBUG_BUGS if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { @@ -1088,13 +1152,20 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ + /* Can we transfer the packet when we get the interrupt or wait? */ + if (test_bit (IDEFLOPPY_ZIP_DRIVE, &floppy->flags)) { + pkt_xfer_routine = &idefloppy_transfer_pc1; /* wait */ + } else { + pkt_xfer_routine = &idefloppy_transfer_pc; /* immediate */ + } + if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { - ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD, NULL); + ide_set_handler (drive, pkt_xfer_routine, IDEFLOPPY_WAIT_CMD, NULL); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); - return idefloppy_transfer_pc (drive); + return (*pkt_xfer_routine) (drive); } } @@ -1914,13 +1985,18 @@ { int major = HWIF(drive)->major; int minor = drive->select.b.unit << PARTN_BITS; + idefloppy_floppy_t *floppy = drive->driver_data; +/* + * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function + */ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); + ide_add_setting(drive, "ticks", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &floppy->ticks, NULL); } @@ -1954,20 +2030,12 @@ if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0) { + set_bit(IDEFLOPPY_ZIP_DRIVE, &floppy->flags); + /* This value will be visible in the /proc/ide/hdx/settings */ + floppy->ticks = IDEFLOPPY_TICKS_DELAY; for (i = 0; i < 1 << PARTN_BITS; i++) max_sectors[major][minor + i] = 64; } - /* - * Guess what? The IOMEGA Clik! drive also needs the - * above fix. It makes nasty clicking noises without - * it, so please don't remove this. - */ - if (strcmp(drive->id->model, "IOMEGA Clik! 40 CZ ATAPI") == 0) - { - for (i = 0; i < 1 << PARTN_BITS; i++) - max_sectors[major][minor + i] = 64; - set_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags); - } /* * Guess what? The IOMEGA Clik! drive also needs the @@ -2019,10 +2087,7 @@ #endif /* CONFIG_PROC_FS */ -static int idefloppy_reinit (ide_drive_t *drive) -{ - return 0; -} +int idefloppy_reinit(ide_drive_t *drive); /* * IDE subdriver functions, registered with ide.c @@ -2035,6 +2100,8 @@ supports_dma: 1, supports_dsc_overlap: 0, cleanup: idefloppy_cleanup, + standby: NULL, + flushcache: NULL, do_request: idefloppy_do_request, end_request: idefloppy_end_request, ioctl: idefloppy_ioctl, @@ -2046,7 +2113,9 @@ capacity: idefloppy_capacity, special: NULL, proc: idefloppy_proc, - driver_reinit: idefloppy_reinit, + reinit: idefloppy_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, }; int idefloppy_init (void); @@ -2057,6 +2126,40 @@ NULL }; +int idefloppy_reinit (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy; + int failed = 0; + + MOD_INC_USE_COUNT; + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { + if (!idefloppy_identify_device (drive, drive->id)) { + printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); + continue; + } + if (drive->scsi) { + printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) { + printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (floppy); + continue; + } + DRIVER(drive)->busy++; + idefloppy_setup (drive, floppy); + DRIVER(drive)->busy--; + failed--; + } + ide_register_module(&idefloppy_module); + MOD_DEC_USE_COUNT; + return 0; +} + MODULE_DESCRIPTION("ATAPI FLOPPY Driver"); static void __exit idefloppy_exit (void) @@ -2108,7 +2211,9 @@ kfree (floppy); continue; } + DRIVER(drive)->busy++; idefloppy_setup (drive, floppy); + DRIVER(drive)->busy--; failed--; } ide_register_module(&idefloppy_module); diff -Nru a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c --- a/drivers/ide/ide-geometry.c Wed Feb 13 20:03:30 2002 +++ b/drivers/ide/ide-geometry.c Wed Feb 13 20:03:30 2002 @@ -6,6 +6,8 @@ #include #include +#ifdef CONFIG_BLK_DEV_IDE + /* * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc * controller that is BIOS compatible with ST-506, and thus showing up in our @@ -40,7 +42,11 @@ * Consequently, also the former "drive->present = 1" below was a mistake. * * Eventually the entire routine below should be removed. + * + * 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS + * chip. */ + void probe_cmos_for_drives (ide_hwif_t *hwif) { #ifdef __i386__ @@ -80,9 +86,10 @@ } #endif } +#endif /* CONFIG_BLK_DEV_IDE */ -#ifdef CONFIG_BLK_DEV_IDE +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) extern ide_drive_t * get_info_ptr(kdev_t); extern unsigned long current_capacity (ide_drive_t *); @@ -214,4 +221,4 @@ drive->bios_cyl, drive->bios_head, drive->bios_sect); return ret; } -#endif /* CONFIG_BLK_DEV_IDE */ +#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */ diff -Nru a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c --- a/drivers/ide/ide-pci.c Wed Feb 13 20:03:56 2002 +++ b/drivers/ide/ide-pci.c Wed Feb 13 20:03:56 2002 @@ -12,6 +12,13 @@ * configuration of all PCI IDE interfaces present in a system. */ +/* + * Chipsets that are on the IDE_IGNORE list because of problems of not being + * set at compile time. + * + * CONFIG_BLK_DEV_PDC202XX + */ + #include #include #include @@ -47,6 +54,8 @@ #define DEVID_PDC20267 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267}) #define DEVID_PDC20268 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268}) #define DEVID_PDC20268R ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R}) +#define DEVID_PDC20269 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269}) +#define DEVID_PDC20275 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275}) #define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000}) #define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001}) #define DEVID_SAMURAI ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE}) @@ -55,6 +64,7 @@ #define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646}) #define DEVID_CMD648 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648}) #define DEVID_CMD649 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649}) +#define DEVID_CMD680 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_680}) #define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513}) #define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621}) #define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558}) @@ -79,6 +89,7 @@ #define DEVID_AMD7401 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401}) #define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409}) #define DEVID_AMD7411 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411}) +#define DEVID_AMD7441 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7441}) #define DEVID_PDCADMA ((ide_pci_devid_t){PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841}) #define DEVID_SLC90E66 ((ide_pci_devid_t){PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1}) #define DEVID_OSB4 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE}) @@ -86,6 +97,7 @@ #define DEVID_ITE8172G ((ide_pci_devid_t){PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G}) #define IDE_IGNORE ((void *)-1) +#define IDE_NO_DRIVER ((void *)-2) #ifdef CONFIG_BLK_DEV_AEC62XX extern unsigned int pci_init_aec62xx(struct pci_dev *, const char *); @@ -99,7 +111,7 @@ #else #define PCI_AEC62XX NULL #define ATA66_AEC62XX NULL -#define INIT_AEC62XX NULL +#define INIT_AEC62XX IDE_NO_DRIVER #define DMA_AEC62XX NULL #endif @@ -115,7 +127,7 @@ #else #define PCI_ALI15X3 NULL #define ATA66_ALI15X3 NULL -#define INIT_ALI15X3 NULL +#define INIT_ALI15X3 IDE_NO_DRIVER #define DMA_ALI15X3 NULL #endif @@ -131,7 +143,7 @@ #else #define PCI_AMD74XX NULL #define ATA66_AMD74XX NULL -#define INIT_AMD74XX NULL +#define INIT_AMD74XX IDE_NO_DRIVER #define DMA_AMD74XX NULL #endif @@ -149,7 +161,7 @@ #ifdef __sparc_v9__ #define INIT_CMD64X IDE_IGNORE #else -#define INIT_CMD64X NULL +#define INIT_CMD64X IDE_NO_DRIVER #endif #endif @@ -160,7 +172,7 @@ #define INIT_CY82C693 &ide_init_cy82c693 #else #define PCI_CY82C693 NULL -#define INIT_CY82C693 NULL +#define INIT_CY82C693 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_CS5530 @@ -170,7 +182,7 @@ #define INIT_CS5530 &ide_init_cs5530 #else #define PCI_CS5530 NULL -#define INIT_CS5530 NULL +#define INIT_CS5530 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_HPT34X @@ -199,7 +211,7 @@ static byte hpt363_shared_pin; #define PCI_HPT366 NULL #define ATA66_HPT366 NULL -#define INIT_HPT366 NULL +#define INIT_HPT366 IDE_NO_DRIVER #define DMA_HPT366 NULL #endif @@ -214,7 +226,7 @@ extern void ide_init_opti621(ide_hwif_t *); #define INIT_OPTI621 &ide_init_opti621 #else -#define INIT_OPTI621 NULL +#define INIT_OPTI621 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_PDC_ADMA @@ -241,9 +253,9 @@ #define ATA66_PDC202XX &ata66_pdc202xx #define INIT_PDC202XX &ide_init_pdc202xx #else -#define PCI_PDC202XX NULL -#define ATA66_PDC202XX NULL -#define INIT_PDC202XX NULL +#define PCI_PDC202XX IDE_IGNORE +#define ATA66_PDC202XX IDE_IGNORE +#define INIT_PDC202XX IDE_IGNORE #endif #ifdef CONFIG_BLK_DEV_PIIX @@ -256,7 +268,7 @@ #else #define PCI_PIIX NULL #define ATA66_PIIX NULL -#define INIT_PIIX NULL +#define INIT_PIIX IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_IT8172 @@ -268,7 +280,7 @@ #else #define PCI_IT8172 NULL #define ATA66_IT8172 NULL -#define INIT_IT8172 NULL +#define INIT_IT8172 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_RZ1000 @@ -290,7 +302,7 @@ #else #define PCI_SVWKS NULL #define ATA66_SVWKS NULL -#define INIT_SVWKS NULL +#define INIT_SVWKS IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SIS5513 @@ -303,7 +315,7 @@ #else #define PCI_SIS5513 NULL #define ATA66_SIS5513 NULL -#define INIT_SIS5513 NULL +#define INIT_SIS5513 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SLC90E66 @@ -316,7 +328,7 @@ #else #define PCI_SLC90E66 NULL #define ATA66_SLC90E66 NULL -#define INIT_SLC90E66 NULL +#define INIT_SLC90E66 IDE_NO_DRIVER #endif #ifdef CONFIG_BLK_DEV_SL82C105 @@ -351,7 +363,7 @@ #else #define PCI_VIA82CXXX NULL #define ATA66_VIA82CXXX NULL -#define INIT_VIA82CXXX NULL +#define INIT_VIA82CXXX IDE_NO_DRIVER #define DMA_VIA82CXXX NULL #endif @@ -393,7 +405,7 @@ #ifdef CONFIG_PDC202XX_FORCE {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, - {DEVID_PDC20265,"PDC20265", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, + {DEVID_PDC20265,"PDC20265", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 48 }, {DEVID_PDC20267,"PDC20267", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, #else /* !CONFIG_PDC202XX_FORCE */ {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 }, @@ -401,11 +413,13 @@ {DEVID_PDC20265,"PDC20265", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, {DEVID_PDC20267,"PDC20267", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, #endif - {DEVID_PDC20268,"PDC20268", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, + {DEVID_PDC20268,"PDC20268", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, /* Promise used a different PCI ident for the raid card apparently to try and prevent Linux detecting it and using our own raid code. We want to detect it for the ataraid drivers, so we have to list both here.. */ - {DEVID_PDC20268R,"PDC20268", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, + {DEVID_PDC20268R,"PDC20270", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20269,"PDC20269", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20275,"PDC20275", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, {DEVID_RZ1000, "RZ1000", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_RZ1001, "RZ1001", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_SAMURAI, "SAMURAI", NULL, NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, @@ -416,6 +430,11 @@ {DEVID_CMD646, "CMD646", PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_CMD648, "CMD648", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_CMD649, "CMD649", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, +#ifndef CONFIG_BLK_DEV_CMD680 + {DEVID_CMD680, "CMD680", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, +#else /* CONFIG_BLK_DEV_CMD680 */ + {DEVID_CMD680, "CMD680", PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, +#endif /* !CONFIG_BLK_DEV_CMD680 */ {DEVID_HT6565, "HT6565", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_OPTI621, "OPTI621", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, {DEVID_OPTI621X,"OPTI621X", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, @@ -434,9 +453,10 @@ {DEVID_CY82C693,"CY82C693", PCI_CY82C693, NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, {DEVID_CS5530, "CS5530", PCI_CS5530, NULL, INIT_CS5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, - {DEVID_AMD7401, "AMD7401", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_AMD7401, "AMD7401", NULL, NULL, NULL, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, {DEVID_AMD7409, "AMD7409", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, {DEVID_AMD7411, "AMD7411", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, + {DEVID_AMD7441, "AMD7441", PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, {DEVID_PDCADMA, "PDCADMA", PCI_PDCADMA, ATA66_PDCADMA, INIT_PDCADMA, DMA_PDCADMA, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, {DEVID_SLC90E66,"SLC90E66", PCI_SLC90E66, ATA66_SLC90E66, INIT_SLC90E66, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_OSB4, "ServerWorks OSB4", PCI_SVWKS, ATA66_SVWKS, INIT_SVWKS, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, @@ -458,6 +478,9 @@ case PCI_DEVICE_ID_PROMISE_20265: case PCI_DEVICE_ID_PROMISE_20267: case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20275: case PCI_DEVICE_ID_ARTOP_ATP850UF: case PCI_DEVICE_ID_ARTOP_ATP860: case PCI_DEVICE_ID_ARTOP_ATP860R: @@ -592,7 +615,15 @@ autodma = 1; #endif - pci_enable_device(dev); + if (d->init_hwif == IDE_NO_DRIVER) { + printk(KERN_WARNING "%s: detected chipset, but driver not compiled in!\n", d->name); + d->init_hwif = NULL; + } + + if (pci_enable_device(dev)) { + printk(KERN_WARNING "%s: (ide_setup_pci_device:) Could not enable device.\n", d->name); + return; + } check_if_enabled: if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { @@ -752,7 +783,8 @@ } if (IDE_PCI_DEVID_EQ(d->devid, DEVID_MPIIX)) goto bypass_piix_dma; - + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDCADMA)) + goto bypass_legacy_dma; if (hwif->udma_four) { printk("%s: ATA-66/100 forced bit set (WARNING)!!\n", d->name); } else { @@ -769,12 +801,15 @@ autodma = 0; if (autodma) hwif->autodma = 1; + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20267) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20269) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20275) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260R) || @@ -785,6 +820,7 @@ IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD646) || IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD648) || IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD649) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD680) || IDE_PCI_DEVID_EQ(d->devid, DEVID_OSB4) || ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) { unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name); @@ -811,6 +847,7 @@ } } #endif /* CONFIG_BLK_DEV_IDEDMA */ +bypass_legacy_dma: bypass_piix_dma: bypass_umc_dma: if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ @@ -822,20 +859,60 @@ printk("%s: neither IDE port enabled (BIOS)\n", d->name); } +static void __init pdc20270_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *dev2 = NULL, *findev; + ide_pci_device_t *d2; + + if ((dev->bus->self && + dev->bus->self->vendor == PCI_VENDOR_ID_DEC) && + (dev->bus->self->device == PCI_DEVICE_ID_DEC_21150)) { + if (PCI_SLOT(dev->devfn) & 2) { + return; + } + d->extra = 0; + pci_for_each_dev(findev) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + (PCI_SLOT(findev->devfn) & 2)) { + byte irq = 0, irq2 = 0; + dev2 = findev; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + pci_read_config_byte(dev2, PCI_INTERRUPT_LINE, &irq2); + if (irq != irq2) { + dev2->irq = dev->irq; + pci_write_config_byte(dev2, PCI_INTERRUPT_LINE, irq); + } + + } + } + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + if (!dev2) + return; + d2 = d; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn); + ide_setup_pci_device(dev2, d2); +} + static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d) { struct pci_dev *dev2 = NULL, *findev; ide_pci_device_t *d2; unsigned char pin1 = 0, pin2 = 0; unsigned int class_rev; - char *chipset_names[] = {"HPT366", "HPT366", "HPT368", "HPT370", "HPT370A"}; + char *chipset_names[] = {"HPT366", "HPT366", "HPT368", "HPT370", "HPT370A", "HPT372"}; if (PCI_FUNC(dev->devfn) & 1) return; pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; - + if (class_rev > 5) + class_rev = 5; + strcpy(d->name, chipset_names[class_rev]); switch(class_rev) { @@ -902,6 +979,8 @@ return; /* UM8886A/BF pair */ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366)) hpt366_device_order_fixup(dev, d); + else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R)) + pdc20270_device_order_fixup(dev, d); else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) { if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL)) printk("%s: unknown IDE controller on PCI bus %02x device %02x, VID=%04x, DID=%04x\n", diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c --- a/drivers/ide/ide-probe.c Wed Feb 13 20:03:29 2002 +++ b/drivers/ide/ide-probe.c Wed Feb 13 20:03:29 2002 @@ -58,6 +58,11 @@ struct hd_driveid *id; id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_ATOMIC); /* called with interrupts disabled! */ + if (!id) { + printk(KERN_WARNING "(ide-probe::do_identify) Out of memory.\n"); + goto err_kmalloc; + } + ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */ ide__sti(); /* local CPU only */ ide_fix_driveid(id); @@ -76,8 +81,7 @@ if ((id->model[0] == 'P' && id->model[1] == 'M') || (id->model[0] == 'S' && id->model[1] == 'K')) { printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model); - drive->present = 0; - return; + goto err_misc; } #endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */ @@ -96,7 +100,7 @@ ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap); if (strstr(id->model, "E X A B Y T E N E S T")) - return; + goto err_misc; id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */ printk("%s: %s, ", drive->name, id->model); @@ -111,8 +115,7 @@ #ifdef CONFIG_BLK_DEV_PDC4030 if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) { printk(" -- not supported on 2nd Promise port\n"); - drive->present = 0; - return; + goto err_misc; } #endif /* CONFIG_BLK_DEV_PDC4030 */ switch (type) { @@ -174,6 +177,12 @@ printk("ATA DISK drive\n"); QUIRK_LIST(HWIF(drive),drive); return; + +err_misc: + kfree(id); +err_kmalloc: + drive->present = 0; + return; } /* @@ -597,6 +606,12 @@ q->queuedata = HWGROUP(drive); blk_init_queue(q, do_ide_request); + + if (drive->media == ide_disk) { +#ifdef CONFIG_BLK_DEV_ELEVATOR_NOOP + elevator_init(&q->elevator, ELEVATOR_NOOP); +#endif + } } /* @@ -685,6 +700,10 @@ #else /* !CONFIG_IDEPCI_SHARE_IRQ */ int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT; #endif /* CONFIG_IDEPCI_SHARE_IRQ */ + + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + OUT_BYTE(0x08, hwif->io_ports[IDE_CONTROL_OFFSET]); /* clear nIEN */ + if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) { if (!match) kfree(hwgroup); @@ -752,18 +771,35 @@ int *bs, *max_sect, *max_ra; extern devfs_handle_t ide_devfs_handle; +#if 1 + units = MAX_DRIVES; +#else /* figure out maximum drive number on the interface */ for (units = MAX_DRIVES; units > 0; --units) { if (hwif->drives[units-1].present) break; } +#endif + minors = units * (1<sizes = kmalloc (minors * sizeof(int), GFP_KERNEL); + if (!gd->sizes) + goto err_kmalloc_gd_sizes; gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); + if (!gd->part) + goto err_kmalloc_gd_part; bs = kmalloc (minors*sizeof(int), GFP_KERNEL); + if (!bs) + goto err_kmalloc_bs; max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL); + if (!max_sect) + goto err_kmalloc_max_sect; max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL); + if (!max_ra) + goto err_kmalloc_max_ra; memset(gd->part, 0, minors * sizeof(struct hd_struct)); @@ -773,12 +809,10 @@ max_readahead[hwif->major] = max_ra; for (unit = 0; unit < minors; ++unit) { *bs++ = BLOCK_SIZE; -#ifdef CONFIG_BLK_DEV_PDC4030 - *max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : 255); -#else - /* IDE can do up to 128K per request. */ - *max_sect++ = 255; -#endif + /* + * IDE can do up to 128K per request == 256 + */ + *max_sect++ = ((hwif->chipset == ide_pdc4030) ? 127 : 128); *max_ra++ = vm_max_readahead; } @@ -804,6 +838,17 @@ add_gendisk(gd); for (unit = 0; unit < units; ++unit) { +#if 1 + char name[64]; + ide_add_generic_settings(hwif->drives + unit); + hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); + sprintf (name, "host%d/bus%d/target%d/lun%d", + (hwif->channel && hwif->mate) ? + hwif->mate->index : hwif->index, + hwif->channel, unit, hwif->drives[unit].lun); + if (hwif->drives[unit].present) + hwif->drives[unit].de = devfs_mk_dir(ide_devfs_handle, name, NULL); +#else if (hwif->drives[unit].present) { char name[64]; @@ -815,7 +860,23 @@ hwif->drives[unit].de = devfs_mk_dir (ide_devfs_handle, name, NULL); } +#endif } + return; + +err_kmalloc_max_ra: + kfree(max_sect); +err_kmalloc_max_sect: + kfree(bs); +err_kmalloc_bs: + kfree(gd->part); +err_kmalloc_gd_part: + kfree(gd->sizes); +err_kmalloc_gd_sizes: + kfree(gd); +err_kmalloc_gd: + printk(KERN_WARNING "(ide::init_gendisk) Out of memory\n"); + return; } static int hwif_init (ide_hwif_t *hwif) @@ -880,6 +941,19 @@ return hwif->present; } +void export_ide_init_queue (ide_drive_t *drive) +{ + ide_init_queue(drive); +} + +byte export_probe_for_drive (ide_drive_t *drive) +{ + return probe_for_drive(drive); +} + +EXPORT_SYMBOL(export_ide_init_queue); +EXPORT_SYMBOL(export_probe_for_drive); + int ideprobe_init (void); static ide_module_t ideprobe_module = { IDE_PROBE_MODULE, @@ -913,6 +987,8 @@ } #ifdef MODULE +extern int (*ide_xlate_1024_hook)(kdev_t, int, int, const char *); + int init_module (void) { unsigned int index; @@ -921,12 +997,14 @@ ide_unregister(index); ideprobe_init(); create_proc_ide_interfaces(); + ide_xlate_1024_hook = ide_xlate_1024; return 0; } void cleanup_module (void) { ide_probe = NULL; + ide_xlate_1024_hook = 0; } MODULE_LICENSE("GPL"); #endif /* MODULE */ diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c --- a/drivers/ide/ide-proc.c Wed Feb 13 20:03:32 2002 +++ b/drivers/ide/ide-proc.c Wed Feb 13 20:03:32 2002 @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -447,7 +448,15 @@ static int proc_ide_get_identify(ide_drive_t *drive, byte *buf) { - return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf); + struct hd_drive_task_hdr taskfile; + struct hd_drive_hob_hdr hobfile; + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + memset(&hobfile, 0, sizeof(struct hd_drive_hob_hdr)); + + taskfile.sector_count = 0x01; + taskfile.command = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY ; + + return ide_wait_taskfile(drive, &taskfile, &hobfile, buf); } static int proc_ide_read_identify @@ -457,7 +466,7 @@ int len = 0, i = 0; if (drive && !proc_ide_get_identify(drive, page)) { - unsigned short *val = ((unsigned short *)page) + 2; + unsigned short *val = (unsigned short *) page; char *out = ((char *)val) + (SECTOR_WORDS * 4); page = out; do { @@ -588,7 +597,7 @@ if (!driver) len = sprintf(page, "(none)\n"); else - len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); + len = sprintf(page,"%llu\n", (__u64) ((ide_driver_t *)drive->driver)->capacity(drive)); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -732,22 +741,57 @@ } } -void destroy_proc_ide_drives(ide_hwif_t *hwif) +void recreate_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) { - int d; + struct proc_dir_entry *ent; + struct proc_dir_entry *parent = hwif->proc; + char name[64]; +// ide_driver_t *driver = drive->driver; - for (d = 0; d < MAX_DRIVES; d++) { - ide_drive_t *drive = &hwif->drives[d]; - ide_driver_t *driver = drive->driver; + if (drive->present && !drive->proc) { + drive->proc = proc_mkdir(drive->name, parent); + if (drive->proc) + ide_add_proc_entries(drive->proc, generic_drive_entries, drive); - if (!drive->proc) - continue; +/* + * assume that we have these already, however, should test FIXME! + * if (driver) { + * ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); + * ide_add_proc_entries(drive->proc, driver->proc, drive); + * } + * + */ + sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name); + ent = proc_symlink(drive->name, proc_ide_root, name); + if (!ent) + return; + } +} + +void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_driver_t *driver = drive->driver; + + if (drive->proc) { if (driver) ide_remove_proc_entries(drive->proc, driver->proc); ide_remove_proc_entries(drive->proc, generic_drive_entries); remove_proc_entry(drive->name, proc_ide_root); remove_proc_entry(drive->name, hwif->proc); drive->proc = NULL; + } +} + +void destroy_proc_ide_drives(ide_hwif_t *hwif) +{ + int d; + + for (d = 0; d < MAX_DRIVES; d++) { + ide_drive_t *drive = &hwif->drives[d]; +// ide_driver_t *driver = drive->driver; + + if (drive->proc) + destroy_proc_ide_device(hwif, drive); } } diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c --- a/drivers/ide/ide-tape.c Wed Feb 13 20:03:36 2002 +++ b/drivers/ide/ide-tape.c Wed Feb 13 20:03:36 2002 @@ -6132,10 +6132,7 @@ #endif -static int idetape_reinit (ide_drive_t *drive) -{ - return 0; -} +int idetape_reinit(ide_drive_t *drive); /* * IDE subdriver functions, registered with ide.c @@ -6148,6 +6145,8 @@ supports_dma: 1, supports_dsc_overlap: 1, cleanup: idetape_cleanup, + standby: NULL, + flushcache: NULL, do_request: idetape_do_request, end_request: idetape_end_request, ioctl: idetape_blkdev_ioctl, @@ -6158,7 +6157,9 @@ pre_reset: idetape_pre_reset, capacity: NULL, proc: idetape_proc, - driver_reinit: idetape_reinit, + reinit: idetape_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, }; int idetape_init (void); @@ -6181,6 +6182,92 @@ release: idetape_chrdev_release, }; +int idetape_reinit (ide_drive_t *drive) +{ +#if 0 + idetape_tape_t *tape; + int minor, failed = 0, supported = 0; +/* DRIVER(drive)->busy++; */ + MOD_INC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n"); +#endif + if (!idetape_chrdev_present) + for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) + idetape_chrdevs[minor].drive = NULL; + + if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { + ide_register_module (&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return 0; + } + if (!idetape_chrdev_present && + devfs_register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) { + printk (KERN_ERR "ide-tape: Failed to register character device interface\n"); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return -EBUSY; + } + do { + if (!idetape_identify_device (drive, drive->id)) { + printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); + continue; + } + if (drive->scsi) { + if (strstr(drive->id->model, "OnStream DI-30")) { + printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); + } else { + printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + } + tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL); + if (tape == NULL) { + printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (tape); + continue; + } + for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++); + idetape_setup (drive, tape, minor); + idetape_chrdevs[minor].drive = drive; + tape->de_r = + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + tape->de_n = + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + devfs_register_tape (tape->de_r); + supported++; failed--; + } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL); + if (!idetape_chrdev_present && !supported) { + devfs_unregister_chrdev (IDETAPE_MAJOR, "ht"); + } else + idetape_chrdev_present = 1; + ide_register_module (&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + + return 0; +#else + return 1; +#endif +} + MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); MODULE_LICENSE("GPL"); @@ -6205,7 +6292,7 @@ ide_drive_t *drive; idetape_tape_t *tape; int minor, failed = 0, supported = 0; - +/* DRIVER(drive)->busy++; */ MOD_INC_USE_COUNT; #if ONSTREAM_DEBUG printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n"); diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-taskfile.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,1723 @@ +/* + * linux/drivers/ide/ide-taskfile.c Version 0.20 Oct 11, 2000 + * + * Copyright (C) 2000 Michael Cornwell + * Copyright (C) 2000 Andre Hedrick + * + * May be copied or modified under the terms of the GNU General Public License + * + * IDE_DEBUG(__LINE__); + */ + +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_IDE_TASKFILE_IO +# define __TASKFILE__IO +#else /* CONFIG_IDE_TASKFILE_IO */ +# undef __TASKFILE__IO +#endif /* CONFIG_IDE_TASKFILE_IO */ + +#define DEBUG_TASKFILE 0 /* unset when fixed */ + +#if DEBUG_TASKFILE +#define DTF(x...) printk(##x) +#else +#define DTF(x...) +#endif + +inline u32 task_read_24 (ide_drive_t *drive) +{ + return (IN_BYTE(IDE_HCYL_REG)<<16) | + (IN_BYTE(IDE_LCYL_REG)<<8) | + IN_BYTE(IDE_SECTOR_REG); +} + +static void ata_bswap_data (void *buffer, int wcount) +{ + u16 *p = buffer; + + while (wcount--) { + *p = *p << 8 | *p >> 8; p++; + *p = *p << 8 | *p >> 8; p++; + } +} + +#if SUPPORT_VLB_SYNC +/* + * Some localbus EIDE interfaces require a special access sequence + * when using 32-bit I/O instructions to transfer data. We call this + * the "vlb_sync" sequence, which consists of three successive reads + * of the sector count register location, with interrupts disabled + * to ensure that the reads all happen together. + */ +static inline void task_vlb_sync (ide_ioreg_t port) { + (void) inb (port); + (void) inb (port); + (void) inb (port); +} +#endif /* SUPPORT_VLB_SYNC */ + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +void ata_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + byte io_32bit = drive->io_32bit; + + if (io_32bit) { +#if SUPPORT_VLB_SYNC + if (io_32bit & 2) { + unsigned long flags; + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + task_vlb_sync(IDE_NSECTOR_REG); + insl(IDE_DATA_REG, buffer, wcount); + __restore_flags(flags); /* local CPU only */ + } else +#endif /* SUPPORT_VLB_SYNC */ + insl(IDE_DATA_REG, buffer, wcount); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + *ptr++ = inw_p(IDE_DATA_REG); + *ptr++ = inw_p(IDE_DATA_REG); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + insw(IDE_DATA_REG, buffer, wcount<<1); + } +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +void ata_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + byte io_32bit = drive->io_32bit; + + if (io_32bit) { +#if SUPPORT_VLB_SYNC + if (io_32bit & 2) { + unsigned long flags; + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + task_vlb_sync(IDE_NSECTOR_REG); + outsl(IDE_DATA_REG, buffer, wcount); + __restore_flags(flags); /* local CPU only */ + } else +#endif /* SUPPORT_VLB_SYNC */ + outsl(IDE_DATA_REG, buffer, wcount); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + outw_p(*ptr++, IDE_DATA_REG); + outw_p(*ptr++, IDE_DATA_REG); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + outsw(IDE_DATA_REG, buffer, wcount<<1); + } +} + + +static inline void taskfile_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + ata_input_data(drive, buffer, wcount); + if (drive->bswap) + ata_bswap_data(buffer, wcount); +} + +static inline void taskfile_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + if (drive->bswap) { + ata_bswap_data(buffer, wcount); + ata_output_data(drive, buffer, wcount); + ata_bswap_data(buffer, wcount); + } else { + ata_output_data(drive, buffer, wcount); + } +} + +ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; + struct hd_driveid *id = drive->id; + byte HIHI = (drive->addressing) ? 0xE0 : 0xEF; + + /* (ks/hs): Moved to start, do not use for multiple out commands */ + if (task->handler != task_mulout_intr) { + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + } + + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(hobfile->feature, IDE_FEATURE_REG); + OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); + OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); + OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); + } + + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to number of sectors to transfer */ + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + /* refers to sector offset or start sector */ + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + + OUT_BYTE((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); + if (task->handler != NULL) { +#if 0 + ide_set_handler (drive, task->handler, WAIT_CMD, NULL); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + /* + * warning check for race between handler and prehandler for + * writing first block of data. however since we are well + * inside the boundaries of the seek, we should be okay. + */ + if (task->prehandler != NULL) { + return task->prehandler(drive, task->rq); + } +#else + ide_startstop_t startstop; + + ide_set_handler (drive, task->handler, WAIT_CMD, NULL); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", + drive->name, + drive->mult_count ? "MULTWRITE" : "WRITE"); + return startstop; + } + /* (ks/hs): Fixed Multi Write */ + if ((taskfile->command != WIN_MULTWRITE) && + (taskfile->command != WIN_MULTWRITE_EXT)) { + struct request *rq = HWGROUP(drive)->rq; + /* For Write_sectors we need to stuff the first sector */ + taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); + rq->current_nr_sectors--; + } else { + /* Stuff first sector(s) by implicitly calling the handler */ + if (!(drive_is_ready(drive))) { + /* FIXME: Replace hard-coded 100, error handling? */ + int i; + for (i=0; i<100; i++) { + if (drive_is_ready(drive)) + break; + } + } + return task->handler(drive); + } +#endif + } else { + /* for dma commands we down set the handler */ + if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); + } + + return ide_started; +} + +void do_taskfile (ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, ide_handler_t *handler) +{ + struct hd_driveid *id = drive->id; + byte HIHI = (drive->addressing) ? 0xE0 : 0xEF; + + /* (ks/hs): Moved to start, do not use for multiple out commands */ + if (*handler != task_mulout_intr) { + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + } + + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(hobfile->feature, IDE_FEATURE_REG); + OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); + OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); + OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); + } + + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to number of sectors to transfer */ + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + /* refers to sector offset or start sector */ + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + + OUT_BYTE((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); + if (handler != NULL) { + ide_set_handler (drive, handler, WAIT_CMD, NULL); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + } else { + /* for dma commands we down set the handler */ + if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); + } +} + +#if 0 +ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; + struct hd_driveid *id = drive->id; + + /* + * (KS) Check taskfile in/out flags. + * If set, then execute as it is defined. + * If not set, then define default settings. + * The default values are: + * write and read all taskfile registers (except data) + * write and read the hob registers (sector,nsector,lcyl,hcyl) + */ + if (task->tf_out_flags.all == 0) { + task->tf_out_flags.all = IDE_TASKFILE_STD_OUT_FLAGS; + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + task->tf_out_flags.all != (IDE_HOB_STD_OUT_FLAGS << 8); + } + } + + if (task->tf_in_flags.all == 0) { + task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + task->tf_in_flags.all != (IDE_HOB_STD_IN_FLAGS << 8); + } + } + + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + + if (task->tf_out_flags.b.data) { + unsigned short data = taskfile->data + (hobfile->data << 8); + OUT_WORD (data, IDE_DATA_REG); + } + + /* (KS) send hob registers first */ + if (task->tf_out_flags.b.nsector_hob) + OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); + if (task->tf_out_flags.b.sector_hob) + OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl_hob) + OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl_hob) + OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); + + + /* (KS) Send now the standard registers */ + if (task->tf_out_flags.b.error_feature) + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + /* refers to number of sectors to transfer */ + if (task->tf_out_flags.b.nsector) + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to sector offset or start sector */ + if (task->tf_out_flags.b.sector) + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl) + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl) + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + + /* + * (KS) Do not modify the specified taskfile. We want to have a + * universal pass through, so we must execute ALL specified values. + * + * (KS) The drive head register is mandatory. + * Don't care about the out flags ! + */ + OUT_BYTE(taskfile->device_head | drive->select.all, IDE_SELECT_REG); + if (task->handler != NULL) { +#if 0 + ide_set_handler (drive, task->handler, WAIT_CMD, NULL); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + /* + * warning check for race between handler and prehandler for + * writing first block of data. however since we are well + * inside the boundaries of the seek, we should be okay. + */ + if (task->prehandler != NULL) { + return task->prehandler(drive, task->rq); + } +#else + ide_startstop_t startstop; + + ide_set_handler (drive, task->handler, WAIT_CMD, NULL); + + /* + * (KS) The drive command register is also mandatory. + * Don't care about the out flags ! + */ + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", + drive->name, + drive->mult_count ? "MULTWRITE" : "WRITE"); + return startstop; + } + /* (ks/hs): Fixed Multi Write */ + if ((taskfile->command != WIN_MULTWRITE) && + (taskfile->command != WIN_MULTWRITE_EXT)) { + struct request *rq = HWGROUP(drive)->rq; + /* For Write_sectors we need to stuff the first sector */ + taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); + rq->current_nr_sectors--; + } else { + /* Stuff first sector(s) by implicitly calling the handler */ + if (!(drive_is_ready(drive))) { + /* FIXME: Replace hard-coded 100, error handling? */ + int i; + for (i=0; i<100; i++) { + if (drive_is_ready(drive)) + break; + } + } + return task->handler(drive); + } +#endif + } else { + /* for dma commands we down set the handler */ + if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); + } + + return ide_started; +} +#endif + +#if 0 +/* + * Error reporting, in human readable form (luxurious, but a memory hog). + */ +byte taskfile_dump_status (ide_drive_t *drive, const char *msg, byte stat) +{ + unsigned long flags; + byte err = 0; + + __save_flags (flags); /* local CPU only */ + ide__sti(); /* local CPU only */ + printk("%s: %s: status=0x%02x", drive->name, msg, stat); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (stat & BUSY_STAT) + printk("Busy "); + else { + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("DeviceFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + } + printk("}"); +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = GET_ERR(); + printk("%s: %s: error=0x%02x", drive->name, msg, err); +#if FANCY_STATUS_DUMPS + if (drive->media == ide_disk) { + printk(" { "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); + if (err & ECC_ERR) printk("UncorrectableError "); + if (err & ID_ERR) printk("SectorIdNotFound "); + if (err & TRK0_ERR) printk("TrackZeroNotFound "); + if (err & MARK_ERR) printk("AddrMarkNotFound "); + printk("}"); + if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = task_read_24(drive); + OUT_BYTE(0x80, IDE_CONTROL_REG); + high = task_read_24(drive); + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%lld", sectors); + } else { + byte cur = IN_BYTE(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(IN_BYTE(IDE_HCYL_REG)<<16) + |(IN_BYTE(IDE_LCYL_REG)<<8) + | IN_BYTE(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (IN_BYTE(IDE_HCYL_REG)<<8) + + IN_BYTE(IDE_LCYL_REG), + cur & 0xf, + IN_BYTE(IDE_SECTOR_REG)); + } + } + if (HWGROUP(drive)->rq) + printk(", sector=%llu", (__u64) HWGROUP(drive)->rq->sector); + } + } +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + } + __restore_flags (flags); /* local CPU only */ + return err; +} + +/* + * Clean up after success/failure of an explicit taskfile operation. + */ +void ide_end_taskfile (ide_drive_t *drive, byte stat, byte err) +{ + unsigned long flags; + struct request *rq; + ide_task_t *args; + task_ioreg_t command; + + spin_lock_irqsave(&io_request_lock, flags); + rq = HWGROUP(drive)->rq; + spin_unlock_irqrestore(&io_request_lock, flags); + args = (ide_task_t *) rq->special; + + command = args->tfRegister[IDE_COMMAND_OFFSET]; + + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + args->tfRegister[IDE_ERROR_OFFSET] = err; + args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); + args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); + args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); + args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); + args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); + args->tfRegister[IDE_STATUS_OFFSET] = stat; + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); + args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); + args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); + args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); + args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); + args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); + } + +/* taskfile_settings_update(drive, args, command); */ + + spin_lock_irqsave(&io_request_lock, flags); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* + * try_to_flush_leftover_data() is invoked in response to a drive + * unexpectedly having its DRQ_STAT bit set. As an alternative to + * resetting the drive, this routine tries to clear the condition + * by read a sector's worth of data from the drive. Of course, + * this may not help if the drive is *waiting* for data from *us*. + */ +void task_try_to_flush_leftover_data (ide_drive_t *drive) +{ + int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; + + if (drive->media != ide_disk) + return; + while (i > 0) { + u32 buffer[16]; + unsigned int wcount = (i > 16) ? 16 : i; + i -= wcount; + taskfile_input_data (drive, buffer, wcount); + } +} + +/* + * taskfile_error() takes action based on the error returned by the drive. + */ +ide_startstop_t taskfile_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + + err = taskfile_dump_status(drive, msg, stat); + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + /* retry only "normal" I/O: */ + if (rq->cmd == IDE_DRIVE_TASKFILE) { + rq->errors = 1; + ide_end_taskfile(drive, stat, err); + return ide_stopped; + } + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else { + if (drive->media == ide_disk && (stat & ERR_STAT)) { + /* err has different meaning on cdrom and tape */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return ide_stopped; /* some newer drives don't support WIN_SPECIFY */ + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { + drive->crc_count++; /* UDMA crc error -- just retry the operation */ + } else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ + rq->errors = ERROR_MAX; + else if (err & TRK0_ERR) /* help it find track zero */ + rq->errors |= ERROR_RECAL; + } + if ((stat & DRQ_STAT) && rq->cmd != WRITE) + task_try_to_flush_leftover_data(drive); + } + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ + + if (rq->errors >= ERROR_MAX) { + if (drive->driver != NULL) + DRIVER(drive)->end_request(0, HWGROUP(drive)); + else + ide_end_request(0, HWGROUP(drive)); + } else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + drive->special.b.recalibrate = 1; + ++rq->errors; + } + return ide_stopped; +} +#endif + +/* + * Handler for special commands without a data phase from ide-disk + */ + +/* + * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. + */ +ide_startstop_t set_multmode_intr (ide_drive_t *drive) +{ + byte stat; + + if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) { + drive->mult_count = drive->mult_req; + } else { + drive->mult_req = drive->mult_count = 0; + drive->special.b.recalibrate = 1; + (void) ide_dump_status(drive, "set_multmode", stat); + } + return ide_stopped; +} + +/* + * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. + */ +ide_startstop_t set_geometry_intr (ide_drive_t *drive) +{ + byte stat; + + if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) + return ide_stopped; + + if (stat & (ERR_STAT|DRQ_STAT)) + return ide_error(drive, "set_geometry_intr", stat); + + ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* + * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. + */ +ide_startstop_t recal_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + + if (!OK_STAT(stat,READY_STAT,BAD_STAT)) + return ide_error(drive, "recal_intr", stat); + return ide_stopped; +} + +/* + * Handler for commands without a data phase + */ +ide_startstop_t task_no_data_intr (ide_drive_t *drive) +{ + ide_task_t *args = HWGROUP(drive)->rq->special; + byte stat = GET_STAT(); + + ide__sti(); /* local CPU only */ + + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + return ide_error(drive, "task_no_data_intr", stat); /* calls ide_end_drive_cmd */ + + if (args) + ide_end_drive_cmd (drive, stat, GET_ERR()); + + return ide_stopped; +} + +/* + * Handler for command with PIO data-in phase + */ +ide_startstop_t task_in_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "task_in_intr", stat); + } + if (!(stat & BUSY_STAT)) { + DTF("task_in_intr to Soon wait for next interrupt\n"); + ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); + return ide_started; + } + } + DTF("stat: %02x\n", stat); + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Read: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors); + + drive->io_32bit = 0; + taskfile_input_data(drive, pBuf, SECTOR_WORDS); + drive->io_32bit = io_32bit; + + if (--rq->current_nr_sectors <= 0) { + /* (hs): swapped next 2 lines */ + DTF("Request Ended stat: %02x\n", GET_STAT()); + ide_end_request(1, HWGROUP(drive)); + } else { + ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); + return ide_started; + } + return ide_stopped; +} + +#undef ALTSTAT_SCREW_UP + +#ifdef ALTSTAT_SCREW_UP +/* + * (ks/hs): Poll Alternate Status Register to ensure + * that drive is not busy. + */ +byte altstat_multi_busy (ide_drive_t *drive, byte stat, const char *msg) +{ + int i; + + DTF("multi%s: ASR = %x\n", msg, stat); + if (stat & BUSY_STAT) { + /* (ks/hs): FIXME: Replace hard-coded 100, error handling? */ + for (i=0; i<100; i++) { + stat = GET_ALTSTAT(); + if ((stat & BUSY_STAT) == 0) + break; + } + } + /* + * (ks/hs): Read Status AFTER Alternate Status Register + */ + return(GET_STAT()); +} + +/* + * (ks/hs): Poll Alternate status register to wait for drive + * to become ready for next transfer + */ +byte altstat_multi_poll (ide_drive_t *drive, byte stat, const char *msg) +{ + /* (ks/hs): FIXME: Error handling, time-out? */ + while (stat & BUSY_STAT) + stat = GET_ALTSTAT(); + DTF("multi%s: nsect=1, ASR = %x\n", msg, stat); + return(GET_STAT()); /* (ks/hs): Clear pending IRQ */ +} +#endif /* ALTSTAT_SCREW_UP */ + +/* + * Handler for command with Read Multiple + */ +ide_startstop_t task_mulin_intr (ide_drive_t *drive) +{ + unsigned int msect, nsect; + +#ifdef ALTSTAT_SCREW_UP + byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "read"); +#else + byte stat = GET_STAT(); +#endif /* ALTSTAT_SCREW_UP */ + + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "task_mulin_intr", stat); + } + /* no data yet, so wait for another interrupt */ + ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); + return ide_started; + } + + /* (ks/hs): Fixed Multi-Sector transfer */ + msect = drive->mult_count; + +#ifdef ALTSTAT_SCREW_UP + /* + * Screw the request we do not support bad data-phase setups! + * Either read and learn the ATA standard or crash yourself! + */ + if (!msect) { + /* + * (ks/hs): Drive supports multi-sector transfer, + * drive->mult_count was not set + */ + nsect = 1; + while (rq->current_nr_sectors) { + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Multiread: %p, nsect: %d, rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + stat = altstat_multi_poll(drive, GET_ALTSTAT(), "read"); + } + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } +#endif /* ALTSTAT_SCREW_UP */ + + nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + + DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + if (rq->current_nr_sectors != 0) { + ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); + return ide_started; + } + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; +} + +ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq) +{ + ide_task_t *args = rq->special; + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); + return startstop; + } + + /* (ks/hs): Fixed Multi Write */ + if ((args->tfRegister[IDE_COMMAND_OFFSET] != WIN_MULTWRITE) && + (args->tfRegister[IDE_COMMAND_OFFSET] != WIN_MULTWRITE_EXT)) { + /* For Write_sectors we need to stuff the first sector */ + taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); + rq->current_nr_sectors--; + return ide_started; + } else { + /* + * (ks/hs): Stuff the first sector(s) + * by implicitly calling the handler + */ + if (!(drive_is_ready(drive))) { + int i; + /* + * (ks/hs): FIXME: Replace hard-coded + * 100, error handling? + */ + for (i=0; i<100; i++) { + if (drive_is_ready(drive)) + break; + } + } + return args->handler(drive); + } + return ide_started; +} + +/* + * Handler for command with PIO data-out phase + */ +ide_startstop_t task_out_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + + if (!rq->current_nr_sectors) { + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } + + if (!OK_STAT(stat,DRIVE_READY,drive->bad_wstat)) { + return ide_error(drive, "task_out_intr", stat); + } + if ((rq->current_nr_sectors==1) ^ (stat & DRQ_STAT)) { + rq = HWGROUP(drive)->rq; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("write: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_output_data(drive, pBuf, SECTOR_WORDS); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors--; + } + + if (rq->current_nr_sectors <= 0) { + ide_end_request(1, HWGROUP(drive)); + } else { + ide_set_handler(drive, &task_out_intr, WAIT_CMD, NULL); + return ide_started; + } + return ide_stopped; +} + +/* + * Handler for command write multiple + * Called directly from execute_drive_cmd for the first bunch of sectors, + * afterwards only by the ISR + */ +ide_startstop_t task_mulout_intr (ide_drive_t *drive) +{ + unsigned int msect, nsect; + +#ifdef ALTSTAT_SCREW_UP + byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "write"); +#else + byte stat = GET_STAT(); +#endif /* ALTSTAT_SCREW_UP */ + + byte io_32bit = drive->io_32bit; + struct request *rq = HWGROUP(drive)->rq; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + char *pBuf = NULL; + + /* + * (ks/hs): Handle last IRQ on multi-sector transfer, + * occurs after all data was sent + */ + if (rq->current_nr_sectors == 0) { + if (stat & (ERR_STAT|DRQ_STAT)) + return ide_error(drive, "task_mulout_intr", stat); + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return ide_error(drive, "task_mulout_intr", stat); + } + /* no data yet, so wait for another interrupt */ + if (hwgroup->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; + } + + /* (ks/hs): See task_mulin_intr */ + msect = drive->mult_count; + +#ifdef ALTSTAT_SCREW_UP + /* + * Screw the request we do not support bad data-phase setups! + * Either read and learn the ATA standard or crash yourself! + */ + if (!msect) { + nsect = 1; + while (rq->current_nr_sectors) { + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Multiwrite: %p, nsect: %d, rq->current_nr_sectors: %ld\n", pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + stat = altstat_multi_poll(drive, GET_ALTSTAT(), "write"); + } + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } +#endif /* ALTSTAT_SCREW_UP */ + + nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Multiwrite: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + drive->io_32bit = 0; + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + drive->io_32bit = io_32bit; + rq->errors = 0; + rq->current_nr_sectors -= nsect; + if (hwgroup->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* Called by internal to feature out type of command being called */ +ide_pre_handler_t * ide_pre_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) +{ + switch(taskfile->command) { + /* IDE_DRIVE_TASK_RAW_WRITE */ + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + case WIN_MULTWRITE_EXT: +// case WIN_WRITEDMA: +// case WIN_WRITEDMA_QUEUED: +// case WIN_WRITEDMA_EXT: +// case WIN_WRITEDMA_QUEUED_EXT: + /* IDE_DRIVE_TASK_OUT */ + case WIN_WRITE: + case WIN_WRITE_VERIFY: + case WIN_WRITE_BUFFER: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_DOWNLOAD_MICROCODE: + return &pre_task_out_intr; + /* IDE_DRIVE_TASK_OUT */ + case WIN_SMART: + if (taskfile->feature == SMART_WRITE_LOG_SECTOR) + return &pre_task_out_intr; + default: + break; + } + return(NULL); +} + +/* Called by internal to feature out type of command being called */ +ide_handler_t * ide_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) +{ + switch(taskfile->command) { + case WIN_IDENTIFY: + case WIN_PIDENTIFY: + case CFA_TRANSLATE_SECTOR: + case WIN_READ_BUFFER: + case WIN_READ: + case WIN_READ_EXT: + return &task_in_intr; + case WIN_SECURITY_DISABLE: + case WIN_SECURITY_ERASE_UNIT: + case WIN_SECURITY_SET_PASS: + case WIN_SECURITY_UNLOCK: + case WIN_DOWNLOAD_MICROCODE: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_WRITE_BUFFER: + case WIN_WRITE_VERIFY: + case WIN_WRITE: + case WIN_WRITE_EXT: + return &task_out_intr; + case WIN_MULTREAD: + case WIN_MULTREAD_EXT: + return &task_mulin_intr; + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + case WIN_MULTWRITE_EXT: + return &task_mulout_intr; + case WIN_SMART: + switch(taskfile->feature) { + case SMART_READ_VALUES: + case SMART_READ_THRESHOLDS: + case SMART_READ_LOG_SECTOR: + return &task_in_intr; + case SMART_WRITE_LOG_SECTOR: + return &task_out_intr; + default: + return &task_no_data_intr; + } + case CFA_REQ_EXT_ERROR_CODE: + case CFA_ERASE_SECTORS: + case WIN_VERIFY: + case WIN_VERIFY_EXT: + case WIN_SEEK: + return &task_no_data_intr; + case WIN_SPECIFY: + return &set_geometry_intr; + case WIN_RESTORE: + return &recal_intr; + case WIN_DIAGNOSE: + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: + case WIN_SLEEPNOW1: + case WIN_SLEEPNOW2: + case WIN_SETIDLE1: + case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: + case WIN_GETMEDIASTATUS: + case WIN_MEDIAEJECT: + return &task_no_data_intr; + case WIN_SETMULT: + return &set_multmode_intr; + case WIN_READ_NATIVE_MAX: + case WIN_SET_MAX: + case WIN_READ_NATIVE_MAX_EXT: + case WIN_SET_MAX_EXT: + case WIN_SECURITY_ERASE_PREPARE: + case WIN_SECURITY_FREEZE_LOCK: + case WIN_DOORLOCK: + case WIN_DOORUNLOCK: + case WIN_SETFEATURES: + return &task_no_data_intr; + case DISABLE_SEAGATE: + case EXABYTE_ENABLE_NEST: + return &task_no_data_intr; +#ifdef CONFIG_BLK_DEV_IDEDMA + case WIN_READDMA: + case WIN_IDENTIFY_DMA: + case WIN_READDMA_QUEUED: + case WIN_READDMA_EXT: + case WIN_READDMA_QUEUED_EXT: + case WIN_WRITEDMA: + case WIN_WRITEDMA_QUEUED: + case WIN_WRITEDMA_EXT: + case WIN_WRITEDMA_QUEUED_EXT: +#endif + case WIN_FORMAT: + case WIN_INIT: + case WIN_DEVICE_RESET: + case WIN_QUEUED_SERVICE: + case WIN_PACKETCMD: + default: + return(NULL); + } +} + +/* Called by ioctl to feature out type of command being called */ +int ide_cmd_type_parser (ide_task_t *args) +{ + struct hd_drive_task_hdr *taskfile = (struct hd_drive_task_hdr *) args->tfRegister; + struct hd_drive_hob_hdr *hobfile = (struct hd_drive_hob_hdr *) args->hobRegister; + + args->prehandler = ide_pre_handler_parser(taskfile, hobfile); + args->handler = ide_handler_parser(taskfile, hobfile); + + switch(args->tfRegister[IDE_COMMAND_OFFSET]) { + case WIN_IDENTIFY: + case WIN_PIDENTIFY: + return IDE_DRIVE_TASK_IN; + case CFA_TRANSLATE_SECTOR: + case WIN_READ: + case WIN_READ_BUFFER: + return IDE_DRIVE_TASK_IN; + case WIN_WRITE: + case WIN_WRITE_VERIFY: + case WIN_WRITE_BUFFER: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_DOWNLOAD_MICROCODE: + return IDE_DRIVE_TASK_RAW_WRITE; + case WIN_MULTREAD: + return IDE_DRIVE_TASK_IN; + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + return IDE_DRIVE_TASK_RAW_WRITE; + case WIN_SECURITY_DISABLE: + case WIN_SECURITY_ERASE_UNIT: + case WIN_SECURITY_SET_PASS: + case WIN_SECURITY_UNLOCK: + return IDE_DRIVE_TASK_OUT; + case WIN_SMART: + args->tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args->tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + switch(args->tfRegister[IDE_FEATURE_OFFSET]) { + case SMART_READ_VALUES: + case SMART_READ_THRESHOLDS: + case SMART_READ_LOG_SECTOR: + return IDE_DRIVE_TASK_IN; + case SMART_WRITE_LOG_SECTOR: + return IDE_DRIVE_TASK_OUT; + default: + return IDE_DRIVE_TASK_NO_DATA; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + case WIN_READDMA: + case WIN_IDENTIFY_DMA: + case WIN_READDMA_QUEUED: + case WIN_READDMA_EXT: + case WIN_READDMA_QUEUED_EXT: + return IDE_DRIVE_TASK_IN; + case WIN_WRITEDMA: + case WIN_WRITEDMA_QUEUED: + case WIN_WRITEDMA_EXT: + case WIN_WRITEDMA_QUEUED_EXT: + return IDE_DRIVE_TASK_RAW_WRITE; +#endif + case WIN_SETFEATURES: + switch(args->tfRegister[IDE_FEATURE_OFFSET]) { + case SETFEATURES_XFER: + return IDE_DRIVE_TASK_SET_XFER; + case SETFEATURES_DIS_DEFECT: + case SETFEATURES_EN_APM: + case SETFEATURES_DIS_MSN: + case SETFEATURES_EN_RI: + case SETFEATURES_EN_SI: + case SETFEATURES_DIS_RPOD: + case SETFEATURES_DIS_WCACHE: + case SETFEATURES_EN_DEFECT: + case SETFEATURES_DIS_APM: + case SETFEATURES_EN_MSN: + case SETFEATURES_EN_RLA: + case SETFEATURES_PREFETCH: + case SETFEATURES_EN_RPOD: + case SETFEATURES_DIS_RI: + case SETFEATURES_DIS_SI: + default: + return IDE_DRIVE_TASK_NO_DATA; + } + case WIN_NOP: + case CFA_REQ_EXT_ERROR_CODE: + case CFA_ERASE_SECTORS: + case WIN_VERIFY: + case WIN_VERIFY_EXT: + case WIN_SEEK: + case WIN_SPECIFY: + case WIN_RESTORE: + case WIN_DIAGNOSE: + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: + case WIN_SLEEPNOW1: + case WIN_SLEEPNOW2: + case WIN_SETIDLE1: + case DISABLE_SEAGATE: + case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: + case WIN_GETMEDIASTATUS: + case WIN_MEDIAEJECT: + case WIN_SETMULT: + case WIN_READ_NATIVE_MAX: + case WIN_SET_MAX: + case WIN_READ_NATIVE_MAX_EXT: + case WIN_SET_MAX_EXT: + case WIN_SECURITY_ERASE_PREPARE: + case WIN_SECURITY_FREEZE_LOCK: + case EXABYTE_ENABLE_NEST: + case WIN_DOORLOCK: + case WIN_DOORUNLOCK: + return IDE_DRIVE_TASK_NO_DATA; + case WIN_FORMAT: + case WIN_INIT: + case WIN_DEVICE_RESET: + case WIN_QUEUED_SERVICE: + case WIN_PACKETCMD: + default: + return IDE_DRIVE_TASK_INVALID; + } +} + +/* + * This function is intended to be used prior to invoking ide_do_drive_cmd(). + */ +void ide_init_drive_taskfile (struct request *rq) +{ + memset(rq, 0, sizeof(*rq)); + rq->cmd = IDE_DRIVE_TASK_NO_DATA; +} + +/* + * This is kept for internal use only !!! + * This is an internal call and nobody in user-space has a damn + * reason to call this taskfile. + * + * ide_raw_taskfile is the one that user-space executes. + */ +int ide_wait_taskfile (ide_drive_t *drive, struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile, byte *buf) +{ + struct request rq; + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + + args.tfRegister[IDE_DATA_OFFSET] = taskfile->data; + args.tfRegister[IDE_FEATURE_OFFSET] = taskfile->feature; + args.tfRegister[IDE_NSECTOR_OFFSET] = taskfile->sector_count; + args.tfRegister[IDE_SECTOR_OFFSET] = taskfile->sector_number; + args.tfRegister[IDE_LCYL_OFFSET] = taskfile->low_cylinder; + args.tfRegister[IDE_HCYL_OFFSET] = taskfile->high_cylinder; + args.tfRegister[IDE_SELECT_OFFSET] = taskfile->device_head; + args.tfRegister[IDE_COMMAND_OFFSET] = taskfile->command; + + args.hobRegister[IDE_DATA_OFFSET_HOB] = hobfile->data; + args.hobRegister[IDE_FEATURE_OFFSET_HOB] = hobfile->feature; + args.hobRegister[IDE_NSECTOR_OFFSET_HOB] = hobfile->sector_count; + args.hobRegister[IDE_SECTOR_OFFSET_HOB] = hobfile->sector_number; + args.hobRegister[IDE_LCYL_OFFSET_HOB] = hobfile->low_cylinder; + args.hobRegister[IDE_HCYL_OFFSET_HOB] = hobfile->high_cylinder; + args.hobRegister[IDE_SELECT_OFFSET_HOB] = hobfile->device_head; + args.hobRegister[IDE_CONTROL_OFFSET_HOB] = hobfile->control; + + ide_init_drive_taskfile(&rq); + /* This is kept for internal use only !!! */ + args.command_type = ide_cmd_type_parser (&args); + if (args.command_type != IDE_DRIVE_TASK_NO_DATA) + rq.current_nr_sectors = rq.nr_sectors = (hobfile->sector_count << 8) | taskfile->sector_count; + + rq.cmd = IDE_DRIVE_TASKFILE; + rq.buffer = buf; + rq.special = &args; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, byte *buf) +{ + struct request rq; + ide_init_drive_taskfile(&rq); + rq.cmd = IDE_DRIVE_TASKFILE; + rq.buffer = buf; + + if (args->command_type != IDE_DRIVE_TASK_NO_DATA) + rq.current_nr_sectors = rq.nr_sectors = (args->hobRegister[IDE_NSECTOR_OFFSET_HOB] << 8) | args->tfRegister[IDE_NSECTOR_OFFSET]; + + rq.special = args; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG +char * ide_ioctl_verbose (unsigned int cmd) +{ + return("unknown"); +} + +char * ide_task_cmd_verbose (byte task) +{ + return("unknown"); +} +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + +/* + * The taskfile glue table + * + * reqtask.data_phase reqtask.req_cmd + * args.command_type args.handler + * + * TASKFILE_P_OUT_DMAQ ?? ?? + * TASKFILE_P_IN_DMAQ ?? ?? + * TASKFILE_P_OUT_DMA ?? ?? + * TASKFILE_P_IN_DMA ?? ?? + * TASKFILE_P_OUT ?? ?? + * TASKFILE_P_IN ?? ?? + * + * TASKFILE_OUT_DMAQ IDE_DRIVE_TASK_RAW_WRITE NULL + * TASKFILE_IN_DMAQ IDE_DRIVE_TASK_IN NULL + * + * TASKFILE_OUT_DMA IDE_DRIVE_TASK_RAW_WRITE NULL + * TASKFILE_IN_DMA IDE_DRIVE_TASK_IN NULL + * + * TASKFILE_IN_OUT ?? ?? + * + * TASKFILE_MULTI_OUT IDE_DRIVE_TASK_RAW_WRITE task_mulout_intr + * TASKFILE_MULTI_IN IDE_DRIVE_TASK_IN task_mulin_intr + * + * TASKFILE_OUT IDE_DRIVE_TASK_RAW_WRITE task_out_intr + * TASKFILE_OUT IDE_DRIVE_TASK_OUT task_out_intr + * + * TASKFILE_IN IDE_DRIVE_TASK_IN task_in_intr + * TASKFILE_NO_DATA IDE_DRIVE_TASK_NO_DATA task_no_data_intr + * + * IDE_DRIVE_TASK_SET_XFER task_no_data_intr + * IDE_DRIVE_TASK_INVALID + * + */ + +#define MAX_DMA (256*SECTOR_WORDS) + +int ide_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + ide_task_request_t *req_task; + ide_task_t args; + + byte *outbuf = NULL; + byte *inbuf = NULL; + task_ioreg_t *argsptr = args.tfRegister; + task_ioreg_t *hobsptr = args.hobRegister; + int err = 0; + int tasksize = sizeof(struct ide_task_request_s); + int taskin = 0; + int taskout = 0; + + req_task = kmalloc(tasksize, GFP_KERNEL); + if (req_task == NULL) return -ENOMEM; + memset(req_task, 0, tasksize); + if (copy_from_user(req_task, (void *) arg, tasksize)) { + kfree(req_task); + return -EFAULT; + } + + taskout = (int) req_task->out_size; + taskin = (int) req_task->in_size; + + if (taskout) { + int outtotal = tasksize; + outbuf = kmalloc(taskout, GFP_KERNEL); + if (outbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(outbuf, 0, taskout); + if (copy_from_user(outbuf, (void *)arg + outtotal, taskout)) { + err = -EFAULT; + goto abort; + } + } + + if (taskin) { + int intotal = tasksize + taskout; + inbuf = kmalloc(taskin, GFP_KERNEL); + if (inbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(inbuf, 0, taskin); + if (copy_from_user(inbuf, (void *)arg + intotal , taskin)) { + err = -EFAULT; + goto abort; + } + } + + memset(argsptr, 0, HDIO_DRIVE_TASK_HDR_SIZE); + memset(hobsptr, 0, HDIO_DRIVE_HOB_HDR_SIZE); + memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE); + + args.tf_in_flags = req_task->in_flags; + args.tf_out_flags = req_task->out_flags; + args.data_phase = req_task->data_phase; + args.command_type = req_task->req_cmd; + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + DTF("%s: ide_ioctl_cmd %s: ide_task_cmd %s\n", + drive->name, + ide_ioctl_verbose(cmd), + ide_task_cmd_verbose(args.tfRegister[IDE_COMMAND_OFFSET])); +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + + switch(req_task->data_phase) { + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + args.prehandler = NULL; + args.handler = NULL; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, outbuf); + break; + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + args.prehandler = NULL; + args.handler = NULL; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, inbuf); + break; + case TASKFILE_IN_OUT: +#if 0 + args.prehandler = &pre_task_out_intr; + args.handler = &task_out_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, outbuf); + args.prehandler = NULL; + args.handler = &task_in_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, inbuf); + break; +#else + err = -EFAULT; + goto abort; +#endif + case TASKFILE_MULTI_OUT: + if (drive->mult_count) { + args.prehandler = &pre_task_out_intr; + args.handler = &task_mulout_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, outbuf); + } else { + /* (hs): give up if multcount is not set */ + printk("%s: %s Multimode Write " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + break; + case TASKFILE_OUT: + args.prehandler = &pre_task_out_intr; + args.handler = &task_out_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, outbuf); + break; + case TASKFILE_MULTI_IN: + if (drive->mult_count) { + args.prehandler = NULL; + args.handler = &task_mulin_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, inbuf); + } else { + /* (hs): give up if multcount is not set */ + printk("%s: %s Multimode Read failure " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + break; + case TASKFILE_IN: + args.prehandler = NULL; + args.handler = &task_in_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, inbuf); + break; + case TASKFILE_NO_DATA: + args.prehandler = NULL; + args.handler = &task_no_data_intr; + args.posthandler = NULL; + err = ide_raw_taskfile(drive, &args, NULL); + break; + default: + args.prehandler = NULL; + args.handler = NULL; + args.posthandler = NULL; + err = -EFAULT; + goto abort; + } + + memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE); + req_task->in_flags = args.tf_in_flags; + req_task->out_flags = args.tf_out_flags; + + if (copy_to_user((void *)arg, req_task, tasksize)) { + err = -EFAULT; + goto abort; + } + if (taskout) { + int outtotal = tasksize; + if (copy_to_user((void *)arg+outtotal, outbuf, taskout)) { + err = -EFAULT; + goto abort; + } + } + if (taskin) { + int intotal = tasksize + taskout; + if (copy_to_user((void *)arg+intotal, inbuf, taskin)) { + err = -EFAULT; + goto abort; + } + } +abort: + kfree(req_task); + if (outbuf != NULL) + kfree(outbuf); + if (inbuf != NULL) + kfree(inbuf); + return err; +} + +EXPORT_SYMBOL(task_read_24); +EXPORT_SYMBOL(do_rw_taskfile); +EXPORT_SYMBOL(do_taskfile); +// EXPORT_SYMBOL(flagged_taskfile); + +//EXPORT_SYMBOL(ide_end_taskfile); + +EXPORT_SYMBOL(set_multmode_intr); +EXPORT_SYMBOL(set_geometry_intr); +EXPORT_SYMBOL(recal_intr); + +EXPORT_SYMBOL(task_no_data_intr); +EXPORT_SYMBOL(task_in_intr); +EXPORT_SYMBOL(task_mulin_intr); +EXPORT_SYMBOL(pre_task_out_intr); +EXPORT_SYMBOL(task_out_intr); +EXPORT_SYMBOL(task_mulout_intr); + +EXPORT_SYMBOL(ide_init_drive_taskfile); +EXPORT_SYMBOL(ide_wait_taskfile); +EXPORT_SYMBOL(ide_raw_taskfile); +EXPORT_SYMBOL(ide_pre_handler_parser); +EXPORT_SYMBOL(ide_handler_parser); +EXPORT_SYMBOL(ide_cmd_type_parser); +EXPORT_SYMBOL(ide_taskfile_ioctl); + +#ifdef CONFIG_PKT_TASK_IOCTL + +#if 0 +{ + +{ /* start cdrom */ + + struct cdrom_info *info = drive->driver_data; + + if (info->dma) { + if (info->cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (info->cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } + + /* Set up the controller registers. */ + OUT_BYTE (info->dma, IDE_FEATURE_REG); + OUT_BYTE (0, IDE_NSECTOR_REG); + OUT_BYTE (0, IDE_SECTOR_REG); + + OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG); + OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG); + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl, IDE_CONTROL_REG); + + if (info->dma) + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return ide_started; + } else { + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return (*handler) (drive); + } + +} /* end cdrom */ + +{ /* start floppy */ + + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_bcount_reg_t bcount; + int dma_ok = 0; + + floppy->pc=pc; /* Set the current packet command */ + + pc->retries++; + pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->current_position=pc->buffer; + bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all,IDE_SELECT_REG); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +} /* end floppy */ + +{ /* start tape */ + + idetape_tape_t *tape = drive->driver_data; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { + printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n"); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all,IDE_SELECT_REG); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { + ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return idetape_transfer_pc(drive); + } + +} /* end tape */ + +} +#endif + +int pkt_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ +#if 0 + switch(req_task->data_phase) { + case TASKFILE_P_OUT_DMAQ: + case TASKFILE_P_IN_DMAQ: + case TASKFILE_P_OUT_DMA: + case TASKFILE_P_IN_DMA: + case TASKFILE_P_OUT: + case TASKFILE_P_IN: + } +#endif + return -ENOMSG; +} + +EXPORT_SYMBOL(pkt_taskfile_ioctl); + +#endif /* CONFIG_PKT_TASK_IOCTL */ diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c --- a/drivers/ide/ide.c Wed Feb 13 20:03:50 2002 +++ b/drivers/ide/ide.c Wed Feb 13 20:03:50 2002 @@ -149,6 +149,7 @@ #include #include #include +#include #include #include @@ -162,6 +163,16 @@ #include #endif /* CONFIG_KMOD */ +#ifdef CONFIG_IDE_TASKFILE_IO +# define __TASKFILE__IO +#else /* CONFIG_IDE_TASKFILE_IO */ +# undef __TASKFILE__IO +#endif /* CONFIG_IDE_TASKFILE_IO */ + +#ifdef __TASKFILE__IO +#else /* !__TASKFILE__IO */ +#endif /* __TASKFILE__IO */ + /* default maximum number of failures */ #define IDE_DEFAULT_MAX_FAILURES 1 @@ -515,7 +526,8 @@ /* * Needed for PCI irq sharing */ -static inline int drive_is_ready (ide_drive_t *drive) +//static inline +int drive_is_ready (ide_drive_t *drive) { byte stat = 0; if (drive->waiting_for_dma) @@ -809,7 +821,11 @@ */ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */ udelay(10); /* more than enough time */ - OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ + if (drive->quirk_list == 2) { + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear SRST and nIEN */ + } else { + OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ + } udelay(10); /* more than enough time */ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL); @@ -836,6 +852,13 @@ return do_reset1 (drive, 0); } +static inline u32 read_24 (ide_drive_t *drive) +{ + return (IN_BYTE(IDE_HCYL_REG)<<16) | + (IN_BYTE(IDE_LCYL_REG)<<8) | + IN_BYTE(IDE_SECTOR_REG); +} + /* * Clean up after success/failure of an explicit drive cmd */ @@ -848,26 +871,66 @@ rq = HWGROUP(drive)->rq; spin_unlock_irqrestore(&io_request_lock, flags); - if (rq->cmd == IDE_DRIVE_CMD) { - byte *args = (byte *) rq->buffer; - rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); - if (args) { - args[0] = stat; - args[1] = err; - args[2] = IN_BYTE(IDE_NSECTOR_REG); - } - } else if (rq->cmd == IDE_DRIVE_TASK) { - byte *args = (byte *) rq->buffer; - rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); - if (args) { - args[0] = stat; - args[1] = err; - args[2] = IN_BYTE(IDE_NSECTOR_REG); - args[3] = IN_BYTE(IDE_SECTOR_REG); - args[4] = IN_BYTE(IDE_LCYL_REG); - args[5] = IN_BYTE(IDE_HCYL_REG); - args[6] = IN_BYTE(IDE_SELECT_REG); + switch(rq->cmd) { + case IDE_DRIVE_CMD: + { + byte *args = (byte *) rq->buffer; + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + if (args) { + args[0] = stat; + args[1] = err; + args[2] = IN_BYTE(IDE_NSECTOR_REG); + } + break; + } + case IDE_DRIVE_TASK: + { + byte *args = (byte *) rq->buffer; + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + if (args) { + args[0] = stat; + args[1] = err; + args[2] = IN_BYTE(IDE_NSECTOR_REG); + args[3] = IN_BYTE(IDE_SECTOR_REG); + args[4] = IN_BYTE(IDE_LCYL_REG); + args[5] = IN_BYTE(IDE_HCYL_REG); + args[6] = IN_BYTE(IDE_SELECT_REG); + } + break; + } + case IDE_DRIVE_TASKFILE: + { + ide_task_t *args = (ide_task_t *) rq->special; + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + if (args) { + if (args->tf_in_flags.b.data) { + unsigned short data = IN_WORD(IDE_DATA_REG); + args->tfRegister[IDE_DATA_OFFSET] = (data) & 0xFF; + args->hobRegister[IDE_DATA_OFFSET_HOB] = (data >> 8) & 0xFF; + } + args->tfRegister[IDE_ERROR_OFFSET] = err; + args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); + args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); + args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); + args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); + args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); + args->tfRegister[IDE_STATUS_OFFSET] = stat; + + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); + args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); + args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); + args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); + args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); + args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); + } + } + break; } + default: + break; } spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); @@ -917,19 +980,32 @@ if (err & MARK_ERR) printk("AddrMarkNotFound "); printk("}"); if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { - byte cur = IN_BYTE(IDE_SELECT_REG); - if (cur & 0x40) { /* using LBA? */ - printk(", LBAsect=%ld", (unsigned long) - ((cur&0xf)<<24) - |(IN_BYTE(IDE_HCYL_REG)<<16) - |(IN_BYTE(IDE_LCYL_REG)<<8) - | IN_BYTE(IDE_SECTOR_REG)); + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = read_24(drive); + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG); + high = read_24(drive); + + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%lld, high=%d, low=%d", sectors, high, low); } else { - printk(", CHS=%d/%d/%d", - (IN_BYTE(IDE_HCYL_REG)<<8) + - IN_BYTE(IDE_LCYL_REG), - cur & 0xf, - IN_BYTE(IDE_SECTOR_REG)); + byte cur = IN_BYTE(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(IN_BYTE(IDE_HCYL_REG)<<16) + |(IN_BYTE(IDE_LCYL_REG)<<8) + | IN_BYTE(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (IN_BYTE(IDE_HCYL_REG)<<8) + + IN_BYTE(IDE_LCYL_REG), + cur & 0xf, + IN_BYTE(IDE_SECTOR_REG)); + } } if (HWGROUP(drive) && HWGROUP(drive)->rq) printk(", sector=%ld", HWGROUP(drive)->rq->sector); @@ -980,6 +1056,13 @@ ide_end_drive_cmd(drive, stat, err); return ide_stopped; } + if (rq->cmd == IDE_DRIVE_TASKFILE) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); +// ide_end_taskfile(drive, stat, err); + return ide_stopped; + } + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; } else { @@ -1141,62 +1224,125 @@ */ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { - byte *args = rq->buffer; - if (args && rq->cmd == IDE_DRIVE_TASK) { - byte sel; + switch(rq->cmd) { + case IDE_DRIVE_TASKFILE: + { + ide_task_t *args = rq->special; + + if (!(args)) break; + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + { + printk(KERN_INFO "%s: ", drive->name); +// printk("TF.0=x%02x ", args->tfRegister[IDE_DATA_OFFSET]); + printk("TF.1=x%02x ", args->tfRegister[IDE_FEATURE_OFFSET]); + printk("TF.2=x%02x ", args->tfRegister[IDE_NSECTOR_OFFSET]); + printk("TF.3=x%02x ", args->tfRegister[IDE_SECTOR_OFFSET]); + printk("TF.4=x%02x ", args->tfRegister[IDE_LCYL_OFFSET]); + printk("TF.5=x%02x ", args->tfRegister[IDE_HCYL_OFFSET]); + printk("TF.6=x%02x ", args->tfRegister[IDE_SELECT_OFFSET]); + printk("TF.7=x%02x\n", args->tfRegister[IDE_COMMAND_OFFSET]); + printk(KERN_INFO "%s: ", drive->name); +// printk("HTF.0=x%02x ", args->hobRegister[IDE_DATA_OFFSET_HOB]); + printk("HTF.1=x%02x ", args->hobRegister[IDE_FEATURE_OFFSET_HOB]); + printk("HTF.2=x%02x ", args->hobRegister[IDE_NSECTOR_OFFSET_HOB]); + printk("HTF.3=x%02x ", args->hobRegister[IDE_SECTOR_OFFSET_HOB]); + printk("HTF.4=x%02x ", args->hobRegister[IDE_LCYL_OFFSET_HOB]); + printk("HTF.5=x%02x ", args->hobRegister[IDE_HCYL_OFFSET_HOB]); + printk("HTF.6=x%02x ", args->hobRegister[IDE_SELECT_OFFSET_HOB]); + printk("HTF.7=x%02x\n", args->hobRegister[IDE_CONTROL_OFFSET_HOB]); + } +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + +// if (args->tf_out_flags.all == 0) { + do_taskfile(drive, + (struct hd_drive_task_hdr *)&args->tfRegister, + (struct hd_drive_hob_hdr *)&args->hobRegister, + args->handler); +// } else { +// return flagged_taskfile(drive, args); +// } + + if (((args->command_type == IDE_DRIVE_TASK_RAW_WRITE) || + (args->command_type == IDE_DRIVE_TASK_OUT)) && + args->prehandler && args->handler) + return args->prehandler(drive, rq); + return ide_started; + } + case IDE_DRIVE_TASK: + { + byte *args = rq->buffer; + byte sel; + + if (!(args)) break; #ifdef DEBUG - printk("%s: DRIVE_TASK_CMD data=x%02x cmd=0x%02x fr=0x%02x ns=0x%02x sc=0x%02x lcyl=0x%02x hcyl=0x%02x sel=0x%02x\n", - drive->name, - args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7]); + printk("%s: DRIVE_TASK_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("fr=0x%02x ", args[1]); + printk("ns=0x%02x ", args[2]); + printk("sc=0x%02x ", args[3]); + printk("lcyl=0x%02x ", args[4]); + printk("hcyl=0x%02x ", args[5]); + printk("sel=0x%02x\n", args[6]); #endif - OUT_BYTE(args[1], IDE_FEATURE_REG); - OUT_BYTE(args[3], IDE_SECTOR_REG); - OUT_BYTE(args[4], IDE_LCYL_REG); - OUT_BYTE(args[5], IDE_HCYL_REG); - sel = (args[6] & ~0x10); - if (drive->select.b.unit) - sel |= 0x10; - OUT_BYTE(sel, IDE_SELECT_REG); - ide_cmd(drive, args[0], args[2], &drive_cmd_intr); - return ide_started; - } else if (args) { + OUT_BYTE(args[1], IDE_FEATURE_REG); + OUT_BYTE(args[3], IDE_SECTOR_REG); + OUT_BYTE(args[4], IDE_LCYL_REG); + OUT_BYTE(args[5], IDE_HCYL_REG); + sel = (args[6] & ~0x10); + if (drive->select.b.unit) + sel |= 0x10; + OUT_BYTE(sel, IDE_SELECT_REG); + ide_cmd(drive, args[0], args[2], &drive_cmd_intr); + return ide_started; + } + case IDE_DRIVE_CMD: + { + byte *args = rq->buffer; + + if (!(args)) break; #ifdef DEBUG - printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n", - drive->name, args[0], args[1], args[2], args[3]); + printk("%s: DRIVE_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("sc=0x%02x ", args[1]); + printk("fr=0x%02x ", args[2]); + printk("xx=0x%02x\n", args[3]); #endif - if (args[0] == WIN_SMART) { - OUT_BYTE(0x4f, IDE_LCYL_REG); - OUT_BYTE(0xc2, IDE_HCYL_REG); - OUT_BYTE(args[2],IDE_FEATURE_REG); - OUT_BYTE(args[1],IDE_SECTOR_REG); - ide_cmd(drive, args[0], args[3], &drive_cmd_intr); - return ide_started; - } - OUT_BYTE(args[2],IDE_FEATURE_REG); - ide_cmd(drive, args[0], args[1], &drive_cmd_intr); - return ide_started; - } else { - /* - * NULL is actually a valid way of waiting for - * all current requests to be flushed from the queue. - */ + if (args[0] == WIN_SMART) { + OUT_BYTE(0x4f, IDE_LCYL_REG); + OUT_BYTE(0xc2, IDE_HCYL_REG); + OUT_BYTE(args[2],IDE_FEATURE_REG); + OUT_BYTE(args[1],IDE_SECTOR_REG); + ide_cmd(drive, args[0], args[3], &drive_cmd_intr); + return ide_started; + } + OUT_BYTE(args[2],IDE_FEATURE_REG); + ide_cmd(drive, args[0], args[1], &drive_cmd_intr); + return ide_started; + } + default: + break; + } + /* + * NULL is actually a valid way of waiting for + * all current requests to be flushed from the queue. + */ #ifdef DEBUG - printk("%s: DRIVE_CMD (null)\n", drive->name); + printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); - return ide_stopped; - } + ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); + return ide_stopped; } /* * start_request() initiates handling of a new I/O request + * needed to reverse the perverted changes anonymously made back + * 2.3.99-pre6 */ -static ide_startstop_t start_request (ide_drive_t *drive) +static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) { ide_startstop_t startstop; unsigned long block, blockend; - struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head); unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS; ide_hwif_t *hwif = HWIF(drive); @@ -1245,8 +1391,13 @@ return startstop; } if (!drive->special.all) { - if (rq->cmd == IDE_DRIVE_CMD || rq->cmd == IDE_DRIVE_TASK) { - return execute_drive_cmd(drive, rq); + switch(rq->cmd) { + case IDE_DRIVE_CMD: + case IDE_DRIVE_TASK: + case IDE_DRIVE_TASKFILE: + return execute_drive_cmd(drive, rq); + default: + break; } if (drive->driver != NULL) { return (DRIVER(drive)->do_request(drive, rq, block)); @@ -1267,13 +1418,15 @@ { ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned long flags; + struct request *rq; spin_lock_irqsave(&io_request_lock, flags); hwgroup->handler = NULL; del_timer(&hwgroup->timer); + rq = hwgroup->rq; spin_unlock_irqrestore(&io_request_lock, flags); - return start_request(drive); + return start_request(drive, rq); } /* @@ -1370,10 +1523,11 @@ /* --BenH: made non-static as ide-pmac.c uses it to kick the hwgroup back * into life on wakeup from machine sleep. */ -void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq) +void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) { ide_drive_t *drive; ide_hwif_t *hwif; + struct request *rq; ide_startstop_t startstop; ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only: POSSIBLY BROKEN HERE(?) */ @@ -1426,7 +1580,8 @@ if ( drive->queue.plugged ) /* paranoia */ printk("%s: Huh? nuking plugged queue\n", drive->name); - hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head); + + rq = hwgroup->rq = blkdev_entry_next_request(&drive->queue.queue_head); /* * Some systems have trouble with IDE IRQs arriving while * the driver is still setting things up. So, here we disable @@ -1439,7 +1594,7 @@ disable_irq_nosync(hwif->irq); spin_unlock(&io_request_lock); ide__sti(); /* allow other IRQs while we start this request */ - startstop = start_request(drive); + startstop = start_request(drive, rq); spin_lock_irq(&io_request_lock); if (masked_irq && hwif->irq != masked_irq) enable_irq(hwif->irq); @@ -1967,6 +2122,10 @@ (void) request_module("ide-tape"); if (drive->media == ide_floppy) (void) request_module("ide-floppy"); +#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) + if (drive->media == ide_scsi) + (void) request_module("ide-scsi"); +#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ } #endif /* CONFIG_KMOD */ while (drive->busy) @@ -2295,6 +2454,7 @@ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); hwif->irq = hw->irq; hwif->noprobe = 0; + hwif->chipset = hw->chipset; if (!initializing) { ide_probe_module(); @@ -2590,6 +2750,61 @@ return((int) ((!system_bus_speed) ? ide_system_bus_speed() : system_bus_speed )); } +int ide_reinit_drive (ide_drive_t *drive) +{ + switch (drive->media) { +#ifdef CONFIG_BLK_DEV_IDECD + case ide_cdrom: + { + extern int ide_cdrom_reinit(ide_drive_t *drive); + if (ide_cdrom_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDECD */ +#ifdef CONFIG_BLK_DEV_IDEDISK + case ide_disk: + { + extern int idedisk_reinit(ide_drive_t *drive); + if (idedisk_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDEDISK */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + case ide_floppy: + { + extern int idefloppy_reinit(ide_drive_t *drive); + if (idefloppy_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDETAPE + case ide_tape: + { + extern int idetape_reinit(ide_drive_t *drive); + if (idetape_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDESCSI +/* + * { + * extern int idescsi_reinit(ide_drive_t *drive); + * if (idescsi_reinit(drive)) + * return 1; + * break; + * } + */ +#endif /* CONFIG_BLK_DEV_IDESCSI */ + default: + return 1; + } + return 0; +} + static int ide_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -2681,16 +2896,47 @@ drive->nice1 << IDE_NICE_1 | drive->nice2 << IDE_NICE_2, (long *) arg); + +#ifdef CONFIG_IDE_TASK_IOCTL + case HDIO_DRIVE_TASKFILE: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + switch(drive->media) { + case ide_disk: + return ide_taskfile_ioctl(drive, inode, file, cmd, arg); +#ifdef CONFIG_PKT_TASK_IOCTL + case ide_cdrom: + case ide_tape: + case ide_floppy: + return pkt_taskfile_ioctl(drive, inode, file, cmd, arg); +#endif /* CONFIG_PKT_TASK_IOCTL */ + default: + return -ENOMSG; + } +#endif /* CONFIG_IDE_TASK_IOCTL */ + case HDIO_DRIVE_CMD: { byte args[4], *argbuf = args; byte xfer_rate = 0; int argsize = 4; - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; + ide_task_t tfargs; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; if (NULL == (void *) arg) return ide_do_drive_cmd(drive, &rq, ide_wait); if (copy_from_user(args, (void *)arg, 4)) return -EFAULT; + + tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2]; + tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3]; + tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1]; + tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00; + tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0]; + if (args[3]) { argsize = 4 + (SECTOR_WORDS * 4 * args[3]); argbuf = kmalloc(argsize, GFP_KERNEL); @@ -2699,9 +2945,9 @@ memcpy(argbuf, args, 4); } - if (set_transfer(drive, args[0], args[1], args[2])) { + if (set_transfer(drive, &tfargs)) { xfer_rate = args[1]; - if (ide_ata66_check(drive, args[0], args[1], args[2])) + if (ide_ata66_check(drive, &tfargs)) goto abort; } @@ -2761,7 +3007,24 @@ drive->nice1 = (arg >> IDE_NICE_1) & 1; return 0; case HDIO_DRIVE_RESET: + { + unsigned long flags; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + if (!capable(CAP_SYS_ADMIN)) return -EACCES; +#if 1 + spin_lock_irqsave(&io_request_lock, flags); + if (hwgroup->handler != NULL) { + printk("%s: ide_set_handler: handler not null; %p\n", drive->name, hwgroup->handler); + (void) hwgroup->handler(drive); +// hwgroup->handler = NULL; +// hwgroup->expiry = NULL; + hwgroup->timer.expires = jiffies + 0;; + del_timer(&hwgroup->timer); + } + spin_unlock_irqrestore(&io_request_lock, flags); + +#endif (void) ide_do_reset(drive); if (drive->suspend_reset) { /* @@ -2776,7 +3039,7 @@ return ide_revalidate_disk(inode->i_rdev); } return 0; - + } case BLKROSET: case BLKROGET: case BLKFLSBUF: @@ -2799,7 +3062,7 @@ if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (HWIF(drive)->busproc) - HWIF(drive)->busproc(HWIF(drive), arg); + HWIF(drive)->busproc(drive, (int)arg); return 0; default: @@ -3460,6 +3723,16 @@ return ide_unregister_subdriver(drive); } +static int default_standby (ide_drive_t *drive) +{ + return 0; +} + +static int default_flushcache (ide_drive_t *drive) +{ + return 0; +} + static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_end_request(0, HWGROUP(drive)); @@ -3510,7 +3783,7 @@ return ide_stopped; } -static int default_driver_reinit (ide_drive_t *drive) +static int default_reinit (ide_drive_t *drive) { printk(KERN_ERR "%s: does not support hotswap of device class !\n", drive->name); @@ -3522,6 +3795,8 @@ ide_driver_t *d = drive->driver; if (d->cleanup == NULL) d->cleanup = default_cleanup; + if (d->standby == NULL) d->standby = default_standby; + if (d->flushcache == NULL) d->flushcache = default_flushcache; if (d->do_request == NULL) d->do_request = default_do_request; if (d->end_request == NULL) d->end_request = default_end_request; if (d->ioctl == NULL) d->ioctl = default_ioctl; @@ -3531,7 +3806,7 @@ if (d->pre_reset == NULL) d->pre_reset = default_pre_reset; if (d->capacity == NULL) d->capacity = default_capacity; if (d->special == NULL) d->special = default_special; - if (d->driver_reinit == NULL) d->driver_reinit = default_driver_reinit; + if (d->reinit == NULL) d->reinit = default_reinit; } ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n) @@ -3676,6 +3951,7 @@ EXPORT_SYMBOL(ide_output_data); EXPORT_SYMBOL(atapi_input_bytes); EXPORT_SYMBOL(atapi_output_bytes); +EXPORT_SYMBOL(drive_is_ready); EXPORT_SYMBOL(ide_set_handler); EXPORT_SYMBOL(ide_dump_status); EXPORT_SYMBOL(ide_error); @@ -3698,6 +3974,8 @@ EXPORT_SYMBOL(ide_remove_proc_entries); EXPORT_SYMBOL(proc_ide_read_geometry); EXPORT_SYMBOL(create_proc_ide_interfaces); +EXPORT_SYMBOL(recreate_proc_ide_device); +EXPORT_SYMBOL(destroy_proc_ide_device); #endif EXPORT_SYMBOL(ide_add_setting); EXPORT_SYMBOL(ide_remove_setting); @@ -3712,6 +3990,54 @@ EXPORT_SYMBOL(system_bus_clock); +EXPORT_SYMBOL(ide_reinit_drive); + +static int ide_notify_reboot (struct notifier_block *this, unsigned long event, void *x) +{ + ide_hwif_t *hwif; + ide_drive_t *drive; + int i, unit; + + switch (event) { + case SYS_HALT: + case SYS_POWER_OFF: + case SYS_RESTART: + break; + default: + return NOTIFY_DONE; + } + + printk("flushing ide devices: "); + + for (i = 0; i < MAX_HWIFS; i++) { + hwif = &ide_hwifs[i]; + if (!hwif->present) + continue; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &hwif->drives[unit]; + if (!drive->present) + continue; + + /* set the drive to standby */ + printk("%s ", drive->name); + if (event != SYS_RESTART) + if (drive->driver != NULL && DRIVER(drive)->standby(drive)) + continue; + + if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) + continue; + } + } + printk("\n"); + return NOTIFY_DONE; +} + +static struct notifier_block ide_notifier = { + ide_notify_reboot, + NULL, + 5 +}; + /* * This is gets invoked once during initialization, to set *everything* up */ @@ -3739,6 +4065,7 @@ ide_geninit(hwif); } + register_reboot_notifier(&ide_notifier); return 0; } @@ -3771,6 +4098,7 @@ { int index; + unregister_reboot_notifier(&ide_notifier); for (index = 0; index < MAX_HWIFS; ++index) { ide_unregister(index); #if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c --- a/drivers/ide/pdc202xx.c Wed Feb 13 20:03:56 2002 +++ b/drivers/ide/pdc202xx.c Wed Feb 13 20:03:56 2002 @@ -108,7 +108,7 @@ u32 bibma = pci_resource_start(dev, 4); u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0; u16 reg50h = 0, pmask = (1<<10), smask = (1<<11); - u8 hi = 0, lo = 0, invalid_data_set = 0; + u8 hi = 0, lo = 0; /* * at that point bibma+0x2 et bibma+0xa are byte registers @@ -132,11 +132,6 @@ pci_read_config_dword(dev, 0x6c, ®6ch); switch(dev->device) { - case PCI_DEVICE_ID_PROMISE_20268: - case PCI_DEVICE_ID_PROMISE_20268R: - p += sprintf(p, "\n PDC20268 TX2 Chipset.\n"); - invalid_data_set = 1; - break; case PCI_DEVICE_ID_PROMISE_20267: p += sprintf(p, "\n PDC20267 Chipset.\n"); break; @@ -180,45 +175,77 @@ p += sprintf(p, " Mode %s Mode %s\n", (sc1a & 0x01) ? "MASTER" : "PCI ", (sc1b & 0x01) ? "MASTER" : "PCI "); - if (!(invalid_data_set)) - p += sprintf(p, " %s %s\n", - (sc1d & 0x08) ? "Error " : - ((sc1d & 0x05) == 0x05) ? "Not My INTR " : - (sc1d & 0x04) ? "Interrupting" : - (sc1d & 0x02) ? "FIFO Full " : - (sc1d & 0x01) ? "FIFO Empty " : "????????????", - (sc1d & 0x80) ? "Error " : - ((sc1d & 0x50) == 0x50) ? "Not My INTR " : - (sc1d & 0x40) ? "Interrupting" : - (sc1d & 0x20) ? "FIFO Full " : - (sc1d & 0x10) ? "FIFO Empty " : "????????????"); + p += sprintf(p, " %s %s\n", + (sc1d & 0x08) ? "Error " : + ((sc1d & 0x05) == 0x05) ? "Not My INTR " : + (sc1d & 0x04) ? "Interrupting" : + (sc1d & 0x02) ? "FIFO Full " : + (sc1d & 0x01) ? "FIFO Empty " : "????????????", + (sc1d & 0x80) ? "Error " : + ((sc1d & 0x50) == 0x50) ? "Not My INTR " : + (sc1d & 0x40) ? "Interrupting" : + (sc1d & 0x20) ? "FIFO Full " : + (sc1d & 0x10) ? "FIFO Empty " : "????????????"); p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n"); p += sprintf(p, "DMA enabled: %s %s %s %s\n", (c0&0x20)?"yes":"no ",(c0&0x40)?"yes":"no ",(c1&0x20)?"yes":"no ",(c1&0x40)?"yes":"no "); - if (!(invalid_data_set)) - p += sprintf(p, "DMA Mode: %s %s %s %s\n", - pdc202xx_ultra_verbose(reg60h, (reg50h & pmask)), - pdc202xx_ultra_verbose(reg64h, (reg50h & pmask)), - pdc202xx_ultra_verbose(reg68h, (reg50h & smask)), - pdc202xx_ultra_verbose(reg6ch, (reg50h & smask))); - if (!(invalid_data_set)) - p += sprintf(p, "PIO Mode: %s %s %s %s\n", - pdc202xx_pio_verbose(reg60h), - pdc202xx_pio_verbose(reg64h), - pdc202xx_pio_verbose(reg68h), - pdc202xx_pio_verbose(reg6ch)); + p += sprintf(p, "DMA Mode: %s %s %s %s\n", + pdc202xx_ultra_verbose(reg60h, (reg50h & pmask)), + pdc202xx_ultra_verbose(reg64h, (reg50h & pmask)), + pdc202xx_ultra_verbose(reg68h, (reg50h & smask)), + pdc202xx_ultra_verbose(reg6ch, (reg50h & smask))); + p += sprintf(p, "PIO Mode: %s %s %s %s\n", + pdc202xx_pio_verbose(reg60h), + pdc202xx_pio_verbose(reg64h), + pdc202xx_pio_verbose(reg68h), + pdc202xx_pio_verbose(reg6ch)); #if 0 p += sprintf(p, "--------------- Can ATAPI DMA ---------------\n"); #endif - if (invalid_data_set) - p += sprintf(p, "--------------- Cannot Decode HOST ---------------\n"); + return (char *)p; +} + +static char * pdc202xx_info_new (char *buf, struct pci_dev *dev) +{ + char *p = buf; +// u32 bibma = pci_resource_start(dev, 4); + +// u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0; +// u16 reg50h = 0, word88 = 0; +// int udmasel[4]={0,0,0,0}, piosel[4]={0,0,0,0}, i=0, hd=0; + + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + p += sprintf(p, "\n PDC20275 Chipset.\n"); + break; + case PCI_DEVICE_ID_PROMISE_20269: + p += sprintf(p, "\n PDC20269 TX2 Chipset.\n"); + break; + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + p += sprintf(p, "\n PDC20268 TX2 Chipset.\n"); + break; +default: + p += sprintf(p, "\n PDC202XX Chipset.\n"); + break; + } return (char *)p; } static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count) { char *p = buffer; - p = pdc202xx_info(buffer, bmide_dev); + switch(bmide_dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + p = pdc202xx_info_new(buffer, bmide_dev); + break; + default: + p = pdc202xx_info(buffer, bmide_dev); + break; + } return p-buffer; /* => must be less than 4k! */ } #endif /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */ @@ -387,9 +414,6 @@ if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) return -1; - if (dev->device == PCI_DEVICE_ID_PROMISE_20268) - goto skip_register_hell; - pci_read_config_dword(dev, drive_pci, &drive_conf); pci_read_config_byte(dev, (drive_pci), &AP); pci_read_config_byte(dev, (drive_pci)|0x01, &BP); @@ -432,6 +456,7 @@ switch(speed) { #ifdef CONFIG_BLK_DEV_IDEDMA + /* case XFER_UDMA_6: */ case XFER_UDMA_5: case XFER_UDMA_4: TB = 0x20; TC = 0x01; break; /* speed 8 == UDMA mode 4 */ case XFER_UDMA_3: TB = 0x40; TC = 0x02; break; /* speed 7 == UDMA mode 3 */ @@ -477,8 +502,6 @@ decode_registers(REG_D, DP); #endif /* PDC202XX_DECODE_REGISTER_INFO */ -skip_register_hell: - if (!drive->init_speed) drive->init_speed = speed; err = ide_config_drive_speed(drive, speed); @@ -494,6 +517,159 @@ return err; } +static int pdc202xx_new_tune_chipset (ide_drive_t *drive, byte speed) +{ + ide_hwif_t *hwif = HWIF(drive); +#ifdef CONFIG_BLK_DEV_IDEDMA + unsigned long indexreg = (hwif->dma_base + 1); + unsigned long datareg = (hwif->dma_base + 3); +#else + struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long indexreg = high_16 + (hwif->channel ? 0x09 : 0x01); + unsigned long datareg = (indexreg + 2); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + byte thold = 0x10; + byte adj = (drive->dn%2) ? 0x08 : 0x00; + + int err; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed == XFER_UDMA_2) { + OUT_BYTE((thold + adj), indexreg); + OUT_BYTE((IN_BYTE(datareg) & 0x7f), datareg); + } + switch (speed) { + case XFER_UDMA_7: + speed = XFER_UDMA_6; + case XFER_UDMA_6: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x01, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcb, datareg); + break; + case XFER_UDMA_5: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x02, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcb, datareg); + break; + case XFER_UDMA_4: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x03, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_3: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x05, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_2: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x2a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x07, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_1: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x3a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x0a, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xd0, datareg); + break; + case XFER_UDMA_0: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x4a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x0f, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xd5, datareg); + break; + case XFER_MW_DMA_2: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0x69, datareg); + OUT_BYTE((0x0f + adj), indexreg); + OUT_BYTE(0x25, datareg); + break; + case XFER_MW_DMA_1: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0x6b, datareg); + OUT_BYTE((0x0f+ adj), indexreg); + OUT_BYTE(0x27, datareg); + break; + case XFER_MW_DMA_0: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0xdf, datareg); + OUT_BYTE((0x0f + adj), indexreg); + OUT_BYTE(0x5f, datareg); + break; +#else + switch (speed) { +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x23, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x09, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x25, datareg); + break; + case XFER_PIO_3: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x27, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x0d, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x35, datareg); + break; + case XFER_PIO_2: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x23, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x26, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x64, datareg); + break; + case XFER_PIO_1: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x46, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x29, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0xa4, datareg); + break; + case XFER_PIO_0: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0xfb, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x2b, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0xac, datareg); + break; + default: + } + + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + + return err; +} + /* 0 1 2 3 4 5 6 7 8 * 960, 480, 390, 300, 240, 180, 120, 90, 60 * 180, 150, 120, 90, 60 @@ -524,18 +700,75 @@ struct pci_dev *dev = hwif->pci_dev; unsigned long high_16 = pci_resource_start(dev, 4); unsigned long dma_base = hwif->dma_base; + unsigned long indexreg = dma_base + 1; + unsigned long datareg = dma_base + 3; + byte iordy = 0x13; + byte adj = (drive->dn%2) ? 0x08 : 0x00; + byte cable = 0; + byte jumpbit = 0; byte unit = (drive->select.b.unit & 0x01); - unsigned int drive_conf; - byte drive_pci; + byte drive_pci = 0; byte test1, test2, speed = -1; byte AP; unsigned short EP; - byte CLKSPD = IN_BYTE(high_16 + 0x11); - byte udma_33 = ultra ? (inb(high_16 + 0x001f) & 1) : 0; + byte CLKSPD = 0; + byte udma_33 = ultra; +// byte udma_33 = ultra ? (IN_BYTE(high_16 + 0x001f) & 1) : 0; byte udma_66 = ((eighty_ninty_three(drive)) && udma_33) ? 1 : 0; - byte udma_100 = (((dev->device == PCI_DEVICE_ID_PROMISE_20265) || (dev->device == PCI_DEVICE_ID_PROMISE_20267) || (dev->device == PCI_DEVICE_ID_PROMISE_20268)) && udma_66) ? 1 : 0; + byte udma_100 = 0; + byte udma_133 = 0; + byte mask = hwif->channel ? 0x08 : 0x02; + unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10); + + byte ultra_66 = ((id->dma_ultra & 0x0010) || + (id->dma_ultra & 0x0008)) ? 1 : 0; + byte ultra_100 = ((id->dma_ultra & 0x0020) || + (ultra_66)) ? 1 : 0; + byte ultra_133 = ((id->dma_ultra & 0x0040) || + (ultra_100)) ? 1 : 0; + + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + udma_133 = (udma_66) ? 1 : 0; + udma_100 = (udma_66) ? 1 : 0; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20268R: + udma_100 = 1; + udma_66 = 1; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20268: + udma_100 = (udma_66) ? 1 : 0; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + udma_100 = (udma_66) ? 1 : 0; + pci_read_config_word(dev, 0x50, &EP); + cable = (EP & c_mask); + jumpbit = 0; + break; + case PCI_DEVICE_ID_PROMISE_20262: + pci_read_config_word(dev, 0x50, &EP); + cable = (EP & c_mask); + jumpbit = 0; + break; + default: + udma_100 = 0; udma_133 = 0; cable = 1; jumpbit = 0; + break; + } + if (!jumpbit) + CLKSPD = IN_BYTE(high_16 + 0x11); /* * Set the control register to use the 66Mhz system * clock for UDMA 3/4 mode operation. If one drive on @@ -549,47 +782,52 @@ * parameters. */ - byte mask = hwif->channel ? 0x08 : 0x02; - unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10); - byte ultra_66 = ((id->dma_ultra & 0x0010) || - (id->dma_ultra & 0x0008)) ? 1 : 0; - byte ultra_100 = ((id->dma_ultra & 0x0020) || - (id->dma_ultra & 0x0010) || - (id->dma_ultra & 0x0008)) ? 1 : 0; - - if (dev->device == PCI_DEVICE_ID_PROMISE_20268) - goto jump_pci_mucking; - - pci_read_config_word(dev, 0x50, &EP); - - if (((ultra_66) || (ultra_100)) && (EP & c_mask)) { + if (((ultra_66) || (ultra_100) || (ultra_133)) && (cable)) { #ifdef DEBUG printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary" : "Primary"); printk(" Switching to Ultra33 mode.\n"); #endif /* DEBUG */ /* Primary : zero out second bit */ /* Secondary : zero out fourth bit */ - OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + if (!jumpbit) + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + printk("Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary"); + printk("%s reduced to Ultra33 mode.\n", drive->name); + udma_66 = 0; udma_100 = 0; udma_133 = 0; } else { - if ((ultra_66) || (ultra_100)) { + if ((ultra_66) || (ultra_100) || (ultra_133)) { /* * check to make sure drive on same channel * is u66 capable */ if (hwif->drives[!(drive->dn%2)].id) { - if ((hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0020) || + if ((hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0040) || + (hwif->drives[!(drive->dn%2)].id->dma_ultra +& 0x0020) || (hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0010) || (hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0008)) { - OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + if (!jumpbit) + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); } else { - OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + if (!jumpbit) + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); } } else { /* udma4 drive by itself */ - OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + if (!jumpbit) + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); } } } + if (jumpbit) { + if (drive->media != ide_disk) return ide_dma_off_quietly; + if (id->capability & 4) { /* IORDY_EN & PREFETCH_EN */ + OUT_BYTE((iordy + adj), indexreg); + OUT_BYTE((IN_BYTE(datareg)|0x03), datareg); + } + goto jumpbit_is_set; + } + switch(drive->dn) { case 0: drive_pci = 0x60; pci_read_config_dword(dev, drive_pci, &drive_conf); @@ -640,31 +878,33 @@ if (drive->media == ide_disk) /* PREFETCH_EN */ pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN); -jump_pci_mucking: +jumpbit_is_set: - if ((id->dma_ultra & 0x0020) && (udma_100)) speed = XFER_UDMA_5; - else if ((id->dma_ultra & 0x0010) && (udma_66)) speed = XFER_UDMA_4; - else if ((id->dma_ultra & 0x0008) && (udma_66)) speed = XFER_UDMA_3; - else if ((id->dma_ultra & 0x0004) && (udma_33)) speed = XFER_UDMA_2; - else if ((id->dma_ultra & 0x0002) && (udma_33)) speed = XFER_UDMA_1; - else if ((id->dma_ultra & 0x0001) && (udma_33)) speed = XFER_UDMA_0; + if ((id->dma_ultra & 0x0040)&&(udma_133)) speed = XFER_UDMA_6; + else if ((id->dma_ultra & 0x0020)&&(udma_100)) speed = XFER_UDMA_5; + else if ((id->dma_ultra & 0x0010)&&(udma_66)) speed = XFER_UDMA_4; + else if ((id->dma_ultra & 0x0008)&&(udma_66)) speed = XFER_UDMA_3; + else if ((id->dma_ultra & 0x0004)&&(udma_33)) speed = XFER_UDMA_2; + else if ((id->dma_ultra & 0x0002)&&(udma_33)) speed = XFER_UDMA_1; + else if ((id->dma_ultra & 0x0001)&&(udma_33)) speed = XFER_UDMA_0; else if (id->dma_mword & 0x0004) speed = XFER_MW_DMA_2; else if (id->dma_mword & 0x0002) speed = XFER_MW_DMA_1; else if (id->dma_mword & 0x0001) speed = XFER_MW_DMA_0; - else if (id->dma_1word & 0x0004) speed = XFER_SW_DMA_2; - else if (id->dma_1word & 0x0002) speed = XFER_SW_DMA_1; - else if (id->dma_1word & 0x0001) speed = XFER_SW_DMA_0; + else if ((id->dma_1word & 0x0004)&&(!jumpbit)) speed = XFER_SW_DMA_2; + else if ((id->dma_1word & 0x0002)&&(!jumpbit)) speed = XFER_SW_DMA_1; + else if ((id->dma_1word & 0x0001)&&(!jumpbit)) speed = XFER_SW_DMA_0; else { /* restore original pci-config space */ - if (dev->device != PCI_DEVICE_ID_PROMISE_20268) + if (!jumpbit) pci_write_config_dword(dev, drive_pci, drive_conf); return ide_dma_off_quietly; } outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); - (void) pdc202xx_tune_chipset(drive, speed); + (void) hwif->speedproc(drive, speed); - return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + return ((int) ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : ((id->dma_mword >> 8) & 7) ? ide_dma_on : ((id->dma_1word >> 8) & 7) ? ide_dma_on : @@ -685,7 +925,7 @@ } dma_func = ide_dma_off_quietly; if (id->field_valid & 4) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x007F) { /* Force if Capable UltraDMA */ dma_func = config_chipset_for_dma(drive, 1); if ((id->field_valid & 2) && @@ -732,16 +972,65 @@ */ int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { - byte dma_stat = 0, sc1d = 0; - unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); - unsigned long dma_base = HWIF(drive)->dma_base; + byte dma_stat = 0; + byte sc1d = 0; + byte newchip = 0; + byte clock = 0; + byte hardware48hack = 0; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x00); + unsigned long dma_base = hwif->dma_base; + + switch (dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20268: + newchip = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20262: + hardware48hack = 1; + clock = IN_BYTE(high_16 + 0x11); + default: + break; + } switch (func) { case ide_dma_check: return config_drive_xfer_rate(drive); + case ide_dma_begin: + /* Note that this is done *after* the cmd has + * been issued to the drive, as per the BM-IDE spec. + * The Promise Ultra33 doesn't work correctly when + * we do this part before issuing the drive cmd. + */ + if ((drive->addressing) && (hardware48hack)) { + struct request *rq = HWGROUP(drive)->rq; + unsigned long word_count = 0; + + outb(clock|(hwif->channel ? 0x08 : 0x02), high_16 + 0x11); + word_count = (rq->nr_sectors << 8); + word_count = (rq->cmd == READ) ? word_count | 0x05000000 : word_count | 0x06000000; + outl(word_count, atapi_reg); + } + break; + case ide_dma_end: + if ((drive->addressing) && (hardware48hack)) { + outl(0, atapi_reg); /* zero out extra */ + clock = IN_BYTE(high_16 + 0x11); + OUT_BYTE(clock & ~(hwif->channel ? 0x08:0x02), high_16 + 0x11); + } + break; case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ - dma_stat = inb(dma_base+2); - sc1d = inb(high_16 + 0x001d); + dma_stat = IN_BYTE(dma_base+2); + if (newchip) + return (dma_stat & 4) == 4; + + sc1d = IN_BYTE(high_16 + 0x001d); if (HWIF(drive)->channel) { if ((sc1d & 0x50) == 0x50) goto somebody_else; else if ((sc1d & 0x40) == 0x40) @@ -764,61 +1053,113 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ +void pdc202xx_new_reset (ide_drive_t *drive) +{ + OUT_BYTE(0x04,IDE_CONTROL_REG); + mdelay(1000); + OUT_BYTE(0x00,IDE_CONTROL_REG); + mdelay(1000); + printk("PDC202XX: %s channel reset.\n", + HWIF(drive)->channel ? "Secondary" : "Primary"); +} + void pdc202xx_reset (ide_drive_t *drive) { unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); - byte udma_speed_flag = inb(high_16 + 0x001f); + byte udma_speed_flag = IN_BYTE(high_16 + 0x001f); OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); mdelay(100); OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); mdelay(2000); /* 2 seconds ?! */ + printk("PDC202XX: %s channel reset.\n", + HWIF(drive)->channel ? "Secondary" : "Primary"); } -unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ +static int pdc202xx_tristate (ide_drive_t * drive, int state) { - unsigned long high_16 = pci_resource_start(dev, 4); - byte udma_speed_flag = inb(high_16 + 0x001f); - byte primary_mode = inb(high_16 + 0x001a); - byte secondary_mode = inb(high_16 + 0x001b); - - if ((dev->device == PCI_DEVICE_ID_PROMISE_20262) || - (dev->device == PCI_DEVICE_ID_PROMISE_20265) || - (dev->device == PCI_DEVICE_ID_PROMISE_20267)) { - /* - * software reset - this is required because the bios - * will set UDMA timing on if the hdd supports it. The - * user may want to turn udma off. A bug in the pdc20262 - * is that it cannot handle a downgrade in timing from UDMA - * to DMA. Disk accesses after issuing a set feature command - * will result in errors. A software reset leaves the timing - * registers intact, but resets the drives. - */ - - OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); - mdelay(100); - OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); - mdelay(2000); /* 2 seconds ?! */ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4); + byte sc1f = inb(high_16 + 0x001f); + + if (!hwif) + return -EINVAL; + +// hwif->bus_state = state; + + if (state) { + outb(sc1f | 0x08, high_16 + 0x001f); + } else { + outb(sc1f & ~0x08, high_16 + 0x001f); } +#endif + return 0; +} + +unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) +{ + unsigned long high_16 = pci_resource_start(dev, 4); + byte udma_speed_flag = IN_BYTE(high_16 + 0x001f); + byte primary_mode = IN_BYTE(high_16 + 0x001a); + byte secondary_mode = IN_BYTE(high_16 + 0x001b); + byte newchip = 0; if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); } - if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) { - byte irq = 0, irq2 = 0; - pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); - pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */ - if ((irq != irq2) && - (dev->device != PCI_DEVICE_ID_PROMISE_20265) && - (dev->device != PCI_DEVICE_ID_PROMISE_20267) && - (dev->device != PCI_DEVICE_ID_PROMISE_20268)) { - pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ - printk("%s: pci-config space interrupt mirror fixed.\n", name); - } + switch (dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20268: + newchip = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); + mdelay(100); + OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); + mdelay(2000); /* 2 seconds ?! */ + break; + case PCI_DEVICE_ID_PROMISE_20262: + /* + * software reset - this is required because the bios + * will set UDMA timing on if the hdd supports it. The + * user may want to turn udma off. A bug in the pdc20262 + * is that it cannot handle a downgrade in timing from + * UDMA to DMA. Disk accesses after issuing a set + * feature command will result in errors. A software + * reset leaves the timing registers intact, + * but resets the drives. + */ + OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); + mdelay(100); + OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); + mdelay(2000); /* 2 seconds ?! */ + default: + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) { + byte irq = 0, irq2 = 0; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */ + if (irq != irq2) { + pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ + printk("%s: pci-config space interrupt mirror fixed.\n", name); + } + } + break; } + if (newchip) + goto fttk_tx_series; + printk("%s: (U)DMA Burst Bit %sABLED " \ "Primary %s Mode " \ "Secondary %s Mode.\n", @@ -830,8 +1171,8 @@ #ifdef CONFIG_PDC202XX_BURST if (!(udma_speed_flag & 1)) { printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1)); - outb(udma_speed_flag|1, high_16 + 0x001f); - printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA"); + OUT_BYTE(udma_speed_flag|1, high_16 + 0x001f); + printk("%sCTIVE\n", (IN_BYTE(high_16 + 0x001f) & 1) ? "A" : "INA"); } #endif /* CONFIG_PDC202XX_BURST */ @@ -839,18 +1180,20 @@ if (!(primary_mode & 1)) { printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ", name, primary_mode, (primary_mode|1)); - outb(primary_mode|1, high_16 + 0x001a); - printk("%s\n", (inb(high_16 + 0x001a) & 1) ? "MASTER" : "PCI"); + OUT_BYTE(primary_mode|1, high_16 + 0x001a); + printk("%s\n", (IN_BYTE(high_16 + 0x001a) & 1) ? "MASTER" : "PCI"); } if (!(secondary_mode & 1)) { printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ", name, secondary_mode, (secondary_mode|1)); - outb(secondary_mode|1, high_16 + 0x001b); - printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI"); + OUT_BYTE(secondary_mode|1, high_16 + 0x001b); + printk("%s\n", (IN_BYTE(high_16 + 0x001b) & 1) ? "MASTER" : "PCI"); } #endif /* CONFIG_PDC202XX_MASTER */ +fttk_tx_series: + #if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) if (!pdc202xx_proc) { pdc202xx_proc = 1; @@ -866,20 +1209,41 @@ unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10); unsigned short CIS; - pci_read_config_word(hwif->pci_dev, 0x50, &CIS); - return ((CIS & mask) ? 0 : 1); + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + return (!(IN_BYTE((hwif->dma_base + 3)) & 0x04)); + default: + pci_read_config_word(hwif->pci_dev, 0x50, &CIS); + return (!(CIS & mask)); + } } void __init ide_init_pdc202xx (ide_hwif_t *hwif) { - hwif->tuneproc = &pdc202xx_tune_drive; - hwif->speedproc = &pdc202xx_tune_chipset; - hwif->quirkproc = &pdc202xx_quirkproc; - - if ((hwif->pci_dev->device == PCI_DEVICE_ID_PROMISE_20262) || - (hwif->pci_dev->device == PCI_DEVICE_ID_PROMISE_20265) || - (hwif->pci_dev->device == PCI_DEVICE_ID_PROMISE_20267)) { - hwif->resetproc = &pdc202xx_reset; + hwif->tuneproc = &pdc202xx_tune_drive; + hwif->quirkproc = &pdc202xx_quirkproc; + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + hwif->speedproc = &pdc202xx_new_tune_chipset; + hwif->resetproc = &pdc202xx_new_reset; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20262: + hwif->busproc = &pdc202xx_tristate; + hwif->resetproc = &pdc202xx_reset; + case PCI_DEVICE_ID_PROMISE_20246: + hwif->speedproc = &pdc202xx_tune_chipset; + default: + break; } #undef CONFIG_PDC202XX_32_UNMASK diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c --- a/drivers/ide/pdc4030.c Wed Feb 13 20:03:40 2002 +++ b/drivers/ide/pdc4030.c Wed Feb 13 20:03:40 2002 @@ -89,6 +89,12 @@ #include "pdc4030.h" +#ifdef CONFIG_IDE_TASKFILE_IO +# define __TASKFILE__IO +#else /* CONFIG_IDE_TASKFILE_IO */ +# undef __TASKFILE__IO +#endif /* CONFIG_IDE_TASKFILE_IO */ + /* * promise_selectproc() is invoked by ide.c * in preparation for access to the specified drive. @@ -298,8 +304,6 @@ } } - - /* * promise_read_intr() is the handler for disk read/multread interrupts */ @@ -495,11 +499,15 @@ */ ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq) { + ide_startstop_t startstop; unsigned long timeout; byte stat; - if (rq->cmd == READ) { - OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); + switch(rq->cmd) { + case READ: +#ifndef __TASKFILE__IO + OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); +#endif /* * The card's behaviour is odd at this point. If the data is * available, DRQ will be true, and no interrupt will be @@ -510,44 +518,61 @@ * If neither of these is the case, we wait for up to 50ms (badly I'm * afraid!) until one of them is. */ - timeout = jiffies + HZ/20; /* 50ms wait */ - do { - stat=GET_STAT(); - if (stat & DRQ_STAT) { - udelay(1); - return promise_read_intr(drive); - } - if (IN_BYTE(IDE_SELECT_REG) & 0x01) { + timeout = jiffies + HZ/20; /* 50ms wait */ + do { + stat=GET_STAT(); + if (stat & DRQ_STAT) { + udelay(1); + return promise_read_intr(drive); + } + if (IN_BYTE(IDE_SELECT_REG) & 0x01) { #ifdef DEBUG_READ - printk(KERN_DEBUG "%s: read: waiting for " - "interrupt\n", drive->name); + printk(KERN_DEBUG "%s: read: waiting for interrupt\n", drive->name); #endif - ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL); - return ide_started; - } - udelay(1); - } while (time_before(jiffies, timeout)); - - printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n", - drive->name); - return ide_stopped; - } else if (rq->cmd == WRITE) { - ide_startstop_t startstop; - OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { - printk(KERN_ERR "%s: no DRQ after issuing " - "PROMISE_WRITE\n", drive->name); - return startstop; - } - if (!drive->unmask) - __cli(); /* local CPU only */ - HWGROUP(drive)->wrq = *rq; /* scratchpad */ - return promise_write(drive); + ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL); + return ide_started; + } + udelay(1); + } while (time_before(jiffies, timeout)); - } else { - printk("KERN_WARNING %s: bad command: %d\n", - drive->name, rq->cmd); - ide_end_request(0, HWGROUP(drive)); - return ide_stopped; + printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n", drive->name); + return ide_stopped; + case WRITE: +#ifndef __TASKFILE__IO + OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); +#endif + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); + return startstop; + } + if (!drive->unmask) + __cli(); /* local CPU only */ + HWGROUP(drive)->wrq = *rq; /* scratchpad */ + return promise_write(drive); + default: + printk("KERN_WARNING %s: bad command: %d\n", drive->name, rq->cmd); + ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } } + +#ifdef __TASKFILE__IO + +ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + struct hd_drive_task_hdr taskfile; + + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + + taskfile.sector_count = rq->nr_sectors; + taskfile.sector_number = block; + taskfile.low_cylinder = (block>>=8); + taskfile.high_cylinder = (block>>=8); + taskfile.device_head = ((block>>8)&0x0f)|drive->select.all; + taskfile.command = (rq->cmd==READ)?PROMISE_READ:PROMISE_WRITE; + + do_taskfile(drive, &taskfile, NULL, NULL); + return do_pdc4030_io(drive, rq); +} +#endif + diff -Nru a/drivers/ide/pdcadma.c b/drivers/ide/pdcadma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pdcadma.c Wed Feb 13 20:04:00 2002 @@ -0,0 +1,109 @@ +/* + * linux/drivers/ide/pdcadma.c Version 0.01 June 21, 2001 + * + * Copyright (C) 1999-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#undef DISPLAY_PDCADMA_TIMINGS + +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int pdcadma_get_info(char *, char **, off_t, int); +extern int (*pdcadma_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +extern char *ide_media_verbose(ide_drive_t *); +static struct pci_dev *bmide_dev; + +static int pdcadma_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + + p += sprintf(p, "\n PDC ADMA %04X Chipset.\n", bmide_dev->device); + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "PIO\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte pdcadma_proc = 0; + +extern char *ide_xfer_verbose (byte xfer_rate); + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * pdcadma_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ + +int pdcadma_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + func = ide_dma_off_quietly; + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_pdcadma (struct pci_dev *dev, const char *name) +{ +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) + if (!pdcadma_proc) { + pdcadma_proc = 1; + bmide_dev = dev; + pdcadma_display_info = &pdcadma_get_info; + } +#endif /* DISPLAY_PDCADMA_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +unsigned int __init ata66_pdcadma (ide_hwif_t *hwif) +{ + return 1; +} + +void __init ide_init_pdcadma (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->dma_base = 0; + +// hwif->tuneproc = &pdcadma_tune_drive; +// hwif->speedproc = &pdcadma_tune_chipset; + +// if (hwif->dma_base) { +// hwif->dmaproc = &pdcadma_dmaproc; +// hwif->autodma = 1; +// } +} + +void __init ide_dmacapable_pdcadma (ide_hwif_t *hwif, unsigned long dmabase) +{ +// ide_setup_dma(hwif, dmabase, 8); +} + diff -Nru a/drivers/ide/piix.c b/drivers/ide/piix.c --- a/drivers/ide/piix.c Wed Feb 13 20:03:49 2002 +++ b/drivers/ide/piix.c Wed Feb 13 20:03:49 2002 @@ -425,7 +425,7 @@ } dma_func = ide_dma_off_quietly; if (id->field_valid & 4) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x003F) { /* Force if Capable UltraDMA */ dma_func = piix_config_drive_for_dma(drive); if ((id->field_valid & 2) && diff -Nru a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c --- a/drivers/ide/qd65xx.c Wed Feb 13 20:03:42 2002 +++ b/drivers/ide/qd65xx.c Wed Feb 13 20:03:42 2002 @@ -1,14 +1,15 @@ /* - * linux/drivers/ide/qd65xx.c Version 0.06 Aug 3, 2000 + * linux/drivers/ide/qd65xx.c Version 0.07 Sep 30, 2001 * - * Copyright (C) 1996-2000 Linus Torvalds & author (see below) + * Copyright (C) 1996-2001 Linus Torvalds & author (see below) */ /* * Version 0.03 Cleaned auto-tune, added probe * Version 0.04 Added second channel tuning * Version 0.05 Enhanced tuning ; added qd6500 support - * Version 0.06 added dos driver's list + * Version 0.06 Added dos driver's list + * Version 0.07 Second channel bug fix * * QDI QD6500/QD6580 EIDE controller fast support * @@ -67,6 +68,7 @@ * qd6500: 1100 * qd6580: either 1010 or 0101 * + * * base+0x02: Timer2 (qd6580 only) * * @@ -137,12 +139,12 @@ { byte active_cycle,recovery_cycle; - if (system_bus_clock()<=33) { - active_cycle = 9 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 9); - recovery_cycle = 15 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 0, 15); + if (ide_system_bus_speed()<=33) { + active_cycle = 9 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 9); + recovery_cycle = 15 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 0, 15); } else { - active_cycle = 8 - IDE_IN(active_time * system_bus_clock() / 1000 + 1, 1, 8); - recovery_cycle = 18 - IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 3, 18); + active_cycle = 8 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 1, 8); + recovery_cycle = 18 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 3, 18); } return((recovery_cycle<<4) | 0x08 | active_cycle); @@ -156,8 +158,8 @@ static byte qd6580_compute_timing (int active_time, int recovery_time) { - byte active_cycle = 17-IDE_IN(active_time * system_bus_clock() / 1000 + 1, 2, 17); - byte recovery_cycle = 15-IDE_IN(recovery_time * system_bus_clock() / 1000 + 1, 2, 15); + byte active_cycle = 17-IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 17); + byte recovery_cycle = 15-IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 2, 15); return((recovery_cycle<<4) | active_cycle); } @@ -427,7 +429,8 @@ ide_hwifs[i].tuneproc = &qd6580_tune_drive; for (j=0;j<2;j++) { - ide_hwifs[i].drives[j].drive_data = QD6580_DEF_DATA; + ide_hwifs[i].drives[j].drive_data = + i?QD6580_DEF_DATA2:QD6580_DEF_DATA; ide_hwifs[i].drives[j].io_32bit = 1; } } diff -Nru a/drivers/ide/qd65xx.h b/drivers/ide/qd65xx.h --- a/drivers/ide/qd65xx.h Wed Feb 13 20:03:42 2002 +++ b/drivers/ide/qd65xx.h Wed Feb 13 20:03:42 2002 @@ -29,7 +29,7 @@ #define QD_CONTR_SEC_DISABLED 0x01 -#define QD_ID3 (config & QD_CONFIG_ID3) +#define QD_ID3 ((config & QD_CONFIG_ID3)!=0) #define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff) #define QD_CONTROL(hwif) (((hwif)->config_data & 0xff00) >> 8) @@ -39,6 +39,7 @@ #define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08)) #define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) +#define QD6580_DEF_DATA2 ((QD_TIM2_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) #define QD_DEF_CONTR (0x40 | ((control & 0x02) ? 0x9f : 0x1f)) #define QD_TESTVAL 0x19 /* safe value */ diff -Nru a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c --- a/drivers/ide/serverworks.c Wed Feb 13 20:03:30 2002 +++ b/drivers/ide/serverworks.c Wed Feb 13 20:03:30 2002 @@ -1,16 +1,26 @@ /* - * linux/drivers/ide/serverworks.c Version 0.2 17 Oct 2000 + * linux/drivers/ide/serverworks.c Version 0.3 26 Oct 2001 * - * Copyright (C) 2000 Cobalt Networks, Inc. - * May be copied or modified under the terms of the GNU General Public License + * May be copied or modified under the terms of the GNU General Public License * - * interface borrowed from alim15x3.c: - * Copyright (C) 1998-2000 Michel Aubry, Maintainer - * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer + * Copyright (C) 1998-2000 Michel Aubry + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz + * Copyright (C) 1998-2000 Andre Hedrick + * Portions copyright (c) 2001 Sun Microsystems * - * Copyright (C) 1998-2000 Andre Hedrick * - * IDE support for the ServerWorks OSB4 IDE chipset + * RCC/ServerWorks IDE driver for Linux + * + * OSB4: `Open South Bridge' IDE Interface (fn 1) + * supports UDMA mode 2 (33 MB/s) + * + * CSB5: `Champion South Bridge' IDE Interface (fn 1) + * all revisions support UDMA mode 4 (66 MB/s) + * revision A2.0 and up support UDMA mode 5 (100 MB/s) + * + * *** The CSB5 does not provide ANY register *** + * *** to detect 80-conductor cable presence. *** + * * * here's the default lspci: * @@ -83,15 +93,15 @@ #include "ide_modes.h" -#define SVWKS_DEBUG_DRIVE_INFO 0 - -#define DISPLAY_SVWKS_TIMINGS +#define DISPLAY_SVWKS_TIMINGS 1 +#undef SVWKS_DEBUG_DRIVE_INFO #if defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) #include #include static struct pci_dev *bmide_dev; +static byte svwks_revision = 0; static int svwks_get_info(char *, char **, off_t, int); extern int (*svwks_display_info)(char *, char **, off_t, int); /* ide-proc.c */ @@ -103,7 +113,7 @@ u32 bibma = pci_resource_start(bmide_dev, 4); u32 reg40, reg44; u16 reg48, reg56; - u8 c0 = 0, c1 = 0, reg54; + u8 reg54, c0=0, c1=0; pci_read_config_dword(bmide_dev, 0x40, ®40); pci_read_config_dword(bmide_dev, 0x44, ®44); @@ -120,20 +130,23 @@ switch(bmide_dev->device) { case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: - p += sprintf(p, "\n ServerWorks CSB5 Chipset.\n"); + p += sprintf(p, "\n " + "ServerWorks CSB5 Chipset (rev %02x)\n", + svwks_revision); break; - case PCI_DEVICE_ID_SERVERWORKS_OSB4: - p += sprintf(p, "\n ServerWorks OSB4 Chipset.\n"); + case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: + p += sprintf(p, "\n " + "ServerWorks OSB4 Chipset (rev %02x)\n", + svwks_revision); break; default: - p += sprintf(p, "\n ServerWorks 0x%04x Chipset.\n", bmide_dev->device); + p += sprintf(p, "\n " + "ServerWorks %04x Chipset (rev %02x)\n", + bmide_dev->device, svwks_revision); break; } p += sprintf(p, "------------------------------- General Status ---------------------------------\n"); -#if 0 - p += sprintf(p, " : %s\n", "str"); -#endif p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); p += sprintf(p, " %sabled %sabled\n", (c0&0x80) ? "dis" : " en", @@ -191,11 +204,7 @@ ((reg44&0x00210000)==0x00210000)?"1": ((reg44&0x00770000)==0x00770000)?"0": ((reg44&0x00FF0000)==0x00FF0000)?"X":"?"); -#if 0 - if (bmide_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) - p += sprintf(p, "PIO enabled: %s %s %s %s\n", - if (bmide_dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4) -#endif + p += sprintf(p, "PIO enabled: %s %s %s %s\n", ((reg40&0x00002000)==0x00002000)?"4": ((reg40&0x00002200)==0x00002200)?"3": @@ -221,7 +230,7 @@ } #endif /* defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) */ -static byte svwks_revision = 0; +#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ byte svwks_proc = 0; @@ -292,6 +301,7 @@ pio_timing |= pio_modes[speed - XFER_PIO_0]; csb5_pio |= ((speed - XFER_PIO_0) << (4*drive->dn)); break; + #ifdef CONFIG_BLK_DEV_IDEDMA case XFER_MW_DMA_2: case XFER_MW_DMA_1: @@ -307,9 +317,9 @@ case XFER_UDMA_2: case XFER_UDMA_1: case XFER_UDMA_0: - pio_timing |= pio_modes[pio]; - csb5_pio |= (pio << (4*drive->dn)); - dma_timing |= dma_modes[2]; + pio_timing |= pio_modes[pio]; + csb5_pio |= (pio << (4*drive->dn)); + dma_timing |= dma_modes[2]; ultra_timing |= ((udma_modes[speed - XFER_UDMA_0]) << (4*unit)); ultra_enable |= (0x01 << drive->dn); #endif @@ -322,9 +332,9 @@ drive->name, ultra_timing, dma_timing, pio_timing); #endif -#if OSB4_DEBUG_DRIVE_INFO +#if SVWKS_DEBUG_DRIVE_INFO printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); -#endif /* OSB4_DEBUG_DRIVE_INFO */ +#endif /* SVWKS_DEBUG_DRIVE_INFO */ if (!drive->init_speed) drive->init_speed = speed; @@ -338,11 +348,10 @@ pci_write_config_byte(dev, drive_pci3, ultra_timing); pci_write_config_byte(dev, 0x54, ultra_enable); - if (speed > XFER_PIO_4) { + if (speed > XFER_PIO_4) outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); - } else { + else outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); - } #endif /* CONFIG_BLK_DEV_IDEDMA */ err = ide_config_drive_speed(drive, speed); @@ -354,25 +363,24 @@ { unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90}; unsigned short xfer_pio = drive->id->eide_pio_modes; - byte timing, speed, pio; + byte timing, speed, pio; pio = ide_get_best_pio_mode(drive, 255, 5, NULL); if (xfer_pio> 4) xfer_pio = 0; - if (drive->id->eide_pio_iordy > 0) { + if (drive->id->eide_pio_iordy > 0) for (xfer_pio = 5; xfer_pio>0 && drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio]; xfer_pio--); - } else { + else xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 : (drive->id->eide_pio_modes & 2) ? 0x04 : (drive->id->eide_pio_modes & 1) ? 0x03 : (drive->id->tPIO & 2) ? 0x02 : (drive->id->tPIO & 1) ? 0x01 : xfer_pio; - } timing = (xfer_pio >= pio) ? xfer_pio : pio; @@ -407,12 +415,10 @@ { struct hd_driveid *id = drive->id; struct pci_dev *dev = HWIF(drive)->pci_dev; - byte udma_66 = eighty_ninty_three(drive); - byte speed; - - int ultra66 = (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ? 1 : 0; - /* need specs to figure out if osb4 is capable of ata/66/100 */ - int ultra100 = (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ? 1 : 0; + byte udma_66 = eighty_ninty_three(drive); + int ultra66 = (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ? 1 : 0; + int ultra100 = (ultra66 && svwks_revision >= SVWKS_CSB5_REVISION_NEW) ? 1 : 0; + byte speed; if ((id->dma_ultra & 0x0020) && (udma_66) && (ultra100)) { speed = XFER_UDMA_5; @@ -458,7 +464,7 @@ } dma_func = ide_dma_off_quietly; if (id->field_valid & 4) { - if (id->dma_ultra & 0x002F) { + if (id->dma_ultra & 0x003F) { /* Force if Capable UltraDMA */ dma_func = config_chipset_for_dma(drive); if ((id->field_valid & 2) && @@ -499,7 +505,41 @@ switch (func) { case ide_dma_check: return config_drive_xfer_rate(drive); - default : + case ide_dma_end: + { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + + if(inb(dma_base+0x02)&1) + { +#if 0 + int i; + printk(KERN_ERR "Curious - OSB4 thinks the DMA is still running.\n"); + for(i=0;i<10;i++) + { + if(!(inb(dma_base+0x02)&1)) + { + printk(KERN_ERR "OSB4 now finished.\n"); + break; + } + udelay(5); + } +#endif + printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n"); + printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n"); + printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n"); +#if 0 + /* Panic might sys_sync -> death by corrupt disk */ + panic("OSB4: continuing might cause disk corruption.\n"); +#else + printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n"); + while(1) + cpu_relax(); +#endif + } + /* and drop through */ + } + default: break; } /* Other cases are done by generic IDE-DMA code. */ @@ -509,30 +549,43 @@ unsigned int __init pci_init_svwks (struct pci_dev *dev, const char *name) { - unsigned int reg64; + unsigned int reg; + byte btr; + /* save revision id to determine DMA capability */ pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision); - if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { - isa_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); + /* force Master Latency Timer value to 64 PCICLKs */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); - pci_read_config_dword(isa_dev, 0x64, ®64); -#ifdef DEBUG - printk("%s: reg64 == 0x%08x\n", name, reg64); -#endif + /* OSB4 : South Bridge and IDE */ + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + isa_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); + if (isa_dev) { + pci_read_config_dword(isa_dev, 0x64, ®); + reg &= ~0x00002000; /* disable 600ns interrupt mask */ + reg |= 0x00004000; /* enable UDMA/33 support */ + pci_write_config_dword(isa_dev, 0x64, reg); + } + } -// reg64 &= ~0x0000A000; -//#ifdef CONFIG_SMP -// reg64 |= 0x00008000; -//#endif - /* Assume the APIC was set up properly by the BIOS for now . If it - wasnt we need to do a fix up _way_ earlier. Bits 15,10,3 control - APIC enable, routing and decode */ - - reg64 &= ~0x00002000; - pci_write_config_dword(isa_dev, 0x64, reg64); + /* setup CSB5 : South Bridge and IDE */ + else if (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) { + /* setup the UDMA Control register + * + * 1. clear bit 6 to enable DMA + * 2. enable DMA modes with bits 0-1 + * 00 : legacy + * 01 : udma2 + * 10 : udma2/udma4 + * 11 : udma2/udma4/udma5 + */ + pci_read_config_byte(dev, 0x5A, &btr); + btr &= ~0x40; + btr |= (svwks_revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; + pci_write_config_byte(dev, 0x5A, btr); } - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); #if defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) if (!svwks_proc) { @@ -551,26 +604,46 @@ * Bit 14 clear = primary IDE channel does not have 80-pin cable. * Bit 14 set = primary IDE channel has 80-pin cable. */ - static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) { - struct pci_dev *dev = hwif->pci_dev; - if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && - dev->vendor == PCI_VENDOR_ID_SERVERWORKS && - dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) return ((1 << (hwif->channel + 14)) & dev->subsystem_device) ? 1 : 0; - return 0; +} +/* Sun Cobalt Alpine hardware avoids the 80-pin cable + * detect issue by attaching the drives directly to the board. + * This check follows the Dell precedent (how scary is that?!) + * + * WARNING: this only works on Alpine hardware! + */ +static unsigned int __init ata66_svwks_cobalt (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + return 0; } unsigned int __init ata66_svwks (ide_hwif_t *hwif) { - struct pci_dev *dev = hwif->pci_dev; + struct pci_dev *dev = hwif->pci_dev; + + /* Dell PowerEdge */ if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL) return ata66_svwks_dell (hwif); - + + /* Cobalt Alpine */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN) + return ata66_svwks_cobalt (hwif); + return 0; } @@ -586,9 +659,7 @@ hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->autodma = 0; - return; #else /* CONFIG_BLK_DEV_IDEDMA */ - if (hwif->dma_base) { if (!noautodma) hwif->autodma = 1; diff -Nru a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c --- a/drivers/ide/sis5513.c Wed Feb 13 20:03:49 2002 +++ b/drivers/ide/sis5513.c Wed Feb 13 20:03:49 2002 @@ -1,11 +1,28 @@ /* - * linux/drivers/ide/sis5513.c Version 0.11 June 9, 2000 + * linux/drivers/ide/sis5513.c Version 0.13 January 23, 2002 * * Copyright (C) 1999-2000 Andre Hedrick + * Copyright (C) 2002 Lionel Bouton , Maintainer * May be copied or modified under the terms of the GNU General Public License * * Thanks to SIS Taiwan for direct support and hardware. - * Tested and designed on the SiS620/5513 chipset. + * Thanks to Daniela Engert for ATA100 support advice. + * Original tests and design on the SiS620/5513 chipset. + * ATA100 tests and design on the SiS735/5513 chipset. + * ATA16/33 design from specs + */ + +/* + * TODO: + * - Are there pre-ATA_16 SiS chips ? -> tune init code for them + * or remove ATA_00 defines + * - Get ridden of SisHostChipInfo[] completness dependancy. + * - Get ATA-133 datasheets, implement ATA-133 init code. + * - Check latency timer init correctness. + * - More checks in the config registers (force values instead of + * relying on the BIOS setting them correctly). + * - Further optimisations ? + * . for example ATA66+ regs 0x48 & 0x4A */ #include @@ -28,88 +45,181 @@ #include "ide_modes.h" +#define DEBUG +/* if BROKEN_LEVEL is defined it limits the DMA mode + at boot time to its value */ +// #define BROKEN_LEVEL XFER_SW_DMA_0 #define DISPLAY_SIS_TIMINGS -#define SIS5513_DEBUG_DRIVE_INFO 0 -static struct pci_dev *host_dev = NULL; +/* Miscellaneaous flags */ +#define SIS5513_LATENCY 0x01 +/* ATA transfer mode capabilities */ +#define ATA_00 0x00 +#define ATA_16 0x01 +#define ATA_33 0x02 +#define ATA_66 0x03 +#define ATA_100 0x04 +#define ATA_133 0x05 + +static unsigned char dma_capability = 0x00; + + +/* + * Debug code: following IDE config registers' changes + */ +#ifdef DEBUG +/* Copy of IDE Config registers 0x00 -> 0x58 + Fewer might be used depending on the actual chipset */ +static unsigned char ide_regs_copy[] = { + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0 +}; + +static byte sis5513_max_config_register(void) { + switch(dma_capability) { + case ATA_00: + case ATA_16: return 0x4f; + case ATA_33: return 0x52; + case ATA_66: + case ATA_100: + case ATA_133: + default: return 0x57; + } +} + +/* Read config registers, print differences from previous read */ +static void sis5513_load_verify_registers(struct pci_dev* dev, char* info) { + int i; + byte reg_val; + byte changed=0; + byte max = sis5513_max_config_register(); + + printk("SIS5513: %s, changed registers:\n", info); + for(i=0; i<=max; i++) { + pci_read_config_byte(dev, i, ®_val); + if (reg_val != ide_regs_copy[i]) { + printk("%0#x: %0#x -> %0#x\n", + i, ide_regs_copy[i], reg_val); + ide_regs_copy[i]=reg_val; + changed=1; + } + } + + if (!changed) { + printk("none\n"); + } +} + +/* Load config registers, no printing */ +static void sis5513_load_registers(struct pci_dev* dev) { + int i; + byte max = sis5513_max_config_register(); + + for(i=0; i<=max; i++) { + pci_read_config_byte(dev, i, &(ide_regs_copy[i])); + } +} + +/* Print a register */ +static void sis5513_print_register(int reg) { + printk(" %0#x:%0#x", reg, ide_regs_copy[reg]); +} + +/* Print valuable registers (for ATA100) */ +static void sis5513_print_registers(struct pci_dev* dev, char* marker) { + int i; + byte max = sis5513_max_config_register(); + + sis5513_load_registers(dev); + printk("SIS5513 %s\n", marker); + printk("SIS5513 dump:"); + for(i=0x00; i<0x40; i++) { + if ((i % 0x10)==0) printk("\n "); + sis5513_print_register(i); + } + for(; i<49; i++) { + sis5513_print_register(i); + } + printk("\n "); + + for(; i<=max; i++) { + sis5513_print_register(i); + } + printk("\n"); +} +#endif -#define SIS5513_FLAG_ATA_00 0x00000000 -#define SIS5513_FLAG_ATA_16 0x00000001 -#define SIS5513_FLAG_ATA_33 0x00000002 -#define SIS5513_FLAG_ATA_66 0x00000004 -#define SIS5513_FLAG_LATENCY 0x00000010 +/* + * Devices supported + */ static const struct { const char *name; unsigned short host_id; - unsigned int flags; + unsigned char dma_capability; + unsigned char flags; } SiSHostChipInfo[] = { - { "SiS530", PCI_DEVICE_ID_SI_530, SIS5513_FLAG_ATA_66, }, - { "SiS540", PCI_DEVICE_ID_SI_540, SIS5513_FLAG_ATA_66, }, - { "SiS620", PCI_DEVICE_ID_SI_620, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS630", PCI_DEVICE_ID_SI_630, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS635", PCI_DEVICE_ID_SI_635, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS640", PCI_DEVICE_ID_SI_640, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS645", PCI_DEVICE_ID_SI_645, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS650", PCI_DEVICE_ID_SI_650, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS730", PCI_DEVICE_ID_SI_730, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS735", PCI_DEVICE_ID_SI_735, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS740", PCI_DEVICE_ID_SI_740, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS745", PCI_DEVICE_ID_SI_745, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS750", PCI_DEVICE_ID_SI_750, SIS5513_FLAG_ATA_66|SIS5513_FLAG_LATENCY, }, - { "SiS5591", PCI_DEVICE_ID_SI_5591, SIS5513_FLAG_ATA_33, }, - { "SiS5597", PCI_DEVICE_ID_SI_5597, SIS5513_FLAG_ATA_33, }, - { "SiS5600", PCI_DEVICE_ID_SI_5600, SIS5513_FLAG_ATA_33, }, - { "SiS5511", PCI_DEVICE_ID_SI_5511, SIS5513_FLAG_ATA_16, }, + { "SiS5511", PCI_DEVICE_ID_SI_5511, ATA_16, 0}, + { "SiS5513", PCI_DEVICE_ID_SI_5513, ATA_16, 0}, + { "SiS5591", PCI_DEVICE_ID_SI_5591, ATA_33, 0}, + { "SiS5597", PCI_DEVICE_ID_SI_5597, ATA_33, 0}, + { "SiS5598", PCI_DEVICE_ID_SI_5598, ATA_33, 0}, + { "SiS5600", PCI_DEVICE_ID_SI_5600, ATA_33, 0}, + { "SiS530", PCI_DEVICE_ID_SI_530, ATA_66, 0}, + { "SiS540", PCI_DEVICE_ID_SI_540, ATA_66, 0}, + { "SiS620", PCI_DEVICE_ID_SI_620, ATA_66, SIS5513_LATENCY }, + { "SiS630", PCI_DEVICE_ID_SI_630, ATA_66, SIS5513_LATENCY }, + { "SiS635", PCI_DEVICE_ID_SI_635, ATA_100, SIS5513_LATENCY }, + { "SiS640", PCI_DEVICE_ID_SI_640, ATA_66, SIS5513_LATENCY }, + { "SiS645", PCI_DEVICE_ID_SI_645, ATA_100, SIS5513_LATENCY }, + { "SiS650", PCI_DEVICE_ID_SI_650, ATA_100, SIS5513_LATENCY }, + { "SiS730", PCI_DEVICE_ID_SI_730, ATA_100, SIS5513_LATENCY }, + { "SiS735", PCI_DEVICE_ID_SI_735, ATA_100, SIS5513_LATENCY }, + { "SiS740", PCI_DEVICE_ID_SI_740, ATA_100, SIS5513_LATENCY }, + { "SiS745", PCI_DEVICE_ID_SI_745, ATA_100, SIS5513_LATENCY }, + { "SiS750", PCI_DEVICE_ID_SI_750, ATA_100, SIS5513_LATENCY } }; -#if 0 - -static struct _pio_mode_mapping { - byte data_active; - byte recovery; - byte pio_mode; -} pio_mode_mapping[] = { - { 8, 12, 0 }, - { 6, 7, 1 }, - { 4, 4, 2 }, - { 3, 3, 3 }, - { 3, 1, 4 } -}; - -static struct _dma_mode_mapping { - byte data_active; - byte recovery; - byte dma_mode; -} dma_mode_mapping[] = { - { 8, 8, 0 }, - { 3, 2, 1 }, - { 3, 1, 2 } +/* Cycle time bits and values vary accross chip dma capabilities + These three arrays hold the register layout and the values to set. + Indexed by dma_capability and (dma_mode - XFER_UDMA_0) */ +static byte cycle_time_offset[] = {0,0,5,4,0,0}; +static byte cycle_time_range[] = {0,0,2,3,4,4}; +static byte cycle_time_value[][XFER_UDMA_5 - XFER_UDMA_0 + 1] = { + {0,0,0,0,0,0}, /* no udma */ + {0,0,0,0,0,0}, /* no udma */ + {3,2,1,0,0,0}, + {7,5,3,2,1,0}, + {11,7,5,4,2,1}, + {0,0,0,0,0,0} /* not yet known, ask SiS */ }; -static struct _udma_mode_mapping { - byte cycle_time; - char * udma_mode; -} udma_mode_mapping[] = { - { 8, "Mode 0" }, - { 6, "Mode 1" }, - { 4, "Mode 2" }, - { 3, "Mode 3" }, - { 2, "Mode 4" }, - { 0, "Mode 5" } -}; +static struct pci_dev *host_dev = NULL; -static __inline__ char * find_udma_mode (byte cycle_time) -{ - int n; - - for (n = 0; n <= 4; n++) - if (udma_mode_mapping[n].cycle_time <= cycle_time) - return udma_mode_mapping[n].udma_mode; - return udma_mode_mapping[4].udma_mode; -} -#endif +/* + * Printing configuration + */ #if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) #include #include @@ -118,12 +228,12 @@ extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */ static struct pci_dev *bmide_dev; -static char *cable_type[] = { +static char* cable_type[] = { "80 pins", "40 pins" }; -static char *recovery_time [] ={ +static char* recovery_time[] ={ "12 PCICLK", "1 PCICLK", "2 PCICLK", "3 PCICLK", "4 PCICLK", "5 PCICLCK", @@ -134,101 +244,179 @@ "15 PCICLK", "15 PCICLK" }; -static char * cycle_time [] = { - "2 CLK", "2 CLK", - "3 CLK", "4 CLK", - "5 CLK", "6 CLK", - "7 CLK", "8 CLK" -}; - -static char * active_time [] = { +static char* active_time[] = { "8 PCICLK", "1 PCICLCK", - "2 PCICLK", "2 PCICLK", + "2 PCICLK", "3 PCICLK", "4 PCICLK", "5 PCICLK", "6 PCICLK", "12 PCICLK" }; +static char* cycle_time[] = { + "Reserved", "2 CLK", + "3 CLK", "4 CLK", + "5 CLK", "6 CLK", + "7 CLK", "8 CLK", + "9 CLK", "10 CLK", + "11 CLK", "12 CLK", + "Reserved", "Reserved", + "Reserved", "Reserved" +}; + +/* Generic add master or slave info function */ +static char* get_drives_info (char *buffer, byte pos) +{ + byte reg00, reg01, reg10, reg11; /* timing registers */ + char* p = buffer; + +/* Postwrite/Prefetch */ + pci_read_config_byte(bmide_dev, 0x4b, ®00); + p += sprintf(p, "Drive %d: Postwrite %s \t \t Postwrite %s\n", + pos, (reg00 & (0x10 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x40 << pos)) ? "Enabled" : "Disabled"); + p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", + (reg00 & (0x01 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x04 << pos)) ? "Enabled" : "Disabled"); + + pci_read_config_byte(bmide_dev, 0x40+2*pos, ®00); + pci_read_config_byte(bmide_dev, 0x41+2*pos, ®01); + pci_read_config_byte(bmide_dev, 0x44+2*pos, ®10); + pci_read_config_byte(bmide_dev, 0x45+2*pos, ®11); + +/* UDMA */ + if (dma_capability >= ATA_33) { + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (reg01 & 0x80) ? "Enabled" : "Disabled", + (reg11 & 0x80) ? "Enabled" : "Disabled"); + + p += sprintf(p, " UDMA Cycle Time "); + switch(dma_capability) { + case ATA_33: p += sprintf(p, cycle_time[(reg01 & 0x60) >> 5]); break; + case ATA_66: p += sprintf(p, cycle_time[(reg01 & 0x70) >> 4]); break; + case ATA_100: p += sprintf(p, cycle_time[reg01 & 0x0F]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, " \t UDMA Cycle Time "); + switch(dma_capability) { + case ATA_33: p += sprintf(p, cycle_time[(reg11 & 0x60) >> 5]); break; + case ATA_66: p += sprintf(p, cycle_time[(reg11 & 0x70) >> 4]); break; + case ATA_100: p += sprintf(p, cycle_time[reg11 & 0x0F]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, "\n"); + } + +/* Data Active */ + p += sprintf(p, " Data Active Time "); + switch(dma_capability) { + case ATA_00: + case ATA_16: /* confirmed */ + case ATA_33: + case ATA_66: p += sprintf(p, active_time[reg01 & 0x07]); break; + case ATA_100: p += sprintf(p, active_time[(reg00 & 0x70) >> 4]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, " \t Data Active Time "); + switch(dma_capability) { + case ATA_00: + case ATA_16: + case ATA_33: + case ATA_66: p += sprintf(p, active_time[reg11 & 0x07]); break; + case ATA_100: p += sprintf(p, active_time[(reg10 & 0x70) >> 4]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, "\n"); + +/* Data Recovery */ + /* warning: may need (reg&0x07) for pre ATA66 chips */ + p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", + recovery_time[reg00 & 0x0f], recovery_time[reg10 & 0x0f]); + + return p; +} + +static char* get_masters_info(char* buffer) +{ + return get_drives_info(buffer, 0); +} + +static char* get_slaves_info(char* buffer) +{ + return get_drives_info(buffer, 1); +} + +/* Main get_info, called on /proc/ide/sis reads */ static int sis_get_info (char *buffer, char **addr, off_t offset, int count) { - int rc; char *p = buffer; - byte reg,reg1; + byte reg; u16 reg2, reg3; + p += sprintf(p, "\nSiS 5513 "); + switch(dma_capability) { + case ATA_00: p += sprintf(p, "Unknown???"); break; + case ATA_16: p += sprintf(p, "DMA 16"); break; + case ATA_33: p += sprintf(p, "Ultra 33"); break; + case ATA_66: p += sprintf(p, "Ultra 66"); break; + case ATA_100: p += sprintf(p, "Ultra 100"); break; + case ATA_133: + default: p+= sprintf(p, "Ultra 133+"); break; + } + p += sprintf(p, " chipset\n"); p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); - rc = pci_read_config_byte(bmide_dev, 0x4a, ®); - p += sprintf(p, "Channel Status: %s \t \t \t \t %s \n", - (reg & 0x02) ? "On" : "Off", - (reg & 0x04) ? "On" : "Off"); - - rc = pci_read_config_byte(bmide_dev, 0x09, ®); + +/* Status */ + pci_read_config_byte(bmide_dev, 0x4a, ®); + p += sprintf(p, "Channel Status: "); + if (dma_capability < ATA_66) { + p += sprintf(p, "%s \t \t \t \t %s\n", + (reg & 0x04) ? "On" : "Off", + (reg & 0x02) ? "On" : "Off"); + } else { + p += sprintf(p, "%s \t \t \t \t %s \n", + (reg & 0x02) ? "On" : "Off", + (reg & 0x04) ? "On" : "Off"); + } + +/* Operation Mode */ + pci_read_config_byte(bmide_dev, 0x09, ®); p += sprintf(p, "Operation Mode: %s \t \t \t %s \n", (reg & 0x01) ? "Native" : "Compatible", (reg & 0x04) ? "Native" : "Compatible"); - - rc = pci_read_config_byte(bmide_dev, 0x48, ®); - p += sprintf(p, "Cable Type: %s \t \t \t %s\n", - (reg & 0x10) ? cable_type[1] : cable_type[0], - (reg & 0x20) ? cable_type[1] : cable_type[0]); - - rc = pci_read_config_word(bmide_dev, 0x4c, ®2); - rc = pci_read_config_word(bmide_dev, 0x4e, ®3); - p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", - reg2, reg3); - - rc = pci_read_config_byte(bmide_dev, 0x4b, ®); - p += sprintf(p, "Drive 0: Postwrite %s \t \t Postwrite %s\n", - (reg & 0x10) ? "Enabled" : "Disabled", - (reg & 0x40) ? "Enabled" : "Disabled"); - p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", - (reg & 0x01) ? "Enabled" : "Disabled", - (reg & 0x04) ? "Enabled" : "Disabled"); - - rc = pci_read_config_byte(bmide_dev, 0x41, ®); - rc = pci_read_config_byte(bmide_dev, 0x45, ®1); - p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", - (reg & 0x80) ? "Enabled" : "Disabled", - (reg1 & 0x80) ? "Enabled" : "Disabled"); - p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n", - cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]); - p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n", - active_time[(reg & 0x07)], active_time[(reg1 &0x07)] ); - - rc = pci_read_config_byte(bmide_dev, 0x40, ®); - rc = pci_read_config_byte(bmide_dev, 0x44, ®1); - p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", - recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]); +/* 80-pin cable ? */ + if (dma_capability > ATA_33) { + pci_read_config_byte(bmide_dev, 0x48, ®); + p += sprintf(p, "Cable Type: %s \t \t \t %s\n", + (reg & 0x10) ? cable_type[1] : cable_type[0], + (reg & 0x20) ? cable_type[1] : cable_type[0]); + } - rc = pci_read_config_byte(bmide_dev, 0x4b, ®); - p += sprintf(p, "Drive 1: Postwrite %s \t \t Postwrite %s\n", - (reg & 0x20) ? "Enabled" : "Disabled", - (reg & 0x80) ? "Enabled" : "Disabled"); - p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", - (reg & 0x02) ? "Enabled" : "Disabled", - (reg & 0x08) ? "Enabled" : "Disabled"); +/* Prefetch Count */ + pci_read_config_word(bmide_dev, 0x4c, ®2); + pci_read_config_word(bmide_dev, 0x4e, ®3); + p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", + reg2, reg3); - rc = pci_read_config_byte(bmide_dev, 0x43, ®); - rc = pci_read_config_byte(bmide_dev, 0x47, ®1); - p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", - (reg & 0x80) ? "Enabled" : "Disabled", - (reg1 & 0x80) ? "Enabled" : "Disabled"); - p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n", - cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]); - p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n", - active_time[(reg & 0x07)], active_time[(reg1 &0x07)] ); + p = get_masters_info(p); + p = get_slaves_info(p); - rc = pci_read_config_byte(bmide_dev, 0x42, ®); - rc = pci_read_config_byte(bmide_dev, 0x46, ®1); - p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", - recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]); return p-buffer; } #endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ + byte sis_proc = 0; extern char *ide_xfer_verbose (byte xfer_rate); + +/* + * Configuration functions + */ +/* Enables per-drive prefetch and postwrite */ static void config_drive_art_rwp (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); @@ -237,14 +425,24 @@ byte reg4bh = 0; byte rw_prefetch = (0x11 << drive->dn); - pci_read_config_byte(dev, 0x4b, ®4bh); +#ifdef DEBUG + printk("SIS5513: config_drive_art_rwp, drive %d\n", drive->dn); + sis5513_load_verify_registers(dev, "config_drive_art_rwp start"); +#endif + if (drive->media != ide_disk) return; - + pci_read_config_byte(dev, 0x4b, ®4bh); + if ((reg4bh & rw_prefetch) != rw_prefetch) pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch); +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp end"); +#endif } + +/* Set per-drive active and recovery time */ static void config_art_rwp_pio (ide_drive_t *drive, byte pio) { ide_hwif_t *hwif = HWIF(drive); @@ -255,6 +453,10 @@ unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90}; unsigned short xfer_pio = drive->id->eide_pio_modes; +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp_pio start"); +#endif + config_drive_art_rwp(drive); pio = ide_get_best_pio_mode(drive, 255, pio, NULL); @@ -263,8 +465,8 @@ if (drive->id->eide_pio_iordy > 0) { for (xfer_pio = 5; - xfer_pio>0 && - drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio]; + (xfer_pio > 0) && + (drive->id->eide_pio_iordy > eide_pio_timing[xfer_pio]); xfer_pio--); } else { xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 : @@ -274,14 +476,10 @@ timing = (xfer_pio >= pio) ? xfer_pio : pio; -/* - * Mode 0 Mode 1 Mode 2 Mode 3 Mode 4 - * Active time 8T (240ns) 6T (180ns) 4T (120ns) 3T (90ns) 3T (90ns) - * 0x41 2:0 bits 000 110 100 011 011 - * Recovery time 12T (360ns) 7T (210ns) 4T (120ns) 3T (90ns) 1T (30ns) - * 0x40 3:0 bits 0000 0111 0100 0011 0001 - * Cycle time 20T (600ns) 13T (390ns) 8T (240ns) 6T (180ns) 4T (120ns) - */ +#ifdef DEBUG + printk("SIS5513: config_drive_art_rwp_pio, drive %d, pio %d, timing %d\n", + drive->dn, pio, timing); +#endif switch(drive->dn) { case 0: drive_pci = 0x40; break; @@ -291,31 +489,43 @@ default: return; } - pci_read_config_byte(dev, drive_pci, &test1); - pci_read_config_byte(dev, drive_pci|0x01, &test2); - - /* - * Do a blanket clear of active and recovery timings. - */ - - test1 &= ~0x07; - test2 &= ~0x0F; - - switch(timing) { - case 4: test1 |= 0x01; test2 |= 0x03; break; - case 3: test1 |= 0x03; test2 |= 0x03; break; - case 2: test1 |= 0x04; test2 |= 0x04; break; - case 1: test1 |= 0x07; test2 |= 0x06; break; - default: break; + /* register layout changed with ATA100 chips */ + if (dma_capability < ATA_100) { + pci_read_config_byte(dev, drive_pci, &test1); + pci_read_config_byte(dev, drive_pci+1, &test2); + + /* Clear active and recovery timings */ + test1 &= ~0x0F; + test2 &= ~0x07; + + switch(timing) { + case 4: test1 |= 0x01; test2 |= 0x03; break; + case 3: test1 |= 0x03; test2 |= 0x03; break; + case 2: test1 |= 0x04; test2 |= 0x04; break; + case 1: test1 |= 0x07; test2 |= 0x06; break; + default: break; + } + pci_write_config_byte(dev, drive_pci, test1); + pci_write_config_byte(dev, drive_pci+1, test2); + } else { + switch(timing) { /* active recovery + v v */ + case 4: test1 = 0x30|0x01; break; + case 3: test1 = 0x30|0x03; break; + case 2: test1 = 0x40|0x04; break; + case 1: test1 = 0x60|0x07; break; + default: break; + } + pci_write_config_byte(dev, drive_pci, test1); } - pci_write_config_byte(dev, drive_pci, test1); - pci_write_config_byte(dev, drive_pci|0x01, test2); +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp_pio start"); +#endif } static int config_chipset_for_pio (ide_drive_t *drive, byte pio) { - int err; byte speed; switch(pio) { @@ -328,8 +538,7 @@ config_art_rwp_pio(drive, pio); drive->current_speed = speed; - err = ide_config_drive_speed(drive, speed); - return err; + return ide_config_drive_speed(drive, speed); } static int sis5513_tune_chipset (ide_drive_t *drive, byte speed) @@ -337,82 +546,71 @@ ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; - byte drive_pci, test1, test2; - byte unmask, four_two, mask = 0; - - if (host_dev) { - switch(host_dev->device) { - case PCI_DEVICE_ID_SI_530: - case PCI_DEVICE_ID_SI_540: - case PCI_DEVICE_ID_SI_620: - case PCI_DEVICE_ID_SI_630: - case PCI_DEVICE_ID_SI_635: - case PCI_DEVICE_ID_SI_640: - case PCI_DEVICE_ID_SI_645: - case PCI_DEVICE_ID_SI_650: - case PCI_DEVICE_ID_SI_730: - case PCI_DEVICE_ID_SI_735: - case PCI_DEVICE_ID_SI_740: - case PCI_DEVICE_ID_SI_745: - case PCI_DEVICE_ID_SI_750: - unmask = 0xF0; - four_two = 0x01; - break; - default: - unmask = 0xE0; - four_two = 0x00; - break; - } - } else { - unmask = 0xE0; - four_two = 0x00; - } + byte drive_pci, reg; +#ifdef DEBUG + sis5513_load_verify_registers(dev, "sis5513_tune_chipset start"); + printk("SIS5513: sis5513_tune_chipset, drive %d, speed %d\n", + drive->dn, speed); +#endif switch(drive->dn) { - case 0: drive_pci = 0x40;break; - case 1: drive_pci = 0x42;break; - case 2: drive_pci = 0x44;break; - case 3: drive_pci = 0x46;break; + case 0: drive_pci = 0x40; break; + case 1: drive_pci = 0x42; break; + case 2: drive_pci = 0x44; break; + case 3: drive_pci = 0x46; break; default: return ide_dma_off; } - pci_read_config_byte(dev, drive_pci, &test1); - pci_read_config_byte(dev, drive_pci|0x01, &test2); +#ifdef BROKEN_LEVEL +#ifdef DEBUG + printk("SIS5513: BROKEN_LEVEL activated, speed=%d -> speed=%d\n", speed, BROKEN_LEVEL); +#endif + if (speed > BROKEN_LEVEL) speed = BROKEN_LEVEL; +#endif - if ((speed <= XFER_MW_DMA_2) && (test2 & 0x80)) { - pci_write_config_byte(dev, drive_pci|0x01, test2 & ~0x80); - pci_read_config_byte(dev, drive_pci|0x01, &test2); - } else { - pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); + pci_read_config_byte(dev, drive_pci+1, ®); + /* Disable UDMA bit for non UDMA modes on UDMA chips */ + if ((speed < XFER_UDMA_0) && (dma_capability > ATA_16)) { + reg &= 0x7F; + pci_write_config_byte(dev, drive_pci+1, reg); } + /* Config chip for mode */ switch(speed) { #ifdef CONFIG_BLK_DEV_IDEDMA - case XFER_UDMA_5: mask = 0x80; break; - case XFER_UDMA_4: mask = 0x90; break; - case XFER_UDMA_3: mask = 0xA0; break; - case XFER_UDMA_2: mask = (four_two) ? 0xB0 : 0xA0; break; - case XFER_UDMA_1: mask = (four_two) ? 0xD0 : 0xC0; break; - case XFER_UDMA_0: mask = unmask; break; + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + /* clean reg cycle time bits */ + reg &= ~((0xFF >> (8 - cycle_time_range[dma_capability])) + << cycle_time_offset[dma_capability]); + /* set reg cycle time bits */ + reg |= cycle_time_value[dma_capability-ATA_00][speed-XFER_UDMA_0] + << cycle_time_offset[dma_capability]; + pci_write_config_byte(dev, drive_pci+1, reg); + break; case XFER_MW_DMA_2: case XFER_MW_DMA_1: case XFER_MW_DMA_0: case XFER_SW_DMA_2: case XFER_SW_DMA_1: - case XFER_SW_DMA_0: break; + case XFER_SW_DMA_0: + break; #endif /* CONFIG_BLK_DEV_IDEDMA */ case XFER_PIO_4: return((int) config_chipset_for_pio(drive, 4)); case XFER_PIO_3: return((int) config_chipset_for_pio(drive, 3)); case XFER_PIO_2: return((int) config_chipset_for_pio(drive, 2)); case XFER_PIO_1: return((int) config_chipset_for_pio(drive, 1)); case XFER_PIO_0: - default: return((int) config_chipset_for_pio(drive, 0)); + default: return((int) config_chipset_for_pio(drive, 0)); } - - if (speed > XFER_MW_DMA_2) - pci_write_config_byte(dev, drive_pci|0x01, test2|mask); - drive->current_speed = speed; +#ifdef DEBUG + sis5513_load_verify_registers(dev, "sis5513_tune_chipset end"); +#endif return ((int) ide_config_drive_speed(drive, speed)); } @@ -430,47 +628,27 @@ struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); - byte four_two = 0, speed = 0; - int err; + byte speed = 0; byte unit = (drive->select.b.unit & 0x01); byte udma_66 = eighty_ninty_three(drive); - byte ultra_100 = 0; - if (host_dev) { - switch(host_dev->device) { - case PCI_DEVICE_ID_SI_635: - case PCI_DEVICE_ID_SI_640: - case PCI_DEVICE_ID_SI_645: - case PCI_DEVICE_ID_SI_650: - case PCI_DEVICE_ID_SI_730: - case PCI_DEVICE_ID_SI_735: - case PCI_DEVICE_ID_SI_740: - case PCI_DEVICE_ID_SI_745: - case PCI_DEVICE_ID_SI_750: - ultra_100 = 1; - case PCI_DEVICE_ID_SI_530: - case PCI_DEVICE_ID_SI_540: - case PCI_DEVICE_ID_SI_620: - case PCI_DEVICE_ID_SI_630: - four_two = 0x01; - break; - default: - four_two = 0x00; break; - } - } +#ifdef DEBUG + printk("SIS5513: config_chipset_for_dma, drive %d, ultra %d\n", + drive->dn, ultra); +#endif - if ((id->dma_ultra & 0x0020) && (ultra) && (udma_66) && (four_two) && (ultra_100)) + if ((id->dma_ultra & 0x0020) && ultra && udma_66 && (dma_capability >= ATA_100)) speed = XFER_UDMA_5; - else if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) + else if ((id->dma_ultra & 0x0010) && ultra && udma_66 && (dma_capability >= ATA_66)) speed = XFER_UDMA_4; - else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) + else if ((id->dma_ultra & 0x0008) && ultra && udma_66 && (dma_capability >= ATA_66)) speed = XFER_UDMA_3; - else if ((id->dma_ultra & 0x0004) && (ultra)) + else if ((id->dma_ultra & 0x0004) && ultra && (dma_capability >= ATA_33)) speed = XFER_UDMA_2; - else if ((id->dma_ultra & 0x0002) && (ultra)) + else if ((id->dma_ultra & 0x0002) && ultra && (dma_capability >= ATA_33)) speed = XFER_UDMA_1; - else if ((id->dma_ultra & 0x0001) && (ultra)) + else if ((id->dma_ultra & 0x0001) && ultra && (dma_capability >= ATA_33)) speed = XFER_UDMA_0; else if (id->dma_mword & 0x0004) speed = XFER_MW_DMA_2; @@ -489,11 +667,7 @@ outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2); - err = sis5513_tune_chipset(drive, speed); - -#if SIS5513_DEBUG_DRIVE_INFO - printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); -#endif /* SIS5513_DEBUG_DRIVE_INFO */ + sis5513_tune_chipset(drive, speed); return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : @@ -550,9 +724,7 @@ return HWIF(drive)->dmaproc(dma_func, drive); } -/* - * sis5513_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. - */ +/* initiates/aborts (U)DMA read/write operations on a drive. */ int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { switch (func) { @@ -567,14 +739,17 @@ } #endif /* CONFIG_BLK_DEV_IDEDMA */ +/* Chip detection and general config */ unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) { struct pci_dev *host; int i = 0; - byte latency = 0; - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency); +#ifdef DEBUG + sis5513_print_registers(dev, "pci_init_sis5513 start"); +#endif + /* Find the chip */ for (i = 0; i < ARRAY_SIZE (SiSHostChipInfo) && !host_dev; i++) { host = pci_find_device (PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, @@ -583,30 +758,66 @@ continue; host_dev = host; + dma_capability = SiSHostChipInfo[i].dma_capability; printk(SiSHostChipInfo[i].name); printk("\n"); - if (SiSHostChipInfo[i].flags & SIS5513_FLAG_LATENCY) { - if (latency != 0x10) - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10); + + if (SiSHostChipInfo[i].flags & SIS5513_LATENCY) { + byte latency = (dma_capability == ATA_100)? 0x80 : 0x10; /* Lacking specs */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, latency); } } + /* Make general config ops here + 1/ tell IDE channels to operate in Compabitility mode only + 2/ tell old chips to allow per drive IDE timings */ if (host_dev) { - byte reg52h = 0; - - pci_read_config_byte(dev, 0x52, ®52h); - if (!(reg52h & 0x04)) { - /* set IDE controller to operate in Compabitility mode only */ - pci_write_config_byte(dev, 0x52, reg52h|0x04); + byte reg; + switch(dma_capability) { + case ATA_133: + case ATA_100: + /* Set compatibility bit */ + pci_read_config_byte(dev, 0x49, ®); + if (!(reg & 0x01)) { + pci_write_config_byte(dev, 0x49, reg|0x01); + } + break; + case ATA_66: + /* On ATA_66 chips the bit was elsewhere */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x04)) { + pci_write_config_byte(dev, 0x52, reg|0x04); + } + break; + case ATA_33: + /* On ATA_33 we didn't have a single bit to set */ + pci_read_config_byte(dev, 0x09, ®); + if ((reg & 0x0f) != 0x00) { + pci_write_config_byte(dev, 0x09, reg&0xf0); + } + case ATA_16: + /* force per drive recovery and active timings + needed on ATA_33 and below chips */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x08)) { + pci_write_config_byte(dev, 0x52, reg|0x08); + } + break; + case ATA_00: + default: break; } + #if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) if (!sis_proc) { sis_proc = 1; bmide_dev = dev; sis_display_info = &sis_get_info; } -#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ +#endif } +#ifdef DEBUG + sis5513_load_verify_registers(dev, "pci_init_sis5513 end"); +#endif return 0; } @@ -616,27 +827,10 @@ byte mask = hwif->channel ? 0x20 : 0x10; pci_read_config_byte(hwif->pci_dev, 0x48, ®48h); - if (host_dev) { - switch(host_dev->device) { - case PCI_DEVICE_ID_SI_530: - case PCI_DEVICE_ID_SI_540: - case PCI_DEVICE_ID_SI_620: - case PCI_DEVICE_ID_SI_630: - case PCI_DEVICE_ID_SI_635: - case PCI_DEVICE_ID_SI_640: - case PCI_DEVICE_ID_SI_645: - case PCI_DEVICE_ID_SI_650: - case PCI_DEVICE_ID_SI_730: - case PCI_DEVICE_ID_SI_735: - case PCI_DEVICE_ID_SI_740: - case PCI_DEVICE_ID_SI_745: - case PCI_DEVICE_ID_SI_750: - ata66 = (reg48h & mask) ? 0 : 1; - default: - break; - } + if (dma_capability >= ATA_66) { + ata66 = (reg48h & mask) ? 0 : 1; } - return (ata66); + return ata66; } void __init ide_init_sis5513 (ide_hwif_t *hwif) @@ -651,33 +845,16 @@ return; if (host_dev) { - switch(host_dev->device) { #ifdef CONFIG_BLK_DEV_IDEDMA - case PCI_DEVICE_ID_SI_530: - case PCI_DEVICE_ID_SI_540: - case PCI_DEVICE_ID_SI_620: - case PCI_DEVICE_ID_SI_630: - case PCI_DEVICE_ID_SI_635: - case PCI_DEVICE_ID_SI_640: - case PCI_DEVICE_ID_SI_645: - case PCI_DEVICE_ID_SI_650: - case PCI_DEVICE_ID_SI_730: - case PCI_DEVICE_ID_SI_735: - case PCI_DEVICE_ID_SI_740: - case PCI_DEVICE_ID_SI_745: - case PCI_DEVICE_ID_SI_750: - case PCI_DEVICE_ID_SI_5600: - case PCI_DEVICE_ID_SI_5597: - case PCI_DEVICE_ID_SI_5591: - if (!noautodma) - hwif->autodma = 1; - hwif->dmaproc = &sis5513_dmaproc; - break; -#endif /* CONFIG_BLK_DEV_IDEDMA */ - default: - hwif->autodma = 0; - break; + if (dma_capability > ATA_16) { + hwif->autodma = noautodma ? 0 : 1; + hwif->dmaproc = &sis5513_dmaproc; + } else { +#endif + hwif->autodma = 0; +#ifdef CONFIG_BLK_DEV_IDEDMA } +#endif } return; } diff -Nru a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c --- a/drivers/ide/slc90e66.c Wed Feb 13 20:03:30 2002 +++ b/drivers/ide/slc90e66.c Wed Feb 13 20:03:30 2002 @@ -86,8 +86,13 @@ * at that point bibma+0x2 et bibma+0xa are byte registers * to investigate: */ +#ifdef __mips__ /* only for mips? */ + c0 = inb_p(bibma + 0x02); + c1 = inb_p(bibma + 0x0a); +#else c0 = inb_p((unsigned short)bibma + 0x02); c1 = inb_p((unsigned short)bibma + 0x0a); +#endif p += sprintf(p, " SLC90E66 Chipset.\n"); p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); @@ -253,7 +258,9 @@ case XFER_MW_DMA_2: case XFER_MW_DMA_1: case XFER_SW_DMA_2: break; +#if 0 /* allow PIO modes */ default: return -1; +#endif } if (speed >= XFER_UDMA_0) { @@ -291,6 +298,13 @@ byte speed = 0; byte udma_66 = eighty_ninty_three(drive); +#if 1 /* allow PIO modes */ + if (!HWIF(drive)->autodma) { + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + (void) slc90e66_tune_chipset(drive, speed); + return ((int) ide_dma_off_quietly); + } +#endif if ((id->dma_ultra & 0x0010) && (ultra)) { speed = (udma_66) ? XFER_UDMA_4 : XFER_UDMA_2; } else if ((id->dma_ultra & 0x0008) && (ultra)) { diff -Nru a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c --- a/drivers/ieee1394/csr.c Wed Feb 13 20:03:39 2002 +++ b/drivers/ieee1394/csr.c Wed Feb 13 20:03:40 2002 @@ -10,6 +10,7 @@ */ #include +#include #include "ieee1394_types.h" #include "hosts.h" diff -Nru a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c --- a/drivers/isdn/avmb1/kcapi.c Wed Feb 13 20:03:56 2002 +++ b/drivers/isdn/avmb1/kcapi.c Wed Feb 13 20:03:56 2002 @@ -103,8 +103,8 @@ #define APPL(a) (&applications[(a)-1]) #define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a) #define APPL_IS_FREE(a) (APPL(a)->applid == 0) -#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0); -#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0); +#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0) +#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0) #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) diff -Nru a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c --- a/drivers/isdn/hisax/hisax_fcpcipnp.c Wed Feb 13 20:03:42 2002 +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c Wed Feb 13 20:03:42 2002 @@ -65,6 +65,7 @@ static int protocol = 2; /* EURO-ISDN Default */ MODULE_PARM(protocol, "i"); +MODULE_LICENSE("GPL"); // ---------------------------------------------------------------------- diff -Nru a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c --- a/drivers/isdn/hisax/hisax_isac.c Wed Feb 13 20:03:35 2002 +++ b/drivers/isdn/hisax/hisax_isac.c Wed Feb 13 20:03:35 2002 @@ -44,6 +44,7 @@ MODULE_AUTHOR("Kai Germaschewski /Karsten Keil "); MODULE_DESCRIPTION("ISAC/ISAC-SX driver"); +MODULE_LICENSE("GPL"); #define DBG_WARN 0x0001 #define DBG_IRQ 0x0002 diff -Nru a/drivers/md/md.c b/drivers/md/md.c --- a/drivers/md/md.c Wed Feb 13 20:03:43 2002 +++ b/drivers/md/md.c Wed Feb 13 20:03:43 2002 @@ -2936,8 +2936,6 @@ * bdflush, otherwise bdflush will deadlock if there are too * many dirty RAID5 blocks. */ - current->policy = SCHED_OTHER; - current->nice = -20; md_unlock_kernel(); complete(thread->event); @@ -3387,11 +3385,6 @@ "(but not more than %d KB/sec) for reconstruction.\n", sysctl_speed_limit_max); - /* - * Resync has low priority. - */ - current->nice = 19; - is_mddev_idle(mddev); /* this also initializes IO event counters */ for (m = 0; m < SYNC_MARKS; m++) { mark[m] = jiffies; @@ -3469,16 +3462,13 @@ currspeed = (j-mddev->resync_mark_cnt)/2/((jiffies-mddev->resync_mark)/HZ +1) +1; if (currspeed > sysctl_speed_limit_min) { - current->nice = 19; - if ((currspeed > sysctl_speed_limit_max) || !is_mddev_idle(mddev)) { current->state = TASK_INTERRUPTIBLE; md_schedule_timeout(HZ/4); goto repeat; } - } else - current->nice = -20; + } } printk(KERN_INFO "md: md%d: sync done.\n",mdidx(mddev)); err = 0; diff -Nru a/drivers/media/video/Config.in b/drivers/media/video/Config.in --- a/drivers/media/video/Config.in Wed Feb 13 20:03:29 2002 +++ b/drivers/media/video/Config.in Wed Feb 13 20:03:30 2002 @@ -6,11 +6,13 @@ bool ' V4L information in proc filesystem' CONFIG_VIDEO_PROC_FS dep_tristate ' I2C on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT $CONFIG_I2C +dep_tristate ' Backward compatibility for V4L2 drivers' CONFIG_VIDEO_V4LCOMPAT $CONFIG_VIDEO_DEV comment 'Video Adapters' if [ "$CONFIG_I2C_ALGOBIT" = "y" -o "$CONFIG_I2C_ALGOBIT" = "m" ]; then dep_tristate ' BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C_ALGOBIT fi +dep_tristate ' Margi DVD-to-Go' CONFIG_VIDEO_MARGI $CONFIG_PCMCIA $CONFIG_VIDEO_DEV dep_tristate ' Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV if [ "$CONFIG_ALL_PPC" = "y" ]; then dep_tristate ' PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV diff -Nru a/drivers/media/video/Makefile b/drivers/media/video/Makefile --- a/drivers/media/video/Makefile Wed Feb 13 20:03:48 2002 +++ b/drivers/media/video/Makefile Wed Feb 13 20:03:48 2002 @@ -1,5 +1,5 @@ # -# Makefile for the kernel character device drivers. +# Makefile for the video capture/playback device drivers. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -16,22 +16,27 @@ obj-n := obj- := -SUB_DIRS := -MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) - O_TARGET := video.o # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := i2c-old.o videodev.o bttv-if.o cpia.o +export-objs := i2c-old.o videodev.o v4l2-common.o bttv-if.o cpia.o list-multi := bttv.o zoran.o bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o +mod-subdirs := margi + +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o +obj-$(CONFIG_VIDEO_V4LCOMPAT) += v4l1-compat.o + +ifeq ($(CONFIG_VIDEO_MARGI),y) + obj-y += margi/margi_cs.o +endif + +subdir-$(CONFIG_VIDEO_MARGI) += margi obj-$(CONFIG_BUS_I2C) += i2c-old.o obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ @@ -77,8 +82,6 @@ obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) include $(TOPDIR)/Rules.make - -fastdep: zoran.o: zr36120.o zr36120_i2c.o zr36120_mem.o $(LD) $(LD_RFLAG) -r -o $@ zr36120.o zr36120_i2c.o zr36120_mem.o diff -Nru a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c --- a/drivers/media/video/bttv-cards.c Wed Feb 13 20:03:52 2002 +++ b/drivers/media/video/bttv-cards.c Wed Feb 13 20:03:52 2002 @@ -39,9 +39,9 @@ #include "tuner.h" /* fwd decl */ +static void boot_msp34xx(struct bttv *btv, int pin); static void hauppauge_eeprom(struct bttv *btv); static void avermedia_eeprom(struct bttv *btv); - static void init_PXC200(struct bttv *btv); #if 0 static void init_tea5757(struct bttv *btv); @@ -55,6 +55,9 @@ static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set); static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set); static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set); +static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set); +static void windvr_audio(struct bttv *btv, struct video_audio *v, int set); +static void rv605_muxsel(struct bttv *btv, unsigned int input); /* config variables */ static int triton1=0; @@ -77,7 +80,8 @@ MODULE_PARM_DESC(triton1,"set ETBF pci config bit " "[enable bug compatibility for triton1 + others]"); MODULE_PARM(vsfx,"i"); -MODULE_PARM_DESC(vsfx,"set VSFX pci config bit [yet another chipset flaw workaround]"); +MODULE_PARM_DESC(vsfx,"set VSFX pci config bit " + "[yet another chipset flaw workaround]"); MODULE_PARM(no_overlay,"i"); MODULE_PARM(card,"1-4i"); MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); @@ -151,7 +155,7 @@ { 0x00041461, BTTV_AVERMEDIA98, "AVerMedia TVCapture 98" }, { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" }, - { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master" }, + { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" }, { 0x1117153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x1118153b, BTTV_TERRATVALUE, "Terratec TValue" }, @@ -161,6 +165,7 @@ { 0x1127153b, BTTV_TERRATV, "Terratec TV+" }, { 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" }, + { 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, { 0x400d15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, @@ -168,12 +173,14 @@ { 0x401615b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, { 0x010115cb, BTTV_GMV1, "AG GMV1" }, - { 0x010114c7, BTTV_MODTEC_205, "Modular Technology PCTV" }, + { 0x010114c7, BTTV_MODTEC_205, "Modular Technology MM205 PCTV" }, { 0x18501851, BTTV_CHRONOS_VS2, "Flyvideo 98 (LR50)/ Chronos Video Shuttle II" }, { 0x18511851, BTTV_FLYVIDEO98EZ, "Flyvideo 98EZ (LR51)/ CyberMail AV" }, { 0x18521852, BTTV_TYPHOON_TVIEW, "Flyvideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" }, { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, { 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, + { 0x03116000, BTTV_SENSORAY311, "Sensoray 311" }, + { 0x00790e11, BTTV_WINDVR, "Canopus WinDVR PCI" }, { 0, -1, NULL } }; @@ -276,8 +283,8 @@ },{ /* ---- card 0x08 ---------------------------------- */ - name: "Fly Video II (Bt848)", - video_inputs: 3, + name: "FlyVideo II (Bt848) LR26", + video_inputs: 4, audio_inputs: 1, tuner: 0, svhs: 2, @@ -285,9 +292,10 @@ muxsel: { 2, 3, 1, 1}, audiomux: { 0, 0xc00, 0x800, 0x400, 0xc00, 0}, needs_tvaudio: 1, + pll: PLL_28, tuner_type: -1, },{ - name: "TurboTV", + name: "IXMicro TurboTV", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -346,14 +354,14 @@ pll: PLL_28, tuner_type: -1, },{ - name: "Aimslab VHX", + name: "Aimslab Video Highway Xtreme (VHX)", video_inputs: 3, audio_inputs: 1, tuner: 0, svhs: 2, gpiomask: 7, muxsel: { 2, 3, 1, 1}, - audiomux: { 0, 1, 2, 3, 4}, + audiomux: { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */ needs_tvaudio: 1, tuner_type: -1, },{ @@ -393,6 +401,7 @@ needs_tvaudio: 1, tuner_type: -1, audio_hook: winview_audio, + has_radio: 1, },{ name: "AVEC Intercapture", video_inputs: 3, @@ -438,7 +447,7 @@ pll: PLL_28, tuner_type: TUNER_PHILIPS_PAL_I, },{ - name: "Phoebe Tv Master + FM", + name: "Phoebe Tv Master + FM (CPH050)", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -752,6 +761,7 @@ gpiomask: 0x1f0000, muxsel: { 2, 3, 1, 1}, audiomux: { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff }, + needs_tvaudio: 1, no_msp34xx: 1, pll: PLL_35, tuner_type: 1, @@ -788,7 +798,7 @@ video_inputs: 4, audio_inputs: 1, tuner: 0, - svhs: 2, + svhs: 3, gpiomask: 0xAA0000, muxsel: { 2,3,1,1 }, audiomux: { 0x20000, 0, 0x80000, 0x80000, 0xa8000, 0x46000 }, @@ -860,7 +870,7 @@ },{ /* Miguel Angel Alvarez old Easy TV BT848 version (model CPH031) */ - name: "BESTBUY Easy TV", + name: "BESTBUY Easy TV (CPH031)", video_inputs: 4, audio_inputs: 1, tuner: 0, @@ -904,7 +914,7 @@ tuner_type: -1, },{ /* Daniel Herrington */ - name: "Phoebe TV Master Only (No FM)", + name: "Phoebe TV Master Only (No FM) CPH060", video_inputs: 3, audio_inputs: 1, tuner: 0, @@ -917,7 +927,7 @@ tuner_type: TUNER_TEMIC_4036FY5_NTSC, },{ /* Matti Mottus */ - name: "TV Capturer", + name: "TV Capturer (CPH03X)", video_inputs: 4, audio_inputs: 1, tuner: 0, @@ -931,7 +941,7 @@ /* ---- card 0x3c ---------------------------------- */ /* Philip Blundell */ - name: "MM100PCTV", + name: "Modular Technology MM100PCTV", video_inputs: 2, audio_inputs: 2, gpiomask: 11, @@ -977,7 +987,7 @@ tuner: 0, svhs: 2, gpiomask: 0xf03f, - muxsel: { 2, 3, 0, 1}, + muxsel: { 2, 3, 1, 0 }, audiomux: { 0xbffe, 0, 0xbfff, 0, 0xbffe}, pll: PLL_28, tuner_type: TUNER_TEMIC_4006FN5_MULTI_PAL, @@ -1005,7 +1015,13 @@ svhs: 2, gpiomask: 0x18e0, muxsel: { 2, 3, 0, 1}, - audiomux: { 0,0x18e0,0x1000,0x1000,0x1080, 0x1080 }, + /* Radio changed from 1e80 to 0x800 to make + Flyvideo2000S in .hu happy (gm)*/ + /* -dk-???: set mute=0x1800 for tda9874h daughterboard */ + audiomux: { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 }, + audio_hook: fv2000s_audio, + no_msp34xx: 1, + no_tda9875: 1, needs_tvaudio: 1, pll: PLL_28, tuner_type: 5, @@ -1047,11 +1063,12 @@ tuner: 0, svhs: -1, gpiomask: 0x4f8a00, - // 0x100000: 1=MSP enabled (0=disable again) - // 0x010000: somehow influences tuner picture quality (?) - audiomux: {0x947fff, 0x987fff,0x947fff,0x947fff}, - //tvtuner, radio, external,internal,mute,stereo - muxsel: { 2, 3 ,0 ,1}, /* tuner, Composit, SVid, Composit-on-Svid-adapter*/ + // 0x100000: 1=MSP enabled (0=disable again) + // 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) + audiomux: {0x947fff, 0x987fff,0x947fff,0x947fff, 0x947fff}, + // tvtuner, radio, external,internal, mute, stereo + /* tuner, Composit, SVid, Composit-on-Svid-adapter*/ + muxsel: { 2, 3 ,0 ,1}, tuner_type: TUNER_MT2032, pll: PLL_28, has_radio: 1, @@ -1106,8 +1123,61 @@ tuner_type: -1, audio_hook: pvbt878p9b_audio, has_radio: 1, -} -}; +},{ + /* Clay Kunz */ + /* you must jumper JP5 for the card to work */ + name: "Sensoray 311", + video_inputs: 5, + audio_inputs: 0, + tuner: -1, + svhs: 4, + gpiomask: 0, + muxsel: { 2, 3, 1, 0, 0}, + audiomux: { 0 }, + needs_tvaudio: 0, + tuner_type: -1, +},{ + /* Miguel Freitas */ + name: "RemoteVision MX (RV605)", + video_inputs: 16, + audio_inputs: 0, + tuner: 0, + svhs: 0, + gpiomask: 0x00, + gpiomask2: 0x07ff, + muxsel: { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03, + 0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 }, + no_msp34xx: 1, + no_tda9875: 1, + tuner_type: -1, + muxsel_hook: rv605_muxsel, +},{ + name: "Powercolor MTV878/ MTV878R/ MTV878F", + video_inputs: 3, + audio_inputs: 2, + svhs: 2, + gpiomask: 0x1C800F, // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset + muxsel: { 2, 1, 1, }, + audiomux: { 0, 1, 2, 2, 4 }, + needs_tvaudio: 0, + tuner_type: TUNER_PHILIPS_PAL, + pll: PLL_28, + has_radio: 1, +},{ + +/* ---- card 0x4c ---------------------------------- */ + /* Masaki Suzuki */ + name: "Canopus WinDVR PCI", + video_inputs: 3, + audio_inputs: 1, + tuner: 0, + svhs: 2, + gpiomask: 0x140007, + muxsel: { 2, 3, 1, 1 }, + audiomux: { 0, 1, 2, 3, 4, 0 }, + tuner_type: TUNER_PHILIPS_NTSC, + audio_hook: windvr_audio, +}}; const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard)); @@ -1195,12 +1265,58 @@ * (most) board specific initialisations goes here */ +static void flyvideo_gpio(struct bttv *btv) +{ + int gpio,outbits; + int tuner=-1,ttype; + + outbits = btread(BT848_GPIO_OUT_EN); + btwrite(0x00, BT848_GPIO_OUT_EN); + udelay(8); // without this we would see the 0x1800 mask + gpio=btread(BT848_GPIO_DATA); + btwrite(outbits, BT848_GPIO_OUT_EN); + // all cards provide GPIO info, some have an additional eeprom + + // lowest 3 bytes are remote control codes (no handshake needed) + ttype=(gpio&0x0f0000)>>16; + switch(ttype) { + case 0: tuner=4; // None + break; + case 4: tuner=5; // Philips PAL + break; + case 6: tuner=37; // LG PAL (newer TAPC series) + break; + case 0xC: tuner=3; // Philips SECAM(+PAL) + break; + default: + printk(KERN_INFO "bttv%d: flyvideo_gpio: unknown tuner type.\n", btv->nr); + } + + printk(KERN_INFO "bttv%d: Flyvideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", + btv->nr, + gpio&0x400000? "yes":"no", + gpio&0x800000? "yes":"no", tuner, gpio); + + btv->tuner_type = tuner; + btv->has_radio = gpio&0x400000? 1:0; +} + int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1, 14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 }; int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,0,0,0 }; -void __devinit bttv_init_card(struct bttv *btv) +/* initialization part one -- before registering i2c bus */ +void __devinit bttv_init_card1(struct bttv *btv) +{ + if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) + boot_msp34xx(btv,5); + if (btv->type == BTTV_VOODOOTV_FM) + boot_msp34xx(btv,20); +} + +/* initialization part one -- after registering i2c bus */ +void __devinit bttv_init_card2(struct bttv *btv) { /* miro/pinnacle */ if (btv->type == BTTV_MIRO || @@ -1246,6 +1362,15 @@ #endif } + if (btv->type == BTTV_FLYVIDEO_98 || + btv->type == BTTV_FLYVIDEO || + btv->type == BTTV_TYPHOON_TVIEW || + btv->type == BTTV_CHRONOS_VS2 || + btv->type == BTTV_FLYVIDEO_98FM || + btv->type == BTTV_FLYVIDEO2000 || + btv->type == BTTV_FLYVIDEO98EZ) + flyvideo_gpio(btv); + if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { /* pick up some config infos from the eeprom */ bttv_readee(btv,eeprom_data,0xa0); @@ -1449,22 +1574,24 @@ btv->tuner_type, radio ? "yes" : "no"); } -// AVermedia specific stuff... -// from bktr_card.c + +/* ----------------------------------------------------------------------- */ +/* AVermedia specific stuff, from bktr_card.c */ + int tuner_0_table[] = { TUNER_PHILIPS_NTSC, TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL, TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL}; -/* +#if 0 int tuner_0_fm_table[] = { PHILIPS_FR1236_NTSC, PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, PHILIPS_FR1236_SECAM, PHILIPS_FR1236_SECAM, PHILIPS_FR1236_SECAM, PHILIPS_FR1216_PAL}; -*/ +#endif int tuner_1_table[] = { TUNER_TEMIC_NTSC, TUNER_TEMIC_PAL, @@ -1475,11 +1602,12 @@ static void __devinit avermedia_eeprom(struct bttv *btv) { - int tuner_make,tuner_tv_fm,tuner_format,tuner=0; + int tuner_make,tuner_tv_fm,tuner_format,tuner=0,remote; tuner_make = (eeprom_data[0x41] & 0x7); tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3; tuner_format = (eeprom_data[0x42] & 0xf0) >> 4; + remote = (eeprom_data[0x42] & 0x01); if (tuner_make == 0 || tuner_make == 2) if(tuner_format <=9) @@ -1492,11 +1620,32 @@ btv->nr,eeprom_data[0x41],eeprom_data[0x42]); if(tuner) { btv->tuner_type=tuner; - printk("%d\n",tuner); + printk("%d",tuner); } else - printk("Unknown type\n"); + printk("Unknown type"); + printk(" radio:%s remote control:%s\n", + tuner_tv_fm?"yes":"no", + remote?"yes":"no"); } +/* used on Voodoo TV/FM (Voodoo 200), S0 wired to 0x10000 */ +void bttv_tda9880_setnorm(struct bttv *btv, int norm) +{ + // fix up our card entry + if(norm==VIDEO_MODE_NTSC) { + bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x957fff; + bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x957fff; + dprintk("bttv_tda9880_setnorm to NTSC\n"); + } + else { + bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x947fff; + bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x947fff; + dprintk("bttv_tda9880_setnorm to PAL\n"); + } + // set GPIO according + btaor(bttv_tvcards[btv->type].audiomux[btv->audio], + ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); +} /* @@ -1506,7 +1655,7 @@ * Hauppauge: pin 5 * Voodoo: pin 20 */ -void __devinit bttv_boot_msp34xx(struct bttv *btv, int pin) +static void __devinit boot_msp34xx(struct bttv *btv, int pin) { int mask = (1 << pin); @@ -1918,9 +2067,115 @@ } } else { v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | - VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; } } + +/* + * Dariusz Kowalewski + * sound control for FlyVideo 2000S (with tda9874 decoder) + * based on pvbt878p9b_audio() - this is not tested, please fix!!! + */ +static void +fv2000s_audio(struct bttv *btv, struct video_audio *v, int set) +{ + unsigned int val = 0xffff; + +#if BTTV_VERSION_CODE > KERNEL_VERSION(0,8,0) + if (btv->radio_user) + return; +#else + if (btv->radio) + return; +#endif + if (set) { + if (v->mode & VIDEO_SOUND_MONO) { + val = 0x0000; + } + if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2)) + || (v->mode & VIDEO_SOUND_STEREO)) { + val = 0x1080; //-dk-???: 0x0880, 0x0080, 0x1800 ... + } + if (val != 0xffff) { + btaor(val, ~0x1800, BT848_GPIO_DATA); + if (bttv_gpio) + bttv_gpio_tracking(btv,"fv2000s"); + } + } else { + v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | + VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + } +} + +/* + * sound control for Canopus WinDVR PCI + * Masaki Suzuki + */ +static void +windvr_audio(struct bttv *btv, struct video_audio *v, int set) +{ + unsigned long val = 0; + + if (set) { + if (v->mode & VIDEO_SOUND_MONO) + val = 0x040000; + if (v->mode & VIDEO_SOUND_LANG1) + val = 0; + if (v->mode & VIDEO_SOUND_LANG2) + val = 0x100000; + if (v->mode & VIDEO_SOUND_STEREO) + val = 0; + if (val) { + btaor(val, ~0x140000, BT848_GPIO_DATA); + if (bttv_gpio) + bttv_gpio_tracking(btv,"windvr"); + } + } else { + v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | + VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + } +} + +/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas] + * + * This is needed because rv605 don't use a normal multiplex, but a crosspoint + * switch instead (CD22M3494E). This IC can have multiple active connections + * between Xn (input) and Yn (output) pins. We need to clear any existing + * connection prior to establish a new one, pulsing the STROBE pin. + * + * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin. + * GPIO pins are wired as: + * GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroler) + * GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroler) + * GPIO[7] - DATA (xpoint) - P1[7] (microcontroler) + * GPIO[8] - - P3[5] (microcontroler) + * GPIO[9] - RESET (xpoint) - P3[6] (microcontroler) + * GPIO[10] - STROBE (xpoint) - P3[7] (microcontroler) + * GPINTR - - P3[4] (microcontroler) + * + * The microcontroler is a 80C32 like. It should be possible to change xpoint + * configuration either directly (as we are doing) or using the microcontroler + * which is also wired to I2C interface. I have no further info on the + * microcontroler features, one would need to disassembly the firmware. + * note: the vendor refused to give any information on this product, all + * that stuff was found using a multimeter! :) + */ +static void rv605_muxsel(struct bttv *btv, unsigned int input) +{ + /* reset all conections */ + btaor(0x200,~0x200, BT848_GPIO_DATA); + mdelay(1); + btaor(0x000,~0x200, BT848_GPIO_DATA); + mdelay(1); + + /* create a new conection */ + btaor(0x080,~0x480, BT848_GPIO_DATA); + btaor(0x480,~0x480, BT848_GPIO_DATA); + mdelay(1); + btaor(0x080,~0x480, BT848_GPIO_DATA); + mdelay(1); +} + /* ----------------------------------------------------------------------- */ /* motherboard chipset specific stuff */ diff -Nru a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c --- a/drivers/media/video/bttv-driver.c Wed Feb 13 20:03:40 2002 +++ b/drivers/media/video/bttv-driver.c Wed Feb 13 20:03:40 2002 @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -70,6 +69,9 @@ static unsigned int gbufsize = BTTV_MAX_FBUF; static unsigned int combfilter = 0; static unsigned int lumafilter = 0; +static unsigned int automute = 1; +static unsigned int chroma_agc = 0; +static unsigned int adc_crush = 1; static int video_nr = -1; static int radio_nr = -1; static int vbi_nr = -1; @@ -96,8 +98,15 @@ MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (64 max)"); MODULE_PARM(gbufsize,"i"); MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); + MODULE_PARM(combfilter,"i"); MODULE_PARM(lumafilter,"i"); +MODULE_PARM(automute,"i"); +MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); +MODULE_PARM(chroma_agc,"i"); +MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); +MODULE_PARM(adc_crush,"i"); +MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); MODULE_PARM(video_nr,"i"); MODULE_PARM(radio_nr,"i"); @@ -279,7 +288,7 @@ static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern", "audio: intern", "audio: off" }; -static void audio(struct bttv *btv, int mode, int no_irq_context) +static void audio(struct bttv *btv, int mode) { btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_OUT_EN); @@ -313,12 +322,12 @@ ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); if (bttv_gpio) bttv_gpio_tracking(btv,audio_modes[mode]); - if (no_irq_context) + if (!in_interrupt()) bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode)); } -extern inline void bt848_dma(struct bttv *btv, uint state) +static inline void bt848_dma(struct bttv *btv, uint state) { if (state) btor(3, BT848_GPIO_DMA_CTL); @@ -425,18 +434,16 @@ static void bt848_muxsel(struct bttv *btv, unsigned int input) { - -#if 0 /* seems no card uses this ... */ + /* needed by RemoteVideo MX */ btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_OUT_EN); -#endif /* This seems to get rid of some synchronization problems */ btand(~(3<<5), BT848_IFORM); - mdelay(10); - + mdelay(10); + input %= bttv_tvcards[btv->type].video_inputs; - if (input==bttv_tvcards[btv->type].svhs) + if (input==bttv_tvcards[btv->type].svhs) { btor(BT848_CONTROL_COMP, BT848_E_CONTROL); btor(BT848_CONTROL_COMP, BT848_O_CONTROL); @@ -446,20 +453,25 @@ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); } - btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM); - audio(btv, (input!=bttv_tvcards[btv->type].tuner) ? - AUDIO_EXTERN : AUDIO_TUNER, 1); -#if 0 /* seems no card uses this ... */ + btaor((bttv_tvcards[btv->type].muxsel[input]&3)<<5, ~(3<<5), BT848_IFORM); + audio(btv, (input!=bttv_tvcards[btv->type].tuner) ? + AUDIO_EXTERN : AUDIO_TUNER); + btaor(bttv_tvcards[btv->type].muxsel[input]>>4, ~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA); + + /* card specific hook */ + if( bttv_tvcards[btv->type].muxsel_hook ) + bttv_tvcards[btv->type].muxsel_hook ( btv, input ); + if (bttv_gpio) bttv_gpio_tracking(btv,"muxsel"); -#endif + } -struct tvnorm +struct tvnorm { u32 Fsc; u16 swidth, sheight; /* scaled standard width, height */ @@ -582,7 +594,7 @@ #define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int)) static int make_rawrisctab(struct bttv *btv, unsigned int *ro, - unsigned int *re, unsigned int *vbuf) + unsigned int *re, unsigned int *vbuf) { unsigned long line; unsigned long bpl=1024; /* bytes per line */ @@ -1054,8 +1066,7 @@ } -static void bt848_set_geo(struct bttv *btv, - int no_irq_context) +static void bt848_set_geo(struct bttv *btv) { u16 ewidth, eheight, owidth, oheight; u16 format, bswap; @@ -1070,7 +1081,7 @@ btwrite(1, BT848_VBI_PACK_DEL); btv->pll.pll_ofreq = tvn->Fsc; - if (no_irq_context) + if (!in_interrupt()) set_pll(btv); btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0; @@ -1162,7 +1173,7 @@ else btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - bt848_set_geo(btv,1); + bt848_set_geo(btv); } /* @@ -1194,7 +1205,7 @@ if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 > gbufsize) return -EINVAL; - if(-1 == palette2fmt[mp->format]) + if (-1 == palette2fmt[mp->format]) return -EINVAL; /* @@ -1352,7 +1363,7 @@ spin_lock_irqsave(&btv->s_lock, irq_flags); btv->errors = 0; btv->needs_restart = 0; - bt848_set_geo(btv,0); + bt848_set_geo(btv); bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); } @@ -1452,17 +1463,17 @@ /* ioctls and supporting functions */ /***********************************/ -extern inline void bt848_bright(struct bttv *btv, uint bright) +static inline void bt848_bright(struct bttv *btv, uint bright) { btwrite(bright&0xff, BT848_BRIGHT); } -extern inline void bt848_hue(struct bttv *btv, uint hue) +static inline void bt848_hue(struct bttv *btv, uint hue) { btwrite(hue&0xff, BT848_HUE); } -extern inline void bt848_contrast(struct bttv *btv, uint cont) +static inline void bt848_contrast(struct bttv *btv, uint cont) { unsigned int conthi; @@ -1472,7 +1483,7 @@ btaor(conthi, ~4, BT848_O_CONTROL); } -extern inline void bt848_sat_u(struct bttv *btv, unsigned long data) +static inline void bt848_sat_u(struct bttv *btv, unsigned long data) { u32 datahi; @@ -1495,7 +1506,6 @@ /* * ioctl routine */ - static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { @@ -1573,6 +1583,8 @@ bt848_muxsel(btv, v.channel); btv->channel=v.channel; if (btv->win.norm != v.norm) { + if(btv->type== BTTV_VOODOOTV_FM) + bttv_tda9880_setnorm(btv,v.norm); btv->win.norm = v.norm; make_vbitab(btv); spin_lock_irqsave(&btv->s_lock, irq_flags); @@ -1638,10 +1650,12 @@ case VIDIOCSPICT: { struct video_picture p; - if(copy_from_user(&p, arg,sizeof(p))) + if (copy_from_user(&p, arg,sizeof(p))) return -EFAULT; if (p.palette > PALETTEFMT_MAX) return -EINVAL; + if (-1 == palette2fmt[p.palette]) + return -EINVAL; down(&btv->lock); /* We want -128 to 127 we get 0-65535 */ bt848_bright(btv, (p.brightness>>8)-128); @@ -1875,7 +1889,7 @@ return -EFAULT; down(&btv->lock); if(v.flags&VIDEO_AUDIO_MUTE) - audio(btv, AUDIO_MUTE, 1); + audio(btv, AUDIO_MUTE); /* One audio source per tuner -- huh? */ if(v.audio<0 || v.audio >= bttv_tvcards[btv->type].audio_inputs) { up(&btv->lock); @@ -1883,7 +1897,7 @@ } /* bt848_muxsel(btv,v.audio); */ if(!(v.flags&VIDEO_AUDIO_MUTE)) - audio(btv, AUDIO_UNMUTE, 1); + audio(btv, AUDIO_UNMUTE); bttv_call_i2c_clients(btv,cmd,&v); @@ -2368,6 +2382,12 @@ flags |= 0x03; if (btv->vbi_on) flags |= 0x0c; +#if 0 + /* Hmm ... */ + if ((0 != btv->risc_cap_even) || + (0 != btv->risc_cap_odd)) + flags |= 0x0c; +#endif } if (bttv_debug > 1) @@ -2467,15 +2487,19 @@ static int __devinit init_video_dev(struct bttv *btv) { - audio(btv, AUDIO_MUTE, 1); + audio(btv, AUDIO_MUTE); if(do_video_register(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) return -1; + printk(KERN_INFO "bttv%d: registered device video%d\n", + btv->nr,btv->video_dev.minor & 0x1f); if(do_video_register(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) { video_unregister_device(&btv->video_dev); return -1; } + printk(KERN_INFO "bttv%d: registered device vbi%d\n", + btv->nr,btv->vbi_dev.minor & 0x1f); if (btv->has_radio) { if(do_video_register(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0) @@ -2484,13 +2508,15 @@ video_unregister_device(&btv->video_dev); return -1; } + printk(KERN_INFO "bttv%d: registered device radio%d\n", + btv->nr,btv->radio_dev.minor & 0x1f); } return 1; } static int __devinit init_bt848(struct bttv *btv) { - int j; + int j,val; unsigned long irq_flags; btv->user=0; @@ -2592,8 +2618,8 @@ btwrite(0x20, BT848_E_VSCALE_HI); btwrite(0x20, BT848_O_VSCALE_HI); - btwrite(/*BT848_ADC_SYNC_T|*/ - BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC); + btwrite(BT848_ADC_RESERVED | (adc_crush ? BT848_ADC_CRUSH : 0), + BT848_ADC); if (lumafilter) { btwrite(0, BT848_E_CONTROL); @@ -2608,8 +2634,9 @@ btv->picture.hue=128<<8; btv->picture.contrast=0xd8<<7; - btwrite(0x00, BT848_E_SCLOOP); - btwrite(0x00, BT848_O_SCLOOP); + val = chroma_agc ? BT848_SCLOOP_CAGC : 0; + btwrite(val, BT848_E_SCLOOP); + btwrite(val, BT848_O_SCLOOP); /* clear interrupt status */ btwrite(0xfffffUL, BT848_INT_STAT); @@ -2633,17 +2660,14 @@ spin_unlock_irqrestore(&btv->s_lock, irq_flags); /* needs to be done before i2c is registered */ - if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) - bttv_boot_msp34xx(btv,5); - if (btv->type == BTTV_VOODOOTV_FM) - bttv_boot_msp34xx(btv,20); + bttv_init_card1(btv); /* register i2c */ btv->tuner_type=-1; init_bttv_i2c(btv); /* some card-specific stuff (needs working i2c) */ - bttv_init_card(btv); + bttv_init_card2(btv); /* * Now add the template and register the device unit. @@ -2722,7 +2746,7 @@ btand(~15, BT848_GPIO_DMA_CTL); btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD); - bt848_set_geo(btv,0); + bt848_set_geo(btv); bt848_set_risc_jmps(btv,-1); spin_unlock(&btv->s_lock); } else { @@ -2764,14 +2788,14 @@ btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; bt848_set_risc_jmps(btv,-1); - bt848_set_geo(btv,0); + bt848_set_geo(btv); btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); } else { btv->risc_cap_odd = 0; btv->risc_cap_even = 0; bt848_set_risc_jmps(btv,-1); - bt848_set_geo(btv,0); + bt848_set_geo(btv); btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); } @@ -2790,18 +2814,18 @@ btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; bt848_set_risc_jmps(btv,-1); - bt848_set_geo(btv,0); + bt848_set_geo(btv); btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); spin_unlock(&btv->s_lock); } } - if (astat&BT848_INT_HLOCK) { + if (automute && (astat&BT848_INT_HLOCK)) { if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) - audio(btv, AUDIO_ON,0); + audio(btv, AUDIO_ON); else - audio(btv, AUDIO_OFF,0); + audio(btv, AUDIO_OFF); } count++; @@ -2820,7 +2844,7 @@ * Scan for a Bt848 card, request the irq and map the io memory */ -static void __devexit bttv_remove(struct pci_dev *pci_dev) +static void bttv_remove(struct pci_dev *pci_dev) { u8 command; int j; @@ -2869,7 +2893,7 @@ if (btv->vbibuf) vfree((void *) btv->vbibuf); - free_irq(btv->irq,btv); + free_irq(btv->dev->irq,btv); DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem)); if (btv->bt848_mem) iounmap(btv->bt848_mem); @@ -2904,6 +2928,8 @@ unsigned int cmd; #endif + if (bttv_num == BTTV_MAX) + return -ENOMEM; printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); btv=&bttvs[bttv_num]; @@ -2926,10 +2952,17 @@ memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); btv->id=dev->device; - btv->irq=dev->irq; btv->bt848_adr=pci_resource_start(dev,0); - if (pci_enable_device(dev)) + if (pci_enable_device(dev)) { + printk(KERN_WARNING "bttv%d: Can't enable device.\n", + btv->nr); + return -EIO; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + printk(KERN_WARNING "bttv%d: No suitable DMA available.\n", + btv->nr); return -EIO; + } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), "bttv")) { @@ -2946,7 +2979,7 @@ bttv_num,btv->id, btv->revision, dev->bus->number, PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn)); printk("irq: %d, latency: %d, memory: 0x%lx\n", - btv->irq, lat, btv->bt848_adr); + btv->dev->irq, lat, btv->bt848_adr); bttv_idcard(btv); @@ -2967,7 +3000,7 @@ /* clear interrupt mask */ btwrite(0, BT848_INT_MASK); - result = request_irq(btv->irq, bttv_irq, + result = request_irq(btv->dev->irq, bttv_irq, SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv); if (result==-EINVAL) { @@ -2977,7 +3010,7 @@ } if (result==-EBUSY) { - printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); + printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->dev->irq); goto fail1; } if (result < 0) @@ -3000,7 +3033,7 @@ return 0; fail2: - free_irq(btv->irq,btv); + free_irq(btv->dev->irq,btv); fail1: release_mem_region(pci_resource_start(btv->dev,0), pci_resource_len(btv->dev,0)); @@ -3025,10 +3058,10 @@ name: "bttv", id_table: bttv_pci_tbl, probe: bttv_probe, - remove: __devexit_p(bttv_remove), + remove: bttv_remove, }; -int bttv_init_module(void) +static int bttv_init_module(void) { bttv_num = 0; @@ -3049,7 +3082,7 @@ return pci_module_init(&bttv_pci_driver); } -void bttv_cleanup_module(void) +static void bttv_cleanup_module(void) { pci_unregister_driver(&bttv_pci_driver); return; diff -Nru a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h --- a/drivers/media/video/bttv.h Wed Feb 13 20:03:35 2002 +++ b/drivers/media/video/bttv.h Wed Feb 13 20:03:35 2002 @@ -87,7 +87,9 @@ #define BTTV_PV_BT878P_PLUS 0x46 #define BTTV_FLYVIDEO98EZ 0x47 #define BTTV_PV_BT878P_9B 0x48 - +#define BTTV_SENSORAY311 0x49 +#define BTTV_RV605 0x4a +#define BTTV_WINDVR 0x4c /* i2c address list */ #define I2C_TSA5522 0xc2 @@ -95,7 +97,7 @@ #define I2C_TDA8425 0x82 #define I2C_TDA9840 0x84 #define I2C_TDA9850 0xb6 /* also used by 9855,9873 */ -#define I2C_TDA9874A 0xb0 /* also used by 9875 */ +#define I2C_TDA9874 0xb0 /* also used by 9875 */ #define I2C_TDA9875 0xb0 #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae @@ -123,7 +125,7 @@ int tuner; int svhs; u32 gpiomask; - u32 muxsel[8]; + u32 muxsel[16]; u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */ u32 gpiomask2; /* GPIO MUX mask */ @@ -141,6 +143,7 @@ int tuner_type; int has_radio; void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set); + void (*muxsel_hook)(struct bttv *btv, unsigned int input); }; extern struct tvcard bttv_tvcards[]; @@ -148,11 +151,12 @@ /* identification / initialization of the card */ extern void bttv_idcard(struct bttv *btv); -extern void bttv_init_card(struct bttv *btv); +extern void bttv_init_card1(struct bttv *btv); +extern void bttv_init_card2(struct bttv *btv); /* card-specific funtions */ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); -extern void bttv_boot_msp34xx(struct bttv *btv, int pin); +extern void bttv_tda9880_setnorm(struct bttv *btv, int norm); /* kernel cmd line parse helper */ extern int bttv_parse(char *str, int max, int *vals); diff -Nru a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h --- a/drivers/media/video/bttvp.h Wed Feb 13 20:03:49 2002 +++ b/drivers/media/video/bttvp.h Wed Feb 13 20:03:49 2002 @@ -25,7 +25,7 @@ #ifndef _BTTVP_H_ #define _BTTVP_H_ -#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,83) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,90) #include @@ -136,7 +136,6 @@ unsigned int nr; unsigned short id; struct pci_dev *dev; - unsigned int irq; /* IRQ used by Bt848 card */ unsigned char revision; unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ unsigned char *bt848_mem; /* pointer to mapped IO memory */ diff -Nru a/drivers/media/video/id.h b/drivers/media/video/id.h --- a/drivers/media/video/id.h Wed Feb 13 20:03:54 2002 +++ b/drivers/media/video/id.h Wed Feb 13 20:03:54 2002 @@ -24,6 +24,12 @@ #ifndef I2C_DRIVERID_TDA7432 # define I2C_DRIVERID_TDA7432 I2C_DRIVERID_EXP0+6 #endif -#ifndef I2C_DRIVERID_TDA9874A -# define I2C_DRIVERID_TDA9874A I2C_DRIVERID_EXP0+7 +#ifndef I2C_DRIVERID_TDA9874 +# define I2C_DRIVERID_TDA9874 I2C_DRIVERID_EXP0+7 #endif + +/* algorithms */ +#ifndef I2C_ALGO_SAA7134 +# define I2C_ALGO_SAA7134 0x090000 +#endif + diff -Nru a/drivers/media/video/margi/AUTHORS b/drivers/media/video/margi/AUTHORS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/AUTHORS Wed Feb 13 20:03:56 2002 @@ -0,0 +1,6 @@ +Christian Wolff scarabaeus@convergence.de +Marcus Metzler mocm@convergence.de + +Many thanks to +Shigehiro Nomura s.nomura@mba.nifty.ne.jp +for his ZV-patches for Libretto diff -Nru a/drivers/media/video/margi/CHANGES b/drivers/media/video/margi/CHANGES --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/CHANGES Wed Feb 13 20:03:58 2002 @@ -0,0 +1,30 @@ +version 0.6 +- added ZV initialisation +- added diff for ZV support in pcmcia-cs version <= 3.1.25 +- added v4l device +- added dvb api devices +- tested it with powerbook g4 and pcmcia-cs-3.1.24 +- WARNING, on some computers using FFWD and slow motion may cause hangups + +version 0.5 +- should work with 2.4.0-test12 +- added driver to public CVS at linuxtv.org + http://linuxtv.org/cgi-bin/cvsweb.cgi/ +- added 32kHz audio support +- fixed problem with PAL MPEG1 playback +- fixed problem with playback of MPEGs with lots of PTS +- fixed switching audio channels + +version 0.4.3 +- it is now working with 2.4.0-test10 and later kernels. Use pcmcia-cs-3.1.22 + and later.(Don't use kernel pcmcia,it's not tested with that. ) +- added playfile to the testsuite + +version 0.4.2 +- changed major device number to 200 until we get a new one +- changed some buffer settings for DVD playback +- added some programming examples in testsuit directory +- some other minor improvements + +version 0.4.1 +- corrected makefile error diff -Nru a/drivers/media/video/margi/COPYING b/drivers/media/video/margi/COPYING --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/COPYING Wed Feb 13 20:04:01 2002 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -Nru a/drivers/media/video/margi/Makefile b/drivers/media/video/margi/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/Makefile Wed Feb 13 20:04:02 2002 @@ -0,0 +1,33 @@ +# +# Makefile for the Margi DVD-to-Go driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := margilib.o + +CFLAGS_margi_cs.o = -DUSE_OSD -DNOINT -DDVB -DUSE_ZV + +obj-y := +obj-m := +obj-n := +obj- := + +list-multi := margi_cs.o + +margi_cs-objs := margi.o cardbase.o i2c.o dram.o osd.o audio.o \ + video.o streams.o decoder.o spu.o crc.o ringbuffy.o \ + dvb_filter.o cvdv.o + +export-objs := dvbdev.o + +obj-m += margi_cs.o dvbdev.o dmxdev.o dvb_demux.o + +include $(TOPDIR)/Rules.make + +margi_cs.o: $(margi_cs-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(margi_cs-objs) + diff -Nru a/drivers/media/video/margi/README b/drivers/media/video/margi/README --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/README Wed Feb 13 20:03:58 2002 @@ -0,0 +1,97 @@ +This is the driver for the Margi/Billionton MPEG decoder PC-Card. +It is still in a beta state and may cause problems with your system. + +INSTALLING +---------- +0) From now on you will need a 2.4.x kernel to make everything work + +1) Install David Hinds` PCMCIA Card Services package. The current + version is at http://pcmcia.sourceforge.org/. + This driver is known to work under versions 3.1.25 and later. Earlier + version may work too. + + % tar zxvf pcmcia-cs-3.1.25.tar.gz + +2) Enter the pcmcia directory and unpack the margi driver. + + % cd pcmcia-cs-3.1.25 + % tar zxvf margi_cs-0.5.tar.gz + +3) In the pcmcia main directory: + + % make config + % make all + # make install + + +The files for the margi are in margi2. (The name has historical reasons.) + +ATTENTION You now need video4linux support in the kernel. +You can now use /dev/video for playback. + + +Using the driver +---------------- +The driver registers a character device with major number 162. You can +cat an MPEG2 program stream into that device. +If the device doesn`t exist (usually it`s /dev/raw) just + % mknod -m 0666 /dev/margi char 200 0 +Than you + % cat nicempg2.vob > /dev/margi +or + % cat nicempg.mpg > /dev/margi + +At the moment we do not recognize the audio format of the MPEG1/2, so +MPEG audio is hard-coded as default. You can change that in cvdv.c in the +Prepare() routine, or by using the ioctl Decoder_Set_Audiotype, e.g.: + +#include "cvdvext.h" + +main() +{ + struct decodercmd decmd; + + decmd.param1=audio_AC3; + decmd.cmd=Decoder_Set_Audiotype; + DecoderCommand(device,decmd); +... + +} + +In the directory testsuite are some example programs for using the driver. +I hope they are more or less self explanatory. Just use the --help option. + + +If you want the latest drivers apart from the release versions, use +the public CVS at linuxtv.org : http://linuxtv.org/cgi-bin/cvsweb.cgi/ + + +ZV-support +---------- + +You will faind patches for ZV support in the zv-diffs directory. The +ones with the version number for pcmcia-cs ar for the respective +versions of this package. The rest is for graphics chips or sound +chips, like the patches for the Neomagic graphics chip and YMF sound +chip submitted by Shigehiro Nomura. + +There now three module parameters that are all set to 1 (=on) but can +be set to off in /etc/pcmcia/config.opts +They are : "svhs" for switching the svhs output DAC on or off (0 or 1). + "composite" for switching the composite output DAC on or off. + "use_zv" for enabling zv output if you compiled with the + -DUSE_ZV setting in margi_cs.mk. + +E.g. +module "margi_cs" opts "use_zv=0" +turns off zv output. +or +module "margi_cs" opts "composite=0 svhs=0" +turns off the external outputs. + +WHO DO I BLAME/FLAME? +=== == = ============ + +Send comments, patches, free pizza to . + + diff -Nru a/drivers/media/video/margi/README.CVS b/drivers/media/video/margi/README.CVS --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/README.CVS Wed Feb 13 20:04:01 2002 @@ -0,0 +1,13 @@ +1) unpack pcmcia_cs distribution + % tar zxvf pcmcia-cs-3.1.14.tar.gz + +2) cd pcmcia-cs-3.1.14 + % cvs co margi2 + +3) cp margi2/margi_cs.mk.MAIN margi_cs.mk + +4) For ZV support in pcmcia-cs use zv.diff. Remember, this is just the + first step to get to watching the output on your notebook's screen. + If you have pcmcia-cs versio >= 3.1.25 you need to use zv.diff.3.1.25. + The patches currently only work for the Rigoch 5c478 controller. + Anybody with information about ZV regarding graphic chips, please tell me. diff -Nru a/drivers/media/video/margi/audio.c b/drivers/media/video/margi/audio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/audio.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,248 @@ +/* + audio.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +// +// Audio Decoder +// +#define __NO_VERSION__ + +#include "audio.h" +#include "l64021.h" + +// mode=0 pause +// mode=1 normal speed play +// mode=2 fast play, 16/15 +// mode=3 slow play, 16/17 + +void AudioSetPlayMode(struct cvdv_cards *card, int mode) +{ + DecoderMaskByte(card, 0x163, 0x60, (mode & 0x03) << 5); + // audio decoder play mode + DecoderMaskByte(card, 0x164, 0x60, (((mode) ? 1 : 0) & 0x03) << 5); + // S/PDIF formatter play mode +} + +void AudioStartDecode(struct cvdv_cards *card) +{ + DecoderSetByte(card, 0x163, 0x80); +} + +// Stop Decode flushes the Audio ES channel buffer +void AudioStopDecode(struct cvdv_cards *card) +{ + DecoderDelByte(card, 0x163, 0x80); +} + +void AudioStartFormat(struct cvdv_cards *card) +{ + DecoderSetByte(card, 0x164, 0x80); +} + +void AudioStopFormat(struct cvdv_cards *card) +{ + DecoderDelByte(card, 0x164, 0x80); +} + +// Audio source: S/PDIF out: +// mode 0: MPEG IEC958 +// mode 1: AC3 IEC958 +// mode 2: MPEG MPEG +// mode 3: AC3 AC3 +// mode 4: PCM IEC958 (max. 48kHz) +// mode 5: PCM 96->48kHz IEC958 (48kHz) +// mode 6: CD Bypass S/PDIF Bypass +// mode 7: PCM FIFO PCM FIFO +void AudioSetMode(struct cvdv_cards *card, int mode) +{ + mode &= 0x07; + AudioSetPlayMode(card, MAUDIO_PAUSE); + AudioStopFormat(card); + DecoderMaskByte(card, 0x165, 0xE0, mode << 5); + if ((mode == 2) || (mode == 3)) + AudioStartFormat(card); +} + + +// volume: 0..255 +void AudioSetVolume(struct cvdv_cards *card, int volume) +{ + DecoderWriteByte(card, 0x16A, volume); // Set PCM scale to volume +} + +// mute=1: mute audio +void AudioMute(struct cvdv_cards *card, int mute) +{ + DecoderMaskByte(card, 0x166, 0x40, (mute ? 0x40 : 0x00)); + // mute PCM + DecoderMaskByte(card, 0x16E, 0x10, (mute ? 0x10 : 0x00)); + // mute S/PDIF +} + +// mode=0: stereo +// mode=1: surround +void AudioAC3Mode(struct cvdv_cards *card, int mode) +{ + DecoderMaskByte(card, 0x166, 0x10, (mode ? 0x10 : 0x00)); +} + +// mode=0: custom analog +// mode=1: custom digital +// mode=2: line-out (default) +// mode=3: RF mode +void AudioAC3Compression(struct cvdv_cards *card, int mode) +{ + DecoderMaskByte(card, 0x166, 0x03, mode & 0x03); +} + +// mode=0: AC3 +// mode=1: ES1 +void AudioAC3Formatter(struct cvdv_cards *card, int mode) +{ + DecoderMaskByte(card, 0x166, 0x03, mode & 0x03); +} + +// mode=0: Stereo +// mode=1: Right channel only +// mode=2: Left channel only +// mode=3: Mono Mix +void AudioDualMono(struct cvdv_cards *card, int mode) +{ + DecoderMaskByte(card, 0x166, 0x0C, (mode & 0x03) << 2); +} + +// swap=0: L->L, R->R +// swap=1: L->R, R->L +void AudioSwap(struct cvdv_cards *card, int swap) +{ + DecoderMaskByte(card, 0x16B, 0x04, (swap ? 0x00 : 0x04)); +} + +// select=0: use clock from ACLK_441 pin -> ACLK=44.1kHz*N +// select=1: use clock from ACLK_48 pin -> ACLK=48.0kHz*N +// select=2: use clock from ACLK_32 pin -> ACLK=32.0kHz*N +// Since the programmable sample rate generator of the PCM1723 is connected to +// all 3 of them, it doen't matter wich one you choose. +// divider=0: ACLK=768*Fs / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/3 +// divider=1: ACLK=768*Fs / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/2 +// divider=2: ACLK=512*Fs / S/PDIF-BCLK=ACLK/4 / DAC-BCLK=ACLK/8 / DAC-A_ACLK=ACLK/2 +// divider=3: ACLK=384*Fs / S/PDIF-BCLK=ACLK/3 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// divider=4: ACLK=256*Fs / S/PDIF-BCLK=ACLK/2 / DAC-BCLK=ACLK/4 / DAC-A_ACLK=ACLK/1 +// divider=5: ACLK=768*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// divider=6: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/4 / DAC-BCLK=ACLK/4 / DAC-A_ACLK=ACLK/1 +// divider=C: ACLK=768*48kHz / S/PDIF-BCLK=ACLK/9 / DAC-BCLK=ACLK/18 / DAC-A_ACLK=ACLK/3 +// divider=D: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/3 +// divider=E: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/2 +// divider=F: ACLK=256*48kHz / S/PDIF-BCLK=ACLK/3 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// Fs is the audio sample frequency +// For the normal cases, (32, 44.1, and 48 kHz) select divider 0 through 4 and set +// sample frequency in PCM1723 accordingly +// For 96 kHz, select divider 5 or 6, and set PCM1723 to 48kHz*768 or *512 resp. +// Divider C through F are for 32 kHz sample frequency with a 48kHz*x ACLK +void AudioSetACLK(struct cvdv_cards *card, int select, int divider) +{ + DecoderMaskByte(card, 0x16B, 0x03, select & 0x03); + DecoderMaskByte(card, 0x16C, 0x0F, divider & 0x0F); +} + +int AudioOpen(struct cvdv_cards *card) +{ + // initialize the audio card + MDEBUG(1, ": -- AudioOpen\n"); + write_indexed_register(card, IIO_OSC_AUD, 0x10); + return 0; +} + +int AudioClose(struct cvdv_cards *card) +{ + MDEBUG(1, ": -- AudioClose\n"); + card->AudioInitialized = 0; + return 0; +} + +// audiorate: 16, 32, 64, 22(.05), 44(.1), 88(.2), 24, 48, 96 kHz +// surround=0: Stereo +// surround=1: Surround +int AudioInit(struct cvdv_cards *card, int audiorate, int surround) +{ + //if ((audiorate!=44) && (audiorate!=32)) audiorate=48; + MDEBUG(1, ": -- AudioInit %d\n", audiorate); + + DACSetFrequency(card, audiorate, 256); // put Fs*256 on ACLK inputs + + if (audiorate == 96) + AudioSetACLK(card, 1, 0x06); // 512*48kHz at ACLK + else + AudioSetACLK(card, 1, 0x04); // 256 times Fs at ACLK + + DecoderDelByte(card, 0x166, 80); // no mute on error + DecoderWriteByte(card, 0x168, 0xFF); // dynscalehigh + DecoderWriteByte(card, 0x169, 0xFF); // dynscalelow + DecoderWriteByte(card, 0x16A, 0xFF); // PCM scale + + // IEC958 Setup + DecoderDelByte(card, 0x16D, 0x20); // Overwrite Emphasis off + DecoderSetByte(card, 0x16D, 0x40); // Copyright Override + DecoderDelByte(card, 0x16D, 0x80); // Copyright Bit off + DecoderDelByte(card, 0x16E, 0x01); // Overwrite Category off + DecoderDelByte(card, 0x16E, 0x08); // Overwrite Quatization off + DecoderSetByte(card, 0x170, 0x08); // Musicam Stream Debug + + AudioAC3Mode(card, (surround ? 1 : 0)); + AudioAC3Compression(card, 2); + AudioAC3Formatter(card, 0); + + AudioDualMono(card, 0); + AudioSwap(card, 0); + + AudioMute(card, 0); +// AudioSetPlayMode(card,MAUDIO_PLAY); + + card->AudioInitialized = 1; + return 0; +} + + +// returns size of the Video ES Buffer in bytes or 0=error +u32 DecoderGetAudioESSize(struct cvdv_cards * card) +{ + if (!card->ChannelBuffersAllocated) + return 0; // buffer not initialised + return (u32) ((DecoderReadWord(card, 0x04E) & 0x3FFF) - + (DecoderReadWord(card, 0x04C) & 0x3FFF)) * 256; // bytes +} + +// returns level of fullness in bytes +u32 DecoderGetAudioESLevel(struct cvdv_cards * card) +{ + u32 items; + items = DecoderReadByte(card, 0x089); + items |= ((DecoderReadWord(card, 0x08A) & 0x07FF) << 8); + items *= 8; // 64 bit per item + return items; +} + +int DecoderKaraoke(struct cvdv_cards *card, int vocal1, int vocal2, + int melody) +{ + DecoderMaskByte(card, 0x18C, 0x40, ((vocal1) ? 0x40 : 0x00)); + DecoderMaskByte(card, 0x18C, 0x80, ((vocal2) ? 0x80 : 0x00)); + DecoderMaskByte(card, 0x18C, 0x20, ((melody) ? 0x20 : 0x00)); + return 0; +} diff -Nru a/drivers/media/video/margi/audio.h b/drivers/media/video/margi/audio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/audio.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,136 @@ +/* + audio.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef CVDV_AUDIO_H +#define CVDV_AUDIO_H + + // + // Audio Decoder +// +#define __NO_VERSION__ + +#include "cardbase.h" + +#define MAUDIO_PAUSE 0 +#define MAUDIO_PLAY 1 +#define MAUDIO_FAST 2 +#define MAUDIO_SLOW 3 + +// mode=0 pause +// mode=1 normal speed play +// mode=2 fast play, 16/15 +// mode=3 slow play, 16/17 +void AudioSetPlayMode(struct cvdv_cards *card, int mode); + +void AudioStartDecode(struct cvdv_cards *card); + +// Stop Decode flushes the Audio ES channel buffer +void AudioStopDecode(struct cvdv_cards *card); + +void AudioStartFormat(struct cvdv_cards *card); + +void AudioStopFormat(struct cvdv_cards *card); + +// Audio source: S/PDIF out: +// mode 0: MPEG IEC958 +// mode 1: AC3 IEC958 +// mode 2: MPEG MPEG +// mode 3: AC3 AC3 +// mode 4: PCM IEC958 (max. 48kHz) +// mode 5: PCM 96->48kHz IEC958 (48kHz) +// mode 6: CD Bypass S/PDIF Bypass +// mode 7: PCM FIFO PCM FIFO +void AudioSetMode(struct cvdv_cards *card, int mode); + + +// volume: 0..255 +void AudioSetVolume(struct cvdv_cards *card, int volume); + +// mute=1: mute audio +void AudioMute(struct cvdv_cards *card, int mute); + +// mode=0: stereo +// mode=1: surround +void AudioAC3Mode(struct cvdv_cards *card, int mode); + +// mode=0: custom analog +// mode=1: custom digital +// mode=2: line-out (default) +// mode=3: RF mode +void AudioAC3Compression(struct cvdv_cards *card, int mode); + +// mode=0: AC3 +// mode=1: ES1 +void AudioAC3Formatter(struct cvdv_cards *card, int mode); + +// mode=0: Stereo +// mode=1: Right channel only +// mode=2: Left channel only +// mode=3: Mono Mix +void AudioDualMono(struct cvdv_cards *card, int mode); + +// swap=0: L->L, R->R +// swap=1: L->R, R->L +void AudioSwap(struct cvdv_cards *card, int swap); + +// select=0: use clock from ACLK_441 pin -> ACLK=44.1kHz*N +// select=1: use clock from ACLK_48 pin -> ACLK=48.0kHz*N +// select=2: use clock from ACLK_32 pin -> ACLK=32.0kHz*N +// Since the programmable sample rate generator of the PCM1723 is connected to +// all 3 of them, it doen't matter wich one you choose. +// divider=0: ACLK=768*Fs / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/3 +// divider=1: ACLK=768*Fs / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/2 +// divider=2: ACLK=512*Fs / S/PDIF-BCLK=ACLK/4 / DAC-BCLK=ACLK/8 / DAC-A_ACLK=ACLK/2 +// divider=3: ACLK=384*Fs / S/PDIF-BCLK=ACLK/3 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// divider=4: ACLK=256*Fs / S/PDIF-BCLK=ACLK/2 / DAC-BCLK=ACLK/4 / DAC-A_ACLK=ACLK/1 +// divider=5: ACLK=768*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// divider=6: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/4 / DAC-BCLK=ACLK/4 / DAC-A_ACLK=ACLK/1 +// divider=C: ACLK=768*48kHz / S/PDIF-BCLK=ACLK/9 / DAC-BCLK=ACLK/18 / DAC-A_ACLK=ACLK/3 +// divider=D: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/3 +// divider=E: ACLK=512*48kHz / S/PDIF-BCLK=ACLK/6 / DAC-BCLK=ACLK/12 / DAC-A_ACLK=ACLK/2 +// divider=F: ACLK=256*48kHz / S/PDIF-BCLK=ACLK/3 / DAC-BCLK=ACLK/6 / DAC-A_ACLK=ACLK/1 +// Fs is the audio sample frequency +// For the normal cases, (32, 44.1, and 48 kHz) select divider 0 through 4 and set +// sample frequency in PCM1723 accordingly +// For 96 kHz, select divider 5 or 6, and set PCM1723 to 48kHz*768 or *512 resp. +// Divider C through F are for 32 kHz sample frequency with a 48kHz*x ACLK +void AudioSetACLK(struct cvdv_cards *card, int select, int divider); + +int AudioOpen(struct cvdv_cards *card); + +int AudioClose(struct cvdv_cards *card); + +// audiorate: 16, 32, 64, 22(.05), 44(.1), 88(.2), 24, 48, 96 kHz +// surround=0: Stereo +// surround=1: Surround +int AudioInit(struct cvdv_cards *card, int audiorate, int surround); + + +// returns size of the Video ES Buffer in bytes or 0=error +u32 DecoderGetAudioESSize(struct cvdv_cards *card); + +// returns level of fullness in bytes +u32 DecoderGetAudioESLevel(struct cvdv_cards *card); + +int DecoderKaraoke(struct cvdv_cards *card, int vocal1, int vocal2, + int melody); + +#endif /* CVDV_AUDIO_H */ diff -Nru a/drivers/media/video/margi/cardbase.c b/drivers/media/video/margi/cardbase.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cardbase.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,234 @@ +/* + cardbase.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "cardbase.h" + +// List of pci cards in the system +struct cvdv_cards *first_card = NULL; +struct cvdv_cards *minorlist[MAXDEV]; + +u8 FlushPacket[32] = { + 0x00, 0x00, 0x01, 0xE0, // video stream start code + 0x00, 0x1a, // 26 more bytes + 0x81, 0xC1, // flags: copy=1, PTS_DTS=11, PES_extension=1 + 0x0D, // 13 more header bytes + 0x31, 0x00, 0x03, 0x5F, 0xEB, // PTS + 0x11, 0x00, 0x03, 0x48, 0x75, // DTS + 0x1E, // flags: P-STD_buffer=1, + 0x60, 0xE8, // P-STD_buffer_scale=1, P-STD_buffer_size=232(kByte) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void DecoderStreamReset(struct cvdv_cards *card) +{ + card->stream.valid = 0; + card->stream.sh.valid = 0; + card->stream.se.valid = 0; + card->stream.gop.valid = 0; + card->stream.MPEG2 = 0; + card->stream.audio.valid = 0; + memset(&card->stream.audio.mpeg,0,sizeof(struct AudioMPEG)); + memset(&card->stream.audio.ac3,0,sizeof(struct AudioAC3)); + memset(&card->stream.audio.pcm,0,sizeof(struct AudioPCM)); + card->AuxFifoExt = 0; + card->AuxFifoLayer = -1; +} +void PTSStoreInit(PTSStorage * store, int size) +{ + int i; + if (size > MAX_PTS) + size = MAX_PTS; + store->size = size; + store->begin = 0; + store->end = 0; + store->LastAddr = 0; + for (i = 0; i < store->size; i++) { + store->AddrB[i] = 0; + store->AddrE[i] = 0; + store->PTS[i] = 0; + } +} + +void DecoderCSSReset(struct cvdv_cards *card) +{ + card->css.status = 0; + card->css.ChallengeReady = 0; + card->css.ResponseReady = 0; + card->css.DiskKey = 0; + card->css.TitleKey = 0; + card->css.Error = 0; + card->css.TitleKeyDiff = 0; + card->LastAddr = 0; // last used address in PES buffer + card->VPTS = 0; + card->oldVPTS = 0; + card->VSCR = 0; + card->APTS = 0; + card->oldAPTS = 0; + card->ASCR = 0; + card->SyncTime = 0; + card->paused = 0; // pause status + card->lastvattr = 0; // last set dvd video attribute + card->lastaattr = 0; // last set dvd audio attribute + card->nonblock = 0; +} + +void DecoderSetupReset(struct cvdv_cards *card) +{ + card->DecoderOpen = 0; + card->closing = 0; + card->channelrun = 0; + card->setup.streamtype = stream_none; + card->setup.audioselect = audio_none; + card->setup.videoID = 0; + card->setup.audioID = 0; + card->setup.audioIDext = -1; + card->setup.SPDIFmode = 0; + card->startingV = 0; + card->startingA = 0; + card->startingDVDV = 0; + card->startingDVDA = 0; + card->videodelay = 0; + card->videodelay_last = 0; + card->videoslow_last = 0; + card->videoslow = 0; + card->videoffwd = 0; + card->videoffwd_last = 0; + card->videoskip = 0; + card->videoskip_last = 0; + card->videosync = 0; + card->paused = 0; + PTSStoreInit(&card->VideoPTSStore, MAX_PTS); + PTSStoreInit(&card->AudioPTSStore, MAX_PTS); +#ifdef DVB + card->audiostate.AVSyncState=true; +#endif +} + + + +void card_init(struct cvdv_cards *card, unsigned int minor) +{ + card->DRAMFirstBlock = NULL; + card->DRAMSize = 0; + card->OSD.open = 0; + card->DMAABusy = 0; + card->DMABBusy = 0; + card->IntInstalled = 0; + card->ChannelBuffersAllocated = 0; + card->VideoES = BLANK; + card->AudioES = BLANK; + card->VideoPES = BLANK; + card->DataDump = BLANK; + card->AudioPES = BLANK; + card->NaviBank = BLANK; + card->FrameBuffersAllocated = 0; + card->FrameStoreLuma1 = BLANK; + card->FrameStoreChroma1 = BLANK; + card->FrameStoreLuma2 = BLANK; + card->FrameStoreChroma2 = BLANK; + card->FrameStoreLumaB = BLANK; + card->FrameStoreChromaB = BLANK; + card->DecoderOpen = 0; + card->AuxFifoHead = 0; + card->AuxFifoTail = 0; + card->DataFifoHead = 0; + card->DataFifoTail = 0; + card->FifoALast = -1; + card->FifoBLast = -1; + //reset_stream(card); + DecoderStreamReset(card); + DecoderSetupReset(card); + card->AudioInitialized = 0; + card->AudioOldMode = -1; + card->closing = 0; + card->startingV = 0; + card->startingA = 0; + card->startingDVDV = 0; + card->startingDVDA = 0; + card->channelrun = 0; + card->fields = 0; + DecoderCSSReset(card); + card->NaviPackAddress = 0; + init_waitqueue_head(&card->wqA); + init_waitqueue_head(&card->wqB); + card->navihead = 0; // write pointer for navi ring buffer + card->navitail = 0; // read pointer for navi ring buffer + card->intdecodestatus = 0; // last status of decode interrupt + card->showvideo = 0; // show video instead black as soon as picture slice is there + card->videodelay = 0; // slow counter + card->videodelay_last = 0; // slow counter + card->videoffwd = 0; // fast playback + card->videoffwd_last = 0; // fast playback + card->videoskip = 0; // fast counter + card->videoskip_last = 0; // fast counter + card->videoslow_last = 0; + card->videoslow = 0; + card->videosync = 0; // do audio/video sync basen on PTS? + PTSStoreInit(&card->VideoPTSStore, MAX_PTS); + PTSStoreInit(&card->AudioPTSStore, MAX_PTS); + card->LastAddr = 0; // last used address in PES buffer + card->VPTS = 0; + card->oldVPTS = 0; + card->VSCR = 0; + card->APTS = 0; + card->oldAPTS = 0; + card->ASCR = 0; + card->SyncTime = 0; + card->paused = 0; // pause status + card->lastvattr = 0; // last set dvd video attribute + card->lastaattr = 0; // last set dvd audio attribute + card->reg08F = 0; // mirror of decoder registers + card->reg090 = 0; + card->reg091 = 0; + card->reg092 = 0; + card->reg093 = 0; + card->highlight_valid = 0; // SPU highlight info available for next BAV int + card->do_flush = 0; + + card->open = 0; + + card->VideoESSize = 0; + card->AudioESSize = 0; + card->VideoPESSize = 0; + card->DataDumpSize = 0; + card->AudioPESSize = 0; + card->NaviBankSize = 0; + card->currentType = -1; + card->rbufA.buffy = NULL; + card->rbufB.buffy = NULL; + card->use_ringA = 0; + card->use_ringB = 0; + card->minor = minor; + card->hasZV = 0; +#ifdef NOINT + init_timer(&card->timer); + spin_lock_init(&card->timelock); +#endif +#ifdef DVB + card->dvb_registered = 0; + card->audiostate.AVSyncState=true; + card->nonblock = 0; +#endif + card->svhs = 0; + card->composite = 0; +} + diff -Nru a/drivers/media/video/margi/cardbase.h b/drivers/media/video/margi/cardbase.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cardbase.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,454 @@ +/* + cardbase.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CARDBASE_H +#define CARDBASE_H + +#define __DVB_PACK__ +#define USE_OSD +#define NOINT +#define DVB +#define USE_ZV + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define MARGI_DEBUG (pc_debug) +#else +#define MARGI_DEBUG 2 +#endif + +// all the internal structs + +#include + +#include "ringbuffy.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DVB +#include "dvbdev.h" +#ifdef __DVB_PACK__ +#include "ost/video.h" +#include "ost/audio.h" +#include "ost/demux.h" +#include "ost/dmx.h" +#include "ost/sec.h" +#include "ost/frontend.h" +#include "ost/ca.h" +#include "ost/osd.h" +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_filter.h" +#endif +// List of pci cards in the system + +#include "cvdvtypes.h" + +#define DVERSION "0.6.0" +#define SHORTDEVNAME "ConvDVD" +#define MEDDEVNAME "convergence DVD" +#define LONGDEVNAME "convergence DVD Video Decoder" +#define LOGNAME "convdvd " +#define NBBUF 8 + + +#ifdef MARGI_DEBUG +#define MDEBUG(n, args...) if (MARGI_DEBUG>(n)) printk(KERN_ERR LOGNAME args) +#else +#define MDEBUG(n, args...) +#endif + + +#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ +#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ +#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ +#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ + + +// Character device definitions +// char dev name +#define CVDV_PROCNAME "msc" // Media Stream Consumer +// got to get another number +#define CVDV_MAJOR 200 // 0=dynamic assignment + +// Author definitions +#define NAME "Christian Wolff" +#define EMAIL "scarabaeus@convergence.de" +#define COMPANY "convergence integrated media GmbH" +#define AUTHOR NAME " <" EMAIL "> " COMPANY + +#define MAXDEV 1 // maximum number of cards, distance between minor devices + +#define MINORNUM (256/MAXDEV) // number of minor devices + +#define NAVISIZE 1024 // size of one navi block +#define NAVIBUFFERSIZE NAVISIZE*10 // size of ten navi blocks + +#define BLANK 0xFFFFFFFF + +#define FIFO_MASK 1023 + +#define CCIR601Lines(system) (((system==NTSC) || (system==NTSC60) || (system==PALM) || (system==PALM60) || (system==PAL60))?525:625) + +// default video mode +#define VIDEO_MODE PAL +//#define VIDEO_MODE NTSC + +struct DRAMBlock { + u32 start; // start address of the block; (21 bit word address, 64 bit aligned) + u32 length; // length of the block (in 16 bit words) + struct DRAMBlock *next; // chain link +}; + +struct CSS { + u8 status; // interrupt status from Register 0x0B0 + int ChallengeReady; // 1 if challenge data valid + u8 challenge[10]; // challenge data + int ResponseReady; // 1 if response data valid + u8 response[5]; // response data + int DiskKey; // 1 if disk key extraction complete + int TitleKey; // 1 if title key decryption complete + int Error; // 1 if authentication or disc key extraction + int TitleKeyDiff; // 1 if title key different from previous +}; + +struct GOPHeader { + int valid; // 1: struct contains valid data + int timecode; + int closedgop; + int brokenlink; +}; + +struct SequenceHeader { + int valid; // 1: struct contains valid data + int hsize; + int vsize; + int aspectratio; + int frameratecode; + int bitrate; + int vbvbuffersize; + int constrained; +}; + +struct SequenceExtension { + int valid; // 1: struct contains valid data + int profilelevel; + int progressive; + int chroma; + int hsizeext; + int vsizeext; + int bitrateext; + int vbvbuffersizeext; + int lowdelay; + int frextn; + int frextd; +}; + +struct AudioMPEG { + int present; // true: MPEG audio stream present + int MPEG2; // 0:MPEG1 Audio + int layer; // 1..3 (I..III) + int bitrate; // 0=free, 32-448 kbps + int samplefreq; // 32,44,48 (44 eq. 44.1) + int mode; // 0=stereo 1=joint-stereo 2=dualchannel 3=single channel (just right channel) + int modeext; // Layer I&II: intensity stereo subbands Layer III: bit 0=intensity stereo, bit 1=ms-stereo + int copyright; // true=copyrighted material + int original; // 0=copy true=original + int emphasis; // 0=no emph. 1=50/15usec 3=CCITT J.17 +}; + +struct AudioAC3 { + int present; // 1: AC3 audio stream present + int acmod; // parameters from the AC3 documentation + int bsmod; + int dialnorm; + int dialnorm2; + int surmixlev; + int mixlevel; + int cmixlev; + int mixlevel2; + int fscod; + int lfeon; + int bsid; + int dsurmod; + int frmsizecod; + int langcod; + int langcod2; + int timecod; + int roomtyp; + int timecod2; + int roomtyp2; +}; + +struct AudioPCM { + int present; // 1: PCM audio stream present + int audio_frm_num; + int num_of_audio_ch; + int Fs; + int quantization; + int emphasis; + int mute_bit; +}; + +struct AudioParam { + int valid; + struct AudioMPEG mpeg; + struct AudioAC3 ac3; + struct AudioPCM pcm; +}; + +struct OSDPicture { // all u32 pointers are 21 bit word addresses + int open; // are the buffers initialized? + int width; // frame width + int height; // frame height + int bpp; // bit per pixel + int evenfirst; // first line is in even field + int aspectratio; // pixel aspect ratio: 11/aspectratio + int oddheight; // height of the odd field + u32 oddmem; // DRAM address of allocated memory + u32 odddata; // data (=header) pointer + u32 oddpalette; // pointer to palette inside data + u32 oddbitmap; // pointer to bitmap inside data + u32 oddterm; // pointer to termination header + int evenheight; // height of the even field + u32 evenmem; // DRAM address of allocated memory + u32 evendata; // data (=header) pointer + u32 evenpalette; // pointer to palette inside data + u32 evenbitmap; // pointer to bitmap inside data + u32 eventerm; // pointer to termination header +}; + +struct StreamInfo { + int valid; // 1: struct contains valid data + int MPEG2; // 0: MPEG1/ISO11172 1: MPEG2/ISO13818 + int hsize; // overall hsize (hsize&hsizeext) + int vsize; // overall vsize (vsize&vsizeext) + int bitrate; // overall bitrate (bitrate&bitrateext) + int vbvbuffersize; // overall... + struct GOPHeader gop; + struct SequenceHeader sh; + struct SequenceExtension se; + struct AudioParam audio; +}; + +struct StreamSetup { // user selected parameters for the stream playback + stream_type streamtype; // what is the type of our input stream? + audio_type audioselect; // 0=auto/unknown 1=MPEG 2=LPCM 3=AC3 + int videoID; // stream ID of the video ES, -1 for any + int audioID; // stream ID of the audio ES, -1 for any + int audioIDext; // stream ID of the audio extension ES, -1 for none + int SPDIFmode; // 0:MPEG/AC3 data on digital S/PDIF out 1:IEC956 data on digital S/PDIF out +}; + +#define MAX_PTS 256 + +typedef struct PTSRecord { + int begin; + int end; + int size; + u32 LastAddr; + u32 AddrB[MAX_PTS]; + u32 AddrE[MAX_PTS]; + u32 PTS[MAX_PTS]; +} PTSStorage; + +#define DVB_DEVS_MAX 9 + +typedef struct dvb_devs_s { + int num; + int tab[DVB_DEVS_MAX]; + int max_users[DVB_DEVS_MAX]; + int max_writers[DVB_DEVS_MAX]; +} dvb_devs_t; + +struct cvdv_cards { +#ifdef DVB + struct dvb_device dvb_dev; + dvb_demux_t demux; +#endif + struct cvdv_cards *next; + void *margi; + struct bus_operations *bus; + u_char scl; + u_char sda; + int i2c_addr; + u32 VideoESSize; + u32 AudioESSize; + u32 VideoPESSize; + u32 DataDumpSize; + u32 AudioPESSize; + u32 NaviBankSize; + int currentType; + ringbuffy rbufA; + ringbuffy rbufB; + int use_ringA; + int use_ringB; + int nonblock; + u8 *addr; + unsigned int size; + unsigned int minor; + struct DRAMBlock *DRAMFirstBlock; + u32 DRAMSize; + struct OSDPicture OSD; + int DMAABusy; // Is the DMA A currently in use? + int DMABBusy; // Is the DMA B currently in use? + int IntInstalled; // is the card interrupt routine installed? + int ChannelBuffersAllocated; // Are the channel buffers for the decoder allocated? + u32 VideoES; // 21 bit word address of the allocated channel + u32 AudioES; // 21 bit word address of the allocated channel + u32 VideoPES; // 21 bit word address of the allocated channel + u32 DataDump; // 21 bit word address of the allocated channel + u32 AudioPES; // 21 bit word address of the allocated channel + u32 NaviBank; // 21 bit word address of the allocated channel + int FrameBuffersAllocated; // Are the frame buffers for the decoder allocated? + u32 FrameStoreLuma1; // 21 bit word address of the allocated frame + u32 FrameStoreChroma1; // 21 bit word address of the allocated frame + u32 FrameStoreLuma2; // 21 bit word address of the allocated frame + u32 FrameStoreChroma2; // 21 bit word address of the allocated frame + u32 FrameStoreLumaB; // 21 bit word address of the allocated frame + u32 FrameStoreChromaB; // 21 bit word address of the allocated frame + int DecoderOpen; // Is the Decoder initialized? + u16 AuxFifo[FIFO_MASK + 1]; // Auxiliary Fifo Data + int AuxFifoHead; // Auxiliary Fifo Position + int AuxFifoTail; // Auxiliary Fifo Position + u16 DataFifo[FIFO_MASK + 1]; // Data Fifo Data + int DataFifoHead; // Data Fifo Position + int DataFifoTail; // Data Fifo Position + int FifoALast; // last used thread of FIFO A + int FifoBLast; // last used thread of FIFO B + videosystem videomode; // current video output mode, PAL or NTSC + struct StreamInfo stream; // header information of the current stream + struct StreamSetup setup; // should be filled bevor sending data, but default is OK + int AuxFifoExt; // used by Aux FIFO parser + int AuxFifoLayer; // " " " " " + int AudioInitialized; // Is the Audio set up? + int AudioOldMode; // remainder of the previous mode while trickmodes, or -1 + int open; // is the 64017 initialized and the video out active? + int closing; // 1 if char device closed, but DMA still running + int startingV; // 1 if card is waiting for the Video ES buffer to fill up, to start the decoder + int startingA; // 1 if card is waiting for the Audio ES buffer to fill up, to start the decoder + int startingDVDV; // 1 if card is waiting for the Video ES buffer to fill up, to start the decoder + int startingDVDA; // 1 if card is waiting for the Audio ES buffer to fill up, to start the decoder + int channelrun; // 1 if channel has been started by the host + int fields; // counter of video fields, debugging only + struct CSS css; // CSS data + u32 NaviPackAddress; // Read address of the Navi Pack Buffer + wait_queue_head_t wqA; + wait_queue_head_t wqB; + u8 navibuffer[NAVIBUFFERSIZE]; + int navihead; + int navitail; + int intdecodestatus; + int showvideo; + int videodelay; + int videodelay_last; + int videoskip; + int videoskip_last; + int videosync; + int videoslow; + int videoslow_last; + int videoffwd; + int videoffwd_last; + PTSStorage VideoPTSStore; + PTSStorage AudioPTSStore; + u32 LastAddr; + u32 VPTS; + u32 oldVPTS; + long VSCR; + u32 APTS; + u32 oldAPTS; + int scrset; + long ASCR; + long SyncTime; + int paused; + u16 lastvattr; + u16 lastaattr; + u8 reg07B; // mirrors of write-only register + u8 reg08F; + u8 reg090; + u8 reg091; + u8 reg092; + u8 reg093; + u8 highlight[10]; // content of registers 1C0 thru 1C0, to be written after next BAV int. + int highlight_valid; // if 1 + int do_flush; // if 1, send flush packet after last transfer done + int hasZV; +#ifdef NOINT + struct timer_list timer; + spinlock_t timelock; +#endif + +#ifdef DVB + dvb_devs_t *dvb_devs; + int users[DVB_DEVS_MAX]; + int writers[DVB_DEVS_MAX]; + + dmxdev_t dmxdev; + boolean video_blank; + struct videoStatus videostate; + struct audioStatus audiostate; + int dvb_registered; + char demux_id[16]; + dmx_frontend_t mem_frontend; + ipack tsa; + ipack tsv; +#endif + + int svhs; + int composite; +}; + +extern u8 FlushPacket[32]; + +extern struct cvdv_cards *first_card; +extern struct cvdv_cards *minorlist[MAXDEV]; + +void DecoderStreamReset(struct cvdv_cards *card); + +void DecoderSetupReset(struct cvdv_cards *card); + +void DecoderCSSReset(struct cvdv_cards *card); + +void card_init(struct cvdv_cards *card, unsigned int minor); + +#endif /* CARDBASE_H */ diff -Nru a/drivers/media/video/margi/crc.c b/drivers/media/video/margi/crc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/crc.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,78 @@ +/* + crc.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/*--------------------------------------------------------- + +Cyclic Redundancy Check 16 and 32 Bit + +Christian Wolff, 19990122 + +---------------------------------------------------------*/ + +#define __NO_VERSION__ + +#include "crc.h" + +unsigned short crc_16_table[256]; +unsigned long crc_32_table[256]; + +// generate the tables of CRC-16 and CRC-32 remainders for all possible bytes +void gen_crc_table() +{ + register int i, j; + register unsigned short crc16; + register unsigned long crc32; + for (i = 0; i < 256; i++) { + crc16 = (unsigned short) i << 8; + crc32 = (unsigned long) i << 24; + for (j = 0; j < 8; j++) { + if (crc16 & 0x8000) + crc16 = (crc16 << 1) ^ POLYNOMIAL_16; + else + crc16 = (crc16 << 1); + if (crc32 & 0x80000000L) + crc32 = (crc32 << 1) ^ POLYNOMIAL_32; + else + crc32 = (crc32 << 1); + } + crc_16_table[i] = crc16; + crc_32_table[i] = crc32; + } +} + +// update the CRC on the data block one byte at a time +unsigned short update_crc_16_block(unsigned short crc, + char *data_block_ptr, + int data_block_size) +{ + register int i; + for (i = 0; i < data_block_size; i++) + crc = update_crc_16(crc, *data_block_ptr++); + return crc; +} + +unsigned long update_crc_32_block(unsigned long crc, char *data_block_ptr, + int data_block_size) +{ + register int i; + for (i = 0; i < data_block_size; i++) + crc = update_crc_32(crc, *data_block_ptr++); + return crc; +} diff -Nru a/drivers/media/video/margi/crc.h b/drivers/media/video/margi/crc.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/crc.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,56 @@ +/* + crc.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/*--------------------------------------------------------- + +Cyclic Redundancy Check 16 and 32 Bit - Header File + +Christian Wolff, 19990122 + +---------------------------------------------------------*/ + +#ifndef _CRC_H_ +#define _CRC_H_ + +// 16 Bit CCITT standard polynomial x^16 + x^12 + x^5 + x^1 + x^0 +#define POLYNOMIAL_16 0x1021 +// 32 Bit standard polynomial x^32 + x^26 + x^23 + x^22 + x^16 + +// x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 +#define POLYNOMIAL_32 0x04C11DB7L + +#define CRC_INIT_16 0xFFFF +#define CRC_INIT_32 0xFFFFFFFFL + +#define update_crc_16(crc, data) ((crc << 8) ^ crc_16_table[((int)(crc >> 8 ) ^ (data)) & 0xFF]) +#define update_crc_32(crc, data) ((crc << 8) ^ crc_32_table[((int)(crc >> 24) ^ (data)) & 0xFF]) + +extern unsigned short crc_16_table[256]; +extern unsigned long crc_32_table[256]; + +extern void gen_crc_table(void); + +extern unsigned short update_crc_16_block(unsigned short crc, + char *data_block_ptr, + int data_block_size); +extern unsigned long update_crc_32_block(unsigned long crc, + char *data_block_ptr, + int data_block_size); + +#endif diff -Nru a/drivers/media/video/margi/cvdv.c b/drivers/media/video/margi/cvdv.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cvdv.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,1985 @@ +/* + cvdv.c + + Copyright (C) Christian Wolff + Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + ///////////////////////////////////////////////////////////////////// + // // + // Driver for the Convergence Digital Video decoder card (pci) // + // with L64017, L64021, PCM1723, and Bt864/Bt865 chipset // + // (c) Christian Wolff 19990209 for convergence integrated media // + // // +///////////////////////////////////////////////////////////////////// + +// Convergence CV2300i +#define __NO_VERSION__ + +#include +#include "cvdv.h" +#include "ost/osd.h" +#include "i2c.h" + + ////////////////////// + // global variables // +////////////////////// + +// Our major device number +unsigned int major_device_number; + + +// my little random function for memory test +uint16_t rnd_seed; +uint16_t rnd(uint16_t range) +{ // returns random 0..(range-1) range<=872 + uint32_t b = 75 * (rnd_seed + 1) - 1; + rnd_seed = (uint16_t) (b & 0xFFFF); + return ((b * range) / 0xFFFF) - ((b / 0xFFFF) * range); +} +void rnd_omize(void) +{ + rnd_seed = (uint16_t) jiffies; +} + +static char *cimlogo[] = { +".............................................", +".............................................", +"......................###....................", +".....................#####...................", +".....................######..................", +"..............#......#####...................", +"...........#####....######...................", +".........########...######...................", +"........########....######...................", +".......#########...######....................", +"......########.....######...####.............", +".....#######.......#####...#####.............", +".....######.......######...######............", +"....#######.......######....######...........", +"....######........######....######...........", +"....#####........######......#####...........", +"...######........######......#####...........", +"...#####.........######......######..........", +"...#####.........#####.......######..........", +"...#####........######........#####..........", +"...#####........######.......######..........", +"...#####........#####.........#####..........", +"...#####.......######........######..........", +"...#####.......######........#####...........", +"...######.......####.........#####...........", +"....#####........##.........######...........", +"....######..................######...........", +"....######.................######............", +".....#######..............######.....#####...", +".....########............#######....#######..", +"......#########........########.....#######..", +".......#######################......########.", +"........#####################.......#######..", +"..........#################.........#######..", +"............#############............#####...", +"...............#.#####.................##....", +".............................................", +"............................................." +}; + + ///////////////////////////////////////////// + // // + // Controlling the L64021 MPEG-2 Decoder // + // // +///////////////////////////////////////////// + +int OSDTest(struct cvdv_cards *card) +{ + int i, j, col, x0, y0, x1, y1,aspx; + uint8_t b; + + + if (!card->OSD.open) + return -2; + + OSDQuery(card, &x0, &y0, &x1, &y1, &aspx); + OSDShow(card); + OSDSetColor(card, 0, 0, 0, 0, 0, 0, 0); + OSDSetColor(card, 1, 128, 255, 255, 0, 0, 0); + for ( i = 0; i < cimlogo_width; i++){ + for ( j = 0; j < cimlogo_height; j++){ + b = cimlogo[j][i]; + col = (b == '#') ? 1: 0; + OSDSetPixel(card, x0+i, y0+j, col); + } + } + + return 0; +} + + +void SetVideoSystem(struct cvdv_cards *card) +{ + uint8_t reg; + + // set the hsync and vsync generators in the L64017 according to the video standard + reg = read_indexed_register(card, IIO_VIDEO_CONTROL1); + reg &= ~0x03; + switch (card->videomode) { + case PAL: // 864*625*50Hz = 27MHz, 25fps + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x41 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x96); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0x13); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x54); + reg |= VMS_PAL; + break; + case PALN: + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0xa1 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x96); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0x13); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x54); + reg |= VMS_PAL; + break; + + case PALNc: + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x81 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x8c); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x28); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xed); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43); + reg |= VMS_PAL; + break; + + case NTSC: // 858*525*59.94006Hz = 27MHz, 29.97fps + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x01 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x1c); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x3e); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0xf8); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe0); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43); + reg |= VMS_NTSC; + break; + + case PALM: + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x01 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x4e); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x4a); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe1); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43); + reg |= VMS_PAL; + break; + + case NTSC60: // 857*525*60.010002Hz = 27MHz, 30fps + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x21 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x1c); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x3e); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0xf8); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe0); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43); + reg |= VMS_NTSC; + break; + + case PALM60: + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x61 | 0x0a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x4e); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x4a); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe1); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43); + reg |= VMS_PAL; + break; + + case PAL60: + break; + } + write_indexed_register(card, IIO_VIDEO_CONTROL1, reg); + // set the pixel generators according to the video standard + L64021Setup(card); +} + +int SetVideoAttr(struct cvdv_cards *card, uint16_t vattr) +{ + uint8_t video_compression_mode; + uint8_t tv_system; + uint8_t aspect_ratio; + uint8_t display_mode; + uint8_t line_21_switch_1; + uint8_t line_21_switch_2; + uint8_t source_picture_resolution; + uint8_t source_picture_letterboxed; + uint8_t reserved; + uint8_t film_camera_mode; + uint16_t hsize, vsize; + if (vattr != card->lastvattr) { + video_compression_mode = (vattr >> 14) & 0x03; + tv_system = (vattr >> 12) & 0x03; + aspect_ratio = (vattr >> 10) & 0x03; + display_mode = (vattr >> 8) & 0x03; + line_21_switch_1 = (vattr >> 7) & 0x01; + line_21_switch_2 = (vattr >> 6) & 0x01; + source_picture_resolution = (vattr >> 3) & 0x07; + source_picture_letterboxed = (vattr >> 2) & 0x01; + reserved = (vattr >> 1) & 0x01; + film_camera_mode = (vattr >> 0) & 0x01; + card->videomode = + ((tv_system == 0) ? NTSC : ((tv_system == 1) ? + PAL : PAL)); + SetVideoSystem(card); + hsize = + ((source_picture_resolution == 0) ? 720 + : ((source_picture_resolution == 1) ? 702 : 352)); + vsize = ((source_picture_resolution == 3) + ? ((tv_system == 0) ? 240 : 288) + : ((tv_system == 0) ? 480 : 576)); + if (DecoderOpen + (card, hsize, vsize, ((aspect_ratio) ? 3 : 2), + ((video_compression_mode) ? 0 : 1), + source_picture_letterboxed, tv_system)) { + MDEBUG(0, + ": Video Decoder Open failed: On-card memory insufficient for frame stores\n"); + } + card->lastvattr = vattr; + } else { + MDEBUG(0, + ": Video attribute not set, equal to previous one.\n"); + } + return 0; +} + +int SetAudioAttr(struct cvdv_cards *card, uint16_t aattr) +{ + uint8_t audio_coding_mode; + uint8_t multichannel_extension; + uint8_t audio_type; + uint8_t audio_application_mode; + uint8_t quantization_drc; + uint8_t fs; + uint8_t reserved; + uint8_t num_audio_ch; + if (aattr) { + if (aattr != card->lastaattr) { + audio_coding_mode = (aattr >> 13) & 0x07; + multichannel_extension = (aattr >> 12) & 0x01; + audio_type = (aattr >> 10) & 0x03; + audio_application_mode = (aattr >> 8) & 0x03; + quantization_drc = (aattr >> 6) & 0x03; + fs = (aattr >> 4) & 0x03; + reserved = (aattr >> 3) & 0x01; + num_audio_ch = (aattr >> 0) & 0x07; + switch (audio_coding_mode) { + case 0: // AC-3 + card->setup.audioselect = audio_AC3; + break; + case 2: // MPEG Audio + card->setup.audioselect = audio_MPEG; + break; + case 3: // MPEG Audio with ext. + card->setup.audioselect = audio_MPEG_EXT; + break; + case 4: // Linear Pulse Code Modulation LPCM + card->setup.audioselect = audio_LPCM; + break; + case 6: // DTS + card->setup.audioselect = audio_DTS; + break; + case 7: // SDDS + card->setup.audioselect = audio_SDDS; + break; + } + DecoderPrepareAudio(card); + AudioInit(card, ((fs) ? 96 : 48), + ((audio_application_mode == 2) ? 1 : 0)); + } else { + MDEBUG(0, + ": Audio attribute not set, equal to previous one.\n"); + } + } else { + card->setup.audioselect = audio_none; + DecoderPrepareAudio(card); + } + card->lastaattr = aattr; + return 0; +} + +int Prepare(struct cvdv_cards *card) +{ + int err, h; + struct StreamSetup *setup = &card->setup; + + if (!card->ChannelBuffersAllocated) { + + DecoderStreamReset(card); + if (setup->streamtype == stream_none) { + setup->streamtype = stream_PS; + } + + if (setup->audioselect == audio_none) { + setup->audioselect = audio_MPEG; + } + + DecoderPrepareAudio(card); + AudioMute(card, 1); + DecoderPrepareVideo(card); + VideoSetBackground(card, 1, 0, 0, 0); // black + + switch (setup->streamtype) { + default: + case stream_none: // unknown stream! + MDEBUG(0, + ": Video Decoder Prepare failed: unknown stream type\n"); + return -ENODEV; // not an MPEG stream! + case stream_ES: // Elementary Stream + err = DecoderPrepareES(card); + break; + case stream_PES: // Packetized Elementary Stream + err = DecoderPreparePES(card); + break; + case stream_PS: // MPEG-1 System Stream / MPEG-2 Program Stream + err = DecoderPreparePS(card, -1, 0, 0, 0, 0, 0); + break; + case stream_DVD: // DVD Stream + err = DecoderPreparePS(card, 0, 0, 0, 0, 3, 1); + break; + } + if (err) { // insufficient memory + MDEBUG(0, + ": Video Decoder Prepare failed: no kernel memory, please reboot if possible\n"); + CloseCard(card); + return -ENODEV; + } + } + + // Set up the Video Decoder as we have the stream information + if ((!card->FrameBuffersAllocated) + && (card->ChannelBuffersAllocated) && (card->stream.sh.valid)) { + // Automatic PAL/NTSC-switch according to MPEG-Source + h = card->stream.vsize; + if (h < 480) + h *= 2; // catch quarter sized images + printk(KERN_INFO LOGNAME ": Video mode: %s\n", + ((h == 480) ? "NTSC" : "PAL")); + card->videomode = ((h == 480) ? NTSC : PAL); + SetVideoSystem(card); + // Open the Video Decoder with the parameters retreived from the stream + if ( + (err = + DecoderOpen(card, card->stream.hsize, + card->stream.vsize, + card->stream.sh.aspectratio, + !card->stream.MPEG2, 0, + (card->stream.hsize > 480)))) { // TODO: include vbvbuffersize + MDEBUG(0, + ": Video Decoder Open failed: %s\n", + ((err == 1) ? + "Picture size too big (>1440 pixel wide)" : + "On-card memory insufficient for frame stores")); + CloseCard(card); + return -ENODEV; // picture too big or insufficient memory + } + MDEBUG(1, ": Ready to go\n"); + card->startingV = 1; // tell the card to start playing as soon as ES-buffers are sufficiently full + card->startingA = 1; // tell the card to start playing as soon as ES-buffers are sufficiently full + } + + + return 0; +} + +int SetSCRstart(struct cvdv_cards *card, uint32_t SCR_base) +{ + uint32_t SCR_compare; + uint32_t SCR_compareA; + uint32_t SCR_compareV; + if (card->startingV) { + MDEBUG(0, ": SCR in DVD Pack: 0x%08X\n", + SCR_base); + card->startingV = 0; + card->startingA = 0; + DecoderMaskByte(card, 0x007, 0xD2, 0xD2); // Set 0x010, halt SCR counter + SCR_compare = SCR_base + 000; + if (SCR_base < 900) + SCR_base = 0; + else + SCR_base -= 900; + //DecoderWriteDWord(card,0x009,SCR_base); // Set SCR counter + DecoderWriteByte(card, 0x009, SCR_base & 0xFF); // Set SCR counter + DecoderWriteByte(card, 0x00A, (SCR_base >> 8) & 0xFF); + DecoderWriteByte(card, 0x00B, (SCR_base >> 16) & 0xFF); + DecoderWriteByte(card, 0x00C, (SCR_base >> 24) & 0xFF); + DecoderMaskByte(card, 0x011, 0x03, 0x02); // compare, not capture + MDEBUG(0, ": SCR compare value: 0x%08X\n", + SCR_compare); + //DecoderWriteDWord(card,0x00D,SCR_compare); // Set Compare register + DecoderWriteByte(card, 0x00D, SCR_compare & 0xFF); // Set Compare register + DecoderWriteByte(card, 0x00E, (SCR_compare >> 8) & 0xFF); + DecoderWriteByte(card, 0x00F, (SCR_compare >> 16) & 0xFF); + DecoderWriteByte(card, 0x010, (SCR_compare >> 24) & 0xFF); + //DecoderWriteDWord(card,0x014,SCR_compare); // Set audio compare reg. + DecoderWriteByte(card, 0x014, SCR_compare & 0xFF); // Set audio compare reg. + DecoderWriteByte(card, 0x015, (SCR_compare >> 8) & 0xFF); + DecoderWriteByte(card, 0x016, (SCR_compare >> 16) & 0xFF); + DecoderWriteByte(card, 0x017, (SCR_compare >> 24) & 0xFF); + DecoderSetByte(card, 0x013, 0x03); // Video and Audio start on cmp. + //DecoderSetVideoPanic(card,0,DecoderGetVideoESSize(card)/4); // video panic at 25 percent + VideoSetBackground(card, 1, 0, 0, 0); // black + SCR_base = DecoderReadByte(card, 0x009); + SCR_base = + SCR_base | ((uint32_t) DecoderReadByte(card, 0x00A) << 8); + SCR_base = + SCR_base | ((uint32_t) DecoderReadByte(card, 0x00B) << 16); + SCR_base = + SCR_base | ((uint32_t) DecoderReadByte(card, 0x00C) << 24); + SCR_compareA = DecoderReadByte(card, 0x014); + SCR_compareA = + SCR_compareA | ((uint32_t) DecoderReadByte(card, 0x015) << + 8); + SCR_compareA = + SCR_compareA | ((uint32_t) DecoderReadByte(card, 0x016) << + 16); + SCR_compareA = + SCR_compareA | ((uint32_t) DecoderReadByte(card, 0x017) << + 24); + SCR_compareV = DecoderReadByte(card, 0x00D); + SCR_compareV = + SCR_compareV | ((uint32_t) DecoderReadByte(card, 0x00E) << + 8); + SCR_compareV = + SCR_compareV | ((uint32_t) DecoderReadByte(card, 0x00F) << + 16); + SCR_compareV = + SCR_compareV | ((uint32_t) DecoderReadByte(card, 0x010) << + 24); + if (DecoderReadByte(card, 0x013) & 0x03) + MDEBUG(1,": SCR 0x%08X, videocmp=0x%08X, audiocmp=0x%08X %02X\n", + SCR_base, SCR_compareV, SCR_compareA, + DecoderReadByte(card, 0x013)); + DecoderMaskByte(card, 0x007, 0xD2, 0xC2); // Del 0x010, SCR counter run + } + return 0; +} + +int DecoderWriteBlock(struct cvdv_cards *card, uint8_t * data, int size, + int initial, int setSCR) +{ + //int a,v,as,vs,ap,vp; + int res; + uint32_t SCR_base; + int co = 0; + // uint32_t SCR_compare; + res = 0; + + Prepare(card); + + if (size > 0) { + + if (!card->use_ringA) + MargiSetBuffers(card, NBBUF*CHANNELBUFFERSIZE,0); + + if (card->startingDVDV || card->startingDVDA) + setSCR = 1; + + if (initial) { + DecoderStreamReset(card); + //TODO stop and start channel interface + setSCR = 1; + } + + if (setSCR) { + SCR_base = ParseSCR(data); + SetSCR(card, SCR_base); + } + card->DMAABusy = 0; + while (((res = MargiPushA(card, size, data)) < size) + && co < 1000) { + data+=res; + size-=res; + co++; + MDEBUG(2, + ": DecoderWriteBlock - buffers only filled with %d instead of %d bytes\n",res, size); + if (card->DMAABusy){ + interruptible_sleep_on(&card->wqA); + } + } + + if (card->startingDVDV) { + card->startingDVDV = 0; + card->startingV = 1; + DecoderStartDecode(card); + } + if (card->startingDVDA) { + card->startingDVDA = 0; + card->startingA = 1; + AudioSetPlayMode(card, MAUDIO_PLAY); + } + } + return 0; +} + + + + + + ////////////////////////////// + // // + // Char Device Procedures // + // // +////////////////////////////// +static long margi_write(struct cvdv_cards *card, const char *data, + unsigned long count, int nonblock) +{ + + int res; + long int out=0; + int free; + + free = ring_write_rest(&(card->rbufA)); + + if (card != NULL) { + card->nonblock = nonblock; + if (count > 0) { // Do we have data? + if ((res = Prepare(card))) + return res; + if (!card->use_ringA) + MargiSetBuffers(card, NBBUF*CHANNELBUFFERSIZE, + 0); + if (!nonblock && + !wait_event_interruptible( + card->wqA, + ring_write_rest(&(card->rbufA)) >count )){ + + out = MargiPushA(card, count, + data); + } else { + out = MargiPushA(card, count, data); + } + } + return out; + } else { + MDEBUG(0, + ": Video Decoder Prepare failed: device with this minor number not found\n"); + return -ENODEV; // device with this minor number not found + } +} + + +static long margi_write_audio(struct cvdv_cards *card, const char *data, + unsigned long count, int nonblock) +{ + struct StreamSetup *setup = &card->setup; + + int res; + long int out=0; + int free; + + free = ring_write_rest(&(card->rbufB)); + + if (card != NULL) { + card->nonblock = nonblock; + + if (count > 0) { // Do we have data? + if ((res = Prepare(card))) + return res; + if ((setup->streamtype == stream_ES) + || (setup->streamtype == stream_PES)){ + if (!card->use_ringB) + MargiSetBuffers(card, NBBUF* + CHANNELBUFFERSIZE,1); + if (!nonblock && + !wait_event_interruptible( + card->wqB, + ring_write_rest(&(card->rbufB)) + > count)){ + out = MargiPushB(card, count, + data); + } else { + out = MargiPushB(card, count, data); + } + } + } + return out; + } else { + MDEBUG(0, + ": Video Decoder Prepare failed: device with this minor number not found\n"); + return -ENODEV; // device with this minor number not found + } +} + +void pes_write(uint8_t *buf, int count, void *priv) +{ + struct cvdv_cards *card = (struct cvdv_cards *) priv; + + margi_write(card, buf, count, 0); +} + + +static ssize_t PSwrite(struct file *file, const char *data, size_t count, + loff_t * offset) +{ + struct cvdv_cards *card = + minorlist[MINOR(file->f_dentry->d_inode->i_rdev) % MAXDEV]; // minor number modulo 16 + return margi_write(card, data, count, file->f_flags&O_NONBLOCK); +} + +static unsigned int PSpoll(struct file *file, poll_table * table) +{ + struct cvdv_cards *card = + minorlist[MINOR(file->f_dentry->d_inode->i_rdev) % MAXDEV]; // minor number modulo 16 + if (card != NULL) { + poll_wait(file, &card->wqA , table); + if ( !card->rbufA.buffy || ring_write_rest(&(card->rbufA)) ) + return (POLLOUT | POLLWRNORM); + else { + return 0; + } + } else + return POLLERR; +} + +static unsigned int poll_audio(struct file *file, poll_table * table) +{ + struct cvdv_cards *card = + minorlist[MINOR(file->f_dentry->d_inode->i_rdev) % MAXDEV]; // minor number modulo 16 + if (card != NULL) { + poll_wait(file, &card->wqB, table); + if ( !card->rbufB.buffy || ring_write_rest(&(card->rbufB)) ) + return (POLLOUT | POLLWRNORM); + else { + return 0; + } + } else + return POLLERR; +} + +static int +OSD_DrawCommand(struct cvdv_cards *card,osd_cmd_t *dc) +{ + + switch (dc->cmd) { + case OSD_Close: + MDEBUG(1,": OSD Close\n"); + return OSDClose(card); + case OSD_Open: // Open(x0,y0,x1,y1,BitPerPixel(2/4/8),mix(0..15)) + return OSDOpen(card, dc->x0, + dc->y0, dc->x1, + dc->y1, + dc->color & 0x0F, + (dc->color >> 4) & + 0x0F); + case OSD_Show: + return OSDShow(card); + case OSD_Hide: + return OSDHide(card); + case OSD_Clear: + return OSDClear(card); + case OSD_Fill: // Fill(color) + return OSDFill(card, dc->color); + case OSD_SetColor: // SetColor(color,R(x0),G(y0),B(x1),opacity(y1)) + return (OSDSetColor + (card, dc->color, dc->x0, + dc->y0, dc->x1, 0, + (dc->y1 != 255), + (dc->y1 == 0)) >= 0); + case OSD_SetPalette:// SetPalette(firstcolor{color},lastcolor{x0},data) + return OSDSetPalette(card, + dc->color, + dc->x0, (uint8_t *) + dc->data); + case OSD_SetTrans: // SetTrans(transparency{color}) + return OSDSetTrans(card, + (dc->color >> 4) + & 0x0F); + case OSD_SetPixel: // SetPixel(x0,y0,color) + return OSDSetPixel(card, dc->x0, + dc->y0, + dc->color); + case OSD_GetPixel: // GetPixel(x0,y0); + return OSDGetPixel(card, dc->x0, + dc->y0); + case OSD_SetRow: // SetRow(x0,y0,x1,(uint8_t*)data) + return OSDSetRow(card, dc->x0, + dc->y0, dc->x1, + (uint8_t *) dc->data); + case OSD_SetBlock: // SetBlock(x0,y0,x1,y1,(uint8_t*)data) + return OSDSetBlock(card, dc->x0, + dc->y0, dc->x1, + dc->y1, + dc->color, + (uint8_t *) + dc->data); + case OSD_FillRow: // FillRow(x0,y0,x1,color) + return OSDFillRow(card, dc->x0, + dc->y0, dc->x1, + dc->color); + case OSD_FillBlock: // FillRow(x0,y0,x1,y1,color) + return OSDFillBlock(card, dc->x0, + dc->y0, dc->x1, + dc->y1, + dc->color); + case OSD_Line: // Line(x0,y0,x1,y1,color); + return OSDLine(card, dc->x0, + dc->y0, dc->x1, + dc->y1, dc->color); + case OSD_Query: // Query(x0,y0,x1,y1,aspect(color:11) + return OSDQuery(card, &dc->x0, + &dc->y0, &dc->x1, + &dc->y1, + &dc->color); + case OSD_Test: + return OSDTest(card); + default: + return -EINVAL; + } +} + + +static int PSioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV]; // minor number modulo 16 + osd_cmd_t *dc; + struct decodercmd *command; + uint16_t attr; + + if (card != NULL) { + if (_IOC_TYPE(cmd) == CVDV_IOCTL_MAGIC) + switch (_IOC_NR(cmd)) { + case IOCTL_DRAW: // Drawing commands + dc = (osd_cmd_t *) arg; + return OSD_DrawCommand(card,dc); + break; + case IOCTL_DECODER: + command = (struct decodercmd *) arg; + switch (command->cmd) { + + case Decoder_CSS: + /* + return DecoderCSS(card, + command->param1, + command->data1); + */ + break; + + case Decoder_Set_Videosystem: + MDEBUG(1,": -- Decoder_Set_Videosystem\n"); + card->videomode = + (videosystem) command->param1; + SetVideoSystem(card); + return 0; + break; + + case Decoder_Set_Streamtype: + MDEBUG(1,": -- Decoder_Set_Streamtype\n"); + card->setup.streamtype = + (stream_type) command->param1; + return 0; + break; + + case Decoder_Set_Audiotype: + MDEBUG(1,": -- Decoder_Set_Audiotype\n"); + card->setup.audioselect = + (audio_type) command->param1; + DecoderPrepareAudio(card); + return 0; + break; + + case Decoder_Set_VideoStreamID: + MDEBUG(1,": -- Decoder_Set_VideoStreamID\n"); + card->setup.videoID = + command->param1; + DecoderPrepareVideo(card); + return 0; + break; + + case Decoder_Set_AudioStreamID: + MDEBUG(1,": -- Decoder_Set_AudioStreamID 0x%02X 0x%02X\n", + command->param1,command->param2); + card->setup.audioID = + command->param1; + card->setup.audioIDext = + command->param2; + attr = card->lastaattr; + DecoderSelectAudioID(card); + card->lastaattr = attr; + return 0; + break; + + case Decoder_Still_Put: + return DecoderShowStill(card, + command-> + param1, + command-> + param2, + command-> + data1, + command-> + data2); + break; + + case Decoder_Still_Get: + return DecoderGetStill(card, + &command-> + param1, + &command-> + param2, + command-> + data1, + command-> + data2); + break; + + case Decoder_Pause: // pause{param1} 0=run 1=pause 2=toggle + if (command->param1 == 2) { + if (card->paused) + DecoderUnPause + (card); + else + DecoderPause(card); + } else { + if (!command->param1) + DecoderUnPause + (card); + else + DecoderPause(card); + } + return 0; + + /* Too buggy + case Decoder_FFWD: // pause{param1} =normal 1=ffwd 2=toggle + if (command->param1 == 2) { + if (card->videoffwd) + card->videoffwd = 0; + else + card->videoffwd = 3; + } else { + if (!command->param1) + card->videoffwd = 0; + else + card->videoffwd = 3; + } + return 0; + + case Decoder_Slow: // pause{param1} =normal 1=slow 2=toggle + if (command->param1 == 2) { + if (card->videoslow) + card->videoslow = 0; + else + card->videoslow = 4; + } else { + if (!command->param1) + card->videoslow = 0; + else + card->videoslow = 4; + } + return 0; + */ + case Decoder_Highlight: // active{param1}, color information(SL_COLI or AC_COLI){data1[4]}, button position(BTN_POSI){data2[6]} + return DecoderHighlight(card, + command-> + param1, + command-> + data1, + command-> + data2); + case Decoder_SPU: // stream{param1}, active{param2} + return DecoderSPUStream(card, + command-> + param1, + command-> + param2); + case Decoder_SPU_Palette: // length{param1}, palette{data1} + return DecoderSPUPalette(card, + command-> + param1, + command-> + data1); + case Decoder_GetNavi: // data1 will be filled with PCI or DSI pack, and 1024 will be returned + return DecoderGetNavi(card, + command-> + data1); + case Decoder_SetKaraoke: // Vocal1{param1}, Vocal2{param2}, Melody{param3} + return DecoderKaraoke(card, + command-> + param1, + command-> + param2, + command-> + param3); + case Decoder_Set_Videoattribute: + MDEBUG(1,": -- Decoder_Set_Videoattribute\n"); + if (!card->ChannelBuffersAllocated) { + DecoderStreamReset(card); + MargiFlush(card); + + card->setup.streamtype = + stream_DVD; + card->setup.videoID = 0; + DecoderPrepareVideo(card); + DecoderPreparePS(card, 0, + 0, 2, 2, + 3, 1); + } + + SetVideoAttr(card, + command->param1); + card->startingDVDV = 1; +// tell the card to start playing as soon as ES-buffers are sufficiently full + return 0; + case Decoder_Set_Audioattribute: + MDEBUG(1,": -- Decoder_Set_Audioattribute\n"); + SetAudioAttr(card, + command->param1); + card->startingDVDA = + ((card->setup.audioselect != + audio_none) + && (card->setup.audioselect != audio_disable)); // tell the card to start playing as soon as ES-buffers are sufficiently full + return 0; + case Decoder_WriteBlock: // DVD-Sector{data1}, sectorsize{param1{2048}}, initialsector{param2{bool}}, set_SCR{param3} + return DecoderWriteBlock(card, + command-> + data1, + command-> + param1, + command-> + param2, + command-> + param3); + default: + return -EINVAL; + } + default: + return -EINVAL; + } else + return -EINVAL; + } else { + MDEBUG(0, + ": Video Decoder Prepare failed: device with this minor number not found\n"); + return -ENODEV; // device with this minor number not found + } +} + + +static int PSmmap(struct file *file, struct vm_area_struct *vm) +{ + return -ENODEV; +} + + + +static int margi_open(struct cvdv_cards *card, int flags) +{ + int closed; + + printk("Open card = %p\n", card); + + if (card != NULL) { + MDEBUG(1, ": -- open \n"); + CloseCard(card); + OSDClose(card); + + printk("Card and OSD closed.\n"); +#ifdef NOINT + card->timer.function = Timerfunction; + card->timer.data=(unsigned long) card; + card->timer.expires=jiffies+1; + add_timer(&card->timer); +#endif + printk("Timer added.\n"); + + if (card->open) + MDEBUG(0,": PSopen - already open\n"); + closed = 1; + if (card->open) + closed = 0; + if (closed) { // first open() for this card? + printk("Freeing buffers.\n"); + MargiFreeBuffers(card); + printk("Fade to black.\n"); + VideoSetBackground(card, 1, 0, 0, 0); // black + } + printk("Go\n"); + card->open++; + return 0; + } else { + MDEBUG(0, + ": Video Decoder Prepare failed: device with this minor number not found\n"); + return -ENODEV; // device with this minor number not found + } + +} + + +static int PSopen(struct inode *inode, struct file *file) +{ + struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV]; +#ifdef DVB + if(card) + card->audiostate.AVSyncState=true; +#endif + return margi_open(card, file->f_flags); +} + + +static int all_margi_close(struct cvdv_cards *card) +{ + + if (card != NULL) { + MDEBUG(1, ": -- PSrelease\n"); + if (card->open <= 0) + MDEBUG(1,": PSrelease - not open\n"); + card->open--; + + if (!card->open) { + MDEBUG(1,": PSrelease - last close\n"); + CloseCard(card); // close immediately + } + return 0; + } else { + MDEBUG(0,": Video Decoder Prepare failed:\n"); + return -ENODEV; // device with this minor number not found + } + +} + +static int PSrelease(struct inode *inode, struct file *file) +{ + struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV]; // minor number modulo 16 + return all_margi_close(card); +} + + ////////////////////////// + // // + // Char Device Hookup // + // // +////////////////////////// + +// Hookups for a write-only device, that accepts MPEG-2 Program Stream +struct file_operations cvdv_fileops = { + owner: THIS_MODULE, + write: PSwrite, + poll: PSpoll, + ioctl: PSioctl, + mmap: PSmmap, + open: PSopen, + release: PSrelease, +}; + + +#ifdef DVB + +static inline int +num2type(struct cvdv_cards *card, int num) +{ + if (!card->dvb_devs) + return -2; + if (num>=card->dvb_devs->num) + return -2; + return card->dvb_devs->tab[num]; +} + +static int +dvbdev_open(struct dvb_device *dvbdev, int num, + struct inode *inode, struct file *file) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + int type=num2type(card, num); + int ret=0; + + if (type<0) + return -EINVAL; + + if (card->users[num] >= card->dvb_devs->max_users[num]) + return -EBUSY; + + if ((file->f_flags&O_ACCMODE)!=O_RDONLY) + if (card->writers[num] >= card->dvb_devs->max_writers[num]) + return -EBUSY; + + switch (type) { + case DVB_DEVICE_VIDEO_0: + card->video_blank=true; + card->audiostate.AVSyncState=true; + card->videostate.streamSource=VIDEO_SOURCE_DEMUX; + margi_open(card, file->f_flags); + break; + + case DVB_DEVICE_AUDIO_0: + card->audiostate.AVSyncState=true; + card->audiostate.streamSource=AUDIO_SOURCE_DEMUX; + break; + + case DVB_DEVICE_DEMUX_0: + if ((file->f_flags&O_ACCMODE)!=O_RDWR) + return -EINVAL; + ret=DmxDevFilterAlloc(&card->dmxdev, file); + break; + + case DVB_DEVICE_DVR_0: + card->audiostate.AVSyncState=true; + card->setup.streamtype = stream_PES; + margi_open(card, file->f_flags); + ret=DmxDevDVROpen(&card->dmxdev, file); + break; + + case DVB_DEVICE_OSD_0: + break; + default: + return -EINVAL; + } + if (ret<0) + return ret; + if ((file->f_flags&O_ACCMODE)!=O_RDONLY) + card->writers[num]++; + card->users[num]++; + return ret; +} + +static int +dvbdev_close(struct dvb_device *dvbdev, int num, + struct inode *inode, struct file *file) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + int type=num2type(card, num); + int ret=0; + + if (type<0) + return -EINVAL; + + switch (type) { + case DVB_DEVICE_VIDEO_0: + case DVB_DEVICE_AUDIO_0: + if (card->open) + all_margi_close(card); + break; + + case DVB_DEVICE_DEMUX_0: + ret=DmxDevFilterFree(&card->dmxdev, file); + break; + + case DVB_DEVICE_DVR_0: + ret=DmxDevDVRClose(&card->dmxdev, file); + if (card->open) + all_margi_close(card); + break; + case DVB_DEVICE_OSD_0: + break; + default: + return -EINVAL; + } + if (ret<0) + return ret; + if ((file->f_flags&O_ACCMODE)!=O_RDONLY) + card->writers[num]--; + card->users[num]--; + return ret; +} + + +static ssize_t +dvbdev_write(struct dvb_device *dvbdev, int num, + struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + int type=num2type(card, num); + + switch (type) { + case DVB_DEVICE_VIDEO_0: + if (card->videostate.streamSource!=VIDEO_SOURCE_MEMORY) + return -EPERM; + return margi_write(card, buf, count, + file->f_flags&O_NONBLOCK); + + case DVB_DEVICE_AUDIO_0: + if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY) + return -EPERM; + if ( card->setup.streamtype != stream_PES ) + return -EPERM; + + return margi_write_audio(card, buf, count, + file->f_flags&O_NONBLOCK); + + case DVB_DEVICE_DVR_0: + return DmxDevDVRWrite(&card->dmxdev, file, buf, count, ppos); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static ssize_t +dvbdev_read(struct dvb_device *dvbdev, int num, + struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + int type=num2type(card, num); + + switch (type) { + case DVB_DEVICE_VIDEO_0: + break; + case DVB_DEVICE_AUDIO_0: + break; + case DVB_DEVICE_DEMUX_0: + return DmxDevRead(&card->dmxdev, file, buf, count, ppos); + case DVB_DEVICE_DVR_0: + return DmxDevDVRRead(&card->dmxdev, file, buf, count, ppos); + case DVB_DEVICE_CA_0: + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + + + +static int +dvbdev_ioctl(struct dvb_device *dvbdev, int num, + struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + void *parg=(void *)arg; + int type=num2type(card, num); + uint16_t attr; + + switch (type) { + case DVB_DEVICE_VIDEO_0: + if (((file->f_flags&O_ACCMODE)==O_RDONLY) && + (cmd!=VIDEO_GET_STATUS)) + return -EPERM; + + switch (cmd) { + + case VIDEO_STOP: + DecoderPause(card); + card->videostate.playState = VIDEO_STOPPED; + if (card->videostate.videoBlank) + VideoSetBackground(card, 1, 0, 0, 0); + + + return 0; + + case VIDEO_PLAY: + + if (card->videostate.streamSource== + VIDEO_SOURCE_MEMORY) { + if (card->videostate.playState==VIDEO_FREEZED){ + DecoderUnPause(card); + } else { + DecoderUnPause(card); + } + } + break; + + case VIDEO_FREEZE: + DecoderPause(card); + break; + + case VIDEO_CONTINUE: + if (card->videostate.playState==VIDEO_FREEZED) { + DecoderUnPause(card); + } + break; + + case VIDEO_SELECT_SOURCE: + card->videostate.streamSource=(videoStreamSource_t) arg; + break; + + case VIDEO_SET_BLANK: + card->videostate.videoBlank=(boolean) arg; + break; + + case VIDEO_GET_STATUS: + if(copy_to_user(parg, &card->videostate, + sizeof(struct videoStatus))) + return -EFAULT; + break; + + case VIDEO_GET_EVENT: + return -EOPNOTSUPP; + + case VIDEO_SET_DISPLAY_FORMAT: + { + videoDisplayFormat_t format=(videoDisplayFormat_t) arg; + uint16_t val=0; + + switch(format) { + case VIDEO_PAN_SCAN: + val=VID_PAN_SCAN_PREF; + break; + + case VIDEO_LETTER_BOX: + val=VID_VC_AND_PS_PREF; + break; + + case VIDEO_CENTER_CUT_OUT: + val=VID_CENTRE_CUT_PREF; + break; + + default: + return -EINVAL; + } + + card->videostate.videoFormat=format; + return 0; + } + + case VIDEO_STILLPICTURE: + { + struct videoDisplayStillPicture pic; + + if(copy_from_user(&pic, parg, + sizeof(struct videoDisplayStillPicture))) + return -EFAULT; + + break; + } + + case VIDEO_FAST_FORWARD: + if (card->videostate.streamSource != + VIDEO_SOURCE_MEMORY) + return -EPERM; + card->videoffwd = 3; + break; + + case VIDEO_SLOWMOTION: + if (card->videostate.streamSource!=VIDEO_SOURCE_MEMORY) + return -EPERM; + card->videoslow = arg; + + break; + + case VIDEO_GET_CAPABILITIES: + { + int cap=VIDEO_CAP_MPEG1| + VIDEO_CAP_MPEG2| + VIDEO_CAP_SYS| + VIDEO_CAP_PROG| + VIDEO_CAP_SPU| + VIDEO_CAP_NAVI| + VIDEO_CAP_CSS; + + + if (copy_to_user(parg, &cap, + sizeof(cap))) + return -EFAULT; + break; + } + + case VIDEO_SET_STREAMTYPE: + { + int f = -1; + switch(arg){ + case VIDEO_CAP_MPEG1: + case VIDEO_CAP_MPEG2: + f = stream_PES; + break; + + case VIDEO_CAP_SYS: + case VIDEO_CAP_PROG: + f = stream_PS; + break; + + case VIDEO_CAP_SPU: + case VIDEO_CAP_NAVI: + case VIDEO_CAP_CSS: + f = stream_DVD; + } + card->setup.streamtype = f; + + } + break; + + case VIDEO_SET_ID: + card->setup.videoID = arg; + DecoderPrepareVideo(card); + break; + + case VIDEO_SET_SYSTEM: + card->videomode = (videosystem) arg; + SetVideoSystem(card); + break; + + case VIDEO_SET_HIGHLIGHT: + { + uint8_t data1[4]; + uint8_t data2[6]; + videoHighlight_t vh; + + if(copy_from_user(&vh, parg, sizeof(videoHighlight_t))) + return -EFAULT; + + data1[0] = vh.contrast1; + data1[1] = vh.contrast2; + data1[2] = vh.color1; + data1[3] = vh.color2; + data2[0] = vh.ypos & 0xFF; + data2[1] = (uint8_t) ((vh.ypos >> 1) & 0xFF); + data2[2] = (uint8_t) ((vh.ypos >> 2) & 0xFF); + data2[3] = vh.xpos & 0xFF; + data2[4] = (uint8_t) ((vh.xpos >> 1) & 0xFF); + data2[5] = (uint8_t) ((vh.xpos >> 2) & 0xFF); + return DecoderHighlight(card, vh.active, data1, data2); + break; + } + + case VIDEO_SET_SPU: + { + videoSPU_t spu; + + if(copy_from_user(&spu, parg, sizeof(videoSPU_t))) + return -EFAULT; + + return DecoderSPUStream(card, spu.streamID, spu.active); + break; + } + + case VIDEO_SET_SPU_PALETTE: + { + videoSPUPalette_t spup; + + if(copy_from_user(&spup, parg, sizeof(videoSPUPalette_t))) + return -EFAULT; + + return DecoderSPUPalette(card, spup.length, spup.palette); + break; + } + + case VIDEO_GET_NAVI: + { + videoNaviPack_t navi; + + navi.length = DecoderGetNavi(card, (u8 *)&(navi.data)); + if(copy_to_user(parg, &navi, sizeof(videoNaviPack_t))) + return -EFAULT; + } + break; + + case VIDEO_SET_ATTRIBUTES: + { + if (!card->ChannelBuffersAllocated) { + DecoderStreamReset(card); + MargiFlush(card); + + card->setup.streamtype = stream_DVD; + card->setup.videoID = 0; + DecoderPrepareVideo(card); + DecoderPreparePS(card, 0, 0, 2, 2, 3, 1); + } + + SetVideoAttr(card, arg); + card->startingDVDV = 1; + } + break; + + default: + return -ENOIOCTLCMD; + } + return 0; + + case DVB_DEVICE_AUDIO_0: + if (((file->f_flags&O_ACCMODE)==O_RDONLY) && + (cmd!=AUDIO_GET_STATUS)) + return -EPERM; + + switch (cmd) { + + case AUDIO_STOP: + if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY) + break; + AudioStopDecode(card); + card->audiostate.playState=AUDIO_STOPPED; + break; + + case AUDIO_PLAY: + if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY) + break; + AudioSetPlayMode(card, MAUDIO_PLAY); + card->audiostate.playState=AUDIO_PLAYING; + break; + + case AUDIO_PAUSE: + card->audiostate.playState=AUDIO_PAUSED; + AudioSetPlayMode(card, MAUDIO_PAUSE); + break; + + case AUDIO_CONTINUE: + if (card->audiostate.playState==AUDIO_PAUSED) { + card->audiostate.playState=AUDIO_PLAYING; + AudioSetPlayMode(card, MAUDIO_PLAY); + } + break; + + case AUDIO_SELECT_SOURCE: + card->audiostate.streamSource= + (audioStreamSource_t) arg; + break; + + case AUDIO_SET_MUTE: + { + AudioMute(card, arg); + card->audiostate.muteState=(boolean) arg; + break; + } + + case AUDIO_SET_AV_SYNC: + card->videosync=(boolean) arg; + card->audiostate.AVSyncState=(boolean) arg; + break; + + case AUDIO_SET_BYPASS_MODE: + return -EINVAL; + + case AUDIO_CHANNEL_SELECT: + card->audiostate.channelSelect=(audioChannelSelect_t) arg; + + switch(card->audiostate.channelSelect) { + case AUDIO_STEREO: + break; + + case AUDIO_MONO_LEFT: + break; + + case AUDIO_MONO_RIGHT: + break; + + default: + return -EINVAL; + } + return 0; + + case AUDIO_GET_STATUS: + if(copy_to_user(parg, &card->audiostate, + sizeof(struct audioStatus))) + return -EFAULT; + break; + + case AUDIO_GET_CAPABILITIES: + { + int cap=AUDIO_CAP_LPCM| + AUDIO_CAP_MP1| + AUDIO_CAP_MP2| + AUDIO_CAP_AC3; + + if (copy_to_user(parg, &cap, + sizeof(cap))) + return -EFAULT; + } + break; + + + case AUDIO_SET_STREAMTYPE: + { + int f = -1; + + switch(arg){ + case AUDIO_CAP_DTS: + case AUDIO_CAP_MP3: + case AUDIO_CAP_AAC: + case AUDIO_CAP_SDDS: + case AUDIO_CAP_OGG: + f = audio_none; + break; + + case AUDIO_CAP_LPCM: + f = audio_LPCM; + break; + + case AUDIO_CAP_MP1: + case AUDIO_CAP_MP2: + f = audio_MPEG; + break; + + case AUDIO_CAP_AC3: + f = audio_AC3; + break; + } + + card->setup.audioselect = (audio_type) f; + DecoderPrepareAudio(card); + break; + } + + case AUDIO_SET_ID: + if (arg < 0 || arg >32) arg = 0; + card->setup.audioID = arg; + arg = 0; + case AUDIO_SET_EXT_ID: + if (arg < 0 || arg >32) arg = 0; + card->setup.audioIDext = arg; + + attr = card->lastaattr; + DecoderSelectAudioID(card); + card->lastaattr = attr; + break; + + case AUDIO_SET_MIXER: + return -EINVAL; + + case AUDIO_SET_ATTRIBUTES: + SetAudioAttr(card,arg); + card->startingDVDA = ((card->setup.audioselect != audio_none) + && (card->setup.audioselect != + audio_disable)); + break; + + + case AUDIO_SET_KARAOKE: + { + break; + } + + default: + return -ENOIOCTLCMD; + } + break; + + case DVB_DEVICE_DEMUX_0: + return DmxDevIoctl(&card->dmxdev, file, cmd, arg); + break; + + case DVB_DEVICE_OSD_0: + { + switch (cmd) { + case OSD_SEND_CMD: + { + osd_cmd_t doc; + + if(copy_from_user(&doc, parg, + sizeof(osd_cmd_t))) + return -EFAULT; + return OSD_DrawCommand(card, &doc); + } + default: + return -EINVAL; + } + break; + } + default: + return -EOPNOTSUPP; + } + return 0; +} + +static unsigned int +dvbdev_poll(struct dvb_device *dvbdev, int num, + struct file *file, poll_table * wait) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + int type=num2type(card, num); + + switch (type) { + case DVB_DEVICE_DEMUX_0: + return DmxDevPoll(&card->dmxdev, file, wait); + + case DVB_DEVICE_VIDEO_0: + return PSpoll(file, wait); + + case DVB_DEVICE_AUDIO_0: + return poll_audio(file, wait); + + case DVB_DEVICE_CA_0: + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + + +static int +dvbdev_device_type(struct dvb_device *dvbdev, unsigned int num) +{ + struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv; + + return num2type(card, num); +} +#endif + +/****************************************************************************** + * driver registration + ******************************************************************************/ + + +#ifdef DVB + +#define INFU 32768 + + + +static dvb_devs_t mdvb_devs = { + 9, + { + DVB_DEVICE_VIDEO_0, DVB_DEVICE_AUDIO_0, + -1, -1, + DVB_DEVICE_DEMUX_0, DVB_DEVICE_DVR_0, + -1, -1, + DVB_DEVICE_OSD_0, + }, + { INFU, INFU, INFU, INFU, INFU, 1, 1, INFU, 1 }, + { 1, 1, 1, 1, INFU, 1, 1, 1, 1} +}; + + +static int +dvb_start_feed(dvb_demux_feed_t *dvbdmxfeed) +{ + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + struct cvdv_cards * card = (struct cvdv_cards *)dvbdmx->priv; + + if (!dvbdmx->dmx.frontend || !card) + return -EINVAL; + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + if ((dvbdmxfeed->ts_type & TS_DECODER) + && (dvbdmxfeed->pes_typedmx.frontend->source) { + case DMX_MEMORY_FE: + if (dvbdmxfeed->ts_type & TS_DECODER) + if (dvbdmxfeed->pes_type<2 && + dvbdmx->pids[0]!=0xffff && + dvbdmx->pids[1]!=0xffff) { + + setup_ts2pes( &card->tsa, + &card->tsv, + dvbdmx->pids, + dvbdmx->pids+1, + pes_write, + (void *)card); + + dvbdmx->playing=1; + } + break; + default: + return -EINVAL; + break; + } + } + } + + if (dvbdmxfeed->type == DMX_TYPE_SEC) { + int i; + + for (i=0; ifilternum; i++) { + if (dvbdmx->filter[i].state!=DMX_STATE_READY) + continue; + if (dvbdmx->filter[i].type!=DMX_TYPE_SEC) + continue; + if (dvbdmx->filter[i].filter.parent!= + &dvbdmxfeed->feed.sec) + continue; + + dvbdmxfeed->feed.sec.is_filtering=1; + dvbdmx->filter[i].state=DMX_STATE_GO; + } + } + + return 0; +} + + +static int +dvb_stop_feed(dvb_demux_feed_t *dvbdmxfeed) +{ + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + struct cvdv_cards * card = (struct cvdv_cards *)dvbdmx->priv; + if (!card) + return -EINVAL; + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + if ((dvbdmxfeed->ts_type & TS_DECODER) + && (dvbdmxfeed->pes_type<=1)) { + if (dvbdmx->playing) { + free_ipack(&card->tsa); + free_ipack(&card->tsv); + DecoderPause(card); + dvbdmx->playing=0; + } + } + + } + if (dvbdmxfeed->type == DMX_TYPE_SEC) { + int i; + + for (i=0; ifilternum; i++) + if (dvbdmx->filter[i].state==DMX_STATE_GO && + dvbdmx->filter[i].filter.parent== + &dvbdmxfeed->feed.sec) { + dvbdmx->filter[i].state=DMX_STATE_READY; + } + + } + return 0; +} + +static uint16_t get_pid(uint8_t *pid) +{ + uint16_t pp = 0; + + pp = (pid[0] & PID_MASK_HI)<<8; + pp |= pid[1]; + + return pp; +} + + +static int +dvb_write_to_decoder(dvb_demux_feed_t *dvbdmxfeed, uint8_t *buf, size_t count) +{ + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + struct cvdv_cards * card = (struct cvdv_cards *)dvbdmx->priv; + uint16_t pid = 0; + int off = 0; + + ipack *p; + + if (!card) + return -EINVAL; + + pid = get_pid(buf+1); + + if (pid == *(card->tsa.pid)) p = &(card->tsa); + else if (pid == *(card->tsv.pid)) p = &(card->tsv); + else return 0; + + if (dvbdmxfeed->pes_type>1) + return -1; + if (!(buf[3]&0x10)) // no payload? + return -1; + + if (count != TS_SIZE) return -1; + + if ( buf[3] & ADAPT_FIELD) { // adaptation field? + off = buf[4] + 1; + } + + + if (pid == *(card->tsa.pid)){ + MDEBUG(0,"AUDIO count: %d off: %d\n",count,off); + margi_write_audio(card, buf+off+4, TS_SIZE-4-off, 0); + } else { + MDEBUG(0,"VIDEO count: %d off: %d\n",count,off); + margi_write(card, buf+off+4, TS_SIZE-4-off, 0); + } + +// ts_to_pes( p, buf); // don't need count (=188) + return 0; +} + +int dvb_register(struct cvdv_cards *card) +{ + int i,ret; + struct dvb_device *dvbd=&card->dvb_dev; + + dvb_demux_t *dvbdemux = (dvb_demux_t *)&card->demux; + + if (card->dvb_registered) + return -1; + card->dvb_registered=1; + + card->audiostate.AVSyncState=0; + card->audiostate.muteState=0; + card->audiostate.playState=AUDIO_STOPPED; + card->audiostate.streamSource=AUDIO_SOURCE_MEMORY; + card->audiostate.channelSelect=AUDIO_STEREO; + card->audiostate.bypassMode=0; + + card->videostate.videoBlank=0; + card->videostate.playState=VIDEO_STOPPED; + card->videostate.streamSource=VIDEO_SOURCE_MEMORY; + card->videostate.videoFormat=VIDEO_FORMAT_4_3; + card->videostate.displayFormat=VIDEO_CENTER_CUT_OUT; + + // init and register demuxes + memcpy(card->demux_id, "demux0_0", 9); + card->demux_id[7] = 1+0x30; + dvbdemux->priv = (void *) card; + dvbdemux->filternum = 32; + dvbdemux->feednum = 32; + dvbdemux->start_feed = dvb_start_feed; + dvbdemux->stop_feed = dvb_stop_feed; + dvbdemux->write_to_decoder = dvb_write_to_decoder; + + dvbdemux->dmx.vendor="CIM"; + dvbdemux->dmx.model="sw"; + dvbdemux->dmx.id=card->demux_id; + dvbdemux->dmx.capabilities=(DMX_TS_FILTERING| + DMX_SECTION_FILTERING| + DMX_MEMORY_BASED_FILTERING); + + DvbDmxInit(&card->demux); + + card->dmxdev.filternum=32; + card->dmxdev.demux=&dvbdemux->dmx; + card->dmxdev.capabilities=0; + + DmxDevInit(&card->dmxdev); + + card->mem_frontend.id="mem_frontend"; + card->mem_frontend.vendor="memory"; + card->mem_frontend.model="sw"; + card->mem_frontend.source=DMX_MEMORY_FE; + ret=dvbdemux->dmx.add_frontend(&dvbdemux->dmx, + &card->mem_frontend); + if (ret<0) + return ret; + ret=dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, + &card->mem_frontend); + if (ret<0) + return ret; + + // init and register dvb device structure + dvbd->priv=(void *) card; + dvbd->open=dvbdev_open; + dvbd->close=dvbdev_close; + dvbd->write=dvbdev_write; + dvbd->read=dvbdev_read; + dvbd->ioctl=dvbdev_ioctl; + dvbd->poll=dvbdev_poll; + dvbd->device_type=dvbdev_device_type; + + for (i=0; iusers[i]=card->writers[i]=0; + + card->dvb_devs=0; + card->dvb_devs=&mdvb_devs; + + return dvb_register_device(dvbd); +} + +void dvb_unregister(struct cvdv_cards *card) +{ + dvb_demux_t *dvbdemux=&card->demux; + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &card->mem_frontend); + DmxDevRelease(&card->dmxdev); + DvbDmxRelease(&card->demux); + dvb_unregister_device(&card->dvb_dev); +} +#endif diff -Nru a/drivers/media/video/margi/cvdv.h b/drivers/media/video/margi/cvdv.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cvdv.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,58 @@ +/* + cvdv.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CVDV_H_ +#define _CVDV_H_ + + ////////////////////////////////////////////////////////// + // // + // Convergence Digital Video Decoder Card // + // Definitions for the PCI-Card and the Char-Driver // + // // +////////////////////////////////////////////////////////// + + +#include "cardbase.h" +#include "dram.h" +#include "osd.h" +#include "crc.h" +#include "l64021.h" +#include "audio.h" +#include "video.h" +#include "streams.h" +#include "decoder.h" +#include "spu.h" + +void SetVideoSystem(struct cvdv_cards *card); +u16 rnd(u16 range); +// debugging of the card: 0=normal, 1=color bars, 2=sync out +#define USE_DEBUG 0 + +#define cimlogo_width 45 +#define cimlogo_height 38 + +#define CHANNELBUFFERSIZE 32768*8 + +int Prepare(struct cvdv_cards *card); +int OSDTest(struct cvdv_cards *card); +void v4l_init(struct cvdv_cards *card); +int dvb_register(struct cvdv_cards *card); +void dvb_unregister(struct cvdv_cards *card); +#endif // _CVDV_H_ diff -Nru a/drivers/media/video/margi/cvdvext.h b/drivers/media/video/margi/cvdvext.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cvdvext.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,30 @@ +/* + cvdvtypes.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CVDVEXT_H_ +#define _CVDVEXT_H_ + +#include +#include "cvdvtypes.h" + +#define OSD_Draw(file,cmd) ioctl(fileno(file),_IO(CVDV_IOCTL_MAGIC,IOCTL_DRAW),&cmd) +#define DecoderCommand(file,cmd) ioctl(fileno(file),_IO(CVDV_IOCTL_MAGIC,IOCTL_DECODER),&cmd) + +#endif // _CVDVEXT_H_ diff -Nru a/drivers/media/video/margi/cvdvtypes.h b/drivers/media/video/margi/cvdvtypes.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/cvdvtypes.h Wed Feb 13 20:03:59 2002 @@ -0,0 +1,281 @@ +/* + cvdvtypes.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + ///////////////////////////////////////////////// + // // + // Convergence Digital Video Decoder Card // + // External Definitions for the Char-Driver // + // Copyright (c) 1999 Christian Wolff / // + // convergence integrated media GmbH Berlin // + // // +///////////////////////////////////////////////// + +// As of 1999-11-09 + +#ifndef _CVDVTYPE_H_ +#define _CVDVTYPE_H_ + +// our ioctl number: _IOC_TYPE() is 0xA2 (162) and the range of _IOC_NR() is 0x00 to 0x0F. +// submitted 99/10/15 to mec@shout.net +#define CVDV_IOCTL_MAGIC 0xA2 + +// command numbers _IOC_NR() for ioctl +typedef enum { + IOCTL_DRAW = 0x01, + IOCTL_DECODER = 0x02 +} IOCTL_Command; + + +// supported Videosystems +// everything but PAL and NTSC is untested and probably won't work. +typedef enum { + NTSC = 1, // NTSC 29.97 fps + NTSC60, // NTSC 30 fps + PAL, // PAL-B, D, G, H, I, 25 fps + PALM, // PAL-M 29.97 fps + PALM60, // PAL-M 30 fps + PALN, // PAL-N 25 fps + PALNc, // PAL-Nc 25 fps + PAL60 // PAL 30 fps (doesn't work, yet...) +} videosystem; + +typedef enum { + stream_none = 0, // unknown + stream_ES, + stream_PES, + stream_PS, + stream_DVD +} stream_type; + +typedef enum { + audio_disable = -1, + audio_none = 0, // unknown + audio_MPEG, + audio_MPEG_EXT, + audio_LPCM, + audio_AC3, + audio_DTS, + audio_SDDS +} audio_type; + +#if 0 +typedef enum { + // All functions return -2 on "not open" + OSD_Close = 1, // () + // Disables OSD and releases the buffers + // returns 0 on success + OSD_Open, // (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0)) + // Opens OSD with this size and bit depth + // returns 0 on success, -1 on DRAM allocation error, -2 on "already open" + OSD_Show, // () + // enables OSD mode + // returns 0 on success + OSD_Hide, // () + // disables OSD mode + // returns 0 on success + OSD_Clear, // () + // Sets all pixel to color 0 + // returns 0 on success + OSD_Fill, // (color) + // Sets all pixel to color + // returns 0 on success + OSD_SetColor, // (color,R{x0},G{y0},B{x1},opacity{y1}) + // set palette entry to , and apply + // R,G,B: 0..255 + // R=Red, G=Green, B=Blue + // opacity=0: pixel opacity 0% (only video pixel shows) + // opacity=1..254: pixel opacity as specified in header + // opacity=255: pixel opacity 100% (only OSD pixel shows) + // returns 0 on success, -1 on error + OSD_SetPalette, // (firstcolor{color},lastcolor{x0},data) + // Set a number of entries in the palette + // sets the entries "firstcolor" through "lastcolor" from the array "data" + // data has 4 byte for each color: + // R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel + OSD_SetTrans, // (transparency{color}) + // Sets transparency of mixed pixel (0..15) + // returns 0 on success + OSD_SetPixel, // (x0,y0,color) + // sets pixel , to color number + // returns 0 on success, -1 on error + OSD_GetPixel, // (x0,y0) + // returns color number of pixel ,, or -1 + OSD_SetRow, // (x0,y0,x1,data) + // fills pixels x0,y through x1,y with the content of data[] + // returns 0 on success, -1 on clipping all pixel (no pixel drawn) + OSD_SetBlock, // (x0,y0,x1,y1,increment{color},data) + // fills pixels x0,y0 through x1,y1 with the content of data[] + // inc contains the width of one line in the data block, + // inc<=0 uses blockwidth as linewidth + // returns 0 on success, -1 on clipping all pixel + OSD_FillRow, // (x0,y0,x1,color) + // fills pixels x0,y through x1,y with the color + // returns 0 on success, -1 on clipping all pixel + OSD_FillBlock, // (x0,y0,x1,y1,color) + // fills pixels x0,y0 through x1,y1 with the color + // returns 0 on success, -1 on clipping all pixel + OSD_Line, // (x0,y0,x1,y1,color) + // draw a line from x0,y0 to x1,y1 with the color + // returns 0 on success + OSD_Query, // (x0,y0,x1,y1,xasp{color}}), yasp=11 + // fills parameters with the picture dimensions and the pixel aspect ratio + // returns 0 on success + OSD_Test // () + // draws a test picture. for debugging purposes only + // returns 0 on success +// TODO: remove "test" in final version +} OSD_Command; + +struct drawcmd { + OSD_Command cmd; + int x0; + int y0; + int x1; + int y1; + int color; + void *data; +}; +#endif + +typedef enum { + Decoder_Pause, // pause{param1} 0=run 1=pause 2=toggle + Decoder_Still_Put, // (width{param1}, height{param2}, luma{data1}, chroma{data2}) + // show still picture of specified size + // width; width of the image + // height; height of the image + // luma; Y values, one byte per pixel, width*height bytes + // chroma; 4:2:0 U and V values, interlaced, one byte each U, one byte each V, width*height/2 bytes + Decoder_Still_Get, // (width{param1}, height{param2}, luma{data1}, chroma{data2}) + // grab current showing image + // width and height will be set to current picture size + // if luma and croma are NULL, only width and height will be reported + // otherwise the pixel data is filled in there, same format as Still_put + Decoder_Set_Videosystem, // (videosystem{param1}) + // videosystem: see enum {} videosystem; + Decoder_Set_Streamtype, // (streamtype{param1}) + // streamtype: according to enum {} stream_type; + // This has to be set BEFORE you send data to the device + // For ES and PES streams, Audio has to go into the first device (e.g.minor 0) and video into the second (e.g.minor 16) + Decoder_Set_Audiotype, // (audiotype{param1}) + // audiotype: see enum {} audio_type, +16 for IEC956 on S/PDIF out + Decoder_Set_VideoStreamID, // (video stream ID {param1}) + // video stream ID: MPEG ID 0..15 of video stream to display (E0..EF), -1 for any/auto + Decoder_Set_AudioStreamID, // (audio stream ID {param1}, audio extension stream ID {param2}) + // audio stream ID: MPEG ID 0..31 of audio stream to display (C0..DF), -1 for any/auto + // audio extension stream ID: MPEG ID 0..31 of audio extension stream (C0..DF), -1 for none + Decoder_CSS, // Passes CSS information to and from the decoder + // action{param1}, + // data block{data1} MSB first + // execute 1 to 4 once for each disc, then 5 to 8 for each title + // returns 0 on success, <0 on error + // -1: timeout reading data from card + // -2: data pointer not initialized + // -3: invalid action number + // action=0 -> disable and bypass CSS + // Disk key: + // action=1 -> retreive drive challenge (10 byte) from card + // action=2 -> post drive response (5 byte) to card + // action=3 -> post card challenge (10 byte) and retreive card response (5 byte) + // action=4 -> post disk key (2048 byte) into the card + // Title key: + // action=5 -> retreive title challenge (10 byte) from card + // action=6 -> post title response (5 byte) to card + // action=7 -> post card challenge (10 byte) and retreive card response (5 byte) + // action=8 -> post encrypted title key (5 byte) into the card + Decoder_Highlight, // post SPU Highlight information, + // active{param1} + // 1=show highlight, 0=hide highlight + // color information(SL_COLI or AC_COLI){data1[4]} MSB first + // bits: descr. + // 31-28 Emphasis pixel-2 color + // 27-24 Emphasis pixel-1 color + // 23-20 Pattern pixel color + // 19-16 Background pixel color + // 15-12 Emphasis pixel-2 contrast + // 11- 8 Emphasis pixel-1 contrast + // 7- 4 Pattern pixel contrast + // 3- 0 Background pixel contrast + // button position(BTN_POSI){data2[6]} MSB first + // bits: descr. + // 47-46 button color number + // 45-36 start x + // 33-24 end x + // 23-22 auto action mode + // 21-12 start y + // 9- 0 end y + Decoder_SPU, // Activate SPU decoding and select SPU stream ID + // stream{param1} + // active{param2} + Decoder_SPU_Palette, // post SPU Palette information + // length{param1} + // palette{data1} + Decoder_GetNavi, // Retreives CSS-decrypted navigational information from the stream. + // data1 will be filled with PCI or DSI pack (private stream 2 stream_id), + // and the length of data1 (1024 or 0) will be returned + Decoder_SetKaraoke, // Vocal1{param1}, Vocal2{param2}, Melody{param3} + // if Vocal1 or Vocal2 are non-zero, they get mixed into left and right at 70% each + // if both, Vocal1 and Vocal2 are non-zero, Vocal1 gets mixed into the left channel and + // Vocal2 into the right channel at 100% each. + // if Melody is non-zero, the melody channel gets mixed into left and right + Decoder_Set_Videoattribute, // Set the video parameters + // attribute{param1} (2 byte V_ATR) + // bits: descr. + // 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) + // 13-12 TV system (0=525/60, 1=625/50) + // 11-10 Aspect ratio (0=4:3, 3=16:9) + // 9- 8 permitted display mode on 4:3 monitor (0=both, 1=only pan-scan, 2=only letterbox) + // 7 line 21-1 data present in GOP (1=yes, 0=no) + // 6 line 21-2 data present in GOP (1=yes, 0=no) + // 5- 3 source resolution (0=720x480/576, 1=704x480/576, 2=352x480/576, 3=352x240/288) + // 2 source letterboxed (1=yes, 0=no) + // 0 film/camera mode (0=camera, 1=film (625/50 only)) + Decoder_Set_Audioattribute, // Set the audio parameters + // attribute{param1} (2 most significan bytes of A_ATR (bit 63 through 48)) + // bits: descr. + // 15-13 audio coding mode (0=ac3, 2=mpeg1, 3=mpeg2ext, 4=LPCM, 6=DTS, 7=SDDS) + // 12 multichannel extension + // 11-10 audio type (0=not spec, 1=language included) + // 9- 8 audio application mode (0=not spec, 1=karaoke, 2=surround) + // 7- 6 Quantization / DRC (mpeg audio: 1=DRC exists)(lpcm: 0=16bit, 1=20bit, 2=24bit) + // 5- 4 Sample frequency fs (0=48kHz, 1=96kHz) + // 2- 0 number of audio channels (n+1 channels) + Decoder_WriteBlock, // Post one block of data, e.g. one DVD sector of 2048 byte, into the decoder queue + // sectordata{data1} + // length{param1} + // is_initial_block{param2} + // set_SCR{param3} + /* + Decoder_FFWD, // ffwd{param1} 0=normal 1=ffwd 2=toggle + Decoder_Slow // slow{param1} 0=normal 1=slow 2=toggle + */ +} Decoder_Command; + +struct decodercmd { + Decoder_Command cmd; + int param1; + int param2; + int param3; + int param4; + int param5; + void *data1; + void *data2; +}; + +#endif // _CVDVTYPE_H_ diff -Nru a/drivers/media/video/margi/decoder.c b/drivers/media/video/margi/decoder.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/decoder.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,1536 @@ +/* + decoder.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "decoder.h" +#include "l64021.h" +#include "video.h" +#include "audio.h" +#include "streams.h" +#include "osd.h" +#include "dram.h" +#include "cvdv.h" + +int DecoderGetNavi(struct cvdv_cards *card, u8 *navidata) +{ + if (card->navihead == card->navitail) return 0; + MDEBUG(3, ": Retreiving NaviPack\n"); + memcpy(navidata, &card->navibuffer[card->navitail], NAVISIZE); + card->navitail += NAVISIZE; + if (card->navitail >= NAVIBUFFERSIZE) card->navitail = 0; + return NAVISIZE; +} + +// returns 1 on overrun, 0 on no error +int DecoderQueueNavi(struct cvdv_cards *card, u8 *navidata) +{ + memcpy(&card->navibuffer[card->navihead], navidata, NAVISIZE); + card->navihead += NAVISIZE; + if (card->navihead >= NAVIBUFFERSIZE) card->navihead = 0; + if (card->navihead == card->navitail) { + MDEBUG(3, ": NaviPack buffer overflow\n"); + card->navitail += NAVISIZE; + if (card->navitail >= NAVIBUFFERSIZE) card->navitail = 0; + return 1; + } + return 0; +} + +u32 ParseSCR(const u8 *data) +{ + u32 SCR_base=0; + u8 scrdata[9]; + copy_from_user (scrdata, data, 9); + + if ((!scrdata[0]) && (!scrdata[1]) && (scrdata[2]==1) + && (scrdata[3]==0xBA) && ((scrdata[4]&0xC0)==0x40)) { + SCR_base=((scrdata[4]>>3)&0x07); + SCR_base=(SCR_base<<2) | (scrdata[4]&0x03); + SCR_base=(SCR_base<<8) | scrdata[5]; + SCR_base=(SCR_base<<5) | ((scrdata[6]>>3)&0x1F); + SCR_base=(SCR_base<<2) | (scrdata[6]&0x03); + SCR_base=(SCR_base<<8) | scrdata[7]; + SCR_base=(SCR_base<<5) | ((scrdata[8]>>3)&0x1F); + } + return SCR_base; +} + +u32 SetSCR(struct cvdv_cards *card, u32 SCR_base) +{ + MDEBUG(3, ": SCR in DVD Pack: 0x%08X\n",SCR_base); + if (DecoderReadByte(card, 0x007) & 0x10) { // SCR already stopped + DecoderWriteByte(card,0x009,SCR_base&0xFF); // Set SCR counter + DecoderWriteByte(card,0x00A,(SCR_base>>8)&0xFF); + DecoderWriteByte(card,0x00B,(SCR_base>>16)&0xFF); + DecoderWriteByte(card,0x00C,(SCR_base>>24)&0xFF); + } else { + DecoderMaskByte(card,0x007,0xD2,0xD2); + // Set 0x10, halt SCR counter + DecoderWriteByte(card,0x009,SCR_base&0xFF); // Set SCR counter + DecoderWriteByte(card,0x00A,(SCR_base>>8)&0xFF); + DecoderWriteByte(card,0x00B,(SCR_base>>16)&0xFF); + DecoderWriteByte(card,0x00C,(SCR_base>>24)&0xFF); + DecoderMaskByte(card,0x007,0xD2,0xC2); + // Del 0x10, SCR counter run + } + return SCR_base; +} + +void DecoderPause(struct cvdv_cards *card) +{ + DecoderMaskByte(card, 0x007, 0xD2, 0xD2); + // Set 0x010, halt SCR counter + AudioSetPlayMode(card, MAUDIO_PAUSE); + DecoderStopDecode(card); +#ifdef DVB + card->videostate.playState=VIDEO_FREEZED; +#endif + card->videoffwd = 0; + card->videoslow = 0; +} + +void DecoderUnPause(struct cvdv_cards *card) +{ + DecoderStartDecode(card); + card->videoffwd = 0; + AudioSetPlayMode(card, MAUDIO_PLAY); + DecoderMaskByte(card, 0x007, 0xD2, 0xC2); + // Del 0x010, SCR counter run +#ifdef DVB + card->videostate.playState=VIDEO_PLAYING;; +#endif + card->videoslow = 0; +} + +void CloseCard(struct cvdv_cards *card) +{ +#ifdef NOINT + spin_lock(&card->timelock); + del_timer(&card->timer); + spin_unlock(&card->timelock); +#endif + MargiFlush(card); + MDEBUG(1, ": Closing card\n"); + card->DecoderOpen = 1; + DecoderClose(card); + DecoderUnPrepare(card); + DecoderStreamReset(card); + DecoderSetupReset(card); + VideoSetBackground(card, 1, 0, 0, 0); + + AudioClose(card); + OSDClose(card); + L64021Init(card); + MargiFreeBuffers(card); + + OSDOpen(card, 50, 50, 150, 150, 2, 1); + OSDTest(card); +} + + +void DecoderReadAudioInfo(struct cvdv_cards *card) +{ + u8 data; + static int bitrates[17] = {0, 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320, 384, 0}; + struct AudioParam *audio = &card->stream.audio; + data = DecoderReadByte(card, 0x150); + audio->mpeg.present = data & 0x60; + // MPEG Layer Code 00 reserverd, we can assume valid MPEG params + if (audio->mpeg.present) { + audio->mpeg.MPEG2 = data & 0x80; + audio->mpeg.layer = 4 - ((data >> 5) & 0x03); + if (data & 0x0F) { + if ((data & 0x0F) == 1) audio->mpeg.bitrate = 32; + else switch (audio->mpeg.layer) { + case 1: + audio->mpeg.bitrate = 32 * (data & 0x0F); + break; // Layer I + case 2: + audio->mpeg.bitrate = bitrates[(data & 0x0F) + + 1]; + break; // Layer II + default: + audio->mpeg.bitrate = bitrates[data & 0x0F]; + // Layer III + } + } else audio->mpeg.bitrate = 0; + data = DecoderReadByte(card, 0x151); + switch ((data >> 6) & 0x03) { + case 0: + audio->mpeg.samplefreq = 44; + break; + case 1: + audio->mpeg.samplefreq = 48; + break; + case 2: + audio->mpeg.samplefreq = 32; + break; + default: + audio->mpeg.samplefreq = 0; // invalid + } + audio->mpeg.mode = (data >> 3) & 0x03; + audio->mpeg.modeext = (data >> 1) & 0x03; + audio->mpeg.copyright = data & 0x01; + data=DecoderReadByte(card, 0x152); + audio->mpeg.original = data & 0x80; + audio->mpeg.emphasis = (data >> 5) & 0x03; + } + data = DecoderReadByte(card, 0x153); + audio->ac3.present = (data != 0); + // value 0 for bits 0..5 forbidden, we can assume valid ac3 params + if (audio->ac3.present) { + audio->ac3.acmod = (data >> 5) & 0x07; + audio->ac3.dialnorm = data & 0x1F; + data = DecoderReadByte(card, 0x154); + audio->ac3.bsmod = (data >> 5) & 0x07; + audio->ac3.dialnorm2 = data > 0x1F; + data = DecoderReadByte(card, 0x155); + audio->ac3.surmixlev = (data >> 6) & 0x03; + audio->ac3.mixlevel = (data >> 1) & 0x1F; + data = DecoderReadByte(card, 0x156); + audio->ac3.cmixlev = (data >> 6) & 0x03; + audio->ac3.mixlevel2 = (data >> 1) & 0x1F; + data = DecoderReadByte(card, 0x157); + audio->ac3.fscod = (data >> 6) & 0x03; + audio->ac3.lfeon = (data >> 5) & 0x01; + audio->ac3.bsid = data & 0x1F; + data = DecoderReadByte(card, 0x158); + audio->ac3.dsurmod = (data >> 6) & 0x03; + audio->ac3.frmsizecod = data & 0x3F; + audio->ac3.langcod = DecoderReadByte(card, 0x159); + audio->ac3.langcod2 = DecoderReadByte(card, 0x15A); + audio->ac3.timecod = DecoderReadByte(card, 0x15B); + data = DecoderReadByte(card, 0x15C); + audio->ac3.timecod = (audio->ac3.timecod << 6) | + ((data >> 2) & 0x3F); + audio->ac3.roomtyp = data & 0x03; + audio->ac3.timecod2 = DecoderReadByte(card, 0x15D); + data = DecoderReadByte(card, 0x15E); + audio->ac3.timecod2 = (audio->ac3.timecod2 << 6) | + ((data >> 2) & 0x3F); + audio->ac3.roomtyp2 = data & 0x03; + } + audio->pcm.present =! (DecoderReadByte(card, 0x161) & 0x20); + // PCM FIFO not empty? Then, we can assume valid LPCM params + if (audio->pcm.present) { + data = DecoderReadByte(card, 0x15F); + audio->pcm.audio_frm_num = (data >> 3) & 0x1F; + audio->pcm.num_of_audio_ch = data & 0x07; + data = DecoderReadByte(card, 0x160); + audio->pcm.Fs = (data >> 6) & 0x03; + audio->pcm.quantization = (data >> 4) & 0x03; + audio->pcm.emphasis = (data >> 2) & 0x03; + audio->pcm.mute_bit = (data >> 1) & 0x01; + } + switch (card->setup.audioselect) { + case audio_disable: + audio->valid = 0; + break; + case audio_none: + case audio_DTS: + case audio_SDDS: + if ((audio->valid = (audio->ac3.present || + audio->pcm.present || + audio->mpeg.present))) { + if (audio->mpeg.present) { + card->setup.audioselect = audio_MPEG; + } else if (audio->pcm.present) { + card->setup.audioselect = audio_LPCM; + } else if (audio->ac3.present) { + card->setup.audioselect = audio_AC3; + } + } else { + audio->valid = 0; + card->setup.audioselect = audio_none; + } + break; + case audio_MPEG: // MPEG Audio + case audio_MPEG_EXT: // MPEG Audio with extension stream + audio->valid = audio->mpeg.present; + break; + case audio_LPCM: // Linear Pulse Code Modulation LPCM + audio->valid = audio->pcm.present; + break; + case audio_AC3: // AC-3 + audio->valid = audio->ac3.present; + break; + } + MDEBUG(1, ": -- DecoderReadAudioInfo - type/valid %d/%d:\n", card->setup.audioselect, audio->valid); + if (audio->mpeg.present || audio->ac3.present || audio->pcm.present) + MDEBUG(1, ": Audio - Decoded parameters:\n"); + if (audio->mpeg.present) MDEBUG(1, ": MPEG%s Layer %d, %d kHz, %d kbps, %s, %s%s, %s emphasis\n", + ((audio->mpeg.MPEG2) ? "2" : "1"), + audio->mpeg.layer, + audio->mpeg.samplefreq, + audio->mpeg.bitrate, + ((audio->mpeg.mode == 0) ? "stereo" : ((audio->mpeg.mode == 1) ? "joint stereo" : ((audio->mpeg.mode == 2) ? "dual channel" : "single channel"))), + ((audio->mpeg.copyright) ? "copyrighted " : ""), + ((audio->mpeg.original) ? "original" : "copy"), + ((audio->mpeg.emphasis == 0) ? "no" : ((audio->mpeg.emphasis == 1) ? "50/15 usec." : ((audio->mpeg.emphasis == 2) ? "invalid" : "J.17"))) + ); + if (audio->ac3.present) MDEBUG(1, ": AC3 acmod=%d bsmod=%d dialnorm=%d dialnorm2=%d surmixlev=%d mixlevel=%d cmixlev=%d mixlevel2=%d fscod=%d lfeon=%d bsid=%d dsurmod=%d frmsizecod=%d langcod=%d langcod2=%d timecod=%d roomtyp=%d timecod2=%d roomtyp2=%d\n", + audio->ac3.acmod, + audio->ac3.bsmod, + audio->ac3.dialnorm, + audio->ac3.dialnorm2, + audio->ac3.surmixlev, + audio->ac3.mixlevel, + audio->ac3.cmixlev, + audio->ac3.mixlevel2, + audio->ac3.fscod, + audio->ac3.lfeon, + audio->ac3.bsid, + audio->ac3.dsurmod, + audio->ac3.frmsizecod, + audio->ac3.langcod, + audio->ac3.langcod2, + audio->ac3.timecod, + audio->ac3.roomtyp, + audio->ac3.timecod2, + audio->ac3.roomtyp2); + if (audio->pcm.present) MDEBUG(1, ": LPCM audio_frm_num=%d num_of_audio_ch=%d Fs=%d quantization=%d emphasis=%d mute_bit=%d\n", + audio->pcm.audio_frm_num, + audio->pcm.num_of_audio_ch, + audio->pcm.Fs, + audio->pcm.quantization, + audio->pcm.emphasis, + audio->pcm.mute_bit); +} + +void DecoderReadAuxFifo(struct cvdv_cards *card) +{ + int i = 0; + u8 data; + int layer; + + struct StreamInfo *stream = &card->stream; + MDEBUG(3, ": AUX - %03X ", card->AuxFifo[card->AuxFifoTail]); + while (card->AuxFifoHead != card->AuxFifoTail) { + + layer = (card->AuxFifo[card->AuxFifoTail] >> 8) & 0x07; + data = card->AuxFifo[card->AuxFifoTail] & 0xFF; + card->AuxFifoTail = (card->AuxFifoTail + 1) & FIFO_MASK; + if (layer != card->AuxFifoLayer) { // start of a new layer? + i = 0; + card->AuxFifoLayer = layer; + } else i++; + switch (layer) { // layer code + case 0: // sequence header + if (! stream->sh.valid) switch (i) { + case 0: + stream->sh.hsize = data & 0x0F; + break; + case 1: + stream->sh.hsize = (stream->sh.hsize << 8) + | data; + stream->hsize = stream->sh.hsize; + break; + case 2: + stream->sh.vsize = data & 0x0F; + break; + case 3: + stream->sh.vsize = (stream->sh.vsize << 8) | + data; + stream->vsize = stream->sh.vsize; + break; + case 4: + stream->sh.aspectratio = data & 0x0F; + break; + case 5: + stream->sh.frameratecode = data & 0x0F; + break; + case 6: + stream->sh.bitrate = data & 0x03; + break; + case 7: + stream->sh.bitrate = (stream->sh.bitrate << 8) + | data; + break; + case 8: + stream->sh.bitrate = (stream->sh.bitrate << 8) + | data; + stream->bitrate = stream->sh.bitrate; + break; + case 9: + stream->sh.vbvbuffersize = data & 0x03; + break; + case 10: + stream->sh.vbvbuffersize = + (stream->sh.vbvbuffersize << 8) | + data; + stream->vbvbuffersize = + stream->sh.vbvbuffersize; + break; + case 11: + stream->sh.constrained = data & 0x01; + stream->sh.valid = 1; + MDEBUG(1, ": AUX - MPEG1 - %dx%d %s %s fps, %d bps, %d kByte vbv%s\n", stream->sh.hsize, stream->sh.vsize, + ((stream->sh.aspectratio == 1) ? "1:1" : + ((stream->sh.aspectratio == 2) ? "3:4" : + ((stream->sh.aspectratio == 3) ? "9:16" : + ((stream->sh.aspectratio == 4) ? "1:2.21" : + "?:?")))), + ((stream->sh.frameratecode == 1) ? "23.976" : + ((stream->sh.frameratecode == 2) ? "24" : + ((stream->sh.frameratecode == 3) ? "25" : + ((stream->sh.frameratecode == 4) ? "29.97" : + ((stream->sh.frameratecode == 5) ? "30" : + ((stream->sh.frameratecode == 6) ? "50" : + ((stream->sh.frameratecode == 7) ? "59.94" : + ((stream->sh.frameratecode == 8) ? "60" : + "?")))))))), + stream->sh.bitrate * 400, + stream->sh.vbvbuffersize * 16, + ((stream->sh.constrained) ? ", constrained" : "") + ); + break; + } + break; + case 1: // group of pictures + if (! stream->gop.valid) + switch (i) { + case 0: + stream->gop.timecode = data & 0x01; + break; + case 1: + stream->gop.timecode = + (stream->gop.timecode << 8) | + data; + break; + case 2: + stream->gop.timecode = + (stream->gop.timecode << 8) | + data; + break; + case 3: + stream->gop.timecode = + (stream->gop.timecode << 8) | + data; + break; + case 4: + stream->gop.closedgop = data & 0x01; + break; + case 5: + stream->gop.brokenlink = data & 0x01; + stream->gop.valid = 1; + break; + } + break; + case 2: // picture + if (0) + switch (i) { + case 0: + break; + } + break; + case 7: // extension layer + if (i == 0) card->AuxFifoExt = data; + else + switch (card->AuxFifoExt) { // extension code + case 1: // sequence extension + if ((stream->sh.valid) && + (! stream->se.valid)) + switch (i) { + case 1: + stream->se.profilelevel + = data; + break; + case 2: + stream->se.progressive + = data & 0x01; + break; + case 3: + stream->se.chroma = + (data >> 4) & + 0x03; + stream->se.hsizeext = + (data >> 2) & + 0x03; + stream->se.vsizeext = + data & 0x03; + stream->hsize |= + (stream->se.hsizeext << 12); + stream->vsize |= + (stream->se.vsizeext << 12); + break; + case 4: + stream->se.bitrateext = + data & 0x0F; + break; + case 5: + stream->se.bitrateext = + (stream->se.bitrateext << 8) | data; + stream->bitrate |= + (stream->se.bitrateext << 18); + break; + case 6: + stream->se.vbvbuffersizeext = data; + stream->vbvbuffersize |= (stream->se.vbvbuffersizeext << 10); + break; + case 7: + stream->se.lowdelay = + (data >> 7) & + 0x01; + stream->se.frextn = + (data >> 5) & + 0x03; + stream->se.frextd = + data & 0x1F; + stream->se.valid = 1; + stream->MPEG2 = 1; + MDEBUG(1, ": AUX - MPEG2 - %dx%d %s %s*%d/%d fps, %d bps, %d kByte vbv%s%s\n", stream->hsize, stream->vsize, + ((stream->sh.aspectratio == 1) ? "1:1" : + ((stream->sh.aspectratio == 2) ? "3:4" : + ((stream->sh.aspectratio == 3) ? "9:16" : + ((stream->sh.aspectratio == 4) ? "1:2.21" : + "?:?")))), + ((stream->sh.frameratecode == 1) ? "23.976" : + ((stream->sh.frameratecode == 2) ? "24" : + ((stream->sh.frameratecode == 3) ? "25" : + ((stream->sh.frameratecode == 4) ? "29.97" : + ((stream->sh.frameratecode == 5) ? "30" : + ((stream->sh.frameratecode == 6) ? "50" : + ((stream->sh.frameratecode == 7) ? "59.94" : + ((stream->sh.frameratecode == 8) ? "60" : + "?")))))))), + stream->se.frextn + 1, + stream->se.frextd + 1, + stream->bitrate * 400, + stream->vbvbuffersize * 16, + ((stream->sh.constrained) ? ", constrained" : ""), + ((stream->se.lowdelay) ? ", low delay" : "") + ); + break; + } + break; + case 2: // sequence display extension + if (0) + switch (i) { + case 0: + break; + } + break; + case 3: // quant matrix extension + if (0) + switch (i) { + case 0: + break; + } + break; + case 4: // copyright extension + if (0) + switch (i) { + case 0: + break; + } + break; + case 7: // picture display extension + if (0) switch (i) { + case 0: + break; + } + break; + case 8: // picture coding extension + if (0) + switch (i) { + case 0: + break; + } + break; + default: + break; + } + break; + default:break; + } + + } +} + +void DecoderReadDataFifo(struct cvdv_cards *card) +{ + MDEBUG(3, ": DATA - "); + while (card->DataFifoHead != card->DataFifoTail) { + MDEBUG(3,"%03X ", card->DataFifo[card->DataFifoTail]); + card->DataFifoTail = (card->DataFifoTail + 1) & FIFO_MASK; + } + MDEBUG(3,"\n"); +} + +int DecoderReadNavipack(struct cvdv_cards *card) +{ + u32 startaddr, endaddr, writeaddr; + u8 navipack[1024]; + u16 PacketLength; + u8 SubStreamID; + //struct Navi navi; + int i; + startaddr = (DecoderReadWord(card, 0x05C) & 0x3FFF) << 7; + // 21 bit word address + endaddr = (DecoderReadWord(card, 0x05E) & 0x3FFF) << 7; + // 21 bit word address + writeaddr = DecoderReadByte(card, 0x075) & 0xFF; + writeaddr |= (DecoderReadWord(card, 0x077) & 0x0FFF) << 8; + //writeaddr <<= 3; + MDEBUG(3, ": -- DecoderReadNavipack 0x%08X-0x%08X, ->0x%08X <-0x%08X\n", + startaddr, endaddr, writeaddr, card->NaviPackAddress); + + if (DecoderReadByte(card, 0x07B) & 0xC0) { // navi pack available? + DRAMReadByte(card, card->NaviPackAddress, 1024, navipack, 0); + card->reg07B |= 0x20; // decrement navi counter + DecoderWriteByte(card, 0x07B, card->reg07B); + card->reg07B &= ~0x20; + //DecoderSetByte(card, 0x07B, 0x20); // decrement navi counter + card->NaviPackAddress += 512; // increment in words + if (card->NaviPackAddress >= endaddr) + card->NaviPackAddress = startaddr; + MDEBUG(4, ": Navipack %02X %02X %02X %02X %02X %02X %02X %02X\n", + navipack[0], navipack[1], navipack[2], navipack[3], navipack[4], + navipack[5], navipack[6], navipack[7]); + if ((!navipack[0]) && (!navipack[1]) && (navipack[2] == 1) && + (navipack[3] == 0xBF)) { + PacketLength = (navipack[4] << 8) | navipack[5]; + SubStreamID = navipack[6]; + MDEBUG(4, ": Navipack Len=%d, ID=%d\n", PacketLength, SubStreamID); + i = 7; // start of payload data in navipack[] + switch (SubStreamID) { + case 0: // Presentation Control Information (PCI) + if (PacketLength < 980) return 1; // Packet too small + DecoderQueueNavi(card, navipack); + break; + case 1: // Data Search Information (DSI) + if (PacketLength < 1018) return 1; // Packet too small + DecoderQueueNavi(card, navipack); + break; + default: + break; + } + } else { + MDEBUG(4, "navipack format error:%02X %02X %02X %02X %02X %02X %02X %02X\n", + navipack[0], navipack[1], navipack[2], navipack[3], navipack[4], + navipack[5], navipack[6], navipack[7]); + } + } else { + MDEBUG(4, ": no navi pack avail.\n"); + } + return 0; +} + +int AudioStart(struct cvdv_cards *card) +{ + DecoderReadAudioInfo(card); // detect audio type + if (card->stream.audio.valid) { + MDEBUG(1, ": Audio Init in delayed decoder start\n"); + if (card->AudioInitialized) AudioClose(card); + switch (card->setup.audioselect) { + case audio_MPEG: // MPEG Audio + case audio_MPEG_EXT: // MPEG Audio with ext. + MDEBUG(1, ": Using MPEG Audio\n"); + AudioInit(card, card->stream.audio.mpeg.samplefreq, 0); + if (card->stream.audio.mpeg.mode == 3) AudioDualMono(card, 2); // left channel only + else AudioDualMono(card, 0); + break; + case audio_DTS: + case audio_LPCM: // Linear Pulse Code Modulation LPCM + MDEBUG(1, ": Using LPCM Audio\n"); + AudioInit(card, 48, 0); // or 96 + break; + case audio_AC3: // AC-3 + MDEBUG(1, ": Using AC-3 Audio\n"); + switch (card->stream.audio.ac3.fscod) { + case 0:AudioInit(card, 48, 0); break; + case 1:AudioInit(card, 44, 0); break; + case 2:AudioInit(card, 32, 0); break; + } + break; + case audio_none: + case audio_disable: + case audio_SDDS: + } + } else return 1; + return 0; +} + +u32 DecoderReadSCR(struct cvdv_cards *card, u16 address) +{ + u32 SCR; + SCR = DecoderReadByte(card, address); + SCR |= ((u32)DecoderReadByte(card, address+1) << 8); + SCR |= ((u32)DecoderReadByte(card, address+2) << 16); + SCR |= ((u32)DecoderReadByte(card, address+3) << 24); + return SCR; +} + +u32 DecoderReadRWAddr(struct cvdv_cards *card, u16 address) +{ + u32 addr; + addr = DecoderReadByte(card, address) & 0xFF; + addr |= (((u32)DecoderReadByte(card, address+1) & 0xFF) << 8); + addr |= (((u32)DecoderReadByte(card, address+2) & 0x0F) << 16); + return addr; +} + +int PTSGetFirstPTS(PTSStorage *store, u32 *PTS) +{ + if ( store->end == store->begin ) { + return 0; + } else { + *PTS = store->PTS[store->begin]; + return 1; + } +} + +void PTSStoreAdd(PTSStorage *store, u32 PTS, u32 AddrB, u32 AddrE) +{ + int new; + MDEBUG(3, ": PTSStoreAdd - store in [%d] %08X - %08X\n", store->end, AddrB, AddrE); + + // cheap fix: don't store if address rollover + if ((AddrB & 0x00080000) != (AddrE & 0x00080000)) return; + + new = store->end; + + store->end++; + if (store->end >= store->size) store->end = 0; + if (store->end == store->begin) { + store->begin++; + if (store->begin >= store->size) store->begin = 0; + } + + store->AddrB[new] = AddrB; + store->AddrE[new] = AddrE; + store->PTS[new] = PTS; +} + +int PTSGetPTS (PTSStorage *store, u32 Addr, u32 *PTS ) +{ + u32 AddrB; + u32 AddrE; + int i; + int found; + int search; + + MDEBUG(3, ": PTSGetPTS - search %08X\n", Addr); + + if (store->end == store->begin) { + store->LastAddr = Addr; + return 0; + } + + // Search for the PTS in the array + found = 0; + search = 1; + while (search && !found) { + // Get the first value + i = store->begin; + AddrB = store->AddrB[i]; + AddrE = store->AddrE[i]; + + MDEBUG(3, ": PTSGetPTS - search in [%d] %08X - %08X\n", i, AddrB, AddrE); + + //If in range, keep it + if ((Addr >= AddrB) && (Addr <= AddrE)) { + *PTS = store->PTS[i]; + found = 1; + } else { + if ((Addr & 0x00080000) == (AddrB & 0x00080000)) { + if (Addr < AddrB ) search = 0; + } else { + if ((store->LastAddr & 0x00080000) == (Addr & 0x00080000)) search = 0; + } + } + if (search) { + store->begin++; + if (store->begin >= store->size) store->begin = 0; + if (store->end == store->begin ) search = 0; + } + } + store->LastAddr = Addr; + return found; +} + + +u32 GetPTS(u8 *data, u32* MediaPointer, int mpeg, int hlength,int off) +{ + u32 PTS = 0xFFFFFFFFUL; + int p = 0; + + // Read PTS, if present + if ((mpeg == 2 && data[p + 7] & 0x80) || + (mpeg == 1 && off)) { + if (mpeg == 1) p = off-9; + PTS = (data[p + 9] >> 1) & 0x03UL; + PTS = (PTS << 8) | (data[p + 10] & 0xFFUL); + PTS = (PTS << 7) | ((data[p + 11] >> 1) & 0x7FUL); + PTS = (PTS << 8) | (data[p + 12] & 0xFFULL); + PTS = (PTS << 7) | ((data[p + 13] >> 1) & 0x7FUL); + } + // Now, skip rest of PES header and stuffing + if (mpeg == 2){ + p += (9 + (data[p + 8] & 0xFF)); + p = ((p + 7) / 8) * 8; + } else p = hlength+7; + if (!(data[p++] | data[p++] | data[p++] | data[p++])) { + *MediaPointer = (u32)data[p++] & 0xFF; + *MediaPointer = (*MediaPointer << 8) | ((u32)data[p++] & 0xFF); + *MediaPointer = (*MediaPointer << 8) | ((u32)data[p++] & 0xFF); + *MediaPointer = (*MediaPointer << 8) | ((u32)data[p++] & 0xFF); + } else { + *MediaPointer = 0xFFFFFFFFUL; + } + return PTS; +} + +int ReadPESChunk(struct cvdv_cards *card, u32 *addr, u8 *data, u32 start, u32 end) +{ + int i = 5, err = -1; + while (err && (i--)) err &= DRAMReadByte(card, *addr << 2, 8, &data[0], 0); + if (err) return 1; + (*addr)++; + if (*addr >= end) *addr = start; + return 0; +} + +void ReadPESHeaders(struct cvdv_cards *card) +{ + u8 startcode[] = {0x00, 0x00, 0x01}; + int LoopCount; + u32 LastVAddr; // Current Video Address + u32 LastAAddr; // Current Audio Address + u32 Addr; // Current Header Address + u32 PESAddr; // Pointer from Header Block + u32 PTS; // PTS from Header Block + u8 Data[32]; + u32 AudioPESStart; + u32 AudioPESEnd; + int i, j, p, fail; + u32 FailAddr; + int hlength=0; + int mpeg=0; + int check; + int mp=0; + int off=0; + + AudioPESStart = (DecoderReadWord(card, 0x058) & 0x3FFF) << 5; + AudioPESEnd = ((DecoderReadWord(card, 0x05A) & 0x3FFF) + 1) << 5; + + LastVAddr = DecoderReadRWAddr(card, 0x060); + LastAAddr = DecoderReadRWAddr(card, 0x063); + + if (card->LastAddr == 0) card->LastAddr = AudioPESStart; + + //Read the PES header buffer + Addr = DecoderReadRWAddr(card, 0x072) & 0x0007FFFF; + if (Addr >= AudioPESEnd) { + Addr = card->LastAddr = AudioPESStart; + } + + LoopCount = 0; + while ((card->LastAddr != Addr) && (LoopCount++ < 200)) { + FailAddr = card->LastAddr; + fail = 0; + p = 0; + + if (ReadPESChunk(card, &card->LastAddr, &Data[p], + AudioPESStart, AudioPESEnd)) continue; + p+=8; + j=1; + + if (memcmp(Data, startcode, 3)) continue; + if ((Data[3] == 0xE0) || (Data[3] == 0xBD) + || ((Data[3] & 0xE0) == 0xC0)) { + + fail |= ReadPESChunk(card, &card->LastAddr, + &Data[p], AudioPESStart, + AudioPESEnd); + + + p+=8; + j++; + if ( (Data[6] & 0xC0) == 0x80 ){ + hlength = 9+Data[8]; + mpeg = 2; + } else { + mpeg = 1; + mp = 6; + check = Data[mp]; + mp++; + while (check == 0xFF){ + if (!fail && mp == p) { + fail |= ReadPESChunk( + card, + &card->LastAddr, + &Data[p], + AudioPESStart, + AudioPESEnd); + p+=8; + j++; + } + check = Data[mp]; + mp++; + } + if (!fail && mp == p) { + fail |= ReadPESChunk( + card, + &card->LastAddr, + &Data[p], + AudioPESStart, + AudioPESEnd); + p+=8; + j++; + } + + if ( !fail && (check & 0xC0) == 0x40){ + check = Data[mp]; + mp++; + if (!fail && mp == p) { + fail |= ReadPESChunk( + card, + &card->LastAddr, + &Data[p], + AudioPESStart, + AudioPESEnd); + p+=8; + j++; + } + check = Data[mp]; + mp++; + } + if ( !fail && (check & 0x20)){ + if (check & 0x30) hlength = mp+10; + else hlength = mp+5; + off = mp-1; + } + } + + for (i = 1; (i < ((hlength+7) / 8)) && (!fail); + i++) { + fail |= ReadPESChunk(card, &card->LastAddr, + &Data[p], AudioPESStart, + AudioPESEnd); + p+=8; + j++; + } + + if (!fail) { + PTS = GetPTS(Data, &PESAddr, + mpeg, hlength,off); + if ((PTS != 0xFFFFFFFF) && + (PESAddr != 0xFFFFFFFF)) { + if (Data[3] == 0xE0) { // Video + PTSStoreAdd(&card->VideoPTSStore, PTS, PESAddr, LastVAddr); + } else { // Audio + PTSStoreAdd(&card->AudioPTSStore, PTS, PESAddr, LastAAddr); + } + } + } + } else { + //card->LastAddr = Addr; + } + // In case of error, rewind and try again + if (fail) card->LastAddr = FailAddr; + } +} + +void L64021Intr(struct cvdv_cards *card) +{ + u32 SCR_base, SCR_compareV, SCR_compareA; + u32 VideoAddr, AudioAddr, PTS; + int i, a, v, as, vs, ap, vp; + u8 intr[5]; + u8 layer; + long ISRTime, DeltaSyncTime, Offset; + + int used = 0; + u8 err; + + err = DecoderReadByte(card, 0x095); + if (err & 0x17) { + MDEBUG(0, ": Packet Error: 0x%02X\n", err); + } + + ISRTime = 0; // TODO system time + + for (i = 0; i < 5; i++) + if ((intr[i] = DecoderReadByte(card, i))) used = 1; + if (used) { + if (intr[0] & 0x80) { // new field + card->fields++; + + if (card->videoffwd){ + if (!card->videoffwd_last){ + AudioStopDecode(card); + card->videosync = 0; + card->videoskip = card->videoffwd; + card->videoskip = 0; + card->videoffwd_last = 1; + card->videoskip_last = 0; + } else { + if (card->videoskip_last == -1){ + card->videoskip = + card->videoffwd; + } + + if (!card->videoskip) + card->videoskip_last = -1; + else + card->videoffwd_last = + card->videoffwd; + } + } else if( card->videoffwd_last ){ + card->videoffwd_last = 0; +#ifdef DVB + if (card->audiostate.AVSyncState) +#endif + card->videosync = 1; + AudioStartDecode(card); + } + + + if (card->videoslow){ + if (!card->videoslow_last){ + AudioStopDecode(card); + card->videosync = 0; + card->videodelay = card->videoslow; + card->videoskip = 0; + card->videoslow_last = 1; + card->videodelay_last = 0; + } else { + if (card->videodelay_last == -1){ + card->videodelay = + card->videoslow; + } + + if (!card->videodelay) + card->videodelay_last = -1; + else + card->videodelay_last = + card->videodelay; + } + } else if( card->videoslow_last ){ + card->videoslow_last = 0; +#ifdef DVB + if (card->audiostate.AVSyncState) +#endif + card->videosync = 1; + AudioStartDecode(card); + } + + + if (card->videodelay > 0) { + if( (DecoderReadByte(card, 0x0ED) & 0x03) + == 0x00) { + card->videodelay--; + if(card->videodelay){ + DecoderWriteByte(card, 0x0ED, + 0x01); + } else { + DecoderWriteByte(card, 0x0ED, + 0x00); + } + } else { + card->videodelay--; + if(!card->videodelay){ + DecoderWriteByte(card, 0x0ED, + 0x00); + } + } + } else if (card->videoskip > 0) { + if ((DecoderReadByte(card, 0x0EC) & 0x03) + == 0x00) { + if (DecoderReadWord(card, 0x096) > 5){ + // pictures in video ES channel + card->videoskip--; + if(card->videoskip) { + DecoderWriteByte(card, + 0x0EC + ,0x03); + } else { + DecoderWriteByte(card, + 0x0EC + ,0x00); + } + } else { + card->videoskip = 0; + DecoderWriteByte (card, 0x0EC, + 0x00); + } + } + } + + + i = (DecoderReadByte(card, 0x113) & 0xFC) | + (DecoderReadByte(card, 0x114) & 0x01); + v = DecoderGetVideoESLevel(card); + if (card->startingV) { + vs = card->VideoESSize; + if (vs > 0) vp = (100 * v) / vs; + else vp = 0; + if (vp > 90) { + MDEBUG(0,": Delayed Video Decoder start\n"); + card->startingV = 0; + DecoderStartDecode(card); + //DecoderSetVideoPanic(card, 1, 3); + // video panic at 3 pictures + //DecoderSetVideoPanic(card, 0, DecoderGetVideoESSize(card) / 4); // video panic at 25 percent + } + } + a = DecoderGetAudioESLevel(card); + if (card->startingA) { + as = card->AudioESSize; + if (as > 0) ap = (100 * a) / as; + else ap = 0; + if (ap > 90) { + MDEBUG(0,": Delayed Audio Decoder start\n"); + AudioSetPlayMode(card, MAUDIO_PLAY); + if (!AudioStart(card)) { + card->startingA = 0; + } + } + } + if (card->fields >= 250) { // 5 seconds (PAL) + SCR_base = DecoderReadSCR(card, 0x009); + SCR_compareA = DecoderReadSCR(card, 0x014); + SCR_compareV = DecoderReadSCR(card, 0x00D); + if (DecoderReadByte(card, 0x013) & 0x03) + card->fields = 0; + } + } + + if (intr[0] & 0x04) { // First Slice Start Code + if (card->showvideo) { + // Unmute card video if first picture slice detected + VideoSetBackground(card, 0, 0, 0, 0); // Video on black + card->showvideo = 0; + } + } + + if (intr[0] & 0x02 ) { // Aux/User Data Fifo + used = 0; + while ( (used++ < 1000) && + (layer = DecoderReadByte(card, 0x040)) & 0x03){ + card->AuxFifo[card->AuxFifoHead] = + ((layer << 6) & 0x0700) | + DecoderReadByte(card, 0x043); + card->AuxFifoHead = (card->AuxFifoHead + 1) & + FIFO_MASK; + } + if (used < 1000) DecoderReadAuxFifo(card); + used = 0; + + while ( (used++ < 1000) && + (layer = DecoderReadByte(card, 0x041)) & 0x03){ + card->DataFifo[card->DataFifoHead] = + ((layer << 6) & 0x0300) | + DecoderReadByte(card, 0x043); + card->DataFifoHead = (card->DataFifoHead + 1) + & FIFO_MASK; + } + if (used < 1000 ) DecoderReadDataFifo(card); + } + + if ((intr[0] & 0x01) != card->intdecodestatus) { + // decode status + card->intdecodestatus = intr[0] & 0x01; + MDEBUG(0, ": Int - decode status now %s\n", + ((card->intdecodestatus) ? + "running" : "stopped")); + if (card->intdecodestatus) { // now running + //DecoderSetVideoPanic(card, 1, 3); + // video panic at 3 pictures + card->showvideo = 1; + } else { // now stopped + if (card->closing) { + card->closing = 0; + CloseCard(card); + } + } + + } + + if (intr[1] & 0x10) { // Begin Active Video + if (card->highlight_valid) { + for (i = 0; i < 10; i++) + DecoderWriteByte(card, 0x1C0 + i, + card->highlight[i]); + card->highlight_valid = 0; + } + } + if (intr[1] & 0x08) { // SPU Start Code Detected + MDEBUG(0, ": Int - SPU Start Code Detected\n"); + } + + if (intr[1] & 0x04) { // SCR compare audio + MDEBUG(0, ": Int - SCR compare audio\n"); + DecoderDelByte(card, 0x013, 0x01); + AudioStart(card); + } + + if (intr[2] & 0x20) { // DSI PES data ready + DecoderReadNavipack(card); + } + + if (intr[2] & 0x06) { // Audio / Video PES data ready + ReadPESHeaders(card); + } + + if (intr[3] & 0x40) { // CSS + card->css.status = DecoderReadByte(card, 0x0B0); + if (card->css.status&0x01) + card->css.ChallengeReady = 1; + // challenge ready + if (card->css.status&0x02) + card->css.ResponseReady = 1; + // response ready + if (card->css.status&0x04) + card->css.DiskKey = 1; + // Disk key ready + if (card->css.status&0x08) + card->css.Error = 1; + // Disk key error + if (card->css.status&0x10) + card->css.TitleKey = 1; + // Title key ready + if (card->css.status&0x20) + card->css.TitleKeyDiff = 1; + // Title key error + } + + + if (intr[3] & 0x30) { + // Audio/Video ES channel buffer underflow + MDEBUG(1,": Int - ES channel buffer underflow\n"); + if (card->closing) { + card->closing = 0; + CloseCard(card); + } + } + + if (intr[4] & 0x10 ) { // SPU decode error + MDEBUG(1,": Int - SPU decode error: (1CA)=0x%02X\n", + DecoderReadByte(card, 0x1CA)); + DecoderDelByte(card, 0x1A0, 0x01); // SPU decode stop + DecoderSetByte(card, 0x1A0, 0x01); // SPU decode start + } + + // Audio / Video Syncronisation + + if (card->videosync && !card->videoskip && !card->videodelay) { + SCR_base = DecoderReadSCR(card, 0x009); + SCR_compareV = DecoderReadSCR(card, 0x00D); + if (intr[1] & 0x02) { // picture start code detected + DecoderMaskByte(card, 0x011, 0x03, 0x01); + // Set SCR compare/capture mode to capture + DecoderSetByte(card, 0x11, 0x04); + // Set "capture on picture start" + if (intr[1] & 0x01) { + // audio sync code detected + DecoderSetByte(card, 0x11, 0x08); + // Set "capture on audio sync code" + } + VideoAddr = DecoderReadRWAddr(card,0x080); + if (PTSGetPTS(&card->VideoPTSStore, VideoAddr, + &PTS)) { + card->oldVPTS = card->VPTS; + card->VPTS = PTS; + card->VSCR = ((long)SCR_compareV + - (long)PTS) / 2; +// card->VideoTime = ISRTime; + } + } else if (intr[1] & 0x01) { + // audio sync code detected + DecoderMaskByte(card, 0x011, 0x03, 0x01); + // Set SCR compare/capture mode to capture + DecoderSetByte(card, 0x11, 0x08); + // Set "capture on audio sync code" + AudioAddr = DecoderReadRWAddr(card,0x083); + if (PTSGetPTS(&card->AudioPTSStore, AudioAddr, + &PTS)) { + card->oldAPTS = card->APTS; + card->APTS = PTS; + card->ASCR = ((long)SCR_compareV - + (long)PTS) / 2; + } else { + card->ASCR = 0x7FFFFFFF; + } + + if (card->VSCR != 0x7FFFFFFF) { + if (card->ASCR != 0x7FFFFFFF) { + DeltaSyncTime = ISRTime - + card->SyncTime; + card->SyncTime = ISRTime; + + // Calculate Audio and Video SCR difference + Offset = (card->ASCR - + card->VSCR - + (10 * 736)) / 736; + + // if the APTS and SCR are off update SCR to keep SubPic synced + if ((SCR_compareV > card->APTS) + || ((card->APTS - + SCR_compareV) > + 10000)) { + Offset = 0; + SetSCR(card, + card->APTS); + } + + // if more than 3 frames away + if ((Offset > 3) || + (Offset < -3)) { + if (Offset > 0 ) { + card->videodelay = 0; + if (Offset < 100) { + if (Offset < 10) { + card->videodelay = 1; + } else { + card->videodelay = Offset / 2; + if (card->videodelay > 20) { + card->videodelay = 20; + } + } + MDEBUG(0,": <<< Pausing %d\n", card->videodelay); + } else { + } + } else { + card->videoskip = 0; + if (Offset > -100) { + if (Offset < -10) { + card->videoskip = 10; + } else { + card->videoskip = 3; + } + MDEBUG(0, ": >>> FForward %d\n", card->videoskip); + } + } + } else { + } + card->VSCR = 0x7FFFFFFF; + } + } + } + } + } + DecoderWriteByte(card, 0x006, 0x01); // Clear Interrupt Pin +} + +// Enable the IRQ Masks +void L64021InstallIntr(struct cvdv_cards *card) { + u8 data; + + data=0; + data |= 0x80; // new field + data |= 0x40; // audio sync recovery + data |= 0x20; // SPU SCR compare + // data |= 0x10; // SDRAM Transfer Done + // data |= 0x08; // Sequence End Code Detect + data |= 0x04; // First Slice Start Code + data |= 0x02; // Aux/User Data Fifo + data |= 0x01; // decode status + DecoderWriteByte(card, 0x000, (~data) & 0xFF); + + data = 0; + // data |= 0x80; // SCR compare + // data |= 0x40; // SCR Overflow + // data |= 0x20; // Begin Vertical Blank + data |= 0x10; // Begin Active Video + data |= 0x08; // SPU Start Code Detected + data |= 0x04; // SCR compare audio + data |= 0x02; // picture start code detected + data |= 0x01; // audio sync code detected + DecoderWriteByte(card, 0x001, (~data) & 0xFF); + + data = 0; + // data |= 0x80; // DTS video event + // data |= 0x40; // DTS audio event + data |= 0x20; // DSI PES data ready + // data |= 0x10; // Seq end code in video channel + data |= 0x08; // SPU PES data ready + data |= 0x04; // Video PES data ready + data |= 0x02; // Audio PES data ready + // data |= 0x01; // Pack data ready + DecoderWriteByte(card, 0x002, (~data) & 0xFF); + + data = 0; + // data |= 0x80; // Reserved + data |= 0x40; // CSS + data |= 0x20; // Video ES channel buffer underflow + data |= 0x10; // Audio ES channel buffer underflow + // data |= 0x08; // Data Dump channel PES data ready + data |= 0x04; // SPU channel buffer overflow + //data |= 0x02; // Video ES channel buffer overflow + //data |= 0x01; // Audio ES channel buffer overflow + DecoderWriteByte(card, 0x003, (~data) & 0xFF); + + data = 0; +// data |= 0x80; // S/PDIF channel buffer underflow + // data |= 0x40; // packet error + // data |= 0x20; // reserved + data |= 0x10; // SPU decode error +// data |= 0x08; // Audio Sync error +// data |= 0x04; // Audio CRC or illegal bit error +// data |= 0x02; // context error +// data |= 0x01; // VLC or Run length error + DecoderWriteByte(card, 0x004, (~data) & 0xFF); + card->IntInstalled = 1; +} + +int L64021RemoveIntr(struct cvdv_cards *card) { + // Disable the IRQ Masks + DecoderWriteByte(card, 0x000, 0xFF); // No ints + DecoderWriteByte(card, 0x001, 0xFF); // No ints + DecoderWriteByte(card, 0x002, 0xFF); // No ints + DecoderWriteByte(card, 0x003, 0xFF); // No ints + DecoderWriteByte(card, 0x004, 0xFF); // No ints + card->IntInstalled = 0; + return 0; +} + +int L64021Reset(struct cvdv_cards *card) { + L64021RemoveIntr(card); // Stop interrupts + // Reset + MDEBUG(1, ": L64021 Software reset...\n"); + //DecoderSetByte(card, 0x007, 0x20); // reset on + DecoderMaskByte(card, 0x007, 0xE2, 0xE2); // reset on + while (!(DecoderReadByte(card, 0x007) & 0x02)) ; // wait until reset is done + //DecoderDelByte(card, 0x007, 0x20); // reset off + DecoderMaskByte(card, 0x007, 0xE2, 0xC2); // reset off + MDEBUG(1, ": L64021 Software reset done.\n"); + DecoderStopChannel(card); + DecoderStopDecode(card); + DecoderStreamReset(card); + DecoderSetupReset(card); + printk(KERN_INFO LOGNAME ": L64021 Rev. 0x%02X reset successfully.\n", +DecoderReadByte(card, 0x0F5)); + return 0; +} + +int L64021Setup(struct cvdv_cards *card) { + MDEBUG(1, ": -- L64021Setup\n"); + DecoderWriteByte(card, 0x0C1, 0x88); // + switch (card->videomode) { + case NTSC: // NTSC M, N. America, Taiwan, Japan + DecoderMaskByte(card, 0x122, 0x03, 0x01); // Television Standard: NTSC + /* Default values: + DecoderWriteByte(card, 0x116, 90); // Main Reads per Line + DecoderWriteByte(card, 0x11A, 4); // Vline Count Init + DecoderWriteByte(card, 0x11C, 0x13); // Pixel State Reset Value / BT.656 Mode / Sync Active Low + DecoderWriteByte(card, 0x129, 23); // Start- and End Row + DecoderWriteByte(card, 0x12A, 262 & 0xFF); + DecoderWriteByte(card, 0x12B, (262>>4)&0x70); + DecoderWriteByte(card, 0x12C, 244 & 0xFF); // Start- and End Column + DecoderWriteByte(card, 0x12D, 1683 & 0xFF); + DecoderWriteByte(card, 0x12E, ((1683>>4)&0x70)|((244>>8)&0x07)); + DecoderWriteByte(card, 0x132, 240 & 0xFF); // SAV Column + DecoderWriteByte(card, 0x133, 1684 & 0xFF); // EAV Column + DecoderWriteByte(card, 0x134, ((1684>>4)&0x70)|((240>>8)&0x07)); + DecoderWriteByte(card, 0x12F, (21&0x1F)|((262>>3)&0x20)|(1<<6)|((265>>1)&0x80)); // VCode Zero... + DecoderWriteByte(card, 0x130, 262&0xFF); // ... and VCode Even + DecoderWriteByte(card, 0x131, 265&0xFF); // ... and FCode + */ + break; + case PAL: // PAL-B, D, G, H, I, Europe, Asia + DecoderMaskByte(card, 0x122, 0x03, 0x02); // Television Standard: PAL + /* Default values: + DecoderWriteByte(card, 0x116, 90); // Main Reads per Line + DecoderWriteByte(card, 0x11A, 1); // Vline Count Init + DecoderWriteByte(card, 0x11C, 0x13); // Pixel State Reset Value / BT.656 Mode / Sync Active Low + DecoderWriteByte(card, 0x129, 23); // Start- and End Row + DecoderWriteByte(card, 0x12A, 310 & 0xFF); + DecoderWriteByte(card, 0x12B, (310>>4)&0x70); + DecoderWriteByte(card, 0x12C, 264 & 0xFF); // Start- and End Column + DecoderWriteByte(card, 0x12D, 1703 & 0xFF); + DecoderWriteByte(card, 0x12E, ((1703>>4)&0x70)|((264>>8)&0x07)); + DecoderWriteByte(card, 0x132, 260 & 0xFF); // SAV Column + DecoderWriteByte(card, 0x133, 1704 & 0xFF); // EAV Column + DecoderWriteByte(card, 0x134, ((1704>>4)&0x70)|((260>>8)&0x07)); + DecoderWriteByte(card, 0x12F, (21&0x1F)|((310>>3)&0x20)|(0<<6)|((312>>1)&0x80)); // VCode Zero... + DecoderWriteByte(card, 0x130, 310&0xFF); // ... and VCode Even + DecoderWriteByte(card, 0x131, 312&0xFF); // ... and FCode + */ + break; + case PAL60: // PAL 60Hz + case NTSC60: // NTSC 60Hz, USA HDTV + case PALM: // PAL-M normal, Brazil + case PALM60: // PAL-M HDTV, Brazil + case PALN: // PAL-N, Uruguay, Paraguay + case PALNc: // PAL-Nc, Argentinia + default: // TODO: set mode according to other standards + DecoderMaskByte(card, 0x122, 0x03, 0x00); // Television Standard: User programmed + DecoderWriteByte(card, 0x116, 90); // Main Reads per Line + DecoderWriteByte(card, 0x11A, 1); // Vline Count Init + DecoderWriteByte(card, 0x11C, 0x13); // Pixel State Reset Value / BT.656 Mode / Sync Active Low + DecoderWriteByte(card, 0x129, 23); // Start- and End Row + DecoderWriteByte(card, 0x12A, 310 & 0xFF); + DecoderWriteByte(card, 0x12B, (310>>4)&0x70); + DecoderWriteByte(card, 0x12C, 264 & 0xFF); // Start- and End Column + DecoderWriteByte(card, 0x12D, 1703 & 0xFF); + DecoderWriteByte(card, 0x12E, ((1703>>4)&0x70)|((264>>8)&0x07)); + DecoderWriteByte(card, 0x132, 260 & 0xFF); // SAV Column + DecoderWriteByte(card, 0x133, 1704 & 0xFF); // EAV Column + DecoderWriteByte(card, 0x134, ((1704>>4)&0x70)|((260>>8)&0x07)); + DecoderWriteByte(card, 0x12F, (21&0x1F)|((310>>3)&0x20)|(0<<6)|((312>>1)&0x80)); // VCode Zero... + DecoderWriteByte(card, 0x130, 310&0xFF); // ... and VCode Even + DecoderWriteByte(card, 0x131, 312&0xFF); // ... and FCode + break; + } + DecoderWriteByte(card, 0x045, 0x00); // disable compares and panic mode + DecoderWriteByte(card, 0x094, 0x00); // disable TOS Detect + DecoderMaskByte(card, 0x109, 0x30, 0x00); // Display Override off, don't change OSD, Background + DecoderWriteByte(card, 0x112, 0x00); // Disable Horizontal 2:1 Filter + DecoderWriteByte(card, 0x113, 0x14); // FreezeMode 1 / 3:2 Pulldown / Repeat First Field / Top Field First + DecoderWriteByte(card, 0x114, ( 5 <<3)|( 0 <<1)|( 0 <<2)|( 1 <<7)); // VideoMode/FilterEnable/FilterAB/FieldSyncEnable + DecoderWriteByte(card, 0x115, 0); // Horizontal Filter Scale + DecoderWriteByte(card, 0x117, 0x80); // Automatic Field Inversion Correction +// DecoderWriteByte(card, 0x117, 0x00); // no Automatic Field Inversion Correction + DecoderWriteByte(card, 0x118, 0); // Horizontal Pan and Scan Word Offset (signed) + DecoderWriteByte(card, 0x119, 0); // Vertical Pan and Scan Line Offset + DecoderWriteByte(card, 0x11B, 0x00); // Override Picture Width +// if (0) { // letterbox +// DecoderWriteByte(card, 0x114, (DecoderReadByte(card, 0x114) & ~0x78) | 0x40); // mode 8 +// DecoderWriteByte(card, 0x129, 0x35); +// DecoderWriteByte(card, 0x12A, 0xE7); +// DecoderWriteByte(card, 0x114, DecoderReadByte(card, 0x114) & ~0x77); // ??? +// } else { +// if (0) { // MPEG-1 +// DecoderWriteByte(card, 0x114, (DecoderReadByte(card, 0x114) & ~0x78) | 0x10); // mode 2 +// } else { // MPEG-2 +// DecoderWriteByte(card, 0x114, (DecoderReadByte(card, 0x114) & ~0x78) | 0x28); // mode 5 +// } +// } + L64021InstallIntr(card); // Set the interrupt masks, again + + return 0; +} + +int L64021Init(struct cvdv_cards *card) { +MDEBUG(1, ": -- L64021Init\n"); + L64021Reset(card); + L64021Setup(card); + VideoSetBackground(card, 1, 0, 0, 0); // black + DecoderWriteByte(card, 0x135, 0x01); // Enable Video Out, Disable SPU Mix + DecoderWriteByte(card,0x11C,0x13); // Pixel State Reset Value / BT.656 Mode / Sync Active Low + L64021InstallIntr(card); + return 0; +} + + diff -Nru a/drivers/media/video/margi/decoder.h b/drivers/media/video/margi/decoder.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/decoder.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,70 @@ +/* + decoder.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CVDV_DECODER_H +#define CVDV_DECODER_H + +#include "cardbase.h" + + +int DecoderGetNavi(struct cvdv_cards *card, u8 * navidata); + +// returns 1 on overrun, 0 on no error +int DecoderQueueNavi(struct cvdv_cards *card, u8 * navidata); + +u32 ParseSCR(const u8 * scrdata); + +u32 SetSCR(struct cvdv_cards *card, u32 SCR_base); + +void DecoderPause(struct cvdv_cards *card); + +void DecoderUnPause(struct cvdv_cards *card); + +void CloseCard(struct cvdv_cards *card); + + +void DecoderReadAudioInfo(struct cvdv_cards *card); + +void DecoderReadAuxFifo(struct cvdv_cards *card); + +void DecoderReadDataFifo(struct cvdv_cards *card); + +int DecoderReadNavipack(struct cvdv_cards *card); + +int AudioStart(struct cvdv_cards *card); + +// Puts decoder in pause after so many fields +void StepsToPause(struct cvdv_cards *card, int steps); + +void L64021Intr(struct cvdv_cards *card); +//static void L64021Intr(struct cvdv_cards *card); + +// Enable the IRQ Masks +void L64021InstallIntr(struct cvdv_cards *card); + +int L64021RemoveIntr(struct cvdv_cards *card); + +int L64021Reset(struct cvdv_cards *card); + +int L64021Setup(struct cvdv_cards *card); + +int L64021Init(struct cvdv_cards *card); + +#endif /* CVDV_DECODER_H */ diff -Nru a/drivers/media/video/margi/dmxdev.c b/drivers/media/video/margi/dmxdev.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dmxdev.c Wed Feb 13 20:03:59 2002 @@ -0,0 +1,1065 @@ +/* + * dmxdev.c - DVB demultiplexer device + * + * Copyright (C) 2000 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "cardbase.h" +#include "dmxdev.h" + +#ifdef MODULE +MODULE_DESCRIPTION(""); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug,"i"); +#endif +static int debug = 0; + +#define dprintk if (debug) printk + +inline dmxdev_filter_t * +DmxDevFile2Filter(dmxdev_t *dmxdev, struct file *file) +{ + return (dmxdev_filter_t *) file->private_data; +} + +inline dmxdev_dvr_t * +DmxDevFile2DVR(dmxdev_t *dmxdev, struct file *file) +{ + return (dmxdev_dvr_t *) file->private_data; +} + +static inline void +DmxDevBufferInit(dmxdev_buffer_t *buffer) +{ + buffer->data=0; + buffer->size=8192; + buffer->pread=0; + buffer->pwrite=0; + buffer->error=0; + init_waitqueue_head(&buffer->queue); +} + +static inline int +DmxDevBufferWrite(dmxdev_buffer_t *buf, uint8_t *src, int len) +{ + int split; + int free; + int todo; + + if (!len) + return 0; + if (!buf->data) + return 0; + + free=buf->pread-buf->pwrite; + split=0; + if (free<=0) { + free+=buf->size; + split=buf->size-buf->pwrite; + } + if (len>=free) { + dprintk("dmxdev: buffer overflow\n"); + return -1; + } + if (split>=len) + split=0; + todo=len; + if (split) { + memcpy(buf->data + buf->pwrite, src, split); + todo-=split; + buf->pwrite=0; + } + memcpy(buf->data + buf->pwrite, src+split, todo); + buf->pwrite=(buf->pwrite+todo)%buf->size; + return len; +} + +static ssize_t +DmxDevBufferRead(dmxdev_buffer_t *src, int non_blocking, + char *buf, size_t count, loff_t *ppos) +{ + unsigned long todo=count; + int split, avail, error; + + if (!src->data) + return 0; + if ((error=src->error)) { + src->error=0; + return error; + } + + if (non_blocking && (src->pwrite==src->pread)) + return -EWOULDBLOCK; + + while (todo>0) { + if (non_blocking && (src->pwrite==src->pread)) + return (count-todo) ? (count-todo) : -EWOULDBLOCK; + + if (wait_event_interruptible(src->queue, + (src->pread!=src->pwrite) || + (src->error))<0) + return count-todo; + + if ((error=src->error)) { + src->error=0; + return error; + } + + split=src->size; + avail=src->pwrite - src->pread; + if (avail<0) { + avail+=src->size; + split=src->size - src->pread; + } + if (avail>todo) + avail=todo; + if (splitdata+src->pread, split)) + return -EFAULT; + buf+=split; + src->pread=0; + todo-=split; + avail-=split; + } + if (avail) { + if (copy_to_user(buf, src->data+src->pread, avail)) + return -EFAULT; + src->pread = (src->pread + avail) % src->size; + todo-=avail; + buf+=avail; + } + } + return count; +} + +static dmx_frontend_t * +get_fe(dmx_demux_t *demux, int type) +{ + struct list_head *head, *pos; + + head=demux->get_frontends(demux); + if (!head) + return 0; + list_for_each(pos, head) + if (DMX_FE_ENTRY(pos)->source==type) + return DMX_FE_ENTRY(pos); + + return 0; +} + +static inline void +DmxDevDVRStateSet(dmxdev_dvr_t *dmxdevdvr, int state) +{ + spin_lock_irq(&dmxdevdvr->dev->lock); + dmxdevdvr->state=state; + spin_unlock_irq(&dmxdevdvr->dev->lock); +} + +int +DmxDevDVROpen(dmxdev_t *dmxdev, struct file *file) +{ + dmx_frontend_t *front; + + down(&dmxdev->mutex); + if ((file->f_flags&O_ACCMODE)==O_RDWR) { + if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) { + up(&dmxdev->mutex); + return -EOPNOTSUPP; + } + } + + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + DmxDevBufferInit(&dmxdev->dvr_buffer); + dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE; + dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE); + if (!dmxdev->dvr_buffer.data) { + up(&dmxdev->mutex); + return -ENOMEM; + } + } + + if ((file->f_flags&O_ACCMODE)==O_WRONLY) { + dmxdev->dvr_orig_fe=dmxdev->demux->frontend; + + if (!dmxdev->demux->write) { + up(&dmxdev->mutex); + return -EOPNOTSUPP; + } + + front=get_fe(dmxdev->demux, DMX_MEMORY_FE); + + if (!front) { + up(&dmxdev->mutex); + return -EINVAL; + } + dmxdev->demux->disconnect_frontend(dmxdev->demux); + dmxdev->demux->connect_frontend(dmxdev->demux, front); + } + up(&dmxdev->mutex); + return 0; +} + +int +DmxDevDVRClose(dmxdev_t *dmxdev, struct file *file) +{ + down(&dmxdev->mutex); + if ((file->f_flags&O_ACCMODE)==O_WRONLY) { + dmxdev->demux->disconnect_frontend(dmxdev->demux); + dmxdev->demux->connect_frontend(dmxdev->demux, + dmxdev->dvr_orig_fe); + } + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + if (dmxdev->dvr_buffer.data) { + void *mem=dmxdev->dvr_buffer.data; + mb(); + spin_lock_irq(&dmxdev->lock); + dmxdev->dvr_buffer.data=0; + spin_unlock_irq(&dmxdev->lock); + vfree(mem); + } + } + up(&dmxdev->mutex); + return 0; +} + +ssize_t +DmxDevDVRWrite(dmxdev_t *dmxdev, struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + int ret; + + if (!dmxdev->demux->write) + return -EOPNOTSUPP; + if ((file->f_flags&O_ACCMODE)!=O_WRONLY) + return -EINVAL; + down(&dmxdev->mutex); + ret=dmxdev->demux->write(dmxdev->demux, buf, count); + up(&dmxdev->mutex); + return ret; +} + +ssize_t +DmxDevDVRRead(dmxdev_t *dmxdev, struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + int ret; + + down(&dmxdev->mutex); + ret= DmxDevBufferRead(&dmxdev->dvr_buffer, + file->f_flags&O_NONBLOCK, + buf, count, ppos); + up(&dmxdev->mutex); + return ret; +} + +static inline void +DmxDevFilterStateSet(dmxdev_filter_t *dmxdevfilter, int state) +{ + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->state=state; + spin_unlock_irq(&dmxdevfilter->dev->lock); +} + +static int +DmxDevSetBufferSize(dmxdev_filter_t *dmxdevfilter, unsigned long size) +{ + dmxdev_buffer_t *buf=&dmxdevfilter->buffer; + + if (buf->size==size) + return 0; + if (dmxdevfilter->state>=DMXDEV_STATE_GO) + return -EBUSY; + spin_lock_irq(&dmxdevfilter->dev->lock); + if (buf->data) + vfree(buf->data); + buf->data=0; + buf->size=size; + buf->pwrite=buf->pread=0; + spin_unlock_irq(&dmxdevfilter->dev->lock); + + if (buf->size) { + void *mem=vmalloc(dmxdevfilter->buffer.size); + + if (!mem) + return -ENOMEM; + spin_lock_irq(&dmxdevfilter->dev->lock); + buf->data=mem; + spin_unlock_irq(&dmxdevfilter->dev->lock); + } + return 0; +} + +static void +DmxDevFilterTimeout(unsigned long data) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *)data; + + dmxdevfilter->buffer.error=-ETIMEDOUT; + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT; + spin_unlock_irq(&dmxdevfilter->dev->lock); + wake_up(&dmxdevfilter->buffer.queue); +} + +static void +DmxDevFilterTimer(dmxdev_filter_t *dmxdevfilter) +{ + struct dmxSctFilterParams *para=&dmxdevfilter->params.sec; + + del_timer(&dmxdevfilter->timer); + if (para->timeout) { + dmxdevfilter->timer.function=DmxDevFilterTimeout; + dmxdevfilter->timer.data=(unsigned long) dmxdevfilter; + dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000; + add_timer(&dmxdevfilter->timer); + } +} + +static int +DmxDevSectionCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + dmx_section_filter_t *filter, + dmx_success_t success) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) filter->priv; + int ret; + + if (dmxdevfilter->buffer.error) + return 0; + spin_lock(&dmxdevfilter->dev->lock); + if (dmxdevfilter->state!=DMXDEV_STATE_GO) { + spin_unlock(&dmxdevfilter->dev->lock); + return 0; + } + del_timer(&dmxdevfilter->timer); + dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n", + buffer1[0], buffer1[1], + buffer1[2], buffer1[3], + buffer1[4], buffer1[5]); + ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer1, buffer1_len); + if (ret==buffer1_len) { + ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer2, buffer2_len); + } + if (ret<0) { + dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread; + dmxdevfilter->buffer.error=-EBUFFEROVERFLOW; + } + if (dmxdevfilter->params.sec.flags&DMX_ONESHOT) + dmxdevfilter->state=DMXDEV_STATE_DONE; + spin_unlock(&dmxdevfilter->dev->lock); + wake_up(&dmxdevfilter->buffer.queue); + return 0; +} + +static int +DmxDevTSCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + dmx_ts_feed_t *feed, + dmx_success_t success) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) feed->priv; + dmxdev_buffer_t *buffer; + int ret; + + if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) + return 0; + + if (dmxdevfilter->params.pes.output==DMX_OUT_TAP) + buffer=&dmxdevfilter->buffer; + else + buffer=&dmxdevfilter->dev->dvr_buffer; + if (buffer->error) { + wake_up(&buffer->queue); + return 0; + } + ret=DmxDevBufferWrite(buffer, buffer1, buffer1_len); + if (ret==buffer1_len) + ret=DmxDevBufferWrite(buffer, buffer2, buffer2_len); + if (ret<0) { + buffer->pwrite=buffer->pread; + buffer->error=-EBUFFEROVERFLOW; + } + wake_up(&buffer->queue); + return 0; +} + + +/* stop feed but only mark the specified filter as stopped (state set) */ + +static int +DmxDevFeedStop(dmxdev_filter_t *dmxdevfilter) +{ + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + del_timer(&dmxdevfilter->timer); + dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); + break; + case DMXDEV_TYPE_PES: + dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); + break; + default: + return -EINVAL; + } + return 0; +} + + +/* start feed associated with the specified filter */ + +static int +DmxDevFeedStart(dmxdev_filter_t *dmxdevfilter) +{ + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO); + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + dmxdevfilter->feed.sec->start_filtering(dmxdevfilter->feed.sec); + break; + case DMXDEV_TYPE_PES: + dmxdevfilter->feed.ts->start_filtering(dmxdevfilter->feed.ts); + break; + default: + return -EINVAL; + } + return 0; +} + + +/* restart section feed if it has filters left associated with it, + otherwise release the feed */ + +static int +DmxDevFeedRestart(dmxdev_filter_t *dmxdevfilter) +{ + int i; + dmxdev_t *dmxdev=dmxdevfilter->dev; + dvb_pid_t pid=dmxdevfilter->params.sec.pid; + + for (i=0; ifilternum; i++) + if (dmxdev->filter[i].state>=DMXDEV_STATE_GO && + dmxdev->filter[i].type==DMXDEV_TYPE_SEC && + dmxdev->filter[i].pid==pid) { + DmxDevFeedStart(&dmxdev->filter[i]); + return 0; + } + + dmxdevfilter->dev->demux-> + release_section_feed(dmxdev->demux, + dmxdevfilter->feed.sec); + + return 0; +} + +static int +DmxDevFilterStop(dmxdev_filter_t *dmxdevfilter) +{ + if (dmxdevfilter->statetype) { + case DMXDEV_TYPE_SEC: + if (!dmxdevfilter->feed.sec) + break; + DmxDevFeedStop(dmxdevfilter); + if (dmxdevfilter->filter.sec) + dmxdevfilter->feed.sec-> + release_filter(dmxdevfilter->feed.sec, + dmxdevfilter->filter.sec); + DmxDevFeedRestart(dmxdevfilter); + dmxdevfilter->feed.sec=0; + break; + case DMXDEV_TYPE_PES: + if (!dmxdevfilter->feed.ts) + break; + DmxDevFeedStop(dmxdevfilter); + dmxdevfilter->dev->demux-> + release_ts_feed(dmxdevfilter->dev->demux, + dmxdevfilter->feed.ts); + dmxdevfilter->feed.ts=0; + break; + default: + if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED) + return 0; + return -EINVAL; + } + dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0; + return 0; +} + +static inline int +DmxDevFilterReset(dmxdev_filter_t *dmxdevfilter) +{ + if (dmxdevfilter->statetype=DMXDEV_TYPE_NONE; + dmxdevfilter->pid=0xffff; + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED); + return 0; +} + +static int +DmxDevFilterStart(dmxdev_filter_t *dmxdevfilter) +{ + dmxdev_t *dmxdev=dmxdevfilter->dev; + void *mem; + int ret, i; + + if (dmxdevfilter->statestate>=DMXDEV_STATE_GO) + DmxDevFilterStop(dmxdevfilter); + + mem=dmxdevfilter->buffer.data; + if (!mem) { + mem=vmalloc(dmxdevfilter->buffer.size); + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->buffer.data=mem; + spin_unlock_irq(&dmxdevfilter->dev->lock); + if (!dmxdevfilter->buffer.data) + return -ENOMEM; + } + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + { + struct dmxSctFilterParams *para=&dmxdevfilter->params.sec; + dmx_section_filter_t **secfilter=&dmxdevfilter->filter.sec; + dmx_section_feed_t **secfeed=&dmxdevfilter->feed.sec; + + *secfilter=0; + *secfeed=0; + + /* find active filter/feed with same PID */ + for (i=0; ifilternum; i++) + if (dmxdev->filter[i].state>=DMXDEV_STATE_GO && + dmxdev->filter[i].pid==para->pid) { + if (dmxdev->filter[i].type!=DMXDEV_TYPE_SEC) + return -EBUSY; + *secfeed=dmxdev->filter[i].feed.sec; + break; + } + + /* if no feed found, try to allocate new one */ + if (!*secfeed) { + ret=dmxdev->demux-> + allocate_section_feed(dmxdev->demux, + secfeed, + DmxDevSectionCallback); + if (ret<0) { + printk ("could not alloc feed\n"); + return ret; + } + + ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0, + (para->flags & DMX_CHECK_CRC) ? 1 : 0); + + if (ret<0) { + printk ("could not set feed\n"); + DmxDevFeedRestart(dmxdevfilter); + return ret; + } + } + else + DmxDevFeedStop(dmxdevfilter); + + ret=(*secfeed)->allocate_filter(*secfeed, secfilter); + if (ret<0) { + DmxDevFeedRestart(dmxdevfilter); + dmxdevfilter->feed.sec-> + start_filtering(*secfeed); + dprintk ("could not get filter\n"); + return ret; + } + + (*secfilter)->priv=(void *) dmxdevfilter; + memcpy(&((*secfilter)->filter_value[3]), + &(para->filter.filter[1]), DMX_FILTER_SIZE-1); + memcpy(&(*secfilter)->filter_mask[3], + ¶->filter.mask[1], DMX_FILTER_SIZE-1); + (*secfilter)->filter_value[0]=para->filter.filter[0]; + (*secfilter)->filter_mask[0]=para->filter.mask[0]; + (*secfilter)->filter_mask[1]=0; + (*secfilter)->filter_mask[2]=0; + + dmxdevfilter->todo=0; + dmxdevfilter->feed.sec-> + start_filtering(dmxdevfilter->feed.sec); + DmxDevFilterTimer(dmxdevfilter); + break; + } + + case DMXDEV_TYPE_PES: + { + struct timespec timeout = {0 }; + struct dmxPesFilterParams *para=&dmxdevfilter->params.pes; + dmxOutput_t otype; + int ret; + int ts_type; + dmx_ts_pes_t ts_pes; + dmx_ts_feed_t **tsfeed=&dmxdevfilter->feed.ts; + + dmxdevfilter->feed.ts=0; + otype=para->output; + + ts_pes=(dmx_ts_pes_t) para->pesType; + + if (ts_pesdemux->allocate_ts_feed(dmxdev->demux, + tsfeed, + DmxDevTSCallback); + if (ret<0) + return ret; + + (*tsfeed)->priv=(void *) dmxdevfilter; + ret=(*tsfeed)->set(*tsfeed, para->pid, 188, 32768, 0, timeout); + if (ret<0) { + dmxdev->demux-> + release_ts_feed(dmxdev->demux, *tsfeed); + return ret; + } + if ((*tsfeed)->set_type) + ret=(*tsfeed)->set_type(*tsfeed, ts_type, ts_pes); + if (ret<0) { + dmxdev->demux-> + release_ts_feed(dmxdev->demux, *tsfeed); + return ret; + } + dmxdevfilter->feed.ts-> + start_filtering(dmxdevfilter->feed.ts); + break; + } + default: + return -EINVAL; + } + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO); + return 0; +} + +int +DmxDevFilterNum(dmxdev_t *dmxdev) +{ + int i, num; + + if (!dmxdev->filter) + return 0; + down(&dmxdev->mutex); + for (i=0, num=0; ifilternum; i++) + if (dmxdev->filter[i].state==DMXDEV_STATE_FREE) + num++; + up(&dmxdev->mutex); + return num; +} + +int +DmxDevFilterAlloc(dmxdev_t *dmxdev, struct file *file) +{ + int i; + dmxdev_filter_t *dmxdevfilter; + + if (!dmxdev->filter) + return -EINVAL; + down(&dmxdev->mutex); + for (i=0; ifilternum; i++) + if (dmxdev->filter[i].state==DMXDEV_STATE_FREE) + break; + if (i==dmxdev->filternum) { + up(&dmxdev->mutex); + return -EMFILE; + } + dmxdevfilter=&dmxdev->filter[i]; + file->private_data=dmxdevfilter; + + DmxDevBufferInit(&dmxdevfilter->buffer); + dmxdevfilter->type=DMXDEV_TYPE_NONE; + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED); + dmxdevfilter->feed.ts=0; + init_timer(&dmxdevfilter->timer); + + up(&dmxdev->mutex); + //printk("free filters = %d\n", DmxDevFilterNum(dmxdev)); + return 0; +} + +int +DmxDevFilterFree(dmxdev_t *dmxdev, struct file *file) +{ + dmxdev_filter_t *dmxdevfilter; + + down(&dmxdev->mutex); + + if (!(dmxdevfilter=DmxDevFile2Filter(dmxdev, file))) { + up(&dmxdev->mutex); + return -EINVAL; + } + + DmxDevFilterStop(dmxdevfilter); + DmxDevFilterReset(dmxdevfilter); + + if (dmxdevfilter->buffer.data) { + void *mem=dmxdevfilter->buffer.data; + + spin_lock_irq(&dmxdev->lock); + dmxdevfilter->buffer.data=0; + spin_unlock_irq(&dmxdev->lock); + vfree(mem); + } + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_FREE); + wake_up(&dmxdevfilter->buffer.queue); + up(&dmxdev->mutex); + //printk("free filters = %d\n", DmxDevFilterNum(dmxdev)); + return 0; +} + + +static int +DmxDevFilterSet(dmxdev_t *dmxdev, + dmxdev_filter_t *dmxdevfilter, + struct dmxSctFilterParams *params) +{ + dprintk ("function : %s\n", __FUNCTION__); + + DmxDevFilterStop(dmxdevfilter); + + dmxdevfilter->type=DMXDEV_TYPE_SEC; + dmxdevfilter->pid=params->pid; + memcpy(&dmxdevfilter->params.sec, + params, sizeof(struct dmxSctFilterParams)); + + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + if (params->flags&DMX_IMMEDIATE_START) + return DmxDevFilterStart(dmxdevfilter); + + return 0; +} + +static int +DmxDevPesFilterSet(dmxdev_t *dmxdev, + dmxdev_filter_t *dmxdevfilter, + struct dmxPesFilterParams *params) +{ + DmxDevFilterStop(dmxdevfilter); + + if (params->pesType>DMX_PES_OTHER || params->pesType<0) + return -EINVAL; + + dmxdevfilter->type=DMXDEV_TYPE_PES; + dmxdevfilter->pid=params->pid; + memcpy(&dmxdevfilter->params, params, sizeof(struct dmxPesFilterParams)); + + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + if (params->flags&DMX_IMMEDIATE_START) + return DmxDevFilterStart(dmxdevfilter); + + return 0; +} + +int +DmxDevInit(dmxdev_t *dmxdev) +{ + int i; + + if (dmxdev->demux->open(dmxdev->demux)<0) + return -EUSERS; + + dmxdev->filter=vmalloc(dmxdev->filternum*sizeof(dmxdev_filter_t)); + if (!dmxdev->filter) + return -ENOMEM; + + dmxdev->dvr=vmalloc(dmxdev->filternum*sizeof(dmxdev_dvr_t)); + if (!dmxdev->dvr) { + vfree(dmxdev->filter); + dmxdev->filter=0; + return -ENOMEM; + } + sema_init(&dmxdev->mutex, 1); + spin_lock_init(&dmxdev->lock); + for (i=0; ifilternum; i++) { + dmxdev->filter[i].dev=dmxdev; + dmxdev->filter[i].buffer.data=0; + DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE); + dmxdev->dvr[i].dev=dmxdev; + dmxdev->dvr[i].buffer.data=0; + DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE); + DmxDevDVRStateSet(&dmxdev->dvr[i], DMXDEV_STATE_FREE); + } + DmxDevBufferInit(&dmxdev->dvr_buffer); + MOD_INC_USE_COUNT; + return 0; +} + +void +DmxDevRelease(dmxdev_t *dmxdev) +{ + if (dmxdev->filter) { + vfree(dmxdev->filter); + dmxdev->filter=0; + } + if (dmxdev->dvr) { + vfree(dmxdev->dvr); + dmxdev->dvr=0; + } + dmxdev->demux->close(dmxdev->demux); + MOD_DEC_USE_COUNT; +} + +static ssize_t +DmxDevReadSec(dmxdev_filter_t *dfil, struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + int result, hcount; + int done=0; + + if (dfil->todo<=0) { + hcount=3+dfil->todo; + if (hcount>count) + hcount=count; + result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK, + buf, hcount, ppos); + if (result<0) { + dfil->todo=0; + return result; + } + if (copy_from_user(dfil->secheader-dfil->todo, buf, result)) + return -EFAULT; + buf+=result; + done=result; + count-=result; + dfil->todo-=result; + if (dfil->todo>-3) + return done; + dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff; + if (!count) + return done; + } + if (count>dfil->todo) + count=dfil->todo; + result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK, + buf, count, ppos); + if (result<0) + return result; + dfil->todo-=result; + return (result+done); +} + + +ssize_t +DmxDevRead(dmxdev_t *dmxdev, struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(dmxdev, file); + int ret=0; + + down(&dmxdev->mutex); + if (dmxdevfilter->type==DMXDEV_TYPE_SEC) + ret=DmxDevReadSec(dmxdevfilter, file, buf, count, ppos); + else + ret=DmxDevBufferRead(&dmxdevfilter->buffer, + file->f_flags&O_NONBLOCK, + buf, count, ppos); + up(&dmxdev->mutex); + return ret; +} + + +int DmxDevIoctl(dmxdev_t *dmxdev, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void *parg=(void *)arg; + int ret=0; + + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(dmxdev, file); + + if (!dmxdevfilter) + return -EINVAL; + + down(&dmxdev->mutex); + switch (cmd) { + case DMX_START: + if (dmxdevfilter->statedemux->get_pes_pids) { + ret=-EINVAL; + break; + } + dmxdev->demux->get_pes_pids(dmxdev->demux, pids); + if (copy_to_user(parg, pids, 5*sizeof(dvb_pid_t))) + ret=-EFAULT; + break; + } + + default: + ret=-EINVAL; + } + up(&dmxdev->mutex); + return ret; +} + +unsigned int +DmxDevPoll(dmxdev_t *dmxdev, struct file *file, poll_table * wait) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(dmxdev, file); + + if (!dmxdevfilter) + return -EINVAL; + + if (dmxdevfilter->state==DMXDEV_STATE_FREE) + return 0; + + if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite || + dmxdevfilter->buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI); + + if (dmxdevfilter->state!=DMXDEV_STATE_GO) + return 0; + + poll_wait(file, &dmxdevfilter->buffer.queue, wait); + + if (dmxdevfilter->state==DMXDEV_STATE_FREE) + return 0; + + if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite || + dmxdevfilter->buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI); + + return 0; +} + +int DmxDevDVRIoctl(dmxdev_t *dmxdev, struct file *file, + unsigned int cmd, unsigned long arg) +{ + //void *parg=(void *)arg; + int ret=0; + + down(&dmxdev->mutex); + switch (cmd) { + case DMX_SET_BUFFER_SIZE: + // FIXME: implement + ret=0; + break; + + default: + ret=-EINVAL; + } + up(&dmxdev->mutex); + return ret; +} + +unsigned int +DmxDevDVRPoll(dmxdev_t *dmxdev, struct file *file, poll_table * wait) +{ + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + poll_wait(file, &dmxdev->dvr_buffer.queue, wait); + + if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + return 0; + } else + return (POLLOUT | POLLWRNORM | POLLPRI); +} + + +#ifdef MODULE +#ifdef EXPORT_SYMTAB +EXPORT_SYMBOL(DmxDevInit); +EXPORT_SYMBOL(DmxDevRelease); + +EXPORT_SYMBOL(DmxDevDVROpen); +EXPORT_SYMBOL(DmxDevDVRClose); +EXPORT_SYMBOL(DmxDevDVRRead); +EXPORT_SYMBOL(DmxDevDVRWrite); +EXPORT_SYMBOL(DmxDevDVRIoctl); +EXPORT_SYMBOL(DmxDevDVRPoll); + +EXPORT_SYMBOL(DmxDevFilterAlloc); +EXPORT_SYMBOL(DmxDevFilterFree); +EXPORT_SYMBOL(DmxDevRead); +EXPORT_SYMBOL(DmxDevIoctl); +EXPORT_SYMBOL(DmxDevPoll); +#endif +#endif diff -Nru a/drivers/media/video/margi/dmxdev.h b/drivers/media/video/margi/dmxdev.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dmxdev.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,154 @@ +/* + * dmxdev.h + * + * Copyright (C) 2000 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DMXDEV_H_ +#define _DMXDEV_H_ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#ifdef __DVB_PACK__ +#include "ost/demux.h" +#include "ost/dmx.h" +#else +#include +#include +#endif +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < 0x020300 +#define WAIT_QUEUE struct wait_queue* +#define init_waitqueue_head(wq) *(wq) = NULL; +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) +#else +#define WAIT_QUEUE wait_queue_head_t +#endif + +typedef enum { + DMXDEV_TYPE_NONE, + DMXDEV_TYPE_SEC, + DMXDEV_TYPE_PES, +} dmxdev_type_t; + +typedef enum { + DMXDEV_STATE_FREE, + DMXDEV_STATE_ALLOCATED, + DMXDEV_STATE_SET, + DMXDEV_STATE_GO, + DMXDEV_STATE_DONE, + DMXDEV_STATE_TIMEDOUT +} dmxdev_state_t; + +typedef struct dmxdev_buffer_s { + uint8_t *data; + uint32_t size; + int32_t pread; + int32_t pwrite; + WAIT_QUEUE queue; + int error; +} dmxdev_buffer_t; + + +typedef struct dmxdev_filter_s { + union { + dmx_pes_filter_t *pes; + dmx_section_filter_t *sec; + } filter; + + union { + dmx_ts_feed_t *ts; + dmx_section_feed_t *sec; + } feed; + + union { + struct dmxSctFilterParams sec; + struct dmxPesFilterParams pes; + } params; + + int type; + dmxdev_state_t state; + struct dmxdev_s *dev; + dmxdev_buffer_t buffer; + + // only for sections + struct timer_list timer; + int todo; + uint8_t secheader[3]; + + u16 pid; +} dmxdev_filter_t; + + +typedef struct dmxdev_dvr_s { + int state; + struct dmxdev_s *dev; + dmxdev_buffer_t buffer; +} dmxdev_dvr_t; + + +typedef struct dmxdev_s { + dmxdev_filter_t *filter; + dmxdev_dvr_t *dvr; + dmx_demux_t *demux; + + int filternum; + int capabilities; +#define DMXDEV_CAP_DUPLEX 1 + dmx_frontend_t *dvr_orig_fe; + + dmxdev_buffer_t dvr_buffer; +#define DVR_BUFFER_SIZE (512*1024) + + struct semaphore mutex; + spinlock_t lock; +} dmxdev_t; + + +int DmxDevInit(dmxdev_t *dmxdev); +void DmxDevRelease(dmxdev_t *dmxdev); + +int DmxDevFilterAlloc(dmxdev_t *dmxdev, struct file *file); +int DmxDevFilterFree(dmxdev_t *dmxdev, struct file *file); +int DmxDevIoctl(dmxdev_t *dmxdev, struct file *file, + unsigned int cmd, unsigned long arg); +unsigned int DmxDevPoll(dmxdev_t *dmxdev, struct file *file, poll_table * wait); +ssize_t DmxDevRead(dmxdev_t *dmxdev, struct file *file, + char *buf, size_t count, loff_t *ppos); + +int DmxDevDVROpen(dmxdev_t *dmxdev, struct file *file); +int DmxDevDVRClose(dmxdev_t *dmxdev, struct file *file); +ssize_t DmxDevDVRWrite(dmxdev_t *dmxdev, struct file *file, + const char *buf, size_t count, loff_t *ppos); +ssize_t DmxDevDVRRead(dmxdev_t *dmxdev, struct file *file, + char *buf, size_t count, loff_t *ppos); +int DmxDevDVRIoctl(dmxdev_t *dmxdev, struct file *file, + unsigned int cmd, unsigned long arg); +unsigned int DmxDevDVRPoll(dmxdev_t *dmxdev, struct file *file, poll_table * wait); + +#endif /* _DMXDEV_H_ */ diff -Nru a/drivers/media/video/margi/dram.c b/drivers/media/video/margi/dram.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dram.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,515 @@ +/* + dram.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + ///////////////////////////////// + // // + // L64021 DRAM Memory Access // + // // +///////////////////////////////// + +#define __NO_VERSION__ + +#include "dram.h" +#include "l64021.h" + +#define EMERGENCYCOUNTER 5 + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: bytes (8 byte aligned, remainder will be filled with fill value) + // data: fill value +// returns 0 on success, -1 on collision with DMA transfer +int DRAMFillByte(struct cvdv_cards *card, u32 where, int size, u8 data) +{ + int i, j, k, n; + u8 volatile flag; + + size = (size >> 3) + ((size & 7) ? 1 : 0); // 8 bytes at a time, padding with garbage + where >>= 2; // 8 byte aligned data + DecoderSetByte(card, 0x0C1, 0x08); +//TODO: 0x80? + + DecoderWriteByte(card, 0x0C6, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C5, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C4, (u8) (where & 0x000000FFL)); + i = 0; + for (j = 0; j < size; j++) { + for (k = 0; k < 8; k++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x08) && n--); + if (n<0) + return -1; + DecoderWriteByte(card, 0x0C3, data); + } + } + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x04) && n--); + return ((n>=0) ? 0 : -1); +} + + // where: 21 bit DRAM Word-Address, 8 byte aligned + // size: bytes (8 byte aligned, remainder will be filled with garbage) + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMWriteByte(struct cvdv_cards *card, u32 where, int size, u8 * data, + int swapburst) +{ + int i, j, k, n; + u8 volatile flag; + + size = (size >> 3) + ((size & 7) ? 1 : 0); // 8 bytes at a time, padding with garbage + where >>= 2; // 8 byte aligned data + MDEBUG(4, ": Moving %d 64-bit-words to DRAM 0x%08X\n",size,where); + //if (swap) DecoderDelByte(card,0x0C1,0x08); // byte swapping of 8 byte bursts + //else DecoderSetByte(card,0x0C1,0x08); // no byte swapping + DecoderSetByte(card, 0x0C1, 0x08); // no byte swapping + + DecoderWriteByte(card, 0x0C6, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C5, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C4, (u8) (where & 0x000000FFL)); + i = 0; + if (swapburst) { + for (j = 0; j < size; j++) { + for (k = 7; k >= 0; k--) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO full + flag = + DecoderReadByte(card, 0x0C0); + } while ((flag & 0x08) && n--); + if (n<0) + return -1; + DecoderWriteByte(card, 0x0C3, data[i + k]); + } + i += 8; + } + } else { + for (j = 0; j < size; j++) { + for (k = 0; k < 8; k++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO full + flag = + DecoderReadByte(card, 0x0C0); + } while ((flag & 0x08) && n--); + if (n<0) + return -1; + DecoderWriteByte(card, 0x0C3, data[i++]); + } + } + } + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x04) && n--); + return ((n>=0) ? 0 : -1); +} + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words (4 word aligned, remainder will be filled with garbage) + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMWriteWord(struct cvdv_cards *card, u32 where, int size, u16 * data, + int swap) +{ + int i, j, k, n; + u8 volatile flag; + + size = (size >> 2) + ((size & 3) ? 1 : 0); // 4 words at a time, padding with garbage + where >>= 2; // 8 byte aligned data + MDEBUG(4, ": Moving %d 64-bit-words to DRAM 0x%08X\n",size,where); +//TODO: swap manually + if (swap) + DecoderDelByte(card, 0x0C1, 0x08); // byte swapping of 8 byte bursts + else + DecoderSetByte(card, 0x0C1, 0x08); // no byte swapping + + DecoderWriteByte(card, 0x0C6, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C5, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C4, (u8) (where & 0x000000FFL)); + i = 0; + for (j = 0; j < size; j++) { + for (k = 0; k < 4; k++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x08) && n--); + if (n<0) + return -1; + DecoderWriteByte(card, 0x0C3, data[i] >> 8); + n = EMERGENCYCOUNTER; + do { // wait if FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x08) && n--); + if (n<0) + return -1; + DecoderWriteByte(card, 0x0C3, data[i++]); + } + } + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x04) && n--); + return ((n>=0) ? 0 : -1); +} + + // where: 21 bit DRAM Word-Address, 8 byte aligned + // size: bytes + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMReadByte(struct cvdv_cards *card, u32 where, int size, u8 * data, + int swap) +{ + int i, j, rsize, n; + u8 volatile flag; + + rsize = size & 7; // padding bytes + size = size >> 3; // 8 bytes at a time + where >>= 2; // 8 byte aligned data + MDEBUG(4, ": Moving %d 64-bit-words to DRAM 0x%08X\n",size,where); +//TODO: swap manually + if (swap) + DecoderDelByte(card, 0x0C1, 0x08); // byte swapping of 8 byte bursts + else + DecoderSetByte(card, 0x0C1, 0x08); // no byte swapping + + DecoderWriteByte(card, 0x0C9, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C8, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C7, (u8) (where & 0x000000FFL)); + i = 0; + for (j = 0; j < size; j++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + if (n<0) // WARNING nicht if(!n) + return -1; + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + data[i++] = DecoderReadByte(card, 0x0C2); + } + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + if (n<0) + return -1; + for (j = 0; j < rsize; j++) + data[i++] = DecoderReadByte(card, 0x0C2); + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x02) && n--); + return ((n>=0) ? 0 : -1); +} + + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMReadWord(struct cvdv_cards *card, u32 where, int size, u16 * data, + int swap) +{ + int i, j, rsize, n; + u8 volatile flag; + u8 b; + + rsize = size & 3; // padding words + size >>= 2; // 4 words at a time + where >>= 2; // 8 byte aligned data + MDEBUG(4, ": Reading %d 64-bit-words and %d 16-bit-words from DRAM 0x%08X\n", + size,rsize,where); +//TODO: swap manually + if (swap) + DecoderDelByte(card, 0x0C1, 0x08); // byte swapping of 8 byte bursts + else + DecoderSetByte(card, 0x0C1, 0x08); // no byte swapping + + DecoderWriteByte(card, 0x0C9, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C8, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C7, (u8) (where & 0x000000FFL)); + i = 0; + for (j = 0; j < size; j++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + if (n<0) + return -1; + b = DecoderReadByte(card, 0x0C2); + data[i++] = ((b << 8) | DecoderReadByte(card, 0x0C2)); + b = DecoderReadByte(card, 0x0C2); + data[i++] = ((b << 8) | DecoderReadByte(card, 0x0C2)); + b = DecoderReadByte(card, 0x0C2); + data[i++] = ((b << 8) | DecoderReadByte(card, 0x0C2)); + b = DecoderReadByte(card, 0x0C2); + data[i++] = ((b << 8) | DecoderReadByte(card, 0x0C2)); + } + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + if (n<0) + return -1; + for (j = 0; j < rsize; j++) { + b = DecoderReadByte(card, 0x0C2); + data[i++] = ((b << 8) | DecoderReadByte(card, 0x0C2)); + } + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x02) && n--); + return ((n>=0) ? 0 : -1); +} + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) + // returns -1 on success (equal content), + // word position on error (compare failure), +// -2 on collision with DMA transfer +int DRAMVerifyWord(struct cvdv_cards *card, u32 where, int size, + u16 * data, int swap) +{ + int i, j, rsize, n; + u8 volatile flag, b; + + rsize = size & 3; // padding words + size >>= 2; // 4 words at a time + where >>= 2; // 8 byte aligned data, now 19 bit 64-bit-word-address +//TODO: swap manually + if (swap) + DecoderDelByte(card, 0x0C1, 0x08); // byte swapping of 8 byte bursts + else + DecoderSetByte(card, 0x0C1, 0x08); // no byte swapping + + DecoderWriteByte(card, 0x0C9, (u8) ((where >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0C8, (u8) ((where >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0C7, (u8) (where & 0x000000FFL)); + i = 0; + for (j = 0; j < size; j++) { + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + b = DecoderReadByte(card, 0x0C2); + if (data[i++] != ((b << 8) | DecoderReadByte(card, 0x0C2))) + return i; + b = DecoderReadByte(card, 0x0C2); + if (data[i++] != ((b << 8) | DecoderReadByte(card, 0x0C2))) + return i; + b = DecoderReadByte(card, 0x0C2); + if (data[i++] != ((b << 8) | DecoderReadByte(card, 0x0C2))) + return i; + b = DecoderReadByte(card, 0x0C2); + if (data[i++] != ((b << 8) | DecoderReadByte(card, 0x0C2))) + return i; + } + n = EMERGENCYCOUNTER; + do { // wait if FIFO empty + flag = DecoderReadByte(card, 0x0C0); + } while ((flag & 0x01) && n--); + for (j = 0; j < rsize; j++) { + b = DecoderReadByte(card, 0x0C2); + if (data[i++] != ((b << 8) | DecoderReadByte(card, 0x0C2))) + return i; + } + flag = DecoderReadByte(card, 0x0C0); + n = EMERGENCYCOUNTER; + do { // wait for FIFO full + flag = DecoderReadByte(card, 0x0C0); + } while (!(flag & 0x02) && n--); + return -1; +} + + // WARNING: better not use this one. It can collide with normal DRAM access and other DMA transfers + // If you want to use it, implement card->DMAMoveBusy in all other DMA functions, initialisation, and header file + // source, destination: 21 bit DRAM Word-Address, 4 word aligned + // size: byte (8 byte aligned, hang over bytes will NOT be moved) + // returns 0 on success on success, + // -1 on collision with DMA transfer, +// -2 on interrupt handler not installed +int DRAMMove(struct cvdv_cards *card, u32 source, u32 destination, + int size) +{ + if (!card->IntInstalled) + return -2; + if (card->DMAABusy || card->DMABBusy) + return -1; + + size >>= 3; // 64-bit-words + source >>= 2; // 8 byte aligned data, + destination >>= 2; // now 19 bit 64-bit-word-address + + DecoderDelByte(card, 0x0C1, 0x06); // DMA idle + + DecoderWriteByte(card, 0x0DA, (u8) ((source >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0D9, (u8) ((source >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0D8, (u8) (source & 0x000000FFL)); + DecoderWriteByte(card, 0x0D7, + (u8) ((destination >> 16) & 0x00000007L)); + DecoderWriteByte(card, 0x0D6, + (u8) ((destination >> 8) & 0x000000FFL)); + DecoderWriteByte(card, 0x0D5, (u8) (destination & 0x000000FFL)); + + //card->DMAMoveBusy=1; // would have to catch that in all the other DMA routines + DecoderSetByte(card, 0x0C1, 0x06); // DMA block move + + return 0; +} + + // size in words + // align: number of words on wich start of block will be aligned +// return value is 21 bit word address, or 0xFFFFFFFF on error +u32 DRAMAlloc(struct cvdv_cards * card, u32 size, int align) +{ + struct DRAMBlock *ptr, *ptr2; + u32 addr = 0; + u32 alignmask = align - 1; + int valid = 0; + + printk("DRAMAlloc %d bytes (from %d).\n", size, card->DRAMSize); + + if (size == 0) + { + printk("DRAMAlloc - 0 size.\n"); + return BLANK; + } + + if (size & 3) + size = (size & ~3) + 4; // increase size if not 64 bit aligned + + printk("DRAMAlloc %d bytes.\n", size); + if (card->DRAMFirstBlock == NULL) { // virgin territory? + valid = ((addr + size) <= card->DRAMSize); // does it fit at all? + } else { + addr = 0; + valid = ((addr + size) <= card->DRAMSize); // does it fit at all? + for (ptr2 = card->DRAMFirstBlock; + (ptr2 != NULL) && (valid); ptr2 = ptr2->next) { // check against all existing blocks + if ((ptr2->start >= addr) + && (ptr2->start < (addr + size))) + valid = 0; // existing block start inside new block? + else if (((ptr2->start + ptr2->length) > addr) + && ((ptr2->start + ptr2->length) <= + (addr + size))) + valid = 0; // existing block end inside new block? + else if ((ptr2->start < addr) + && ((ptr2->start + ptr2->length) > + (addr + size))) valid = 0; // new block inside existing block? + } + for (ptr = card->DRAMFirstBlock; (ptr != NULL) && (!valid); + ptr = ptr->next) { // check all existing blocks + addr = ptr->start + ptr->length; // assume, after this block is free space + if (addr & alignmask) + addr = (addr & ~alignmask) + align; // round up to alignation border + valid = ((addr + size) <= card->DRAMSize); // does it fit at all? + for (ptr2 = card->DRAMFirstBlock; + (ptr2 != NULL) && (valid); ptr2 = ptr2->next) { // check against all existing blocks + if ((ptr2->start >= addr) + && (ptr2->start < (addr + size))) + valid = 0; // existing block start inside new block? + else + if ( + ((ptr2->start + ptr2->length) > + addr) + && ((ptr2->start + ptr2->length) <= + (addr + size))) + valid = 0; // existing block end inside new block? + else if ((ptr2->start < addr) + && ((ptr2->start + ptr2->length) > + (addr + size))) + valid = 0; // new block inside existing block? + } + } + } + if (valid) { // The new block fits + ptr = (struct DRAMBlock *) kmalloc(sizeof(struct DRAMBlock), GFP_KERNEL); + if (ptr == NULL) { + printk(KERN_INFO LOGNAME ": ERROR: out of kernel memory for block info. Please reboot if possible.\n"); + return BLANK; // out of kernel mem + } + if (card->DRAMFirstBlock == NULL) { + card->DRAMFirstBlock = ptr; + } else { + ptr2 = card->DRAMFirstBlock; + while (ptr2->next != NULL) + ptr2 = ptr2->next; + ptr2->next = ptr; + } + ptr->next = NULL; + ptr->start = addr; + ptr->length = size; + MDEBUG(1,": DRAM Allocate 0x%08X-0x%08X\n", addr, + addr + size - 1); + + printk("DRAMAlloc ok\n"); + return addr; + } + printk(KERN_ERR "DRAMAlloc: No card memory.\n"); + return BLANK; +} + + // addr is the return value of that resp. DRAMAlloc call +// returns 0 on success (always) +int DRAMFree(struct cvdv_cards *card, u32 addr) +{ + struct DRAMBlock *ptr, *ptr2; + ptr2 = NULL; + for (ptr = card->DRAMFirstBlock; ptr != NULL; ptr = ptr->next) { // check all existent blocks + if (addr == ptr->start) { // this is our block to be removed + if (ptr2 == NULL) + card->DRAMFirstBlock = ptr->next; + else + ptr2->next = ptr->next; + kfree(ptr); + MDEBUG(1, ": DRAM Free 0x%08X\n", addr); + } else + ptr2 = ptr; + } + return 0; +} + + // free all blocks +// returns 0 on success (always) +int DRAMRelease(struct cvdv_cards *card) +{ + struct DRAMBlock *ptr, *ptr2; + MDEBUG(1, ": -- DRAMRelease\n"); + for (ptr = card->DRAMFirstBlock; ptr != NULL; ptr = ptr2) { // check all existent blocks + ptr2 = ptr->next; + MDEBUG(4, ": kfree(0x%08X)\n",(int)ptr); + kfree(ptr); + } + card->DRAMFirstBlock = NULL; + return 0; +} diff -Nru a/drivers/media/video/margi/dram.h b/drivers/media/video/margi/dram.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dram.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,99 @@ +/* + dram.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef DRAM_H +#define DRAM_H + + ///////////////////////////////// + // // + // L64021 DRAM Memory Access // + // // +///////////////////////////////// + +#include "cardbase.h" + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: bytes (8 byte aligned, remainder will be filled with fill value) + // data: fill value +// returns 0 on success, -1 on collision with DMA transfer +int DRAMFillByte(struct cvdv_cards *card, u32 where, int size, u8 data); + + // where: 21 bit DRAM Word-Address, 8 byte aligned + // size: bytes (8 byte aligned, remainder will be filled with garbage) + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMWriteByte(struct cvdv_cards *card, u32 where, int size, u8 * data, + int swapburst); + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words (4 word aligned, remainder will be filled with garbage) + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMWriteWord(struct cvdv_cards *card, u32 where, int size, u16 * data, + int swap); + + // where: 21 bit DRAM Word-Address, 8 byte aligned + // size: bytes + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMReadByte(struct cvdv_cards *card, u32 where, int size, u8 * data, + int swap); + + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) +// returns 0 on success, -1 on collision with DMA transfer +int DRAMReadWord(struct cvdv_cards *card, u32 where, int size, u16 * data, + int swap); + + // where: 21 bit DRAM Word-Address, 4 word aligned + // size: words + // swap: 0=normal mode, 1=write each 8 bytes on reverse order (7,6,5,4,3,2,1,0,15,14,13,etc.) + // returns -1 on success (equal content), + // word position on error (compare failure), +// -2 on collision with DMA transfer +int DRAMVerifyWord(struct cvdv_cards *card, u32 where, int size, + u16 * data, int swap); + + // WARNING: better not use this one. It can collide with normal DRAM access and other DMA transfers + // If you want to use it, implement card->DMAMoveBusy in all other DMA functions, initialisation, and header file + // source, destination: 21 bit DRAM Word-Address, 4 word aligned + // size: byte (8 byte aligned, hang over bytes will NOT be moved) + // returns 0 on success on success, + // -1 on collision with DMA transfer, +// -2 on interrupt handler not installed +int DRAMMove(struct cvdv_cards *card, u32 source, u32 destination, + int size); + + // size in words + // align: number of words on wich start of block will be aligned +// return value is 21 bit word address, or 0xFFFFFFFF on error +u32 DRAMAlloc(struct cvdv_cards *card, u32 size, int align); + + // addr is the return value of that resp. DRAMAlloc call +// returns 0 on success (always) +int DRAMFree(struct cvdv_cards *card, u32 addr); + + // free all blocks +// returns 0 on success (always) +int DRAMRelease(struct cvdv_cards *card); + +#endif /* DRAM_H */ diff -Nru a/drivers/media/video/margi/dvb_demux.c b/drivers/media/video/margi/dvb_demux.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvb_demux.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,1169 @@ +/* + * dvb_demux.c - DVB kernel demux API + * + * Copyright (C) 2000-2001 Ralph Metzler + * & Marcus Metzler + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#include "dvb_demux.h" + +#ifdef MODULE +MODULE_DESCRIPTION(""); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif + +#define NOBUFS + +LIST_HEAD(dmx_muxs); + +int dmx_register_demux(dmx_demux_t *demux) +{ + struct list_head *pos, *head=&dmx_muxs; + + if (!(demux->id && demux->vendor && demux->model)) + return -EINVAL; + list_for_each(pos, head) + { + if (!strcmp(DMX_DIR_ENTRY(pos)->id, demux->id)) + return -EEXIST; + } + demux->users=0; + list_add(&(demux->reg_list), head); + MOD_INC_USE_COUNT; + return 0; +} + +int dmx_unregister_demux(dmx_demux_t* demux) +{ + struct list_head *pos, *head=&dmx_muxs; + + list_for_each(pos, head) + { + if (DMX_DIR_ENTRY(pos)==demux) + { + if (demux->users>0) + return -EINVAL; + list_del(pos); + MOD_DEC_USE_COUNT; + return 0; + } + } + return -ENODEV; +} + + +struct list_head *dmx_get_demuxes(void) +{ + if (list_empty(&dmx_muxs)) + return NULL; + + return &dmx_muxs; +} + +/****************************************************************************** + * static inlined helper functions + ******************************************************************************/ + +static inline u16 +section_length(const u8 *buf) +{ + return 3+((buf[1]&0x0f)<<8)+buf[2]; +} + +static inline u16 +ts_pid(const u8 *buf) +{ + return ((buf[1]&0x1f)<<8)+buf[2]; +} + +static inline int +payload(const u8 *tsp) +{ + if (!(tsp[3]&0x10)) // no payload? + return 0; + if (tsp[3]&0x20) { // adaptation field? + if (tsp[4]>183) // corrupted data? + return 0; + else + return 184-1-tsp[4]; + } + return 184; +} + + +static u32 +dvb_crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; + +u32 dvb_crc32(u8 *data, int len) +{ + int i; + u32 crc = 0xffffffff; + + for (i=0; i> 24) ^ *data++) & 0xff]; + return crc; +} + +void dvb_set_crc32(u8 *data, int length) +{ + u32 crc; + + crc=dvb_crc32(data,length); + data[length] = (crc>>24)&0xff; + data[length+1] = (crc>>16)&0xff; + data[length+2] = (crc>>8)&0xff; + data[length+3] = (crc)&0xff; +} + + +/****************************************************************************** + * Software filter functions + ******************************************************************************/ + +static inline int +DvbDmxSWFilterPayload(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + int p, count; + //int ccok; + //u8 cc; + + if (!(count=payload(buf))) + return -1; + p=188-count; + /* + cc=buf[3]&0x0f; + ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0; + dvbdmxfeed->cc=cc; + if (!ccok) + printk("missed packet!\n"); + */ + if (buf[1]&0x40) // PUSI ? + dvbdmxfeed->peslen=0xfffa; + dvbdmxfeed->peslen+=count; + + return dvbdmxfeed->cb.ts((u8 *)&buf[p], count, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); +} + + +static int +DvbDmxSWFilterSectionFilter(dvb_demux_feed_t *dvbdmxfeed, + dvb_demux_filter_t *dvbdmxfilter) +{ + dmx_section_filter_t *filter=&dvbdmxfilter->filter; +#if 1 + int i; + + for (i=0; ifilter_mask[i]& + (filter->filter_value[i]^dvbdmxfeed->secbuf[i])) + return 0; +#else + u32 res; + u32 *val=(u32 *)(filter->filter_value); + u32 *mask=(u32 *)(filter->filter_mask); + u32 *data=(u32 *)(dvbdmxfeed->secbuf); + + res=mask[0]&(val[0]^data[0]); + if (res) return 0; + + res=mask[1]&(val[1]^data[1]); + if (res) return 0; + + res=mask[2]&(val[2]^data[2]); + if (res) return 0; + + res=mask[3]&(val[3]^data[3]); + if (res) return 0; + + res=*(u16 *)(4+mask) & (*(u16 *)(4+val) ^ *(u16 *)(4+data)); + if (res) return 0; +#endif + + return dvbdmxfeed->cb.sec(dvbdmxfeed->secbuf, dvbdmxfeed->seclen, + 0, 0, filter, DMX_OK); +} + +static inline int +DvbDmxSWFilterSectionFeed(dvb_demux_feed_t *dvbdmxfeed) +{ + u8 *buf=dvbdmxfeed->secbuf; + dvb_demux_filter_t *f; + + if (dvbdmxfeed->secbufp!=dvbdmxfeed->seclen) + return -1; + if (!dvbdmxfeed->feed.sec.is_filtering) + return 0; + if (!(f=dvbdmxfeed->filter)) + return 0; + do + if (DvbDmxSWFilterSectionFilter(dvbdmxfeed, f)<0) + return -1; + while ((f=f->next) && dvbdmxfeed->feed.sec.is_filtering); + + dvbdmxfeed->secbufp=dvbdmxfeed->seclen=0; + memset(buf, 0, DVB_DEMUX_MASK_MAX); + return 0; +} + +static inline int +DvbDmxSWFilterSectionPacket(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + int p, count; + int ccok, rest; + u8 cc; + + if (!(count=payload(buf))) + return -1; + p=188-count; + + cc=buf[3]&0x0f; + ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0; + dvbdmxfeed->cc=cc; + + if (buf[1]&0x40) { // PUSI set + // offset to start of first section is in buf[p] + if (p+buf[p]>187) // trash if it points beyond packet + return -1; + if (buf[p] && ccok) { // rest of previous section? + // did we have enough data in last packet to calc length? + if (dvbdmxfeed->secbufp && dvbdmxfeed->secbufp<3) { + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, + buf+p+1, + 3-dvbdmxfeed->secbufp); + dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf); + if (dvbdmxfeed->seclen>4096) + return -1; + } + rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp; + if (rest==buf[p] && dvbdmxfeed->seclen) { + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, + buf+p+1, buf[p]); + dvbdmxfeed->secbufp+=buf[p]; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + } + } + p+=buf[p]+1; // skip rest of last section + count=188-p; + while (count>0) { + if ((count>2) && // enough data to determine sec length? + ((dvbdmxfeed->seclen=section_length(buf+p))<=count)) { + if (dvbdmxfeed->seclen>4096) + return -1; + memcpy(dvbdmxfeed->secbuf, buf+p, + dvbdmxfeed->seclen); + dvbdmxfeed->secbufp=dvbdmxfeed->seclen; + p+=dvbdmxfeed->seclen; + count=188-p; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + + // filling bytes until packet end? + if (count && buf[p]==0xff) + count=0; + } else { // section continues to following TS packet + memcpy(dvbdmxfeed->secbuf, buf+p, count); + dvbdmxfeed->secbufp+=count; + count=0; + } + } + } else { // section continued below + if (!ccok) + return -1; + if (!dvbdmxfeed->secbufp) // any data in last ts packet? + return -1; + // did we have enough data in last packet to calc section length? + if (dvbdmxfeed->secbufp<3) { + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, + 3-dvbdmxfeed->secbufp); + dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf); + if (dvbdmxfeed->seclen>4096) + return -1; + } + rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp; + if (rest<0) + return -1; + if (rest<=count) { // section completed in this TS packet + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, rest); + dvbdmxfeed->secbufp+=rest; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + } else { // section continues in following ts block + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, count); + dvbdmxfeed->secbufp+=count; + } + + } + return 0; +} + +static inline void +DvbDmxSWFilterPacketType(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + switch(dvbdmxfeed->type) { + case DMX_TYPE_TS: + if (!dvbdmxfeed->feed.ts.is_filtering) + break; + if (dvbdmxfeed->ts_type & TS_PACKET) { + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + DvbDmxSWFilterPayload(dvbdmxfeed, buf); + else + dvbdmxfeed->cb.ts((u8 *)buf, 188, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + } + if (dvbdmxfeed->ts_type & TS_DECODER) + if (dvbdmxfeed->demux->write_to_decoder) + dvbdmxfeed->demux-> + write_to_decoder(dvbdmxfeed, (u8 *)buf, 188); + break; + + case DMX_TYPE_SEC: + if (!dvbdmxfeed->feed.sec.is_filtering) + break; + if (DvbDmxSWFilterSectionPacket(dvbdmxfeed, buf)<0) + dvbdmxfeed->seclen=dvbdmxfeed->secbufp=0; + break; + + default: + break; + } +} + +void inline +DvbDmxSWFilterPacket(dvb_demux_t *dvbdmx, const u8 *buf) +{ + dvb_demux_feed_t *dvbdmxfeed; + + if (!(dvbdmxfeed=dvbdmx->pid2feed[ts_pid(buf)])) + return; + DvbDmxSWFilterPacketType(dvbdmxfeed, buf); +} + +void +DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count) +{ + dvb_demux_feed_t *dvbdmxfeed; + + if ((dvbdmxfeed=dvbdmx->pid2feed[0x2000])) + dvbdmxfeed->cb.ts((u8 *)buf, count*188, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + while (count) { + DvbDmxSWFilterPacket(dvbdmx, buf); + count--; + buf+=188; + } +} + +static inline void +DvbDmxSWFilter(dvb_demux_t *dvbdmx, const u8 *buf, size_t count) +{ + int p=0,i, j; + + if ((i=dvbdmx->tsbufp)) { + if (count<(j=188-i)) { + memcpy(&dvbdmx->tsbuf[i], buf, count); + dvbdmx->tsbufp+=count; + return; + } + memcpy(&dvbdmx->tsbuf[i], buf, j); + DvbDmxSWFilterPacket(dvbdmx, dvbdmx->tsbuf); + dvbdmx->tsbufp=0; + p+=j; + } + + while (p=188) { + DvbDmxSWFilterPacket(dvbdmx, buf+p); + p+=188; + } else { + i=count-p; + memcpy(dvbdmx->tsbuf, buf+p, i); + dvbdmx->tsbufp=i; + return; + } + } else + p++; + } +} + + +/****************************************************************************** + ****************************************************************************** + * DVB DEMUX API LEVEL FUNCTIONS + ****************************************************************************** + ******************************************************************************/ + +static dvb_demux_filter_t * +DvbDmxFilterAlloc(dvb_demux_t *dvbdmx) +{ + int i; + + for (i=0; ifilternum; i++) + if (dvbdmx->filter[i].state==DMX_STATE_FREE) + break; + if (i==dvbdmx->filternum) + return 0; + dvbdmx->filter[i].state=DMX_STATE_ALLOCATED; + return &dvbdmx->filter[i]; +} + +static dvb_demux_feed_t * +DvbDmxFeedAlloc(dvb_demux_t *dvbdmx) +{ + int i; + + for (i=0; ifeednum; i++) + if (dvbdmx->feed[i].state==DMX_STATE_FREE) + break; + if (i==dvbdmx->feednum) + return 0; + dvbdmx->feed[i].state=DMX_STATE_ALLOCATED; + return &dvbdmx->feed[i]; +} + + +/****************************************************************************** + * dmx_ts_feed API calls + ******************************************************************************/ + +static int +dmx_ts_feed_set_type(dmx_ts_feed_t *feed, int type, dmx_ts_pes_t pes_type) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + + down(&dvbdmx->mutex); + dvbdmxfeed->ts_type=type; + dvbdmxfeed->pes_type=pes_type; + + if (dvbdmxfeed->ts_type & TS_DECODER) { + if (pes_type >= DMX_TS_PES_OTHER) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (dvbdmx->pesfilter[pes_type] && + (dvbdmx->pesfilter[pes_type]!=dvbdmxfeed)) { + up(&dvbdmx->mutex); + return -EINVAL; + } + dvbdmx->pesfilter[pes_type]=dvbdmxfeed; + dvbdmx->pids[pes_type]=dvbdmxfeed->pid; + } + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_pid_set(u16 pid, dvb_demux_feed_t *dvbdmxfeed) +{ + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + dvb_demux_feed_t **pid2feed=dvbdmx->pid2feed; + + if (pid>DMX_MAX_PID) + return -EINVAL; + if (dvbdmxfeed->pid!=0xffff) { + if (dvbdmxfeed->pid<=DMX_MAX_PID) + pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + if (pid2feed[pid]) { + return -EBUSY; + } + pid2feed[pid]=dvbdmxfeed; + dvbdmxfeed->pid=pid; + return 0; +} + + +static int +dmx_ts_feed_set(struct dmx_ts_feed_s* feed, + u16 pid, + size_t callback_length, + size_t circular_buffer_size, + int descramble, + struct timespec timeout + ) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + down(&dvbdmx->mutex); + ret=dmx_pid_set(pid, dvbdmxfeed); + if (ret<0) { + up(&dvbdmx->mutex); + return ret; + } + dvbdmxfeed->buffer_size=circular_buffer_size; + dvbdmxfeed->descramble=descramble; + dvbdmxfeed->timeout=timeout; + dvbdmxfeed->cb_length=callback_length; + dvbdmxfeed->ts_type=TS_PACKET; + + if (dvbdmxfeed->descramble) { + up(&dvbdmx->mutex); + return -ENOSYS; + } + + if (dvbdmxfeed->buffer_size) { +#ifdef NOBUFS + dvbdmxfeed->buffer=0; +#else + dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); + if (!dvbdmxfeed->buffer) { + up(&dvbdmx->mutex); + return -ENOMEM; + } +#endif + } + dvbdmxfeed->state=DMX_STATE_READY; + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_ts_feed_start_filtering(struct dmx_ts_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + down(&dvbdmx->mutex); + if (dvbdmxfeed->state!=DMX_STATE_READY || + dvbdmxfeed->type!=DMX_TYPE_TS) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (!dvbdmx->start_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->start_feed(dvbdmxfeed); + if (ret<0) { + up(&dvbdmx->mutex); + return ret; + } + feed->is_filtering=1; + dvbdmxfeed->state=DMX_STATE_GO; + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_ts_feed_stop_filtering(struct dmx_ts_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + down(&dvbdmx->mutex); + if (dvbdmxfeed->statemutex); + return -EINVAL; + } + if (!dvbdmx->stop_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->stop_feed(dvbdmxfeed); + feed->is_filtering=0; + dvbdmxfeed->state=DMX_STATE_ALLOCATED; + + up(&dvbdmx->mutex); + return ret; +} + +static int dvbdmx_allocate_ts_feed(dmx_demux_t *demux, + dmx_ts_feed_t **feed, + dmx_ts_cb callback) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed; + + down(&dvbdmx->mutex); + if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmxfeed->type=DMX_TYPE_TS; + dvbdmxfeed->cb.ts=callback; + dvbdmxfeed->demux=dvbdmx; + dvbdmxfeed->pid=0xffff; + dvbdmxfeed->peslen=0xfffa; + dvbdmxfeed->buffer=0; + + (*feed)=&dvbdmxfeed->feed.ts; + (*feed)->is_filtering=0; + (*feed)->parent=demux; + (*feed)->priv=0; + (*feed)->set=dmx_ts_feed_set; + (*feed)->set_type=dmx_ts_feed_set_type; + (*feed)->start_filtering=dmx_ts_feed_start_filtering; + (*feed)->stop_filtering=dmx_ts_feed_stop_filtering; + + + if (!(dvbdmxfeed->filter=DvbDmxFilterAlloc(dvbdmx))) { + dvbdmxfeed->state=DMX_STATE_FREE; + up(&dvbdmx->mutex); + return -EBUSY; + } + + dvbdmxfeed->filter->type=DMX_TYPE_TS; + dvbdmxfeed->filter->feed=dvbdmxfeed; + dvbdmxfeed->filter->state=DMX_STATE_READY; + + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_release_ts_feed(dmx_demux_t *demux, dmx_ts_feed_t *feed) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + + down(&dvbdmx->mutex); + if (dvbdmxfeed->state==DMX_STATE_FREE) { + up(&dvbdmx->mutex); + return -EINVAL; + } +#ifndef NOBUFS + if (dvbdmxfeed->buffer) { + vfree(dvbdmxfeed->buffer); + dvbdmxfeed->buffer=0; + } +#endif + dvbdmxfeed->state=DMX_STATE_FREE; + dvbdmxfeed->filter->state=DMX_STATE_FREE; + if (dvbdmxfeed->pid!=0xffff) { + if (dvbdmxfeed->pid<=DMX_MAX_PID) + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + + up(&dvbdmx->mutex); + return 0; +} + + +/****************************************************************************** + * dmx_pes_feed API calls + ******************************************************************************/ +/* +static int +dmx_pes_feed_set(struct dmx_pes_feed_s* feed, + u16 pid, + size_t circular_buffer_size, + int descramble, + struct timespec timeout) +{ + return 0; +} + +static int +dmx_pes_feed_start_filtering(struct dmx_pes_feed_s* feed) +{ + return 0; +} + +static int +dmx_pes_feed_stop_filtering(struct dmx_pes_feed_s* feed) +{ + return 0; +} +*/ + +static int dvbdmx_allocate_pes_feed(dmx_demux_t *demux, + dmx_pes_feed_t **feed, + dmx_pes_cb callback) +{ + return 0; +} + +static int dvbdmx_release_pes_feed(dmx_demux_t *demux, + dmx_pes_feed_t *feed) +{ + return 0; +} + + +/****************************************************************************** + * dmx_section_feed API calls + ******************************************************************************/ + +static int +dmx_section_feed_allocate_filter(struct dmx_section_feed_s* feed, + dmx_section_filter_t** filter) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdemux=dvbdmxfeed->demux; + dvb_demux_filter_t *dvbdmxfilter; + + down(&dvbdemux->mutex); + dvbdmxfilter=DvbDmxFilterAlloc(dvbdemux); + if (!dvbdmxfilter) { + up(&dvbdemux->mutex); + return -ENOSPC; + } + *filter=&dvbdmxfilter->filter; + (*filter)->parent=feed; + (*filter)->priv=0; + dvbdmxfilter->feed=dvbdmxfeed; + dvbdmxfilter->pid=dvbdmxfeed->pid; + dvbdmxfilter->type=DMX_TYPE_SEC; + dvbdmxfilter->state=DMX_STATE_READY; + + dvbdmxfilter->next=dvbdmxfeed->filter; + dvbdmxfeed->filter=dvbdmxfilter; + up(&dvbdemux->mutex); + return 0; +} + +static int +dmx_section_feed_set(struct dmx_section_feed_s* feed, + u16 pid, size_t circular_buffer_size, + int descramble, int check_crc) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + + if (pid>0x1fff) + return -EINVAL; + down(&dvbdmx->mutex); + if (dvbdmxfeed->pid!=0xffff) { + dvbdmx->pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + if (dvbdmx->pid2feed[pid]) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmx->pid2feed[pid]=dvbdmxfeed; + dvbdmxfeed->pid=pid; + + dvbdmxfeed->buffer_size=circular_buffer_size; + dvbdmxfeed->descramble=descramble; + if (dvbdmxfeed->descramble) { + up(&dvbdmx->mutex); + return -ENOSYS; + } + + dvbdmxfeed->check_crc=check_crc; +#ifdef NOBUFS + dvbdmxfeed->buffer=0; +#else + dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); + if (!dvbdmxfeed->buffer) { + up(&dvbdmx->mutex); + return -ENOMEM; + } +#endif + dvbdmxfeed->state=DMX_STATE_READY; + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_section_feed_start_filtering(dmx_section_feed_t *feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + down(&dvbdmx->mutex); + if (feed->is_filtering) { + up(&dvbdmx->mutex); + return -EBUSY; + } + if (!dvbdmxfeed->filter) { + up(&dvbdmx->mutex); + return -EINVAL; + } + dvbdmxfeed->secbufp=0; + dvbdmxfeed->seclen=0; + + if (!dvbdmx->start_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->start_feed(dvbdmxfeed); + if (ret<0) { + up(&dvbdmx->mutex); + return ret; + } + feed->is_filtering=1; + dvbdmxfeed->state=DMX_STATE_GO; + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_section_feed_stop_filtering(struct dmx_section_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + down(&dvbdmx->mutex); + if (!dvbdmx->stop_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->stop_feed(dvbdmxfeed); + + dvbdmxfeed->state=DMX_STATE_READY; + feed->is_filtering=0; + up(&dvbdmx->mutex); + return ret; +} + +static int +dmx_section_feed_release_filter(dmx_section_feed_t *feed, + dmx_section_filter_t* filter) +{ + dvb_demux_filter_t *dvbdmxfilter=(dvb_demux_filter_t *) filter, *f; + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + + down(&dvbdmx->mutex); + if (dvbdmxfilter->feed!=dvbdmxfeed) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (feed->is_filtering) + feed->stop_filtering(feed); + + f=dvbdmxfeed->filter; + if (f==dvbdmxfilter) + dvbdmxfeed->filter=dvbdmxfilter->next; + else { + while(f->next!=dvbdmxfilter) + f=f->next; + f->next=f->next->next; + } + dvbdmxfilter->state=DMX_STATE_FREE; + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_allocate_section_feed(dmx_demux_t *demux, + dmx_section_feed_t **feed, + dmx_section_cb callback) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed; + + down(&dvbdmx->mutex); + if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmxfeed->type=DMX_TYPE_SEC; + dvbdmxfeed->cb.sec=callback; + dvbdmxfeed->demux=dvbdmx; + dvbdmxfeed->pid=0xffff; + dvbdmxfeed->secbufp=0; + dvbdmxfeed->filter=0; + dvbdmxfeed->buffer=0; + + (*feed)=&dvbdmxfeed->feed.sec; + (*feed)->is_filtering=0; + (*feed)->parent=demux; + (*feed)->priv=0; + (*feed)->set=dmx_section_feed_set; + (*feed)->allocate_filter=dmx_section_feed_allocate_filter; + (*feed)->release_filter=dmx_section_feed_release_filter; + (*feed)->start_filtering=dmx_section_feed_start_filtering; + (*feed)->stop_filtering=dmx_section_feed_stop_filtering; + + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_release_section_feed(dmx_demux_t *demux, + dmx_section_feed_t *feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + + down(&dvbdmx->mutex); + if (dvbdmxfeed->state==DMX_STATE_FREE) { + up(&dvbdmx->mutex); + return -EINVAL; + } +#ifndef NOBUFS + if (dvbdmxfeed->buffer) { + vfree(dvbdmxfeed->buffer); + dvbdmxfeed->buffer=0; + } +#endif + dvbdmxfeed->state=DMX_STATE_FREE; + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + if (dvbdmxfeed->pid!=0xffff) + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + up(&dvbdmx->mutex); + return 0; +} + + +/****************************************************************************** + * dvb_demux kernel data API calls + ******************************************************************************/ + +static int dvbdmx_open(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (dvbdemux->users>=MAX_DVB_DEMUX_USERS) + return -EUSERS; + dvbdemux->users++; + return 0; +} + +static int dvbdmx_close(struct dmx_demux_s *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (dvbdemux->users==0) + return -ENODEV; + dvbdemux->users--; + //FIXME: release any unneeded resources if users==0 + return 0; +} + +static int dvbdmx_write(dmx_demux_t *demux, const char *buf, size_t count) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if ((!demux->frontend) || + (demux->frontend->source!=DMX_MEMORY_FE)) + return -EINVAL; + + down(&dvbdemux->mutex); + DvbDmxSWFilter(dvbdemux, buf, count); + up(&dvbdemux->mutex); + return count; +} + + +static int dvbdmx_add_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + struct list_head *pos, *head=&dvbdemux->frontend_list; + + //printk ("function : %s\n", __FUNCTION__); + + if (!(frontend->id && frontend->vendor && frontend->model)) + return -EINVAL; + list_for_each(pos, head) + { + if (!strcmp(DMX_FE_ENTRY(pos)->id, frontend->id)) + return -EEXIST; + } + + list_add(&(frontend->connectivity_list), head); + return 0; +} + +static int +dvbdmx_remove_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + struct list_head *pos, *head=&dvbdemux->frontend_list; + + list_for_each(pos, head) + { + if (DMX_FE_ENTRY(pos)==frontend) + { + list_del(pos); + return 0; + } + } + return -ENODEV; +} + +static struct list_head * +dvbdmx_get_frontends(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (list_empty(&dvbdemux->frontend_list)) + return NULL; + return &dvbdemux->frontend_list; +} + +static int dvbdmx_connect_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (demux->frontend) + return -EINVAL; + + down(&dvbdemux->mutex); + demux->frontend=frontend; + up(&dvbdemux->mutex); + return 0; +} + +static int dvbdmx_disconnect_frontend(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + down(&dvbdemux->mutex); + demux->frontend=NULL; + up(&dvbdemux->mutex); + return 0; +} + +static int dvbdmx_get_pes_pids(dmx_demux_t *demux, u16 *pids) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + memcpy(pids, dvbdemux->pids, 5*sizeof(u16)); + return 0; +} + +int +DvbDmxInit(dvb_demux_t *dvbdemux) +{ + int i; + dmx_demux_t *dmx=&dvbdemux->dmx; + + dvbdemux->users=0; + dvbdemux->filter=vmalloc(dvbdemux->filternum*sizeof(dvb_demux_filter_t)); + if (!dvbdemux->filter) + return -ENOMEM; + + dvbdemux->feed=vmalloc(dvbdemux->feednum*sizeof(dvb_demux_feed_t)); + if (!dvbdemux->feed) { + vfree(dvbdemux->filter); + return -ENOMEM; + } + for (i=0; ifilternum; i++) { + dvbdemux->filter[i].state=DMX_STATE_FREE; + dvbdemux->filter[i].index=i; + } + for (i=0; ifeednum; i++) + dvbdemux->feed[i].state=DMX_STATE_FREE; + dvbdemux->frontend_list.next= + dvbdemux->frontend_list.prev= + &dvbdemux->frontend_list; + for (i=0; ipesfilter[i]=NULL; + dvbdemux->pids[i]=0xffff; + } + dvbdemux->playing=dvbdemux->recording=0; + memset(dvbdemux->pid2feed, 0, (DMX_MAX_PID+1)*sizeof(dvb_demux_feed_t *)); + dvbdemux->tsbufp=0; + + dmx->frontend=0; + dmx->reg_list.next=dmx->reg_list.prev=&dmx->reg_list; + dmx->priv=(void *) dvbdemux; + //dmx->users=0; // reset in dmx_register_demux() + dmx->open=dvbdmx_open; + dmx->close=dvbdmx_close; + dmx->write=dvbdmx_write; + dmx->allocate_ts_feed=dvbdmx_allocate_ts_feed; + dmx->release_ts_feed=dvbdmx_release_ts_feed; + dmx->allocate_pes_feed=dvbdmx_allocate_pes_feed; + dmx->release_pes_feed=dvbdmx_release_pes_feed; + dmx->allocate_section_feed=dvbdmx_allocate_section_feed; + dmx->release_section_feed=dvbdmx_release_section_feed; + + dmx->descramble_mac_address=NULL; + dmx->descramble_section_payload=NULL; + + dmx->add_frontend=dvbdmx_add_frontend; + dmx->remove_frontend=dvbdmx_remove_frontend; + dmx->get_frontends=dvbdmx_get_frontends; + dmx->connect_frontend=dvbdmx_connect_frontend; + dmx->disconnect_frontend=dvbdmx_disconnect_frontend; + dmx->get_pes_pids=dvbdmx_get_pes_pids; + sema_init(&dvbdemux->mutex, 1); + + if (dmx_register_demux(dmx)<0) + return -1; + + return 0; +} + +int +DvbDmxRelease(dvb_demux_t *dvbdemux) +{ + dmx_demux_t *dmx=&dvbdemux->dmx; + + dmx_unregister_demux(dmx); + if (dvbdemux->filter) + vfree(dvbdemux->filter); + if (dvbdemux->feed) + vfree(dvbdemux->feed); + return 0; +} diff -Nru a/drivers/media/video/margi/dvb_demux.h b/drivers/media/video/margi/dvb_demux.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvb_demux.h Wed Feb 13 20:03:58 2002 @@ -0,0 +1,141 @@ +/* + * dvb_demux.h - DVB kernel demux API + * + * Copyright (C) 2000-2001 Marcus Metzler + * & Ralph Metzler + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVB_DEMUX_H_ +#define _DVB_DEMUX_H_ + +#if LINUX_VERSION_CODE < 0x020300 +#define WAIT_QUEUE struct wait_queue* +#define init_waitqueue_head(wq) *(wq) = NULL; +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) +#else +#define WAIT_QUEUE wait_queue_head_t +#endif + +#include "ost/demux.h" + +#define DMX_TYPE_TS 0 +#define DMX_TYPE_SEC 1 +#define DMX_TYPE_PES 2 + +#define DMX_STATE_FREE 0 +#define DMX_STATE_ALLOCATED 1 +#define DMX_STATE_SET 2 +#define DMX_STATE_READY 3 +#define DMX_STATE_GO 4 + +#define DVB_DEMUX_MASK_MAX 18 + +typedef struct dvb_demux_filter_s { + dmx_section_filter_t filter; + struct dvb_demux_filter_s *next; + struct dvb_demux_feed_s *feed; + int index; + int state; + int type; + int pesto; + + u32 flags; + u16 handle; + u16 hw_handle; + struct timer_list timer; + int ts_state; + + u16 pid; //to be removed +} dvb_demux_filter_t; + +typedef struct dvb_demux_feed_s { + union { + dmx_ts_feed_t ts; + dmx_section_feed_t sec; + dmx_pes_feed_t pes; + } feed; + + union { + dmx_ts_cb ts; + dmx_section_cb sec; + dmx_pes_cb pes; + } cb; + + struct dvb_demux_s *demux; + int type; + int state; + u16 pid; + u8 *buffer; + int buffer_size; + int descramble; + int check_crc; + + struct timespec timeout; + dvb_demux_filter_t *filter; + int cb_length; + + int ts_type; + dmx_ts_pes_t pes_type; + + u8 secbuf[4096]; + int secbufp; + int seclen; + int cc; + + u16 peslen; +} dvb_demux_feed_t; + +typedef struct dvb_demux_s { + dmx_demux_t dmx; + void *priv; + int filternum; + int feednum; + int (*start_feed)(dvb_demux_feed_t *); + int (*stop_feed)(dvb_demux_feed_t *); + int (*write_to_decoder)(dvb_demux_feed_t *, u8 *, size_t); + + + int users; +#define MAX_DVB_DEMUX_USERS 10 + dvb_demux_filter_t *filter; + dvb_demux_feed_t *feed; + + struct list_head frontend_list; + + dvb_demux_feed_t *pesfilter[DMX_TS_PES_OTHER]; + u16 pids[DMX_TS_PES_OTHER]; + int playing; + int recording; + +#define DMX_MAX_PID 0x2000 + dvb_demux_feed_t *pid2feed[DMX_MAX_PID+1]; + u8 tsbuf[188]; + int tsbufp; + + struct semaphore mutex; +} dvb_demux_t; + + +int DvbDmxInit(dvb_demux_t *dvbdemux); +int DvbDmxRelease(dvb_demux_t *dvbdemux); +void DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count); + +#endif /* _DVB_DEMUX_H_ */ diff -Nru a/drivers/media/video/margi/dvb_filter.c b/drivers/media/video/margi/dvb_filter.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvb_filter.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,769 @@ +#include +#include +#include "dvb_filter.h" +#if 0 +#ifdef MODULE +MODULE_DESCRIPTION(""); +MODULE_AUTHOR("Marcus Metzler, Ralph Metzler"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#endif +#endif + +unsigned int bitrates[3][16] = +{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, + {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, + {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}}; + +uint32_t freq[4] = {441, 480, 320, 0}; + +unsigned int ac3_bitrates[32] = + {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, + 0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint32_t ac3_freq[4] = {480, 441, 320, 0}; +uint32_t ac3_frames[3][32] = + {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, + 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, + 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, + 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; + + + +void pes2ts_init(pes2ts_t *p2ts, unsigned short pid, + pes2ts_cb_t *cb, void *priv) +{ + unsigned char *buf=p2ts->buf; + + buf[0]=0x47; + buf[1]=(pid>>8); + buf[2]=pid&0xff; + p2ts->cc=0; + p2ts->cb=cb; + p2ts->priv=priv; +} + +int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len) +{ + unsigned char *buf=p2ts->buf; + int ret=0, rest; + + //len=6+((pes[4]<<8)|pes[5]); + + buf[1]|=0x40; + while (len>=184) { + buf[3]=0x10|((p2ts->cc++)&0x0f); + memcpy(buf+4, pes, 184); + if ((ret=p2ts->cb(p2ts->priv, buf))) + return ret; + len-=184; pes+=184; + buf[1]&=~0x40; + } + if (!len) + return 0; + buf[3]=0x30|((p2ts->cc++)&0x0f); + rest=183-len; + if (rest) { + buf[5]=0x00; + if (rest-1) + memset(buf+6, 0xff, rest-1); + } + buf[4]=rest; + memcpy(buf+5+rest, pes, len); + return p2ts->cb(p2ts->priv, buf); +} + +void reset_ipack(ipack *p) +{ + p->found = 0; + p->cid = 0; + p->plength = 0; + p->flag1 = 0; + p->flag2 = 0; + p->hlength = 0; + p->mpeg = 0; + p->check = 0; + p->which = 0; + p->done = 0; + p->count = 0; +} + +void init_ipack(ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)) +{ + if ( !(p->buf = vmalloc(size*sizeof(u8))) ){ + printk ("Couldn't allocate memory for ipack\n"); + } + p->size = size; + p->func = func; + p->repack_subids = 0; + reset_ipack(p); +} + +void free_ipack(ipack * p) +{ + if (p->buf) vfree(p->buf); +} + +void send_ipack(ipack *p) +{ + int off; + AudioInfo ai; + int ac3_off = 0; + int streamid=0; + int nframes= 0; + int f=0; + + switch ( p->mpeg ){ + case 2: + if (p->count < 10) return; + p->buf[3] = p->cid; + + p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8); + p->buf[5] = (u8)((p->count-6) & 0x00FF); + if (p->repack_subids && p->cid == PRIVATE_STREAM1){ + + off = 9+p->buf[8]; + streamid = p->buf[off]; + if ((streamid & 0xF8) == 0x80){ + ai.off = 0; + ac3_off = ((p->buf[off+2] << 8)| + p->buf[off+3]); + if (ac3_off < p->count) + f=get_ac3info(p->buf+off+3+ac3_off, + p->count-ac3_off, &ai,0); + if ( !f ){ + nframes = (p->count-off-3-ac3_off)/ + ai.framesize + 1; + p->buf[off+2] = (ac3_off >> 8)& 0xFF; + p->buf[off+3] = (ac3_off)& 0xFF; + p->buf[off+1] = nframes; + + ac3_off += nframes * ai.framesize - + p->count; + } + } + } + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x80; + p->buf[7] = 0x00; + p->buf[8] = 0x00; + p->count = 9; + if (p->repack_subids && p->cid == PRIVATE_STREAM1 + && (streamid & 0xF8)==0x80 ){ + p->count += 4; + p->buf[9] = streamid; + p->buf[10] = (ac3_off >> 8)& 0xFF; + p->buf[11] = (ac3_off)& 0xFF; + p->buf[12] = 0; + } + + break; + case 1: + if (p->count < 8) return; + p->buf[3] = p->cid; + + p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8); + p->buf[5] = (u8)((p->count-6) & 0x00FF); + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x0F; + p->count = 7; + break; + } +} + +void send_ipack_rest(ipack *p) +{ + if (p->plength != MMAX_PLENGTH-6 || p->found<=6) + return; + p->plength = p->found-6; + p->found = 0; + send_ipack(p); + reset_ipack(p); +} + +static void write_ipack(ipack *p, u8 *data, int count) +{ + u8 headr[3] = { 0x00, 0x00, 0x01} ; + + if (p->count < 6){ + memcpy(p->buf, headr, 3); + p->count = 6; + } + + if (p->count + count < p->size){ + memcpy(p->buf+p->count, data, count); + p->count += count; + } else { + int rest = p->size - p->count; + memcpy(p->buf+p->count, data, rest); + p->count += rest; + send_ipack(p); + if (count - rest > 0) + write_ipack(p, data+rest, count-rest); + } +} + +int instant_repack(u8 *buf, int count, ipack *p) +{ + int l; + int c=0; + + while (c < count && (p->mpeg == 0 || + (p->mpeg == 1 && p->found < 7) || + (p->mpeg == 2 && p->found < 9)) + && (p->found < 5 || !p->done)){ + switch ( p->found ){ + case 0: + case 1: + if (buf[c] == 0x00) p->found++; + else p->found = 0; + c++; + break; + case 2: + if (buf[c] == 0x01) p->found++; + else if (buf[c] == 0) { + p->found = 2; + } else p->found = 0; + c++; + break; + case 3: + p->cid = 0; + switch (buf[c]){ + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + p->done = 1; + case PRIVATE_STREAM1: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + p->found++; + p->cid = buf[c]; + c++; + break; + default: + p->found = 0; + break; + } + break; + + case 4: + if (count-c > 1){ + p->plen[0] = buf[c]; + c++; + p->plen[1] = buf[c]; + c++; + p->found+=2; + p->plength=(p->plen[0]<<8)|p->plen[1]; + } else { + p->plen[0] = buf[c]; + p->found++; + return count; + } + break; + case 5: + p->plen[1] = buf[c]; + c++; + p->found++; + p->plength=(p->plen[0]<<8)|p->plen[1]; + break; + case 6: + if (!p->done){ + p->flag1 = buf[c]; + c++; + p->found++; + if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2; + else { + p->hlength = 0; + p->which = 0; + p->mpeg = 1; + p->flag2 = 0; + } + } + break; + + case 7: + if ( !p->done && p->mpeg == 2) { + p->flag2 = buf[c]; + c++; + p->found++; + } + break; + + case 8: + if ( !p->done && p->mpeg == 2) { + p->hlength = buf[c]; + c++; + p->found++; + } + break; + + default: + + break; + } + } + + if (c == count) return count; + + if (!p->plength) p->plength = MMAX_PLENGTH-6; + + if ( p->done || ((p->mpeg == 2 && p->found >= 9) || + (p->mpeg == 1 && p->found >= 7)) ){ + switch (p->cid){ + + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case PRIVATE_STREAM1: + + if (p->mpeg == 2 && p->found == 9) { + write_ipack(p, &p->flag1, 1); + write_ipack(p, &p->flag2, 1); + write_ipack(p, &p->hlength, 1); + } + + if (p->mpeg == 1 && p->found == 7) + write_ipack(p, &p->flag1, 1); + + if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && + p->found < 14) { + while (c < count && p->found < 14) { + p->pts[p->found-9] = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + } + if (c == count) return count; + } + + if (p->mpeg == 1 && p->which < 2000) { + + if (p->found == 7) { + p->check = p->flag1; + p->hlength = 1; + } + + while (!p->which && c < count && + p->check == 0xFF){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + } + + if ( c == count) return count; + + if ( (p->check & 0xC0) == 0x40 && !p->which){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + + p->which = 1; + if ( c == count) return count; + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if ( c == count) return count; + } + + if (p->which == 1){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if ( c == count) return count; + } + + if ( (p->check & 0x30) && p->check != 0xFF){ + p->flag2 = (p->check & 0xF0) << 2; + p->pts[0] = p->check; + p->which = 3; + } + + if ( c == count) return count; + if (p->which > 2){ + if ((p->flag2 & PTS_DTS_FLAGS) + == PTS_ONLY){ + while (c < count && + p->which < 7){ + p->pts[p->which-2] = + buf[c]; + write_ipack(p,buf+c,1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if ( c == count) return count; + } else if ((p->flag2 & PTS_DTS_FLAGS) + == PTS_DTS){ + while (c < count && + p->which< 12){ + if (p->which< 7) + p->pts[p->which + -2] = + buf[c]; + write_ipack(p,buf+c,1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if ( c == count) return count; + } + p->which = 2000; + } + + } + + while (c < count && p->found < p->plength+6){ + l = count -c; + if (l+p->found > p->plength+6) + l = p->plength+6-p->found; + write_ipack(p, buf+c, l); + p->found += l; + c += l; + } + + break; + } + + + if ( p->done ){ + if( p->found + count - c < p->plength+6){ + p->found += count-c; + c = count; + } else { + c += p->plength+6 - p->found; + p->found = p->plength+6; + } + } + + if (p->plength && p->found == p->plength+6) { + send_ipack(p); + reset_ipack(p); + if (c < count) + instant_repack(buf+c, count-c, p); + } + } + return count; +} + + + +void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, + void (*pes_write)(u8 *buf, int count, void *data), + void *priv) +{ + init_ipack(pa, IPACKS, pes_write); + init_ipack(pv, IPACKS, pes_write); + pa->pid = pida; + pv->pid = pidv; + pa->data = priv; + pv->data = priv; +} + +void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188) +{ + u8 off = 0; + + if (!buf || !p ){ + printk("NULL POINTER IDIOT\n"); + return; + } + if (buf[1]&PAY_START) { + if (p->plength == MMAX_PLENGTH-6 && p->found>6){ + p->plength = p->found-6; + p->found = 0; + send_ipack(p); + reset_ipack(p); + } + } + if (buf[3] & ADAPT_FIELD) { // adaptation field? + off = buf[4] + 1; + if (off+4 > 187) return; + } + instant_repack(buf+4+off, TS_SIZE-4-off, p); +} + +int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr) +{ + uint8_t *headr; + int found = 0; + int sw; + int form = -1; + int c = 0; + + while (found < 4 && c+4 < count){ + uint8_t *b; + + b = mbuf+c; + if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01 + && b[3] == 0xb3) found = 4; + else { + c++; + } + } + + if (! found) return -1; + c += 4; + if (c+12 >= count) return -1; + headr = mbuf+c; + + vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4); + vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]); + + sw = (int)((headr[3]&0xF0) >> 4) ; + + switch( sw ){ + case 1: + if (pr) + printk("Videostream: ASPECT: 1:1"); + vi->aspect_ratio = 100; + break; + case 2: + if (pr) + printk("Videostream: ASPECT: 4:3"); + vi->aspect_ratio = 133; + break; + case 3: + if (pr) + printk("Videostream: ASPECT: 16:9"); + vi->aspect_ratio = 177; + break; + case 4: + if (pr) + printk("Videostream: ASPECT: 2.21:1"); + vi->aspect_ratio = 221; + break; + + case 5 ... 15: + if (pr) + printk("Videostream: ASPECT: reserved"); + vi->aspect_ratio = 0; + break; + + default: + vi->aspect_ratio = 0; + return -1; + } + + if (pr) + printk(" Size = %dx%d",vi->horizontal_size,vi->vertical_size); + + sw = (int)(headr[3]&0x0F); + + switch ( sw ) { + case 1: + if (pr) + printk(" FRate: 23.976 fps"); + vi->framerate = 24000/1001.; + form = -1; + break; + case 2: + if (pr) + printk(" FRate: 24 fps"); + vi->framerate = 24; + form = -1; + break; + case 3: + if (pr) + printk(" FRate: 25 fps"); + vi->framerate = 25; + form = VIDEO_MODE_PAL; + break; + case 4: + if (pr) + printk(" FRate: 29.97 fps"); + vi->framerate = 30000/1001.; + form = VIDEO_MODE_NTSC; + break; + case 5: + if (pr) + printk(" FRate: 30 fps"); + vi->framerate = 30; + form = VIDEO_MODE_NTSC; + break; + case 6: + if (pr) + printk(" FRate: 50 fps"); + vi->framerate = 50; + form = VIDEO_MODE_PAL; + break; + case 7: + if (pr) + printk(" FRate: 60 fps"); + vi->framerate = 60; + form = VIDEO_MODE_NTSC; + break; + } + + vi->bit_rate = 400*(((headr[4] << 10) & 0x0003FC00UL) + | ((headr[5] << 2) & 0x000003FCUL) | + (((headr[6] & 0xC0) >> 6) & 0x00000003UL)); + + if (pr){ + printk(" BRate: %d Mbit/s",(vi->bit_rate)); + printk("\n"); + } + vi->video_format = form; + + vi->off = c-4; + return 0; +} + +int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr) +{ + uint8_t *headr; + int found = 0; + int c = 0; + int fr = 0; + + while (found < 2 && c < count){ + uint8_t b[2]; + memcpy( b, mbuf+c, 2); + + if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8) + found = 2; + else { + c++; + } + } + + if (!found) return -1; + + if (c+3 >= count) return -1; + headr = mbuf+c; + + ai->layer = (headr[1] & 0x06) >> 1; + + if (pr) + printk("Audiostream: Layer: %d", 4-ai->layer); + + + ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000; + + if (pr){ + if (ai->bit_rate == 0) + printk(" Bit rate: free"); + else if (ai->bit_rate == 0xf) + printk(" BRate: reserved"); + else + printk(" BRate: %d kb/s", ai->bit_rate/1000); + } + + fr = (headr[2] & 0x0c ) >> 2; + ai->frequency = freq[fr]*100; + if (pr){ + if (ai->frequency == 3) + printk(" Freq: reserved\n"); + else + printk(" Freq: %d kHz\n",ai->frequency); + + } + ai->off = c; + return 0; +} + +int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr) +{ + uint8_t *headr; + int found = 0; + int c = 0; + uint8_t frame = 0; + int fr = 0; + + while ( !found && c < count){ + uint8_t *b = mbuf+c; + + if ( b[0] == 0x0b && b[1] == 0x77 ) + found = 1; + else { + c++; + } + } + + if (!found) return -1; + if (pr) + printk("Audiostream: AC3"); + + ai->off = c; + if (c+5 >= count) return -1; + + ai->layer = 0; // 0 for AC3 + headr = mbuf+c+2; + + frame = (headr[2]&0x3f); + ai->bit_rate = ac3_bitrates[frame >> 1]*1000; + + if (pr) + printk(" BRate: %d kb/s", ai->bit_rate/1000); + + ai->frequency = (headr[2] & 0xc0 ) >> 6; + fr = (headr[2] & 0xc0 ) >> 6; + ai->frequency = freq[fr]*100; + if (pr) printk (" Freq: %d Hz\n", ai->frequency); + + + ai->framesize = ac3_frames[fr][frame >> 1]; + if ((frame & 1) && (fr == 1)) ai->framesize++; + ai->framesize = ai->framesize << 1; + if (pr) printk (" Framesize %d\n", ai->framesize); + + + return 0; +} + +uint8_t *skip_pes_header(uint8_t **bufp) +{ + uint8_t *inbuf = *bufp; + uint8_t *buf = inbuf; + uint8_t *pts = NULL; + int skip = 0; + +int mpeg1_skip_table[16] = { + 1, 0xffff, 5, 10, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + + + if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */ + if (buf[7] & PTS_ONLY) + pts = buf+9; + else pts = NULL; + buf = inbuf + 9 + inbuf[8]; + } else { /* mpeg1 */ + for (buf = inbuf + 6; *buf == 0xff; buf++) + if (buf == inbuf + 6 + 16) { + break; + } + if ((*buf & 0xc0) == 0x40) + buf += 2; + skip = mpeg1_skip_table [*buf >> 4]; + if (skip == 5 || skip == 10) pts = buf; + else pts = NULL; + + buf += mpeg1_skip_table [*buf >> 4]; + } + + *bufp = buf; + return pts; +} diff -Nru a/drivers/media/video/margi/dvb_filter.h b/drivers/media/video/margi/dvb_filter.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvb_filter.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,146 @@ +#ifndef _DVB_FILTER_H_ +#define _DVB_FILTER_H_ + +#include +#include + +#include "ost/demux.h" + +typedef int (pes2ts_cb_t) (void *, unsigned char *); + +typedef struct pes2ts_s { + unsigned char buf[188]; + unsigned char cc; + pes2ts_cb_t *cb; + void *priv; +} pes2ts_t; + +void pes2ts_init(pes2ts_t *p2ts, unsigned short pid, + pes2ts_cb_t *cb, void *priv); +int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len); + + +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +//flags2 +#define PTS_DTS_FLAGS 0xC0 +#define ESCR_FLAG 0x20 +#define ES_RATE_FLAG 0x10 +#define DSM_TRICK_FLAG 0x08 +#define ADD_CPY_FLAG 0x04 +#define PES_CRC_FLAG 0x02 +#define PES_EXT_FLAG 0x01 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 + +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +#define MAX_PLENGTH 0xFFFF +#define MMAX_PLENGTH (256*MAX_PLENGTH) + +#ifndef IPACKS +#define IPACKS 2048 +#endif + +typedef struct ipack_s { + int size; + int found; + u8 *buf; + u8 cid; + uint32_t plength; + u8 plen[2]; + u8 flag1; + u8 flag2; + u8 hlength; + u8 pts[5]; + u16 *pid; + int mpeg; + u8 check; + int which; + int done; + void *data; + void (*func)(u8 *buf, int size, void *priv); + int count; + int repack_subids; +} ipack; + +typedef struct video_i{ + u32 horizontal_size; + u32 vertical_size ; + u32 aspect_ratio ; + double framerate ; + u32 video_format; + u32 bit_rate ; + u32 comp_bit_rate ; + u32 vbv_buffer_size; + u32 CSPF ; + u32 off; +} VideoInfo; + +typedef struct audio_i{ + int layer ; + u32 bit_rate ; + u32 frequency ; + u32 mode ; + u32 mode_extension ; + u32 emphasis ; + u32 framesize; + u32 off; +} AudioInfo; + +void reset_ipack(ipack *p); +int instant_repack(u8 *buf, int count, ipack *p); +void init_ipack(ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)); +void free_ipack(ipack * p); +void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, + void (*pes_write)(u8 *buf, int count, void *data), + void *priv); +void ts_to_pes(ipack *p, u8 *buf); +void send_ipack(ipack *p); +void send_ipack_rest(ipack *p); +int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr); +int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr); +int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr); +uint8_t *skip_pes_header(uint8_t **bufp); +#endif diff -Nru a/drivers/media/video/margi/dvb_formats.h b/drivers/media/video/margi/dvb_formats.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvb_formats.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,152 @@ +/* + * + * Copyright (C) 2000, 2001 Marcus Metzler + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + + * The author can be reached at marcus@convergence.de, + + * the project's page is at http://linuxtv.org/dvb/ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _DVB_FORMATS_H_ +#define _DVB_FORMATS_H_ + + +#define PROG_STREAM_MAP 0xBC +#ifndef PRIVATE_STREAM1 +#define PRIVATE_STREAM1 0xBD +#endif +#define PADDING_STREAM 0xBE +#ifndef PRIVATE_STREAM2 +#define PRIVATE_STREAM2 0xBF +#endif +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define BUFFYSIZE 10*MAX_PLENGTH +//#define MAX_PTS 8192 +#define MAX_FRAME 8192 +#define MAX_PACK_L 4096 +#define PS_HEADER_L1 14 +#define PS_HEADER_L2 (PS_HEADER_L1+18) +#define MAX_H_SIZE (PES_H_MIN + PS_HEADER_L1 + 5) +#define PES_MIN 7 +#define PES_H_MIN 9 + +//flags2 +#define PTS_DTS_FLAGS 0xC0 +#define ESCR_FLAG 0x20 +#define ES_RATE_FLAG 0x10 +#define DSM_TRICK_FLAG 0x08 +#define ADD_CPY_FLAG 0x04 +#define PES_CRC_FLAG 0x02 +#define PES_EXT_FLAG 0x01 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 + +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +#define MAX_PLENGTH 0xFFFF +#define MMAX_PLENGTH (4*MAX_PLENGTH) + +#define IPACKS 2048 + +typedef struct ipack_s { + int size; + int found; + uint8_t *buf; + uint8_t cid; + uint32_t plength; + uint8_t plen[2]; + uint8_t flag1; + uint8_t flag2; + uint8_t hlength; + uint8_t pts[5]; + uint16_t *pid; + int mpeg; + uint8_t check; + int which; + int done; + void *data; + void (*func)(uint8_t *buf, int size, void *priv); + int count; +} ipack; + +void instant_repack (uint8_t *buf, int count, ipack *p); +void init_ipack(ipack *p, int size, + void (*func)(uint8_t *buf, int size, void *priv)); +void free_ipack(ipack * p); +void setup_ts2pes( ipack *pa, ipack *pv, uint16_t *pida, uint16_t *pidv, + void (*pes_write)(uint8_t *buf, int count, void *data), + void *priv); +void ts_to_pes( ipack *p, uint8_t *buf); // don't need count (=188) +uint16_t get_pid(uint8_t *pid); + + +#endif /* _DVB_FORMATS_H_*/ diff -Nru a/drivers/media/video/margi/dvbdev.c b/drivers/media/video/margi/dvbdev.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/dvbdev.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,233 @@ +/* + * dvbdev.c + * + * Copyright (C) 2000 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvbdev.h" + +#ifdef MODULE +MODULE_DESCRIPTION("Device registrar for DVB drivers"); +MODULE_AUTHOR("Marcus Metzler, Ralph Metzler"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("LGPL"); +#endif +#endif + +#define DVB_MAJOR 250 + +static struct dvb_device *dvb_device[DVB_NUM_DEVICES]; +static devfs_handle_t dvb_devfs_handle; + +static inline struct dvb_device * +inode2dev (struct inode *inode) +{ + int minor=(MINOR(inode->i_rdev)>>6); + + return dvb_device[minor]; +} + +static inline int +inode2num(struct inode *inode) +{ + return (0x3f&MINOR(inode->i_rdev)); +} + +static ssize_t +dvb_device_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode=file->f_dentry->d_inode; + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->read(dvbdev, inode2num(inode), file, buf, count, ppos); +} + +static ssize_t +dvb_device_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode=file->f_dentry->d_inode; + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->write(dvbdev, inode2num(inode), file, buf, count, ppos); +} + +static int +dvb_device_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->open(dvbdev, inode2num(inode), inode, file); +} + +static int +dvb_device_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->close(dvbdev, inode2num(inode), inode, file); +} + +static int +dvb_device_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->ioctl(dvbdev, inode2num(inode), file, cmd, arg); +} + +static unsigned int +dvb_device_poll(struct file *file, poll_table *wait) +{ + struct inode *inode=file->f_dentry->d_inode; + struct dvb_device *dvbdev=inode2dev(inode); + + if (!dvbdev) + return -ENODEV; + return dvbdev->poll(dvbdev, inode2num(inode), file, wait); +} + + +static struct file_operations dvb_device_fops = +{ + owner: THIS_MODULE, + read: dvb_device_read, + write: dvb_device_write, + ioctl: dvb_device_ioctl, + open: dvb_device_open, + release: dvb_device_release, + poll: dvb_device_poll, +}; + + +static char *dnames[] = { + "video", "audio", "sec", "frontend", "demux", "dvr", "ca", + "net", "osd" +}; + + +static void dvb_init_device(dvb_device_t *dev) +{ + int i, type; + char name[64]; + + sprintf(name, "card%d", dev->minor); + dev->devfsh = devfs_mk_dir (dvb_devfs_handle, name, NULL); + + for (i=0; (type=dev->device_type(dev,i))>-2; i++) { + if (type==-1) + continue; + + sprintf(name, "%s%d", dnames[type>>2], type&3); + devfs_register(dev->devfsh, name, DEVFS_FL_DEFAULT, + DVB_MAJOR, (dev->minor<<6)+i, + S_IFCHR | S_IRUSR | S_IWUSR, + &dvb_device_fops, NULL); + } + +} + +int dvb_register_device(dvb_device_t *dev) +{ + int i=0; + + for (i=0; iminor=i; + dvb_init_device(dev); + MOD_INC_USE_COUNT; + return 0; + } + } + return -ENFILE; +} + +void dvb_unregister_device(dvb_device_t *dev) +{ + if (dvb_device[dev->minor]!=dev) { + printk("dvbdev: bad unregister\n"); + return; + } + devfs_unregister(dev->devfsh); + dvb_device[dev->minor]=NULL; + MOD_DEC_USE_COUNT; +} + +int __init dvbdev_init(void) +{ + int i=0; + + for(i=0; i + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Lesser Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVBDEV_H_ +#define _DVBDEV_H_ + +#include +#include +#include +#include + +#define DVB_NUM_DEVICES 16 + +struct dvb_device +{ + char name[32]; + int type; + int hardware; + + void *priv; + int minor; + devfs_handle_t devfs_handle; + + int (*open)(struct dvb_device *, int, struct inode *, struct file *); + int (*close)(struct dvb_device *, int, struct inode *, struct file *); + ssize_t (*read)(struct dvb_device *, int, struct file *, char *, + size_t, loff_t *); + ssize_t (*write)(struct dvb_device *, int, struct file *, const char *, + size_t, loff_t *); + int (*ioctl)(struct dvb_device *, int, struct file *, + unsigned int , unsigned long); + unsigned int (*poll)(struct dvb_device *, int type, + struct file *file, poll_table * wait); + + int (*device_type)(struct dvb_device *, unsigned int device_num); +#define DVB_DEVICE_VIDEO_0 0 +#define DVB_DEVICE_AUDIO_0 4 +#define DVB_DEVICE_SEC_0 8 +#define DVB_DEVICE_FRONTEND_0 12 +#define DVB_DEVICE_DEMUX_0 16 +#define DVB_DEVICE_DEMUX_1 17 +#define DVB_DEVICE_DEMUX_2 18 +#define DVB_DEVICE_DEMUX_3 19 +#define DVB_DEVICE_DVR_0 20 +#define DVB_DEVICE_CA_0 24 +#define DVB_DEVICE_NET_0 28 +#define DVB_DEVICE_OSD_0 32 + devfs_handle_t devfsh; +}; + +typedef struct dvb_device dvb_device_t; + +int dvb_register_device(struct dvb_device *); +void dvb_unregister_device(struct dvb_device *); + +#endif /* #ifndef __DVBDEV_H */ diff -Nru a/drivers/media/video/margi/i2c.c b/drivers/media/video/margi/i2c.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/i2c.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,186 @@ +/* + i2c.h + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "i2c.h" + +void out(struct cvdv_cards *card) +{ + write_indexed_register(card, IIO_GPIO_PINS, + (card->scl ? SCL : 0) | + (card->sda ? SDA : 0) | 1); + udelay(10); +} + +void clkon(struct cvdv_cards *card) +{ + card->scl = 1; +} + +void clkoff(struct cvdv_cards *card) +{ + card->scl = 0; +} + +void dat(struct cvdv_cards *card, u_char data) +{ + card->sda = data; +} + +int rdat(struct cvdv_cards *card) +{ + return ((read_indexed_register(card, IIO_GPIO_PINS) & SDA) ? 1 : + 0); +} + + +void I2CStart(struct cvdv_cards *card) +{ + dat(card, 1); + out(card); + clkon(card); + out(card); + dat(card, 0); + out(card); + clkoff(card); + out(card); +} + +void I2CStop(struct cvdv_cards *card) +{ + dat(card, 0); + out(card); + clkon(card); + out(card); + dat(card, 1); + out(card); + clkoff(card); + out(card); +} + +int I2CAck(struct cvdv_cards *card, int ack) +{ + dat(card, ack); + out(card); + write_indexed_register(card, IIO_GPIO_CONTROL, (~SDA) & 0x07); + clkon(card); + out(card); + ack = rdat(card); + clkoff(card); + out(card); + write_indexed_register(card, IIO_GPIO_CONTROL, 0x07); + out(card); + return ack; +} + +u_char I2CReadByte(struct cvdv_cards * card, int ack) +{ + int i; + u_char data = 0; + + clkoff(card); + dat(card, 1); + out(card); + write_indexed_register(card, IIO_GPIO_CONTROL, (~SDA) & 0x07); + for (i = 7; i >= 0; i--) { + clkon(card); + out(card); + data |= (rdat(card) << i); + clkoff(card); + out(card); + } + write_indexed_register(card, IIO_GPIO_CONTROL, 0x07); + I2CAck(card, ack); + return data; +} + + +int I2CSendByte(struct cvdv_cards *card, u_char data) +{ + int i; + + for (i = 7; i >= 0; i--) { + dat(card, data & (1 << i)); + out(card); + clkon(card); + out(card); + clkoff(card); + out(card); + } + i = I2CAck(card, 1); + return i; +} + +void I2CWrite(struct cvdv_cards *card, int adr, int reg, int val) +{ + I2CStart(card); + I2CSendByte(card, adr); + I2CSendByte(card, reg); + I2CSendByte(card, val); + I2CStop(card); +} + + +u_char I2CRead(struct cvdv_cards *card, int adr, int reg) +{ + u_char c; + + I2CStart(card); + I2CSendByte(card, adr); + I2CSendByte(card, reg); + I2CStart(card); + I2CSendByte(card, adr | 1); + c = I2CReadByte(card, 1); + I2CStop(card); + return c; +} + + +int I2CScan(struct cvdv_cards *card, int adr) +{ + int result; + I2CStart(card); + result = I2CSendByte(card, adr); + I2CStop(card); + return result; +} + +void I2CScanBus(struct cvdv_cards *card) +{ + int i; + + for (i = 0; i < 0xff; i += 2) { + if (!I2CScan(card, i)) + MDEBUG(0,"Found i2c device at %d\n", i); + } +} + +void I2CSend(struct cvdv_cards *card, int adr, u_char * vals) +{ + int reg, val; + while (*vals != 0xff) { + reg = *vals; + vals++; + val = *vals; + vals++; + I2CWrite(card, adr, reg, val); + } +} diff -Nru a/drivers/media/video/margi/i2c.h b/drivers/media/video/margi/i2c.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/i2c.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,43 @@ +/* + i2c.h + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef I2C_H +#define I2C_H +#include "cardbase.h" +#include "l64014.h" +#include "margi.h" + +void out(struct cvdv_cards *card); +void clkon(struct cvdv_cards *card); +void clkoff(struct cvdv_cards *card); +void dat(struct cvdv_cards *card, u_char data); +int rdat(struct cvdv_cards *card); +void I2CStart(struct cvdv_cards *card); +void I2CStop(struct cvdv_cards *card); +int I2CAck(struct cvdv_cards *card, int ack); +u_char I2CReadByte(struct cvdv_cards *card, int ack); +int I2CSendByte(struct cvdv_cards *card, u_char data); +void I2CWrite(struct cvdv_cards *card, int adr, int reg, int val); +u_char I2CRead(struct cvdv_cards *card, int adr, int reg); +int I2CScan(struct cvdv_cards *card, int adr); +void I2CScanBus(struct cvdv_cards *card); +void I2CSend(struct cvdv_cards *card, int adr, u_char * vals); + +#endif /* I2C_H */ diff -Nru a/drivers/media/video/margi/l64014.h b/drivers/media/video/margi/l64014.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/l64014.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,188 @@ +/* + l64014.h + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef L64014_h +#define L64014_h + +#include +#include +#include + + +#define DIO_CONTROL_INDEX 0x00 +#define DIO_CONTROL_DATA 0x02 +#define DIO_LSI_STATUS 0x04 +#define DIO_LSI_CHANNEL_DATA 0x04 +#define DIO_LSI_INDEX_LOW 0x08 +#define DIO_LSI_DATA 0x0A +#define DIO_LSI_INDEX_HIGH 0x0C + +#define LSI_READY 0x08 +#define LSI_WAIT 0x04 +#define LSI_ARQ 0x02 +#define LSI_VRQ 0x01 + +#define IIO_ID 0x00 +#define IIO_MODE 0x01 +#define IIO_IRQ_CONTROL 0x02 +#define IIO_IRQ_STATUS 0x03 +#define IIO_LSI_CONTROL 0x06 +#define IIO_OSC_AUD 0x08 +#define IIO_VIDEO_CONTROL0 0x09 +#define IIO_VIDEO_CONTROL1 0x0A +#define IIO_VIDEO_LOOKUP 0x0B +#define IIO_EEPROM_CONTROL 0x0C +#define IIO_VIDEO_HOR_DELAY 0x0D +#define IIO_VIDEO_HOR_ACTIVE 0x0E +#define IIO_VIDEO_HOR_HIGH 0x0F +#define IIO_GPIO_CONTROL 0x10 +#define IIO_GPIO_PINS 0x11 +#define IIO_CSS_COMMAND 0x12 +#define IIO_CSS_STATUS 0x13 +#define IIO_CSS_KEY 0x14 + +#define SCL 0x02 +#define SDA 0x04 + +#define CS_CONTROL0 0x00 +#define CS_CONTROL1 0x01 +#define CS_CONTROL2 0x02 +#define CS_DAC 0x04 +#define CS_STATUS 0x07 +#define CS_BKG_COL 0x08 +#define CS_GPIO_CTRL 0x09 +#define CS_GPIO_DATA 0x0A +#define CS_C_AMP 0x0D +#define CS_Y_AMP 0x0E +#define CS_I2C_ADR 0x0F +#define CS_SC_AMP 0x10 +#define CS_SC_SYNTH0 0x11 +#define CS_SC_SYNTH1 0x12 +#define CS_SC_SYNTH2 0x13 +#define CS_SC_SYNTH3 0x14 +#define CS_HUE_LSB 0x15 +#define CS_HUE_MSB 0x16 +#define CS_CC_EN 0x18 +#define CS_CC_21_1 0x19 +#define CS_CC_21_2 0x1A +#define CS_CC_284_1 0x1B +#define CS_CC_284_2 0x1C +#define CS_INT_EN 0x3B +#define CS_INT_CLR 0x3C +#define CS_ID_REG 0x3D + + +#define CSS_COMMAND 0x12 +#define CSS_STATUS 0x13 +#define CSS_KEY 0x14 + +#define L14_CSS_NONE 0x00 +#define L14_CSS_PASSTHRU 0x01 +#define L14_CSS_DESCRAM 0x05 +#define L14_CSS_GEN_CH 0x08 +#define L14_CSS_RD_CH 0x09 +#define L14_CSS_WR_CH 0x0a +#define L14_CSS_WR_DRVREF 0x0b +#define L14_CSS_DRVAUTH 0x0c +#define L14_CSS_DECAUTH 0x0d +#define L14_CSS_DISCKEY 0x0e +#define L14_CSS_TITLEKEY 0x0f +#define L14_CSS_CMD_START 0x10 + +#define L14_CSS_BUSY 0x01 +#define L14_CSS_SUCCESS 0x02 + +#define DSVC 0x40 +#define RR 0x20 +#define DR 0x01 +#define AF1 0x20 +#define AF0 0x10 +#define SLEEP 0x08 +#define AFS2 0x04 +#define AFS1 0x02 +#define AFS0 0x01 + +#define ZVCLK13 0x04 +#define ZVCLKINV 0x08 +#define ZV16BIT 0x10 +#define ZVVREF_INVERT 0x08 +#define ZVHREF_INVERT 0x10 +#define HSYNC_INVERT 0x20 +#define ZV_OVERRIDE 0x40 +#define ZV_ENABLE 0x80 + + +#define IRQ_EN 0x04 +#define IRQ_MSK 0x08 +#define IRQ_POL 0x10 +#define DEC_EN 0x20 +#define DEC_INT 0x10 +#define VSYNC_EN 0x80 +#define VSYNC_INT 0x40 + +#define VMS_NOSY 0x00 +#define VMS_NTSC 0x01 +#define VMS_PAL 0x02 +#define VMS_PAL24 0x03 + +#define MAUDIO_PAUSE 0 +#define MAUDIO_PLAY 1 +#define MAUDIO_FAST 2 +#define MAUDIO_SLOW 3 + + +#define RegisterReadByte(card,where) read_indexed_register(&(card->link),(where)) +#define RegisterWriteByte(card,where,what) write_indexed_register(&(card->link),where,what) +#define RegisterMaskByte(card,where,mask,bits) RegisterWriteByte(card,where,(RegisterReadByte(card,where)&~(mask))|(bits)) +#define RegisterSetByte(card,where,bits) RegisterWriteByte(card,where,RegisterReadByte(card,where)|(bits)) +#define RegisterDelByte(card,where,mask) RegisterWriteByte(card,where,RegisterReadByte(card,where)&~(mask)) + +#define RegisterReadWord(card,where) (\ + (u16)RegisterReadByte(card,where)|\ + ((u16)RegisterReadByte(card,(where)+1)<<8)) +#define RegisterWriteWord(card,where,what) {\ + RegisterWriteByte(card,where,(what) & 0xFF);\ + RegisterWriteByte(card,(where)+1,((what)>>8) & 0xFF);} + +// 3-byte-wide (medium word, 24 Bit) access to the card's registers, LSB first +#define RegisterReadMWord(card,where) (\ + (u32)RegisterReadByte(card,where)|\ + ((u32)RegisterReadByte(card,(where)+1)<<8)|\ + ((u32)RegisterReadByte(card,(where)+2)<<16)) +#define RegisterWriteMWord(card,where,what) {\ + RegisterWriteByte(card,where,(what) & 0xFF);\ + RegisterWriteByte(card,(where)+1,((what)>>8) & 0xFF);\ + RegisterWriteByte(card,(where)+2,((what)>>16) & 0xFF);} + +// double-word-wide access to the card's registers, LSB first +//#define RegisterReadDWord(card,where) le32_to_cpu(readl(card->addr+(where))) +//#define RegisterWriteDWord(card,where,what) writel(cpu_to_le32(what),card->addr+(where)) +#define RegisterReadDWord(card,where) (\ + (u32)RegisterReadByte(card,where)|\ + ((u32)RegisterReadByte(card,(where)+1)<<8)|\ + ((u32)RegisterReadByte(card,(where)+2)<<16)|\ + ((u32)RegisterReadByte(card,(where)+3)<<24)) +#define RegisterWriteDWord(card,where,what) {\ + RegisterWriteByte(card,where,(what) & 0xFF);\ + RegisterWriteByte(card,(where)+1,((what)>>8) & 0xFF);\ + RegisterWriteByte(card,(where)+2,((what)>>16) & 0xFF);\ + RegisterWriteByte(card,(where)+3,((what)>>24) & 0xFF);} + +#endif diff -Nru a/drivers/media/video/margi/l64021.h b/drivers/media/video/margi/l64021.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/l64021.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,108 @@ +/* + l64021.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _L64021_H_ +#define _L64021_H_ + +#include "margi.h" +#include "l64014.h" +// L64021 DRAM definitions + +#define DRAMMaxSize 0x00200000 // 2 MWords of DRAM + +// definitions for the L64021 + +#define DECODER_OFFSET 0x400 + +#define L21INTR0 0x000 +#define L21INTR1 0x001 +#define L21INTR2 0x002 +#define L21INTR3 0x003 +#define L21INTR4 0x004 + + +// Host interface registers + +// Video Decoder Registers + +// CSS Regs + +// Memory Interface + +// Microcontroller + +// Video Interface + +// Audio Decoder + +// RAM Test + +// SPU Decoder + + + + + //////////////////////////////////////////////////// + // // + // Access to the L64021 registers (0x400-0x7FF) // + // // +//////////////////////////////////////////////////// + +#define DecoderWriteByte(card,where,what) WriteByte(card,where,what) +#define DecoderReadByte(card,where) ReadByte(card,where) +#define DecoderMaskByte(card,where,mask,bits) MaskByte(card,where,mask,bits) +#define DecoderSetByte(card,addr,bits) DecoderWriteByte(card,addr,DecoderReadByte(card,addr)|(bits)) +#define DecoderDelByte(card,addr,mask) DecoderWriteByte(card,addr,DecoderReadByte(card,addr)&~(mask)) + +#define DecoderReadWord(card,addr) ((u16)DecoderReadByte(card,addr)|\ + ((u16)DecoderReadByte(card,(addr)+1)<<8)) + +#define DecoderWriteWord(card,addr,data) {\ + DecoderWriteByte(card,addr,(data) & 0xFF);\ + DecoderWriteByte(card,(addr)+1,((data)>>8) & 0xFF);} + + +#define DecoderReadMWord(card, addr)(\ + (u32)DecoderReadByte(card,addr)|\ + ((u32)DecoderReadByte(card,(addr)+1)<<8)|\ + ((u32)DecoderReadByte(card,(addr)+2)<<16)) + +#define DecoderWriteMWord(card,addr,data) {\ + DecoderWriteByte(card,addr,(data) & 0xFF);\ + DecoderWriteByte(card,(addr)+1,((data)>>8) & 0xFF);\ + DecoderWriteByte(card,(addr)+2,((data)>>16) & 0xFF);} + +#define DecoderReadDWord(card,addr) (\ + (u32)DecoderReadByte(card,addr)|\ + ((u32)DecoderReadByte(card,(addr)+1)<<8)|\ + ((u32)DecoderReadByte(card,(addr)+2)<<16)|\ + ((u32)DecoderReadByte(card,(addr)+3)<<24)) + +#define DecoderWriteDWord(card,addr,data) {\ + DecoderWriteByte(card,addr,(data) & 0xFF);\ + DecoderWriteByte(card,(addr)+1,((data)>>8) & 0xFF);\ + DecoderWriteByte(card,(addr)+2,((data)>>16) & 0xFF);\ + DecoderWriteByte(card,(addr)+3,((data)>>24) & 0xFF);} + + +void l64020Reset(struct cvdv_cards *card); + + +#endif // _L64021_H_ diff -Nru a/drivers/media/video/margi/makedev.napi b/drivers/media/video/margi/makedev.napi --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/makedev.napi Wed Feb 13 20:04:01 2002 @@ -0,0 +1,28 @@ +mkdir /dev/ost +chmod 755 /dev/ost +mknod -m 0666 /dev/ost/video0 c 250 0 +mknod -m 0666 /dev/ost/audio0 c 250 1 +mknod -m 0666 /dev/ost/sec0 c 250 2 +mknod -m 0666 /dev/ost/qpskfe0 c 250 3 +mknod -m 0666 /dev/ost/qamfe0 c 250 7 +mknod -m 0666 /dev/ost/demux0 c 250 4 +mknod -m 0666 /dev/ost/dvr0 c 250 5 +mknod -m 0666 /dev/ost/ca0 c 250 6 + +mknod -m 0666 /dev/ost/video1 c 250 64 +mknod -m 0666 /dev/ost/audio1 c 250 65 +mknod -m 0666 /dev/ost/sec1 c 250 66 +mknod -m 0666 /dev/ost/qpskfe1 c 250 67 +mknod -m 0666 /dev/ost/qamfe1 c 250 71 +mknod -m 0666 /dev/ost/demux1 c 250 68 +mknod -m 0666 /dev/ost/dvr1 c 250 69 +mknod -m 0666 /dev/ost/ca1 c 250 70 + +cd /dev/ost +ln -sf ca0 ca +ln -sf video0 video +ln -sf sec0 sec +ln -sf audio0 audio +ln -sf qpskfe0 qpskfe +ln -sf demux0 demux +ln -sf dvr0 dvr diff -Nru a/drivers/media/video/margi/margi b/drivers/media/video/margi/margi --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi Wed Feb 13 20:03:58 2002 @@ -0,0 +1,5 @@ +#!/bin/sh +# +# + +exit 0 diff -Nru a/drivers/media/video/margi/margi.c b/drivers/media/video/margi/margi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,1496 @@ +/* + margi.c + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "margi.h" + +#include +#include +#include +#include +#include +#include +#include + + + +#include "l64014.h" +#include "l64021.h" +#include "i2c.h" +#include "decoder.h" +#include "dram.h" +#include "video.h" +#include "cvdv.h" + + +static char *version = "margi_cs.c 0.6 02/04/2000 (Marcus Metzler)"; + +//#define USE_BH 1 +#ifdef USE_BH +#define MARGI_BH 31 +// shouldn't be a number, but then MARGI_BH must be entered into interrupt.h +#endif + +MODULE_AUTHOR(AUTHOR); +MODULE_DESCRIPTION(MEDDEVNAME " Driver V." DVERSION); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#define MAX_DEV 4 +#define DEVICE_NR(minor) ((minor)>>4) + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ +static int svhs = 1; +MODULE_PARM(svhs,"i"); +static int composite = 1; +MODULE_PARM(composite,"i"); +static int use_zv = 1; +MODULE_PARM(use_zv,"i"); + +/* Release IO ports after configuration? */ +static int free_ports = 0; + +/* The old way: bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static u_int irq_mask = 0xdeb8; +/* Newer, simpler way of listing specific interrupts */ +static int irq_list[4] = { -1 }; + +MODULE_PARM(free_ports, "i"); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +extern unsigned int major_device_number; +extern struct file_operations cvdv_fileops; + +typedef struct margi_info_t { + dev_link_t link; + dev_node_t node; + struct cvdv_cards card; + int stop; +} margi_info_t; + + + +/* + The event() function is this driver's Card Services event handler. + It will be called by Card Services when an appropriate card status + event is received. The config() and release() entry points are + used to configure or release a socket, in response to card + insertion and ejection events. They are invoked from the margi + event handler. +*/ + +static void margi_config(dev_link_t * link); +static void margi_release(u_long arg); +static int margi_event(event_t event, int priority, + event_callback_args_t * args); +/* + The attach() and detach() entry points are used to create and destroy + "instances" of the driver, where each instance represents everything + needed to manage one actual PCMCIA card. +*/ + +static dev_link_t *margi_attach(void); +static void margi_detach(dev_link_t *); +static u_char read_lsi_status(struct cvdv_cards *card); + +/* + You'll also need to prototype all the functions that will actually + be used to talk to your device. See 'memory_cs' for a good example + of a fully self-sufficient driver; the other drivers rely more or + less on other parts of the kernel. +*/ + +/* + The dev_info variable is the "key" that is used to match up this + device driver with appropriate cards, through the card configuration + database. +*/ + +static dev_link_t *dev_table[MAX_DEV] = { NULL, /* ... */ }; + +static dev_info_t dev_info = "margi_cs"; + +/* + A linked list of "instances" of the margi device. Each actual + PCMCIA card corresponds to one device instance, and is described + by one dev_link_t structure (defined in ds.h). + + You may not want to use a linked list for this -- for example, the + memory card driver uses an array of dev_link_t pointers, where minor + device numbers are used to derive the corresponding array index. +*/ + +static dev_link_t *dev_list = NULL; + +/* + A dev_link_t structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a dev_link_t structure can be used to point to + a device-specific private data structure, like this. + + To simplify the data structure handling, we actually include the + dev_link_t structure in the device's private data structure. + + A driver needs to provide a dev_node_t structure for each device + on a card. In some cases, there is only one device per card (for + example, ethernet cards, modems). In other cases, there may be + many actual or logical devices (SCSI adapters, memory cards with + multiple partitions). The dev_node_t structures need to be kept + in a linked list starting at the 'dev' field of a dev_link_t + structure. We allocate them in the card's private data structure, + because they generally shouldn't be allocated dynamically. + + In this case, we also provide a flag to indicate if a device is + "stopped" due to a power management event, or card ejection. The + device IO routines can use a flag like this to throttle IO to a + card that is not ready to accept it. + + The bus_operations pointer is used on platforms for which we need + to use special socket-specific versions of normal IO primitives + (inb, outb, readb, writeb, etc) for card IO. +*/ + +void DACSetFrequency(struct cvdv_cards *card, int khz, int multiple) { + uint8_t b = read_indexed_register(card, IIO_OSC_AUD); + + b &= 0xf8; + + switch (khz){ + case 32: + b |= 0x04; + break; + case 48: + b |= 0x00; + break; + case 44: + b |= 0x01; + break; + case 96: + b |= 0x02; + break; + default: + b |= 0x00; + break; + } + write_indexed_register(card, IIO_OSC_AUD, b); + +} + +int MargiFreeBuffers(struct cvdv_cards *card) +{ + MDEBUG(1, ": -- MargiFreeBuffers\n"); + + ring_destroy(&(card->rbufB)); + card->use_ringB = 0; + ring_destroy(&(card->rbufA)); + card->use_ringA = 0; + + return 0; +} + + +int MargiSetBuffers(struct cvdv_cards *card, uint32_t size, int isB) +{ + int err = 0; + + MDEBUG(0, ": -- MargiSetBuffers(%d) %d\n", + size, isB); + + if (isB){ + err = ring_init(&(card->rbufB),size); + if (!err) card->use_ringB = 1; + } else { + err = ring_init(&(card->rbufA),size); + if (!err) card->use_ringA = 1; + } + + MDEBUG(0,"set buffers: %d use_ringA: %d use_ringB: %d\n",err, +card->use_ringA,card->use_ringB); + return err; +} + + +int MargiFlush (struct cvdv_cards *card) +{ + int co = 0; + int i; + for (i=0;i<100;i++){ + MargiPushA(card, 32, FlushPacket); + MargiPushB(card, 32, FlushPacket); + } + while ( (ring_write_rest(&(card->rbufA))|| ring_write_rest(&(card->rbufB))) && co<100) + co++; + VideoSetBackground(card, 1, 0, 0, 0); // black + + if (card->use_ringA) ring_flush(&(card->rbufA)); + if (card->use_ringB) ring_flush(&(card->rbufB)); + card->DMAABusy = 0; + card->DMABBusy = 0; + + + DecoderStopChannel(card); + DecoderStreamReset(card); + DecoderSetupReset(card); + card->channelrun = 0; + + MDEBUG(1, ": Margi Flush \n"); + return 0; +} + + +int MargiPushA(struct cvdv_cards *card, int count, const char *data) +{ + int fill; + + fill = ring_read_rest(&(card->rbufA)); + + if (!card->use_ringA) + return 0; + if ((count>fill || fill > 3*card->rbufA.size/4) + && !card->channelrun){ + DecoderStartChannel(card); + card->DMAABusy = 1; + } + + count = ring_write(&(card->rbufA),data,count); + + return count; +} + +int MargiPushB(struct cvdv_cards *card, int count, const char *data) +{ + int fill; + + fill = ring_read_rest(&(card->rbufB)); + + if (!card->use_ringB) + return 0; + if ((count>fill || fill > 3*card->rbufB.size/4) + && !card->channelrun){ + DecoderStartChannel(card); + card->DMABBusy = 1; + } + + count = ring_write(&(card->rbufB),data,count); + + return count; +} + +int DecoderStartChannel(struct cvdv_cards *card) +{ + DecoderMaskByte(card, 0x007, 0xC3, 0xC3); // channel start + +#ifdef BYPASS + DecoderMaskByte(card,0x005,0x0F,0x08); +#else + DecoderMaskByte(card,0x005,0x0F,0x01); +#endif + card->channelrun = 1; + return 0; +} + +int DecoderStopChannel(struct cvdv_cards *card) +{ + DecoderMaskByte(card, 0x007, 0xC3, 0xC2); // channel reset + DecoderSetByte(card, 0x005, 0x04); // channel pause + card->channelrun = 0; + return 0; +} + +uint32_t DecoderGetAudioBufferSpace(struct cvdv_cards *card) +{ + + uint32_t MaxSize, Size; + + MaxSize = card->AudioESSize; + Size = DecoderGetAudioESLevel(card); + + if (Size>MaxSize) + return 0; + return (MaxSize - Size); + +} + +uint32_t DecoderGetVideoBufferSpace(struct cvdv_cards *card) +{ + + uint32_t MaxSize, Size; + + MaxSize = card->VideoESSize; + Size = DecoderGetVideoESLevel(card); + + if (Size>MaxSize) + return 0; + return (MaxSize - Size); + +} + +uint32_t DecoderGetBufferSpace(struct cvdv_cards *card) +{ + uint32_t audio,video; + + audio = DecoderGetAudioBufferSpace(card); + video = DecoderGetVideoBufferSpace(card); + + if (audio > 2048) audio -= 2048; + if (video > 2048) video -= 2048; + + if (audio < video) return audio; + return video; +} + + + +static int ringDMA (struct cvdv_cards *card){ + + uint32_t size = 0; + u_char stat; + dev_link_t *link = &(((margi_info_t *) card->margi)->link); + uint32_t acount=0; + uint32_t vcount=0; + uint8_t data; + ringbuffy *buffy; + int stype; + wait_queue_head_t *wq; + stat = read_lsi_status(card); + + + stype = card->setup.streamtype; + + if (stat & LSI_ARQ) { + stat = read_lsi_status(card); + } + + if (stat & LSI_READY){ + data = read_indexed_register(card, IIO_LSI_CONTROL); + data |= RR; + write_indexed_register(card, IIO_LSI_CONTROL, data); + return 0; + } + + if ((stat & LSI_ARQ) == 0) { + switch(stype){ + case stream_PES: + case stream_ES: + data = read_indexed_register(card, IIO_LSI_CONTROL); + data &= ~DSVC; + write_indexed_register(card, IIO_LSI_CONTROL, data); + buffy = &card->rbufB; + wq = &(card->wqB); + acount = ring_read_rest(buffy); + size = DecoderGetAudioBufferSpace(card); + if (size > 2048) size -= 2048; + break; + default: + buffy = &card->rbufA; + wq = &(card->wqA); + acount = ring_read_rest(buffy); + size = DecoderGetBufferSpace(card); + break; + } + if (acount > size) acount = size & 0xfffffffc; + if (acount>=2048) acount &=0xfffff800; + acount &=0xfffffffc; + + if (acount > size) acount = size & 0xfffffffc; + if (acount) { + ring_read_direct(buffy, + link->io.BasePort1+DIO_LSI_STATUS, + acount); + } else { + wake_up_interruptible(wq); + acount = 0; + } + } else { + acount = 0; + } + + if ((stat & LSI_VRQ) == 0 && + (stype == stream_PES || stype == stream_ES)) { + data = read_indexed_register(card, IIO_LSI_CONTROL); + data |= DSVC; + write_indexed_register(card, IIO_LSI_CONTROL, data); + buffy = &card->rbufA; + wq = &(card->wqA); + vcount = ring_read_rest(buffy); + + size = DecoderGetVideoBufferSpace(card); + if (size > 2048) size -= 2048; + if (vcount > size) vcount = size & 0xfffffffc; + if (vcount>=2048) vcount &=0xfffff800; + vcount &=0xfffffffc; + + if (vcount > size) vcount = size & 0xfffffffc; + if (vcount) { + ring_read_direct(buffy, + link->io.BasePort1+DIO_LSI_STATUS, + vcount); + } else { + wake_up_interruptible(wq); + vcount = 0; + } + } else { + vcount = 0; + } + + return vcount+acount; +} + + +u_char read_indexed_register(struct cvdv_cards * card, int addr) +{ + dev_link_t *link = &(((margi_info_t *) card->margi)->link); + u_char data; +#ifdef NOINT + spin_lock(&card->timelock); +#endif + outb(addr, link->io.BasePort1 + DIO_CONTROL_INDEX); + data = (inb(link->io.BasePort1 + DIO_CONTROL_DATA)); +#ifdef NOINT + spin_unlock(&card->timelock); +#endif + return data; +} + + +void write_indexed_register(struct cvdv_cards *card, int addr, u_char data) +{ + dev_link_t *link = &(((margi_info_t *) card->margi)->link); +#ifdef NOINT + spin_lock(&card->timelock); +#endif + outb(addr, link->io.BasePort1 + DIO_CONTROL_INDEX); + outb(data, link->io.BasePort1 + DIO_CONTROL_DATA); + +#ifdef NOINT + spin_unlock(&card->timelock); +#endif +} + +void WriteByte(struct cvdv_cards *card, int addr, u_char data) +{ + dev_link_t *link = &(((margi_info_t *) card->margi)->link); + +#ifdef NOINT + spin_lock(&card->timelock); +#endif + outb((u_char) (addr & 255), + link->io.BasePort1 + DIO_LSI_INDEX_LOW); + outb(((addr & 256) ? 1 : 0), + link->io.BasePort1 + DIO_LSI_INDEX_HIGH); + outb(data, link->io.BasePort1 + DIO_LSI_DATA); +#ifdef NOINT + spin_unlock(&card->timelock); +#endif +} + +u_char ReadByte(struct cvdv_cards *card, int addr) +{ + dev_link_t *link = &(((margi_info_t *) card->margi)->link); + u_char data; + +#ifdef NOINT + spin_lock(&card->timelock); +#endif + outb((u_char) (addr & 255), + link->io.BasePort1 + DIO_LSI_INDEX_LOW); + outb(((addr & 256) ? 1 : 0), + link->io.BasePort1 + DIO_LSI_INDEX_HIGH); + data = inb(link->io.BasePort1 + DIO_LSI_DATA); +#ifdef NOINT + spin_unlock(&card->timelock); +#endif + return data; +} + +void MaskByte(struct cvdv_cards *card, int addr, u_char mask, u_char bits) +{ + WriteByte(card, addr, (ReadByte(card, addr) & ~(mask)) | (bits)); +} + + + +#define MAXWRITE CHANNELBUFFERSIZE/2 +#define MAX_COUNT 400 + +#ifdef USE_BH +struct cvdv_cards *bh_card; + +static void do_margi_bh(void) +{ + struct cvdv_cards *card = bh_card; +#else + +static void do_margi(struct cvdv_cards *card) +{ + +#endif + int countA; + int try; + int stype = card->setup.streamtype; + + countA = 0; + + card->currentType = 0; + for ( try = 0; try < MAX_COUNT ;try++) + if (countA < MAXWRITE){ + int count = 0; + switch (stype){ + case stream_PES: + case stream_ES: + count = ringDMA(card); + countA += count; + if (!count) + try=MAX_COUNT; + break; + case stream_PS: + case stream_DVD: + count = ringDMA(card); + countA += count; + if (!count) + try=MAX_COUNT; + break; + } + } else break; + +} + + + + +void L64014Intr_function(struct cvdv_cards *card) +{ + uint8_t control,mask,stat; + int try; + + + control= read_indexed_register(card, IIO_IRQ_CONTROL); + if (control & IRQ_EN){ + mask = 0; + if ( control & DEC_EN ) mask |= DEC_INT; + if ( control & VSYNC_EN ) mask |= VSYNC_INT; + stat = read_indexed_register(card, IIO_IRQ_STATUS); + try = 0; + while ( (try++ < 100) && (stat & mask) ){ + + if (stat & VSYNC_INT) { + + write_indexed_register(card,IIO_IRQ_CONTROL, + control & (~VSYNC_EN)); + write_indexed_register(card,IIO_IRQ_CONTROL, + control); + + + if (card->DMAABusy || card->DMABBusy){ + +#ifdef USE_BH + bh_card = card; + mark_bh(MARGI_BH); +#else + do_margi(card); +#endif + if(card->use_ringA || card->use_ringB){ + L64021Intr(card); + } + } + } + + if (stat & DEC_INT) { + write_indexed_register(card,IIO_IRQ_CONTROL, + control & (~DEC_EN)); + write_indexed_register(card,IIO_IRQ_CONTROL, + control); + + if(card->use_ringA || card->use_ringB){ + L64021Intr(card); + } + } + + stat = read_indexed_register(card, IIO_IRQ_STATUS); + } + } + +} + + +#ifdef NOINT +void Timerfunction(unsigned long data) +{ + struct cvdv_cards *card = (struct cvdv_cards *) data; + + L64014Intr_function(card); + + card->timer.function = Timerfunction; + card->timer.data=(unsigned long) card; + card->timer.expires=jiffies+10; + if ( card->open) + add_timer(&card->timer); + +} +#endif + + +void L64014Intr(int irq, void *dev_id, struct pt_regs *regs) +{ + margi_info_t *margi = dev_id; + struct cvdv_cards *card = &(margi->card); + u_char dio_index, lsi_index_low, lsi_index_high; + +#ifdef NOINT + spin_lock(&card->timelock); +#endif + //save registers + dio_index = inb(margi->link.io.BasePort1 + DIO_CONTROL_INDEX); + lsi_index_low = inb(margi->link.io.BasePort1 + DIO_LSI_INDEX_LOW); + lsi_index_high = inb(margi->link.io.BasePort1 + DIO_LSI_INDEX_HIGH); + + + L64014Intr_function(card); + + //load registers + outb(dio_index, margi->link.io.BasePort1 + DIO_CONTROL_INDEX); + outb(lsi_index_low, margi->link.io.BasePort1 + DIO_LSI_INDEX_LOW); + outb(lsi_index_high,margi->link.io.BasePort1 + DIO_LSI_INDEX_HIGH); +#ifdef NOINT + spin_unlock(&card->timelock); +#endif +} + +int L64014RemoveIntr(struct cvdv_cards *card) +{ + MDEBUG(1, ": -- L64014RemoveIntr\n"); + // Disable the IRQ's + write_indexed_register(card, IIO_IRQ_CONTROL, 0x00); + if (!card->IntInstalled) + return 1; + L64021RemoveIntr(card); + return 0; +} + +void l64020Reset(struct cvdv_cards *card){ + uint8_t data; + + + data = read_indexed_register(card, IIO_LSI_CONTROL); + data &= ~(RR | DR); + write_indexed_register(card, IIO_LSI_CONTROL, data); + mdelay(100); + data = read_indexed_register(card, IIO_LSI_CONTROL); + data |= DR; + write_indexed_register(card, IIO_LSI_CONTROL, data); + + data = read_indexed_register(card,IIO_GPIO_PINS); + data &= ~0x01; + write_indexed_register(card,IIO_GPIO_PINS,data); + data |= 0x01; + write_indexed_register(card,IIO_GPIO_PINS,data); + + //write_indexed_register(card, IIO_LSI_CONTROL, DR); + + data = read_indexed_register(card, IIO_LSI_CONTROL); + data &= ~DSVC; + write_indexed_register(card, IIO_LSI_CONTROL, data); + +} + +void ZV_init(struct cvdv_cards *card) +{ + uint32_t delay, activel; + uint8_t reg; + delay = 235; + activel = delay + 1448; + + // init delay and active lines + write_indexed_register(card, IIO_VIDEO_HOR_DELAY, + (uint8_t)(delay & 0x00FF)); + write_indexed_register(card, IIO_VIDEO_HOR_ACTIVE, + (uint8_t)(activel & 0x00FF)); + reg = ((uint8_t)((activel >> 4) & 0x0070))|((uint8_t)((delay >> 8) & 0x0007)); + write_indexed_register(card, IIO_VIDEO_HOR_HIGH, reg); + + //init video + reg = read_indexed_register(card, IIO_VIDEO_CONTROL0); + reg |= (ZVCLK13 | ZV16BIT | ZVCLKINV); + write_indexed_register(card, IIO_VIDEO_CONTROL0, reg); + reg = read_indexed_register(card, IIO_VIDEO_CONTROL1); + reg |= (ZV_OVERRIDE | ZV_ENABLE); + write_indexed_register(card, IIO_VIDEO_CONTROL1, reg); +} + +void set_svhs(struct cvdv_cards *card, int onoff) +{ + uint8_t val; + + val = I2CRead(card, card->i2c_addr, CS_DAC)&0x0f; + MDEBUG(1, ": --svhs val 0x%02x\n",val); + + if (onoff){ + if (!card->svhs){ + I2CWrite(card, card->i2c_addr, CS_DAC, val|0x03); + card->svhs = 1; + } + } else { + if (!card->svhs){ + I2CWrite(card, card->i2c_addr, CS_DAC, val|0x30); + card->svhs = 1; + } + } + +} + +void set_composite(struct cvdv_cards *card, int onoff) +{ + uint8_t val; + + val = I2CRead(card, card->i2c_addr, CS_DAC)&0x0f; + MDEBUG(1, ": --composite val 0x%02x\n",val); + + + if (onoff){ + if (!card->composite){ + I2CWrite(card, card->i2c_addr, CS_DAC, val|0x84); + card->composite = 1; + } + } else { + if (!card->svhs){ + I2CWrite(card, card->i2c_addr, CS_DAC, val|0xE0); + card->composite = 1; + } + } + +} + + +int L64014Init(struct cvdv_cards *card) +{ + uint16_t testram[16]; + int i, err; + + MDEBUG(1, ": -- L64014Init\n"); + card->videomode = VIDEO_MODE; + + /* Reset 64020 */ + write_indexed_register(card, IIO_GPIO_CONTROL, 0x01); + l64020Reset(card); + /* init GPIO */ + write_indexed_register(card, IIO_GPIO_CONTROL, 0x01); + write_indexed_register(card, IIO_GPIO_PINS, 0xff); + + /* Set to PAL */ + write_indexed_register(card, IIO_VIDEO_CONTROL0, 0); + write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL); + + /* Set Audio freq */ + write_indexed_register(card, IIO_OSC_AUD, 0x12); + + write_indexed_register(card, CSS_COMMAND, 0x01); + + + MDEBUG(0, "CSID: %02x\n", I2CRead(card, 0, 0x3d)); + card->i2c_addr = I2CRead(card, 0, 0x0f); + MDEBUG(0, "I2CADDR: %02x\n", card->i2c_addr); + + I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x4a); + I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04); + I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x96); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x15); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0x13); + I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x54); + +// I2CWrite(card, card->i2c_addr, CS_DAC, 0x87); + if (svhs) set_svhs(card, 1); + if (composite) set_composite(card, 1); + I2CWrite(card, card->i2c_addr, CS_BKG_COL, 0x03); + + MDEBUG(0,"Decoder Status: %d\n", read_lsi_status(card)); + MDEBUG(0,"lsi stat %d\n", DecoderReadByte(card, 0x005)); + +#ifdef USE_ZV + if (use_zv) ZV_init(card); +#endif + L64021Init(card); + + // Find out how much DRAM we have + card->DRAMSize = 0x00100000; // maximum size + do { + MDEBUG(0, + ": Probing DRAM Size: 0x%08X (%d kByte) ... ", + card->DRAMSize, card->DRAMSize / 512); + for (i = 0; i < 8; i++) + testram[i] = rnd(0x100) | (rnd(0x100) << 8); + if (DRAMWriteWord(card, 0, 4, &testram[0], 0)) + MDEBUG(0, ": DRAM Write error.\n"); + if (DRAMWriteWord + (card, card->DRAMSize - 4, 4, &testram[4], + 0)) MDEBUG(0, + ": DRAM Write error.\n"); + if (DRAMReadWord(card, 0, 4, &testram[8], 0)) + MDEBUG(0, ": DRAM Read error.\n"); + if (DRAMReadWord + (card, card->DRAMSize - 4, 4, &testram[12], + 0)) MDEBUG(0, ": DRAM Read error.\n"); + err = 0; + for (i = 0; (!err) && (i < 8); i++) + if (testram[i] != testram[i + 8]) + err = i + 1; + if (err) { + MDEBUG(0," failed\n"); + } else { + MDEBUG(0," ok\n"); + } + if (err) + MDEBUG(2,": DRAM compare error at cell %d: 0x%04X %04X %04X %04X->0x%04X %04X %04X %04X / 0x%04X %04X %04X %04X->0x%04X %04X %04X %04X\n", + err, testram[0], testram[1], testram[2], + testram[3], testram[8], testram[9], + testram[10], testram[11], testram[4], + testram[5], testram[6], testram[7], + testram[12], testram[13], testram[14], + testram[15]); + if (err) + card->DRAMSize >>= 1; + } while (err && (card->DRAMSize >= 0x00100000)); + printk(KERN_INFO LOGNAME ": DRAM Size: 0x%08X (%d kByte)\n", + card->DRAMSize, card->DRAMSize / 512); + if (card->DRAMSize < 0x00100000) { // minimum size + printk(KERN_INFO LOGNAME + ": DRAM ERROR: Not enough memory on card!\n"); + return 1; + } + return 0; +} + + +void CardDeInit(struct cvdv_cards *card) +{ + CloseCard(card); + MargiFlush(card); + MargiFreeBuffers(card); + + L64014RemoveIntr(card); + card_init(card, 0); +} + + +static u_char read_lsi_status(struct cvdv_cards *card) +{ + margi_info_t *margi = (margi_info_t *) card->margi; + return (inb(margi->link.io.BasePort1 + DIO_LSI_STATUS) & 15); + +} + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + margi_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + + The dev_link structure is initialized, but we don't actually + configure the card at this point -- we wait until we receive a + card insertion event. + +======================================================================*/ + +static dev_link_t *margi_attach(void) +{ + margi_info_t *local; + dev_link_t *link; + client_reg_t client_reg; + int ret, i; + + MDEBUG(0, "margi_attach()\n"); + + for (i = 0; i < MAX_DEV; i++) + if (dev_table[i] == NULL) + break; + if (i == MAX_DEV) { + printk(KERN_NOTICE "margi_cs: no devices available\n"); + return NULL; + } + + /* Allocate space for private device-specific data */ + local = kmalloc(sizeof(margi_info_t), GFP_KERNEL); + if (!local) + return NULL; + memset(local, 0, sizeof(margi_info_t)); + link = &local->link; + link->priv = local; + local->card.margi = (void *) local; + dev_table[i] = link; + + /* Initialize the dev_link_t structure */ + link->release.function = &margi_release; + link->release.data = (u_long) link; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = NULL; + + /* + General socket configuration defaults can go here. In this + client, we assume very little, and rely on the CIS for almost + everything. In most clients, many details (i.e., number, sizes, + and attributes of IO windows) are fixed by the nature of the + device, and can be hard-wired here. + */ + link->conf.Attributes = 0; + link->conf.Vcc = 50; + +#ifndef USE_ZV + link->conf.IntType = INT_MEMORY_AND_IO; +#else + link->conf.IntType = INT_ZOOMED_VIDEO; +#endif + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &margi_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + margi_detach(link); + return NULL; + } + + return link; +} /* margi_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void margi_detach(dev_link_t * link) +{ + dev_link_t **linkp; + + int nd; + + MDEBUG(0, "margi_detach(0x%p)\n", link); + + for (nd = 0; nd < MAX_DEV; nd++) + if (dev_table[nd] == link) + break; + if (nd == MAX_DEV) + return; + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) + return; + + /* + If the device is currently configured and active, we won't + actually delete it yet. Instead, it is marked so that when + the release() function is called, that will trigger a proper + detach(). + */ + if (link->state & DEV_CONFIG) { + MDEBUG(2, "margi_cs: detach postponed, '%s' " + "still locked\n", link->dev->dev_name); + link->state |= DEV_STALE_LINK; + return; + } + + /* Break the link with Card Services */ + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, and free it */ + *linkp = link->next; + /* This points to the parent struct cvdv_cards struct */ + dev_table[nd] = NULL; + + kfree(link->priv); + +} /* margi_detach */ + +/*====================================================================== + + margi_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static void margi_config(dev_link_t * link) +{ + client_handle_t handle = link->handle; + margi_info_t *dev = link->priv; + struct cvdv_cards *card = &(dev->card); + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret, i; + u_char buf[64]; + config_info_t conf; + win_req_t req; + memreq_t map; + int minor = 0; + + MDEBUG(0, "margi_config(0x%p)\n", link); + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, handle, &conf); + link->conf.Vcc = conf.Vcc; + + /* + In this loop, we scan the CIS for configuration table entries, + each of which describes a valid card configuration, including + voltage, IO window, memory window, and interrupt settings. + + We make no assumptions about the card to be configured: we use + just the information available in the CIS. In an ideal world, + this would work for any PCMCIA card, but it requires a complete + and accurate CIS. In practice, a driver usually "knows" most of + these things without consulting the CIS, and most client drivers + will only use the CIS to fill in implementation-defined details. + */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + cistpl_cftable_entry_t dflt = { 0 }; + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + dflt = *cfg; + if (cfg->index == 0) + goto next_entry; + link->conf.ConfigIndex = cfg->index; + + /* Does this card need audio output? */ + if (cfg->flags & CISTPL_CFTABLE_AUDIO) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != + cfg->vcc.param[CISTPL_POWER_VNOM] / + 10000) goto next_entry; + } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != + dflt.vcc.param[CISTPL_POWER_VNOM] / + 10000) goto next_entry; + } + + if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; + else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* + Allocate an interrupt line. Note that this does not assign a + handler to the interrupt, unless the 'Handler' member of the + irq structure is initialized. + */ +#ifndef NOINT + link->irq.Attributes = + IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.Handler = &L64014Intr; + link->irq.Instance = link; + link->conf.Attributes |= CONF_ENABLE_IRQ; +#ifdef USE_BH + init_bh(MARGI_BH, do_margi_bh); +#endif + if (link->conf.Attributes & CONF_ENABLE_IRQ) + CS_CHECK(RequestIRQ, link->handle, &link->irq); +#endif + + /* IO window settings */ + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = + (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (!(io->flags & CISTPL_IO_8BIT)) + link->io.Attributes1 = + IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = + IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = + io->flags & CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = + link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + } + + /* This reserves IO space but doesn't actually enable it */ + CFG_CHECK(RequestIO, link->handle, &link->io); + + /* + Now set up a common memory window, if needed. There is room + in the dev_link_t structure for one memory window handle, + but if the base addresses need to be saved, or if multiple + windows are needed, the info should go in the private data + structure for this device. + + Note that the memory window base is a physical address, and + needs to be mapped to virtual space with ioremap() before it + is used. + */ + if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) { + cistpl_mem_t *mem = + (cfg->mem.nwin) ? &cfg->mem : &dflt.mem; + req.Attributes = + WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM; + req.Attributes |= WIN_ENABLE; + req.Base = mem->win[0].host_addr; + req.Size = mem->win[0].len; + req.AccessSpeed = 0; + link->win = (window_handle_t) link->handle; + CFG_CHECK(RequestWindow, &link->win, &req); + map.Page = 0; + map.CardOffset = mem->win[0].card_addr; + CFG_CHECK(MapMemPage, link->win, &map); + } + /* If we got this far, we're cool! */ + break; + + next_entry: + CS_CHECK(GetNextTuple, handle, &tuple); + } + + /* + This actually configures the PCMCIA socket -- setting up + the I/O windows and the interrupt mapping, and putting the + card and host interface into "Memory and IO" mode. + */ + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + + /* + We can release the IO port allocations here, if some other + driver for the card is going to loaded, and will expect the + ports to be available. + */ + if (free_ports) { + if (link->io.BasePort1) + release_region(link->io.BasePort1, + link->io.NumPorts1); + if (link->io.BasePort2) + release_region(link->io.BasePort2, + link->io.NumPorts2); + } + + /* + At this point, the dev_node_t structure(s) need to be + initialized and arranged in a linked list at link->dev. + */ + + first_card = card; + minor=0; + card->next = NULL; + card_init(card, minor); + if ((i = register_chrdev(CVDV_MAJOR, CVDV_PROCNAME, &cvdv_fileops)) + >= 0) { + major_device_number = ((i) ? i : CVDV_MAJOR); + printk(KERN_INFO LOGNAME + ": Char-device with major number %d installed\n", + major_device_number); + } else { + printk(KERN_ERR LOGNAME + ": ERROR: Failed to install Char-device %d, error %d\n", + CVDV_MAJOR, i); + } + + + sprintf(dev->node.dev_name, "margi"); + dev->node.major = major_device_number; + dev->node.minor = minor; + link->dev = &dev->node; +#ifdef DVB + dvb_register(card); +#endif + /* Finally, report what we've done */ + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", + dev->node.dev_name, link->conf.ConfigIndex, + link->conf.Vcc / 10, link->conf.Vcc % 10); + if (link->conf.Vpp1) + printk(", Vpp %d.%d", link->conf.Vpp1 / 10, + link->conf.Vpp1 % 10); + if (link->conf.Attributes & CONF_ENABLE_IRQ) + printk(", irq %d", link->irq.AssignedIRQ); + if (link->io.NumPorts1) + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1 + link->io.NumPorts1 - 1); + if (link->io.NumPorts2) + printk(" & 0x%04x-0x%04x", link->io.BasePort2, + link->io.BasePort2 + link->io.NumPorts2 - 1); + if (link->win) + printk(", mem 0x%06lx-0x%06lx", req.Base, + req.Base + req.Size - 1); + printk("\n"); + + link->state &= ~DEV_CONFIG_PENDING; + if (0xdd == read_indexed_register(card, IIO_ID)) { + printk("L64014 Version %d in mode %d detected\n", + (read_indexed_register(card, IIO_MODE) & 248) >> 3, + read_indexed_register(card, IIO_MODE) & 7); + write_indexed_register(card, IIO_GPIO_CONTROL, 0x07); + + L64014Init(card); + + // default: color bars + VideoSetBackground(card, 1, 0, 0, 0); // black + SetVideoSystem(card); + minorlist[minor] = card; // fast access for the char driver + + + /*enable L64014 IRQ */ + write_indexed_register(card, IIO_IRQ_CONTROL, + IRQ_POL | IRQ_EN | VSYNC_EN); +// write_indexed_register(card, IIO_IRQ_CONTROL, 0x24); + + OSDOpen(card, 50, 50, 150, 150, 2, 1); + OSDTest(card); + } + return; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + margi_release((u_long) link); + +} /* margi_config */ + +/*====================================================================== + + After a card is removed, margi_release() will unregister the + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void margi_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *) arg; + margi_info_t *dev = link->priv; + struct cvdv_cards *card = &(dev->card); + + MDEBUG(0, "margi_release(0x%p)\n", link); + /* + If the device is currently in use, we won't release until it + is actually closed, because until then, we can't be sure that + no one will try to access the device or its data structures. + */ + if (link->open) { + MDEBUG(1, "margi_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Unlink the device chain */ + link->dev = NULL; + + /* + In a normal driver, additional code may be needed to release + other kernel data structures associated with this device. + */ + + MDEBUG(1,": Unloading device driver\n"); + if (major_device_number) + unregister_chrdev(major_device_number, CVDV_PROCNAME); + CardDeInit(card); + +#ifndef NOINT +#ifdef USE_BH + remove_bh(MARGI_BH); +#endif + mdelay(100); +#endif + CloseCard(card); +#ifdef DVB + dvb_unregister(card); +#endif + /* Don't bother checking to see if these succeed or not */ + if (link->win) + CardServices(ReleaseWindow, link->win); + CardServices(ReleaseConfiguration, link->handle); + if (link->io.NumPorts1) + CardServices(ReleaseIO, link->handle, &link->io); +#ifndef NOINT + if (link->irq.AssignedIRQ) + CardServices(ReleaseIRQ, link->handle, &link->irq); +#endif + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) + margi_detach(link); + +} /* margi_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. + + When a CARD_REMOVAL event is received, we immediately set a + private flag to block future accesses to this device. All the + functions that actually access the device should check this flag + to make sure the card is still present. + +======================================================================*/ + +static int margi_event(event_t event, int priority, + event_callback_args_t * args) +{ + dev_link_t *link = args->client_data; + margi_info_t *dev = link->priv; + + MDEBUG(1, "margi_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + ((margi_info_t *) link->priv)->stop = 1; + link->release.expires = jiffies + HZ / 20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + dev->card.bus = args->bus; + margi_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + /* Mark the device as stopped, to block IO until later */ + dev->stop = 1; + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) + CardServices(RequestConfiguration, link->handle, + &link->conf); + dev->stop = 0; + /* + In a normal driver, additional code may go here to restore + the device state and restart IO. + */ + break; + } + return 0; +} /* margi_event */ + +/*====================================================================*/ + +static int __init init_margi_cs(void) +{ + servinfo_t serv; + MDEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "margi_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &margi_attach, &margi_detach); + return 0; +} + +static void __exit exit_margi_cs(void) +{ + MDEBUG(0, "margi_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) + margi_release((u_long) dev_list); + margi_detach(dev_list); + } +} + +module_init(init_margi_cs); +module_exit(exit_margi_cs); diff -Nru a/drivers/media/video/margi/margi.conf b/drivers/media/video/margi/margi.conf --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi.conf Wed Feb 13 20:04:01 2002 @@ -0,0 +1,9 @@ +device "margi_cs" + class "margi" module "margi_cs" + +# Margi +card "MARGI-Billionton" +manfid 0x01d8, 0x1000 +bind "margi_cs" + + diff -Nru a/drivers/media/video/margi/margi.h b/drivers/media/video/margi/margi.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,62 @@ +/* + margi.h + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef margi_cs_h +#define margi_cs_h + +#include "cardbase.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PBUFFER 100 + +u_char read_indexed_register(struct cvdv_cards *card, int addr); +void write_indexed_register(struct cvdv_cards *card, int addr, + u_char data); +void WriteByte(struct cvdv_cards *card, int addr, u_char data); +u_char ReadByte(struct cvdv_cards *card, int addr); +void MaskByte(struct cvdv_cards *card, int addr, u_char mask, u_char bits); +int MargiFreeBuffers(struct cvdv_cards *card); +int MargiSetBuffers(struct cvdv_cards *card, uint32_t size, int isB); +int MargiFlush (struct cvdv_cards *card); +int MargiPushA(struct cvdv_cards *card, int count, const char *data); +int MargiPushB(struct cvdv_cards *card, int count, const char *data); +int DecoderStartChannel(struct cvdv_cards *card); +int DecoderStopChannel(struct cvdv_cards *card); +void DACSetFrequency(struct cvdv_cards *card, int khz, int multiple); +stream_type get_stream_type(struct cvdv_cards *card); +audio_type get_audio_type(struct cvdv_cards *card); + +#ifdef NOINT +void Timerfunction(unsigned long data); +#endif + + +#endif diff -Nru a/drivers/media/video/margi/margi.opts b/drivers/media/video/margi/margi.opts --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi.opts Wed Feb 13 20:03:56 2002 @@ -0,0 +1,4 @@ +# +# Option for margi +# + diff -Nru a/drivers/media/video/margi/margi_cs.4 b/drivers/media/video/margi/margi_cs.4 --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi_cs.4 Wed Feb 13 20:03:59 2002 @@ -0,0 +1,34 @@ +.\" Copyright (c) 1997,1998,1999 Koji OKAMURA +.\" +.TH ISCC_CS 4 "1997/09/28" "" +.SH NAME +iscc_cs \- IBM Smart Capture Card device driver +.SH SYNOPSIS +.B insmod iscc_cs.o +.RB [ pc_debug=n ] +.RB [ mem_speed=n ] +.SH DESCRIPTION +.B Iscc_cs +is the low-level Card Services driver for the IBM Smart Capture Card +PCMCIA Video Capture adapter. When this driver is attached to a card, it +allocates the next available Video Capture Card device +.RB ( iscc0 .. iscc# ). +This +device name will be reported in the kernel log file, and passed on to +.BR cardmgr (8). +.SH PARAMETERS +.TP +.B pc_debug=n +Selects the PCMCIA debugging level. This parameter is only available +if the module is compiled with debugging enabled. A non-zero value +enables debugging. +.TP +.B mem_speed=n +Sets the access speed of the shared memory window, in nanoseconds. +The default is 0 (i.e., no extra wait states). Values of up to 1000 +are legal. +.SH AUTHOR +Koji OKAMURA \- Kyushu University + +.SH "SEE ALSO" +cardmgr(8), pcmcia(5). diff -Nru a/drivers/media/video/margi/margi_cs.mk b/drivers/media/video/margi/margi_cs.mk --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi_cs.mk Wed Feb 13 20:04:01 2002 @@ -0,0 +1,113 @@ +# +# Makefile for margi_cs +# Marcus Metzler +# + +include ../config.mk + +CC = $(KCC) $(AFLAGS) $(KFLAGS) + +CFLAGS += -g -O2 -Wall -Wstrict-prototypes -pipe -Wall -D__DVB_PACK__ -DUSE_OSD -DNOINT -DDVB -DUSE_ZV + +CPPFLAGS += $(PCDEBUG) -D__KERNEL__ -DMODULE -DMODVERSIONS -I../include \ + -I$(LINUX)/include -I$(LINUX) -Iinclude + +CC_MODULE = $(CC) -c $(CFLAGS) $(CPPFLAGS) + +ETC = $(PREFIX)/etc/pcmcia +MANDIR = $(PREFIX)/usr/man +MX_OBJS = dmxdev.o dvb_demux.o dvbdev.o + +all: margi_cs.o $(MX_OBJS) + +install-modules: $(MODULES) $(MX_OBJS) + mkdir -p $(PREFIX)/$(MODDIR)/pcmcia + cp $(MODULES) $(PREFIX)/$(MODDIR)/pcmcia + su -c "mkdir -p $(MODDIR)/misc; cp -v $(MX_OBJS) $(MX_OBJS) $(MODDIR)/misc" + +install-clients: + for f in $(CLIENTS) ; do \ + [ -r $$f.conf ] && cp $$f.conf $(ETC)/$$f.conf ; \ + cmp -s $$f $(ETC)/$$f && continue ; \ + [ -r $(ETC)/$$f ] && mv $(ETC)/$$f $(ETC)/$$f.O ; \ + cp $$f $(ETC)/$$f ; \ + OPTS=$(ETC)/$$f.opts ; \ + test -r $$OPTS || cp $$f.opts $$OPTS ; \ + done + cp cvdvext.h cvdvtypes.h /usr/include/linux + mkdir -p /usr/include/ost/ + cp include/ost/*.h /usr/include/ost + rm -rf /dev/ost + makedev.napi + depmod -a + +install-man4: $(MAN4) + mkdir -p $(MANDIR)/man4 + cp $(MAN4) $(MANDIR)/man4 + + +MMODULES = margi.o cvdv.o cardbase.o i2c.o dram.o osd.o audio.o video.o streams.o decoder.o spu.o crc.o ringbuffy.o dvb_formats.o + +margi_cs.o: $(MMODULES) + $(LD) -r -o margi_cs.o $(MMODULES) + chmod -x margi_cs.o + + +margi.o: margi.h cardbase.h l64014.h l64021.h i2c.h decoder.h dram.h\ + video.h cvdv.h margi.c + $(CC_MODULE) margi.c + +cvdv.o: cvdv.c cvdv.h cardbase.h cvdvtypes.h l64021.h l64014.h dram.h\ + osd.h audio.h video.h streams.h decoder.h spu.h \ + crc.h + $(CC_MODULE) cvdv.c + +cardbase.o: cardbase.c cardbase.h cvdvtypes.h + $(CC_MODULE) cardbase.c + +i2c.o: i2c.c i2c.h cardbase.h cvdvtypes.h l64014.h + $(CC_MODULE) i2c.c + +dram.o: dram.c dram.h cardbase.h cvdvtypes.h l64021.h l64014.h + $(CC_MODULE) dram.c + +osd.o: osd.c osd.h cardbase.h cvdvtypes.h dram.h l64021.h l64014.h + $(CC_MODULE) osd.c + +audio.o: audio.c audio.h cardbase.h cvdvtypes.h l64021.h l64014.h + $(CC_MODULE) audio.c + +video.o: video.c video.h cardbase.h cvdvtypes.h dram.h l64021.h l64014.h + $(CC_MODULE) video.c + +streams.o: streams.c streams.h cardbase.h cvdvtypes.h dram.h l64021.h l64014.h \ +video.h dram.h audio.h + $(CC_MODULE) streams.c + +decoder.o: decoder.c decoder.h cardbase.h cvdvtypes.h dram.h l64021.h l64014.h \ +video.h dram.h audio.h streams.h i2c.h osd.h dram.h + $(CC_MODULE) decoder.c + +spu.o: spu.c spu.h cardbase.h cvdvtypes.h l64021.h l64014.h + $(CC_MODULE) spu.c + +crc.o: crc.c crc.h + $(CC_MODULE) crc.c + +ringbuffy.o: ringbuffy.h ringbuffy.c + $(CC_MODULE) ringbuffy.c + +dvb_formats.o: dvb_formats.h dvb_formats.c + $(CC_MODULE) dvb_formats.c + +dmxdev.o: dmxdev.h dvb_demux.h + $(CC_MODULE) -include $(LINUX)/include/linux/modversions.h dmxdev.c + +dvb_demux.o: dvb_demux.h dmxdev.h dvbdev.h + $(CC_MODULE) -include $(LINUX)/include/linux/modversions.h dvb_demux.c + +dvbdev.o: dvbdev.h + $(CC_MODULE) -include $(LINUX)/include/linux/modversions.h -DEXPORT_SYMTAB -c dvbdev.c + +clean: + rm -f core core.* *.o .*.o *.s *.a *~ .depend .depfiles/*.d diff -Nru a/drivers/media/video/margi/margi_cs.mk.MAIN b/drivers/media/video/margi/margi_cs.mk.MAIN --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/margi_cs.mk.MAIN Wed Feb 13 20:04:01 2002 @@ -0,0 +1,43 @@ +# +# Makefile for margi_cs +# Marcus Metzler +# + +VER = 0.5.0 + +FILES = margi_cs.mk margi2/margi_cs.mk \ + margi2/margi margi2/margi.opts margi2/margi.conf margi2/margi_cs.4\ + margi2/audio.c margi2/audio.h margi2/cardbase.c margi2/cardbase.h\ + margi2/crc.c margi2/crc.h margi2/cvdv.c\ + margi2/cvdv.h margi2/cvdvtypes.h margi2/decoder.c margi2/decoder.h\ + margi2/dram.c margi2/dram.h margi2/i2c.c margi2/i2c.h margi2/l64014.h\ + margi2/l64021.h margi2/margi.c margi2/margi.h margi2/video.c \ + margi2/video.h \ + margi2/osd.c margi2/osd.h margi2/spu.c margi2/spu.h margi2/streams.c\ + margi2/streams.h margi2/ringbuffy.c margi2/ringbuffy.h \ + margi2/README margi2/COPYING margi2/AUTHORS margi2/CHANGES\ + margi2/cvdvext.h\ + margi2/testsuite/Makefile margi2/testsuite/cvdvutil.c \ + margi2/testsuite/cvdvutil.h margi2/testsuite/osdtest.c \ + margi2/testsuite/osdwrap.c margi2/testsuite/osdwrap.h \ + margi2/testsuite/showpic.c margi2/testsuite/showpicmovie.c\ + margi2/testsuite/showstill.c margi2/testsuite/testpattern.c\ + margi2/testsuite/showstill.c margi2/testsuite/playfile.c\ + margi2/include/ost/audio.h margi2/include/ost/video.h \ + margi2/include/ost/ca.h margi2/include/ost/demux.h\ + margi2/include/ost/dmx.h margi2/include/ost/frontend.h\ + margi2/dvb_demux.h margi2/dvb_demux.c margi2/dvbdev.h margi2/dvbdev.c\ + margi2/include/ost/sec.h margi2/dmxdev.h margi2/dmxdev.c \ + margi2/include/ost/osd.h margi2/dvb_formats.h margi2/dvb_formats.c + +all: + $(MAKE) -C margi2 -f margi_cs.mk + +install: + $(MAKE) -C margi2 install-modules MODULES="margi_cs.o" -f margi_cs.mk + $(MAKE) -C margi2 install-clients CLIENTS=margi -f margi_cs.mk + $(MAKE) -C margi2 install-man4 MAN4=margi_cs.4 -f margi_cs.mk + +dist: + tar czvf margi_cs-$(VER).tar.gz $(FILES) + diff -Nru a/drivers/media/video/margi/osd.c b/drivers/media/video/margi/osd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/osd.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,947 @@ +/* + osd.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + //////////////////////////////////////////////////////////////// + // // + // Functions to Draw on the On Screen Display of the L64021 // + // CLUT-Mode with 2, 4, or 8 bit per pixel, up to 720*576 // + // // +//////////////////////////////////////////////////////////////// +// OSD Pixel Aspect Ratio: +// CCIR601 525 Lines (NTSC,PAL-M): 11/10 (100*100 appears as 100*110) +// CCIR601 625 Lines (PAL): 11/12 (100*100 appears as 100*91.6) +// +// OSD functions for external use: +// int OSDOpen(struct cvdv_cards *card); +// int OSDClose(struct cvdv_cards *card); +// int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1, int *aspx, int *aspy); +// int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width, int height, int bit, int mix); +// void OSDShow(struct cvdv_cards *card); +// void OSDHide(struct cvdv_cards *card); +// void OSDClear(struct cvdv_cards *card); +// void OSDFill(struct cvdv_cards *card, int col); +// int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B, int mix, int trans); +// int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col); +// int OSDGetPixel(struct cvdv_cards *card, int x, int y); +// int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 *data); +// int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col); +// void OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1, int col); +// +// Return codes: (unless otherwise specified) +// 0: OK +// -1: Range error +// -2: OSD not open +// + +#define __NO_VERSION__ + +#include "osd.h" +#include "dram.h" +#include "l64021.h" + + // Builds a 4-word picture header in buf +// returns number of words in pixel field on success, -1 on error +int OSDHeader(u16 * buf, // 4 words + int *bit, // bit per pixel: 2, 4, or 8 + int *startrow, // position of our block, + int *stoprow, // row: 0..313 + int *startcol, // col: 0..864 + int *stopcol, // + int *mix, // opacity for mixed pixel, 0..15 (0%..94% resp.) + int nopal) +{ // 1: use previous palette + int count; + if (buf != NULL) { + if (*bit == 8) + *bit = 1; + else if (*bit == 2) + *bit = 0; + else + *bit = 2; + if (*startrow < 0) + *startrow = 0; + if (*startrow > 312) + *startrow = 312; + if (*stoprow <= *startrow) + *stoprow = *startrow + 1; + if (*stoprow > 313) + *stoprow = 313; + if (*startcol < 0) + *startcol = 0; + if (*startcol > 863) + *startcol = 863; + if (*stopcol <= *startcol) + *stopcol = *startcol + 2; + if (*stopcol > 864) + *stopcol = 864; + if ((*stopcol - *startcol + 1) & 1) + (*stopcol)--; + if (*mix < 0) + *mix = 0; + if (*mix > 15) + *mix = 15; + buf[0] = ((*bit << 14) & 0x8000) | (*startrow & 0x01FF); + buf[1] = + ((*mix << 12) & 0xF000) | ((*bit << 11) & 0x0800) | + ((nopal) ? 0x0400 : 0x0000) | (*stoprow & 0x01FF); + buf[2] = *startcol & 0x03FF; + buf[3] = *stopcol & 0x03FF; + count = + (*stoprow - *startrow + 1) * (*stopcol - *startcol + + 1); + if (*bit == 1) { + count = + ((count >> 3) + ((count & 0x07) ? 1 : 0)) << 2; + *bit = 8; + } else if (*bit == 0) { + count = + ((count >> 5) + ((count & 0x1F) ? 1 : 0)) << 2; + *bit = 2; + } else if (*bit == 2) { + count = + ((count >> 4) + ((count & 0x0F) ? 1 : 0)) << 2; + *bit = 4; + } + return count; // word count of pixel data + } else + return -1; +} + +// enables OSD mode +int OSDShow(struct cvdv_cards *card) +{ + if (card->OSD.open) { + DecoderMaskByte(card, 0x109, 0x03, 0x01); + DecoderDelByte(card, 0x112, 0x10); // no filter + return 0; + } else + return -2; +} + +// disables OSD mode +int OSDHide(struct cvdv_cards *card) +{ + if (card->OSD.open) { + DecoderMaskByte(card, 0x109, 0x03, 0x00); + return 0; + } else + return -2; +} + +// creates an empty picture in the memory of the card +// ONLY ONE PICTURE PER CARD! +// maximum sizes: NTSC: 720*525 PAL: 720*576 +// maximum positions: NTSC: 858*525 PAL: 864*625 +// returns 0 on success, -1 on DRAM allocation error +int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width, + int height, int bit, int mix) +{ + u16 TermHeader[] = { 0x01FF, 0x05FF, 0x0000, 0x0000 }; + u16 header[4]; + int size, pixelsize, palsize, frametop, startrow, stoprow, + startcol, stopcol; + + if (card->OSD.open) + return -2; + if (top & 1) { + card->OSD.evenfirst = 0; + card->OSD.evenheight = height / 2; + card->OSD.oddheight = height - card->OSD.evenheight; + } else { + card->OSD.evenfirst = 1; + card->OSD.oddheight = height / 2; + card->OSD.evenheight = height - card->OSD.oddheight; + } + + // Setting the picture for the lines in the even field + frametop = top / 2; + startrow = frametop; + stoprow = frametop + card->OSD.evenheight - 1; + startcol = left; + stopcol = left + width - 1; + pixelsize = + OSDHeader(header, &bit, &startrow, &stoprow, &startcol, + &stopcol, &mix, 0); + card->OSD.evenheight = stoprow - startrow + 1; + card->OSD.bpp = bit; + if (bit == 8) + palsize = 256; + else if (bit == 2) + palsize = 4; + else + palsize = 16; + size = 8 + palsize + pixelsize; + card->OSD.evenmem = DRAMAlloc(card, size, 32); + if (card->OSD.evenmem == BLANK) + return -1; + card->OSD.evendata = card->OSD.evenmem; + card->OSD.evenpalette = card->OSD.evendata + 4; + card->OSD.evenbitmap = card->OSD.evenpalette + palsize; + card->OSD.eventerm = card->OSD.evenbitmap + pixelsize; + DecoderWriteWord(card, 0x110, (u16) (card->OSD.evendata >> 5)); + DRAMWriteWord(card, card->OSD.evendata, 4, header, 0); + DRAMFillByte(card, card->OSD.evenpalette, + (palsize + pixelsize) * 2, 0x00); + DRAMWriteWord(card, card->OSD.eventerm, 4, TermHeader, 0); + + // Setting the picture for the lines in the odd frame + frametop += card->OSD.evenfirst; + startrow = frametop; + stoprow = frametop + card->OSD.oddheight - 1; + pixelsize = + OSDHeader(header, &bit, &startrow, &stoprow, &startcol, + &stopcol, &mix, 0); + card->OSD.oddheight = stoprow - startrow + 1; + size = 8 + palsize + pixelsize; + card->OSD.oddmem = DRAMAlloc(card, size, 32); + if (card->OSD.oddmem == BLANK) + return -1; + card->OSD.odddata = card->OSD.oddmem; + card->OSD.oddpalette = card->OSD.odddata + 4; + card->OSD.oddbitmap = card->OSD.oddpalette + palsize; + card->OSD.oddterm = card->OSD.oddbitmap + pixelsize; + DecoderWriteWord(card, 0x10E, (u16) (card->OSD.odddata >> 5)); + DRAMWriteWord(card, card->OSD.odddata, 4, header, 0); + DRAMFillByte(card, card->OSD.oddpalette, (palsize + pixelsize) * 2, + 0x00); + DRAMWriteWord(card, card->OSD.oddterm, 4, TermHeader, 0); + + // Update of the picture dimensions + card->OSD.width = stopcol - startcol + 1; + card->OSD.height = card->OSD.evenheight + card->OSD.oddheight; + card->OSD.open = 1; + + MDEBUG(1,": OSD Open %dX%d, %d bit, mem 0x%08X/0x%08X\n", + card->OSD.width, card->OSD.height, card->OSD.bpp, + card->OSD.evendata, card->OSD.odddata); + return 0; +} + +// Disables OSD and releases the buffers +// returns 0 on success, 1 on "not open" +int OSDClose(struct cvdv_cards *card) +{ + if (card->OSD.open) { + OSDHide(card); + DRAMFree(card, card->OSD.evenmem); + DRAMFree(card, card->OSD.oddmem); + card->OSD.open = 0; + return 0; + } else + return -2; +} + +// Opens OSD with this size and bit depth +// returns 0 on success, 1 on DRAM allocation error, 2 on "already open" +int OSDOpen(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int bit, int mix) +{ + int ret; + if (card->OSD.open) + OSDClose(card); + if (bit < 0) + bit = 8; + else if (bit < 2) + bit = 2; + else if (bit < 4) + bit = 4; + else + bit = 8; + if (x0 < 0) + x0 = 0; + if (x1 < 0) + x1 = 720 - 1; + if (x1 < x0) + x1 = x0; + if (y0 < 0) + y0 = 0; + if (y1 < 0) + y1 = 576 - 1; + if (y1 < y0) + y1 = y0; + if ((x1 + 1) > 720) + x1 = 720 - 1; + if (x0 > x1) + x0 = x1; + if (CCIR601Lines(card->videomode) == 625) { // PAL + if ((y1 + 1) > 576) + y1 = 576 - 1; + if (y0 > y1) + y0 = y1; + if (! + (ret = + OSDStartPicture(card, 134 + x0, 48 + y0, x1 - x0 + 1, + y1 - y0 + 1, bit, mix))) + card->OSD.aspectratio = 12; // pixel aspect ratio 12/11 + } else { // NTSC + if ((y1 + 1) > 484) + y1 = 484 - 1; + if (y0 > y1) + y0 = y1; + if (! + (ret = + OSDStartPicture(card, 126 + x0, 44 + y0, x1 - x0 + 1, + y1 - y0 + 1, bit, mix))) + card->OSD.aspectratio = 10; // pixel aspect ratio 10/11 + } + return ret; +} + +// fills parameters with the picture dimensions and the pixel aspect ratio (aspy=11) +int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1, + int *aspx) +{ + if (!card->OSD.open) + return -2; + *x0 = 0; + *x1 = card->OSD.width - 1; + *y0 = 0; + *y1 = card->OSD.height - 1; + *aspx = card->OSD.aspectratio; + return 0; +} + +// Sets all pixel to color 0 +int OSDClear(struct cvdv_cards *card) +{ + if (!card->OSD.open) + return -2; + DRAMFillByte(card, card->OSD.oddbitmap, + (int) (card->OSD.oddterm - card->OSD.oddbitmap) * 2, + 0x00); + DRAMFillByte(card, card->OSD.evenbitmap, + (int) (card->OSD.eventerm - card->OSD.evenbitmap) * 2, + 0x00); + return 0; +} + +// Sets all pixel to color +int OSDFill(struct cvdv_cards *card, int col) +{ + u8 color; + if (!card->OSD.open) + return -2; + if (card->OSD.bpp == 8) { + color = col & 0xFF; + } else if (card->OSD.bpp == 4) { + color = (col & 0xF); + color |= (color << 4); + } else if (card->OSD.bpp == 2) { + color = (col & 0x03); + for (col = 1; col <= 3; col++) + color |= (color << 2); + } else + color = 0x00; + DRAMFillByte(card, card->OSD.oddbitmap, + (int) (card->OSD.oddterm - card->OSD.oddbitmap) * 2, + color); + DRAMFillByte(card, card->OSD.evenbitmap, + (int) (card->OSD.eventerm - card->OSD.evenbitmap) * 2, + color); + return 0; +} + +// converts RGB(8 bit) to YCrCb(OSD format) +// mix: 0=opacity 100% 1=opacity at mix value +// trans: 0=mix bit applies 1=opacity 0% +// returns word in OSD palette format +u16 OSDColor(u8 R, u8 G, u8 B, int mix, int trans) +{ + u16 Y, Cr, Cb; + Y = R * 77 + G * 150 + B * 29; // Luma=0.299R+0.587G+0.114B 0..65535 + Cb = 2048 + B * 8 - (Y >> 5); // Cr 0..4095 + Cr = 2048 + R * 10 - (Y >> 5); // Cb 0..4095 + return ((trans) ? 0 : // transparent pixel + (Y & 0xFC00) | // Luma 0..63 + ((mix) ? 0x0100 : 0x0000) | // Opacity applies + ((Cb >> 4) & 0x00F0) | // Cb 0..15 + ((Cr >> 8) & 0x000F) // Cr 0..15 + ); +} + +// set palette entry to , and apply +// R,G,B: 0..255 +// RGB=1: R=Red, G=Green, B=Blue RGB=0: R=Y G=Cb B=Cr +// mix=0, trans=0: pixel opacity 100% (only OSD pixel shows) +// mix=1, trans=0: pixel opacity as specified in header +// trans=1: pixel opacity 0% (only video pixel shows) +// returns 0 on success, 1 on error +int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B, + int YUV, int mix, int trans) +{ + u16 burst[4]; // minimal memory unit + u32 addr; + u16 color; + if (!card->OSD.open) + return -2; + if (R < 0) + R = 0; + if (R > 255) + R = 255; + if (G < 0) + G = 0; + if (G > 255) + G = 255; + if (B < 0) + B = 0; + if (B > 255) + B = 255; + if ((num >= 0) && (num < (1 << card->OSD.bpp))) { + if (num==0) MDEBUG(4,"OSD SetColor num=%d, R=%d, G=%d, B=%d, YUV=%d, mix=%d, trans=%d\n", + num,R,G,B,YUV,mix,trans); + color = ((YUV) + ? ((trans) ? 0 : ((R << 8) & 0xFC00) | + ((mix) ? 0x0100 : 0x0000) | (G & 0x00F0) | + ((B >> 4) & 0x000F)) : OSDColor(R, G, B, mix, + trans)); + + addr = card->OSD.oddpalette + num; + DRAMReadWord(card, addr & ~3, 4, burst, 0); + burst[addr & 3] = color; + DRAMWriteWord(card, addr & ~3, 4, burst, 0); + + addr = card->OSD.evenpalette + num; + DRAMReadWord(card, addr & ~3, 4, burst, 0); + burst[addr & 3] = color; + DRAMWriteWord(card, addr & ~3, 4, burst, 0); + + return 0; + } else + return -1; +} + +// Set a number of entries in the palette +// sets the entries "firstcolor" through "lastcolor" from the array "data" +// data has 4 byte for each color: +// R,G,B, and a transparency value: 0->tranparent, 1..254->mix, 255->no mix +int OSDSetPalette(struct cvdv_cards *card, int firstcolor, int lastcolor, + u8 * data) +{ + u16 burst[4]; // minimal memory unit + u32 addr; + u16 color; + int num, i = 0; + if (!card->OSD.open) + return -2; + for (num = firstcolor; num <= lastcolor; num++) + if ((num >= 0) && (num < (1 << card->OSD.bpp))) { + color = + OSDColor(data[i], data[i + 1], data[i + 2], + ((data[i + 3] < 255) ? 1 : 0), + ((data[i + 3] == 0) ? 1 : 0)); + i += 4; + + addr = card->OSD.oddpalette + num; + DRAMReadWord(card, addr & ~3, 4, burst, 0); + burst[addr & 3] = color; + DRAMWriteWord(card, addr & ~3, 4, burst, 0); + + addr = card->OSD.evenpalette + num; + DRAMReadWord(card, addr & ~3, 4, burst, 0); + burst[addr & 3] = color; + DRAMWriteWord(card, addr & ~3, 4, burst, 0); + } + return 0; +} + +// Sets transparency of mixed pixel (0..15) +int OSDSetTrans(struct cvdv_cards *card, int trans) +{ + u16 burst[4]; // minimal memory unit + if (!card->OSD.open) + return -2; + trans &= 0x000F; + DRAMReadWord(card, card->OSD.evendata, 4, burst, 0); + burst[1] = (burst[1] & 0x0FFF) | (trans << 12); + DRAMWriteWord(card, card->OSD.evendata, 4, burst, 0); + + DRAMReadWord(card, card->OSD.odddata, 4, burst, 0); + burst[1] = (burst[1] & 0x0FFF) | (trans << 12); + DRAMWriteWord(card, card->OSD.odddata, 4, burst, 0); + return 0; +} + +// sets pixel , to color number +// returns 0 on success, 1 on error +int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col) +{ + u16 burst[4]; // minimal memory unit od DRAM + u32 addr; + int offset, ppw, pos, shift, height, posmask; + u16 mask; + + if (!card->OSD.open) + return -2; + if ((y & 1) == card->OSD.evenfirst) { // even or odd frame? + addr = card->OSD.oddbitmap; + height = card->OSD.oddheight; + } else { + addr = card->OSD.evenbitmap; + height = card->OSD.evenheight; + } + y >>= 1; + if ((x >= 0) && (x < card->OSD.width) && (y >= 0) && (y < height)) { // clipping + ppw = + ((card->OSD.bpp == 4) ? 2 : ((card->OSD.bpp == 8) ? 1 : 3)); // OK, 4-(ln(bpp)/ln(2)) would have worked, too... + pos = x + y * card->OSD.width; // pixel number in bitfield + addr += (pos >> ppw); // 21 bit address of word with our pixel + offset = addr & 3; // offset in burst + addr &= ~3; // 21 bit burst address + posmask = (1 << ppw) - 1; // mask for position inside word + shift = ((posmask - (pos & posmask)) << (4 - ppw)); // pixel shift inside word + mask = (1 << (1 << (4 - ppw))) - 1; // pixel mask + DRAMReadWord(card, addr, 4, burst, 0); // get the burst with our pixel... + burst[offset] = + (burst[offset] & ~(mask << shift)) | ((col & mask) << + shift); + DRAMWriteWord(card, addr, 4, burst, 0); // ...and write it back + return 0; + } else + return -1; +} + +// returns color number of pixel ,, or -1 +int OSDGetPixel(struct cvdv_cards *card, int x, int y) +{ + u16 burst[4]; // minimal memory unit + u32 addr; + int offset, ppw, pos, shift, height, posmask; + u16 mask; + + if (!card->OSD.open) + return -2; + if ((y & 1) == card->OSD.evenfirst) { // even or odd frame? + addr = card->OSD.oddbitmap; + height = card->OSD.oddheight; + } else { + addr = card->OSD.evenbitmap; + height = card->OSD.evenheight; + } + y >>= 1; + if ((x >= 0) && (x < card->OSD.width) && (y >= 0) && (y < height)) { // clipping + ppw = + ((card->OSD.bpp == 4) ? 2 : ((card->OSD.bpp == 8) ? 1 : 3)); // OK, 4-(ln(bpp)/ln(2)) would have worked, too... + pos = x + y * card->OSD.width; // pixel number in bitfield + addr += (pos >> ppw); // 21 bit address of word with our pixel + offset = addr & 3; // offset in burst + addr &= ~3; // 21 bit burst address + posmask = (1 << ppw) - 1; // mask for position inside word + shift = ((posmask - (pos & posmask)) << (4 - ppw)); // pixel shift inside word + mask = (1 << (1 << (4 - ppw))) - 1; // pixel mask + DRAMReadWord(card, addr, 4, burst, 0); // get the burst with our pixel... + return (burst[offset] >> shift) & mask; // ...and return it's value + } else + return -1; +} + +// fills pixels x0,y through x1,y with the content of data[] +// returns 0 on success, -1 on clipping all pixel +int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 * data) +{ + u16 burst[4]; // minimal memory unit + u32 addr, addr1, bitmap; + int offset, offset1, ppw, pos, pos1, shift, shift0, shift1, + shiftstep, height, bpp, x, i, endburst, endword; + u16 mask, posmask; + + if (!card->OSD.open) + return -2; + if ((y & 1) == card->OSD.evenfirst) { + bitmap = card->OSD.oddbitmap; + height = card->OSD.oddheight; + } else { + bitmap = card->OSD.evenbitmap; + height = card->OSD.evenheight; + } + y >>= 1; + if ((y >= 0) && (y < height)) { + i = 0; + if (x0 > x1) { + x = x1; + x1 = x0; + x0 = x; + } + if ((x0 >= card->OSD.width) || (x1 < 0)) + return -1; + if (x0 < 0) { + i -= x0; + x0 = 0; + } + if (x1 >= card->OSD.width) + x1 = card->OSD.width - 1; + bpp = card->OSD.bpp; // bits per pixel + ppw = ((bpp == 4) ? 2 : ((bpp == 8) ? 1 : 3)); // positional parameter + mask = (1 << bpp) - 1; // mask for one pixel + posmask = (1 << ppw) - 1; // mask for position inside word + + pos = x0 + (y * card->OSD.width); // pixel number of first pixel + pos1 = pos + x1 - x0; // pixel number of last pixel + shift0 = ((posmask - (pos & posmask)) << (4 - ppw)); + shift1 = ((posmask - (pos1 & posmask)) << (4 - ppw)); + shiftstep = 1 << (4 - ppw); + + addr = bitmap + (pos >> ppw); // DRAM address of word with first pixel + addr1 = bitmap + (pos1 >> ppw); // " " " " " last " + offset = (int) (addr & 3); // word position inside burst + offset1 = (int) (addr1 & 3); // number of last word in the last burst + addr &= ~3; // burst address + addr1 &= ~3; // burst address of last pixel + + endburst = (addr1 != addr); // end in other burst + endword = (offset1 != offset); // end in other word + + // read old content of first burst if the row start after the beginning or + // end before the end of the first burst + if (offset || (pos & posmask) || + (!endburst + && ((offset1 != 3) + || ((pos1 & posmask) != posmask)))) { + DRAMReadWord(card, addr, 4, burst, 0); + } + // End beyond or at the end of this word? + if (endburst || endword || ((pos1 & posmask) == posmask)) { + // Fill first word + for (shift = shift0; shift >= 0; shift -= shiftstep) { // bit position inside word + burst[offset] = + (burst[offset] & ~(mask << shift)) | + ((data[i++] & mask) << shift); + } + if (endburst || endword) { // Any more words to fill? + shift0 = posmask << (4 - ppw); // from here on, we start at the beginning of each word + offset++; // fill the rest of the burst + if (endburst) { // end not in this burst? + while (offset <= 3) { // fill remaining words + burst[offset] = 0x0000; // clear first + for (shift = shift0; + shift >= 0; + shift -= shiftstep) { + burst[offset] |= + ((data + [i++] & mask) + << shift); + } + offset++; + } + DRAMWriteWord(card, addr, 4, burst, 0); // write first burst + addr += 4; // go on to the next burst + while (addr < addr1) { // all bursts between start and end burst + for (offset = 0; + offset <= 3; offset++) { // 4 words per burst + burst[offset] = 0x0000; // clear first + for (shift = + shift0; + shift >= 0; + shift -= + shiftstep) { + burst + [offset] + |= + ((data + [i++] + & + mask) + << + shift); + } + } + DRAMWriteWord(card, addr, + 4, burst, 0); // write full burst + addr += 4; // next burst + } + offset = 0; + if ((offset1 < 3) || shift1) { // does the row ends before the end of the burst? + DRAMReadWord(card, addr, 4, + burst, 0); // then we have to read the old content + } + } + while (offset < offset1) { // end not in this word + burst[offset] = 0x0000; // clear first + for (shift = shift0; shift >= 0; + shift -= shiftstep) { + burst[offset] |= + ((data[i++] & mask) << + shift); + } + offset++; + } + for (shift = shift0; shift >= shift1; + shift -= shiftstep) { // last word + burst[offset] = + (burst[offset] & + ~(mask << shift)) | + ((data[i++] & mask) << shift); + } + } + } else { // row starts and ends in one word + for (shift = shift0; shift >= shift1; shift -= shiftstep) { // bit position inside word + burst[offset] = + (burst[offset] & ~(mask << shift)) | + ((data[i++] & mask) << shift); + } + } + DRAMWriteWord(card, addr, 4, burst, 0); // write only/last burst + return 0; + } else + return -1; +} + +// fills pixels x0,y0 through x1,y1 with the content of data[] +// inc contains the width of one line in the data block, +// inc<=0 uses blockwidth as linewidth +// returns 0 on success, -1 on clipping all pixel +int OSDSetBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int inc, u8 * data) +{ + int i, w = x1 - x0 + 1, ret = 0; + if (inc > 0) + w = inc; + for (i = y0; i <= y1; i++) { + ret |= OSDSetRow(card, x0, i, x1, data); + data += w; + } + return ret; +} + +// fills pixels x0,y through x1,y with the color +// returns 0 on success, -1 on clipping all pixel +int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col) +{ + u16 burst[4]; // minimal memory unit + u32 addr, addr1, bitmap; + int offset, offset1, ppw, pos, pos1, shift, shift0, shift1, + shiftstep, height, bpp, x, i, endburst, endword; + u16 mask, posmask; + + if (!card->OSD.open) + return -2; + if ((y & 1) == card->OSD.evenfirst) { + bitmap = card->OSD.oddbitmap; + height = card->OSD.oddheight; + } else { + bitmap = card->OSD.evenbitmap; + height = card->OSD.evenheight; + } + y >>= 1; + if ((y >= 0) && (y < height)) { + i = 0; + if (x0 > x1) { + x = x1; + x1 = x0; + x0 = x; + } + if ((x0 >= card->OSD.width) || (x1 < 0)) + return -1; + if (x0 < 0) { + i -= x0; + x0 = 0; + } + if (x1 >= card->OSD.width) + x1 = card->OSD.width - 1; + bpp = card->OSD.bpp; // bits per pixel + ppw = ((bpp == 4) ? 2 : ((bpp == 8) ? 1 : 3)); // positional parameter + mask = (1 << bpp) - 1; // mask for one pixel + posmask = (1 << ppw) - 1; // mask for position inside word + + pos = x0 + (y * card->OSD.width); // pixel number of first pixel + pos1 = pos + x1 - x0; // pixel number of last pixel + shift0 = ((posmask - (pos & posmask)) << (4 - ppw)); + shift1 = ((posmask - (pos1 & posmask)) << (4 - ppw)); + shiftstep = 1 << (4 - ppw); + + addr = bitmap + (pos >> ppw); // DRAM address of word with first pixel + addr1 = bitmap + (pos1 >> ppw); // " " " " " last " + offset = (int) (addr & 3); // word position inside burst + offset1 = (int) (addr1 & 3); // number of last word in the last burst + addr &= ~3; // burst address + addr1 &= ~3; // burst address of last pixel + + endburst = (addr1 != addr); // end in other burst + endword = (offset1 != offset); // end in other word + + // read old content of first burst if the row start after the beginning or + // end before the end of the first burst + if (offset || (pos & posmask) || + (!endburst + && ((offset1 != 3) + || ((pos1 & posmask) != posmask)))) { + DRAMReadWord(card, addr, 4, burst, 0); + } + if (endburst || endword || ((pos1 & posmask) == posmask)) { // end beyond or at the end of this word? + for (shift = shift0; shift >= 0; shift -= shiftstep) { // bit position inside word + burst[offset] = + (burst[offset] & ~(mask << shift)) | + ((col & mask) << shift); + } + if (endburst || endword) { + shift0 = posmask << (4 - ppw); // from here on, we start at the beginning of each word + offset++; // fill the rest of the burst + if (endburst) { // end not in this burst? + while (offset <= 3) { // fill remaining words + burst[offset] = 0x0000; // clear first + for (shift = shift0; + shift >= 0; + shift -= shiftstep) { + burst[offset] |= + ((col & mask) + << shift); + } + offset++; + } + DRAMWriteWord(card, addr, 4, burst, 0); // write first burst + addr += 4; // next burst + while (addr < addr1) { // write all the bursts between start and end burst + for (offset = 0; + offset <= 3; offset++) { + burst[offset] = + 0x0000; + for (shift = + shift0; + shift >= 0; + shift -= + shiftstep) { + burst + [offset] + |= + ((col + & + mask) + << + shift); + } + } + DRAMWriteWord(card, addr, + 4, burst, 0); + addr += 4; + } + offset = 0; + if ((offset1 < 3) || shift1) { // does the row ends before the end of the burst? + DRAMReadWord(card, addr, 4, + burst, 0); // then we have to read the old content + } + } + while (offset < offset1) { // end not in this word + burst[offset] = 0x0000; + for (shift = shift0; shift >= 0; + shift -= shiftstep) { + burst[offset] |= + ((col & mask) << + shift); + } + offset++; + } + for (shift = shift0; shift >= shift1; + shift -= shiftstep) { + burst[offset] = + (burst[offset] & + ~(mask << shift)) | ((col & + mask) << + shift); + } + } + } else { // row starts and ends in one word + for (shift = shift0; shift >= shift1; shift -= shiftstep) { // bit position inside word + burst[offset] = + (burst[offset] & ~(mask << shift)) | + ((col & mask) << shift); + } + } + DRAMWriteWord(card, addr, 4, burst, 0); + return 0; + } else + return -1; +} + +// fills pixels x0,y0 through x1,y1 with the color +// returns 0 on success, -1 on clipping all pixel +int OSDFillBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int col) +{ + int i, ret = 0; + for (i = y0; i <= y1; i++) + ret |= OSDFillRow(card, x0, i, x1, col); + return ret; +} + +// draw a line from x0,y0 to x1,y1 with the color +int OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int col) +{ + int ct, ix, iy, ax, ay, dx, dy, off; +#define sgn(a) ((a)?(((a)>0)?1:-1):0) + if (!card->OSD.open) + return -2; + dx = x1 - x0; + dy = y1 - y0; + if (dx == 0) { + if (dy < 0) + for (iy = y1; iy <= y0; iy++) + OSDSetPixel(card, x0, iy, col); + else + for (iy = y0; iy <= y1; iy++) + OSDSetPixel(card, x0, iy, col); + } else if (dy == 0) { + OSDFillRow(card, x0, y0, x1, col); + } else { + ay = 0; + ax = 0; + ix = sgn(dx); + dx = abs(dx); + iy = sgn(dy); + dy = abs(dy); + if (dx < dy) { + off = dx; + dx = dy; + dy = off; + ay = ix; + ax = iy; + ix = 0; + iy = 0; + } + off = dx >> 1; + ct = 1; + OSDSetPixel(card, x0, y0, col); + x1 = x0; + y1 = y0; + while (dx >= ct) { + x0 += ix; + y0 += ax; + ct++; + off += dy; + if (off > dx) { + off -= dx; + x0 += ay; + y0 += iy; + } + if (ax) { + OSDSetPixel(card, x0, y0, col); + } else { + if (y0 != y1) { + OSDFillRow(card, x1, y1, x0 - ay, + col); + x1 = x0; + y1 = y0; + } + } + } + if (!ax) + OSDFillRow(card, x1, y0, x0, col); + } + return 0; +} diff -Nru a/drivers/media/video/margi/osd.h b/drivers/media/video/margi/osd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/osd.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,148 @@ +/* + osd.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CVDV_OSD_H +#define CVDV_OSD_H + + //////////////////////////////////////////////////////////////// + // // + // Functions to Draw on the On Screen Display of the L64021 // + // CLUT-Mode with 2, 4, or 8 bit per pixel, up to 720*576 // + // // +//////////////////////////////////////////////////////////////// +// OSD Pixel Aspect Ratio: +// CCIR601 525 Lines (NTSC,PAL-M): 11/10 (100*100 appears as 100*110) +// CCIR601 625 Lines (PAL): 11/12 (100*100 appears as 100*91.6) +// +// OSD functions for external use: +// int OSDOpen(struct cvdv_cards *card); +// int OSDClose(struct cvdv_cards *card); +// int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1, int *aspx, int *aspy); +// int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width, int height, int bit, int mix); +// void OSDShow(struct cvdv_cards *card); +// void OSDHide(struct cvdv_cards *card); +// void OSDClear(struct cvdv_cards *card); +// void OSDFill(struct cvdv_cards *card, int col); +// int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B, int mix, int trans); +// int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col); +// int OSDGetPixel(struct cvdv_cards *card, int x, int y); +// int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 *data); +// int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col); +// void OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1, int col); +// +// Return codes: (unless otherwise specified) +// 0: OK +// -1: Range error +// -2: OSD not open +// + +#include "cardbase.h" + +// enables OSD mode +int OSDShow(struct cvdv_cards *card); + +// disables OSD mode +int OSDHide(struct cvdv_cards *card); + +// creates an empty picture in the memory of the card +// ONLY ONE PICTURE PER CARD! ( might be changed in the future, if i find time...) +// maximum sizes: NTSC: 720*525 PAL: 720*576 +// maximum positions: NTSC: 858*525 PAL: 864*625 +// returns 0 on success, -1 on DRAM allocation error +int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width, + int height, int bit, int mix); + +// Disables OSD and releases the buffers +// returns 0 on success, 1 on "not open" +int OSDClose(struct cvdv_cards *card); + +// Opens OSD with this size and bit depth +// returns 0 on success, 1 on DRAM allocation error, 2 on "already open" +int OSDOpen(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int bit, int mix); + +// fills parameters with the picture dimensions and the pixel aspect ratio (aspy=11) +int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1, + int *aspx); + +// Sets all pixel to color 0 +int OSDClear(struct cvdv_cards *card); + +// Sets all pixel to color +int OSDFill(struct cvdv_cards *card, int col); + +// converts RGB(8 bit) to YCrCb(OSD format) +// mix: 0=opacity 100% 1=opacity at mix value +// trans: 0=mix bit applies 1=opacity 0% +// returns word in OSD palette format +u16 OSDColor(u8 R, u8 G, u8 B, int mix, int trans); + +// set palette entry to , and apply +// R,G,B: 0..255 +// RGB=1: R=Red, G=Green, B=Blue RGB=0: R=Y G=Cb B=Cr +// mix=0, trans=0: pixel opacity 100% (only OSD pixel shows) +// mix=1, trans=0: pixel opacity as specified in header +// trans=1: pixel opacity 0% (only video pixel shows) +// returns 0 on success, 1 on error +int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B, + int YUV, int mix, int trans); + +// Set a number of entries in the palette +// sets the entries "firstcolor" through "lastcolor" from the array "data" +// data has 4 byte for each color: +// R,G,B, and a transparency value: 0->tranparent, 1..254->mix, 255->no mix +int OSDSetPalette(struct cvdv_cards *card, int firstcolor, int lastcolor, + u8 * data); + +// Sets transparency of mixed pixel (0..15) +int OSDSetTrans(struct cvdv_cards *card, int trans); + +// sets pixel , to color number +// returns 0 on success, 1 on error +int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col); + +// returns color number of pixel ,, or -1 +int OSDGetPixel(struct cvdv_cards *card, int x, int y); + +// fills pixels x0,y through x1,y with the content of data[] +// returns 0 on success, -1 on clipping all pixel +int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 * data); + +// fills pixels x0,y0 through x1,y1 with the content of data[] +// inc contains the width of one line in the data block, +// inc<=0 uses blockwidth as linewidth +// returns 0 on success, -1 on clipping all pixel +int OSDSetBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int inc, u8 * data); + +// fills pixels x0,y through x1,y with the color +// returns 0 on success, -1 on clipping all pixel +int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col); + +// fills pixels x0,y0 through x1,y1 with the color +// returns 0 on success, -1 on clipping all pixel +int OSDFillBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int col); + +// draw a line from x0,y0 to x1,y1 with the color +int OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1, + int col); + +#endif /* CVDV_OSD_H */ diff -Nru a/drivers/media/video/margi/ost/audio.h b/drivers/media/video/margi/ost/audio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/audio.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,120 @@ +/* + * audio.h + * + * Copyright (C) 2000 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Lesser Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_AUDIO_H_ +#define _OST_AUDIO_H_ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#define boolean int +#define true 1 +#define false 0 + +typedef enum { + AUDIO_SOURCE_DEMUX, /* Select the demux as the main source */ + AUDIO_SOURCE_MEMORY /* Select internal memory as the main source */ +} audioStreamSource_t; + +typedef enum { + AUDIO_STOPPED, /* Device is stopped */ + AUDIO_PLAYING, /* Device is currently playing */ + AUDIO_PAUSED /* Device is paused */ +} audioPlayState_t; + +typedef enum { + AUDIO_STEREO, + AUDIO_MONO_LEFT, + AUDIO_MONO_RIGHT, +} audioChannelSelect_t; + +typedef struct audioStatus { + boolean AVSyncState; /* sync audio and video? */ + boolean muteState; /* audio is muted */ + audioPlayState_t playState; /* current playback state */ + audioStreamSource_t streamSource; /* current stream source */ + audioChannelSelect_t channelSelect; /* currently selected channel */ + boolean bypassMode; /* pass on audio data to separate + decoder hardware */ +} audioStatus_t; + +typedef struct audioMixer { + unsigned int volume_left; + unsigned int volume_right; + // what else do we need? bass, pass-through, ... +} audioMixer_t; + +typedef +struct audioKaraoke{ /* if Vocal1 or Vocal2 are non-zero, they get mixed */ + int vocal1; /* into left and right t at 70% each */ + int vocal2; /* if both, Vocal1 and Vocal2 are non-zero, Vocal1 gets */ + int melody; /* mixed into the left channel and */ + /* Vocal2 into the right channel at 100% each. */ + /* if Melody is non-zero, the melody channel gets mixed */ /* into left and right */ +} audioKaraoke_t; + +typedef uint16_t audioAttributes_t; +/* bits: descr. */ +/* 15-13 audio coding mode (0=ac3, 2=mpeg1, 3=mpeg2ext, 4=LPCM, 6=DTS, */ +/* 12 multichannel extension */ +/* 11-10 audio type (0=not spec, 1=language included) */ +/* 9- 8 audio application mode (0=not spec, 1=karaoke, 2=surround) */ +/* 7- 6 Quantization / DRC (mpeg audio: 1=DRC exists)(lpcm: 0=16bit, */ +/* 5- 4 Sample frequency fs (0=48kHz, 1=96kHz) */ +/* 2- 0 number of audio channels (n+1 channels) */ + + +/* for GET_CAPABILITIES and SET_FORMAT, the latter should only set one bit */ +#define AUDIO_CAP_DTS 1 +#define AUDIO_CAP_LPCM 2 +#define AUDIO_CAP_MP1 4 +#define AUDIO_CAP_MP2 8 +#define AUDIO_CAP_MP3 16 +#define AUDIO_CAP_AAC 32 +#define AUDIO_CAP_OGG 64 +#define AUDIO_CAP_SDDS 128 +#define AUDIO_CAP_AC3 256 + +#define AUDIO_STOP _IO('o', 1) +#define AUDIO_PLAY _IO('o', 2) +#define AUDIO_PAUSE _IO('o', 3) +#define AUDIO_CONTINUE _IO('o', 4) +#define AUDIO_SELECT_SOURCE _IOW('o', 5, audioStreamSource_t) +#define AUDIO_SET_MUTE _IOW('o', 6, boolean) +#define AUDIO_SET_AV_SYNC _IOW('o', 7, boolean) +#define AUDIO_SET_BYPASS_MODE _IOW('o', 8, boolean) +#define AUDIO_CHANNEL_SELECT _IOW('o', 9, audioChannelSelect_t) +#define AUDIO_GET_STATUS _IOR('o', 10, audioStatus_t *) + +#define AUDIO_GET_CAPABILITIES _IOR('o', 11, unsigned int *) +#define AUDIO_CLEAR_BUFFER _IO('o', 12) +#define AUDIO_SET_ID _IOW('o', 13, int) +#define AUDIO_SET_MIXER _IOW('o', 14, audioMixer_t *) +#define AUDIO_SET_STREAMTYPE _IOW('o', 15, unsigned int) +#define AUDIO_SET_EXT_ID _IOW('o', 16, int) +#define AUDIO_SET_ATTRIBUTES _IOW('o', 17, audioAttributes_t) +#define AUDIO_SET_KARAOKE _IOW('o', 18, audioKaraoke_t *) +#endif /* _OST_AUDIO_H_ */ diff -Nru a/drivers/media/video/margi/ost/ca.h b/drivers/media/video/margi/ost/ca.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/ca.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,85 @@ +/* + * ca.h + * + * Copyright (C) 2000 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Lesser Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_CA_H_ +#define _OST_CA_H_ + +/* slot interface types and info */ + +typedef struct ca_slot_info_s { + int num; /* slot number */ + + int type; /* CA interface this slot supports */ +#define CA_CI 1 /* CI high level interface */ +#define CA_CI_LINK 2 /* CI link layer level interface */ +#define CA_CI_PHYS 4 /* CI physical layer level interface */ +#define CA_SC 128 /* simple smart card interface */ + + unsigned int flags; +#define CA_CI_MODULE_PRESENT 1 /* module (or card) inserted */ +#define CA_CI_MODULE_READY 2 +} ca_slot_info_t; + + +/* descrambler types and info */ + +typedef struct ca_descr_info_s { + unsigned int num; /* number of available descramblers (keys) */ + unsigned int type; /* type of supported scrambling system */ +#define CA_ECD 1 +#define CA_NDS 2 +#define CA_DSS 4 +} ca_descr_info_t; + +typedef struct ca_cap_s { + unsigned int slot_num; /* total number of CA card and module slots */ + unsigned int slot_type; /* OR of all supported types */ + unsigned int descr_num; /* total number of descrambler slots (keys) */ + unsigned int descr_type; /* OR of all supported types */ +} ca_cap_t; + +/* a message to/from a CI-CAM */ +typedef struct ca_msg_s { + unsigned int index; + unsigned int type; + unsigned int length; + unsigned char msg[256]; +} ca_msg_t; + +typedef struct ca_descr_s { + unsigned int index; + unsigned int parity; + unsigned char cw[8]; +} ca_descr_t; + +#define CA_RESET _IOW('o', 128, int) +#define CA_GET_CAP _IOR('o', 129, ca_cap_t *) +#define CA_GET_SLOT_INFO _IOR('o', 130, ca_slot_info_t *) +#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t *) +#define CA_GET_MSG _IOR('o', 132, ca_msg_t *) +#define CA_SEND_MSG _IOW('o', 133, ca_msg_t *) +#define CA_SET_DESCR _IOW('o', 134, ca_descr_t *) +#define CA_SELECT_SLOT _IOW('o', 135, int) + +#endif + diff -Nru a/drivers/media/video/margi/ost/demux.h b/drivers/media/video/margi/ost/demux.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/demux.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,346 @@ +/* * demux.h * * Copyright (c) 2000 Nokia Research Center + * Tampere, FINLAND + * + * Project: + * Universal Broadcast Access + * + * Contains: + * Type definitions of a Linux kernel-level API for filtering MPEG-2 TS + * packets and MPEG-2 sections. Support for PES packet filtering will be + * added later. + * + * History: + * 12.01.2000/JPL File created - Initial version. + * 18.02.2000/JPL Minor corrections. + * 21.02.2000/JPL DMX_NAME_SIZE and dmx_in_use() removed, typos fixed, + * some names changed. + * 23.02.2000/JPL Added a parameter indicating the callback source in + * the callback functions. + * 10.03.2000/JPL Added the macros DMX_DIR_ENTRY() and DMX_FE_ENTRY(). + * 15.03.2000/JPL Added the capabilities field to dmx_demux_t. + * 22.03.2000/JPL Corrected the callback parameter in the + * allocate_x_feed() functions. + * 03.04.2000/JPL Added support for optional resource conflict resolution + * and scarce resource handling. + * 05.04.2000/JPL Changed the dmx_resolve_conflict() to use resource + * type as a parameter. + * 12.04.2000/JPL Added a second buffer parameter for dmx_x_callback() + * functions to better handle buffer wrapping. + * 26.04.2000/JPL Added functions for section-level descrambling. + * 03.09.2000/JPL Removed support for conflict resolution and scarce + * resource handling. Otherwise only minor changes to + * data structures and function prototypes. + * + * + * Author: + * Juha-Pekka Luoma (JPL) + * Nokia Research Center + * + * Notes: + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +/* $Id: demux.h,v 1.14 2002/02/03 11:38:56 mocm Exp $ */ + +#ifndef __DEMUX_H +#define __DEMUX_H + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include /* __u8, __u16, ... */ +#include /* list_entry(), struct list_head */ +#include /* struct timespec */ +#include /* Function return values */ + +/*--------------------------------------------------------------------------*/ +/* Common definitions */ +/*--------------------------------------------------------------------------*/ + +/* + * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter. + */ + +#ifndef DMX_MAX_FILTER_SIZE +#define DMX_MAX_FILTER_SIZE 18 +#endif +/* + * dmx_success_t: Success codes for the Demux Callback API. + */ + +typedef enum { + DMX_OK = 0, /* Received Ok */ + DMX_LENGTH_ERROR, /* Incorrect length */ + DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */ + DMX_CRC_ERROR, /* Incorrect CRC */ + DMX_FRAME_ERROR, /* Frame alignment error */ + DMX_FIFO_ERROR, /* Receiver FIFO overrun */ + DMX_MISSED_ERROR /* Receiver missed packet */ +} dmx_success_t; + +/*--------------------------------------------------------------------------*/ +/* TS packet reception */ +/*--------------------------------------------------------------------------*/ + +/* TS filter type for set_type() */ + +#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */ +#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS + payload (<=184 bytes per packet) to callback */ +#define TS_DECODER 4 /* send stream to built-in decoder (if present) */ + +/* PES type for filters which write to built-in decoder */ +/* these should be kept identical to the types in dmx.h */ + +typedef enum +{ + DMX_TS_PES_AUDIO, /* also send packets to audio decoder (if it exists) */ + DMX_TS_PES_VIDEO, /* ... */ + DMX_TS_PES_TELETEXT, + DMX_TS_PES_SUBTITLE, + DMX_TS_PES_PCR, + DMX_TS_PES_OTHER, +} dmx_ts_pes_t; + + +struct dmx_ts_feed_s { + int is_filtering; /* Set to non-zero when filtering in progress */ + struct dmx_demux_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ + int (*set) (struct dmx_ts_feed_s* feed, + __u16 pid, + size_t callback_length, + size_t circular_buffer_size, + int descramble, + struct timespec timeout); + int (*start_filtering) (struct dmx_ts_feed_s* feed); + int (*stop_filtering) (struct dmx_ts_feed_s* feed); + int (*set_type) (struct dmx_ts_feed_s* feed, + int type, + dmx_ts_pes_t pes_type); +}; + +typedef struct dmx_ts_feed_s dmx_ts_feed_t; + +/*--------------------------------------------------------------------------*/ +/* PES packet reception (not supported yet) */ +/*--------------------------------------------------------------------------*/ + +typedef struct dmx_pes_filter_s { + struct dmx_pes_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ +} dmx_pes_filter_t; + +typedef struct dmx_pes_feed_s { + int is_filtering; /* Set to non-zero when filtering in progress */ + struct dmx_demux_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ + int (*set) (struct dmx_pes_feed_s* feed, + __u16 pid, + size_t circular_buffer_size, + int descramble, + struct timespec timeout); + int (*start_filtering) (struct dmx_pes_feed_s* feed); + int (*stop_filtering) (struct dmx_pes_feed_s* feed); + int (*allocate_filter) (struct dmx_pes_feed_s* feed, + dmx_pes_filter_t** filter); + int (*release_filter) (struct dmx_pes_feed_s* feed, + dmx_pes_filter_t* filter); +} dmx_pes_feed_t; + +/*--------------------------------------------------------------------------*/ +/* Section reception */ +/*--------------------------------------------------------------------------*/ + +typedef struct { + __u8 filter_value [DMX_MAX_FILTER_SIZE]; + __u8 filter_mask [DMX_MAX_FILTER_SIZE]; + struct dmx_section_feed_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ +} dmx_section_filter_t; + +struct dmx_section_feed_s { + int is_filtering; /* Set to non-zero when filtering in progress */ + struct dmx_demux_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ + int (*set) (struct dmx_section_feed_s* feed, + __u16 pid, + size_t circular_buffer_size, + int descramble, + int check_crc); + int (*allocate_filter) (struct dmx_section_feed_s* feed, + dmx_section_filter_t** filter); + int (*release_filter) (struct dmx_section_feed_s* feed, + dmx_section_filter_t* filter); + int (*start_filtering) (struct dmx_section_feed_s* feed); + int (*stop_filtering) (struct dmx_section_feed_s* feed); +}; +typedef struct dmx_section_feed_s dmx_section_feed_t; + +/*--------------------------------------------------------------------------*/ +/* Callback functions */ +/*--------------------------------------------------------------------------*/ + +typedef int (*dmx_ts_cb) ( __u8 * buffer1, + size_t buffer1_length, + __u8 * buffer2, + size_t buffer2_length, + dmx_ts_feed_t* source, + dmx_success_t success); + +typedef int (*dmx_section_cb) ( __u8 * buffer1, + size_t buffer1_len, + __u8 * buffer2, + size_t buffer2_len, + dmx_section_filter_t * source, + dmx_success_t success); + +typedef int (*dmx_pes_cb) ( __u8 * buffer1, + size_t buffer1_len, + __u8 * buffer2, + size_t buffer2_len, + dmx_pes_filter_t* source, + dmx_success_t success); + +/*--------------------------------------------------------------------------*/ +/* DVB Front-End */ +/*--------------------------------------------------------------------------*/ + +typedef enum { + DMX_OTHER_FE = 0, + DMX_SATELLITE_FE, + DMX_CABLE_FE, + DMX_TERRESTRIAL_FE, + DMX_LVDS_FE, + DMX_ASI_FE, /* DVB-ASI interface */ + DMX_MEMORY_FE +} dmx_frontend_source_t; + +typedef struct { + /* The following char* fields point to NULL terminated strings */ + char* id; /* Unique front-end identifier */ + char* vendor; /* Name of the front-end vendor */ + char* model; /* Name of the front-end model */ + struct list_head connectivity_list; /* List of front-ends that can + be connected to a particular + demux */ + void* priv; /* Pointer to private data of the API client */ + dmx_frontend_source_t source; +} dmx_frontend_t; + +/*--------------------------------------------------------------------------*/ +/* MPEG-2 TS Demux */ +/*--------------------------------------------------------------------------*/ + +/* + * Flags OR'ed in the capabilites field of struct dmx_demux_s. + */ + +#define DMX_TS_FILTERING 1 +#define DMX_PES_FILTERING 2 +#define DMX_SECTION_FILTERING 4 +#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */ +#define DMX_CRC_CHECKING 16 +#define DMX_TS_DESCRAMBLING 32 +#define DMX_SECTION_PAYLOAD_DESCRAMBLING 64 +#define DMX_MAC_ADDRESS_DESCRAMBLING 128 + +/* + * Demux resource type identifier. +*/ + +/* + * DMX_FE_ENTRY(): Casts elements in the list of registered + * front-ends from the generic type struct list_head + * to the type * dmx_frontend_t + *. +*/ + +#define DMX_FE_ENTRY(list) list_entry(list, dmx_frontend_t, connectivity_list) + +struct dmx_demux_s { + /* The following char* fields point to NULL terminated strings */ + char* id; /* Unique demux identifier */ + char* vendor; /* Name of the demux vendor */ + char* model; /* Name of the demux model */ + __u32 capabilities; /* Bitfield of capability flags */ + dmx_frontend_t* frontend; /* Front-end connected to the demux */ + struct list_head reg_list; /* List of registered demuxes */ + void* priv; /* Pointer to private data of the API client */ + int users; /* Number of users */ + int (*open) (struct dmx_demux_s* demux); + int (*close) (struct dmx_demux_s* demux); + int (*write) (struct dmx_demux_s* demux, const char* buf, size_t count); + int (*allocate_ts_feed) (struct dmx_demux_s* demux, + dmx_ts_feed_t** feed, + dmx_ts_cb callback); + int (*release_ts_feed) (struct dmx_demux_s* demux, + dmx_ts_feed_t* feed); + int (*allocate_pes_feed) (struct dmx_demux_s* demux, + dmx_pes_feed_t** feed, + dmx_pes_cb callback); + int (*release_pes_feed) (struct dmx_demux_s* demux, + dmx_pes_feed_t* feed); + int (*allocate_section_feed) (struct dmx_demux_s* demux, + dmx_section_feed_t** feed, + dmx_section_cb callback); + int (*release_section_feed) (struct dmx_demux_s* demux, + dmx_section_feed_t* feed); + int (*descramble_mac_address) (struct dmx_demux_s* demux, + __u8* buffer1, + size_t buffer1_length, + __u8* buffer2, + size_t buffer2_length, + __u16 pid); + int (*descramble_section_payload) (struct dmx_demux_s* demux, + __u8* buffer1, + size_t buffer1_length, + __u8* buffer2, size_t buffer2_length, + __u16 pid); + int (*add_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + int (*remove_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + struct list_head* (*get_frontends) (struct dmx_demux_s* demux); + int (*connect_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + int (*disconnect_frontend) (struct dmx_demux_s* demux); + + + /* added because js cannot keep track of these himself */ + int (*get_pes_pids) (struct dmx_demux_s* demux, __u16 *pids); +}; +typedef struct dmx_demux_s dmx_demux_t; + +/*--------------------------------------------------------------------------*/ +/* Demux directory */ +/*--------------------------------------------------------------------------*/ + +/* + * DMX_DIR_ENTRY(): Casts elements in the list of registered + * demuxes from the generic type struct list_head* to the type dmx_demux_t + *. + */ + +#define DMX_DIR_ENTRY(list) list_entry(list, dmx_demux_t, reg_list) + +int dmx_register_demux (dmx_demux_t* demux); +int dmx_unregister_demux (dmx_demux_t* demux); +struct list_head* dmx_get_demuxes (void); + +#endif /* #ifndef __DEMUX_H */ + diff -Nru a/drivers/media/video/margi/ost/dmx.h b/drivers/media/video/margi/ost/dmx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/dmx.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,147 @@ +/* + * dmx.h + * + * Copyright (C) 2000 Marcus Metzler + * & Ralph Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_DMX_H_ +#define _OST_DMX_H_ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#ifndef EBUFFEROVERFLOW +#define EBUFFEROVERFLOW 769 +#endif + +typedef uint16_t dvb_pid_t; + +#define DMX_FILTER_SIZE 16 + +typedef enum +{ + DMX_OUT_DECODER, /* Streaming directly to decoder. */ + DMX_OUT_TAP, /* Output going to a memory buffer */ + /* (to be retrieved via the read command).*/ + DMX_OUT_TS_TAP /* Output multiplexed into a new TS */ + /* (to be retrieved by reading from the */ + /* logical DVR device). */ +} dmxOutput_t; + + +typedef enum +{ + DMX_IN_FRONTEND, /* Input from a front-end device. */ + DMX_IN_DVR /* Input from the logical DVR device. */ +} dmxInput_t; + + +typedef enum +{ + DMX_PES_AUDIO, + DMX_PES_VIDEO, + DMX_PES_TELETEXT, + DMX_PES_SUBTITLE, + DMX_PES_PCR, + DMX_PES_OTHER +} dmxPesType_t; + + +typedef enum +{ + DMX_SCRAMBLING_EV, + DMX_FRONTEND_EV +} dmxEvent_t; + + +typedef enum +{ + DMX_SCRAMBLING_OFF, + DMX_SCRAMBLING_ON +} dmxScramblingStatus_t; + + +typedef struct dmxFilter +{ + uint8_t filter[DMX_FILTER_SIZE]; + uint8_t mask[DMX_FILTER_SIZE]; +} dmxFilter_t; + + +struct dmxFrontEnd +{ + +}; + + +struct dmxSctFilterParams +{ + dvb_pid_t pid; + dmxFilter_t filter; + uint32_t timeout; + uint32_t flags; +#define DMX_CHECK_CRC 1 +#define DMX_ONESHOT 2 +#define DMX_IMMEDIATE_START 4 +#define DMX_KERNEL_CLIENT 0x8000 +}; + + +struct dmxPesFilterParams +{ + dvb_pid_t pid; + dmxInput_t input; + dmxOutput_t output; + dmxPesType_t pesType; + uint32_t flags; +}; + + +struct dmxEvent +{ + dmxEvent_t event; + time_t timeStamp; + union + { + dmxScramblingStatus_t scrambling; + } u; +}; + + +typedef struct dmxCaps_s +{ + uint32_t caps; /* */ + int num_decoders; +} dmxCaps_t; + + +#define DMX_START _IOW('o',41,int) +#define DMX_STOP _IOW('o',42,int) +#define DMX_SET_FILTER _IOW('o',43,struct dmxSctFilterParams *) +#define DMX_SET_PES_FILTER _IOW('o',44,struct dmxPesFilterParams *) +#define DMX_SET_BUFFER_SIZE _IOW('o',45,unsigned long) +#define DMX_GET_EVENT _IOR('o',46,struct dmxEvent *) +#define DMX_GET_PES_PIDS _IOR('o',47,dvb_pid_t *) +#define DMX_GET_CAPS _IOR('o',48,dmxCaps_t *) + +#endif /*_OST_DMX_H_*/ diff -Nru a/drivers/media/video/margi/ost/frontend.h b/drivers/media/video/margi/ost/frontend.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/frontend.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,208 @@ +/* + * frontend.h + * + * Copyright (C) 2000 Marcus Metzler + * & Ralph Metzler + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _FRONTEND_H_ +#define _FRONTEND_H_ + +#include + + +#define ENOSIGNAL 768 +#ifndef EBUFFEROVERFLOW +#define EBUFFEROVERFLOW 769 +#endif + + +typedef __u32 FrontendStatus; + +/* bit definitions for FrontendStatus */ +#define FE_HAS_POWER 1 +#define FE_HAS_SIGNAL 2 +#define FE_SPECTRUM_INV 4 +#define FE_HAS_LOCK 8 +#define FE_HAS_CARRIER 16 +#define FE_HAS_VITERBI 32 +#define FE_HAS_SYNC 64 +#define FE_TUNER_HAS_LOCK 128 + + +/* possible values for spectral inversion */ +typedef enum { + INVERSION_OFF, + INVERSION_ON, + INVERSION_AUTO +} SpectralInversion; + +/* possible values for FEC_inner/FEC_outer */ +typedef enum { + FEC_AUTO, + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_5_6, + FEC_7_8, + FEC_NONE +} CodeRate; + + +typedef enum { + QPSK, + QAM_16, + QAM_32, + QAM_64, + QAM_128, + QAM_256 +} Modulation; + + +typedef enum { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_8K +} TransmitMode; + +typedef enum { + BANDWIDTH_8_MHZ, + BANDWIDTH_7_MHZ, + BANDWIDTH_6_MHZ +} BandWidth; + + +typedef enum { + GUARD_INTERVAL_1_32, + GUARD_INTERVAL_1_16, + GUARD_INTERVAL_1_8, + GUARD_INTERVAL_1_4 +} GuardInterval; + + +typedef enum { + HIERARCHY_NONE, + HIERARCHY_1, + HIERARCHY_2, + HIERARCHY_4 +} Hierarchy; + + +typedef struct { + __u32 SymbolRate; /* symbol rate in Symbols per second */ + CodeRate FEC_inner; /* forward error correction (see above) */ +} QPSKParameters; + + +typedef struct { + __u32 SymbolRate; /* symbol rate in Symbols per second */ + CodeRate FEC_inner; /* forward error correction (see above) */ + Modulation QAM; /* modulation type (see above) */ +} QAMParameters; + + +typedef struct { + BandWidth bandWidth; + CodeRate HP_CodeRate; /* high priority stream code rate */ + CodeRate LP_CodeRate; /* low priority stream code rate */ + Modulation Constellation; /* modulation type (see above) */ + TransmitMode TransmissionMode; + GuardInterval guardInterval; + Hierarchy HierarchyInformation; +} OFDMParameters; + + +typedef enum { + FE_QPSK, + FE_QAM, + FE_OFDM +} FrontendType; + + +typedef struct { + __u32 Frequency; /* (absolute) frequency in Hz for QAM/OFDM */ + /* intermediate frequency in kHz for QPSK */ + SpectralInversion Inversion; /* spectral inversion */ + union { + QPSKParameters qpsk; + QAMParameters qam; + OFDMParameters ofdm; + } u; +} FrontendParameters; + + +typedef enum { + FE_UNEXPECTED_EV, /* unexpected event (e.g. loss of lock) */ + FE_COMPLETION_EV, /* completion event, tuning succeeded */ + FE_FAILURE_EV /* failure event, we couldn't tune */ +} EventType; + + +typedef struct { + EventType type; /* type of event, FE_UNEXPECTED_EV, ... */ + + long timestamp; /* time in seconds since 1970-01-01 */ + + union { + struct { + FrontendStatus previousStatus; /* status before event */ + FrontendStatus currentStatus; /* status during event */ + } unexpectedEvent; + FrontendParameters completionEvent; /* parameters for which the + tuning succeeded */ + FrontendStatus failureEvent; /* status at failure (e.g. no lock) */ + } u; +} FrontendEvent; + +typedef struct { + FrontendType type; + __u32 minFrequency; + __u32 maxFrequency; + __u32 maxSymbolRate; + __u32 minSymbolRate; + __u32 hwType; + __u32 hwVersion; +} FrontendInfo; + + +typedef enum { + FE_POWER_ON, + FE_POWER_STANDBY, + FE_POWER_SUSPEND, + FE_POWER_OFF +} FrontendPowerState; + + +#define FE_SELFTEST _IO('o', 61) +#define FE_SET_POWER_STATE _IOW('o', 62, FrontendPowerState) +#define FE_GET_POWER_STATE _IOR('o', 63, FrontendPowerState*) +#define FE_READ_STATUS _IOR('o', 64, FrontendStatus*) +#define FE_READ_BER _IOW('o', 65, __u32*) +#define FE_READ_SIGNAL_STRENGTH _IOR('o', 66, __s32*) +#define FE_READ_SNR _IOR('o', 67, __s32*) +#define FE_READ_UNCORRECTED_BLOCKS _IOW('o', 68, __u32*) +#define FE_GET_NEXT_FREQUENCY _IOW('o', 69, __u32*) +#define FE_GET_NEXT_SYMBOL_RATE _IOW('o', 70, __u32*) + +#define FE_SET_FRONTEND _IOW('o', 71, FrontendParameters*) +#define FE_GET_FRONTEND _IOR('o', 72, FrontendParameters*) +#define FE_GET_INFO _IOR('o', 73, FrontendInfo*) +#define FE_GET_EVENT _IOR('o', 74, FrontendEvent*) + +#endif /*_FRONTEND_H_*/ + diff -Nru a/drivers/media/video/margi/ost/net.h b/drivers/media/video/margi/ost/net.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/net.h Wed Feb 13 20:04:02 2002 @@ -0,0 +1,40 @@ +/* + * net.h + * + * Copyright (C) 2000 Marcus Metzler + * & Ralph Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_NET_H_ +#define _OST_NET_H_ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +struct dvb_net_if { + uint16_t pid; + uint16_t if_num; +}; + +#define NET_ADD_IF _IOWR('o', 52, struct dvb_net_if *) +#define NET_REMOVE_IF _IOW('o', 53, uint16_t) +#endif /*_OST_VIDEO_H_*/ diff -Nru a/drivers/media/video/margi/ost/osd.h b/drivers/media/video/margi/ost/osd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/osd.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,111 @@ +/* + * osd.h + * + * Copyright (C) 2001 Ralph Metzler + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Lesser Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_OSD_H_ +#define _OST_OSD_H_ + +typedef enum { + // All functions return -2 on "not open" + OSD_Close=1, // () + // Disables OSD and releases the buffers + // returns 0 on success + OSD_Open, // (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0)) + // Opens OSD with this size and bit depth + // returns 0 on success, -1 on DRAM allocation error, -2 on "already open" + OSD_Show, // () + // enables OSD mode + // returns 0 on success + OSD_Hide, // () + // disables OSD mode + // returns 0 on success + OSD_Clear, // () + // Sets all pixel to color 0 + // returns 0 on success + OSD_Fill, // (color) + // Sets all pixel to color + // returns 0 on success + OSD_SetColor, // (color,R{x0},G{y0},B{x1},opacity{y1}) + // set palette entry to , and apply + // R,G,B: 0..255 + // R=Red, G=Green, B=Blue + // opacity=0: pixel opacity 0% (only video pixel shows) + // opacity=1..254: pixel opacity as specified in header + // opacity=255: pixel opacity 100% (only OSD pixel shows) + // returns 0 on success, -1 on error + OSD_SetPalette, // (firstcolor{color},lastcolor{x0},data) + // Set a number of entries in the palette + // sets the entries "firstcolor" through "lastcolor" from the array "data" + // data has 4 byte for each color: + // R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel + OSD_SetTrans, // (transparency{color}) + // Sets transparency of mixed pixel (0..15) + // returns 0 on success + OSD_SetPixel, // (x0,y0,color) + // sets pixel , to color number + // returns 0 on success, -1 on error + OSD_GetPixel, // (x0,y0) + // returns color number of pixel ,, or -1 + OSD_SetRow, // (x0,y0,x1,data) + // fills pixels x0,y through x1,y with the content of data[] + // returns 0 on success, -1 on clipping all pixel (no pixel drawn) + OSD_SetBlock, // (x0,y0,x1,y1,increment{color},data) + // fills pixels x0,y0 through x1,y1 with the content of data[] + // inc contains the width of one line in the data block, + // inc<=0 uses blockwidth as linewidth + // returns 0 on success, -1 on clipping all pixel + OSD_FillRow, // (x0,y0,x1,color) + // fills pixels x0,y through x1,y with the color + // returns 0 on success, -1 on clipping all pixel + OSD_FillBlock, // (x0,y0,x1,y1,color) + // fills pixels x0,y0 through x1,y1 with the color + // returns 0 on success, -1 on clipping all pixel + OSD_Line, // (x0,y0,x1,y1,color) + // draw a line from x0,y0 to x1,y1 with the color + // returns 0 on success + OSD_Query, // (x0,y0,x1,y1,xasp{color}}), yasp=11 + // fills parameters with the picture dimensions and the pixel aspect ratio + // returns 0 on success + OSD_Test, // () + // draws a test picture. for debugging purposes only + // returns 0 on success +// TODO: remove "test" in final version + OSD_Text, // (x0,y0,size,color,text) + OSD_SetWindow, // (x0) set window with number 0 + * & Marcus Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_SEC_H_ +#define _OST_SEC_H_ + +#define SEC_MAX_DISEQC_PARAMS 3 + +struct secDiseqcCmd { + uint8_t addr; + uint8_t cmd; + uint8_t numParams; + uint8_t params[SEC_MAX_DISEQC_PARAMS]; +}; + +typedef uint32_t secVoltage; + +enum { + SEC_VOLTAGE_OFF, + SEC_VOLTAGE_LT, + SEC_VOLTAGE_13, + SEC_VOLTAGE_13_5, + SEC_VOLTAGE_18, + SEC_VOLTAGE_18_5 +}; + +#define SEC_VOLTAGE_HORIZONTAL SEC_VOLTAGE_18 +#define SEC_VOLTAGE_VERTICAL SEC_VOLTAGE_13 + +typedef uint32_t secToneMode; + +typedef enum { + SEC_TONE_ON, + SEC_TONE_OFF +} secToneMode_t; + + +typedef uint32_t secMiniCmd; + +typedef enum { + SEC_MINI_NONE, + SEC_MINI_A, + SEC_MINI_B +} secMiniCmd_t; + +struct secStatus { + int32_t busMode; + secVoltage selVolt; + secToneMode contTone; +}; + +enum { + SEC_BUS_IDLE, + SEC_BUS_BUSY, + SEC_BUS_OFF, + SEC_BUS_OVERLOAD +}; + +struct secCommand { + int32_t type; + union { + struct secDiseqcCmd diseqc; + uint8_t vsec; + uint32_t pause; + } u; +}; + +struct secCmdSequence { + secVoltage voltage; + secMiniCmd miniCommand; + secToneMode continuousTone; + + uint32_t numCommands; + struct secCommand* commands; +}; + +enum { + SEC_CMDTYPE_DISEQC, + SEC_CMDTYPE_VSEC, + SEC_CMDTYPE_PAUSE +}; + + +#define SEC_GET_STATUS _IOR('o',91,struct secStatus *) +#define SEC_RESET_OVERLOAD _IOW('o',92,void) +#define SEC_SEND_SEQUENCE _IOW('o',93,struct secCmdSequence *) +#define SEC_SET_TONE _IOW('o',94,secToneMode) +#define SEC_SET_VOLTAGE _IOW('o',95,secVoltage) + +typedef enum { + SEC_DISEQC_SENT, + SEC_VSEC_SENT, + SEC_PAUSE_COMPLETE, + SEC_CALLBACK_ERROR +} secCallback_t; + + +#endif /*_OST_SEC_H_*/ diff -Nru a/drivers/media/video/margi/ost/video.h b/drivers/media/video/margi/ost/video.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ost/video.h Wed Feb 13 20:03:59 2002 @@ -0,0 +1,186 @@ +/* + * video.h + * + * Copyright (C) 2000 Marcus Metzler + * & Ralph Metzler + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _OST_VIDEO_H_ +#define _OST_VIDEO_H_ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#define boolean int +#define true 1 +#define false 0 + +typedef enum { + VIDEO_FORMAT_4_3, /* Select 4:3 format */ + VIDEO_FORMAT_16_9 /* Select 16:9 format. */ +} videoFormat_t; + +typedef enum { + VIDEO_SYSTEM_PAL, + VIDEO_SYSTEM_NTSC, + VIDEO_SYSTEM_PALN, + VIDEO_SYSTEM_PALNc, + VIDEO_SYSTEM_PALM, + VIDEO_SYSTEM_NTSC60, + VIDEO_SYSTEM_PAL60, + VIDEO_SYSTEM_PALM60 +} videoSystem_t; + +typedef enum { + VIDEO_PAN_SCAN, /* use pan and scan format */ + VIDEO_LETTER_BOX, /* use letterbox format */ + VIDEO_CENTER_CUT_OUT /* use center cut out format */ +} videoDisplayFormat_t; + +typedef enum { + VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ + VIDEO_SOURCE_MEMORY /* If this source is selected, the stream + comes from the user through the write + system call */ +} videoStreamSource_t; + +typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ +} videoPlayState_t; + +struct videoEvent { + int32_t type; + time_t timestamp; + union { + videoFormat_t videoFormat; + } u; +}; + +struct videoStatus { + boolean videoBlank; /* blank video on freeze? */ + videoPlayState_t playState; /* current state of playback */ + videoStreamSource_t streamSource; /* current source (demux/memory) */ + videoFormat_t videoFormat; /* current aspect ratio of stream */ + videoDisplayFormat_t displayFormat; /* selected cropping mode */ +}; + +/* pointer to and size of a single iframe in memory */ +struct videoDisplayStillPicture { + char *iFrame; + int32_t size; +}; + + +typedef +struct videoHighlight { + boolean active; /* 1=show highlight, 0=hide highlight */ + uint8_t contrast1; /* 7- 4 Pattern pixel contrast */ + /* 3- 0 Background pixel contrast */ + uint8_t contrast2; /* 7- 4 Emphasis pixel-2 contrast */ + /* 3- 0 Emphasis pixel-1 contrast */ + uint8_t color1; /* 7- 4 Pattern pixel color */ + /* 3- 0 Background pixel color */ + uint8_t color2; /* 7- 4 Emphasis pixel-2 color */ + /* 3- 0 Emphasis pixel-1 color */ + uint32_t ypos; /* 23-22 auto action mode */ + /* 21-12 start y */ + /* 9- 0 end y */ + uint32_t xpos; /* 23-22 button color number */ + /* 21-12 start x */ + /* 9- 0 end x */ +} videoHighlight_t; + + +typedef +struct videoSPU { + boolean active; + int streamID; +} videoSPU_t; + +typedef +struct videoSPUPalette{ /* SPU Palette information */ + int length; + uint8_t *palette; +} videoSPUPalette_t; + +typedef +struct videoNaviPack{ + int length; /* 0 ... 1024 */ + uint8_t data[1024]; +} videoNaviPack_t; + + +typedef uint16_t videoAttributes_t; +/* bits: descr. */ +/* 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) */ +/* 13-12 TV system (0=525/60, 1=625/50) */ +/* 11-10 Aspect ratio (0=4:3, 3=16:9) */ +/* 9- 8 permitted display mode on 4:3 monitor (0=both, 1=only pan-sca */ +/* 7 line 21-1 data present in GOP (1=yes, 0=no) */ +/* 6 line 21-2 data present in GOP (1=yes, 0=no) */ +/* 5- 3 source resolution (0=720x480/576, 1=704x480/576, 2=352x480/57 */ +/* 2 source letterboxed (1=yes, 0=no) */ +/* 0 film/camera mode (0=camera, 1=film (625/50 only)) */ + + +/* bit definitions for capabilities: */ +/* can the hardware decode MPEG1 and/or MPEG2? */ +#define VIDEO_CAP_MPEG1 1 +#define VIDEO_CAP_MPEG2 2 +/* can you send a system and/or program stream to video device? + (you still have to open the video and the audio device but only + send the stream to the video device) */ +#define VIDEO_CAP_SYS 4 +#define VIDEO_CAP_PROG 8 +/* can the driver also handle SPU, NAVI and CSS encoded data? + (CSS API is not present yet) */ +#define VIDEO_CAP_SPU 16 +#define VIDEO_CAP_NAVI 32 +#define VIDEO_CAP_CSS 64 + + +#define VIDEO_STOP _IOW('o', 21, boolean) +#define VIDEO_PLAY _IO('o', 22) +#define VIDEO_FREEZE _IO('o', 23) +#define VIDEO_CONTINUE _IO('o', 24) +#define VIDEO_SELECT_SOURCE _IOW('o', 25, videoStreamSource_t) +#define VIDEO_SET_BLANK _IOW('o', 26, boolean) +#define VIDEO_GET_STATUS _IOR('o', 27, struct videoStatus *) +#define VIDEO_GET_EVENT _IOR('o', 28, struct videoEvent *) +#define VIDEO_SET_DISPLAY_FORMAT _IOW('o', 29, videoDisplayFormat_t) +#define VIDEO_STILLPICTURE _IOW('o', 30, struct videoDisplayStillPicture *) +#define VIDEO_FAST_FORWARD _IOW('o', 31, int) +#define VIDEO_SLOWMOTION _IOW('o', 32, int) +#define VIDEO_GET_CAPABILITIES _IOR('o', 33, unsigned int *) +#define VIDEO_CLEAR_BUFFER _IO('o', 34) +#define VIDEO_SET_ID _IOW('o', 35, unsigned char) +#define VIDEO_SET_STREAMTYPE _IOW('o', 36, int) +#define VIDEO_SET_FORMAT _IOW('o', 37, videoFormat_t) +#define VIDEO_SET_SYSTEM _IOW('o', 38, videoSystem_t) +#define VIDEO_SET_HIGHLIGHT _IOW('o', 39, videoHighlight_t *) +#define VIDEO_SET_SPU _IOW('o', 50, videoSPU_t *) +#define VIDEO_SET_SPU_PALETTE _IOW('o', 51, videoSPUPalette_t *) +#define VIDEO_GET_NAVI _IOR('o', 52, videoNaviPack_t *) +#define VIDEO_SET_ATTRIBUTES _IOW('o', 53, videoAttributes_t) +#endif /*_OST_VIDEO_H_*/ diff -Nru a/drivers/media/video/margi/po b/drivers/media/video/margi/po --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/po Wed Feb 13 20:03:57 2002 @@ -0,0 +1,6 @@ +make: execvp: /usr/src/LINUS/linux.18p7-ac3/scripts/pathdown.sh: Permission denied +P¼@P¼@cc -DKBUILD_BASENAME=video -c -o video.o video.c +In file included from video.h:28, + from video.c:26: +cardbase.h:40:28: pcmcia/version.h: No such file or directory +make: *** [video.o] Error 1 diff -Nru a/drivers/media/video/margi/ringbuffy.c b/drivers/media/video/margi/ringbuffy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ringbuffy.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,212 @@ +/* + ringbuffy.c + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "margi.h" +#include "ringbuffy.h" + +#ifndef outsl_ns +#define outsl_ns outsl +#endif + +int ring_init (ringbuffy *rbuf, long size) +{ + rbuf->size = 0; + rbuf->read_pos = 0; + rbuf->write_pos = 0; + + if (size > 0){ + if( !(rbuf->buffy = (char *) vmalloc(sizeof(char)*size)) ){ + MDEBUG(0, + "Not enough memory for ringbuffy\n"); + return -1; + } + } else { + MDEBUG(0, "Wrong size for ringbuffy\n"); + return -1; + } + + rbuf->size = size; + return 0; +} + + +void ring_destroy(ringbuffy *rbuf) +{ + if (rbuf->size){ + vfree(rbuf->buffy); + rbuf->buffy = NULL; + } + rbuf->size = 0; + rbuf->read_pos = 0; + rbuf->write_pos = 0; +} + + +int ring_write(ringbuffy *rbuf, const char *data, int count) +{ + + long diff, free, pos, rest; + + + if (count <=0 || !rbuf->buffy) return 0; + pos = rbuf->write_pos; + rest = rbuf->size - pos; + diff = rbuf->read_pos - pos; + free = (diff > 0) ? diff-4 : rbuf->size+diff-4; + + if ( free <= 0 ) return 0; + if ( free < count ) count = free; + + if (count >= rest){ + if(copy_from_user (rbuf->buffy+pos, data, rest)) + return -EFAULT; + if (count - rest) + if(copy_from_user(rbuf->buffy, data+rest, + count - rest)) + return -EFAULT; + rbuf->write_pos = count - rest; + } else { + copy_from_user (rbuf->buffy+pos, data, count); + rbuf->write_pos += count; + } + + return count; +} + + +int ring_writek(ringbuffy *rbuf, const char *data, int count) +{ + + long diff, free, pos, rest; + + + if (count <=0 || !rbuf->buffy) return 0; + pos = rbuf->write_pos; + rest = rbuf->size - pos; + diff = rbuf->read_pos - pos; + free = (diff > 0) ? diff-4 : rbuf->size+diff-4; + + if ( free <= 0 ) return 0; + if ( free < count ) count = free; + + if (count >= rest){ + if(memcpy(rbuf->buffy+pos, data, rest)) + return -EFAULT; + if (count - rest) + if(memcpy(rbuf->buffy, data+rest, + count - rest)) + return -EFAULT; + rbuf->write_pos = count - rest; + } else { + memcpy(rbuf->buffy+pos, data, count); + rbuf->write_pos += count; + } + + return count; +} + + + + +int ring_read(ringbuffy *rbuf, char *data, int count) +{ + + long diff, free, pos, rest; + + + if (count <=0 || !rbuf->buffy) return 0; + pos = rbuf->read_pos; + rest = rbuf->size - pos; + diff = rbuf->write_pos - pos; + free = (diff >= 0) ? diff : rbuf->size+diff; + + if ( free <= 0 ) return 0; + if ( free < count ) count = free; + + if ( count >= rest ){ + memcpy(data,rbuf->buffy+pos,rest); + if ( count - rest) + memcpy(data+rest,rbuf->buffy,count-rest); + rbuf->read_pos = count - rest; + } else { + memcpy(data,rbuf->buffy+pos,count); + rbuf->read_pos += count; + } + + return count; +} + +int ring_read_direct(ringbuffy *rbuf, int addr, int count) +{ + + long diff, free, pos, rest; + + + if (count <=0 || !rbuf->buffy) return 0; + pos = rbuf->read_pos; + rest = rbuf->size - pos; + diff = rbuf->write_pos - pos; + free = (diff >= 0) ? diff : rbuf->size+diff; + + if ( free <= 0 ) return 0; + if ( free < count ) count = free; + + if ( count >= rest ){ + outsl_ns(addr,rbuf->buffy+pos,rest/4); + if ( count - rest) + outsl_ns(addr,rbuf->buffy,(count-rest)/4); + rbuf->read_pos = count - rest; + } else { + outsl_ns(addr,rbuf->buffy+pos,count/4); + rbuf->read_pos += count; + } + + return count; +} + + +long ring_read_rest(ringbuffy *rbuf){ + long diff, free, pos; + + if (!rbuf->buffy) return 0; + pos = rbuf->read_pos; + diff = rbuf->write_pos - pos; + free = (diff >= 0) ? diff : rbuf->size+diff; + + return free; +} + +long ring_write_rest(ringbuffy *rbuf){ + long diff, free, pos; + + if (!rbuf->buffy) return 0; + pos = rbuf->write_pos; + diff = rbuf->read_pos - pos; + free = (diff > 0) ? diff-4 : rbuf->size+diff-4; + + return free; +} + +void ring_flush(ringbuffy *rbuf){ + rbuf->read_pos = 0; + rbuf->write_pos = 0; +} diff -Nru a/drivers/media/video/margi/ringbuffy.h b/drivers/media/video/margi/ringbuffy.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/ringbuffy.h Wed Feb 13 20:03:57 2002 @@ -0,0 +1,43 @@ +/* + cvdv.h + + Copyright (C) Marcus Metzler for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef RINGBUFFY_H +#define RINGBUFFY_H + + +#define FULL_BUFFER -1000 +typedef struct ringbuffy{ + long read_pos; + long write_pos; + long size; + char *buffy; +} ringbuffy; + +int ring_init (ringbuffy *rbuf, long size); +void ring_destroy(ringbuffy *rbuf); +int ring_write(ringbuffy *rbuf, const char *data, int count); +int ring_writek(ringbuffy *rbuf, const char *data, int count); +int ring_read(ringbuffy *rbuf, char *data, int count); +long ring_read_rest(ringbuffy *rbuf); +long ring_write_rest(ringbuffy *rbuf); +void ring_flush(ringbuffy *rbuf); +int ring_read_direct(ringbuffy *rbuf, int addr, int count); + +#endif /* RINGBUFFY_H */ diff -Nru a/drivers/media/video/margi/spu.c b/drivers/media/video/margi/spu.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/spu.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,103 @@ +/* + spu.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "spu.h" +#include "l64021.h" + +int DecoderHighlight(struct cvdv_cards *card, int active, u8 * coli, + u8 * btn_posi) +{ + int i; + if ((coli == NULL) || (btn_posi == NULL)) + return 1; + MDEBUG(0,": -- DecoderHighlight: col 0x%02X%02X, contr 0x%02X%02X, act %d, %d,%d - %d,%d\n", + coli[0], coli[1], coli[2], coli[3], active, + (((int) btn_posi[0] & 0x3F) << 4) | (btn_posi[1] >> 4), + (((int) btn_posi[3] & 0x3F) << 4) | (btn_posi[4] >> 4), + (((int) btn_posi[1] & 0x03) << 8) | btn_posi[2], + (((int) btn_posi[4] & 0x03) << 8) | btn_posi[5]); + //for (i=0; i<4; i++) DecoderWriteByte(card,0x1C0+i,coli[i]); +// DecoderWriteByte(card,0x1C0,coli[1]); +// DecoderWriteByte(card,0x1C1,coli[0]); +// DecoderWriteByte(card,0x1C2,coli[3]); +// DecoderWriteByte(card,0x1C3,coli[2]); + //for (i=0; i<6; i++) DecoderWriteByte(card,0x1C4+i,btn_posi[i]); +// for (i=0; i<6; i++) DecoderWriteByte(card,0x1C4+i,btn_posi[5-i]); + //if (active) DecoderSetByte(card,0x1BF,0x01); + //else DecoderDelByte(card,0x1BF,0x01); + + //for (i=0; i<4; i++) card->highlight[i]=coli[3-i]; + card->highlight[0] = coli[1]; + card->highlight[1] = coli[0]; + card->highlight[2] = coli[3]; + card->highlight[3] = coli[2]; + for (i = 0; i < 6; i++) + card->highlight[4 + i] = btn_posi[5 - i]; + card->highlight_valid = 1; + if (active) + DecoderWriteByte(card, 0x1BF, 0x01); + else + DecoderWriteByte(card, 0x1BF, 0x00); +//DecoderSetByte(card,0x135,0x02); // Enable SPU Mix +//DecoderWriteByte(card,0x1A0,0x01); // decode start, display on + return 0; +} + +int DecoderSPUPalette(struct cvdv_cards *card, int length, u8 * palette) +{ + int i; + MDEBUG(1,": -- DecoderSPUPalette: setting up %d bytes of SPU palette(Y,Cr,Cb):", length); + for (i = 0; i < (length / 3); i++) + MDEBUG(1," %d=(%d,%d,%d)", i, palette[i * 3],palette[i * 3 + 1], + palette[i * 3 + 2]); + MDEBUG(1,"\n"); + DecoderDelByte(card, 0x1A0, 0x01); // SPU decode stop + DecoderSetByte(card, 0x1A0, 0x10); + for (i = 0; i < length; i++) + DecoderWriteByte(card, 0x1BE, palette[i]); + DecoderSetByte(card, 0x1A0, 0x01); // SPU decode start + return 0; +} + +int DecoderSPUStream(struct cvdv_cards *card, int stream, int active) +{ + MDEBUG(1,": -- DecoderSPUStream: stream %d, active %d\n", stream, + active); + if (stream < 32) { + card->reg092 |= (0x20 | (stream & 0x1F)); // stream ID and select + DecoderWriteByte(card, 0x092, card->reg092); + DecoderMaskByte(card, 0x112, 0x20, 0x20); // chroma filter enable + DecoderMaskByte(card, 0x1A1, 0x0F, 0x00); // SPU timeout + DecoderWriteByte(card, 0x1BF, 0x00); // HighLight off + DecoderSetByte(card, 0x135, 0x02); // Enable SPU Mix + if (active) + DecoderWriteByte(card, 0x1A0, 0x01); // decode start, display on + else + DecoderWriteByte(card, 0x1A0, 0x05); // decode start, display off + } else { + DecoderWriteByte(card, 0x1A0, 0x04); // decode stop, display off + card->reg092 &= (~0x20); // stream select off + DecoderWriteByte(card, 0x092, card->reg092); + DecoderDelByte(card, 0x135, 0x02); // Disable SPU Mix + } + return 0; +} diff -Nru a/drivers/media/video/margi/spu.h b/drivers/media/video/margi/spu.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/spu.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,33 @@ +/* + spu.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CVDV_SPU_H +#define CVDV_SPU_H + +#include "cardbase.h" + +int DecoderHighlight(struct cvdv_cards *card, int active, u8 * coli, + u8 * btn_posi); + +int DecoderSPUPalette(struct cvdv_cards *card, int length, u8 * palette); + +int DecoderSPUStream(struct cvdv_cards *card, int stream, int active); + +#endif /* CVDV_SPU_H */ diff -Nru a/drivers/media/video/margi/streams.c b/drivers/media/video/margi/streams.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/streams.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,444 @@ +/* + streams.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define __NO_VERSION__ + +#include "streams.h" +#include "dram.h" +#include "l64021.h" +#include "video.h" +#include "audio.h" + +// Frees allocated channel buffers +int DecoderKillChannelBuffers(struct cvdv_cards *card) +{ + MDEBUG(1, ": -- DecoderKillChannelBuffers\n"); + DecoderStopDecode(card); + DRAMFree(card, card->VideoES); + card->VideoES = BLANK; + DRAMFree(card, card->AudioES); + card->AudioES = BLANK; + DRAMFree(card, card->VideoPES); + card->VideoPES = BLANK; + DRAMFree(card, card->DataDump); + card->DataDump = BLANK; + DRAMFree(card, card->AudioPES); + card->AudioPES = BLANK; + DRAMFree(card, card->NaviBank); + card->NaviBank = BLANK; + card->ChannelBuffersAllocated = 0; +// DecoderWriteWord( + return 0; +} + +// Allocates channel buffers +// All sizes in bytes, preferably multiple of 256 (will be rounded up otherwise) +int DecoderSetChannelBuffers(struct cvdv_cards *card, int VideoES, // Video ES Channel Buffer size, e.g. 229376 byte for NTSC + int AudioES, // Audio ES Channel Buffer size, 4096 byte + int VideoPES, // Video PES Header / SPU Channel Buffer size, 512 byte + int DataDump, // Data Dump Channel Buffer size, e.g. 80896 byte + int AudioPES, // Audio PES Header / System Channel Buffer size, 512 byte + int NaviBank) +{ // Navi Bank Channel Buffer size, 2048 byte +#define BUFFERSET(buf, id, adr,align) if (buf>0) {\ + if (buf&((1<buf=addr;\ + addr>>=align;\ + DecoderWriteByte(card,adr,addr&0xFF);\ + DecoderWriteByte(card,adr+1,(addr>>8)&(0x003F));\ + addr+=(buf>>align);\ + DecoderWriteByte(card,adr+2,(addr-1)&0xFF);\ + DecoderWriteByte(card,adr+3,((addr-1)>>8)&0x003F);\ +} + u32 addr; + MDEBUG(1, ": -- DecoderSetChannelBuffers\n"); + //DecoderStopDecode(card); + DecoderStopChannel(card); + VideoES >>= 1; // change to word sizes + AudioES >>= 1; + VideoPES >>= 1; + DataDump >>= 1; + AudioPES >>= 1; + NaviBank >>= 1; + if (card->ChannelBuffersAllocated) + DecoderKillChannelBuffers(card); + BUFFERSET(VideoES, "VideoES", 0x048, 7); + BUFFERSET(AudioES, "AudioES", 0x04C, 7); + BUFFERSET(VideoPES, "VideoPES", 0x050, 7); + BUFFERSET(DataDump, "DataDump", 0x054, 7); + BUFFERSET(AudioPES, "AudioPES", 0x058, 7); + BUFFERSET(NaviBank, "NaviBank", 0x05C, 7); + + card->VideoESSize = VideoES; + card->AudioESSize = AudioES; + card->VideoPESSize = VideoPES; + card->DataDumpSize = DataDump; + card->AudioPESSize = AudioPES; + card->NaviBankSize = NaviBank; + + DecoderWriteByte(card, 0x044, 0x7F); + DecoderWriteByte(card, 0x044, 0x01); + if (NaviBank) { + card->reg07B |= 0x10; // navi pack counter enable + DecoderWriteByte(card, 0x07B, card->reg07B); + //DecoderSetByte(card,0x07B,0x10); // navi pack counter enable + card->NaviPackAddress = + (DecoderReadWord(card, 0x05C) & 0x3FFF) << 7; + MDEBUG(4, ": navi bank init'ed: 0x%08X\n",card->NaviPackAddress); + } else { + card->reg07B &= ~0x10; // navi pack counter disable + DecoderWriteByte(card, 0x07B, card->reg07B); + //DecoderDelByte(card,0x07B,0x10); // navi pack counter disable + card->NaviPackAddress = 0; + } + card->ChannelBuffersAllocated = 1; +#undef BUFFERSET + return 0; +} + +//int DecoderReadFifo + +int DecoderUnPrepare(struct cvdv_cards *card) +{ + MDEBUG(0, ": -- DecoderUnPrepare\n"); + //DecoderStopDecode(card); + DecoderStopChannel(card); + DecoderKillChannelBuffers(card); + return 0; +} + +void DecoderPrepare(struct cvdv_cards *card) +{ + //VideoSetBackground(card,0,0,0,0); // Video on black + VideoSetBackground(card, 1, 0, 0, 0); // black + //VideoSetBackground(card,2,83,90,249); // Red + //VideoSetBackground(card,2,155,53,53); // Green + //VideoSetBackground(card,2,35,212,114); // Blue + //VideoSetBackground(card,2,4,128,128); // Black + //VideoSetBackground(card,3,155,53,53); // Video on Green + + //DecoderWriteByte(card,0x044,0x00); // Reset channel buffers on error +// DecoderWriteByte(card,0x044,0x01); // don't Reset channel buffers on error + + DecoderWriteByte(card, 0x040, 0x01); // Reset Aux FIFO + DecoderWriteByte(card, 0x041, 0x01); // Reset Data FIFO + //DecoderWriteByte(card,0x044,0x7E); // Reset channel buffers, Reset channel buffers on error + DecoderWriteByte(card, 0x044, 0x7F); // Reset channel buffers, don't Reset channel buffers on error +// udelay(100); +// DecoderWriteByte(card,0x040,0x00); // Reset Aux FIFO +// DecoderWriteByte(card,0x041,0x00); // Reset Data FIFO +// DecoderDelByte(card,0x044,0x7E); // Reset channel buffers +} + +// Selects audio type MPEG and sets stream ID's +// AID: -1=all MPEG, Audio Stream ID: 0..31 +// AExt: -1=unused, Audio Stream Extension ID: 0..31, only used if AType=5 +void DecoderSelectAudioID(struct cvdv_cards *card) +{ + int AID = card->setup.audioID; + int AExt = card->setup.audioIDext; + MDEBUG(1, ": -- SelectAudio %d %d\n", AID, AExt); + DecoderWriteByte(card, 0x07C, AExt & 0x1F); // Audio Stream Extension ID + card->reg08F = (card->reg08F & ~0x1F) | (AID & 0x1F); + DecoderWriteByte(card, 0x08F, card->reg08F); + //DecoderMaskByte(card,0x08F,0x1F,AID&0x1F); // Set Stream ID +} + +// AHeader: 0=No Headers, 1=first PTS/DTS header, 2=all headers, 3=All with PTS/DTS +// AType: 0=disable audio, 1=MPEG ID (MPEG 1), 2=Lin.PCM ID, 3=AC3 ID, 4=all MPEG (use only, if just one MPEG audio stream), 5=MPEG multichannel ID (MPEG 2) +// AID: -1=all MPEG, Audio Stream ID: 0..31 +// AExt: -1=unused, Audio Stream Extension ID: 0..31, only used if AType=5 +// IEC956: 0:MPEG/AC3 data on digital out 1:IEC956 data on digital S/PDIF out +void DecoderPrepareAudio(struct cvdv_cards *card) +{ + int AHeader = 2; + int AType = 3; + int AID = card->setup.audioID; + int AExt = card->setup.audioIDext; + int IEC956 = card->setup.SPDIFmode; + MDEBUG(1, ": -- PrepAudio %d %d %d %d %d\n", + AHeader, card->setup.audioselect, AID, AExt, IEC956); + switch (card->setup.audioselect) { + case audio_disable: + case audio_none: + case audio_SDDS: + AType = 0; + break; + case audio_MPEG: // MPEG Audio + AType = 1; + break; + case audio_MPEG_EXT: // MPEG Audio with extension stream + AType = 5; + break; + case audio_LPCM: // Linear Pulse Code Modulation LPCM + AType = 2; + break; + case audio_AC3: // AC-3 + AType = 3; + break; + case audio_DTS: // DTS + AType = 8; + break; + } + if (AType <= 0) { + card->reg08F = 0x00; // disable audio and discard all packets + DecoderWriteByte(card, 0x08F, card->reg08F); + //DecoderWriteByte(card,0x08F,0x00); // disable audio and discard all packets + //DecoderMaskByte(card,0x093,0xC3,0xC0); // write no headers + card->reg093 = (card->reg093 & ~0x03); // write no headers + DecoderWriteByte(card, 0x093, card->reg093); + } else { + AudioOpen(card); + DecoderMaskByte(card, 0x165, 0x1F, 0x00); // reset the register + if (AType == 8) { // DTS + card->reg090 |= 0x01; // DTS in Transport Private 1 Stream stored in AudioES channel buffer + DecoderWriteByte(card, 0x090, card->reg090); + //DecoderSetByte(card,0x090,0x01); // DTS in Transport Private 1 Stream stored in AudioES channel buffer + AudioSetMode(card, 0); + DecoderSetByte(card, 0x165, 0x01); + AudioStartFormat(card); + } else if (AType == 3) { // AC3 + card->reg090 |= 0x01; // AC3 in Transport Private 1 Stream stored in AudioES channel buffer + DecoderWriteByte(card, 0x090, card->reg090); + //DecoderSetByte(card,0x090,0x01); // AC3 in Transport Private 1 Stream stored in AudioES channel buffer + AudioSetMode(card, ((IEC956) ? 1 : 3)); + } else if (AType == 2) { // PCM + card->reg090 |= 0x01; // PCM in Transport Private 1 Stream stored in AudioES channel buffer + DecoderWriteByte(card, 0x090, card->reg090); + //DecoderSetByte(card,0x090,0x01); // PCM in Transport Private 1 Stream stored in AudioES channel buffer + AudioSetMode(card, 4); + } else { // MPEG + card->reg090 &= ~0x01; // MPEG Audio stored in AudioES channel buffer + DecoderWriteByte(card, 0x090, card->reg090); + //DecoderDelByte(card,0x090,0x01); // MPEG Audio stored in AudioES channel buffer + if (AID < 0) + AType = 4; + if (AExt >= 0) + AType = 5; + else + AExt = -1; + AudioSetMode(card, ((IEC956) ? 0 : 2)); + } + card->setup.audioID = AID; + card->setup.audioIDext = AExt; + DecoderSelectAudioID(card); + card->reg08F = (card->reg08F & ~0xE0) | ((AType & 0x07) << 5); // Set Stream Type + DecoderWriteByte(card, 0x08F, card->reg08F); + //DecoderMaskByte(card,0x08F,0xE0,(AType&0x07)<<5); // Set Stream Type + AudioSetVolume(card, 0xFF); // Set PCM scale to full volume + //DecoderMaskByte(card,0x093,0xC3,(AHeader&0x03)|0xC0); // write header select + card->reg093 = (card->reg093 & ~0x03) | (AHeader & 0x03); // write header select + DecoderWriteByte(card, 0x093, card->reg093); + // Mute the card and put it in play mode, then wait for the parameters to be parsed and un-mute if successful + //AudioMute(card,1); + if (AType > 0) { + AudioStartDecode(card); + //AudioSetPlayMode(card,MAUDIO_PLAY); + AudioSetPlayMode(card, MAUDIO_PAUSE); + } + //card->startingA=1; + } + card->lastaattr = 0; +} + +// VHeader: -1=disable Video, 0=No Headers, 1=first PTS/DTS header, 2=all headers, 3=All with PTS/DTS +// VID: -1=all MPEG, 0..15=Video Stream ID +void DecoderPrepareVideo(struct cvdv_cards *card) +{ + int VHeader = 3; + int VID = card->setup.videoID; + if (VHeader < 0) { + card->reg091 = 0x00; + DecoderWriteByte(card, 0x091, card->reg091); + //DecoderWriteByte(card,0x091,0x00); + } else { + if (VID < 0) { + card->reg091 = ((VHeader & 0x03) << 6) | (2 << 4); + DecoderWriteByte(card, 0x091, card->reg091); + //DecoderWriteByte(card,0x091,((VHeader&0x03)<<6)|(2<<4)); + } else { + card->reg091 = + ((VHeader & 0x03) << 6) | (1 << 4) | (VID & + 0x0F); + DecoderWriteByte(card, 0x091, card->reg091); + //DecoderWriteByte(card,0x091,((VHeader&0x03)<<6)|(1<<4)|(VID&0x0F)); + } + } +} + +// Prepare Decoder for Elementary Streams, Disable Preparser +int DecoderPrepareES(struct cvdv_cards *card) +{ + int i; + MDEBUG(1, ": -- PrepareES\n"); + //DecoderStopDecode(card); + +// DecoderWriteByte(card,0x05,0x00); + + DecoderMaskByte(card, 0x007, 0xCE, 0xC2 | (3 << 2)); // Stream Select: A/V Elementary Stream + MDEBUG(3, ": Int - A VideoES w/r addr: %08X %08X\n", + (DecoderReadByte(card,0x060)|(DecoderReadByte(card,0x061)<<8)| + (DecoderReadByte(card,0x062)<<16))<<2, + (DecoderReadByte(card,0x06C)|(DecoderReadByte(card,0x06D)<<8)| + (DecoderReadByte(card,0x06E)<<16))<<2); + // set the decoding buffers + card->reg093 = (card->reg093 & ~0xFC); // write no header + DecoderWriteByte(card, 0x093, card->reg093); + if ((i = DecoderSetChannelBuffers(card, 256000, 4096, 0, 0, 0, 0))) { + MDEBUG(0, ": SetDecoderBuffers failed for buffer at 0x%03X\n", i); + DecoderKillChannelBuffers(card); + return 1; + } + MDEBUG(3, ": Int - B VideoES w/r addr: %08X %08X\n", + (DecoderReadByte(card,0x060)|(DecoderReadByte(card,0x061)<<8)| + (DecoderReadByte(card,0x062)<<16))<<2, + (DecoderReadByte(card,0x06C)|(DecoderReadByte(card,0x06D)<<8)| + (DecoderReadByte(card,0x06E)<<16))<<2); + + MDEBUG(3, ": Int - C VideoES w/r addr: %08X %08X\n", + (DecoderReadByte(card,0x060)|(DecoderReadByte(card,0x061)<<8)| + (DecoderReadByte(card,0x062)<<16))<<2, + (DecoderReadByte(card,0x06C)|(DecoderReadByte(card,0x06D)<<8)| + (DecoderReadByte(card,0x06E)<<16))<<2); + +// DecoderStartChannel(card); +// DecoderStartDecode(card); + + MDEBUG(3, ": Int - D VideoES w/r addr: %08X %08X\n", + (DecoderReadByte(card,0x060)|(DecoderReadByte(card,0x061)<<8)| + (DecoderReadByte(card,0x062)<<16))<<2, + (DecoderReadByte(card,0x06C)|(DecoderReadByte(card,0x06D)<<8)| + (DecoderReadByte(card,0x06E)<<16))<<2); + + DecoderPrepare(card); + + return 0; +} + +// Prepare Decoder for Packetised Elementary Streams, set parameters of Preparser +int DecoderPreparePES(struct cvdv_cards *card) +{ + + // SPUID: -1=No SPU, 0..31=Display SPU of this ID + // DataDump: 0=disable DataDump, 1=process DataDump Substreams + // PackHeader: 0=write no headers, 1=write one header, 2=write all headers + // SysHeader: 0=write no headers, 1=write one header, 2=write all headers + // DSIHeader: 0=write no headers, 3=write PCI and DSI headers and packets + int i; + int SPUID = -1; + int DataDump = 0; + int PackHeader = 0; + int SysHeader = 0; + int DSIHeader = 0; + + MDEBUG(1, ": -- PreparePES\n"); + DecoderMaskByte(card, 0x007, 0xCE, 0xC2 | (0 << 2)); // Stream Select: A/V PES Packets + + if (SPUID < 0) + card->reg092 = 0; // Do we use SPU? + else + card->reg092 = 0x20 | (SPUID & 0x1F); + if (DataDump) + card->reg092 |= 0x40; // Do we use DataDump? + DecoderWriteByte(card, 0x092, card->reg092); + //DecoderMaskByte(card,0x093,0xFC,((DSIHeader&0x03)<<6)|((PackHeader&0x03)<<4)|((SysHeader&0x03)<<2)); + card->reg093 = + (card->reg093 & ~0xFC) | (((DSIHeader & 0x03) << 6) | + ((PackHeader & 0x03) << 4) | + ((SysHeader & 0x03) << 2)); + DecoderWriteByte(card, 0x093, card->reg093); + // set the decoding buffers + if ( + (i = + DecoderSetChannelBuffers(card, 256000, 4096, 512, 0, 512, + 0))) { + MDEBUG(0,": SetDecoderBuffers failed for buffer at 0x%03X\n", i); + DecoderKillChannelBuffers(card); + return 1; + } + + DecoderPrepare(card); + + return 0; +} + + +// Prepare Decoder for MPEG 1 Systems Streams or MPEG 2 Program Streams +// SPUID: -1:ignore, 0...15 SPU Substream ID +// DataDump: 0:disable data dump stream, 1:enable data dump stream +// PackHeader: 0:write no headers, 1:write one header, 2:write all headers, 3:always discard +// SysHeader: 0:always discard, 1:write one header, 2:write all headers, 3:always discard +// DSIHeader: 0:write no DSI or PCI headers, 3:write DSI and PCI headers + packets +// DVD: 0: normal MPEG-2 data, 1: DVD stream with navi pack data +int DecoderPreparePS(struct cvdv_cards *card, + int SPUID, int DataDump, + int PackHeader, int SysHeader, int DSIHeader, int DVD) +{ + int i=0; + MDEBUG(1, ": -- PreparePS %s\n", ((DVD) ? "DVD" : "")); + //DecoderStopDecode(card); + DecoderMaskByte(card, 0x007, 0xCE, 0xC2 | (1 << 2)); // Stream Select: MPEG1 System / MPEG2 Program Stream + + if (SPUID < 0) + card->reg092 = 0; // Do we use SPU? + else + card->reg092 = 0x20 | (SPUID & 0x1F); + if (DataDump) + card->reg092 |= 0x40; // Do we use DataDump? + DecoderWriteByte(card, 0x092, card->reg092); + //DecoderMaskByte(card,0x093,0xFC,((DSIHeader&0x03)<<6)|((PackHeader&0x03)<<4)|((SysHeader&0x03)<<2)); + card->reg093 = + (card->reg093 & ~0xFC) | (((DSIHeader & 0x03) << 6) | + ((PackHeader & 0x03) << 4) | + ((SysHeader & 0x03) << 2)); + DecoderWriteByte(card, 0x093, card->reg093); + // set the decoding buffers + if (DVD) { // do we need SPU-, navi- and datadump-buffers? + + // if(card->videomode == NTSC) + i = DecoderSetChannelBuffers(card, 340000, 32768, 32768, 0, + 512,4096) ; + //else + // i = DecoderSetChannelBuffers(card, 291878, 16384, 512, 0, + // 512,0) ; + + if (i) { + MDEBUG(0,": SetDecoderBuffers failed for buffer at 0x%03X\n", i); + DecoderKillChannelBuffers(card); + return 1; + } + + } else { // normal PS + if ( + (i = + DecoderSetChannelBuffers(card, 340000, 32768, 512, + 0, 512, 0))) { + MDEBUG(0,": SetDecoderBuffers failed for buffer at 0x%03X\n", i); + DecoderKillChannelBuffers(card); + return 1; + } + } + + DecoderPrepare(card); + + return 0; +} diff -Nru a/drivers/media/video/margi/streams.h b/drivers/media/video/margi/streams.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/streams.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,79 @@ +/* + streams.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CVDV_STREAMS_H +#define CVDV_STREAMS_H + +#include "cardbase.h" + +// Frees allocated channel buffers +int DecoderKillChannelBuffers(struct cvdv_cards *card); + +// Allocates channel buffers +// All sizes in bytes, preferably multiple of 256 (will be rounded up otherwise) +int DecoderSetChannelBuffers(struct cvdv_cards *card, int VideoES, // Video ES Channel Buffer size, e.g. 229376 byte for NTSC + int AudioES, // Audio ES Channel Buffer size, 4096 byte + int VideoPES, // Video PES Header / SPU Channel Buffer size, 512 byte + int DataDump, // Data Dump Channel Buffer size, e.g. 80896 byte + int AudioPES, // Audio PES Header / System Channel Buffer size, 512 byte + int NaviBank); // Navi Bank Channel Buffer size, 2048 byte + +//int DecoderReadFifo + +int DecoderUnPrepare(struct cvdv_cards *card); + +void DecoderPrepare(struct cvdv_cards *card); + +// Selects audio type MPEG and sets stream ID's +// AID: -1=all MPEG, Audio Stream ID: 0..31 +// AExt: -1=unused, Audio Stream Extension ID: 0..31, only used if AType=5 +void DecoderSelectAudioID(struct cvdv_cards *card); + +// AHeader: 0=No Headers, 1=first PTS/DTS header, 2=all headers, 3=All with PTS/DTS +// AType: 0=disable audio, 1=MPEG ID (MPEG 1), 2=Lin.PCM ID, 3=AC3 ID, 4=all MPEG (use only, if just one MPEG audio stream), 5=MPEG multichannel ID (MPEG 2) +// AID: -1=all MPEG, Audio Stream ID: 0..31 +// AExt: -1=unused, Audio Stream Extension ID: 0..31, only used if AType=5 +// IEC956: 0:MPEG/AC3 data on digital out 1:IEC956 data on digital S/PDIF out +void DecoderPrepareAudio(struct cvdv_cards *card); + +// VHeader: -1=disable Video, 0=No Headers, 1=first PTS/DTS header, 2=all headers, 3=All with PTS/DTS +// VID: -1=all MPEG, 0..15=Video Stream ID +void DecoderPrepareVideo(struct cvdv_cards *card); + +// Prepare Decoder for Elementary Streams, Disable Preparser +int DecoderPrepareES(struct cvdv_cards *card); + +// Prepare Decoder for Packetised Elementary Streams, set parameters of Preparser +int DecoderPreparePES(struct cvdv_cards *card); + + +// Prepare Decoder for MPEG 1 Systems Streams or MPEG 2 Program Streams +// SPUID: -1:ignore, 0...15 SPU Substream ID +// DataDump: 0:disable data dump stream, 1:enable data dump stream +// PackHeader: 0:write no headers, 1:write one header, 2:write all headers, 3:always discard +// SysHeaader: 0:always discard, 1:write one header, 2:write all headers, 3:always discard +// DSIHeader: 0:write no DSI or PCI headers, 3:write DSI and PCI headers + packets +// DVD: 0: normal MPEG-2 data, 1: DVD stream with navi pack data +int DecoderPreparePS(struct cvdv_cards *card, + int SPUID, int DataDump, + int PackHeader, int SysHeader, int DSIHeader, + int DVD); + +#endif /* CVDV_STREAMS_H */ diff -Nru a/drivers/media/video/margi/video.c b/drivers/media/video/margi/video.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/video.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,525 @@ +/* + video.c + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +// +// Video Decoder +// +#define __NO_VERSION__ + +#include "video.h" +#include "l64021.h" +#include "dram.h" + +// Set the background of the OSD and SPU and it's color +// mode=0: Video on Black +// mode=1: Black +// mode=2: Selected Color +// mode=3: Video on Selected Color +void VideoSetBackground(struct cvdv_cards *card, int mode, u8 Y, u8 Cb, + u8 Cr) +{ + DecoderWriteByte(card, 0x10A, Y); + DecoderWriteByte(card, 0x10B, Cb); + DecoderWriteByte(card, 0x10C, Cr); + DecoderMaskByte(card, 0x109, 0xC0, mode << 6); +} + + +int DecoderStartDecode(struct cvdv_cards *card) +{ + DecoderSetByte(card, 0x0F6, 0x01); +#ifdef DVB + if (card->audiostate.AVSyncState) +#endif + card->videosync = 1; + return 0; +} + +int DecoderStopDecode(struct cvdv_cards *card) +{ + DecoderDelByte(card, 0x0F6, 0x01); + card->videosync = 0; + return 0; +} + +// Sets Display Override (Still Image Display) to Frame Buffer at specified addresses, +// addresses are 16 bit, in 64 byte resolution +// mode: 0=off, 1=Frame, 2=Field +// width: width of the still picture in 8 pixel units +int DecoderStillImageDisplay(struct cvdv_cards *card, int mode, int width, + u16 LumaAddr, u16 ChromaAddr) +{ + DecoderStopDecode(card); + DecoderWriteWord(card, 0x11D, LumaAddr); + DecoderWriteWord(card, 0x11F, ChromaAddr); + DecoderWriteByte(card, 0x11B, width & 0x7F); + DecoderMaskByte(card, 0x109, 0x30, (mode & 3) << 4); // Display Override Mode + return 0; +} + +// Frees allocated frame buffers +int DecoderKillFrameBuffers(struct cvdv_cards *card) +{ + MDEBUG(1, ": -- DecoderKillFrameBuffers\n"); + DecoderStopDecode(card); + DRAMFree(card, card->FrameStoreLuma1); + card->FrameStoreLuma1 = BLANK; + DRAMFree(card, card->FrameStoreChroma1); + card->FrameStoreChroma1 = BLANK; + DRAMFree(card, card->FrameStoreLuma2); + card->FrameStoreLuma2 = BLANK; + DRAMFree(card, card->FrameStoreChroma2); + card->FrameStoreChroma2 = BLANK; + DRAMFree(card, card->FrameStoreLumaB); + card->FrameStoreLumaB = BLANK; + DRAMFree(card, card->FrameStoreChromaB); + card->FrameStoreChromaB = BLANK; + card->FrameBuffersAllocated = 0; +// DecoderWriteWord( + return 0; +} + +int DecoderSetFrameBuffers(struct cvdv_cards *card, int lines, // number of lines of the decoded MPEG + int TwoFrames, // 1 if no B-Frames are present in the video stream, thus allowing only 2 framestores + int RMM) // 1 if RMM +{ +#define SEGMENTS 44 // 40..54 for PAL, 44 recommended +#define BUFFERSET(buf,adr,align) if (buf>0) {\ + if (buf&((1<buf=addr;\ + addr>>=align;\ + DecoderWriteByte(card,adr,addr&0xFF);\ + DecoderWriteByte(card,adr+1,(addr>>8)&(0x00FF));\ +} + u32 addr; + int pixel, byteperline; // visible pixel per video line, same for PAL and NTSC + int FrameStoreLuma1, FrameStoreChroma1, + FrameStoreLuma2, FrameStoreChroma2, + FrameStoreLumaB, FrameStoreChromaB; + MDEBUG(1, ": -- DecoderSetFrameBuffers\n"); + DecoderStopDecode(card); + //DecoderStopChannel(card); + //lines=((CCIR601Lines(card->videomode)==625)?576:480); + byteperline = (DecoderReadByte(card, 0x116) & 0x7F) * 8; // main 64-bit reads per line + pixel = byteperline * lines; + FrameStoreLuma1 = FrameStoreLuma2 = FrameStoreLumaB = pixel >> 1; // 8 bit luma per pixel in words + FrameStoreChroma1 = FrameStoreChroma2 = FrameStoreChromaB = + pixel >> 2; // 8+8 bit chroma every 2nd pixel every 2nd line + if (card->FrameBuffersAllocated) + DecoderKillFrameBuffers(card); + BUFFERSET(FrameStoreLuma1, 0x0E0, 5); // Anchor Frame Store 1 + BUFFERSET(FrameStoreChroma1, 0x0E2, 5); + BUFFERSET(FrameStoreLuma2, 0x0E4, 5); // Anchor Frame Store 2 + BUFFERSET(FrameStoreChroma2, 0x0E6, 5); + if (TwoFrames) { + DecoderDelByte(card, 0x0F8, 0x01); + } else { +// if (CCIR601Lines(card->videomode)==525) { // Normal Mode, NTSC + if (!RMM) { // Normal Mode, NTSC + BUFFERSET(FrameStoreLumaB, 0x0E8, 5); // B Frame Store + BUFFERSET(FrameStoreChromaB, 0x0EA, 5); + DecoderDelByte(card, 0x0F8, 0x01); + } else { // Reduced Memory Mode, PAL + // 44 segments with 8 lines each (8 bit luma + 4 bit chroma) + // only display modes 4-8, 10, and 11 are allowed + FrameStoreLumaB = + (8 * byteperline * SEGMENTS) >> 1; + FrameStoreChromaB = + (4 * byteperline * SEGMENTS) >> 1; + BUFFERSET(FrameStoreLumaB, 0x0E8, 5); // B Frame Store + BUFFERSET(FrameStoreChromaB, 0x0EA, 5); + DecoderWriteByte(card, 0x121, SEGMENTS << 1); // Number of segments + DecoderSetByte(card, 0x0F8, 0x01); + } + } + card->FrameBuffersAllocated = 1; +#undef SEGMENTS +#undef BUFFERSET + return 0; +} + +// returns size of the Video ES Buffer in bytes or 0=error +u32 DecoderGetVideoESSize(struct cvdv_cards * card) +{ + if (!card->ChannelBuffersAllocated) + return 0; // buffer not initialised + return (u32) ((DecoderReadWord(card, 0x04A) & 0x3FFF) - + (DecoderReadWord(card, 0x048) & 0x3FFF)) * 256; // bytes +} + +// returns level of fullness in bytes +u32 DecoderGetVideoESLevel(struct cvdv_cards * card) +{ + u32 items; + items = DecoderReadByte(card, 0x086); + items |= ((DecoderReadWord(card, 0x087) & 0x07FF) << 8); + items *= 8; // 64 bit per item + return items; +} + +// pics=0 --> items=bytes +// pics=1 --> items=pictures +void DecoderSetVideoPanic(struct cvdv_cards *card, int pics, int items) +{ + if (pics < 0) { + DecoderMaskByte(card, 0x045, 0x18, 0x00 << 3); // disable panic mode + } else { + if (pics) { + DecoderWriteMWord(card, 0x086, items & 0x0003FFFF); + DecoderMaskByte(card, 0x045, 0x18, 0x02 << 3); // set panic mode to "number of pictures" in VideoES + } else { + DecoderWriteMWord(card, 0x086, + (items / 8) & 0x0003FFFF); + DecoderMaskByte(card, 0x045, 0x18, 0x01 << 3); // set panic mode to "number of 8-byte-frames" in VideoES + } + } +} + +int DecoderClose(struct cvdv_cards *card) +{ + if (card->DecoderOpen) { + MDEBUG(1, ": -- DecoderClose\n"); + DecoderStopDecode(card); + DecoderKillFrameBuffers(card); + card->DecoderOpen = 0; + card->lastvattr = 0; + return 0; + } else + return 1; +} + +// returns 0 on success, 1 on "picture size too big", 2 on "out of DRAM memory" +int DecoderOpen(struct cvdv_cards *card, int x, int y, // size of the decoded MPEG picture + int aspect, // pixel or picture aspect ratio of the MPEG picture: 1=square pixel 2=3:4 3=9:16 4=1:2.21 + int Field, // 0:Frame (interlaced, MPEG-2) , 1:Field (non-interlaced, MPEG-1) structure + int Letterbox, // 0:PanScan (4:3), 1:letterbox (16:9, 8:3) picture ratio + int RMM) // 1:use ReducedMemoryMode +{ + int mode, // Display Mode + i, factor, // zoom factor + top, bottom, left, right, width, height, newwidth, newheight, // screen size + vaspx, vaspy, // output video pixel aspect ratio + paspx, paspy, // input picture pixel aspect ratio + SIF; // 0:Full (480/576 lines, MPEG-2), 1:SIF (half, 240/288 lines, MPEG-1) resolution + + MDEBUG(1, ": -- DecoderOpen x:%d y:%d asp:%d field:%d lt:%d rmm:%d\n", + x, y, aspect, Field, Letterbox, RMM); + if ((x <= 0) || (y <= 0)) + return 4; // picture too small +//if (card->DecoderOpen) return 3; + DecoderStopDecode(card); + DecoderClose(card); // closes only, if already open + vaspy = 11; + vaspx = ((CCIR601Lines(card->videomode) == 525) ? 10 : 12); // screen pixel aspect ratio + // note: this aspect ratio applies to 704 pixel width, but the card's default is 720, wich is not 3:4 picture aspect ratio anymore!? + i = ((x == 720) ? 704 : x); // 720 wide is overscan of 704 wide + switch (aspect) { // MPEG data pixel aspect ratio + case 1: + paspx = 1; + paspy = 1; + break; + default: + case 2: + paspx = 4 * y; + paspy = 3 * i; + break; + case 3: + paspx = 16 * y; + paspy = 9 * i; + break; + case 4: + paspx = 221 * y; + paspy = 100 * i; + break; + } + top = + DecoderReadByte(card, + 0x129) | ((DecoderReadByte(card, 0x12B) & 0x07) + << 8); // current Start- and End Column + bottom = + DecoderReadByte(card, + 0x12A) | ((DecoderReadByte(card, 0x12B) & 0x70) + << 4); + height = (bottom - top + 1) * 2; // screen (frame) height + left = + DecoderReadByte(card, + 0x12C) | ((DecoderReadByte(card, 0x12E) & 0x07) + << 8); // current Start- and End Row + right = + DecoderReadByte(card, + 0x12D) | ((DecoderReadByte(card, 0x12E) & 0x70) + << 4); + width = (right - left + 1) / 2; // screen width, 2 clocks = 1 pixel + + if (RMM) + DecoderSetByte(card, 0x0F8, 0x01); + else + DecoderDelByte(card, 0x0F8, 0x01); + + DecoderWriteByte(card, 0x0EF, 0x08); + + //if (x>width) { // Is the picture too wide for the screen? + // DecoderSetByte(card,0x112,0x40); // Horiz. 2:1 Filter enable + // x/=2; + //} else { + DecoderDelByte(card, 0x112, 0x40); // Horiz. 2:1 Filter disable + //} + + + + + if (1 /*Letterbox */ ) { // Fit to width, reduce height + newwidth = (x * vaspy * paspx / (paspy * vaspx)); // width in right aspect ratio + if (newwidth <= 360) { // less then about half the screen size? + SIF = 1; + newwidth *= 2; + } else { + SIF = 0; + } + if ((newwidth == 704) || (newwidth == 720)) + width = newwidth; // standard sizes? + newheight = + (y * vaspx * paspy / (paspx * vaspy)) * width / x; + factor = newheight * 100 / y; + printk(KERN_INFO LOGNAME + ": Decoder Open: Display size %d x %d, Picture size %d x %d, Demanded size: %d x %d, factor %d\n", + width, height, x, y, newwidth, newheight, factor); + // 16:9 Letterbox + if ((aspect == 3) + || ((aspect == 0) + && (((factor >= 65) && (factor <= 80)) + || ((factor >= 140) && (factor <= 160))))) { + if (SIF) { // height * 1.5, SIF Letterbox + if (RMM) + return 1; // not supported! + height = (y * 3) / 2 - 2; + mode = 3; + } else { // height * 0.75, 16:9 Letterbox + height = (y * 3) / 4 - 2; + mode = 8; + } + // 2.21:1 Letterbox + } else if ((aspect == 4) + || ((aspect == 0) + && (((factor >= 45) && (factor <= 60)) + || (SIF && ((factor >= 90) + && (factor <= 110)))))) { + if (SIF) { // height * 1 + height = y; + mode = 5; + } else { // height / 2 + height = y / 2; + mode = 11; + } + // 3:4 aspect ratio + } else { + if (SIF) { + height = y * 2; + mode = ((Field && ~RMM) ? 9 : 10); + } else if (newwidth > 720) { // picture too wide, scale down to 3/4 + height = (y * 3) / 4; + mode = 8; + } else { + height = y; + mode = ((Field) ? 7 : 5); +// mode=((Field)?5:7); + } + } + width = (x * vaspy * paspx / (paspy * vaspx)) * height / y; + if (x < width) { // does the picture needs a horizontal blow-up? + DecoderWriteByte(card, 0x115, + ((x * 256 + width - 1) / width) & 0xFF); // Horiz.Filter scale, x/width*256, rounded up + DecoderSetByte(card, 0x114, 0x02); // Horiz.Filter enable + } else if (x == width) { + DecoderWriteByte(card, 0x115, 0); // 1:1 scale + DecoderDelByte(card, 0x114, 0x02); // Horiz.Filter disable + } else if (x <= 720) { + width = x; + DecoderWriteByte(card, 0x115, 0); // 1:1 scale + DecoderDelByte(card, 0x114, 0x02); // Horiz.Filter disable + } else { // picture is more than twice the screen width. sigh. + return 1; + } + } else { // Pan-Scan, fit height to maximum + DecoderSetByte(card, 0x117, 0x40); // pan-scan from bitstream +//TODO + newwidth = (x * vaspy * paspx / (paspy * vaspx)); // width in right aspect ratio + newheight = y; + if (newheight <= 288) { // less then about half the screen size? + SIF = 1; + newheight *= 2; + } else { + SIF = 0; + } + if ((newwidth == 704) || (newwidth == 720)) + width = newwidth; // standard sizes? + //newheight=(y*vaspx*paspy/(paspx*vaspy))*width/x; + factor = newheight * 100 / y; + printk(KERN_INFO LOGNAME + ": Decoder Open: Display size %d x %d, Picture size %d x %d, Demanded size: %d x %d, factor %d\n", + width, height, x, y, newwidth, newheight, factor); + if (aspect == 3) { // 16:9 Letterbox + if (SIF) { // height * 1.5, SIF Letterbox + if (RMM) + return 1; // not supported! + height = (y * 3) / 2; + mode = 3; + } else { // height * 0.75, 16:9 Letterbox + height = (y * 3) / 4; + mode = 8; + } + } else if (aspect == 4) { // 2.21:1 Letterbox + if (SIF) { // height * 1 + height = y; + mode = 5; + } else { // height / 2 + height = y / 2; + mode = 11; + } + } else if (aspect == 2) { // 3:4 aspect ratio + if (SIF) { + height = y * 2; + mode = ((Field && ~RMM) ? 9 : 10); + } else if (newwidth > 720) { // picture too wide, scale down to 3/4 + height = (y * 3) / 4; + mode = 8; + } else { + height = y; + mode = ((Field) ? 7 : 5); +// mode=((Field)?5:7); + } + } + width = (x * vaspy * paspx / (paspy * vaspx)) * height / y; + if (x < width) { // does the picture needs a horizontal blow-up? + DecoderWriteByte(card, 0x115, + ((x * 256 + width - 1) / width) & 0xFF); // Horiz.Filter scale, x/width*256, rounded up + DecoderSetByte(card, 0x114, 0x02); // Horiz.Filter enable + } else if (x == width) { + DecoderWriteByte(card, 0x115, 0); // 1:1 scale + DecoderDelByte(card, 0x114, 0x02); // Horiz.Filter disable + } else if (x <= 720) { + width = x; + DecoderWriteByte(card, 0x115, 0); // 1:1 scale + DecoderDelByte(card, 0x114, 0x02); // Horiz.Filter disable + } else { // picture is more than twice the screen width. sigh. + return 1; + } + } + printk(KERN_INFO LOGNAME + ": Decoder Open: Display size %d x %d, Picture size %d x %d Mode: %d\n", + width, height, x, y, mode); + + // calculate new picture start- and end rows and columns + height /= 2; // convert back to field height + top += ((bottom - top + 1 - height) / 2); + if (top < 0) + top = 0; + bottom = top + height - 1; + width *= 2; // convert back to clocks + left += ((right - left + 1 - width) / 2); + if (left < 0) + left = 0; + right = left + width - 1; + DecoderWriteByte(card, 0x12C, left & 0xFF); // Start- and End Column + DecoderWriteByte(card, 0x12D, right & 0xFF); + DecoderWriteByte(card, 0x12E, + ((right >> 4) & 0x70) | ((left >> 8) & 0x07)); + DecoderWriteByte(card, 0x129, top & 0xFF); // Start- and End Row + DecoderWriteByte(card, 0x12A, bottom & 0xFF); + DecoderWriteByte(card, 0x12b, + ((bottom >> 4) & 0x70) | ((top >> 8) & 0x07)); + + DecoderWriteByte(card, 0x116, ((x + 7) / 8) & 0x7F); // Main Reads per Line + + // set the new mode + DecoderMaskByte(card, 0x114, 0x78, (mode & 0x0F) << 3); + + MDEBUG(3,": Decoder Open: top/bottom/width / left/right/height / main reads %d/%d/%d / %d/%d/%d / %d\n",top,bottom,width,left,right,height,((x+7)/8)&0x7F); + + // set the frame store buffers + if ((i = DecoderSetFrameBuffers(card, y, 0, RMM))) { + MDEBUG(0,": SetFrameBuffers failed for buffer at 0x%03X\n",i); + DecoderKillFrameBuffers(card); + return 2; + } + + card->lastvattr = 0; + card->DecoderOpen = 1; + return 0; +} + +// displays a still image, whose pixel data is in luma and chroma +int DecoderShowStill(struct cvdv_cards *card, int width, int height, + u8 * luma, u8 * chroma) +{ + u16 addr; + DecoderOpen(card, width, height, + (((width == 320) || (width == 640) || (width == 384) + || (width == 768)) ? 1 : 2), + ((height < 313) ? 1 : 0), 1, 0); + addr = + ((DecoderReadWord(card, 0x11D) == DecoderReadWord(card, 0x0E0)) + ? 0x0E4 : 0x0E0); // choose invisible frame + DRAMWriteByte(card, DecoderReadWord(card, addr) << 5, + width * height, luma, 1); + DRAMWriteByte(card, DecoderReadWord(card, addr + 2) << 5, + width * height / 2, chroma, 1); + DecoderStillImageDisplay(card, ((height < 313) ? 2 : 1), + DecoderReadByte(card, 0x116) & 0x7F, + DecoderReadWord(card, addr), + DecoderReadWord(card, addr + 2)); + VideoSetBackground(card, 0, 0, 0, 0); // video on black + return 0; +} + +// TODO: untested, probably won't work (have to use "main reads per line" instead of width on SIF) +int DecoderGetStill(struct cvdv_cards *card, int *width, int *height, + u8 * luma, u8 * chroma) +{ + int framebuffer; + if (card->DecoderOpen) { + //*width=((DecoderReadByte(card,0x12D)|((DecoderReadByte(card,0x12E)&0x70)<<4))-(DecoderReadByte(card,0x12C)|((DecoderReadByte(card,0x12E)&0x07)<<8))+1)/2; // screen width, 2 clocks = 1 pixel + *width = DecoderReadByte(card, 0x116) * 8; + *height = + ((DecoderReadByte + (card, + 0x12A) | ((DecoderReadByte(card, 0x12B) & 0x70) << + 4)) - + (DecoderReadByte(card, 0x129) | + ((DecoderReadByte(card, 0x12B) & 0x07) << 8)) + 1) * 2; // screen (frame) height + if ((luma != NULL) && (chroma != NULL)) { + framebuffer = + (((DecoderReadByte(card, 0x0EE) & 0x0C) == 1) ? + 0x0E4 : 0x0E0); + DRAMReadByte(card, + DecoderReadWord(card, + framebuffer) << 5, + (*width) * (*height), luma, 1); + DRAMReadByte(card, + DecoderReadWord(card, + framebuffer + 2) << 5, + (*width) * (*height) / 2, chroma, 1); + } + return 0; + } else + return 1; +} diff -Nru a/drivers/media/video/margi/video.h b/drivers/media/video/margi/video.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/margi/video.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,85 @@ +/* + video.h + + Copyright (C) Christian Wolff for convergence integrated media. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CVDV_VIDEO_H +#define CVDV_VIDEO_H + + // + // Video Decoder +// + +#include "cardbase.h" + +// Set the background of the OSD and SPU and it's color +// mode=0: Video on Black +// mode=1: Black +// mode=2: Selected Color +// mode=3: Video on Selected Color +void VideoSetBackground(struct cvdv_cards *card, int mode, u8 Y, u8 Cb, + u8 Cr); + + +int DecoderStartDecode(struct cvdv_cards *card); + +int DecoderStopDecode(struct cvdv_cards *card); + +// Sets Display Override (Still Image Display) to Frame Buffer at specified addresses, +// addresses are 16 bit, in 64 byte resolution +// mode: 0=off, 1=Frame, 2=Field +// width: width of the still picture in 8 pixel units +int DecoderStillImageDisplay(struct cvdv_cards *card, int mode, int width, + u16 LumaAddr, u16 ChromaAddr); + +// Frees allocated frame buffers +int DecoderKillFrameBuffers(struct cvdv_cards *card); + +int DecoderSetFrameBuffers(struct cvdv_cards *card, int lines, // number of lines of the decoded MPEG + int TwoFrames, // 1 if no B-Frames are present in the video stream, thus allowing only 2 framestores + int RMM); // 1 if RMM + +// returns size of the Video ES Buffer in bytes or 0=error +u32 DecoderGetVideoESSize(struct cvdv_cards *card); + +// returns level of fullness in bytes +u32 DecoderGetVideoESLevel(struct cvdv_cards *card); + +// pics=0 --> items=bytes +// pics=1 --> items=pictures +void DecoderSetVideoPanic(struct cvdv_cards *card, int pics, int items); + +int DecoderClose(struct cvdv_cards *card); + +// returns 0 on success, 1 on "picture size too big", 2 on "out of DRAM memory" +int DecoderOpen(struct cvdv_cards *card, int x, int y, // size of the decoded MPEG picture + int aspect, // pixel or picture aspect ratio of the MPEG picture: 1=square pixel 2=3:4 3=9:16 4=1:2.21 + int Field, // 0:Frame (interlaced, MPEG-2) , 1:Field (non-interlaced, MPEG-1) structure + int Letterbox, // 0:PanScan (4:3), 1:letterbox (16:9, 8:3) picture ratio // TODO, ignored for now + int RMM // 1:use ReducedMemoryMode + ); + +// displays a still image, whose pixel data is in luma and chroma +int DecoderShowStill(struct cvdv_cards *card, int width, int height, + u8 * luma, u8 * chroma); + +// TODO: untested, probably won't work (have to use "main reads per line" instead of width on SIF) +int DecoderGetStill(struct cvdv_cards *card, int *width, int *height, + u8 * luma, u8 * chroma); + +#endif /* CVDV_VIDEO_H */ diff -Nru a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c --- a/drivers/media/video/msp3400.c Wed Feb 13 20:03:33 2002 +++ b/drivers/media/video/msp3400.c Wed Feb 13 20:03:33 2002 @@ -97,6 +97,7 @@ int nicam_on; int acb; int main, second; /* sound carrier */ + int input; int muted; int left, right; /* volume */ @@ -462,6 +463,8 @@ /* turn on/off nicam + stereo */ static void msp3400c_setstereo(struct i2c_client *client, int mode) { + static char *strmode[] = { "0", "mono", "stereo", "3", + "lang1", "5", "6", "7", "lang2" }; struct msp3400c *msp = client->data; int nicam=0; /* channel source: FM/AM or nicam */ int src=0; @@ -469,7 +472,7 @@ /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: - dprintk("msp3400: FM setstereo: %d\n",mode); + dprintk("msp3400: FM setstereo: %s\n",strmode[mode]); msp3400c_setcarrier(client,msp->second,msp->main); switch (mode) { case VIDEO_SOUND_STEREO: @@ -483,7 +486,7 @@ } break; case MSP_MODE_FM_SAT: - dprintk("msp3400: SAT setstereo: %d\n",mode); + dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]); switch (mode) { case VIDEO_SOUND_MONO: msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); @@ -502,21 +505,21 @@ case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: case MSP_MODE_AM_NICAM: - dprintk("msp3400: NICAM setstereo: %d\n",mode); + dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]); msp3400c_setcarrier(client,msp->second,msp->main); if (msp->nicam_on) nicam=0x0100; break; case MSP_MODE_BTSC: - dprintk("msp3400: BTSC setstereo: %d\n",mode); + dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]); nicam=0x0300; break; case MSP_MODE_EXTERN: - dprintk("msp3400: extern setstereo: %d\n", mode); + dprintk("msp3400: extern setstereo: %s\n",strmode[mode]); nicam = 0x0200; break; case MSP_MODE_FM_RADIO: - dprintk("msp3400: FM-Radio setstereo: %d\n", mode); + dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]); break; default: dprintk("msp3400: mono setstereo\n"); @@ -627,7 +630,7 @@ switch (msp->mode) { case MSP_MODE_FM_TERRA: val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); - if (val > 32768) + if (val > 32767) val -= 65536; dprintk("msp34xx: stereo detect register: %d\n",val); @@ -822,7 +825,7 @@ msp->restart = 0; val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); - if (val > 32768) + if (val > 32767) val -= 65536; if (val1 < val) val1 = val, max1 = this; @@ -859,7 +862,7 @@ goto restart; val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); - if (val > 32768) + if (val > 32767) val -= 65536; if (val2 < val) val2 = val, max2 = this; @@ -1253,6 +1256,7 @@ msp->right = 65535; msp->bass = 32768; msp->treble = 32768; + msp->input = -1; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; @@ -1391,6 +1395,9 @@ - IN1 is often used for external input - Hauppauge uses IN2 for the radio */ dprintk(KERN_DEBUG "msp34xx: AUDC_SET_INPUT(%d)\n",*sarg); + if (*sarg == msp->input) + break; + msp->input = *sarg; switch (*sarg) { case AUDIO_RADIO: msp->mode = MSP_MODE_FM_RADIO; @@ -1420,6 +1427,7 @@ break; case AUDC_SET_RADIO: + dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1471,6 +1479,7 @@ { struct video_audio *va = arg; + dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n"); va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE | @@ -1497,6 +1506,7 @@ { struct video_audio *va = arg; + dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); msp->muted = (va->flags & VIDEO_AUDIO_MUTE); msp->left = (MIN(65536 - va->balance,32768) * va->volume) / 32768; @@ -1520,6 +1530,7 @@ { struct video_channel *vc = arg; + dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN\n"); dprintk("msp34xx: switching to TV mode\n"); msp->norm = vc->norm; break; @@ -1527,6 +1538,7 @@ case VIDIOCSFREQ: { /* new channel -- kick audio carrier scan */ + dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); msp_wake_thread(client); break; } @@ -1540,13 +1552,13 @@ /* ----------------------------------------------------------------------- */ -int msp3400_init_module(void) +static int msp3400_init_module(void) { i2c_add_driver(&driver); return 0; } -void msp3400_cleanup_module(void) +static void msp3400_cleanup_module(void) { i2c_del_driver(&driver); } diff -Nru a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c --- a/drivers/media/video/tda7432.c Wed Feb 13 20:03:36 2002 +++ b/drivers/media/video/tda7432.c Wed Feb 13 20:03:36 2002 @@ -518,7 +518,7 @@ &driver }; -int tda7432_init(void) +static int tda7432_init(void) { if ( (loudness < 0) || (loudness > 15) ) @@ -531,7 +531,7 @@ return 0; } -void tda7432_fini(void) +static void tda7432_fini(void) { i2c_del_driver(&driver); } diff -Nru a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c --- a/drivers/media/video/tda9875.c Wed Feb 13 20:03:50 2002 +++ b/drivers/media/video/tda9875.c Wed Feb 13 20:03:50 2002 @@ -147,6 +147,24 @@ } #endif +static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg) +{ + unsigned char write[1]; + unsigned char read[1]; + struct i2c_msg msgs[2] = { + { addr, 0, 1, write }, + { addr, I2C_M_RD, 1, read } + }; + write[0] = reg; + + if (2 != i2c_transfer(adap,msgs,2)) { + printk(KERN_WARNING "tda9875: I/O error (read2)\n"); + return -1; + } + dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]); + return read[0]; +} + static void tda9875_set(struct i2c_client *client) { struct tda9875 *tda = client->data; @@ -215,6 +233,22 @@ * i2c interface functions * * *********************** */ +static int tda9875_checkit(struct i2c_adapter *adap, int addr) +{ + int dic,rev; + + dic=i2c_read_register(adap,addr,254); + rev=i2c_read_register(adap,addr,255); + + if(dic==0 || dic==2) { // tda9875 and tda9875A + printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n", + dic==0?"":"A", rev,addr<<1); + return 1; + } + printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev); + return(0); +} + static int tda9875_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) { @@ -232,6 +266,11 @@ client->adapter = adap; client->addr = addr; client->data = t; + + if(!tda9875_checkit(adap,addr)) { + kfree(t); + return 1; + } do_tda9875_init(client); MOD_INC_USE_COUNT; @@ -375,22 +414,19 @@ &driver }; -#ifdef MODULE -int init_module(void) -#else -int tda9875_init(void) -#endif +static int tda9875_init(void) { i2c_add_driver(&driver); return 0; } -#ifdef MODULE -void cleanup_module(void) +static void tda9875_fini(void) { i2c_del_driver(&driver); } -#endif + +module_init(tda9875_init); +module_exit(tda9875_fini); /* * Local variables: diff -Nru a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c --- a/drivers/media/video/tuner.c Wed Feb 13 20:03:43 2002 +++ b/drivers/media/video/tuner.c Wed Feb 13 20:03:43 2002 @@ -154,7 +154,7 @@ 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, { "Alps TSBE1",TEMIC,PAL, 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, - { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modtec MM205 */ + { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modulartech MM205 */ 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632}, { "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ @@ -175,7 +175,7 @@ { "Temic PAL* auto (4006 FN5)", TEMIC, PAL, 16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623}, - { "Temic PAL (4009 FR5)", TEMIC, PAL, + { "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", TEMIC, PAL, 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, { "Temic NTSC (4039 FR5)", TEMIC, NTSC, 16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732}, @@ -271,7 +271,7 @@ } #endif // Initalization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 -int mt2032_init(struct i2c_client *c) +static int mt2032_init(struct i2c_client *c) { unsigned char buf[21]; int ret,xogc,xok=0; @@ -345,7 +345,7 @@ // IsSpurInBand()? -int mt2032_spurcheck(int f1, int f2, int spectrum_from,int spectrum_to) +static int mt2032_spurcheck(int f1, int f2, int spectrum_from,int spectrum_to) { int n1=1,n2,f; @@ -373,7 +373,7 @@ return 1; } -int mt2032_compute_freq(int rfin, int if1, int if2, int spectrum_from, +static int mt2032_compute_freq(int rfin, int if1, int if2, int spectrum_from, int spectrum_to, unsigned char *buf, int *ret_sel, int xogc) //all in Hz { int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, @@ -449,7 +449,7 @@ return 0; } -int mt2032_check_lo_lock(struct i2c_client *c) +static int mt2032_check_lo_lock(struct i2c_client *c) { int try,lock=0; unsigned char buf[2]; @@ -469,7 +469,7 @@ return lock; } -int mt2032_optimize_vco(struct i2c_client *c,int sel,int lock) +static int mt2032_optimize_vco(struct i2c_client *c,int sel,int lock) { unsigned char buf[2]; int tad1; @@ -505,7 +505,7 @@ } -void mt2032_set_if_freq(struct i2c_client *c,int rfin, int if1, int if2, int from, int to) +static void mt2032_set_if_freq(struct i2c_client *c,int rfin, int if1, int if2, int from, int to) { unsigned char buf[21]; int lint_try,ret,sel,lock=0; @@ -560,7 +560,7 @@ } -void mt2032_set_tv_freq(struct i2c_client *c, int freq, int norm) +static void mt2032_set_tv_freq(struct i2c_client *c, int freq, int norm) { int if2,from,to; @@ -815,14 +815,27 @@ static int tuner_probe(struct i2c_adapter *adap) { + int rc; + if (0 != addr) { normal_i2c_range[0] = addr; normal_i2c_range[1] = addr; } this_adap = 0; - if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) - return i2c_probe(adap, &addr_data, tuner_attach); - return 0; + switch (adap->id) { + case I2C_ALGO_BIT | I2C_HW_B_BT848: + case I2C_ALGO_SAA7134: + printk("tuner: probing %s i2c adapter [id=0x%x]\n", + adap->name,adap->id); + rc = i2c_probe(adap, &addr_data, tuner_attach); + break; + default: + printk("tuner: ignoring %s i2c adapter [id=0x%x]\n", + adap->name,adap->id); + rc = 0; + /* nothing */ + } + return rc; } static int tuner_detach(struct i2c_client *client) @@ -966,21 +979,20 @@ &driver }; -EXPORT_NO_SYMBOLS; - -int tuner_init_module(void) +static int tuner_init_module(void) { i2c_add_driver(&driver); return 0; } -void tuner_cleanup_module(void) +static void tuner_cleanup_module(void) { i2c_del_driver(&driver); } module_init(tuner_init_module); module_exit(tuner_cleanup_module); +EXPORT_NO_SYMBOLS; /* * Overrides for Emacs so that we follow Linus's tabbing style. diff -Nru a/drivers/media/video/tuner.h b/drivers/media/video/tuner.h --- a/drivers/media/video/tuner.h Wed Feb 13 20:03:36 2002 +++ b/drivers/media/video/tuner.h Wed Feb 13 20:03:36 2002 @@ -22,6 +22,8 @@ #ifndef _TUNER_H #define _TUNER_H +#include "id.h" + #define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ #define TUNER_PHILIPS_PAL_I 1 #define TUNER_PHILIPS_NTSC 2 diff -Nru a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c --- a/drivers/media/video/tvaudio.c Wed Feb 13 20:03:46 2002 +++ b/drivers/media/video/tvaudio.c Wed Feb 13 20:03:46 2002 @@ -120,14 +120,17 @@ /* current settings */ __u16 left,right,treble,bass,mode; int prevmode; + int norm; /* thread */ struct task_struct *thread; struct semaphore *notify; wait_queue_head_t wq; struct timer_list wt; int done; + int watch_stereo; }; +#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ /* ---------------------------------------------------------------------- */ /* i2c addresses */ @@ -139,7 +142,7 @@ I2C_TDA9840 >> 1, I2C_TDA985x_L >> 1, I2C_TDA985x_H >> 1, - I2C_TDA9874A >> 1, + I2C_TDA9874 >> 1, I2C_PIC16C54 >> 1, I2C_CLIENT_END }; static unsigned short normal_i2c_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; @@ -296,9 +299,9 @@ dprintk("%s: thread wakeup\n", chip->c.name); if (chip->done || signal_pending(current)) break; - - if (0 != chip->mode) - /* don't do anything if mode != auto */ + + /* don't do anything for radio or if mode != auto */ + if (chip->norm == VIDEO_MODE_RADIO || chip->mode != 0) continue; /* have a look what's going on */ @@ -316,7 +319,7 @@ return 0; } -void generic_checkmode(struct CHIPSTATE *chip) +static void generic_checkmode(struct CHIPSTATE *chip) { struct CHIPDESC *desc = chiplist + chip->type; int mode = desc->getmode(chip); @@ -327,12 +330,12 @@ dprintk("%s: thread checkmode\n", chip->c.name); chip->prevmode = mode; - if (mode & VIDEO_SOUND_LANG1) + if (mode & VIDEO_SOUND_STEREO) + desc->setmode(chip,VIDEO_SOUND_STEREO); + else if (mode & VIDEO_SOUND_LANG1) desc->setmode(chip,VIDEO_SOUND_LANG1); else if (mode & VIDEO_SOUND_LANG2) desc->setmode(chip,VIDEO_SOUND_LANG2); - else if (mode & VIDEO_SOUND_STEREO) - desc->setmode(chip,VIDEO_SOUND_STEREO); else desc->setmode(chip,VIDEO_SOUND_MONO); } @@ -360,7 +363,7 @@ #define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ #define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ -int tda9840_getmode(struct CHIPSTATE *chip) +static int tda9840_getmode(struct CHIPSTATE *chip) { int val, mode; @@ -376,7 +379,7 @@ return mode; } -void tda9840_setmode(struct CHIPSTATE *chip, int mode) +static void tda9840_setmode(struct CHIPSTATE *chip, int mode) { int update = 1; int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; @@ -502,11 +505,11 @@ * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ #define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ -int tda9855_volume(int val) { return val/0x2e8+0x27; } -int tda9855_bass(int val) { return val/0xccc+0x06; } -int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } +static int tda9855_volume(int val) { return val/0x2e8+0x27; } +static int tda9855_bass(int val) { return val/0xccc+0x06; } +static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } -int tda985x_getmode(struct CHIPSTATE *chip) +static int tda985x_getmode(struct CHIPSTATE *chip) { int mode; @@ -517,7 +520,7 @@ return mode | VIDEO_SOUND_MONO; } -void tda985x_setmode(struct CHIPSTATE *chip, int mode) +static void tda985x_setmode(struct CHIPSTATE *chip, int mode) { int update = 1; int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; @@ -657,7 +660,7 @@ #define TDA9873_STEREO 2 /* Stereo sound is identified */ #define TDA9873_DUAL 4 /* Dual sound is identified */ -int tda9873_getmode(struct CHIPSTATE *chip) +static int tda9873_getmode(struct CHIPSTATE *chip) { int val,mode; @@ -672,7 +675,7 @@ return mode; } -void tda9873_setmode(struct CHIPSTATE *chip, int mode) +static void tda9873_setmode(struct CHIPSTATE *chip, int mode) { int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ @@ -708,7 +711,7 @@ mode, sw_data); } -int tda9873_checkit(struct CHIPSTATE *chip) +static int tda9873_checkit(struct CHIPSTATE *chip) { int rc; @@ -719,10 +722,10 @@ /* ---------------------------------------------------------------------- */ -/* audio chip description - defines+functions for tda9874a */ +/* audio chip description - defines+functions for tda9874h and tda9874a */ /* Dariusz Kowalewski */ -/* Subaddresses for TDA9874A (slave rx) */ +/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ #define TDA9874A_AGCGR 0x00 /* AGC gain */ #define TDA9874A_GCONR 0x01 /* general config */ #define TDA9874A_MSR 0x02 /* monitor select */ @@ -747,10 +750,10 @@ #define TDA9874A_DAICONR 0x15 /* digital audio interface config */ #define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ #define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ -#define TDA9874A_MDACOSR 0x18 /* mono DAC output select */ -#define TDA9874A_ESP 0xFF /* easy standard progr. */ +#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ +#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ -/* Subaddresses for TDA9874A (slave tx) */ +/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ #define TDA9874A_DSR 0x00 /* device status */ #define TDA9874A_NSR 0x01 /* NICAM status */ #define TDA9874A_NECR 0x02 /* NICAM error count */ @@ -767,37 +770,91 @@ static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ +static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ +static int tda9874a_dic = -1; /* device id. code */ /* insmod options for tda9874a */ static int tda9874a_SIF = -1; +static int tda9874a_AMSEL = -1; static int tda9874a_STD = -1; MODULE_PARM(tda9874a_SIF,"i"); +MODULE_PARM(tda9874a_AMSEL,"i"); MODULE_PARM(tda9874a_STD,"i"); +/* + * initialization table for tda9874 decoder: + * - carrier 1 freq. registers (3 bytes) + * - carrier 2 freq. registers (3 bytes) + * - demudulator config register + * - FM de-emphasis register (slow identification mode) + * Note: frequency registers must be written in single i2c transfer. + */ +static struct tda9874a_MODES { + char *name; + audiocmd cmd; +} tda9874a_modelist[9] = { + { "A2, B/G", + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "A2, M (Korea)", + { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, + { "A2, D/K (1)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, + { "A2, D/K (2)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, + { "A2, D/K (3)", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, + { "NICAM, I", + { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, + { "NICAM, B/G", + { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, D/K", /* default */ + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, + { "NICAM, L", + { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } +}; static int tda9874a_setup(struct CHIPSTATE *chip) { chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); - chip_write(chip, TDA9874A_FMMR, 0x80); + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_FMMR, 0x80); + } else { /* dic == 0x07 */ + chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); + chip_write(chip, TDA9874A_FMMR, 0x00); + } chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_NCONR, 0x00); /* not 0x04 as doc. table 10 says! */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ - chip_write(chip, TDA9874A_AMCONR, 0xf9); - chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); /* 0x81 */ - chip_write(chip, TDA9874A_AOSR, 0x80); - chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); - chip_write(chip, TDA9874A_ESP, tda9874a_ESP); - + /* Note: If signal quality is poor you may want to change NICAM */ + /* error limit registers (NLELR and NUELR) to some greater values. */ + /* Then the sound would remain stereo, but won't be so clear. */ + chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ + chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ + + if(tda9874a_dic == 0x11) { + chip_write(chip, TDA9874A_AMCONR, 0xf9); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x80); + chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); + chip_write(chip, TDA9874A_ESP, tda9874a_ESP); + } else { /* dic == 0x07 */ + chip_write(chip, TDA9874A_AMCONR, 0xfb); + chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); + chip_write(chip, TDA9874A_AOSR, 0x00); // or 0x10 + } + dprintk("tda9874a_setup(): %s [0x%02X].\n", + tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); return 1; } -int tda9874a_getmode(struct CHIPSTATE *chip) +static int tda9874a_getmode(struct CHIPSTATE *chip) { int dsr,nsr,mode; + int necr; /* just for debugging */ mode = VIDEO_SOUND_MONO; @@ -805,55 +862,125 @@ return mode; if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) return mode; + if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) + return mode; + + /* need to store dsr/nsr somewhere */ + chip->shadow.bytes[MAXREGS-2] = dsr; + chip->shadow.bytes[MAXREGS-1] = nsr; if(tda9874a_mode) { - /* check also DSR.RSSF and DSR.AMSTAT bits? */ - if(nsr & 0x02) /* NSR.S/MB */ + /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. + * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates + * that sound has (temporarily) switched from NICAM to + * mono FM (or AM) on 1st sound carrier due to high NICAM bit + * error count. So in fact there is no stereo in this case :-( + * But changing the mode to VIDEO_SOUND_MONO would switch + * external 4052 multiplexer in audio_hook(). + */ +#if 0 + if((nsr & 0x02) && !(dsr & 0x10)) /* NSR.S/MB=1 and DSR.AMSTAT=0 */ mode |= VIDEO_SOUND_STEREO; - if(nsr & 0x01) /* NSR.D/SB */ +#else + if(nsr & 0x02) /* NSR.S/MB=1 */ + mode |= VIDEO_SOUND_STEREO; +#endif + if(nsr & 0x01) /* NSR.D/SB=1 */ mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; } else { - if(dsr & 0x02) /* DSR.IDSTE */ + if(dsr & 0x02) /* DSR.IDSTE=1 */ mode |= VIDEO_SOUND_STEREO; - if(dsr & 0x04) /* DSR.IDDUA */ + if(dsr & 0x04) /* DSR.IDDUA=1 */ mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; } - dprintk("tda9874a_getmode(): DSR=0x%X, NSR=0x%X, return: %d.\n", - dsr, nsr, mode); + dprintk("tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + dsr, nsr, necr, mode); return mode; } -void tda9874a_setmode(struct CHIPSTATE *chip, int mode) +static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) { - int aosr=0x80,mdacosr=0x82; - - /* note: TDA9874A has auto-select function for audio output */ - switch(mode) { - case VIDEO_SOUND_MONO: - case VIDEO_SOUND_STEREO: - break; - case VIDEO_SOUND_LANG1: - aosr = 0x80; /* dual A/A */ - mdacosr = (tda9874a_mode) ? 0x82:0x80; - break; - case VIDEO_SOUND_LANG2: - aosr = 0xa0; /* dual B/B */ - mdacosr = (tda9874a_mode) ? 0x83:0x81; - break; - default: - chip->mode = 0; - return; + /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ + /* If auto-muting is disabled, we can hear a signal of degrading quality. */ + if(tda9874a_mode) { + if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ + tda9874a_NCONR &= 0xfe; /* enable */ + else + tda9874a_NCONR |= 0x01; /* disable */ + chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); } - chip_write(chip, TDA9874A_AOSR, aosr); - chip_write(chip, TDA9874A_MDACOSR, mdacosr); + /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) + * and has auto-select function for audio output (AOSR register). + * Old TDA9874H doesn't support these features. + * TDA9874A also has additional mono output pin (OUTM), which + * on same (all?) tv-cards is not used, anyway (as well as MONOIN). + */ + if(tda9874a_dic == 0x11) { + int aosr = 0x80; + int mdacosr = (tda9874a_mode) ? 0x82:0x80; + + switch(mode) { + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_STEREO: + break; + case VIDEO_SOUND_LANG1: + aosr = 0x80; /* auto-select, dual A/A */ + mdacosr = (tda9874a_mode) ? 0x82:0x80; + break; + case VIDEO_SOUND_LANG2: + aosr = 0xa0; /* auto-select, dual B/B */ + mdacosr = (tda9874a_mode) ? 0x83:0x81; + break; + default: + chip->mode = 0; + return; + } + chip_write(chip, TDA9874A_AOSR, aosr); + chip_write(chip, TDA9874A_MDACOSR, mdacosr); + + dprintk("tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + mode, aosr, mdacosr); - dprintk("tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", - mode, aosr, mdacosr); + } else { /* dic == 0x07 */ + int fmmr,aosr; + + switch(mode) { + case VIDEO_SOUND_MONO: + fmmr = 0x00; /* mono */ + aosr = 0x10; /* A/A */ + break; + case VIDEO_SOUND_STEREO: + if(tda9874a_mode) { + fmmr = 0x00; + aosr = 0x00; /* handled by NICAM auto-mute */ + } else { + fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ + aosr = 0x00; + } + break; + case VIDEO_SOUND_LANG1: + fmmr = 0x02; /* dual */ + aosr = 0x10; /* dual A/A */ + break; + case VIDEO_SOUND_LANG2: + fmmr = 0x02; /* dual */ + aosr = 0x20; /* dual B/B */ + break; + default: + chip->mode = 0; + return; + } + chip_write(chip, TDA9874A_FMMR, fmmr); + chip_write(chip, TDA9874A_AOSR, aosr); + + dprintk("tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + mode, fmmr, aosr); + } } -int tda9874a_checkit(struct CHIPSTATE *chip) +static int tda9874a_checkit(struct CHIPSTATE *chip) { int dic,sic; /* device id. and software id. codes */ @@ -864,10 +991,15 @@ dprintk("tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); - return((dic & 0xff) == 0x11); + if((dic == 0x11)||(dic == 0x07)) { + dprintk("tvaudio: found tda9874%s.\n",(dic == 0x11) ? "a (new)":"h (old)"); + tda9874a_dic = dic; /* remember device id. */ + return 1; + } + return 0; /* not found */ } -int tda9874a_initialize(struct CHIPSTATE *chip) +static int tda9874a_initialize(struct CHIPSTATE *chip) { if(tda9874a_SIF != -1) { if(tda9874a_SIF == 1) @@ -887,6 +1019,15 @@ } } + if(tda9874a_AMSEL != -1) { + if(tda9874a_AMSEL == 0) + tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ + else if(tda9874a_AMSEL == 1) + tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ + else + printk(KERN_WARNING "tda9874a: AMSEL parameter must be 0 or 1.\n"); + } + tda9874a_setup(chip); return 0; @@ -915,8 +1056,8 @@ #define TEA6420_S_SE 0x04 /* stereo E */ #define TEA6420_S_GMU 0x05 /* general mute */ -int tea6300_shift10(int val) { return val >> 10; } -int tea6300_shift12(int val) { return val >> 12; } +static int tea6300_shift10(int val) { return val >> 10; } +static int tea6300_shift12(int val) { return val >> 12; } /* ---------------------------------------------------------------------- */ @@ -931,8 +1072,8 @@ #define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ #define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */ -int tda8425_shift10(int val) { return val >> 10 | 0xc0; } -int tda8425_shift12(int val) { return val >> 12 | 0xf0; } +static int tda8425_shift10(int val) { return val >> 10 | 0xc0; } +static int tda8425_shift12(int val) { return val >> 12 | 0xf0; } /* ---------------------------------------------------------------------- */ @@ -1015,13 +1156,13 @@ }, { - name: "tda9874a", - id: I2C_DRIVERID_TDA9874A, + name: "tda9874h/a", + id: I2C_DRIVERID_TDA9874, checkit: tda9874a_checkit, initialize: tda9874a_initialize, insmodopt: &tda9874a, - addr_lo: I2C_TDA9874A >> 1, - addr_hi: I2C_TDA9874A >> 1, + addr_lo: I2C_TDA9874 >> 1, + addr_hi: I2C_TDA9874 >> 1, getmode: tda9874a_getmode, setmode: tda9874a_setmode, @@ -1160,7 +1301,7 @@ chip->c.data = chip; /* find description for the chip */ - dprintk("tvaudio: chip @ addr=0x%x\n", addr<<1); + dprintk("tvaudio: chip found @ i2c-addr=0x%x\n", addr<<1); for (desc = chiplist; desc->name != NULL; desc++) { if (0 == *(desc->insmodopt)) continue; @@ -1175,7 +1316,8 @@ dprintk("tvaudio: no matching chip description found\n"); return -EIO; } - dprintk("tvaudio: %s matches:%s%s%s\n",desc->name, + printk("tvaudio: found %s\n",desc->name); + dprintk("tvaudio: matches:%s%s%s.\n", (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); @@ -1272,6 +1414,14 @@ chip_write_masked(chip,desc->inputreg,desc->inputmap[*sarg],desc->inputmask); } break; + + case AUDC_SET_RADIO: + dprintk(KERN_DEBUG "tvaudio: AUDC_SET_RADIO\n"); + chip->norm = VIDEO_MODE_RADIO; + chip->watch_stereo = 0; + /* del_timer(&chip->wt); */ + break; + /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ @@ -1290,10 +1440,12 @@ va->bass = chip->bass; va->treble = chip->treble; } - if (desc->getmode) - va->mode = desc->getmode(chip); - else - va->mode = VIDEO_SOUND_MONO; + if (chip->norm != VIDEO_MODE_RADIO) { + if (desc->getmode) + va->mode = desc->getmode(chip); + else + va->mode = VIDEO_SOUND_MONO; + } break; } @@ -1316,11 +1468,21 @@ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); } if (desc->setmode && va->mode) { + chip->watch_stereo = 0; + /* del_timer(&chip->wt); */ chip->mode = va->mode; desc->setmode(chip,va->mode); } break; } + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + dprintk(KERN_DEBUG "tvaudio: VIDIOCSCHAN\n"); + chip->norm = vc->norm; + break; + } case VIDIOCSFREQ: { chip->mode = 0; /* automatic */ @@ -1352,7 +1514,7 @@ driver: &driver, }; -int audiochip_init_module(void) +static int audiochip_init_module(void) { struct CHIPDESC *desc; printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); @@ -1364,7 +1526,7 @@ return 0; } -void audiochip_cleanup_module(void) +static void audiochip_cleanup_module(void) { i2c_del_driver(&driver); } diff -Nru a/drivers/media/video/tvaudio.h b/drivers/media/video/tvaudio.h --- a/drivers/media/video/tvaudio.h Wed Feb 13 20:03:30 2002 +++ b/drivers/media/video/tvaudio.h Wed Feb 13 20:03:30 2002 @@ -6,7 +6,7 @@ #define I2C_TDA9840 0x84 #define I2C_TDA985x_L 0xb4 /* also used by 9873 */ #define I2C_TDA985x_H 0xb6 -#define I2C_TDA9874A 0xb0 /* also used by 9875 */ +#define I2C_TDA9874 0xb0 /* also used by 9875 */ #define I2C_TEA6300 0x80 #define I2C_TEA6420 0x98 diff -Nru a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c --- a/drivers/media/video/tvmixer.c Wed Feb 13 20:03:47 2002 +++ b/drivers/media/video/tvmixer.c Wed Feb 13 20:03:47 2002 @@ -8,8 +8,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -177,7 +178,7 @@ static int tvmixer_open(struct inode *inode, struct file *file) { - int i, minor = MINOR(inode->i_rdev); + int i, minor = minor(inode->i_rdev); struct TVMIXER *mix = NULL; struct i2c_client *client = NULL; @@ -320,7 +321,7 @@ /* ----------------------------------------------------------------------- */ -int tvmixer_init_module(void) +static int tvmixer_init_module(void) { int i; @@ -330,7 +331,7 @@ return 0; } -void tvmixer_cleanup_module(void) +static void tvmixer_cleanup_module(void) { int i; diff -Nru a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/v4l1-compat.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,1030 @@ +/* + * Video for Linux Two + * Backward Compatibility Layer + * + * Support subroutines for providing V4L2 drivers with backward + * compatibility with applications using the old API. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Bill Dirks + * et al. + * + */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +static unsigned int debug = 0; +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"enable debug messages"); +MODULE_AUTHOR("Bill Dirks"); +MODULE_DESCRIPTION("v4l(1) compatibility layer for v4l2 drivers."); +MODULE_LICENSE("GPL"); + +#define dprintk(fmt, arg...) if (debug) \ + printk(KERN_DEBUG "v4l1-compat: " fmt, ## arg) + +/* + * I O C T L T R A N S L A T I O N + * + * From here on down is the code for translating the numerous + * ioctl commands from the old API to the new API. + */ + +static int +get_v4l_control(struct video_device *vfl, + struct inode *inode, + struct file *file, + int cid) +{ + struct v4l2_queryctrl qctrl2; + struct v4l2_control ctrl2; + int err; + + qctrl2.id = cid; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYCTRL, &qctrl2); + if (err < 0) + dprintk("VIDIOC_QUERYCTRL: %d\n",err); + if (err == 0 && + !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) + { + ctrl2.id = qctrl2.id; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_CTRL, &ctrl2); + if (err < 0) + dprintk("VIDIOC_G_CTRL: %d\n",err); + return ((ctrl2.value - qctrl2.minimum) * 65535 + + (qctrl2.maximum - qctrl2.minimum) / 2) + / (qctrl2.maximum - qctrl2.minimum); + } + return 0; +} + +static int +set_v4l_control(struct video_device *vfl, + struct inode *inode, + struct file *file, + int cid, + int value) +{ + struct v4l2_queryctrl qctrl2; + struct v4l2_control ctrl2; + int err; + + qctrl2.id = cid; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYCTRL, &qctrl2); + if (err < 0) + dprintk("VIDIOC_QUERYCTRL: %d\n",err); + if (err == 0 && + !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED) && + !(qctrl2.flags & V4L2_CTRL_FLAG_GRABBED)) + { + if (value < 0) + value = 0; + if (value > 65535) + value = 65535; + if (value && qctrl2.type == V4L2_CTRL_TYPE_BOOLEAN) + value = 65535; + ctrl2.id = qctrl2.id; + ctrl2.value = + (value * (qctrl2.maximum - qctrl2.minimum) + + 32767) + / 65535; + ctrl2.value += qctrl2.minimum; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_CTRL, &ctrl2); + if (err < 0) + dprintk("VIDIOC_S_CTRL: %d\n",err); + } + return 0; +} + +static int +find_tuner(struct video_device *vfl, + struct inode *inode, + struct file *file, + int n) +{ + struct v4l2_input inp2; + int i; + int err=0; + + /* Find the input number of the n'th tuner */ + for (i = 0; i < 100/*arbitrary*/; ++i) + { + inp2.index = i; + err = vfl->kernel_ioctl(inode, file, VIDIOC_ENUMINPUT, &inp2); + if (err < 0) { + dprintk("VIDIOC_ENUMINPUT: %d\n",err); + break; + } + if (inp2.type != V4L2_INPUT_TYPE_TUNER) + continue; + if (n == 0) + break; + --n; + } + if (err < 0) + return err; + return i; +} + +static int +palette_to_pixelformat(int palette) +{ + int pixelformat = 0; + switch (palette) + { + case VIDEO_PALETTE_GREY: + pixelformat = V4L2_PIX_FMT_GREY; + break; + case VIDEO_PALETTE_RGB555: + pixelformat = V4L2_PIX_FMT_RGB555; + break; + case VIDEO_PALETTE_RGB565: + pixelformat = V4L2_PIX_FMT_RGB565; + break; + case VIDEO_PALETTE_RGB24: + pixelformat = V4L2_PIX_FMT_BGR24; + break; + case VIDEO_PALETTE_RGB32: + pixelformat = V4L2_PIX_FMT_BGR32; + break; + /* yuv packed pixel */ + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422: + pixelformat = V4L2_PIX_FMT_YUYV; + break; + case VIDEO_PALETTE_UYVY: + pixelformat = V4L2_PIX_FMT_UYVY; + break; + /* yuv planar */ + case VIDEO_PALETTE_YUV410P: + pixelformat = V4L2_PIX_FMT_YUV410; + break; + case VIDEO_PALETTE_YUV420: /* ??? */ + case VIDEO_PALETTE_YUV420P: + pixelformat = V4L2_PIX_FMT_YUV420; + break; + case VIDEO_PALETTE_YUV411P: + pixelformat = V4L2_PIX_FMT_YUV411P; + break; + case VIDEO_PALETTE_YUV422P: + pixelformat = V4L2_PIX_FMT_YUV422P; + break; + } + return pixelformat; +} + +static int +pixelformat_to_palette(int pixelformat) +{ + int palette = 0; + switch (pixelformat) + { + case V4L2_PIX_FMT_GREY: + palette = VIDEO_PALETTE_GREY; + break; + case V4L2_PIX_FMT_RGB555: + palette = VIDEO_PALETTE_RGB555; + break; + case V4L2_PIX_FMT_RGB565: + palette = VIDEO_PALETTE_RGB565; + break; + case V4L2_PIX_FMT_BGR24: + palette = VIDEO_PALETTE_RGB24; + break; + case V4L2_PIX_FMT_BGR32: + palette = VIDEO_PALETTE_RGB32; + break; + /* yuv packed pixel */ + case V4L2_PIX_FMT_YUYV: + palette = VIDEO_PALETTE_YUYV; + break; + case V4L2_PIX_FMT_UYVY: + palette = VIDEO_PALETTE_UYVY; + break; + /* yuv planar */ + case V4L2_PIX_FMT_YUV410: + palette = VIDEO_PALETTE_YUV420; + break; + case V4L2_PIX_FMT_YUV420: + palette = VIDEO_PALETTE_YUV420; + break; + case V4L2_PIX_FMT_YUV411P: + palette = VIDEO_PALETTE_YUV411P; + break; + case V4L2_PIX_FMT_YUV422P: + palette = VIDEO_PALETTE_YUV422P; + break; + } + return palette; +} + +/* Do an 'in' (wait for input) select on a single file descriptor */ +/* This stuff plaigarized from linux/fs/select.c */ +#define __FD_IN(fds, n) (fds->in + n) +#define BIT(i) (1UL << ((i)&(__NFDBITS-1))) +#define SET(i,m) (*(m) |= (i)) +extern int do_select(int n, fd_set_bits *fds, long *timeout); + + +static int +simple_select(struct file *file) +{ + fd_set_bits fds; + char *bits; + long timeout; + int i, fd, n, ret, size; + + for (i = 0; i < current->files->max_fds; ++i) + if (file == current->files->fd[i]) + break; + if (i == current->files->max_fds) + return -EINVAL; + fd = i; + n = fd + 1; + + timeout = MAX_SCHEDULE_TIMEOUT; + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + /* All zero except our one file descriptor bit, for input */ + memset(bits, 0, 6 * size); + SET(BIT(fd), __FD_IN((&fds), fd / __NFDBITS)); + + ret = do_select(n, &fds, &timeout); + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } +out: + kfree(bits); +out_nofds: + return ret; +} + + +/* + * This function is exported. + */ +static int +v4l_compat_translate_ioctl(struct inode *inode, + struct file *file, + int cmd, + void *arg) +{ + struct video_device *vfl = video_devdata(file); + int err = -ENOIOCTLCMD; + + switch (cmd) + { + case VIDIOCGCAP: /* capability */ + { + struct video_capability *cap = arg; + struct v4l2_capability cap2; + struct v4l2_framebuffer fbuf2; + + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYCAP, &cap2); + if (err < 0) { + dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %d\n",err); + break; + } + if (cap2.flags & V4L2_FLAG_PREVIEW) + { + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FBUF, &fbuf2); + if (err < 0) { + dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %d\n",err); + memset(&fbuf2, 0, sizeof(fbuf2)); + } + err = 0; + } + memset(cap, 0, sizeof(cap)); + memcpy(cap->name, cap2.name, + min(sizeof(cap->name), sizeof(cap2.name))); + cap->name[sizeof(cap->name) - 1] = 0; + if (cap2.type == V4L2_TYPE_CAPTURE) + cap->type = VID_TYPE_CAPTURE; + if (cap2.flags & V4L2_FLAG_TUNER) + cap->type |= VID_TYPE_TUNER; + if (cap2.flags & V4L2_FLAG_DATA_SERVICE) + cap->type |= VID_TYPE_TELETEXT; + if (cap2.flags & V4L2_FLAG_PREVIEW) + cap->type |= VID_TYPE_OVERLAY; + if (cap2.flags & V4L2_FLAG_MONOCHROME) + cap->type |= VID_TYPE_MONOCHROME; + if (fbuf2.flags & V4L2_FBUF_FLAG_PRIMARY) + cap->type |= VID_TYPE_FRAMERAM; + if (fbuf2.capability & V4L2_FBUF_CAP_CHROMAKEY) + cap->type |= VID_TYPE_CHROMAKEY; + if (fbuf2.capability & V4L2_FBUF_CAP_CLIPPING) + cap->type |= VID_TYPE_CLIPPING; + if (fbuf2.capability & V4L2_FBUF_CAP_SCALEUP || + fbuf2.capability & V4L2_FBUF_CAP_SCALEDOWN) + cap->type |= VID_TYPE_SCALES; + cap->channels = cap2.inputs; + cap->audios = cap2.audios; + cap->maxwidth = cap2.maxwidth; + cap->maxheight = cap2.maxheight; + cap->minwidth = cap2.minwidth; + cap->minheight = cap2.minheight; + break; + } + case VIDIOCGFBUF: /* get frame buffer */ + { + struct video_buffer *buffer = arg; + struct v4l2_framebuffer fbuf2; + + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FBUF, &fbuf2); + if (err < 0) { + dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err); + break; + } + buffer->base = fbuf2.base[0]; + buffer->height = fbuf2.fmt.height; + buffer->width = fbuf2.fmt.width; + buffer->depth = fbuf2.fmt.depth; + if (fbuf2.fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE) + buffer->bytesperline = fbuf2.fmt.bytesperline; + else + { + buffer->bytesperline = + (fbuf2.fmt.width * fbuf2.fmt.depth + 7) & 7; + buffer->bytesperline >>= 3; + } + if (fbuf2.fmt.pixelformat == V4L2_PIX_FMT_RGB555) + buffer->depth = 15; + break; + } + case VIDIOCSFBUF: /* set frame buffer */ + { + struct video_buffer *buffer = arg; + struct v4l2_framebuffer fbuf2; + + memset(&fbuf2, 0, sizeof(fbuf2)); + fbuf2.base[0] = buffer->base; + fbuf2.fmt.height = buffer->height; + fbuf2.fmt.width = buffer->width; + fbuf2.fmt.depth = buffer->depth; + switch (fbuf2.fmt.depth) + { + case 8: + fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB332; + break; + case 15: + fbuf2.fmt.depth = 16; + fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB555; + break; + case 16: + fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + break; + case 24: + fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR24; + break; + case 32: + fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR32; + break; + } + fbuf2.fmt.flags |= V4L2_FMT_FLAG_BYTESPERLINE; + fbuf2.fmt.bytesperline = buffer->bytesperline; + fbuf2.flags = V4L2_FBUF_FLAG_PRIMARY; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FBUF, &fbuf2); + if (err < 0) + dprintk("VIDIOCSFBUF / VIDIOC_S_FBUF: %d\n",err); + break; + } + case VIDIOCGWIN: /* get window or capture dimensions */ + { + struct video_window *win = arg; + struct v4l2_window win2; + struct v4l2_format fmt2; + + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_WIN, &win2); + if (err < 0) + dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %d\n",err); + if (err == 0) + { + win->x = win2.x; + win->y = win2.y; + win->width = win2.width; + win->height = win2.height; + win->chromakey = win2.chromakey; + win->clips = NULL; + win->clipcount = 0; + break; + } + fmt2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2); + if (err < 0) { + dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %d\n",err); + break; + } + win->x = 0; + win->y = 0; + win->width = fmt2.fmt.pix.width; + win->height = fmt2.fmt.pix.height; + win->chromakey = 0; + win->clips = NULL; + win->clipcount = 0; + break; + } + case VIDIOCSWIN: /* set window and/or capture dimensions */ + { + struct video_window *win = arg; + struct v4l2_window win2; + struct v4l2_format fmt2; + + fmt2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2); + if (err < 0) + dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %d\n",err); + if (err == 0) + { + fmt2.fmt.pix.width = win->width; + fmt2.fmt.pix.height = win->height; + fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FMT, &fmt2); + if (err < 0) + dprintk("VIDIOCSWIN / VIDIOC_S_FMT: %d\n",err); + win->width = fmt2.fmt.pix.width; + win->height = fmt2.fmt.pix.height; + } + win2.x = win->x; + win2.y = win->y; + win2.width = win->width; + win2.height = win->height; + win2.chromakey = win->chromakey; + win2.clips = (void *)win->clips; + win2.clipcount = win->clipcount; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_WIN, &win2); + if (err < 0) + dprintk("VIDIOCSWIN / VIDIOC_S_WIN: %d\n",err); + break; + } + case VIDIOCCAPTURE: /* turn on/off preview */ + { + err = vfl->kernel_ioctl(inode, file, VIDIOC_PREVIEW, arg); + if (err < 0) + dprintk("VIDIOCCAPTURE / VIDIOC_PREVIEW: %d\n",err); + break; + } + case VIDIOCGCHAN: /* get input information */ + { + struct video_channel *chan = arg; + struct v4l2_input input2; + struct v4l2_standard std2; + int sid; + + input2.index = chan->channel; + err = vfl->kernel_ioctl(inode, file, VIDIOC_ENUMINPUT, &input2); + if (err < 0) { + dprintk("VIDIOCGCHAN / VIDIOC_ENUMINPUT: %d\n",err); + break; + } + chan->channel = input2.index; + memcpy(chan->name, input2.name, + min(sizeof(chan->name), sizeof(input2.name))); + chan->name[sizeof(chan->name) - 1] = 0; + chan->tuners = (input2.type == V4L2_INPUT_TYPE_TUNER) ? 1 : 0; + chan->flags = (chan->tuners) ? VIDEO_VC_TUNER : 0; + if (input2.capability & V4L2_INPUT_CAP_AUDIO) + chan->flags |= VIDEO_VC_AUDIO; + switch (input2.type) + { + case V4L2_INPUT_TYPE_TUNER: + chan->type = VIDEO_TYPE_TV; + break; + default: + case V4L2_INPUT_TYPE_CAMERA: + chan->type = VIDEO_TYPE_CAMERA; + break; + } + chan->norm = 0; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_STD, &std2); + if (err < 0) + dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %d\n",err); + if (err == 0) + { + sid = v4l2_video_std_confirm(&std2); + switch (sid) + { + case V4L2_STD_NTSC: + chan->norm = VIDEO_MODE_NTSC; + break; + case V4L2_STD_PAL: + chan->norm = VIDEO_MODE_PAL; + break; + case V4L2_STD_SECAM: + chan->norm = VIDEO_MODE_SECAM; + break; + } + } + break; + } + case VIDIOCSCHAN: /* set input */ + { + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_INPUT, arg); + if (err < 0) + dprintk("VIDIOCSCHAN / VIDIOC_S_INPUT: %d\n",err); + break; + } + case VIDIOCGPICT: /* get tone controls & partial capture format */ + { + struct video_picture *pict = arg; + struct v4l2_format fmt2; + + pict->brightness = get_v4l_control(vfl, inode, file, + V4L2_CID_BRIGHTNESS); + pict->hue = get_v4l_control(vfl, inode, file, + V4L2_CID_HUE); + pict->contrast = get_v4l_control(vfl, inode, file, + V4L2_CID_CONTRAST); + pict->colour = get_v4l_control(vfl, inode, file, + V4L2_CID_SATURATION); + pict->whiteness = get_v4l_control(vfl, inode, file, + V4L2_CID_WHITENESS); + fmt2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2); + if (err < 0) { + dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %d\n",err); + break; + } + pict->depth = fmt2.fmt.pix.depth; + pict->palette = pixelformat_to_palette( + fmt2.fmt.pix.pixelformat); + if (pict->palette == VIDEO_PALETTE_RGB555) + pict->depth = 15; + break; + } + case VIDIOCSPICT: /* set tone controls & partial capture format */ + { + struct video_picture *pict = arg; + struct v4l2_format fmt2; + struct v4l2_framebuffer fbuf2; + + set_v4l_control(vfl, inode, file, + V4L2_CID_BRIGHTNESS, pict->brightness); + set_v4l_control(vfl, inode, file, + V4L2_CID_HUE, pict->hue); + set_v4l_control(vfl, inode, file, + V4L2_CID_CONTRAST, pict->contrast); + set_v4l_control(vfl, inode, file, + V4L2_CID_SATURATION, pict->colour); + set_v4l_control(vfl, inode, file, + V4L2_CID_WHITENESS, pict->whiteness); + err = 0; + fmt2.type = V4L2_BUF_TYPE_CAPTURE; + vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2); + if (err < 0) + dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %d\n",err); + if (fmt2.fmt.pix.pixelformat != + palette_to_pixelformat(pict->palette)) + { + fmt2.fmt.pix.pixelformat = palette_to_pixelformat( + pict->palette); + fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FMT, &fmt2); + if (err < 0) + dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n",err); + } + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FBUF, &fbuf2); + if (err < 0) + dprintk("VIDIOCSPICT / VIDIOC_G_FBUF: %d\n",err); + if (fbuf2.fmt.pixelformat != + palette_to_pixelformat(pict->palette)) { + fbuf2.fmt.pixelformat = palette_to_pixelformat( + pict->palette); + fbuf2.fmt.flags |= V4L2_FMT_FLAG_INTERLACED; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FBUF, &fbuf2); + if (err < 0) + dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n",err); + } + break; + } + case VIDIOCGTUNER: /* get tuner information */ + { + struct video_tuner *tun = arg; + struct v4l2_tuner tun2; + int i; + int sid; + + i = find_tuner(vfl, inode, file, tun->tuner); + if (i < 0) + { + err = i; + break; + } + tun2.input = i; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) { + dprintk("VIDIOCGTUNER / VIDIOC_G_TUNER: %d\n",err); + break; + } + memcpy(tun->name, tun2.name, + min(sizeof(tun->name), sizeof(tun2.name))); + tun->name[sizeof(tun->name) - 1] = 0; + tun->rangelow = tun2.rangelow; + tun->rangehigh = tun2.rangehigh; + tun->flags = 0; + tun->mode = VIDEO_MODE_AUTO; + sid = v4l2_video_std_confirm(&tun2.std); + switch (sid) + { + case V4L2_STD_NTSC: + tun->flags = VIDEO_TUNER_NTSC; + tun->mode = VIDEO_MODE_NTSC; + break; + case V4L2_STD_PAL: + tun->flags = VIDEO_TUNER_PAL; + tun->mode = VIDEO_MODE_PAL; + break; + case V4L2_STD_SECAM: + tun->flags = VIDEO_TUNER_SECAM; + tun->mode = VIDEO_MODE_SECAM; + break; + } + if (tun2.capability & V4L2_TUNER_CAP_LOW) + tun->flags |= VIDEO_TUNER_LOW; + if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) + tun->flags |= VIDEO_TUNER_STEREO_ON; + tun->signal = tun2.signal; + break; + } + case VIDIOCSTUNER: /* select a tuner input */ + { + int i; + + i = find_tuner(vfl, inode, file, (int)arg); + if (i < 0) + { + err = i; + break; + } + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_INPUT, &i); + if (err < 0) + dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err); + break; + } + case VIDIOCGFREQ: /* get frequency */ + { + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FREQ, arg); + if (err < 0) + dprintk("VIDIOCGFREQ / VIDIOC_G_FREQ: %d\n",err); + break; + } + case VIDIOCSFREQ: /* set frequency */ + { + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FREQ, arg); + if (err < 0) + dprintk("VIDIOCGFREQ / VIDIOC_S_FREQ: %d\n",err); + break; + } + case VIDIOCGAUDIO: /* get audio properties/controls */ + { + struct video_audio *aud = arg; + struct v4l2_audio aud2; + struct v4l2_queryctrl qctrl2; + struct v4l2_tuner tun2; + int v; + + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_AUDIO, &aud2); + if (err < 0) { + dprintk("VIDIOCGAUDIO / VIDIOC_G_AUDIO: %d\n",err); + break; + } + memcpy(aud->name, aud2.name, + min(sizeof(aud->name), sizeof(aud2.name))); + aud->name[sizeof(aud->name) - 1] = 0; + aud->audio = aud2.audio; + aud->flags = 0; + v = get_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_VOLUME); + if (v >= 0) + { + aud->volume = v; + aud->flags |= VIDEO_AUDIO_VOLUME; + } + v = get_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_BASS); + if (v >= 0) + { + aud->bass = v; + aud->flags |= VIDEO_AUDIO_BASS; + } + v = get_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_TREBLE); + if (v >= 0) + { + aud->treble = v; + aud->flags |= VIDEO_AUDIO_TREBLE; + } + v = get_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_BALANCE); + if (v >= 0) + { + aud->balance = v; + aud->flags |= VIDEO_AUDIO_BALANCE; + } + v = get_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_MUTE); + if (v >= 0) + { + if (v) + aud->flags |= VIDEO_AUDIO_MUTE; + aud->flags |= VIDEO_AUDIO_MUTABLE; + } + aud->step = 1; + qctrl2.id = V4L2_CID_AUDIO_VOLUME; + if (vfl->kernel_ioctl(inode, file, VIDIOC_QUERYCTRL, &qctrl2) == 0 && + !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) + aud->step = qctrl2.step; + aud->mode = 0; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) { + dprintk("VIDIOCGAUDIO / VIDIOC_G_TUNER: %d\n",err); + err = 0; + break; + } + if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2) + aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) + aud->mode = VIDEO_SOUND_STEREO; + else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO) + aud->mode = VIDEO_SOUND_MONO; + break; + } + case VIDIOCSAUDIO: /* set audio controls */ + { + struct video_audio *aud = arg; + struct v4l2_audio aud2; + struct v4l2_tuner tun2; + int i; + + aud2.audio = aud->audio; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_AUDIO, &aud2); + if (err < 0) { + dprintk("VIDIOCSAUDIO / VIDIOC_S_AUDIO: %d\n",err); + break; + } + + set_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_VOLUME, + aud->volume); + set_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_BASS, + aud->bass); + set_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_TREBLE, + aud->treble); + set_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_BALANCE, + aud->balance); + set_v4l_control(vfl, inode, file, V4L2_CID_AUDIO_MUTE, + !!(aud->flags & VIDEO_AUDIO_MUTE)); + + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_INPUT, &i); + if (err < 0) { + dprintk("VIDIOCSAUDIO / VIDIOC_G_INPUT: %d\n",err); + err = 0; + break; + } + tun2.input = i; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) + dprintk("VIDIOCSAUDIO / VIDIOC_G_TUNER: %d\n",err); + if (err == 0) + { + switch (aud->mode) + { + default: + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + tun2.audmode = V4L2_TUNER_MODE_MONO; + break; + case VIDEO_SOUND_STEREO: + tun2.audmode = V4L2_TUNER_MODE_STEREO; + break; + case VIDEO_SOUND_LANG2: + tun2.audmode = V4L2_TUNER_MODE_LANG2; + break; + } + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_TUNER, &tun2); + if (err < 0) + dprintk("VIDIOCSAUDIO / VIDIOC_S_TUNER: %d\n",err); + } + err = 0; + break; + } +#if 0 + case VIDIOCGMBUF: /* get mmap parameters */ + { + struct video_mbuf *mbuf = arg; + struct v4l2_requestbuffers reqbuf2; + struct v4l2_buffer buf2; + struct v4l2_format fmt2, fmt2o; + struct v4l2_capability cap2; + int i; + + /* Set the format to maximum dimensions */ + if ((err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYCAP, &cap2)) < 0) + break; + fmt2o.type = V4L2_BUF_TYPE_CAPTURE; + if ((err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2o)) < 0) + break; + fmt2 = fmt2o; + fmt2.fmt.pix.width = cap2.maxwidth; + fmt2.fmt.pix.height = cap2.maxheight; + fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; + if ((err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FMT, &fmt2)) < 0) + break; + reqbuf2.count = 2; /* v4l always used two buffers */ + reqbuf2.type = V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG; + err = vfl->kernel_ioctl(inode, file, VIDIOC_REQBUFS, &reqbuf2); + if (err < 0 || reqbuf2.count < 2 || reqbuf2.type + != (V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG)) + {/* Driver doesn't support v4l back-compatibility */ + fmt2o.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; + vfl->kernel_ioctl(inode, file, VIDIOC_S_FMT, &fmt2o); + reqbuf2.count = 1; + reqbuf2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_REQBUFS, &reqbuf2); + if (err < 0) + { + err = -EINVAL; + break; + } + printk(KERN_INFO"V4L2: Device \"%s\" doesn't support" + " v4l memory mapping\n", vfl->name); + } + buf2.index = 0; + buf2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYBUF, &buf2); + mbuf->size = buf2.length * reqbuf2.count; + mbuf->frames = reqbuf2.count; + memset(mbuf->offsets, 0, sizeof(mbuf->offsets)); + for (i = 0; i < mbuf->frames; ++i) + mbuf->offsets[i] = i * buf2.length; + break; + } +#endif + case VIDIOCMCAPTURE: /* capture a frame */ + { + struct video_mmap *mm = arg; + struct v4l2_buffer buf2; + struct v4l2_format fmt2; + + fmt2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_G_FMT, &fmt2); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_G_FMT: %d\n",err); + break; + } + if (mm->width != fmt2.fmt.pix.width || + mm->height != fmt2.fmt.pix.height || + palette_to_pixelformat(mm->format) != + fmt2.fmt.pix.pixelformat) + {/* New capture format... */ + fmt2.fmt.pix.width = mm->width; + fmt2.fmt.pix.height = mm->height; + fmt2.fmt.pix.pixelformat = + palette_to_pixelformat(mm->format); + fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; + err = vfl->kernel_ioctl(inode, file, VIDIOC_S_FMT, &fmt2); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_S_FMT: %d\n",err); + break; + } + } + buf2.index = mm->frame; + buf2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYBUF, &buf2); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_QUERYBUF: %d\n",err); + break; + } + err = vfl->kernel_ioctl(inode, file, VIDIOC_QBUF, &buf2); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_QBUF: %d\n",err); + break; + } + err = vfl->kernel_ioctl(inode, file, VIDIOC_STREAMON, &buf2.type); + if (err < 0) + dprintk("VIDIOCMCAPTURE / VIDIOC_STREAMON: %d\n",err); + break; + } + case VIDIOCSYNC: /* wait for a frame */ + { + int *i = arg; + struct v4l2_buffer buf2; + + buf2.index = *i; + buf2.type = V4L2_BUF_TYPE_CAPTURE; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYBUF, &buf2); + if (err < 0) { + /* No such buffer */ + dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); + break; + } + if (!(buf2.flags & V4L2_BUF_FLAG_MAPPED)) { + /* Buffer is not mapped */ + err = -EINVAL; + break; + } + + /* Loop as long as the buffer is queued, but not done */ + while ((buf2.flags & + (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) + == V4L2_BUF_FLAG_QUEUED) + { + err = simple_select(file); + if (err < 0 || /* error or sleep was interrupted */ + err == 0) /* timeout? Shouldn't occur. */ + break; + err = vfl->kernel_ioctl(inode, file, VIDIOC_QUERYBUF, &buf2); + if (err < 0) + dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); + } + if (!(buf2.flags & V4L2_BUF_FLAG_DONE)) /* not done */ + break; + do { + err = vfl->kernel_ioctl(inode, file, VIDIOC_DQBUF, &buf2); + if (err < 0) + dprintk("VIDIOCSYNC / VIDIOC_DQBUF: %d\n",err); + } while (err == 0 && buf2.index != *i); + break; + } + case VIDIOCGUNIT: /* get related device minors */ + /* No translation */ + break; + case VIDIOCGCAPTURE: /* */ + /* No translation, yet... */ + printk(KERN_INFO"v4l1-compat: VIDIOCGCAPTURE not implemented." + " Send patches to bdirks@pacbell.net :-)\n"); + break; + case VIDIOCSCAPTURE: /* */ + /* No translation, yet... */ + printk(KERN_INFO"v4l1-compat: VIDIOCSCAPTURE not implemented." + " Send patches to bdirks@pacbell.net :-)\n"); + break; + } + return err; +} + +/* + * Module init and cleanup + */ +int v4l1_compat_init(void) +{ + return v4l2_compat_register(v4l_compat_translate_ioctl); +} + +void v4l1_compat_fini(void) +{ + v4l2_compat_unregister(v4l_compat_translate_ioctl); +} + +module_init(v4l1_compat_init); +module_exit(v4l1_compat_fini); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nru a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/media/video/v4l2-common.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,751 @@ +/* + * Video for Linux Two + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * This file replaces the videodev.c file that comes with the + * regular kernel distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Bill Dirks + * based on code by Alan Cox, + * + */ + +/* + * Video capture interface for Linux + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Author: Alan Cox, + * + * Fixes: + */ + +/* + * Video4linux 1/2 integration by Justin Schoeman + * + * 2.4 PROCFS support ported from 2.4 kernels by + * Iñaki García Etxebarria + * Makefile fix by "W. Michael Petullo" + * 2.4 devfs support ported from 2.4 kernels by + * Dan Merillat + * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#if defined(CONFIG_UST) || defined(CONFIG_UST_MODULE) +#include +#endif + +#include +#include + +MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Justin Schoeman, Gerd Knorr"); +MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers"); +MODULE_LICENSE("GPL"); + +static struct v4l2_clock *masterclock; + +/* + * CONTROL CATEGORIES + */ +void +v4l2_fill_ctrl_category(struct v4l2_queryctrl *qc) +{ + if ((qc->id >= V4L2_CID_BRIGHTNESS && + qc->id <= V4L2_CID_HUE) || + (qc->id >= V4L2_CID_BLACK_LEVEL && + qc->id <= V4L2_CID_LASTP1-1)) + { + qc->category = V4L2_CTRL_CAT_VIDEO; + strcpy(qc->group, "Video"); + } else + if ((qc->id >= V4L2_CID_AUDIO_VOLUME && + qc->id <= V4L2_CID_AUDIO_LOUDNESS)) + { + qc->category = V4L2_CTRL_CAT_AUDIO; + strcpy(qc->group, "Audio"); + } else + if ((qc->id >= V4L2_CID_EFFECT_BASE && + qc->id <= V4L2_CID_EFFECT_BASE + 10000)) + { + qc->category = V4L2_CTRL_CAT_EFFECT; + strcpy(qc->group, "Effect"); + } else + { + strcpy(qc->group, "Private"); + } +} + +/* + * + * V 4 L 2 D R I V E R H E L P E R A P I + * + */ + +void +v4l2_version(int *major, int *minor) +{ + *major = V4L2_MAJOR_VERSION; + *minor = V4L2_MINOR_VERSION; +} + + +/* + * memory management + */ +struct page *kvirt_to_pa(unsigned long adr) +{ + struct page *ret = NULL; + pmd_t *pmd; + pte_t *pte; + pgd_t *pgd; + + pgd = pgd_offset_k(adr); + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + pte = pte_offset(pmd, adr); + if (pte_present(*pte)) { + ret = pte_page(*pte); + } + } + } + + return ret; +} + + +struct page * +v4l2_vmalloc_to_page(void *virt) +{ + return kvirt_to_pa((unsigned long) virt); +} + +#if 0 +/* + * this is obsolete, virt_to_bus() is not supported any more on all + * architectures. You should use pci dma mapping API instead (see + * Documentation/DMA-mapping.txt) + */ +unsigned long +v4l2_vmalloc_to_bus(void *virt) +{ + struct page *page; + unsigned long kva, ret; + + page = kvirt_to_pa((unsigned long) virt); + kva = ((unsigned long)page_address(page)) | (((unsigned long) virt) & (PAGE_SIZE - 1)); + ret = virt_to_bus((void *) kva); + + return ret; +} +#endif + +/* + * Simple queue management + */ +static rwlock_t rw_lock_unlocked = RW_LOCK_UNLOCKED; +void +v4l2_q_init(struct v4l2_queue *q) +{ + if (q == NULL) + return; + q->qlock = rw_lock_unlocked; + q->forw = (struct v4l2_q_node *)q; + q->back = (struct v4l2_q_node *)q; +} +void +v4l2_q_add_head(struct v4l2_queue *q, struct v4l2_q_node *node) +{ + unsigned long flags; + if (q == NULL || node == NULL) + return; + if (q->forw == NULL || q->back == NULL) + v4l2_q_init(q); + write_lock_irqsave(&(q->qlock), flags); + node->forw = q->forw; + node->back = (struct v4l2_q_node *)q; + q->forw->back = node; + q->forw = node; + write_unlock_irqrestore(&(q->qlock), flags); +} +void +v4l2_q_add_tail(struct v4l2_queue *q, struct v4l2_q_node *node) +{ + unsigned long flags; + if (q == NULL || node == NULL) + return; + if (q->forw == NULL || q->back == NULL) + v4l2_q_init(q); + write_lock_irqsave(&(q->qlock), flags); + node->forw = (struct v4l2_q_node *)q; + node->back = q->back; + q->back->forw = node; + q->back = node; + write_unlock_irqrestore(&(q->qlock), flags); +} +void * +v4l2_q_del_head(struct v4l2_queue *q) +{ + unsigned long flags; + struct v4l2_q_node *node; + if (q == NULL) + return NULL; + write_lock_irqsave(&(q->qlock), flags); + if (q->forw == NULL || q->back == NULL || + q->forw == (struct v4l2_q_node *)q || + q->back == (struct v4l2_q_node *)q) + { + write_unlock_irqrestore(&(q->qlock), flags); + return NULL; + } + node = q->forw; + node->forw->back = (struct v4l2_q_node *)q; + q->forw = node->forw; + node->forw = NULL; + node->back = NULL; + write_unlock_irqrestore(&(q->qlock), flags); + return node; +} +void * +v4l2_q_del_tail(struct v4l2_queue *q) +{ + unsigned long flags; + struct v4l2_q_node *node; + if (q == NULL) + return NULL; + write_lock_irqsave(&(q->qlock), flags); + if (q->forw == NULL || q->back == NULL || + q->forw == (struct v4l2_q_node *)q || + q->back == (struct v4l2_q_node *)q) + { + write_unlock_irqrestore(&(q->qlock), flags); + return NULL; + } + node = q->back; + node->back->forw = (struct v4l2_q_node *)q; + q->back = node->back; + node->forw = NULL; + node->back = NULL; + write_unlock_irqrestore(&(q->qlock), flags); + return node; +} +void * +v4l2_q_peek_head(struct v4l2_queue *q) +{ + unsigned long flags; + struct v4l2_q_node *node; + read_lock_irqsave(&(q->qlock), flags); + if (q == NULL || q->forw == NULL || q->forw == (struct v4l2_q_node *)q) + { + read_unlock_irqrestore(&(q->qlock), flags); + return NULL; + } + node = q->forw; + read_unlock_irqrestore(&(q->qlock), flags); + return node; +} +void * +v4l2_q_peek_tail(struct v4l2_queue *q) +{ + unsigned long flags; + struct v4l2_q_node *node; + read_lock_irqsave(&(q->qlock), flags); + if (q == NULL || q->back == NULL || q->back == (struct v4l2_q_node *)q) + { + read_unlock_irqrestore(&(q->qlock), flags); + return NULL; + } + node = q->back; + read_unlock_irqrestore(&(q->qlock), flags); + return node; +} +void * +v4l2_q_yank_node(struct v4l2_queue *q, struct v4l2_q_node *node) +{ + unsigned long flags; + struct v4l2_q_node *t; + if (v4l2_q_peek_head(q) == NULL || node == NULL) + return NULL; + write_lock_irqsave(&(q->qlock), flags); + for (t = q->forw; t != (struct v4l2_q_node *)q; t = t->forw) + if (t == node) + { + node->back->forw = node->forw; + node->forw->back = node->back; + node->forw = NULL; + node->back = NULL; + write_unlock_irqrestore(&(q->qlock), flags); + return node; + } + write_unlock_irqrestore(&(q->qlock), flags); + return NULL; +} +int +v4l2_q_last(struct v4l2_queue *q) +{ +/* This function by Olivier Carmona */ + + unsigned long flags; + read_lock_irqsave(&(q->qlock), flags); + if (q == NULL) + { + read_unlock_irqrestore(&(q->qlock), flags); + return -1; + } + if (q->forw == NULL || q->back == NULL || + q->forw == (struct v4l2_q_node *)q || + q->back == (struct v4l2_q_node *)q) + { + read_unlock_irqrestore(&(q->qlock), flags); + return -1; + } + if (q->forw == q->back) + { + read_unlock_irqrestore(&(q->qlock), flags); + return 1; + } + read_unlock_irqrestore(&(q->qlock), flags); + return 0; +} + + +/* + * Math functions + */ + +#if 0 +u32 +v4l2_math_div6432(u64 a, u32 d, u32 *r) +{ + u32 q, m; +#ifdef __i386__ +/* Danger: This function will fault if the quotient exceeds (1<<32) - 1 */ + __asm__ __volatile__ ( + " movl %2,%%eax\n" + " movl %3,%%edx\n" + " divl %4\n" + " movl %%eax,%0\n" + " movl %%edx,%1\n" + : "=g" (q), "=g" (m) + : "g" ((u32)a), "g" ((u32)(a >> 32)), "g" (d) + : "eax", "edx" + ); +#else + q = a / d; + m = a % d; +#endif + if (r) *r = m; + return q; +} +#endif + +unsigned long +v4l2_timestamp_divide(stamp_t t, unsigned long p_100ns) +{ +#if 0 + /* Note: 't' is in 1ns units, 'p_100ns' is in 100ns units, */ + /* and the quotient is rounded */ + u64 p; + + p = (u64)p_100ns * 100; /* 1ns units */ + t >>= 6; /* /64 to allow p_100ns longer than 4 secs. */ + p >>= 6; /* to keep quotient the same */ + return v4l2_math_div6432((u64)t + (p >> 1), (u32)p, NULL); +#else + u32 p; + + p = ((u64)p_100ns * 100) >> 6; + t = (t >> 6) + (p >> 1); + do_div(t,p); + return t; +#endif +} + +/* Force the timestamp to be an integer multiple of p_100ns */ +unsigned long +v4l2_timestamp_correct(stamp_t *t, unsigned long p_100ns) +{ + /* Note: 't' is in 1ns units, 'p_100ns' is in 100ns units */ + unsigned long n; + + n = v4l2_timestamp_divide((u64)*t, p_100ns); + *t = (u64)p_100ns * n * 100; + return n; +} + +/* + * Master clock operations + */ + +int +v4l2_masterclock_register(struct v4l2_clock *clock) +{ + if (clock == NULL || clock->gettime == NULL) + return -1; + if (masterclock != NULL) + return -1; + masterclock = clock; + MOD_INC_USE_COUNT; + return 0; +} +void +v4l2_masterclock_unregister(struct v4l2_clock *clock) +{ + if (clock != masterclock) + return; + masterclock = NULL; + MOD_DEC_USE_COUNT; +} +void +v4l2_masterclock_gettime(stamp_t *curr) +{ + if (masterclock) + masterclock->gettime(curr); + else + { +#if defined(CONFIG_UST) || defined(CONFIG_UST_MODULE) + ust_gettime(curr); +#else + struct timeval t; + stamp_t stamp; + do_gettimeofday(&t); + stamp = (stamp_t)t.tv_sec * 1000000 + t.tv_usec; + stamp *= 1000; + *curr = stamp; +#endif + } +} + +/* + * Video Standard Operations (contributed by Michael Schimek) + */ + +/* This is the recommended method to deal with the framerate fields. More + sophisticated drivers will access the fields directly. */ +unsigned int +v4l2_video_std_fps(struct v4l2_standard *vs) +{ + if (vs->framerate.numerator > 0) + return (((vs->framerate.denominator << 8) / + vs->framerate.numerator) + + (1 << 7)) / (1 << 8); + return 0; +} + +/* Compute the time per frame in 100ns units */ +unsigned long +v4l2_video_std_tpf(struct v4l2_standard *vs) +{ +#if 0 + return v4l2_math_div6432( + (u64)vs->framerate.numerator * 10000000 + + vs->framerate.denominator / 2, + vs->framerate.denominator, + NULL); +#else + u64 a; + u32 b; + + a = (u64)vs->framerate.numerator * 10000000 + vs->framerate.denominator / 2; + b = vs->framerate.denominator; + do_div(a,b); + return a; +#endif +} + +/* Used only in v4l2_video_std_confirm() */ +static void +catc1p2e6(__u8 *s, char c, int n) +{ + n /= 10000; + sprintf(s + strlen(s), "%c%d.%02d", c, n / 100, n % 100); +} + +/* Verify the validity of the parameters of a v4l2_standard structure and + create the name and id from the other fields. It does not relieve a + driver from examining if it can fulfill the request. Returns an + errno < 0 if inconsistent, 0 if an unknown but maybe usable format, + or the V4L2_STD_XXX_X value if a known standard. */ +int +v4l2_video_std_confirm(struct v4l2_standard *vs) +{ + unsigned int rate = 0; + unsigned int lines = vs->framelines; + int std = 0; + + strcpy(vs->name, "Unknown"); + if (vs->reserved1 || vs->reserved2) + return -EINVAL; + + if (vs->framerate.numerator > 0 && + vs->framerate.denominator > 0) + rate = v4l2_video_std_fps(vs); + + if (vs->framelines >= 624 && vs->framelines <= 626) + lines = 625; + else if (vs->framelines >= 524 && vs->framelines <= 526) + lines = 525; + + if (rate == 0 || lines == 0 || rate > 200) + return -EINVAL; + + switch (vs->colorstandard) + { + case V4L2_COLOR_STD_PAL: + strcpy(vs->name, "PAL"); + if (rate == 25 && lines == 625) + switch (vs->colorstandard_data.pal.colorsubcarrier) + { + case V4L2_COLOR_SUBC_PAL_N: + strcpy(vs->name, "PAL-N"); + if (vs->transmission & ~V4L2_TRANSM_STD_N) + return -EINVAL; + return V4L2_STD_PAL_N; + case V4L2_COLOR_SUBC_PAL: + if (vs->transmission & + ~(V4L2_TRANSM_STD_B | V4L2_TRANSM_STD_G | + V4L2_TRANSM_STD_H | V4L2_TRANSM_STD_I | + V4L2_TRANSM_STD_D)) + return -EINVAL; + std = V4L2_STD_PAL; + goto addtransm; + } + else if (rate == 30 && lines == 525) + switch (vs->colorstandard_data.pal.colorsubcarrier) + { + case V4L2_COLOR_SUBC_PAL_M: + strcpy(vs->name, "PAL-M"); + if (vs->transmission & ~V4L2_TRANSM_STD_M) + return -EINVAL; + return V4L2_STD_PAL_M; + case V4L2_COLOR_SUBC_PAL: + strcpy(vs->name, "PAL-60"); + if (vs->transmission) + return -EINVAL; + return V4L2_STD_PAL_60; + } + if (vs->transmission) + return -EINVAL; + catc1p2e6(vs->name, ' ', + vs->colorstandard_data.pal.colorsubcarrier); + break; + + case V4L2_COLOR_STD_NTSC: + strcpy(vs->name, "NTSC"); + if (rate == 25 && lines == 625) + switch (vs->colorstandard_data.ntsc.colorsubcarrier) + { + case V4L2_COLOR_SUBC_NTSC: + strcpy(vs->name, "NTSC-N"); + if (vs->transmission & ~V4L2_TRANSM_STD_N) + return -EINVAL; + return V4L2_STD_NTSC_N; + } + else if (rate == 30 && lines == 525) + switch (vs->colorstandard_data.ntsc.colorsubcarrier) + { + case V4L2_COLOR_SUBC_NTSC: + if (vs->transmission & ~V4L2_TRANSM_STD_M) + return -EINVAL; + std = V4L2_STD_NTSC; + goto addtransm; + case V4L2_COLOR_SUBC_PAL: + strcpy(vs->name, "NTSC-44"); + if (vs->transmission) + return -EINVAL; + return V4L2_STD_NTSC_44; + } + if (vs->transmission) + return -EINVAL; + catc1p2e6(vs->name, ' ', + vs->colorstandard_data.ntsc.colorsubcarrier); + break; + + case V4L2_COLOR_STD_SECAM: + strcpy(vs->name, "SECAM"); + if (rate == 25 && lines == 625) + if (vs->colorstandard_data.secam.f0b == + V4L2_COLOR_SUBC_SECAMB && + vs->colorstandard_data.secam.f0r == + V4L2_COLOR_SUBC_SECAMR) + { + if (vs->transmission & + ~(V4L2_TRANSM_STD_B | V4L2_TRANSM_STD_D | + V4L2_TRANSM_STD_G | V4L2_TRANSM_STD_K | + V4L2_TRANSM_STD_K1 | V4L2_TRANSM_STD_L)) + return -EINVAL; + std = V4L2_STD_SECAM; + goto addtransm; + } + if (vs->transmission) + return -EINVAL; + catc1p2e6(vs->name, ' ', vs->colorstandard_data.secam.f0b); + catc1p2e6(vs->name, '/', vs->colorstandard_data.secam.f0r); + break; + + default: + return -EINVAL; + } + + sprintf(vs->name + strlen(vs->name), " %d/%d", + vs->framelines, rate); + + return std; + + addtransm: + if (vs->transmission) strcat(vs->name, "-"); + + if (vs->transmission & V4L2_TRANSM_STD_B) strcat(vs->name, "B/"); + if (vs->transmission & V4L2_TRANSM_STD_G) strcat(vs->name, "G/"); + if (vs->transmission & V4L2_TRANSM_STD_H) strcat(vs->name, "H/"); + if (vs->transmission & V4L2_TRANSM_STD_I) strcat(vs->name, "I/"); + if (vs->transmission & V4L2_TRANSM_STD_D) strcat(vs->name, "D/"); + if (vs->transmission & V4L2_TRANSM_STD_K) strcat(vs->name, "K/"); + if (vs->transmission & V4L2_TRANSM_STD_K1) strcat(vs->name, "K1/"); + if (vs->transmission & V4L2_TRANSM_STD_L) strcat(vs->name, "L/"); + if (vs->transmission & V4L2_TRANSM_STD_M) strcat(vs->name, "M/"); + if (vs->transmission & V4L2_TRANSM_STD_N) strcat(vs->name, "N/"); + + if (vs->name[strlen(vs->name) - 1] == '/') + vs->name[strlen(vs->name) - 1] = 0; + + return std; +} + +/* Fill in the fields of a v4l2_standard structure according to the + 'id' and 'transmission' parameters. Returns negative on error. */ +int +v4l2_video_std_construct(struct v4l2_standard *vs, + int id, __u32 transmission) +{ + memset(vs, 0, sizeof(struct v4l2_standard)); + + vs->framerate.numerator = 1; + vs->framerate.denominator = 25; + vs->framelines = 625; + + switch (id) + { + case V4L2_STD_PAL_60: + vs->framerate.numerator = 1001; + vs->framerate.denominator = 30000; + vs->framelines = 525; + /* fall thru */ + case V4L2_STD_PAL: + vs->colorstandard = V4L2_COLOR_STD_PAL; + vs->colorstandard_data.pal.colorsubcarrier = + V4L2_COLOR_SUBC_PAL; + break; + case V4L2_STD_PAL_M: + vs->framerate.numerator = 1001; + vs->framerate.denominator = 30000; + vs->framelines = 525; + vs->colorstandard = V4L2_COLOR_STD_PAL; + vs->colorstandard_data.pal.colorsubcarrier = + V4L2_COLOR_SUBC_PAL_M; + break; + case V4L2_STD_PAL_N: + vs->colorstandard = V4L2_COLOR_STD_PAL; + vs->colorstandard_data.pal.colorsubcarrier = + V4L2_COLOR_SUBC_PAL_N; + break; + + case V4L2_STD_NTSC: + vs->framerate.numerator = 1001; + vs->framerate.denominator = 30000; + vs->framelines = 525; + /* fall thru */ + case V4L2_STD_NTSC_N: + vs->colorstandard = V4L2_COLOR_STD_NTSC; + vs->colorstandard_data.ntsc.colorsubcarrier = + V4L2_COLOR_SUBC_NTSC; + break; + case V4L2_STD_NTSC_44: + vs->framerate.numerator = 1001; + vs->framerate.denominator = 30000; + vs->framelines = 525; + vs->colorstandard = V4L2_COLOR_STD_NTSC; + vs->colorstandard_data.ntsc.colorsubcarrier = + V4L2_COLOR_SUBC_PAL; + break; + + case V4L2_STD_SECAM: + vs->colorstandard = V4L2_COLOR_STD_SECAM; + vs->colorstandard_data.secam.f0b = V4L2_COLOR_SUBC_SECAMB; + vs->colorstandard_data.secam.f0r = V4L2_COLOR_SUBC_SECAMR; + break; + + default: + return -EINVAL; + } + + vs->transmission = transmission; + + return v4l2_video_std_confirm(vs); +} + + +/*---------------------------------------*/ + +EXPORT_SYMBOL(v4l2_version); +EXPORT_SYMBOL(v4l2_fill_ctrl_category); +EXPORT_SYMBOL(v4l2_vmalloc_to_page); +EXPORT_SYMBOL(v4l2_q_init); +EXPORT_SYMBOL(v4l2_q_add_head); +EXPORT_SYMBOL(v4l2_q_add_tail); +EXPORT_SYMBOL(v4l2_q_del_head); +EXPORT_SYMBOL(v4l2_q_del_tail); +EXPORT_SYMBOL(v4l2_q_peek_head); +EXPORT_SYMBOL(v4l2_q_peek_tail); +EXPORT_SYMBOL(v4l2_q_yank_node); +EXPORT_SYMBOL(v4l2_q_last); +#if 0 +EXPORT_SYMBOL(v4l2_math_div6432); +#endif +EXPORT_SYMBOL(v4l2_timestamp_divide); +EXPORT_SYMBOL(v4l2_timestamp_correct); +EXPORT_SYMBOL(v4l2_masterclock_register); +EXPORT_SYMBOL(v4l2_masterclock_unregister); +EXPORT_SYMBOL(v4l2_masterclock_gettime); +EXPORT_SYMBOL(v4l2_video_std_fps); +EXPORT_SYMBOL(v4l2_video_std_tpf); +EXPORT_SYMBOL(v4l2_video_std_confirm); +EXPORT_SYMBOL(v4l2_video_std_construct); +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nru a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c --- a/drivers/media/video/videodev.c Wed Feb 13 20:03:35 2002 +++ b/drivers/media/video/videodev.c Wed Feb 13 20:03:35 2002 @@ -25,15 +25,14 @@ #include #include #include -#include #include - +#include #include #include #include -#include - +#include +#include #define VIDEO_NUM_DEVICES 256 @@ -70,7 +69,7 @@ static ssize_t video_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct video_device *vfl = video_devdata(file); if(vfl->read) return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); else @@ -86,13 +85,18 @@ static ssize_t video_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct video_device *vfl = video_devdata(file); if(vfl->write) return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK); else return 0; } +struct video_device* video_devdata(struct file *file) +{ + return video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; +} + /* * Poll to see if we're readable, can probably be used for timing on incoming * frames, etc.. @@ -100,7 +104,7 @@ static unsigned int video_poll(struct file *file, poll_table * wait) { - struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct video_device *vfl = video_devdata(file); if(vfl->poll) return vfl->poll(vfl, file, wait); else @@ -133,6 +137,22 @@ goto error_out; } } + if (vfl->fops) { + int err = 0; + struct file_operations *old_fops; + + unlock_kernel(); + old_fops = file->f_op; + file->f_op = fops_get(vfl->fops); + if(file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; + } if(vfl->busy) { retval = -EBUSY; goto error_out; @@ -170,7 +190,7 @@ { struct video_device *vfl; lock_kernel(); - vfl=video_device[MINOR(inode->i_rdev)]; + vfl = video_devdata(file); if(vfl->close) vfl->close(vfl); vfl->busy=0; @@ -183,7 +203,7 @@ static int video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *vfl=video_device[MINOR(inode->i_rdev)]; + struct video_device *vfl = video_devdata(file); int err=vfl->ioctl(vfl, cmd, (void *)arg); if(err!=-ENOIOCTLCMD) @@ -203,7 +223,7 @@ int video_mmap(struct file *file, struct vm_area_struct *vma) { int ret = -EINVAL; - struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct video_device *vfl = video_devdata(file); if(vfl->mmap) { lock_kernel(); ret = vfl->mmap(vfl, (char *)vma->vm_start, @@ -214,6 +234,104 @@ } /* + * register/unregister v4l2->v4l1 back compatibility layer hook + */ +static v4l2_ioctl_compat compat_hook = NULL; + +int +v4l2_compat_register(v4l2_ioctl_compat hook) +{ + if (compat_hook != NULL) + return -EBUSY; + compat_hook = hook; + printk(KERN_INFO"V4L2: v4l1 backward compatibility enabled.\n"); + return 0; +} + +void +v4l2_compat_unregister(v4l2_ioctl_compat hook) +{ + if (compat_hook != hook) + BUG(); + compat_hook = NULL; + printk(KERN_INFO"V4L2: v4l1 backward compatibility disabled.\n"); +} + +/* + * ioctl helper function -- handles userspace copying + * also calls into the v4l2->v4l1 compatibility layer if needed + */ +int +video_generic_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vfl = video_devdata(file); + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + + if (vfl->kernel_ioctl == NULL) + return -EINVAL; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = (void *)arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + err = vfl->kernel_ioctl(inode, file, cmd, parg); + if (err == -ENOIOCTLCMD) { + if (_IOC_TYPE(cmd) == 'v') { + /* try to handle unknown v4l1 ioctls using the + v4l2->v4l1 compatibility layer */ + if (!compat_hook) + request_module("v4l1-compat"); + if (compat_hook) + err = compat_hook(inode, file, cmd, parg); + } + if (err == -ENOIOCTLCMD) + err = -EINVAL; + } + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + if (mbuf) + kfree(mbuf); + return err; +} + +/* * /proc support */ @@ -540,12 +658,9 @@ static void __exit videodev_exit(void) { -#ifdef MODULE #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) videodev_proc_destroy (); #endif -#endif - devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture"); } @@ -554,6 +669,11 @@ EXPORT_SYMBOL(video_register_device); EXPORT_SYMBOL(video_unregister_device); +EXPORT_SYMBOL(video_devdata); +EXPORT_SYMBOL(video_generic_ioctl); + +EXPORT_SYMBOL(v4l2_compat_register); +EXPORT_SYMBOL(v4l2_compat_unregister); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers"); diff -Nru a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c --- a/drivers/message/i2o/i2o_block.c Wed Feb 13 20:03:36 2002 +++ b/drivers/message/i2o/i2o_block.c Wed Feb 13 20:03:36 2002 @@ -282,6 +282,7 @@ if(req->cmd == READ) { + DEBUG("READ\n"); __raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); while(bh!=NULL) { @@ -321,6 +322,7 @@ } else if(req->cmd == WRITE) { + DEBUG("WRITE\n"); __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); while(bh!=NULL) { @@ -415,6 +417,7 @@ * It is now ok to complete the request. */ end_that_request_last( req ); + DEBUG("IO COMPLETED\n"); } /* @@ -493,7 +496,7 @@ __raw_writel(i2ob_context|(unit<<8), msg+8); __raw_writel(0, msg+12); __raw_writel(60<<16, msg+16); - + DEBUG("FLUSH"); i2o_post_message(c,m); return 0; } @@ -522,6 +525,7 @@ */ if(m[0] & (1<<13)) { + DEBUG("FAIL"); /* * FAILed message from controller * We increment the error count and abort it @@ -561,7 +565,7 @@ { spin_lock_irqsave(&io_request_lock, flags); dev->constipated=0; - DEBUG(("unconstipated\n")); + DEBUG("unconstipated\n"); if(i2ob_backlog_request(c, dev)==0) i2ob_request(dev->req_queue); spin_unlock_irqrestore(&io_request_lock, flags); @@ -861,7 +865,7 @@ * and tell them to fix their firmware :) */ default: - printk(KERN_INFO "%s: Received event %d we didn't register for\n" + printk(KERN_INFO "%s: Received event 0x%X we didn't register for\n" KERN_INFO " Blame the I2O card manufacturer 8)\n", i2ob_dev[unit].i2odev->dev_name, evt); break; @@ -1205,9 +1209,6 @@ if(!dev->i2odev) return 0; - /* Sync the device so we don't get errors */ - fsync_dev(inode->i_rdev); - if (dev->refcnt <= 0) printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); dev->refcnt--; @@ -1470,19 +1471,16 @@ { int i; - i2ob_queues[unit] = (struct i2ob_iop_queue*) - kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); + i2ob_queues[unit] = (struct i2ob_iop_queue*) kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); if(!i2ob_queues[unit]) { - printk(KERN_WARNING - "Could not allocate request queue for I2O block device!\n"); + printk(KERN_WARNING "Could not allocate request queue for I2O block device!\n"); return -1; } for(i = 0; i< MAX_I2OB_DEPTH; i++) { - i2ob_queues[unit]->request_queue[i].next = - &i2ob_queues[unit]->request_queue[i+1]; + i2ob_queues[unit]->request_queue[i].next = &i2ob_queues[unit]->request_queue[i+1]; i2ob_queues[unit]->request_queue[i].num = i; } @@ -1507,7 +1505,6 @@ static request_queue_t* i2ob_get_queue(kdev_t dev) { int unit = MINOR(dev)&0xF0; - return i2ob_dev[unit].req_queue; } @@ -1530,34 +1527,34 @@ if(c==NULL) continue; - /* - * The device list connected to the I2O Controller is doubly linked - * Here we traverse the end of the list , and start claiming devices - * from that end. This assures that within an I2O controller atleast - * the newly created volumes get claimed after the older ones, thus - * mapping to same major/minor (and hence device file name) after - * every reboot. - * The exception being: - * 1. If there was a TID reuse. - * 2. There was more than one I2O controller. - */ - - if(!bios) - { - for (d=c->devices;d!=NULL;d=d->next) - if(d->next == NULL) - b = d; - } - else - b = c->devices; + /* + * The device list connected to the I2O Controller is doubly linked + * Here we traverse the end of the list , and start claiming devices + * from that end. This assures that within an I2O controller atleast + * the newly created volumes get claimed after the older ones, thus + * mapping to same major/minor (and hence device file name) after + * every reboot. + * The exception being: + * 1. If there was a TID reuse. + * 2. There was more than one I2O controller. + */ - while(b != NULL) - { - d=b; - if(bios) - b = b->next; + if(!bios) + { + for (d=c->devices;d!=NULL;d=d->next) + if(d->next == NULL) + b = d; + } else - b = b->prev; + b = c->devices; + + while(b != NULL) + { + d=b; + if(bios) + b = b->next; + else + b = b->prev; if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) continue; diff -Nru a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c --- a/drivers/message/i2o/i2o_config.c Wed Feb 13 20:03:48 2002 +++ b/drivers/message/i2o/i2o_config.c Wed Feb 13 20:03:48 2002 @@ -45,7 +45,7 @@ static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; struct wait_queue *i2o_wait_queue; -#define MODINC(x,y) (x = x++ % y) +#define MODINC(x,y) ((x) = ((x) + 1) % (y)) struct i2o_cfg_info { diff -Nru a/drivers/message/i2o/i2o_core.c b/drivers/message/i2o/i2o_core.c --- a/drivers/message/i2o/i2o_core.c Wed Feb 13 20:03:33 2002 +++ b/drivers/message/i2o/i2o_core.c Wed Feb 13 20:03:33 2002 @@ -208,7 +208,7 @@ static DECLARE_MUTEX(evt_sem); static DECLARE_COMPLETION(evt_dead); -DECLARE_WAIT_QUEUE_HEAD(evt_wait); +static DECLARE_WAIT_QUEUE_HEAD(evt_wait); static struct notifier_block i2o_reboot_notifier = { @@ -2559,6 +2559,7 @@ int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2) { DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); + DECLARE_WAITQUEUE(wait, current); int complete = 0; int status; unsigned long flags = 0; @@ -2599,12 +2600,19 @@ * complete will be zero. From the point post_this returns * the wait_data may have been deleted. */ + + add_wait_queue(&wq_i2o_post, &wait); + set_current_state(TASK_INTERRUPTIBLE); if ((status = i2o_post_this(c, msg, len))==0) { - sleep_on_timeout(&wq_i2o_post, HZ * timeout); + schedule_timeout(HZ * timeout); } else + { + remove_wait_queue(&wq_i2o_post, &wait); return -EIO; - + } + remove_wait_queue(&wq_i2o_post, &wait); + if(signal_pending(current)) status = -EINTR; diff -Nru a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c --- a/drivers/mtd/ftl.c Wed Feb 13 20:03:42 2002 +++ b/drivers/mtd/ftl.c Wed Feb 13 20:03:42 2002 @@ -87,7 +87,7 @@ #define register_disk(dev, drive, minors, ops, size) \ do { (dev)->part[(drive)*(minors)].nr_sects = size; \ if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ - resetup_one_dev(dev, drive); } while (0); + resetup_one_dev(dev, drive); } while (0) #endif #if (LINUX_VERSION_CODE < 0x20320) diff -Nru a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c --- a/drivers/mtd/nand/nand_ecc.c Wed Feb 13 20:03:43 2002 +++ b/drivers/mtd/nand/nand_ecc.c Wed Feb 13 20:03:43 2002 @@ -207,3 +207,5 @@ EXPORT_SYMBOL(nand_calculate_ecc); EXPORT_SYMBOL(nand_correct_data); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/3c501.c b/drivers/net/3c501.c --- a/drivers/net/3c501.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/3c501.c Wed Feb 13 20:03:30 2002 @@ -419,7 +419,8 @@ if (el_debug > 2) printk("%s: Doing el_open()...", dev->name); - if ((retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev))) + if ((retval = request_irq(dev->irq, &el_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) return retval; spin_lock_irqsave(&lp->lock, flags); diff -Nru a/drivers/net/3c503.c b/drivers/net/3c503.c --- a/drivers/net/3c503.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/3c503.c Wed Feb 13 20:03:36 2002 @@ -348,7 +348,7 @@ outb_p(0x00, E33G_IDCFR); if (*irqp == probe_irq_off(cookie) /* It's a good IRQ line! */ && ((retval = request_irq(dev->irq = *irqp, - ei_interrupt, 0, dev->name, dev)) == 0)) + ei_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev)) == 0)) break; } } while (*++irqp); @@ -357,7 +357,7 @@ return retval; } } else { - if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev))) { return retval; } } diff -Nru a/drivers/net/3c505.c b/drivers/net/3c505.c --- a/drivers/net/3c505.c Wed Feb 13 20:03:54 2002 +++ b/drivers/net/3c505.c Wed Feb 13 20:03:54 2002 @@ -906,7 +906,8 @@ /* * install our interrupt service routine */ - if ((retval = request_irq(dev->irq, &elp_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, &elp_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) { printk(KERN_ERR "%s: could not allocate IRQ%d\n", dev->name, dev->irq); return retval; } diff -Nru a/drivers/net/3c507.c b/drivers/net/3c507.c --- a/drivers/net/3c507.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/3c507.c Wed Feb 13 20:03:56 2002 @@ -373,7 +373,8 @@ irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; - irqval = request_irq(irq, &el16_interrupt, 0, dev->name, dev); + irqval = request_irq(irq, &el16_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (irqval) { printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval); retval = -EAGAIN; diff -Nru a/drivers/net/3c509.c b/drivers/net/3c509.c --- a/drivers/net/3c509.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/3c509.c Wed Feb 13 20:03:30 2002 @@ -573,7 +573,8 @@ outw(RxReset, ioaddr + EL3_CMD); outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev); + i = request_irq(dev->irq, &el3_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; EL3WINDOW(0); diff -Nru a/drivers/net/3c515.c b/drivers/net/3c515.c --- a/drivers/net/3c515.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/3c515.c Wed Feb 13 20:03:35 2002 @@ -803,11 +803,13 @@ /* Corkscrew: Cannot share ISA resources. */ if (dev->irq == 0 || dev->dma == 0 - || request_irq(dev->irq, &corkscrew_interrupt, 0, - vp->product_name, dev)) return -EAGAIN; + || request_irq(dev->irq, &corkscrew_interrupt, + SA_SAMPLE_NET_RANDOM, + vp->product_name, dev)) return -EAGAIN; enable_dma(dev->dma); set_dma_mode(dev->dma, DMA_MODE_CASCADE); - } else if (request_irq(dev->irq, &corkscrew_interrupt, SA_SHIRQ, + } else if (request_irq(dev->irq, &corkscrew_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, vp->product_name, dev)) { return -EAGAIN; } diff -Nru a/drivers/net/3c523.c b/drivers/net/3c523.c --- a/drivers/net/3c523.c Wed Feb 13 20:03:52 2002 +++ b/drivers/net/3c523.c Wed Feb 13 20:03:52 2002 @@ -289,8 +289,8 @@ elmc_id_attn586(); /* disable interrupts */ - ret = request_irq(dev->irq, &elmc_interrupt, SA_SHIRQ | SA_SAMPLE_RANDOM, - dev->name, dev); + ret = request_irq(dev->irq, &elmc_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) { printk(KERN_ERR "%s: couldn't get irq %d\n", dev->name, dev->irq); elmc_id_reset586(); diff -Nru a/drivers/net/3c527.c b/drivers/net/3c527.c --- a/drivers/net/3c527.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/3c527.c Wed Feb 13 20:03:30 2002 @@ -409,7 +409,7 @@ * Grab the IRQ */ - i = request_irq(dev->irq, &mc32_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &mc32_interrupt, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) { release_region(dev->base_addr, MC32_IO_EXTENT); printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); diff -Nru a/drivers/net/3c59x.c b/drivers/net/3c59x.c --- a/drivers/net/3c59x.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/3c59x.c Wed Feb 13 20:03:29 2002 @@ -1578,7 +1578,8 @@ /* Use the now-standard shared IRQ implementation. */ if ((retval = request_irq(dev->irq, vp->full_bus_master_rx ? - &boomerang_interrupt : &vortex_interrupt, SA_SHIRQ, dev->name, dev))) { + &boomerang_interrupt : &vortex_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev))) { printk(KERN_ERR "%s: Could not reserve IRQ %d\n", dev->name, dev->irq); goto out; } diff -Nru a/drivers/net/7990.c b/drivers/net/7990.c --- a/drivers/net/7990.c Wed Feb 13 20:03:37 2002 +++ b/drivers/net/7990.c Wed Feb 13 20:03:37 2002 @@ -463,7 +463,7 @@ DECLARE_LL; /* Install the Interrupt handler. Or we could shunt this out to specific drivers? */ - if (request_irq(lp->irq, lance_interrupt, 0, lp->name, dev)) + if (request_irq(lp->irq, lance_interrupt, SA_SAMPLE_NET_RANDOM, lp->name, dev)) return -EAGAIN; res = lance_reset(dev); diff -Nru a/drivers/net/8139cp.c b/drivers/net/8139cp.c --- a/drivers/net/8139cp.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/8139cp.c Wed Feb 13 20:03:51 2002 @@ -980,7 +980,8 @@ cp_init_hw(cp); - rc = request_irq(dev->irq, cp_interrupt, SA_SHIRQ, dev->name, dev); + rc = request_irq(dev->irq, cp_interrupt, + SA_SAMPLE_NET_RANDOM | SA_SHIRQ, dev->name, dev); if (rc) goto err_out_hw; diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c --- a/drivers/net/8139too.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/8139too.c Wed Feb 13 20:03:29 2002 @@ -216,6 +216,7 @@ DFE538TX, DFE690TXD, FE2000VX, + ALLIED8139, RTL8129, } board_t; @@ -234,6 +235,7 @@ { "D-Link DFE-538TX (RealTek RTL8139)", RTL8139_CAPS }, { "D-Link DFE-690TXD (RealTek RTL8139)", RTL8139_CAPS }, { "AboCom FE2000VX (RealTek RTL8139)", RTL8139_CAPS }, + { "Allied Telesyn 8139 CardBus", RTL8139_CAPS }, { "RealTek RTL8129", RTL8129_CAPS }, }; @@ -248,6 +250,7 @@ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE538TX }, {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE690TXD }, {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FE2000VX }, + {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALLIED8139 }, #ifdef CONFIG_8139TOO_8129 {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, @@ -1255,7 +1258,8 @@ void *ioaddr = tp->mmio_addr; #endif - retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); + retval = request_irq (dev->irq, rtl8139_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) return retval; diff -Nru a/drivers/net/82596.c b/drivers/net/82596.c --- a/drivers/net/82596.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/82596.c Wed Feb 13 20:03:36 2002 @@ -973,7 +973,8 @@ DEB(DEB_OPEN,printk("%s: i596_open() irq %d.\n", dev->name, dev->irq)); - if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) { + if (request_irq(dev->irq, &i596_interrupt, SA_SAMPLE_NET_RANDOM, + "i82596", dev)) { printk("%s: IRQ %d not free\n", dev->name, dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in --- a/drivers/net/Config.in Wed Feb 13 20:03:33 2002 +++ b/drivers/net/Config.in Wed Feb 13 20:03:33 2002 @@ -9,6 +9,7 @@ tristate 'Bonding driver support' CONFIG_BONDING tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER tristate 'Universal TUN/TAP device driver support' CONFIG_TUN +bool 'Allow Net Devices to contribute to /dev/random' CONFIG_NET_RANDOM if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Ethertap network tap (OBSOLETE)' CONFIG_ETHERTAP fi diff -Nru a/drivers/net/a2065.c b/drivers/net/a2065.c --- a/drivers/net/a2065.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/a2065.c Wed Feb 13 20:03:30 2002 @@ -495,8 +495,8 @@ last_dev = dev; /* Install the Interrupt handler */ - ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, SA_SHIRQ, - dev->name, dev); + ret = request_irq(IRQ_AMIGA_PORTS, lance_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) return ret; /* Stop the Lance */ diff -Nru a/drivers/net/ac3200.c b/drivers/net/ac3200.c --- a/drivers/net/ac3200.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/ac3200.c Wed Feb 13 20:03:35 2002 @@ -173,7 +173,8 @@ printk(", assigning"); } - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (retval) { printk (" nothing! Unable to get IRQ %d.\n", dev->irq); goto out1; diff -Nru a/drivers/net/acenic.c b/drivers/net/acenic.c --- a/drivers/net/acenic.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/acenic.c Wed Feb 13 20:03:51 2002 @@ -166,12 +166,12 @@ #endif #ifndef SET_MODULE_OWNER -#define SET_MODULE_OWNER(dev) {do{} while(0);} +#define SET_MODULE_OWNER(dev) do { } while(0) #define ACE_MOD_INC_USE_COUNT MOD_INC_USE_COUNT #define ACE_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT #else -#define ACE_MOD_INC_USE_COUNT {do{} while(0);} -#define ACE_MOD_DEC_USE_COUNT {do{} while(0);} +#define ACE_MOD_INC_USE_COUNT do { } while(0) +#define ACE_MOD_DEC_USE_COUNT do { } while(0) #endif @@ -252,7 +252,7 @@ #define dev_kfree_skb_irq(a) dev_kfree_skb(a) #define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) #define netif_stop_queue(dev) set_bit(0, &dev->tbusy) -#define late_stop_netif_stop_queue(dev) {do{} while(0);} +#define late_stop_netif_stop_queue(dev) do { } while(0) #define early_stop_netif_stop_queue(dev) test_and_set_bit(0,&dev->tbusy) #define early_stop_netif_wake_queue(dev) netif_wake_queue(dev) @@ -266,7 +266,7 @@ #define ace_mark_net_bh() mark_bh(NET_BH) #define netif_queue_stopped(dev) dev->tbusy #define netif_running(dev) dev->start -#define ace_if_down(dev) {do{dev->start = 0;} while(0);} +#define ace_if_down(dev) do { dev->start = 0; } while(0) #define tasklet_struct tq_struct static inline void tasklet_schedule(struct tasklet_struct *tasklet) @@ -284,13 +284,13 @@ tasklet->routine = (void (*)(void *))func; tasklet->data = (void *)data; } -#define tasklet_kill(tasklet) {do{} while(0);} +#define tasklet_kill(tasklet) do { } while(0) #else #define late_stop_netif_stop_queue(dev) netif_stop_queue(dev) #define early_stop_netif_stop_queue(dev) 0 -#define early_stop_netif_wake_queue(dev) {do{} while(0);} -#define ace_mark_net_bh() {do{} while(0);} -#define ace_if_down(dev) {do{} while(0);} +#define early_stop_netif_wake_queue(dev) do { } while(0) +#define ace_mark_net_bh() do { } while(0) +#define ace_if_down(dev) do { } while(0) #endif #if (LINUX_VERSION_CODE >= 0x02031b) @@ -306,7 +306,7 @@ #ifndef ARCH_HAS_PREFETCHW #ifndef prefetchw -#define prefetchw(x) {do{} while(0);} +#define prefetchw(x) do { } while(0) #endif #endif @@ -1370,7 +1370,8 @@ goto init_error; } - ecode = request_irq(dev->irq, ace_interrupt, SA_SHIRQ, dev->name, dev); + ecode = request_irq(dev->irq, ace_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ecode) { printk(KERN_WARNING "%s: Requested IRQ %d is busy\n", dev->name, dev->irq); diff -Nru a/drivers/net/aironet4500_card.c b/drivers/net/aironet4500_card.c --- a/drivers/net/aironet4500_card.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/aironet4500_card.c Wed Feb 13 20:03:34 2002 @@ -213,7 +213,7 @@ dev->watchdog_timeo = AWC_TX_TIMEOUT; - i = request_irq(dev->irq,awc_interrupt, SA_SHIRQ | SA_INTERRUPT, dev->name, dev); + i = request_irq(dev->irq,awc_interrupt, SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) { kfree(dev->priv); dev->priv = NULL; diff -Nru a/drivers/net/am79c961a.c b/drivers/net/am79c961a.c --- a/drivers/net/am79c961a.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/am79c961a.c Wed Feb 13 20:03:56 2002 @@ -267,7 +267,8 @@ memset (&priv->stats, 0, sizeof (priv->stats)); - ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, am79c961_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) return ret; diff -Nru a/drivers/net/apne.c b/drivers/net/apne.c --- a/drivers/net/apne.c Wed Feb 13 20:03:52 2002 +++ b/drivers/net/apne.c Wed Feb 13 20:03:52 2002 @@ -287,7 +287,8 @@ dev->base_addr = ioaddr; /* Install the Interrupt handler */ - i = request_irq(IRQ_AMIGA_PORTS, apne_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(IRQ_AMIGA_PORTS, apne_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; /* Allocate dev->priv and fill in 8390 specific dev fields. */ diff -Nru a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c --- a/drivers/net/appletalk/cops.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/appletalk/cops.c Wed Feb 13 20:03:55 2002 @@ -304,7 +304,8 @@ /* Reserve any actual interrupt. */ if(dev->irq) { - retval = request_irq(dev->irq, &cops_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, &cops_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) goto err_out; } diff -Nru a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c --- a/drivers/net/appletalk/ltpc.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/appletalk/ltpc.c Wed Feb 13 20:03:44 2002 @@ -1200,7 +1200,8 @@ if (irq) { /* grab it and don't let go :-) */ - (void) request_irq( irq, <pc_interrupt, 0, "ltpc", dev); + (void) request_irq(irq, <pc_interrupt, + SA_SAMPLE_NET_RANDOM, "ltpc", dev); (void) inb_p(io+7); /* enable interrupts from board */ (void) inb_p(io+7); /* and reset irq line */ } else { diff -Nru a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c --- a/drivers/net/arcnet/arc-rawmode.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/arcnet/arc-rawmode.c Wed Feb 13 20:03:50 2002 @@ -83,6 +83,7 @@ arcnet_unregister_proto(&rawmode_proto); } +MODULE_LICENSE("GPL"); #endif /* MODULE */ diff -Nru a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c --- a/drivers/net/arcnet/arc-rimi.c Wed Feb 13 20:03:31 2002 +++ b/drivers/net/arcnet/arc-rimi.c Wed Feb 13 20:03:32 2002 @@ -121,7 +121,8 @@ int mirror_size; /* reserve the irq */ { - if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (RIM I)", dev)) + if (request_irq(dev->irq, &arcnet_interrupt, + SA_SAMPLE_NET_RANDOM, "arcnet (RIM I)", dev)) BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } @@ -297,6 +298,7 @@ MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); MODULE_PARM(device, "s"); +MODULE_LICENSE("GPL"); int init_module(void) { diff -Nru a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c --- a/drivers/net/arcnet/arcnet.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/arcnet/arcnet.c Wed Feb 13 20:03:35 2002 @@ -163,6 +163,7 @@ static int debug = ARCNET_DEBUG; MODULE_PARM(debug, "i"); +MODULE_LICENSE("GPL"); int __init init_module(void) { diff -Nru a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c --- a/drivers/net/arcnet/com20020-isa.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/arcnet/com20020-isa.c Wed Feb 13 20:03:35 2002 @@ -133,6 +133,7 @@ MODULE_PARM(backplane, "i"); MODULE_PARM(clockp, "i"); MODULE_PARM(clockm, "i"); +MODULE_LICENSE("GPL"); static void com20020isa_open_close(struct net_device *dev, bool open) { diff -Nru a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c --- a/drivers/net/arcnet/com20020-pci.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/arcnet/com20020-pci.c Wed Feb 13 20:03:36 2002 @@ -57,6 +57,7 @@ MODULE_PARM(backplane, "i"); MODULE_PARM(clockp, "i"); MODULE_PARM(clockm, "i"); +MODULE_LICENSE("GPL"); static void com20020pci_open_close(struct net_device *dev, bool open) { diff -Nru a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c --- a/drivers/net/arcnet/com20020.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/arcnet/com20020.c Wed Feb 13 20:03:44 2002 @@ -196,7 +196,8 @@ outb(dev->dev_addr[0], _XREG); /* reserve the irq */ - if (request_irq(dev->irq, &arcnet_interrupt, shared, + if (request_irq(dev->irq, &arcnet_interrupt, + shared | SA_SAMPLE_NET_RANDOM, "arcnet (COM20020)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; @@ -355,6 +356,8 @@ EXPORT_SYMBOL(com20020_check); EXPORT_SYMBOL(com20020_found); EXPORT_SYMBOL(com20020_remove); + +MODULE_LICENSE("GPL"); int init_module(void) { diff -Nru a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c --- a/drivers/net/arcnet/com90io.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/arcnet/com90io.c Wed Feb 13 20:03:44 2002 @@ -236,7 +236,8 @@ int ioaddr = dev->base_addr; /* Reserve the irq */ - if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) { + if (request_irq(dev->irq, &arcnet_interrupt, + SA_SAMPLE_NET_RANDOM, "arcnet (COM90xx-IO)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } @@ -383,6 +384,7 @@ MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); MODULE_PARM(device, "s"); +MODULE_LICENSE("GPL"); int init_module(void) { diff -Nru a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c --- a/drivers/net/arcnet/com90xx.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/arcnet/com90xx.c Wed Feb 13 20:03:36 2002 @@ -472,7 +472,8 @@ dev->dev_addr[0] = readb(lp->mem_start + 1); /* reserve the irq */ - if (request_irq(airq, &arcnet_interrupt, 0, "arcnet (90xx)", dev)) { + if (request_irq(airq, &arcnet_interrupt, SA_SAMPLE_NET_RANDOM, + "arcnet (90xx)", dev)) { BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq); goto err_unmap; } @@ -619,6 +620,7 @@ MODULE_PARM(irq, "i"); MODULE_PARM(shmem, "i"); MODULE_PARM(device, "s"); +MODULE_LICENSE("GPL"); int init_module(void) { diff -Nru a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c --- a/drivers/net/arcnet/rfc1051.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/arcnet/rfc1051.c Wed Feb 13 20:03:34 2002 @@ -68,6 +68,8 @@ #ifdef MODULE +MODULE_LICENSE("GPL"); + int __init init_module(void) { printk(VERSION); diff -Nru a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c --- a/drivers/net/arcnet/rfc1201.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/arcnet/rfc1201.c Wed Feb 13 20:03:51 2002 @@ -70,6 +70,8 @@ #ifdef MODULE +MODULE_LICENSE("GPL"); + int __init init_module(void) { printk(VERSION); diff -Nru a/drivers/net/ariadne.c b/drivers/net/ariadne.c --- a/drivers/net/ariadne.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/ariadne.c Wed Feb 13 20:03:34 2002 @@ -313,8 +313,8 @@ netif_start_queue(dev); - i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, SA_SHIRQ, - dev->name, dev); + i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; lance->RAP = CSR0; /* PCnet-ISA Controller Status */ diff -Nru a/drivers/net/ariadne2.c b/drivers/net/ariadne2.c --- a/drivers/net/ariadne2.c Wed Feb 13 20:03:32 2002 +++ b/drivers/net/ariadne2.c Wed Feb 13 20:03:32 2002 @@ -194,7 +194,8 @@ dev->irq = IRQ_AMIGA_PORTS; /* Install the Interrupt handler */ - i = request_irq(IRQ_AMIGA_PORTS, ei_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(IRQ_AMIGA_PORTS, ei_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; /* Allocate dev->priv and fill in 8390 specific dev fields. */ diff -Nru a/drivers/net/arlan.c b/drivers/net/arlan.c --- a/drivers/net/arlan.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/arlan.c Wed Feb 13 20:03:34 2002 @@ -1299,7 +1299,8 @@ return ret; arlan = ((struct arlan_private *) dev->priv)->card; - ret = request_irq(dev->irq, &arlan_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, &arlan_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) { printk(KERN_ERR "%s: unable to get IRQ %d .\n", diff -Nru a/drivers/net/at1700.c b/drivers/net/at1700.c --- a/drivers/net/at1700.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/at1700.c Wed Feb 13 20:03:50 2002 @@ -439,7 +439,8 @@ lp->jumpered = is_fmv18x; lp->mca_slot = slot; /* Snarf the interrupt vector now. */ - ret = request_irq(irq, &net_interrupt, 0, dev->name, dev); + ret = request_irq(irq, &net_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) { printk (" AT1700 at %#3x is unusable due to a conflict on" "IRQ %d.\n", ioaddr, irq); @@ -464,7 +465,7 @@ #define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */ /* Delay between EEPROM clock transitions. */ -#define eeprom_delay() do {} while (0); +#define eeprom_delay() do { } while (0) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) diff -Nru a/drivers/net/atarilance.c b/drivers/net/atarilance.c --- a/drivers/net/atarilance.c Wed Feb 13 20:03:40 2002 +++ b/drivers/net/atarilance.c Wed Feb 13 20:03:40 2002 @@ -546,7 +546,8 @@ if (lp->cardtype == PAM_CARD || memaddr == (unsigned short *)0xffe00000) { /* PAMs card and Riebl on ST use level 5 autovector */ - request_irq(IRQ_AUTO_5, lance_interrupt, IRQ_TYPE_PRIO, + request_irq(IRQ_AUTO_5, lance_interrupt, + IRQ_TYPE_PRIO | SA_SAMPLE_NET_RANDOM, "PAM/Riebl-ST Ethernet", dev); dev->irq = (unsigned short)IRQ_AUTO_5; } @@ -560,7 +561,8 @@ printk( "Lance: request for VME interrupt failed\n" ); return( 0 ); } - request_irq(irq, lance_interrupt, IRQ_TYPE_PRIO, + request_irq(irq, lance_interrupt, + IRQ_TYPE_PRIO | SA_SAMPLE_NET_RANDOM, "Riebl-VME Ethernet", dev); dev->irq = irq; } diff -Nru a/drivers/net/atp.c b/drivers/net/atp.c --- a/drivers/net/atp.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/atp.c Wed Feb 13 20:03:51 2002 @@ -438,7 +438,8 @@ /* The interrupt line is turned off (tri-stated) when the device isn't in use. That's especially important for "attached" interfaces where the port or interrupt may be shared. */ - ret = request_irq(dev->irq, &atp_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, &atp_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) return ret; diff -Nru a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c --- a/drivers/net/au1000_eth.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/au1000_eth.c Wed Feb 13 20:03:29 2002 @@ -819,7 +819,8 @@ } netif_start_queue(dev); - if ((retval = request_irq(dev->irq, &au1000_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, &au1000_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); MOD_DEC_USE_COUNT; return retval; diff -Nru a/drivers/net/bagetlance.c b/drivers/net/bagetlance.c --- a/drivers/net/bagetlance.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/bagetlance.c Wed Feb 13 20:03:30 2002 @@ -622,7 +622,8 @@ if (lp->cardtype == PAM_CARD || memaddr == (unsigned short *)0xffe00000) { /* PAMs card and Riebl on ST use level 5 autovector */ - request_irq(BAGET_LANCE_IRQ, lance_interrupt, IRQ_TYPE_PRIO, + request_irq(BAGET_LANCE_IRQ, lance_interrupt, + IRQ_TYPE_PRIO | SA_SAMPLE_NET_RANDOM, "PAM/Riebl-ST Ethernet", dev); dev->irq = (unsigned short)BAGET_LANCE_IRQ; } diff -Nru a/drivers/net/bmac.c b/drivers/net/bmac.c --- a/drivers/net/bmac.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/bmac.c Wed Feb 13 20:03:30 2002 @@ -1418,17 +1418,20 @@ /* init_timer(&bp->tx_timeout); */ /* bp->timeout_active = 0; */ - ret = request_irq(dev->irq, bmac_misc_intr, 0, "BMAC-misc", dev); + ret = request_irq(dev->irq, bmac_misc_intr, SA_SAMPLE_NET_RANDOM, + "BMAC-misc", dev); if (ret) { printk(KERN_ERR "BMAC: can't get irq %d\n", dev->irq); goto err_out_iounmap_rx; } - ret = request_irq(bmac->intrs[1].line, bmac_txdma_intr, 0, "BMAC-txdma", dev); + ret = request_irq(bmac->intrs[1].line, bmac_txdma_intr, + SA_SAMPLE_NET_RANDOM, "BMAC-txdma", dev); if (ret) { printk(KERN_ERR "BMAC: can't get irq %d\n", bmac->intrs[1].line); goto err_out_irq0; } - ret = request_irq(bmac->intrs[2].line, bmac_rxdma_intr, 0, "BMAC-rxdma", dev); + ret = request_irq(bmac->intrs[2].line, bmac_rxdma_intr, + SA_SAMPLE_NET_RANDOM, "BMAC-rxdma", dev); if (ret) { printk(KERN_ERR "BMAC: can't get irq %d\n", bmac->intrs[2].line); goto err_out_irq1; diff -Nru a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c --- a/drivers/net/cs89x0.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/cs89x0.c Wed Feb 13 20:03:56 2002 @@ -1057,7 +1057,7 @@ for (i = 2; i < CS8920_NO_INTS; i++) { if ((1 << dev->irq) & lp->irq_map) { - if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) { + if (request_irq(i, net_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev) == 0) { dev->irq = i; write_irq(dev, lp->chip_type, i); /* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */ @@ -1086,7 +1086,8 @@ writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); #endif write_irq(dev, lp->chip_type, dev->irq); - ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, &net_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) { if (net_debug) printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); diff -Nru a/drivers/net/daynaport.c b/drivers/net/daynaport.c --- a/drivers/net/daynaport.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/daynaport.c Wed Feb 13 20:03:30 2002 @@ -643,7 +643,8 @@ /* - funaho@jurai.org (1999-05-17) */ /* Non-slow interrupt, works around issues with the SONIC driver */ - ret = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) { printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq); return ret; diff -Nru a/drivers/net/de4x5.c b/drivers/net/de4x5.c --- a/drivers/net/de4x5.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/de4x5.c Wed Feb 13 20:03:30 2002 @@ -1404,11 +1404,12 @@ lp->state = OPEN; de4x5_dbg_open(dev); - if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, - lp->adapter_name, dev)) { + if (request_irq(dev->irq, (void *)de4x5_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, lp->adapter_name, dev)) { printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); - if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, - lp->adapter_name, dev)) { + if (request_irq(dev->irq, de4x5_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + lp->adapter_name, dev)) { printk("\n Cannot get IRQ- reconfigure your hardware.\n"); disable_ast(dev); de4x5_free_rx_buffs(dev); diff -Nru a/drivers/net/de600.c b/drivers/net/de600.c --- a/drivers/net/de600.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/de600.c Wed Feb 13 20:03:50 2002 @@ -344,7 +344,8 @@ static int de600_open(struct net_device *dev) { - int ret = request_irq(DE600_IRQ, de600_interrupt, 0, dev->name, dev); + int ret = request_irq(DE600_IRQ, de600_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) { printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ); return ret; diff -Nru a/drivers/net/de620.c b/drivers/net/de620.c --- a/drivers/net/de620.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/de620.c Wed Feb 13 20:03:29 2002 @@ -443,7 +443,8 @@ */ static int de620_open(struct net_device *dev) { - int ret = request_irq(dev->irq, de620_interrupt, 0, dev->name, dev); + int ret = request_irq(dev->irq, de620_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (ret) { printk (KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); return ret; diff -Nru a/drivers/net/declance.c b/drivers/net/declance.c --- a/drivers/net/declance.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/declance.c Wed Feb 13 20:03:30 2002 @@ -800,7 +800,8 @@ netif_start_queue(dev); /* Associate IRQ with lance_interrupt */ - if (request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { + if (request_irq(dev->irq, &lance_interrupt, SA_SAMPLE_NET_RANDOM, + lp->name, dev)) { printk("Lance: Can't get irq %d\n", dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/defxx.c b/drivers/net/defxx.c --- a/drivers/net/defxx.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/defxx.c Wed Feb 13 20:03:29 2002 @@ -1218,7 +1218,8 @@ /* Register IRQ - support shared interrupts by passing device ptr */ - ret = request_irq(dev->irq, (void *)dfx_interrupt, SA_SHIRQ, dev->name, dev); + ret = request_irq(dev->irq, (void *)dfx_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) { printk(KERN_ERR "%s: Requested IRQ %d is busy\n", dev->name, dev->irq); return ret; diff -Nru a/drivers/net/depca.c b/drivers/net/depca.c --- a/drivers/net/depca.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/depca.c Wed Feb 13 20:03:29 2002 @@ -780,7 +780,8 @@ depca_dbg_open(dev); - if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) { + if (request_irq(dev->irq, &depca_interrupt, SA_SAMPLE_NET_RANDOM, + lp->adapter_name, dev)) { printk("depca_open(): Requested IRQ%d is busy\n",dev->irq); status = -EAGAIN; } else { diff -Nru a/drivers/net/dgrs.c b/drivers/net/dgrs.c --- a/drivers/net/dgrs.c Wed Feb 13 20:03:32 2002 +++ b/drivers/net/dgrs.c Wed Feb 13 20:03:32 2002 @@ -1178,7 +1178,8 @@ */ if (priv->plxreg) OUTL(dev->base_addr + PLX_LCL2PCI_DOORBELL, 1); - rc = request_irq(dev->irq, &dgrs_intr, SA_SHIRQ, "RightSwitch", dev); + rc = request_irq(dev->irq, &dgrs_intr, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + "RightSwitch", dev); if (rc) return (rc); diff -Nru a/drivers/net/dl2k.c b/drivers/net/dl2k.c --- a/drivers/net/dl2k.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/dl2k.c Wed Feb 13 20:03:50 2002 @@ -400,7 +400,8 @@ long ioaddr = dev->base_addr; int i; - i = request_irq (dev->irq, &rio_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq (dev->irq, &rio_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; /* DebugCtrl bit 4, 5, 9 must set */ diff -Nru a/drivers/net/dmfe.c b/drivers/net/dmfe.c --- a/drivers/net/dmfe.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/dmfe.c Wed Feb 13 20:03:56 2002 @@ -561,7 +561,8 @@ DMFE_DBUG(0, "dmfe_open", 0); - ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); + ret = request_irq(dev->irq, &dmfe_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) return ret; diff -Nru a/drivers/net/e2100.c b/drivers/net/e2100.c --- a/drivers/net/e2100.c Wed Feb 13 20:03:48 2002 +++ b/drivers/net/e2100.c Wed Feb 13 20:03:48 2002 @@ -261,7 +261,8 @@ short ioaddr = dev->base_addr; int retval; - if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) + if ((retval = request_irq(dev->irq, ei_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) return retval; /* Set the interrupt line and memory base on the hardware. */ diff -Nru a/drivers/net/eepro.c b/drivers/net/eepro.c --- a/drivers/net/eepro.c Wed Feb 13 20:03:37 2002 +++ b/drivers/net/eepro.c Wed Feb 13 20:03:37 2002 @@ -962,7 +962,7 @@ return -EAGAIN; } - if (request_irq(dev->irq , &eepro_interrupt, 0, dev->name, dev)) { + if (request_irq(dev->irq , &eepro_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev)) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c --- a/drivers/net/eepro100.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/eepro100.c Wed Feb 13 20:03:35 2002 @@ -168,6 +168,9 @@ #ifndef PCI_DEVICE_ID_INTEL_ID1030 #define PCI_DEVICE_ID_INTEL_ID1030 0x1030 #endif +#ifndef PCI_DEVICE_ID_INTEL_ID1031 /* support for Intel Pro/100 VE */ +#define PCI_DEVICE_ID_INTEL_ID1031 0x1031 +#endif static int speedo_debug = 1; @@ -943,7 +946,8 @@ sp->in_interrupt = 0; /* .. we can safely take handler calls during init. */ - retval = request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev); + retval = request_irq(dev->irq, &speedo_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { MOD_DEC_USE_COUNT; return retval; @@ -2270,6 +2274,8 @@ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1029, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1031, /* support for Intel Pro/100 VE */ PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_7, PCI_ANY_ID, PCI_ANY_ID, }, diff -Nru a/drivers/net/eexpress.c b/drivers/net/eexpress.c --- a/drivers/net/eexpress.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/eexpress.c Wed Feb 13 20:03:29 2002 @@ -433,7 +433,8 @@ if (!dev->irq || !irqrmap[dev->irq]) return -ENXIO; - ret = request_irq(dev->irq,&eexp_irq,0,dev->name,dev); + ret = request_irq(dev->irq,&eexp_irq,SA_SAMPLE_NET_RANDOM, + dev->name,dev); if (ret) return ret; request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress"); @@ -1674,7 +1675,6 @@ unregister_netdev(dev); kfree(dev->priv); dev->priv = NULL; - release_region(dev->base_addr, EEXP_IO_EXTENT); } } } diff -Nru a/drivers/net/epic100.c b/drivers/net/epic100.c --- a/drivers/net/epic100.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/epic100.c Wed Feb 13 20:03:51 2002 @@ -663,7 +663,8 @@ /* Soft reset the chip. */ outl(0x4001, ioaddr + GENCTL); - if ((retval = request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, dev->name, dev))) + if ((retval = request_irq(dev->irq, &epic_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev))) return retval; epic_init_ring(dev); diff -Nru a/drivers/net/es3210.c b/drivers/net/es3210.c --- a/drivers/net/es3210.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/es3210.c Wed Feb 13 20:03:41 2002 @@ -212,7 +212,8 @@ printk(" assigning IRQ %d", dev->irq); } - if (request_irq(dev->irq, ei_interrupt, 0, "es3210", dev)) { + if (request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + "es3210", dev)) { printk (" unable to get IRQ %d.\n", dev->irq); retval = -EAGAIN; goto out; diff -Nru a/drivers/net/eth16i.c b/drivers/net/eth16i.c --- a/drivers/net/eth16i.c Wed Feb 13 20:03:43 2002 +++ b/drivers/net/eth16i.c Wed Feb 13 20:03:43 2002 @@ -521,7 +521,8 @@ /* Try to obtain interrupt vector */ - if ((retval = request_irq(dev->irq, (void *)ð16i_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, (void *)ð16i_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) { printk(KERN_WARNING "%s: %s at %#3x, but is unusable due conflicting IRQ %d.\n", dev->name, cardname, ioaddr, dev->irq); goto out; diff -Nru a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c --- a/drivers/net/ewrk3.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/ewrk3.c Wed Feb 13 20:03:50 2002 @@ -640,7 +640,8 @@ STOP_EWRK3; if (!lp->hard_strapped) { - if (request_irq(dev->irq, (void *) ewrk3_interrupt, 0, "ewrk3", dev)) { + if (request_irq(dev->irq, (void *) ewrk3_interrupt, + SA_SAMPLE_NET_RANDOM, "ewrk3", dev)) { printk("ewrk3_open(): Requested IRQ%d is busy\n", dev->irq); status = -EAGAIN; } else { diff -Nru a/drivers/net/fc/iph5526.c b/drivers/net/fc/iph5526.c --- a/drivers/net/fc/iph5526.c Wed Feb 13 20:03:40 2002 +++ b/drivers/net/fc/iph5526.c Wed Feb 13 20:03:40 2002 @@ -3844,7 +3844,7 @@ int irqval = 0; /* Found it, get IRQ. */ - irqval = request_irq(pci_irq_line, &tachyon_interrupt, pci_irq_line ? SA_SHIRQ : 0, fi->name, host); + irqval = request_irq(pci_irq_line, &tachyon_interrupt, pci_irq_line ? SA_SHIRQ : 0 | SA_SAMPLE_NET_RANDOM, fi->name, host); if (irqval) { printk("iph5526.c : Unable to get IRQ %d (irqval = %d).\n", pci_irq_line, irqval); scsi_unregister(host); diff -Nru a/drivers/net/fealnx.c b/drivers/net/fealnx.c --- a/drivers/net/fealnx.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/fealnx.c Wed Feb 13 20:03:36 2002 @@ -880,7 +880,8 @@ writel(0x00000001, ioaddr + BCR); /* Reset */ - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) + if (request_irq(dev->irq, &intr_handler, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev)) return -EAGAIN; init_ring(dev); diff -Nru a/drivers/net/fmv18x.c b/drivers/net/fmv18x.c --- a/drivers/net/fmv18x.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/fmv18x.c Wed Feb 13 20:03:33 2002 @@ -200,7 +200,8 @@ } /* Snarf the interrupt vector now. */ - retval = request_irq(irq, &net_interrupt, 0, dev->name, dev); + retval = request_irq(irq, &net_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (retval) { printk ("FMV-18x found at %#3x, but it's unusable due to a conflict on" "IRQ %d.\n", ioaddr, irq); diff -Nru a/drivers/net/gmac.c b/drivers/net/gmac.c --- a/drivers/net/gmac.c Wed Feb 13 20:03:43 2002 +++ b/drivers/net/gmac.c Wed Feb 13 20:03:43 2002 @@ -1085,7 +1085,8 @@ return -EIO; /* Get our interrupt */ - ret = request_irq(dev->irq, gmac_interrupt, 0, dev->name, dev); + ret = request_irq(dev->irq, gmac_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) { printk(KERN_ERR "%s can't get irq %d\n", dev->name, dev->irq); return ret; diff -Nru a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c --- a/drivers/net/gt96100eth.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/gt96100eth.c Wed Feb 13 20:03:56 2002 @@ -826,7 +826,8 @@ printk("%s: gt96100_open: dev=%p\n", dev->name, dev); if ((retval = request_irq(dev->irq, >96100_interrupt, - SA_SHIRQ, dev->name, dev))) { + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); MOD_DEC_USE_COUNT; diff -Nru a/drivers/net/hamachi.c b/drivers/net/hamachi.c --- a/drivers/net/hamachi.c Wed Feb 13 20:03:52 2002 +++ b/drivers/net/hamachi.c Wed Feb 13 20:03:52 2002 @@ -849,7 +849,8 @@ u32 rx_int_var, tx_int_var; u16 fifo_info; - i = request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &hamachi_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; diff -Nru a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c --- a/drivers/net/hamradio/6pack.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/hamradio/6pack.c Wed Feb 13 20:03:37 2002 @@ -1062,6 +1062,7 @@ MODULE_AUTHOR("Andreas Könsgen "); MODULE_DESCRIPTION("6pack driver for AX.25"); +MODULE_LICENSE("GPL"); module_init(sixpack_init_driver); module_exit(sixpack_exit_driver); diff -Nru a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c --- a/drivers/net/hamradio/baycom_epp.c Wed Feb 13 20:03:52 2002 +++ b/drivers/net/hamradio/baycom_epp.c Wed Feb 13 20:03:52 2002 @@ -1414,6 +1414,7 @@ MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); +MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ diff -Nru a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c --- a/drivers/net/hamradio/baycom_par.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/hamradio/baycom_par.c Wed Feb 13 20:03:56 2002 @@ -493,6 +493,7 @@ MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver"); +MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ diff -Nru a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c --- a/drivers/net/hamradio/baycom_ser_fdx.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/hamradio/baycom_ser_fdx.c Wed Feb 13 20:03:30 2002 @@ -428,7 +428,8 @@ outb(0, FCR(dev->base_addr)); /* disable FIFOs */ outb(0x0d, MCR(dev->base_addr)); outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ, + if (request_irq(dev->irq, ser12_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "baycom_ser_fdx", dev)) return -EBUSY; request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx"); @@ -609,6 +610,7 @@ MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver"); +MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ diff -Nru a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c --- a/drivers/net/hamradio/baycom_ser_hdx.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/hamradio/baycom_ser_hdx.c Wed Feb 13 20:03:30 2002 @@ -485,7 +485,8 @@ outb(0, FCR(dev->base_addr)); /* disable FIFOs */ outb(0x0d, MCR(dev->base_addr)); outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ, + if (request_irq(dev->irq, ser12_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "baycom_ser12", dev)) return -EBUSY; request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"); @@ -649,6 +650,7 @@ MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver"); +MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ diff -Nru a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c --- a/drivers/net/hamradio/bpqether.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/hamradio/bpqether.c Wed Feb 13 20:03:51 2002 @@ -645,5 +645,6 @@ MODULE_AUTHOR("Joerg Reuter DL1BKE "); MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); +MODULE_LICENSE("GPL"); module_init(bpq_init_driver); module_exit(bpq_cleanup_driver); diff -Nru a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c --- a/drivers/net/hamradio/dmascc.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/hamradio/dmascc.c Wed Feb 13 20:03:50 2002 @@ -306,6 +306,7 @@ MODULE_AUTHOR("Klaus Kudielka"); MODULE_DESCRIPTION("Driver for high-speed SCC boards"); MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i"); +MODULE_LICENSE("GPL"); int init_module(void) { @@ -712,7 +713,7 @@ /* Request IRQ if not already used by other channel */ if (!info->irq_used) { - if (request_irq(dev->irq, scc_isr, 0, "dmascc", info)) { + if (request_irq(dev->irq, scc_isr, SA_SAMPLE_NET_RANDOM, "dmascc", info)) { MOD_DEC_USE_COUNT; return -EAGAIN; } diff -Nru a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c --- a/drivers/net/hamradio/hdlcdrv.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/hamradio/hdlcdrv.c Wed Feb 13 20:03:39 2002 @@ -901,6 +901,7 @@ MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); +MODULE_LICENSE("GPL"); module_init(hdlcdrv_init_driver); module_exit(hdlcdrv_cleanup_driver); diff -Nru a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c --- a/drivers/net/hamradio/mkiss.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/hamradio/mkiss.c Wed Feb 13 20:03:50 2002 @@ -1008,6 +1008,7 @@ MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); MODULE_PARM(ax25_maxdev, "i"); MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices"); +MODULE_LICENSE("GPL"); module_init(mkiss_init_driver); module_exit(mkiss_exit_driver); diff -Nru a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c --- a/drivers/net/hamradio/scc.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/hamradio/scc.c Wed Feb 13 20:03:44 2002 @@ -1769,7 +1769,7 @@ if (!Ivec[hwcfg.irq].used && hwcfg.irq) { - if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) + if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, "AX.25 SCC", NULL)) printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); else Ivec[hwcfg.irq].used = 1; @@ -2179,5 +2179,6 @@ MODULE_AUTHOR("Joerg Reuter "); MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio"); +MODULE_LICENSE("GPL"); module_init(scc_init_driver); module_exit(scc_cleanup_driver); diff -Nru a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c --- a/drivers/net/hamradio/soundmodem/sm_sbc.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/hamradio/soundmodem/sm_sbc.c Wed Feb 13 20:03:35 2002 @@ -436,7 +436,8 @@ kfree(sm->dma.obuf); return -EBUSY; } - if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, + if (request_irq(dev->irq, sbc_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, sm->hwdrv->hw_name, dev)) { free_dma(dev->dma); kfree(sm->dma.obuf); @@ -815,7 +816,8 @@ free_dma(dev->dma); return -EBUSY; } - if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, + if (request_irq(dev->irq, sbcfdx_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, sm->hwdrv->hw_name, dev)) { kfree(sm->dma.ibuf); kfree(sm->dma.obuf); diff -Nru a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c --- a/drivers/net/hamradio/soundmodem/sm_wss.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/hamradio/soundmodem/sm_wss.c Wed Feb 13 20:03:50 2002 @@ -473,7 +473,8 @@ kfree(sm->dma.obuf); return -EBUSY; } - if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, + if (request_irq(dev->irq, wss_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, sm->hwdrv->hw_name, dev)) { free_dma(dev->dma); kfree(sm->dma.obuf); @@ -827,7 +828,8 @@ free_dma(dev->dma); return -EBUSY; } - if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, + if (request_irq(dev->irq, wssfdx_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, sm->hwdrv->hw_name, dev)) { kfree(sm->dma.ibuf); kfree(sm->dma.obuf); diff -Nru a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c --- a/drivers/net/hamradio/yam.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/hamradio/yam.c Wed Feb 13 20:03:39 2002 @@ -308,7 +308,8 @@ static void delay(int ms) { unsigned long timeout = jiffies + ((ms * HZ) / 1000); - while (time_before(jiffies, timeout)); + while (time_before(jiffies, timeout)) + cpu_relax(); } /* @@ -861,7 +862,7 @@ return -EIO; } outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, dev)) { + if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev)) { printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); return -EBUSY; } @@ -1177,6 +1178,7 @@ MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr"); MODULE_DESCRIPTION("Yam amateur radio modem driver"); +MODULE_LICENSE("GPL"); module_init(yam_init_driver); module_exit(yam_cleanup_driver); diff -Nru a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c --- a/drivers/net/hp-plus.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/hp-plus.c Wed Feb 13 20:03:33 2002 @@ -253,7 +253,8 @@ int option_reg; int retval; - if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) { + if ((retval = request_irq(dev->irq, ei_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) { return retval; } diff -Nru a/drivers/net/hp.c b/drivers/net/hp.c --- a/drivers/net/hp.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/hp.c Wed Feb 13 20:03:39 2002 @@ -157,8 +157,7 @@ /* Twinkle the interrupt, and check if it's seen. */ outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE); outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE); - if (irq == probe_irq_off(cookie) /* It's a good IRQ line! */ - && request_irq (irq, ei_interrupt, 0, dev->name, dev) == 0) { + if (irq == probe_irq_off(cookie) /* It's a good IRQ line! */ && request_irq (irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev) == 0) { printk(" selecting IRQ %d.\n", irq); dev->irq = *irqp; break; diff -Nru a/drivers/net/hp100.c b/drivers/net/hp100.c --- a/drivers/net/hp100.c Wed Feb 13 20:03:49 2002 +++ b/drivers/net/hp100.c Wed Feb 13 20:03:49 2002 @@ -1124,8 +1124,9 @@ /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ if (request_irq(dev->irq, hp100_interrupt, - lp->bus == HP100_BUS_PCI || lp->bus == - HP100_BUS_EISA ? SA_SHIRQ : SA_INTERRUPT, + (lp->bus == HP100_BUS_PCI || lp->bus == + HP100_BUS_EISA ? SA_SHIRQ : SA_INTERRUPT) | + SA_SAMPLE_NET_RANDOM, lp->id->name, dev)) { printk("hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq); return -EAGAIN; diff -Nru a/drivers/net/hydra.c b/drivers/net/hydra.c --- a/drivers/net/hydra.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/hydra.c Wed Feb 13 20:03:51 2002 @@ -129,8 +129,8 @@ dev->irq = IRQ_AMIGA_PORTS; /* Install the Interrupt handler */ - if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, SA_SHIRQ, "Hydra Ethernet", - dev)) + if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "Hydra Ethernet", dev)) return -EAGAIN; /* Allocate dev->priv and fill in 8390 specific dev fields. */ diff -Nru a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c --- a/drivers/net/ibmlana.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/ibmlana.c Wed Feb 13 20:03:39 2002 @@ -856,7 +856,7 @@ result = request_irq(priv->realirq, irq_handler, - SA_SHIRQ | SA_SAMPLE_RANDOM, dev->name, dev); + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (result != 0) { printk("%s: failed to register irq %d\n", dev->name, dev->irq); diff -Nru a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c --- a/drivers/net/ioc3-eth.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/ioc3-eth.c Wed Feb 13 20:03:33 2002 @@ -1313,7 +1313,8 @@ { struct ioc3_private *ip = dev->priv; - if (request_irq(dev->irq, ioc3_interrupt, SA_SHIRQ, ioc3_str, dev)) { + if (request_irq(dev->irq, ioc3_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, ioc3_str, dev)) { printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq); return -EAGAIN; diff -Nru a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c --- a/drivers/net/irda/ali-ircc.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/irda/ali-ircc.c Wed Feb 13 20:03:50 2002 @@ -291,15 +291,13 @@ self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ /* Reserve the ioports that we need */ - ret = check_region(self->io.fir_base, self->io.fir_ext); - if (ret < 0) { + if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n", self->io.fir_base); dev_self[i] = NULL; kfree(self); return -ENODEV; } - request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -1357,7 +1355,8 @@ iobase = self->io.fir_base; /* Request IRQ and install Interrupt Handler */ - if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) + if (request_irq(self->io.irq, ali_ircc_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev)) { WARNING("%s, unable to allocate irq=%d\n", driver_name, self->io.irq); diff -Nru a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c --- a/drivers/net/irda/irda-usb.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/irda/irda-usb.c Wed Feb 13 20:03:55 2002 @@ -1582,3 +1582,4 @@ MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli and Jean Tourrilhes "); MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c --- a/drivers/net/irda/irport.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/irda/irport.c Wed Feb 13 20:03:35 2002 @@ -782,8 +782,8 @@ iobase = self->io.sir_base; - if (request_irq(self->io.irq, self->interrupt, 0, dev->name, - (void *) dev)) { + if (request_irq(self->io.irq, self->interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, (void *) dev)) { IRDA_DEBUG(0, __FUNCTION__ "(), unable to allocate irq=%d\n", self->io.irq); return -EAGAIN; diff -Nru a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c --- a/drivers/net/irda/nsc-ircc.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/irda/nsc-ircc.c Wed Feb 13 20:03:29 2002 @@ -1847,7 +1847,8 @@ iobase = self->io.fir_base; - if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { + if (request_irq(self->io.irq, nsc_ircc_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev)) { WARNING("%s, unable to allocate irq=%d\n", driver_name, self->io.irq); return -EAGAIN; diff -Nru a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c --- a/drivers/net/irda/sa1100_ir.c Wed Feb 13 20:03:49 2002 +++ b/drivers/net/irda/sa1100_ir.c Wed Feb 13 20:03:49 2002 @@ -864,7 +864,8 @@ si->speed = 9600; - err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); + err = request_irq(dev->irq, sa1100_irda_irq, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (err) goto err_irq; diff -Nru a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c --- a/drivers/net/irda/toshoboe.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/irda/toshoboe.c Wed Feb 13 20:03:30 2002 @@ -525,7 +525,8 @@ return 0; if (request_irq (self->io.irq, toshoboe_interrupt, - SA_SHIRQ | SA_INTERRUPT, dev->name, (void *) self)) + SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, + dev->name, (void *) self)) { return -EAGAIN; diff -Nru a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c --- a/drivers/net/irda/vlsi_ir.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/irda/vlsi_ir.c Wed Feb 13 20:03:30 2002 @@ -832,8 +832,8 @@ outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR); - if (request_irq(ndev->irq, vlsi_interrupt, SA_SHIRQ, - drivername, ndev)) { + if (request_irq(ndev->irq, vlsi_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, drivername, ndev)) { printk(KERN_ERR "%s: couldn't get IRQ: %d\n", __FUNCTION__, ndev->irq); pci_release_regions(pdev); diff -Nru a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c --- a/drivers/net/irda/w83977af_ir.c Wed Feb 13 20:03:49 2002 +++ b/drivers/net/irda/w83977af_ir.c Wed Feb 13 20:03:49 2002 @@ -1221,8 +1221,8 @@ iobase = self->io.fir_base; - if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, - (void *) dev)) { + if (request_irq(self->io.irq, w83977af_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, (void *) dev)) { return -EAGAIN; } /* diff -Nru a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c --- a/drivers/net/isa-skeleton.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/isa-skeleton.c Wed Feb 13 20:03:50 2002 @@ -216,7 +216,8 @@ dev->irq = 9; { - int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev); + int irqval = request_irq(dev->irq, &net_interrupt, + SA_SAMPLE_NET_RANDOM, cardname, dev); if (irqval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); diff -Nru a/drivers/net/lance.c b/drivers/net/lance.c --- a/drivers/net/lance.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/lance.c Wed Feb 13 20:03:35 2002 @@ -672,7 +672,8 @@ int i; if (dev->irq == 0 || - request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { + request_irq(dev->irq, &lance_interrupt, + SA_SAMPLE_NET_RANDOM, lp->name, dev)) { return -EAGAIN; } diff -Nru a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c --- a/drivers/net/lasi_82596.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/lasi_82596.c Wed Feb 13 20:03:35 2002 @@ -999,7 +999,8 @@ MOD_INC_USE_COUNT; - if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) { + if (request_irq(dev->irq, &i596_interrupt, SA_SAMPLE_NET_RANDOM, + "i82596", dev)) { printk("%s: IRQ %d not free\n", dev->name, dev->irq); goto out; } diff -Nru a/drivers/net/lne390.c b/drivers/net/lne390.c --- a/drivers/net/lne390.c Wed Feb 13 20:03:48 2002 +++ b/drivers/net/lne390.c Wed Feb 13 20:03:48 2002 @@ -197,7 +197,8 @@ } printk(" IRQ %d,", dev->irq); - if ((ret = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) { + if ((ret = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev))) { printk (" unable to get IRQ %d.\n", dev->irq); kfree(dev->priv); dev->priv = NULL; diff -Nru a/drivers/net/lp486e.c b/drivers/net/lp486e.c --- a/drivers/net/lp486e.c Wed Feb 13 20:03:42 2002 +++ b/drivers/net/lp486e.c Wed Feb 13 20:03:42 2002 @@ -844,7 +844,8 @@ i596_open(struct net_device *dev) { int i; - i = request_irq(dev->irq, &i596_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &i596_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) { printk("%s: IRQ %d not free\n", dev->name, dev->irq); return i; diff -Nru a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c --- a/drivers/net/mac89x0.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/mac89x0.c Wed Feb 13 20:03:55 2002 @@ -329,7 +329,8 @@ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); /* Grab the interrupt */ - if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) + if (request_irq(dev->irq, &net_interrupt, SA_SAMPLE_NET_RANDOM, + "cs89x0", dev)) return -EAGAIN; /* Set up the IRQ - Apparently magic */ diff -Nru a/drivers/net/mace.c b/drivers/net/mace.c --- a/drivers/net/mace.c Wed Feb 13 20:03:42 2002 +++ b/drivers/net/mace.c Wed Feb 13 20:03:42 2002 @@ -230,13 +230,14 @@ mace_reset(dev); - if (request_irq(dev->irq, mace_interrupt, 0, "MACE", dev)) + if (request_irq(dev->irq, mace_interrupt, SA_SAMPLE_NET_RANDOM, + "MACE", dev)) printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); - if (request_irq(mace->intrs[1].line, mace_txdma_intr, 0, "MACE-txdma", - dev)) + if (request_irq(mace->intrs[1].line, mace_txdma_intr, + SA_SAMPLE_NET_RANDOM, "MACE-txdma", dev)) printk(KERN_ERR "MACE: can't get irq %d\n", mace->intrs[1].line); - if (request_irq(mace->intrs[2].line, mace_rxdma_intr, 0, "MACE-rxdma", - dev)) + if (request_irq(mace->intrs[2].line, mace_rxdma_intr, + SA_SAMPLE_NET_RANDOM, "MACE-rxdma", dev)) printk(KERN_ERR "MACE: can't get irq %d\n", mace->intrs[2].line); mp->next_mace = mace_devs; diff -Nru a/drivers/net/macmace.c b/drivers/net/macmace.c --- a/drivers/net/macmace.c Wed Feb 13 20:03:48 2002 +++ b/drivers/net/macmace.c Wed Feb 13 20:03:48 2002 @@ -422,7 +422,8 @@ * The interrupt is fixed and comes off the PSC. */ - if (request_irq(dev->irq, mace68k_interrupt, 0, "68K MACE", dev)) + if (request_irq(dev->irq, mace68k_interrupt, + SA_SAMPLE_NET_RANDOM, "68K MACE", dev)) { printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); return -EAGAIN; diff -Nru a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c --- a/drivers/net/myri_sbus.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/myri_sbus.c Wed Feb 13 20:03:33 2002 @@ -1062,7 +1062,8 @@ /* Register interrupt handler now. */ DET(("Requesting MYRIcom IRQ line.\n")); if (request_irq(dev->irq, &myri_interrupt, - SA_SHIRQ, "MyriCOM Ethernet", (void *) dev)) { + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "MyriCOM Ethernet", + (void *) dev)) { printk("MyriCOM: Cannot register interrupt handler.\n"); goto err; } diff -Nru a/drivers/net/natsemi.c b/drivers/net/natsemi.c --- a/drivers/net/natsemi.c Wed Feb 13 20:03:32 2002 +++ b/drivers/net/natsemi.c Wed Feb 13 20:03:32 2002 @@ -1042,7 +1042,8 @@ /* Reset the chip, just in case. */ natsemi_reset(dev); - i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &intr_handler, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; if (netif_msg_ifup(np)) diff -Nru a/drivers/net/ne.c b/drivers/net/ne.c --- a/drivers/net/ne.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/ne.c Wed Feb 13 20:03:51 2002 @@ -427,7 +427,8 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - ret = request_irq(dev->irq, ei_interrupt, 0, name, dev); + ret = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + name, dev); if (ret) { printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret); goto err_out_kfree; diff -Nru a/drivers/net/ne2.c b/drivers/net/ne2.c --- a/drivers/net/ne2.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/ne2.c Wed Feb 13 20:03:51 2002 @@ -439,7 +439,8 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (retval) { printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, retval); diff -Nru a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c --- a/drivers/net/ne2k-pci.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/ne2k-pci.c Wed Feb 13 20:03:39 2002 @@ -389,7 +389,8 @@ static int ne2k_pci_open(struct net_device *dev) { - int ret = request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev); + int ret = request_irq(dev->irq, ei_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) return ret; diff -Nru a/drivers/net/ne3210.c b/drivers/net/ne3210.c --- a/drivers/net/ne3210.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/ne3210.c Wed Feb 13 20:03:50 2002 @@ -189,7 +189,8 @@ } printk(" IRQ %d,", dev->irq); - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, ei_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { printk (" unable to get IRQ %d.\n", dev->irq); goto out1; diff -Nru a/drivers/net/ni5010.c b/drivers/net/ni5010.c --- a/drivers/net/ni5010.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/ni5010.c Wed Feb 13 20:03:51 2002 @@ -354,7 +354,8 @@ PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name)); - if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) { + if (request_irq(dev->irq, &ni5010_interrupt, SA_SAMPLE_NET_RANDOM, + boardname, dev)) { printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/ni52.c b/drivers/net/ni52.c --- a/drivers/net/ni52.c Wed Feb 13 20:03:31 2002 +++ b/drivers/net/ni52.c Wed Feb 13 20:03:31 2002 @@ -264,7 +264,8 @@ startrecv586(dev); ni_enaint(); - ret = request_irq(dev->irq, &ni52_interrupt,0,dev->name,dev); + ret = request_irq(dev->irq, &ni52_interrupt,SA_SAMPLE_NET_RANDOM, + dev->name,dev); if (ret) { ni_reset586(); diff -Nru a/drivers/net/ni65.c b/drivers/net/ni65.c --- a/drivers/net/ni65.c Wed Feb 13 20:03:42 2002 +++ b/drivers/net/ni65.c Wed Feb 13 20:03:42 2002 @@ -269,7 +269,7 @@ static int ni65_open(struct net_device *dev) { struct priv *p = (struct priv *) dev->priv; - int irqval = request_irq(dev->irq, &ni65_interrupt,0, + int irqval = request_irq(dev->irq, &ni65_interrupt,SA_SAMPLE_NET_RANDOM, cards[p->cardno].cardname,dev); if (irqval) { printk ("%s: unable to get IRQ %d (irqval=%d).\n", diff -Nru a/drivers/net/ns83820.c b/drivers/net/ns83820.c --- a/drivers/net/ns83820.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/ns83820.c Wed Feb 13 20:03:50 2002 @@ -1442,8 +1442,8 @@ setup_ee_mem_bitbanger(&dev->ee, (long)dev->base + MEAR, 3, 2, 1, 0, 0); - err = request_irq(pci_dev->irq, ns83820_irq, SA_SHIRQ, - dev->net_dev.name, dev); + err = request_irq(pci_dev->irq, ns83820_irq, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->net_dev.name, dev); if (err) { printk(KERN_INFO "ns83820: unable to register irq %d\n", pci_dev->irq); diff -Nru a/drivers/net/oaknet.c b/drivers/net/oaknet.c --- a/drivers/net/oaknet.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/oaknet.c Wed Feb 13 20:03:39 2002 @@ -183,7 +183,8 @@ /* Attempt to get the interrupt line */ ret = -EAGAIN; - if (request_irq(dev->irq, ei_interrupt, 0, name, dev)) { + if (request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + name, dev)) { printk("%s: unable to request interrupt %d.\n", dev->name, dev->irq); goto out_priv; diff -Nru a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c --- a/drivers/net/pci-skeleton.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/pci-skeleton.c Wed Feb 13 20:03:30 2002 @@ -1086,7 +1086,8 @@ DPRINTK ("ENTER\n"); - retval = request_irq (dev->irq, netdrv_interrupt, SA_SHIRQ, dev->name, dev); + retval = request_irq (dev->irq, netdrv_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { DPRINTK ("EXIT, returning %d\n", retval); return retval; diff -Nru a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c --- a/drivers/net/pcmcia/axnet_cs.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/pcmcia/axnet_cs.c Wed Feb 13 20:03:30 2002 @@ -658,7 +658,8 @@ link->open++; MOD_INC_USE_COUNT; - request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); + request_irq(dev->irq, ei_irq_wrapper, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev_info, dev); info->link_status = 0x00; info->watchdog.function = &ei_watchdog; diff -Nru a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c --- a/drivers/net/pcmcia/com20020_cs.c Wed Feb 13 20:03:51 2002 +++ b/drivers/net/pcmcia/com20020_cs.c Wed Feb 13 20:03:51 2002 @@ -122,6 +122,7 @@ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); +MODULE_LICENSE("GPL"); /*====================================================================*/ diff -Nru a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c --- a/drivers/net/pcmcia/pcnet_cs.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/pcmcia/pcnet_cs.c Wed Feb 13 20:03:44 2002 @@ -1025,7 +1025,7 @@ MOD_INC_USE_COUNT; set_misc_reg(dev); - request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); + request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev_info, dev); info->phy_id = info->eth_phy; info->link_status = 0x00; diff -Nru a/drivers/net/pcmcia/xircom_cb.c b/drivers/net/pcmcia/xircom_cb.c --- a/drivers/net/pcmcia/xircom_cb.c Wed Feb 13 20:03:40 2002 +++ b/drivers/net/pcmcia/xircom_cb.c Wed Feb 13 20:03:40 2002 @@ -464,7 +464,8 @@ int retval; enter(); printk(KERN_INFO "Xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq); - retval = request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev); + retval = request_irq(dev->irq, &xircom_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { printk(KERN_ERR "xircom_cb: Unable to aquire IRQ %i, aborting.\n",dev->irq); leave(); diff -Nru a/drivers/net/pcmcia/xircom_tulip_cb.c b/drivers/net/pcmcia/xircom_tulip_cb.c --- a/drivers/net/pcmcia/xircom_tulip_cb.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/pcmcia/xircom_tulip_cb.c Wed Feb 13 20:03:35 2002 @@ -797,7 +797,8 @@ { struct xircom_private *tp = dev->priv; - if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) + if (request_irq(dev->irq, &xircom_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev)) return -EAGAIN; xircom_init_ring(dev); diff -Nru a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c --- a/drivers/net/pcnet32.c Wed Feb 13 20:03:42 2002 +++ b/drivers/net/pcnet32.c Wed Feb 13 20:03:42 2002 @@ -823,7 +823,8 @@ if (dev->irq == 0 || request_irq(dev->irq, &pcnet32_interrupt, - lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) { + lp->shared_irq ? SA_SHIRQ : 0 | SA_SAMPLE_NET_RANDOM, + lp->name, (void *)dev)) { return -EAGAIN; } diff -Nru a/drivers/net/pppoe.c b/drivers/net/pppoe.c --- a/drivers/net/pppoe.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/pppoe.c Wed Feb 13 20:03:34 2002 @@ -86,11 +86,11 @@ #if 0 -#define CHECKPTR(x,y) { if (!(x) && pppoe_debug &7 ){ printk(KERN_CRIT "PPPoE Invalid pointer : %s , %p\n",#x,(x)); error=-EINVAL; goto y; }} -#define DEBUG(s,args...) if( pppoe_debug & (s) ) printk(KERN_CRIT args ); +#define CHECKPTR(x,y) do { if (!(x) && pppoe_debug &7 ){ printk(KERN_CRIT "PPPoE Invalid pointer : %s , %p\n",#x,(x)); error=-EINVAL; goto y; }} while (0) +#define DEBUG(s,args...) do { if( pppoe_debug & (s) ) printk(KERN_CRIT args ); } while (0) #else -#define CHECKPTR(x,y) do {} while (0); -#define DEBUG(s,args...) do { } while (0); +#define CHECKPTR(x,y) do { } while (0) +#define DEBUG(s,args...) do { } while (0) #endif diff -Nru a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c --- a/drivers/net/rcpci45.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/rcpci45.c Wed Feb 13 20:03:41 2002 @@ -323,7 +323,8 @@ } /* Request a shared interrupt line. */ - error = request_irq (dev->irq, RCinterrupt, SA_SHIRQ, dev->name, dev); + error = request_irq (dev->irq, RCinterrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (error) { printk (KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); diff -Nru a/drivers/net/rrunner.c b/drivers/net/rrunner.c --- a/drivers/net/rrunner.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/rrunner.c Wed Feb 13 20:03:41 2002 @@ -73,13 +73,13 @@ #define rr_mark_net_bh(foo) mark_bh(foo) #define rr_if_busy(dev) dev->tbusy #define rr_if_running(dev) dev->start /* Currently unused. */ -#define rr_if_down(dev) {do{dev->start = 0;}while (0);} +#define rr_if_down(dev) do { dev->start = 0; } while (0) #else #define NET_BH 0 -#define rr_mark_net_bh(foo) {do{} while(0);} +#define rr_mark_net_bh(foo) do { } while(0) #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);} +#define rr_if_down(dev) do { } while(0) #endif #include "rrunner.h" @@ -269,6 +269,7 @@ #if LINUX_VERSION_CODE > 0x20118 MODULE_AUTHOR("Jes Sorensen "); MODULE_DESCRIPTION("Essential RoadRunner HIPPI driver"); +MODULE_LICENSE("GPL"); #endif int init_module(void) @@ -1227,7 +1228,8 @@ writel(readl(®s->HostCtrl)|HALT_NIC|RR_CLEAR_INT, ®s->HostCtrl); spin_unlock_irqrestore(&rrpriv->lock, flags); - if (request_irq(dev->irq, rr_interrupt, SA_SHIRQ, rrpriv->name, dev)) + if (request_irq(dev->irq, rr_interrupt, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + rrpriv->name, dev)) { printk(KERN_WARNING "%s: Requested IRQ %d is busy\n", dev->name, dev->irq); diff -Nru a/drivers/net/saa9730.c b/drivers/net/saa9730.c --- a/drivers/net/saa9730.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/saa9730.c Wed Feb 13 20:03:30 2002 @@ -786,8 +786,8 @@ (struct lan_saa9730_private *) dev->priv; /* Associate IRQ with lan_saa9730_interrupt */ - if (request_irq(dev->irq, &lan_saa9730_interrupt, 0, "SAA9730 Eth", - dev)) { + if (request_irq(dev->irq, &lan_saa9730_interrupt, SA_SAMPLE_NET_RANDOM, + "SAA9730 Eth", dev)) { printk("lan_saa9730_open: Can't get irq %d\n", dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/sb1000.c b/drivers/net/sb1000.c --- a/drivers/net/sb1000.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/sb1000.c Wed Feb 13 20:03:33 2002 @@ -204,7 +204,12 @@ /* * Ok set it up. */ - + if (!request_region(ioaddr[0], 16, dev->name)) + continue; + if (!request_region(ioaddr[1], 16, dev->name)) { + release_region(ioaddr[0], 16); + continue; + } dev->base_addr = ioaddr[0]; /* rmem_end holds the second I/O address - fv */ @@ -262,9 +267,6 @@ /* Lock resources */ - request_region(ioaddr[0], 16, dev->name); - request_region(ioaddr[1], 16, dev->name); - return 0; } } @@ -962,8 +964,6 @@ /* rmem_end holds the second I/O address - fv */ ioaddr[1] = dev->rmem_end; name = dev->name; - request_region(ioaddr[0], SB1000_IO_EXTENT, "sb1000"); - request_region(ioaddr[1], SB1000_IO_EXTENT, "sb1000"); /* initialize sb1000 */ if ((status = sb1000_reset(ioaddr, name))) @@ -992,7 +992,8 @@ lp->rx_frame_id[1] = 0; lp->rx_frame_id[2] = 0; lp->rx_frame_id[3] = 0; - if (request_irq(dev->irq, &sb1000_interrupt, 0, "sb1000", dev)) { + if (request_irq(dev->irq, &sb1000_interrupt, SA_SAMPLE_NET_RANDOM, + "sb1000", dev)) { return -EAGAIN; } diff -Nru a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c --- a/drivers/net/seeq8005.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/seeq8005.c Wed Feb 13 20:03:39 2002 @@ -347,7 +347,7 @@ struct net_local *lp = (struct net_local *)dev->priv; { - int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev); + int irqval = request_irq(dev->irq, &seeq8005_interrupt, SA_SAMPLE_NET_RANDOM, "seeq8005", dev); if (irqval) { printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); diff -Nru a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c --- a/drivers/net/sgiseeq.c Wed Feb 13 20:03:48 2002 +++ b/drivers/net/sgiseeq.c Wed Feb 13 20:03:48 2002 @@ -452,7 +452,8 @@ int err; save_flags(flags); cli(); - if (request_irq(dev->irq, sgiseeq_interrupt, 0, sgiseeqstr, (void *) dev)) { + if (request_irq(dev->irq, sgiseeq_interrupt, SA_SAMPLE_NET_RANDOM, + sgiseeqstr, (void *) dev)) { printk("Seeq8003: Can't get irq %d\n", dev->irq); restore_flags(flags); return -EAGAIN; diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/sis900.c Wed Feb 13 20:03:50 2002 @@ -863,7 +863,9 @@ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); sis630_set_eq(net_dev, revision); - ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev); + ret = request_irq(net_dev->irq, &sis900_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + net_dev->name, net_dev); if (ret) return ret; diff -Nru a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c --- a/drivers/net/sk98lin/skge.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/sk98lin/skge.c Wed Feb 13 20:03:35 2002 @@ -867,10 +867,11 @@ spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); if (pAC->GIni.GIMacsFound == 2) { - Ret = request_irq(dev->irq, SkGeIsr, SA_SHIRQ, pAC->Name, dev); + Ret = request_irq(dev->irq, SkGeIsr, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, pAC->Name, dev); } else if (pAC->GIni.GIMacsFound == 1) { - Ret = request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ, - pAC->Name, dev); + Ret = request_irq(dev->irq, SkGeIsrOnePort, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, pAC->Name, dev); } else { printk(KERN_WARNING "%s: illegal number of ports: %d\n", dev->name, pAC->GIni.GIMacsFound); diff -Nru a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c --- a/drivers/net/sk_g16.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/sk_g16.c Wed Feb 13 20:03:55 2002 @@ -947,7 +947,8 @@ do { - irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16", dev); + irqval = request_irq(irqtab[i], &SK_interrupt, SA_SAMPLE_NET_RANDOM, + "sk_g16", dev); i++; } while (irqval && irqtab[i]); @@ -964,7 +965,7 @@ } else if (dev->irq == 2) /* IRQ2 is always IRQ9 */ { - if (request_irq(9, &SK_interrupt, 0, "sk_g16", dev)) + if (request_irq(9, &SK_interrupt, SA_SAMPLE_NET_RANDOM, "sk_g16", dev)) { printk("%s: unable to get IRQ 9\n", dev->name); return -EAGAIN; diff -Nru a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c --- a/drivers/net/sk_mca.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/sk_mca.c Wed Feb 13 20:03:35 2002 @@ -861,7 +861,7 @@ /* register resources - only necessary for IRQ */ result = request_irq(priv->realirq, irq_handler, - SA_SHIRQ | SA_SAMPLE_RANDOM, "sk_mca", dev); + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "sk_mca", dev); if (result != 0) { printk("%s: failed to register irq %d\n", dev->name, dev->irq); diff -Nru a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c --- a/drivers/net/skfp/skfddi.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/skfp/skfddi.c Wed Feb 13 20:03:33 2002 @@ -774,8 +774,8 @@ PRINTK(KERN_INFO "entering skfp_open\n"); /* Register IRQ - support shared interrupts by passing device ptr */ - if (request_irq(dev->irq, (void *) skfp_interrupt, SA_SHIRQ, - dev->name, dev)) { + if (request_irq(dev->irq, (void *) skfp_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev)) { printk("%s: Requested IRQ %d is busy\n", dev->name, dev->irq); return (-EAGAIN); } diff -Nru a/drivers/net/slip.c b/drivers/net/slip.c --- a/drivers/net/slip.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/slip.c Wed Feb 13 20:03:55 2002 @@ -1393,10 +1393,8 @@ /* First of all: check for active disciplines and hangup them. */ do { - if (busy) { - current->counter = 0; - schedule(); - } + if (busy) + sys_sched_yield(); busy = 0; local_bh_disable(); diff -Nru a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c --- a/drivers/net/smc-mca.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/smc-mca.c Wed Feb 13 20:03:35 2002 @@ -325,7 +325,8 @@ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ int retval; - if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) + if ((retval = request_irq(dev->irq, ei_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev))) return retval; outb(ULTRA_MEMENB, ioaddr); /* Enable memory */ diff -Nru a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c --- a/drivers/net/smc-ultra.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/smc-ultra.c Wed Feb 13 20:03:35 2002 @@ -329,7 +329,8 @@ unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, dev); if (retval) return retval; diff -Nru a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c --- a/drivers/net/smc-ultra32.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/smc-ultra32.c Wed Feb 13 20:03:44 2002 @@ -256,6 +256,8 @@ int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ; int retval; + irq_flags |= SA_SAMPLE_NET_RANDOM; + retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev); if (retval) return retval; diff -Nru a/drivers/net/smc9194.c b/drivers/net/smc9194.c --- a/drivers/net/smc9194.c Wed Feb 13 20:03:40 2002 +++ b/drivers/net/smc9194.c Wed Feb 13 20:03:40 2002 @@ -1017,7 +1017,7 @@ ether_setup(dev); /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + retval = request_irq(dev->irq, &smc_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, retval); diff -Nru a/drivers/net/sonic.c b/drivers/net/sonic.c --- a/drivers/net/sonic.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/sonic.c Wed Feb 13 20:03:50 2002 @@ -42,8 +42,8 @@ * this glue works ok under all situations. */ // if (sonic_request_irq(dev->irq, &sonic_interrupt, 0, "sonic", dev)) { - if (sonic_request_irq(dev->irq, &sonic_interrupt, SA_INTERRUPT, - "sonic", dev)) { + if (sonic_request_irq(dev->irq, &sonic_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, "sonic", dev)) { printk("\n%s: unable to get IRQ %d .\n", dev->name, dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/starfire.c b/drivers/net/starfire.c --- a/drivers/net/starfire.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/starfire.c Wed Feb 13 20:03:41 2002 @@ -831,7 +831,8 @@ COMPAT_MOD_INC_USE_COUNT; - retval = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + retval = request_irq(dev->irq, &intr_handler, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (retval) { COMPAT_MOD_DEC_USE_COUNT; return retval; diff -Nru a/drivers/net/stnic.c b/drivers/net/stnic.c --- a/drivers/net/stnic.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/stnic.c Wed Feb 13 20:03:35 2002 @@ -135,7 +135,7 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - i = request_irq (dev->irq, ei_interrupt, 0, dev->name, dev); + i = request_irq(dev->irq, ei_interrupt, SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) { printk (KERN_EMERG " unable to get IRQ %d.\n", dev->irq); unregister_netdev(dev); diff -Nru a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c --- a/drivers/net/sun3lance.c Wed Feb 13 20:03:44 2002 +++ b/drivers/net/sun3lance.c Wed Feb 13 20:03:44 2002 @@ -318,7 +318,8 @@ REGA(CSR0) = CSR0_STOP; - request_irq(LANCE_IRQ, lance_interrupt, 0, "SUN3 Lance", dev); + request_irq(LANCE_IRQ, lance_interrupt, SA_SAMPLE_NET_RANDOM, + "SUN3 Lance", dev); dev->irq = (unsigned short)LANCE_IRQ; diff -Nru a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c --- a/drivers/net/sunbmac.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/sunbmac.c Wed Feb 13 20:03:29 2002 @@ -904,7 +904,8 @@ struct bigmac *bp = (struct bigmac *) dev->priv; int ret; - ret = request_irq(dev->irq, &bigmac_interrupt, SA_SHIRQ, dev->name, bp); + ret = request_irq(dev->irq, &bigmac_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, bp); if (ret) { printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq); return ret; diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c --- a/drivers/net/sundance.c Wed Feb 13 20:03:31 2002 +++ b/drivers/net/sundance.c Wed Feb 13 20:03:31 2002 @@ -750,7 +750,8 @@ /* Do we need to reset the chip??? */ - i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &intr_handler, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; diff -Nru a/drivers/net/sungem.c b/drivers/net/sungem.c --- a/drivers/net/sungem.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/sungem.c Wed Feb 13 20:03:50 2002 @@ -2185,7 +2185,8 @@ * on the controller */ if (request_irq(gp->pdev->irq, gem_interrupt, - SA_SHIRQ, dev->name, (void *)dev)) { + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + dev->name, (void *)dev)) { printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name); #ifdef CONFIG_ALL_PPC if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE) diff -Nru a/drivers/net/sunhme.c b/drivers/net/sunhme.c --- a/drivers/net/sunhme.c Wed Feb 13 20:03:47 2002 +++ b/drivers/net/sunhme.c Wed Feb 13 20:03:47 2002 @@ -2219,7 +2219,8 @@ */ if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) { if (request_irq(dev->irq, &happy_meal_interrupt, - SA_SHIRQ, "HAPPY MEAL", (void *)dev)) { + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + "HAPPY MEAL", (void *)dev)) { HMD(("EAGAIN\n")); #ifdef __sparc__ printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n", diff -Nru a/drivers/net/sunlance.c b/drivers/net/sunlance.c --- a/drivers/net/sunlance.c Wed Feb 13 20:03:43 2002 +++ b/drivers/net/sunlance.c Wed Feb 13 20:03:43 2002 @@ -925,7 +925,8 @@ STOP_LANCE(lp); - if (request_irq(dev->irq, &lance_interrupt, SA_SHIRQ, + if (request_irq(dev->irq, &lance_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, lancestr, (void *) dev)) { printk(KERN_ERR "Lance: Can't get irq %s\n", __irq_itoa(dev->irq)); return -EAGAIN; diff -Nru a/drivers/net/sunqe.c b/drivers/net/sunqe.c --- a/drivers/net/sunqe.c Wed Feb 13 20:03:48 2002 +++ b/drivers/net/sunqe.c Wed Feb 13 20:03:48 2002 @@ -896,7 +896,8 @@ * for it now. */ if (request_irq(sdev->irqs[0], &qec_interrupt, - SA_SHIRQ, "QuadEther", (void *) qecp)) { + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + "QuadEther", (void *) qecp)) { printk(KERN_ERR "QuadEther: Can't register QEC master irq handler.\n"); res = EAGAIN; goto qec_free_devs; diff -Nru a/drivers/net/tlan.c b/drivers/net/tlan.c --- a/drivers/net/tlan.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/tlan.c Wed Feb 13 20:03:35 2002 @@ -871,7 +871,8 @@ int err; priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); - err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + err = request_irq( dev->irq, TLan_HandleInterrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, TLanSignature, dev ); if ( err ) { printk(KERN_ERR "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); diff -Nru a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c --- a/drivers/net/tokenring/abyss.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/tokenring/abyss.c Wed Feb 13 20:03:41 2002 @@ -123,8 +123,8 @@ goto err_out_trdev; } - ret = request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, - dev->name, dev); + ret = request_irq(pdev->irq, tms380tr_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) goto err_out_region; diff -Nru a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c --- a/drivers/net/tokenring/ibmtr.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/tokenring/ibmtr.c Wed Feb 13 20:03:34 2002 @@ -662,7 +662,8 @@ /* The PCMCIA has already got the interrupt line and the io port, so no chance of anybody else getting it - MLP */ - if (request_irq(dev->irq = irq, &tok_interrupt, 0, "ibmtr", dev) != 0) { + if (request_irq(dev->irq = irq, &tok_interrupt, + SA_SAMPLE_NET_RANDOM, "ibmtr", dev) != 0) { DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n", irq); iounmap(t_mmio); diff -Nru a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c --- a/drivers/net/tokenring/lanstreamer.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/tokenring/lanstreamer.c Wed Feb 13 20:03:50 2002 @@ -535,7 +535,8 @@ rc=streamer_reset(dev); } - if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "lanstreamer", dev)) { + if (request_irq(dev->irq, &streamer_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "lanstreamer", dev)) { return -EAGAIN; } #if STREAMER_DEBUG diff -Nru a/drivers/net/tokenring/madgemc.c b/drivers/net/tokenring/madgemc.c --- a/drivers/net/tokenring/madgemc.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/tokenring/madgemc.c Wed Feb 13 20:03:50 2002 @@ -331,8 +331,9 @@ */ outb(0, dev->base_addr + MC_CONTROL_REG0); /* sanity */ madgemc_setsifsel(dev, 1); - if(request_irq(dev->irq, madgemc_interrupt, SA_SHIRQ, - "madgemc", dev)) + if(request_irq(dev->irq, madgemc_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + "madgemc", dev)) goto getout; madgemc_chipset_init(dev); /* enables interrupts! */ diff -Nru a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c --- a/drivers/net/tokenring/olympic.c Wed Feb 13 20:03:40 2002 +++ b/drivers/net/tokenring/olympic.c Wed Feb 13 20:03:40 2002 @@ -50,10 +50,16 @@ * adapter when live does not take the system down with it. * * 06/02/01 - Clean up, copy skb for small packets + * + * 06/22/01 - Add EISR error handling routines + * + * 07/19/01 - Improve bad LAA reporting, strip out freemem + * into a separate function, its called from 3 + * different places now. + * 02/09/01 - Replaced sleep_on. * * To Do: * - * Complete full Cardbus / hot-swap support. * Wake on lan * * If Problems do Occur @@ -105,7 +111,7 @@ */ static char version[] __devinitdata = -"Olympic.c v0.9.7 6/02/01 - Peter De Schrijver & Mike Phillips" ; +"Olympic.c v1.0.0 2/9/02 - Peter De Schrijver & Mike Phillips" ; static char *open_maj_error[] = {"No error", "Lobe Media Test", "Physical Insertion", "Address Verification", "Neighbor Notification (Ring Poll)", @@ -172,6 +178,7 @@ static int olympic_xmit(struct sk_buff *skb, struct net_device *dev); static int olympic_close(struct net_device *dev); static void olympic_set_rx_mode(struct net_device *dev); +static void olympic_freemem(struct net_device *dev) ; static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs); static struct net_device_stats * olympic_get_stats(struct net_device *dev); static int olympic_set_mac_address(struct net_device *dev, void *addr) ; @@ -396,7 +403,10 @@ char open_error[255] ; int i, open_finished = 1 ; - if(request_irq(dev->irq, &olympic_interrupt, SA_SHIRQ , "olympic", dev)) { + DECLARE_WAITQUEUE(wait,current) ; + + if(request_irq(dev->irq, &olympic_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, "olympic", dev)) { return -EAGAIN; } @@ -441,8 +451,12 @@ writew(swab16(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), init_srb+8); else writew(swab16(OPEN_ADAPTER_ENABLE_FDX), init_srb+8); + + /* Test OR of first 3 bytes as its totally possible for + * someone to set the first 2 bytes to be zero, although this + * is an error, the first byte must have bit 6 set to 1 */ - if (olympic_priv->olympic_laa[0]) { + if (olympic_priv->olympic_laa[0] | olympic_priv->olympic_laa[1] | olympic_priv->olympic_laa[2]) { writeb(olympic_priv->olympic_laa[0],init_srb+12); writeb(olympic_priv->olympic_laa[1],init_srb+13); writeb(olympic_priv->olympic_laa[2],init_srb+14); @@ -458,8 +472,12 @@ writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); t = jiffies ; + + add_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_INTERRUPTIBLE) ; + while(olympic_priv->srb_queued) { - interruptible_sleep_on_timeout(&olympic_priv->srb_wait, 60*HZ); + schedule() ; if(signal_pending(current)) { printk(KERN_WARNING "%s: Signal received in open.\n", dev->name); @@ -474,7 +492,11 @@ olympic_priv->srb_queued=0; break ; } + set_current_state(TASK_INTERRUPTIBLE) ; } + remove_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_RUNNING) ; + restore_flags(flags); #if OLYMPIC_DEBUG printk("init_srb(%p): ",init_srb); @@ -515,6 +537,17 @@ return -EIO ; } /* if autosense && open_finished */ + } else if (init_srb[2] == 0x32) { + printk(KERN_WARNING "%s: Invalid LAA: %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + olympic_priv->olympic_laa[0], + olympic_priv->olympic_laa[1], + olympic_priv->olympic_laa[2], + olympic_priv->olympic_laa[3], + olympic_priv->olympic_laa[4], + olympic_priv->olympic_laa[5]) ; + free_irq(dev->irq,dev) ; + return -EIO ; } else { printk(KERN_WARNING "%s: Bad OPEN response: %x\n", dev->name,init_srb[2]); free_irq(dev->irq, dev); @@ -634,7 +667,10 @@ olympic_priv->tx_ring_free=0; /* next entry in tx ring to use */ olympic_priv->tx_ring_last_status=OLYMPIC_TX_RING_SIZE-1; /* last processed tx status */ - writel(SISR_TX1_EOF | SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE,olympic_mmio+SISR_MASK_SUM); + writel(0xffffffff, olympic_mmio+EISR_RWM) ; /* clean the eisr */ + writel(0,olympic_mmio+EISR) ; + writel(EISR_MASK_OPTIONS,olympic_mmio+EISR_MASK) ; /* enables most of the TX error interrupts */ + writel(SISR_TX1_EOF | SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE | SISR_ERR,olympic_mmio+SISR_MASK_SUM); #if OLYMPIC_DEBUG printk("BMCTL: %x\n",readl(olympic_mmio+BMCTL_SUM)); @@ -822,6 +858,35 @@ } +static void olympic_freemem(struct net_device *dev) +{ + struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; + int i; + + for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); + if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { + pci_unmap_single(olympic_priv->pdev, + le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), + olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); + } + olympic_priv->rx_status_last_received++; + olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; + } + /* unmap rings */ + pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, + sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, + sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); + + pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, + sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, + sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); + + return ; +} + static void olympic_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev= (struct net_device *)dev_id; @@ -842,9 +907,33 @@ spin_lock(&olympic_priv->olympic_lock); + /* Hotswap gives us this on removal */ + if (sisr == 0xffffffff) { + printk(KERN_WARNING "%s: Hotswap adapter removal.\n",dev->name) ; + olympic_freemem(dev) ; + free_irq(dev->irq, dev) ; + dev->stop = NULL ; + spin_unlock(&olympic_priv->olympic_lock) ; + return ; + } + if (sisr & (SISR_SRB_REPLY | SISR_TX1_EOF | SISR_RX_STATUS | SISR_ADAPTER_CHECK | - SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_RX_NOBUF)) { + SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_RX_NOBUF | SISR_ERR)) { + /* If we ever get this the adapter is seriously dead. Only a reset is going to + * bring it back to life. We're talking pci bus errors and such like :( */ + if((sisr & SISR_ERR) && (readl(olympic_mmio+EISR) & EISR_MASK_OPTIONS)) { + printk(KERN_ERR "Olympic: EISR Error, EISR=%08x\n",readl(olympic_mmio+EISR)) ; + printk(KERN_ERR "The adapter must be reset to clear this condition.\n") ; + printk(KERN_ERR "Please report this error to the driver maintainer and/\n") ; + printk(KERN_ERR "or the linux-tr mailing list.\n") ; + olympic_freemem(dev) ; + free_irq(dev->irq, dev) ; + dev->stop = NULL ; + spin_unlock(&olympic_priv->olympic_lock) ; + return ; + } /* SISR_ERR */ + if(sisr & SISR_SRB_REPLY) { if(olympic_priv->srb_queued==1) { wake_up_interruptible(&olympic_priv->srb_wait); @@ -878,34 +967,12 @@ } /* SISR_RX_STATUS */ if (sisr & SISR_ADAPTER_CHECK) { - int i ; netif_stop_queue(dev); printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name); - writel(readl(olympic_mmio+LAPWWO),olympic_mmio+LAPA); - adapter_check_area = (u8 *)(olympic_mmio+LAPWWO) ; + writel(readl(olympic_mmio+LAPWWC),olympic_mmio+LAPA); + adapter_check_area = olympic_priv->olympic_lap + ((readl(olympic_mmio+LAPWWC)) & (~0xf800)) ; printk(KERN_WARNING "%s: Bytes %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",dev->name, readb(adapter_check_area+0), readb(adapter_check_area+1), readb(adapter_check_area+2), readb(adapter_check_area+3), readb(adapter_check_area+4), readb(adapter_check_area+5), readb(adapter_check_area+6), readb(adapter_check_area+7)) ; - /* The adapter is effectively dead, clean up and exit */ - for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); - if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { - pci_unmap_single(olympic_priv->pdev, - le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), - olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); - } - olympic_priv->rx_status_last_received++; - olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - } - /* unmap rings */ - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, - sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, - sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); - - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, - sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, - sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); - + olympic_freemem(dev) ; free_irq(dev->irq, dev) ; dev->stop = NULL ; spin_unlock(&olympic_priv->olympic_lock) ; @@ -980,7 +1047,8 @@ struct olympic_private *olympic_priv=(struct olympic_private *)dev->priv; u8 *olympic_mmio=olympic_priv->olympic_mmio,*srb; unsigned long t,flags; - int i; + + DECLARE_WAITQUEUE(wait,current) ; netif_stop_queue(dev); @@ -999,8 +1067,12 @@ writel(LISR_SRB_CMD,olympic_mmio+LISR_SUM); t = jiffies ; + + add_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_INTERRUPTIBLE) ; + while(olympic_priv->srb_queued) { - interruptible_sleep_on_timeout(&olympic_priv->srb_wait, jiffies+60*HZ); + schedule() ; if(signal_pending(current)) { printk(KERN_WARNING "%s: SRB timed out.\n",dev->name); printk(KERN_WARNING "SISR=%x MISR=%x\n",readl(olympic_mmio+SISR),readl(olympic_mmio+LISR)); @@ -1012,32 +1084,16 @@ olympic_priv->srb_queued=0; break ; } + set_current_state(TASK_INTERRUPTIBLE) ; } + remove_wait_queue(&olympic_priv->srb_wait,&wait) ; + set_current_state(TASK_RUNNING) ; restore_flags(flags) ; olympic_priv->rx_status_last_received++; olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - - for(i=0;irx_ring_skb[olympic_priv->rx_status_last_received]); - if (olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer != 0xdeadbeef) { - pci_unmap_single(olympic_priv->pdev, - le32_to_cpu(olympic_priv->olympic_rx_ring[olympic_priv->rx_status_last_received].buffer), - olympic_priv->pkt_buf_sz, PCI_DMA_FROMDEVICE); - } - olympic_priv->rx_status_last_received++; - olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1; - } - /* unmap rings */ - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_status_ring_dma_addr, - sizeof(struct olympic_rx_status) * OLYMPIC_RX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->rx_ring_dma_addr, - sizeof(struct olympic_rx_desc) * OLYMPIC_RX_RING_SIZE, PCI_DMA_TODEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_status_ring_dma_addr, - sizeof(struct olympic_tx_status) * OLYMPIC_TX_RING_SIZE, PCI_DMA_FROMDEVICE); - pci_unmap_single(olympic_priv->pdev, olympic_priv->tx_ring_dma_addr, - sizeof(struct olympic_tx_desc) * OLYMPIC_TX_RING_SIZE, PCI_DMA_TODEVICE); + olympic_freemem(dev) ; /* reset tx/rx fifo's and busmaster logic */ diff -Nru a/drivers/net/tokenring/olympic.h b/drivers/net/tokenring/olympic.h --- a/drivers/net/tokenring/olympic.h Wed Feb 13 20:03:51 2002 +++ b/drivers/net/tokenring/olympic.h Wed Feb 13 20:03:51 2002 @@ -79,6 +79,7 @@ #define EISR 0x34 #define EISR_RWM 0x38 #define EISR_MASK 0x3c +#define EISR_MASK_OPTIONS 0x001FFF7F #define LAPA 0x60 #define LAPWWO 0x64 diff -Nru a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c --- a/drivers/net/tokenring/smctr.c Wed Feb 13 20:03:54 2002 +++ b/drivers/net/tokenring/smctr.c Wed Feb 13 20:03:54 2002 @@ -544,7 +544,8 @@ dev->irq = 15; break; } - if(request_irq(dev->irq, smctr_interrupt, SA_SHIRQ, smctr_name, dev)) + if(request_irq(dev->irq, smctr_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, smctr_name, dev)) return (-ENODEV); /* Get RAM base */ diff -Nru a/drivers/net/tokenring/tmsisa.c b/drivers/net/tokenring/tmsisa.c --- a/drivers/net/tokenring/tmsisa.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/tokenring/tmsisa.c Wed Feb 13 20:03:35 2002 @@ -184,8 +184,8 @@ for(j = 0; irqlist[j] != 0; j++) { dev->irq = irqlist[j]; - if (!request_irq(dev->irq, tms380tr_interrupt, 0, - isa_cardname, dev)) + if (!request_irq(dev->irq, tms380tr_interrupt, + SA_SAMPLE_NET_RANDOM, isa_cardname, dev)) break; } diff -Nru a/drivers/net/tokenring/tmspci.c b/drivers/net/tokenring/tmspci.c --- a/drivers/net/tokenring/tmspci.c Wed Feb 13 20:03:56 2002 +++ b/drivers/net/tokenring/tmspci.c Wed Feb 13 20:03:56 2002 @@ -123,8 +123,8 @@ goto err_out_trdev; } - ret = request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, - dev->name, dev); + ret = request_irq(pdev->irq, tms380tr_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (ret) goto err_out_region; diff -Nru a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c --- a/drivers/net/tulip/tulip_core.c Wed Feb 13 20:03:55 2002 +++ b/drivers/net/tulip/tulip_core.c Wed Feb 13 20:03:55 2002 @@ -506,7 +506,9 @@ int retval; MOD_INC_USE_COUNT; - if ((retval = request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))) { + if ((retval = request_irq(dev->irq, &tulip_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + dev->name, dev))) { MOD_DEC_USE_COUNT; return retval; } diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/via-rhine.c Wed Feb 13 20:03:50 2002 @@ -1056,7 +1056,8 @@ /* Reset the chip. */ writew(CmdReset, ioaddr + ChipCmd); - i = request_irq(np->pdev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(np->pdev->irq, &via_rhine_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; diff -Nru a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c --- a/drivers/net/wan/c101.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/wan/c101.c Wed Feb 13 20:03:39 2002 @@ -251,7 +251,7 @@ } memset(card, 0, sizeof(card_t)); - if (request_irq(irq, sca_intr, 0, devname, card)) { + if (request_irq(irq, sca_intr, SA_SAMPLE_NET_RANDOM, devname, card)) { printk(KERN_ERR "c101: could not allocate IRQ\n"); c101_destroy_card(card); return(-EBUSY); diff -Nru a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c --- a/drivers/net/wan/comx-hw-comx.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/wan/comx-hw-comx.c Wed Feb 13 20:03:35 2002 @@ -469,8 +469,8 @@ if (check_region(dev->base_addr, hw->io_extent)) { return -EAGAIN; } - if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, - (void *)dev)) { + if (request_irq(dev->irq, COMX_interrupt, SA_SAMPLE_NET_RANDOM, + dev->name, (void *)dev)) { printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq); return -EAGAIN; } diff -Nru a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c --- a/drivers/net/wan/comx-hw-locomx.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/wan/comx-hw-locomx.c Wed Feb 13 20:03:35 2002 @@ -154,12 +154,10 @@ return -ENODEV; } - if (check_region(dev->base_addr, hw->io_extent)) { + if (!request_region(dev->base_addr, hw->io_extent, dev->name)) { 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; @@ -172,8 +170,8 @@ 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)) { + if(request_irq(dev->irq, z8530_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, dev->name, &hw->board)) { printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, dev->irq); ret=-EAGAIN; diff -Nru a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c --- a/drivers/net/wan/comx-hw-mixcom.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/wan/comx-hw-mixcom.c Wed Feb 13 20:03:50 2002 @@ -518,8 +518,8 @@ } if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) { - if (request_irq(dev->irq, MIXCOM_interrupt, 0, - dev->name, (void *)dev)) { + if (request_irq(dev->irq, MIXCOM_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, (void *)dev)) { printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq); ret = -EAGAIN; goto err_release_region; diff -Nru a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c --- a/drivers/net/wan/cosa.c Wed Feb 13 20:03:30 2002 +++ b/drivers/net/wan/cosa.c Wed Feb 13 20:03:30 2002 @@ -541,7 +541,8 @@ cosa->nchannels = 2; /* FIXME: how to determine this? */ request_region(base, is_8bit(cosa)?2:4, cosa->type); - if (request_irq(cosa->irq, cosa_interrupt, 0, cosa->type, cosa)) + if (request_irq(cosa->irq, cosa_interrupt, SA_SAMPLE_NET_RANDOM, + cosa->type, cosa)) goto bad1; if (request_dma(cosa->dma, cosa->type)) { free_irq(cosa->irq, cosa); diff -Nru a/drivers/net/wan/cycx_main.c b/drivers/net/wan/cycx_main.c --- a/drivers/net/wan/cycx_main.c Wed Feb 13 20:03:53 2002 +++ b/drivers/net/wan/cycx_main.c Wed Feb 13 20:03:53 2002 @@ -214,7 +214,8 @@ /* Allocate IRQ */ irq = conf->irq == 2 ? 9 : conf->irq; /* IRQ2 -> IRQ9 */ - if (request_irq(irq, cycx_isr, 0, wandev->name, card)) { + if (request_irq(irq, cycx_isr, SA_SAMPLE_NET_RANDOM, + wandev->name, card)) { printk(KERN_ERR "%s: can't reserve IRQ %d!\n", wandev->name, irq); goto out; diff -Nru a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c --- a/drivers/net/wan/dscc4.c Wed Feb 13 20:03:41 2002 +++ b/drivers/net/wan/dscc4.c Wed Feb 13 20:03:41 2002 @@ -506,7 +506,8 @@ priv = (struct dscc4_pci_priv *)pci_get_drvdata(pdev); - if (request_irq(pdev->irq, &dscc4_irq, SA_SHIRQ, "dscc4", priv->root)) { + if (request_irq(pdev->irq, &dscc4_irq, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + "dscc4", priv->root)) { printk(KERN_WARNING "dscc4: IRQ %d is busy\n", pdev->irq); goto err_out_iounmap; } diff -Nru a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c --- a/drivers/net/wan/farsync.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/wan/farsync.c Wed Feb 13 20:03:35 2002 @@ -1737,7 +1737,8 @@ card->state = FST_RESET; /* Register the interrupt handler */ - if ( request_irq ( card->irq, fst_intr, SA_SHIRQ, FST_DEV_NAME, card )) + if ( request_irq ( card->irq, fst_intr, SA_SHIRQ | SA_SAMPLE_NET_RANDOM, + FST_DEV_NAME, card )) { printk_err ("Unable to register interrupt %d\n", card->irq ); diff -Nru a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c --- a/drivers/net/wan/hostess_sv11.c Wed Feb 13 20:03:36 2002 +++ b/drivers/net/wan/hostess_sv11.c Wed Feb 13 20:03:36 2002 @@ -254,7 +254,9 @@ /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if(request_irq(irq, &z8530_interrupt, SA_INTERRUPT, "Hostess SV/11", dev)<0) + if(request_irq(irq, &z8530_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, + "Hostess SV/11", dev)<0) { printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq); goto fail1; diff -Nru a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c --- a/drivers/net/wan/lmc/lmc_main.c Wed Feb 13 20:03:45 2002 +++ b/drivers/net/wan/lmc/lmc_main.c Wed Feb 13 20:03:45 2002 @@ -1178,7 +1178,8 @@ lmc_softreset (sc); /* Since we have to use PCI bus, this should work on x86,alpha,ppc */ - if (request_irq (dev->irq, &lmc_interrupt, SA_SHIRQ, dev->name, dev)){ + if (request_irq (dev->irq, &lmc_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev)){ printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq); lmc_trace(dev, "lmc_open irq failed out"); return -EAGAIN; diff -Nru a/drivers/net/wan/lmc/lmc_var.h b/drivers/net/wan/lmc/lmc_var.h --- a/drivers/net/wan/lmc/lmc_var.h Wed Feb 13 20:03:51 2002 +++ b/drivers/net/wan/lmc/lmc_var.h Wed Feb 13 20:03:51 2002 @@ -87,7 +87,7 @@ lmc_delay(); \ LMC_CSR_WRITE((sc), csr_9, 0x30000); \ lmc_delay(); \ - n--; }} while(0); + n--; }} while(0) struct lmc_regfile_t { lmc_csrptr_t csr_busmode; /* CSR0 */ diff -Nru a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c --- a/drivers/net/wan/n2.c Wed Feb 13 20:03:39 2002 +++ b/drivers/net/wan/n2.c Wed Feb 13 20:03:39 2002 @@ -398,7 +398,7 @@ } card->io = io; - if (request_irq(irq, &sca_intr, 0, devname, card)) { + if (request_irq(irq, &sca_intr, SA_SAMPLE_NET_RANDOM, devname, card)) { printk(KERN_ERR "n2: could not allocate IRQ\n"); n2_destroy_card(card); return(-EBUSY); diff -Nru a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c --- a/drivers/net/wan/sbni.c Wed Feb 13 20:03:47 2002 +++ b/drivers/net/wan/sbni.c Wed Feb 13 20:03:47 2002 @@ -1168,7 +1168,8 @@ } } - if( request_irq(dev->irq, sbni_interrupt, SA_SHIRQ, dev->name, dev) ) { + if( request_irq(dev->irq, sbni_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev) ) { printk( KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq ); return -EAGAIN; diff -Nru a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c --- a/drivers/net/wan/sdla.c Wed Feb 13 20:03:54 2002 +++ b/drivers/net/wan/sdla.c Wed Feb 13 20:03:54 2002 @@ -1469,7 +1469,8 @@ } dev->irq = map->irq; - if (request_irq(dev->irq, &sdla_isr, 0, dev->name, dev)) + if (request_irq(dev->irq, &sdla_isr, SA_SAMPLE_NET_RANDOM, + dev->name, dev)) return(-EAGAIN); if (flp->type == SDLA_S507) diff -Nru a/drivers/net/wan/sdlamain.c b/drivers/net/wan/sdlamain.c --- a/drivers/net/wan/sdlamain.c Wed Feb 13 20:03:37 2002 +++ b/drivers/net/wan/sdlamain.c Wed Feb 13 20:03:37 2002 @@ -495,8 +495,7 @@ /* when using the S514 PCI adapter */ if(request_irq(irq, sdla_isr, - (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0, - wandev->name, card)){ + (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0 | SA_SAMPLE_NET_RANDOM, wandev->name, card)){ printk(KERN_INFO "%s: Can't reserve IRQ %d!\n", wandev->name, irq); return -EINVAL; diff -Nru a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c --- a/drivers/net/wan/sealevel.c Wed Feb 13 20:03:34 2002 +++ b/drivers/net/wan/sealevel.c Wed Feb 13 20:03:34 2002 @@ -219,12 +219,11 @@ * Get the needed I/O space */ - if(check_region(iobase, 8)) + if(!request_region(iobase, 8, "Sealevel 4021")) { printk(KERN_WARNING "sealevel: I/O 0x%X already in use.\n", iobase); return NULL; } - request_region(iobase, 8, "Sealevel 4021"); b=(struct slvl_board *)kmalloc(sizeof(struct slvl_board), GFP_KERNEL); if(!b) @@ -281,7 +280,8 @@ /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if(request_irq(irq, &z8530_interrupt, SA_INTERRUPT, "SeaLevel", dev)<0) + if(request_irq(irq, &z8530_interrupt, + SA_INTERRUPT | SA_SAMPLE_NET_RANDOM, "SeaLevel", dev)<0) { printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq); goto fail1_1; diff -Nru a/drivers/net/wavelan.c b/drivers/net/wavelan.c --- a/drivers/net/wavelan.c Wed Feb 13 20:03:47 2002 +++ b/drivers/net/wavelan.c Wed Feb 13 20:03:47 2002 @@ -3900,7 +3900,8 @@ return -ENXIO; } - if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) + if (request_irq(dev->irq, &wavelan_interrupt, SA_SAMPLE_NET_RANDOM, + "WaveLAN", dev) != 0) { #ifdef DEBUG_CONFIG_ERROR printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", diff -Nru a/drivers/net/wd.c b/drivers/net/wd.c --- a/drivers/net/wd.c Wed Feb 13 20:03:33 2002 +++ b/drivers/net/wd.c Wed Feb 13 20:03:33 2002 @@ -266,7 +266,8 @@ /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - i = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + i = request_irq(dev->irq, ei_interrupt, + SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) { printk (" unable to get IRQ %d.\n", dev->irq); kfree(dev->priv); diff -Nru a/drivers/net/winbond-840.c b/drivers/net/winbond-840.c --- a/drivers/net/winbond-840.c Wed Feb 13 20:03:38 2002 +++ b/drivers/net/winbond-840.c Wed Feb 13 20:03:38 2002 @@ -691,7 +691,8 @@ writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */ netif_device_detach(dev); - i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &intr_handler, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) goto out_err; diff -Nru a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c --- a/drivers/net/wireless/airo.c Wed Feb 13 20:03:35 2002 +++ b/drivers/net/wireless/airo.c Wed Feb 13 20:03:35 2002 @@ -1191,7 +1191,8 @@ dev->irq = irq; dev->base_addr = port; - rc = request_irq( dev->irq, airo_interrupt, SA_SHIRQ, dev->name, dev ); + rc = request_irq( dev->irq, airo_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev ); if (rc) { printk(KERN_ERR "airo: register interrupt %d failed, rc %d\n", irq, rc ); goto err_out_unlink; diff -Nru a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c --- a/drivers/net/wireless/airport.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/wireless/airport.c Wed Feb 13 20:03:50 2002 @@ -231,7 +231,8 @@ /* Reset it before we get the interrupt */ hermes_reset(hw); - if (request_irq(ndev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) { + if (request_irq(ndev->irq, orinoco_interrupt, 0 + SA_SAMPLE_NET_RANDOM, "Airport", (void *)priv)) { printk(KERN_ERR "airport: Couldn't get IRQ %d\n", ndev->irq); goto failed; } diff -Nru a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c --- a/drivers/net/wireless/orinoco_plx.c Wed Feb 13 20:03:50 2002 +++ b/drivers/net/wireless/orinoco_plx.c Wed Feb 13 20:03:50 2002 @@ -297,7 +297,8 @@ hermes_struct_init(&(priv->hw), dev->base_addr); pci_set_drvdata(pdev, priv); - err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv); + err = request_irq(pdev->irq, orinoco_plx_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, priv); if (err) { printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq); err = -EBUSY; diff -Nru a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c --- a/drivers/net/yellowfin.c Wed Feb 13 20:03:49 2002 +++ b/drivers/net/yellowfin.c Wed Feb 13 20:03:49 2002 @@ -625,7 +625,8 @@ /* Reset the chip. */ outl(0x80000000, ioaddr + DMACtrl); - i = request_irq(dev->irq, &yellowfin_interrupt, SA_SHIRQ, dev->name, dev); + i = request_irq(dev->irq, &yellowfin_interrupt, + SA_SHIRQ | SA_SAMPLE_NET_RANDOM, dev->name, dev); if (i) return i; if (yellowfin_debug > 1) diff -Nru a/drivers/net/znet.c b/drivers/net/znet.c --- a/drivers/net/znet.c Wed Feb 13 20:03:29 2002 +++ b/drivers/net/znet.c Wed Feb 13 20:03:29 2002 @@ -252,7 +252,8 @@ zn.lock = SPIN_LOCK_UNLOCKED; /* These should never fail. You can't add devices to a sealed box! */ - if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet", dev) + if (request_irq(dev->irq, &znet_interrupt, SA_SAMPLE_NET_RANDOM, + "ZNet", dev) || request_dma(zn.rx_dma,"ZNet rx") || request_dma(zn.tx_dma,"ZNet tx")) { printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name); diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c --- a/drivers/pci/pci.c Wed Feb 13 20:03:42 2002 +++ b/drivers/pci/pci.c Wed Feb 13 20:03:42 2002 @@ -868,6 +868,91 @@ return 0; } +struct page* +pci_vmalloc_to_page(unsigned long virt) +{ + struct page *ret = NULL; + pmd_t *pmd; + pte_t *pte; + pgd_t *pgd; + + pgd = pgd_offset_k(virt); + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, virt); + if (!pmd_none(*pmd)) { + pte = pte_offset(pmd, virt); + if (pte_present(*pte)) { + ret = pte_page(*pte); + } + } + } + return ret; +} + +struct scatterlist* +pci_vmalloc_to_sg(unsigned long virt, int nr_pages) +{ + struct scatterlist *sglist; + struct page *pg; + int i; + + sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); + if (NULL == sglist) + return NULL; + memset(sglist,0,sizeof(struct scatterlist)*nr_pages); + for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { + pg = pci_vmalloc_to_page(virt); + if (NULL == pg) + goto err; + if (PageHighMem(pg)) + /* if you run into this one you should have + * used vmalloc_32() instead of vmalloc() */ + BUG(); + sglist[i].page = pg; + sglist[i].length = PAGE_SIZE; + } + return sglist; + + err: + kfree(sglist); + return NULL; +} + +struct scatterlist* +pci_iobuf_to_sg(struct kiobuf *iobuf) +{ + struct scatterlist *sglist; + int i = 0; + + sglist = kmalloc(sizeof(struct scatterlist) * iobuf->nr_pages, + GFP_KERNEL); + if (NULL == sglist) + return NULL; + memset(sglist,0,sizeof(struct scatterlist) * iobuf->nr_pages); + + if (NULL == iobuf->maplist[0]) + goto err; + if (PageHighMem(iobuf->maplist[0])) + /* DMA to highmem pages might not work */ + goto err; + sglist[0].page = iobuf->maplist[0]; + sglist[0].offset = iobuf->offset; + sglist[0].length = PAGE_SIZE - iobuf->offset; + for (i = 1; i < iobuf->nr_pages; i++) { + if (NULL == iobuf->maplist[i]) + goto err; + if (PageHighMem(iobuf->maplist[i])) + goto err; + sglist[i].page = iobuf->maplist[i]; + sglist[i].length = PAGE_SIZE; + } + return sglist; + + err: + kfree(sglist); + return NULL; +} + /* * Translate the low bits of the PCI base * to the resource type @@ -1981,6 +2066,9 @@ EXPORT_SYMBOL(pci_set_master); EXPORT_SYMBOL(pci_set_dma_mask); EXPORT_SYMBOL(pci_dac_set_dma_mask); +EXPORT_SYMBOL(pci_vmalloc_to_page); +EXPORT_SYMBOL(pci_vmalloc_to_sg); +EXPORT_SYMBOL(pci_iobuf_to_sg); EXPORT_SYMBOL(pci_assign_resource); EXPORT_SYMBOL(pci_register_driver); EXPORT_SYMBOL(pci_unregister_driver); diff -Nru a/drivers/pci/quirks.c b/drivers/pci/quirks.c --- a/drivers/pci/quirks.c Wed Feb 13 20:03:30 2002 +++ b/drivers/pci/quirks.c Wed Feb 13 20:03:30 2002 @@ -444,13 +444,15 @@ static void __init quirk_amd_ordering(struct pci_dev *dev) { u32 pcic; - - pci_read_config_dword(dev, 0x42, &pcic); - if((pcic&2)==0) + pci_read_config_dword(dev, 0x4C, &pcic); + if((pcic&6)!=6) { - pcic |= 2; - printk(KERN_WARNING "BIOS disabled PCI ordering compliance, so we enabled it again.\n"); - pci_write_config_dword(dev, 0x42, pcic); + pcic |= 6; + printk(KERN_WARNING "BIOS failed to enable PCI standards compliance, fixing this error.\n"); + pci_write_config_dword(dev, 0x4C, pcic); + pci_read_config_dword(dev, 0x84, &pcic); + pcic |= (1<<23); /* Required in this mode */ + pci_write_config_dword(dev, 0x84, pcic); } } diff -Nru a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c --- a/drivers/pci/setup-bus.c Wed Feb 13 20:03:50 2002 +++ b/drivers/pci/setup-bus.c Wed Feb 13 20:03:50 2002 @@ -201,6 +201,16 @@ b->resource[0]->end = ranges->io_end - 1; b->resource[1]->end = ranges->mem_end - 1; + /* Add bridge resources to the resource tree. */ + if (b->resource[0]->end > b->resource[0]->start && + request_resource(bus->resource[0], b->resource[0]) < 0) + printk(KERN_ERR "PCI: failed to reserve IO " + "for bus %d\n", b->number); + if (b->resource[1]->end > b->resource[1]->start && + request_resource(bus->resource[1], b->resource[1]) < 0) + printk(KERN_ERR "PCI: failed to reserve MEM " + "for bus %d\n", b->number); + pci_setup_bridge(b); } } diff -Nru a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c --- a/drivers/pcmcia/cs.c Wed Feb 13 20:03:41 2002 +++ b/drivers/pcmcia/cs.c Wed Feb 13 20:03:41 2002 @@ -1268,7 +1268,7 @@ } else c = CONFIG(handle); if ((c != NULL) && (c->state & CONFIG_LOCKED) && - (c->IntType & INT_MEMORY_AND_IO)) { + (c->IntType & (INT_MEMORY_AND_IO|INT_ZOOMED_VIDEO))) { u_char reg; if (c->Present & PRESENT_PIN_REPLACE) { read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); @@ -1696,6 +1696,8 @@ c->Attributes = req->Attributes; if (req->IntType & INT_MEMORY_AND_IO) s->socket.flags |= SS_IOCARD; + if (req->IntType & INT_ZOOMED_VIDEO) + s->socket.flags |= SS_ZVCARD|SS_IOCARD; if (req->Attributes & CONF_ENABLE_DMA) s->socket.flags |= SS_DMA_MODE; if (req->Attributes & CONF_ENABLE_SPKR) diff -Nru a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c --- a/drivers/pcmcia/yenta.c Wed Feb 13 20:03:32 2002 +++ b/drivers/pcmcia/yenta.c Wed Feb 13 20:03:32 2002 @@ -234,6 +234,8 @@ static int yenta_set_socket(pci_socket_t *socket, socket_state_t *state) { + /* hack for the moment */ + static void yenta_set_zoomvideo(pci_socket_t *s, int onoff); u16 bridge; if (state->flags & SS_DEBOUNCED) { @@ -286,6 +288,8 @@ } exca_writeb(socket, I365_CSCINT, reg); exca_readb(socket, I365_CSC); + + yenta_set_zoomvideo(socket, state->flags & SS_ZVCARD); } config_writew(socket, CB_BRIDGE_CONTROL, bridge); /* Socket event mask: get card insert/remove events.. */ @@ -783,6 +787,29 @@ #include "ti113x.h" #include "ricoh.h" + +/* + * This belongs somewhere else - maybe as a pci socket op - as + * it is only valid for TI devices + */ + +static void yenta_set_zoomvideo(pci_socket_t *socket, int onoff) +{ + u8 reg; + + if(socket->dev->vendor != PCI_VENDOR_ID_TI) + return; + + /* If we don't have a Zoom Video switch this is harmless, + we just tristate the unused (ZV) lines */ + reg = config_readb(socket, TI113X_CARD_CONTROL); + if (onoff) + /* Zoom zoom, we will all go together, zoom zoom, zoom zoom */ + reg |= TI113X_CCR_ZVENABLE; + else + reg &= ~TI113X_CCR_ZVENABLE; + config_writeb(socket, TI113X_CARD_CONTROL, reg); +} /* * Different cardbus controllers have slightly different diff -Nru a/drivers/pnp/Config.in b/drivers/pnp/Config.in --- a/drivers/pnp/Config.in Wed Feb 13 20:03:30 2002 +++ b/drivers/pnp/Config.in Wed Feb 13 20:03:30 2002 @@ -8,4 +8,8 @@ dep_tristate ' ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_bool ' PNPBIOS support (EXPERIMENTAL)' CONFIG_PNPBIOS $CONFIG_PNP +fi + endmenu diff -Nru a/drivers/pnp/Makefile b/drivers/pnp/Makefile --- a/drivers/pnp/Makefile Wed Feb 13 20:03:30 2002 +++ b/drivers/pnp/Makefile Wed Feb 13 20:03:30 2002 @@ -10,15 +10,22 @@ O_TARGET := pnp.o -export-objs := isapnp.o -list-multi := isa-pnp.o +export-objs := isapnp.o pnpbios_core.o +multi-objs := isa-pnp.o pnpbios.o -proc-$(CONFIG_PROC_FS) = isapnp_proc.o -isa-pnp-objs := isapnp.o quirks.o $(proc-y) +isa-pnp-proc-$(CONFIG_PROC_FS) = isapnp_proc.o +pnpbios-proc-$(CONFIG_PROC_FS) = pnpbios_proc.o + +isa-pnp-objs := isapnp.o quirks.o $(isa-pnp-proc-y) +pnpbios-objs := pnpbios_core.o $(pnpbios-proc-y) obj-$(CONFIG_ISAPNP) += isa-pnp.o +obj-$(CONFIG_PNPBIOS) += pnpbios.o include $(TOPDIR)/Rules.make isa-pnp.o: $(isa-pnp-objs) $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) + +pnpbios.o: $(pnpbios-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs) diff -Nru a/drivers/pnp/pnpbios_core.c b/drivers/pnp/pnpbios_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pnp/pnpbios_core.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,1283 @@ +/* + * PnP BIOS services + * + * Originally (C) 1998 Christian Schmidt + * Modifications (c) 1998 Tom Lees + * Minor reorganizations by David Hinds + * Modifications (c) 2001 by Thomas Hood + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * References: + * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corporation + * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 + * Plug and Play BIOS Clarification Paper, October 6, 1994 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * + * PnP BIOS INTERFACE + * + */ + +/* PnP BIOS signature: "$PnP" */ +#define PNP_SIGNATURE (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24)) + +#pragma pack(1) +union pnp_bios_expansion_header { + struct { + u32 signature; /* "$PnP" */ + u8 version; /* in BCD */ + u8 length; /* length in bytes, currently 21h */ + u16 control; /* system capabilities */ + u8 checksum; /* all bytes must add up to 0 */ + + u32 eventflag; /* phys. address of the event flag */ + u16 rmoffset; /* real mode entry point */ + u16 rmcseg; + u16 pm16offset; /* 16 bit protected mode entry */ + u32 pm16cseg; + u32 deviceID; /* EISA encoded system ID or 0 */ + u16 rmdseg; /* real mode data segment */ + u32 pm16dseg; /* 16 bit pm data segment base */ + } fields; + char chars[0x21]; /* To calculate the checksum */ +}; +#pragma pack() + +static struct { + u16 offset; + u16 segment; +} pnp_bios_callpoint; + +static union pnp_bios_expansion_header * pnp_bios_hdr = NULL; + +/* The PnP BIOS entries in the GDT */ +#define PNP_GDT (0x0060) +#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ +#define PNP_CS16 (PNP_GDT+0x08) /* code segment for BIOS */ +#define PNP_DS (PNP_GDT+0x10) /* data segment for BIOS */ +#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */ +#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */ + +/* + * These are some opcodes for a "static asmlinkage" + * As this code is *not* executed inside the linux kernel segment, but in a + * alias at offset 0, we need a far return that can not be compiled by + * default (please, prove me wrong! this is *really* ugly!) + * This is the only way to get the bios to return into the kernel code, + * because the bios code runs in 16 bit protected mode and therefore can only + * return to the caller if the call is within the first 64kB, and the linux + * kernel begins at offset 3GB... + */ + +asmlinkage void pnp_bios_callfunc(void); + +__asm__( + ".text \n" + __ALIGN_STR "\n" + SYMBOL_NAME_STR(pnp_bios_callfunc) ":\n" + " pushl %edx \n" + " pushl %ecx \n" + " pushl %ebx \n" + " pushl %eax \n" + " lcallw " SYMBOL_NAME_STR(pnp_bios_callpoint) "\n" + " addl $16, %esp \n" + " lret \n" + ".previous \n" +); + +#define Q_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], __va((u32)(address))); \ +set_limit (gdt [(selname) >> 3], size) + +#define Q2_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], (u32)(address)); \ +set_limit (gdt [(selname) >> 3], size) + +/* + * At some point we want to use this stack frame pointer to unwind + * after PnP BIOS oopses. + */ + +u32 pnp_bios_fault_esp; +u32 pnp_bios_fault_eip; +u32 pnp_bios_is_utter_crap = 0; + +static spinlock_t pnp_bios_lock; + +static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, + u16 arg4, u16 arg5, u16 arg6, u16 arg7) +{ + unsigned long flags; + u16 status; + + /* + * PnP BIOSes are generally not terribly re-entrant. + * Also, don't rely on them to save everything correctly. + */ + if(pnp_bios_is_utter_crap) + return PNP_FUNCTION_NOT_SUPPORTED; + + /* On some boxes IRQ's during PnP BIOS calls are deadly. */ + spin_lock_irqsave(&pnp_bios_lock, flags); + __cli(); + __asm__ __volatile__( + "pushl %%ebp\n\t" + "pushl %%edi\n\t" + "pushl %%esi\n\t" + "pushl %%ds\n\t" + "pushl %%es\n\t" + "pushl %%fs\n\t" + "pushl %%gs\n\t" + "pushfl\n\t" + "movl %%esp, pnp_bios_fault_esp\n\t" + "movl $1f, pnp_bios_fault_eip\n\t" + "lcall %5,%6\n\t" + "1:popfl\n\t" + "popl %%gs\n\t" + "popl %%fs\n\t" + "popl %%es\n\t" + "popl %%ds\n\t" + "popl %%esi\n\t" + "popl %%edi\n\t" + "popl %%ebp\n\t" + : "=a" (status) + : "0" ((func) | (((u32)arg1) << 16)), + "b" ((arg2) | (((u32)arg3) << 16)), + "c" ((arg4) | (((u32)arg5) << 16)), + "d" ((arg6) | (((u32)arg7) << 16)), + "i" (PNP_CS32), + "i" (0) + : "memory" + ); + spin_unlock_irqrestore(&pnp_bios_lock, flags); + + /* If we get here and this is set then the PnP BIOS faulted on us. */ + if(pnp_bios_is_utter_crap) + { + printk(KERN_ERR "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue.\n"); + printk(KERN_ERR "PnPBIOS: You may need to reboot with the \"nobiospnp\" option to operate stably.\n"); + printk(KERN_ERR "PnPBIOS: Check with your vendor for an updated BIOS\n"); + } + + return status; +} + + +/* + * + * UTILITY FUNCTIONS + * + */ + +void *pnpbios_kmalloc(size_t size, int f) +{ + void *p = kmalloc( size, f ); + if ( p == NULL ) + printk(KERN_ERR "PnPBIOS: kmalloc() failed.\n"); + return p; +} + +/* + * Call this only after init time + */ +static int pnp_bios_present(void) +{ + return (pnp_bios_hdr != NULL); +} + +/* Forward declaration */ +static void update_devlist( u8 nodenum, struct pnp_bios_node *data ); + + +/* + * + * PnP BIOS ACCESS FUNCTIONS + * + */ + +#define PNP_GET_NUM_SYS_DEV_NODES 0x00 +#define PNP_GET_SYS_DEV_NODE 0x01 +#define PNP_SET_SYS_DEV_NODE 0x02 +#define PNP_GET_EVENT 0x03 +#define PNP_SEND_MESSAGE 0x04 +#define PNP_GET_DOCKING_STATION_INFORMATION 0x05 +#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09 +#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a +#define PNP_GET_APM_ID_TABLE 0x0b +#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40 +#define PNP_GET_ESCD_INFO 0x41 +#define PNP_READ_ESCD 0x42 +#define PNP_WRITE_ESCD 0x43 + +/* + * Call PnP BIOS with function 0x00, "get number of system device nodes" + */ +static int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info)); + status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0); + data->no_nodes &= 0xff; + return status; +} + +int pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + int status = __pnp_bios_dev_node_info( data ); + if ( status ) + printk(KERN_WARNING "PnPBIOS: dev_node_info: Unexpected status 0x%x\n", status); + return status; +} + +/* + * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible + * death if they are asked to access the "current" configuration. + * Therefore, if it's a matter of indifference, it's better to call + * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0. + */ + +/* + * Call PnP BIOS with function 0x01, "get system device node" + * Input: *nodenum = desired node, + * boot = whether to get nonvolatile boot (!=0) + * or volatile current (0) config + * Output: *nodenum=next node or 0xff if no more nodes + */ +static int __pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + if ( !boot & pnpbios_dont_use_current_config ) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char)); + Q2_SET_SEL(PNP_TS2, data, 64 * 1024); + status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0); + return status; +} + +int pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + int status; + status = __pnp_bios_get_dev_node( nodenum, boot, data ); + if ( status ) + printk(KERN_WARNING "PnPBIOS: get_dev_node: Unexpected 0x%x\n", status); + return status; +} + + +/* + * Call PnP BIOS with function 0x02, "set system device node" + * Input: *nodenum = desired node, + * boot = whether to set nonvolatile boot (!=0) + * or volatile current (0) config + */ +static int __pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + if ( !boot & pnpbios_dont_use_current_config ) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536); + status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0); + return status; +} + +int pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + int status; + status = __pnp_bios_set_dev_node( nodenum, boot, data ); + if ( status ) { + printk(KERN_WARNING "PnPBIOS: set_dev_node: Unexpected set_dev_node status 0x%x\n", status); + return status; + } + if ( !boot ) { + /* Update devlist */ + u8 thisnodenum = nodenum; + status = __pnp_bios_get_dev_node( &nodenum, boot, data ); + if ( status ) { + printk(KERN_WARNING "PnPBIOS: set_dev_node: Unexpected get_dev_node status 0x%x\n", status); + return status; + } + update_devlist( thisnodenum, data ); + } + return status; +} + +#if needed +/* + * Call PnP BIOS with function 0x03, "get event" + */ +static int pnp_bios_get_event(u16 *event) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, event, sizeof(u16)); + status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x04, "send message" + */ +static int pnp_bios_send_message(u16 message) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0); + return status; +} +#endif + +#ifdef CONFIG_HOTPLUG +/* + * Call PnP BIOS with function 0x05, "get docking station information" + */ +static int pnp_bios_dock_station_info(struct pnp_docking_station_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_docking_station_info)); + status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x09, "set statically allocated resource + * information" + */ +static int pnp_bios_set_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, *((u16 *) info)); + status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} +#endif + +/* + * Call PnP BIOS with function 0x0a, "get statically allocated resource + * information" + */ +int pnp_bios_get_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, 64 * 1024); + status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} + +#if needed +/* + * Call PnP BIOS with function 0x0b, "get APM id table" + */ +static int pnp_bios_apm_id_table(char *table, u16 *size) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, table, *size); + Q2_SET_SEL(PNP_TS2, size, sizeof(u16)); + status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0); + return status; +} +#endif + +/* + * Call PnP BIOS with function 0x40, "get isa pnp configuration structure" + */ +int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_isa_config_struc)); + status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + return status; +} + +/* + * Call PnP BIOS with function 0x41, "get ESCD info" + */ +int pnp_bios_escd_info(struct escd_info_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct escd_info_struc)); + status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS); + return status; +} + +/* + * Call PnP BIOS function 0x42, "read ESCD" + * nvram_base is determined by calling escd_info + */ +int pnp_bios_read_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + return status; +} + +#if needed +/* + * Call PnP BIOS function 0x43, "write ESCD" + */ +static int pnp_bios_write_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + return status; +} +#endif + + +/* + * + * DOCKING FUNCTIONS + * + */ + +#ifdef CONFIG_HOTPLUG + +static int unloading = 0; +static struct completion unload_sem; + +/* + * (Much of this belongs in a shared routine somewhere) + */ + +static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) +{ + char *argv [3], **envp, *buf, *scratch; + int i = 0, value; + + if (!hotplug_path [0]) + return -ENOENT; + if (!current->fs->root) { + return -EAGAIN; + } + if (!(envp = (char **) pnpbios_kmalloc (20 * sizeof (char *), GFP_KERNEL))) { + return -ENOMEM; + } + if (!(buf = pnpbios_kmalloc (256, GFP_KERNEL))) { + kfree (envp); + return -ENOMEM; + } + + /* only one standardized param to hotplug command: type */ + argv [0] = hotplug_path; + argv [1] = "dock"; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + +#ifdef DEBUG + /* hint that policy agent should enter no-stdout debug mode */ + envp [i++] = "DEBUG=kernel"; +#endif + /* extensible set of named bus-specific parameters, + * supporting multiple driver selection algorithms. + */ + scratch = buf; + + /* action: add, remove */ + envp [i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", dock?"add":"remove") + 1; + + /* Report the ident for the dock */ + envp [i++] = scratch; + scratch += sprintf (scratch, "DOCK=%x/%x/%x", + info->location_id, info->serial, info->capabilities); + envp[i] = 0; + + value = call_usermodehelper (argv [0], argv, envp); + kfree (buf); + kfree (envp); + return 0; +} + +/* + * Poll the PnP docking at regular intervals + */ +static int pnp_dock_thread(void * unused) +{ + static struct pnp_docking_station_info now; + int docked = -1, d = 0; + daemonize(); + reparent_to_init(); + strcpy(current->comm, "kpnpbios"); + while(!unloading && !signal_pending(current)) + { + int err; + + /* + * Poll every 2 seconds + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ*2); + if(signal_pending(current)) + break; + + err = pnp_bios_dock_station_info(&now); + + switch(err) + { + /* + * No dock to manage + */ + case PNP_FUNCTION_NOT_SUPPORTED: + complete_and_exit(&unload_sem, 0); + case PNP_SYSTEM_NOT_DOCKED: + d = 0; + break; + case PNP_SUCCESS: + d = 1; + break; + default: + printk(KERN_WARNING "PnPBIOS: pnp_dock_thread: Unexpected status 0x%x returned by BIOS.\n", err); + continue; + } + if(d != docked) + { + if(pnp_dock_event(d, &now)==0) + { + docked = d; +#if 0 + printk(KERN_INFO "PnPBIOS: Docking station %stached.\n", docked?"at":"de"); +#endif + } + } + } + complete_and_exit(&unload_sem, 0); +} + +#endif /* CONFIG_HOTPLUG */ + + +/* + * + * NODE DATA PARSING FUNCTIONS + * + */ + +static void add_irqresource(struct pci_dev *dev, int irq) +{ + int i = 0; + while (!(dev->irq_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_IRQ) i++; + if (i < DEVICE_COUNT_IRQ) { + dev->irq_resource[i].start = (unsigned long) irq; + dev->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag + } +} + +static void add_dmaresource(struct pci_dev *dev, int dma) +{ + int i = 0; + while (!(dev->dma_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_DMA) i++; + if (i < DEVICE_COUNT_DMA) { + dev->dma_resource[i].start = (unsigned long) dma; + dev->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag + } +} + +static void add_ioresource(struct pci_dev *dev, int io, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = (unsigned long) io; + dev->resource[i].end = (unsigned long)(io + len - 1); + dev->resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag + } +} + +static void add_memresource(struct pci_dev *dev, int mem, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = (unsigned long) mem; + dev->resource[i].end = (unsigned long)(mem + len - 1); + dev->resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag + } +} + +static void node_resource_data_to_dev(struct pnp_bios_node *node, struct pci_dev *dev) +{ + unsigned char *p = node->data, *lastp=NULL; + int i; + + /* + * First, set resource info to default values + */ + for (i=0;iresource[i].start = 0; // "disabled" + dev->resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;iirq_resource[i].start = (unsigned long)-1; // "disabled" + dev->irq_resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;idma_resource[i].start = (unsigned long)-1; // "disabled" + dev->dma_resource[i].flags = IORESOURCE_UNSET; + } + + /* + * Fill in dev resource info + */ + while ( (char *)p < ((char *)node->data + node->size )) { + if(p==lastp) break; + + if( p[0] & 0x80 ) {// large item + switch (p[0] & 0x7f) { + case 0x01: // memory + { + int io = *(short *) &p[4]; + int len = *(short *) &p[10]; + add_memresource(dev, io, len); + break; + } + case 0x02: // device name + { + int len = *(short *) &p[1]; + memcpy(dev->name, p + 3, len >= 80 ? 79 : len); + break; + } + case 0x05: // 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[16]; + add_memresource(dev, io, len); + break; + } + case 0x06: // fixed location 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[8]; + add_memresource(dev, io, len); + break; + } + } /* switch */ + lastp = p+3; + p = p + p[1] + p[2]*256 + 3; + continue; + } + if ((p[0]>>3) == 0x0f) // end tag + break; + switch (p[0]>>3) { + case 0x04: // irq + { + int i, mask, irq = -1; + mask= p[1] + p[2]*256; + for (i=0;i<16;i++, mask=mask>>1) + if(mask & 0x01) irq=i; + add_irqresource(dev, irq); + break; + } + case 0x05: // dma + { + int i, mask, dma = -1; + mask = p[1]; + for (i=0;i<8;i++, mask = mask>>1) + if(mask & 0x01) dma=i; + add_dmaresource(dev, dma); + break; + } + case 0x08: // io + { + int io= p[2] + p[3] *256; + int len = p[7]; + add_ioresource(dev, io, len); + break; + } + case 0x09: // fixed location io + { + int io = p[1] + p[2] * 256; + int len = p[3]; + add_ioresource(dev, io, len); + break; + } + } /* switch */ + lastp=p+1; + p = p + (p[0] & 0x07) + 1; + + } /* while */ + + return; +} + + +/* + * + * DEVICE LIST MANAGEMENT FUNCTIONS + * + * + * Some of these are exported to give public access + * + * Question: Why maintain a device list when the PnP BIOS can + * list devices for us? Answer: Some PnP BIOSes can't report + * the current configuration, only the boot configuration. + * The boot configuration can be changed, so we need to keep + * a record of what the configuration was when we booted; + * presumably it continues to describe the current config. + * For those BIOSes that can change the current config, we + * keep the information in the devlist up to date. + * + * Note that it is currently assumed that the list does not + * grow or shrink in size after init time, and slot_name + * never changes. The list is protected by a spinlock. + */ + +static LIST_HEAD(pnpbios_devices); + +static spinlock_t pnpbios_devices_lock; + +static int inline insert_device(struct pci_dev *dev) +{ + + /* + * FIXME: Check for re-add of existing node; + * return -1 if node already present + */ + + /* We don't lock because we only do this at init time */ + list_add_tail(&dev->global_list, &pnpbios_devices); + + return 0; +} + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) +// +static void inline pnpid32_to_pnpid(u32 id, char *str) +{ + const char *hex = "0123456789abcdef"; + + id = be32_to_cpu(id); + str[0] = CHAR(id, 26); + str[1] = CHAR(id, 21); + str[2] = CHAR(id,16); + str[3] = HEX(id, 12); + str[4] = HEX(id, 8); + str[5] = HEX(id, 4); + str[6] = HEX(id, 0); + str[7] = '\0'; + + return; +} +// +#undef CHAR +#undef HEX + +/* + * Build a linked list of pci_devs in order of ascending node number + * Called only at init time. + */ +static void __init build_devlist(void) +{ + int i; + int nodenum; + int nodes_got = 0; + int devs = 0; + struct pnp_bios_node *node; + struct pnp_dev_node_info node_info; + struct pci_dev *dev; + + if (!pnp_bios_present ()) + return; + + if (pnp_bios_dev_node_info(&node_info) != 0) + return; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) + return; + + for(i=0,nodenum=0; i<0xff && nodenum!=0xff; i++) { + int thisnodenum = nodenum; + /* For now we build the list from the "boot" config + * because asking for the "current" config causes + * some BIOSes to crash. */ + if (pnp_bios_get_dev_node((u8 *)&nodenum, (char )1 , node)) { + printk(KERN_WARNING "PnPBIOS: PnP BIOS reported error on attempt to get dev node.\n"); + break; + } + /* The BIOS returns with nodenum = the next node number */ + if (nodenum < thisnodenum) { + printk(KERN_WARNING "PnPBIOS: Node number is out of sequence. Naughty BIOS!\n"); + break; + } + nodes_got++; + dev = pnpbios_kmalloc(sizeof (struct pci_dev), GFP_KERNEL); + if (!dev) + break; + memset(dev,0,sizeof(struct pci_dev)); + dev->devfn=thisnodenum; + memcpy(dev->name,"PNPBIOS",8); + pnpid32_to_pnpid(node->eisa_id,dev->slot_name); + node_resource_data_to_dev(node,dev); + if(insert_device(dev)<0) + kfree(dev); + else + devs++; + } + kfree(node); + + printk(KERN_INFO "PnPBIOS: %i node%s reported by PnP BIOS; %i recorded by driver.\n", + nodes_got, nodes_got != 1 ? "s" : "", devs); +} + +static struct pci_dev *find_device_by_nodenum( u8 nodenum ) +{ + struct pci_dev *dev; + + pnpbios_for_each_dev(dev) { + if(dev->devfn == nodenum) + return dev; + } + + return NULL; +} + +static void update_devlist( u8 nodenum, struct pnp_bios_node *data ) +{ + unsigned long flags; + struct pci_dev *dev; + + spin_lock_irqsave(&pnpbios_devices_lock, flags); + dev = find_device_by_nodenum( nodenum ); + if ( dev ) { + node_resource_data_to_dev(data,dev); + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); + + return; +} + + +/* + * + * DRIVER REGISTRATION FUNCTIONS + * + * + * Exported to give public access + * + */ + +static LIST_HEAD(pnpbios_drivers); + +static const struct pnpbios_device_id * +match_device(const struct pnpbios_device_id *ids, const struct pci_dev *dev) +{ + while (*ids->id) + { + if(memcmp(ids->id, dev->slot_name, 7)==0) + return ids; + ids++; + } + return NULL; +} + +static int announce_device(struct pnpbios_driver *drv, struct pci_dev *dev) +{ + const struct pnpbios_device_id *id; + struct pci_dev tmpdev; + int ret; + + if (drv->id_table) { + id = match_device(drv->id_table, dev); + if (!id) + return 0; + } else + id = NULL; + + memcpy( &tmpdev, dev, sizeof(struct pci_dev)); + tmpdev.global_list.prev = NULL; + tmpdev.global_list.next = NULL; + + dev_probe_lock(); + /* Obviously, probe() should not call any pnpbios functions */ + ret = drv->probe(&tmpdev, id); + dev_probe_unlock(); + if (ret < 1) + return 0; + + dev->driver = (void *)drv; + + return 1; +} + +/** + * pnpbios_register_driver - register a new pci driver + * @drv: the driver structure to register + * + * Adds the driver structure to the list of registered drivers + * + * For each device in the pnpbios device list that matches one of + * the ids in drv->id_table, calls the driver's "probe" function with + * arguments (1) a pointer to a *temporary* struct pci_dev containing + * resource info for the device, and (2) a pointer to the id string + * of the device. Expects the probe function to return 1 if the + * driver claims the device (otherwise 0) in which case, marks the + * device as having this driver. + * + * Returns the number of pci devices which were claimed by the driver + * during registration. The driver remains registered even if the + * return value is zero. + */ +int pnpbios_register_driver(struct pnpbios_driver *drv) +{ + struct pci_dev *dev; + unsigned long flags; + int count = 0; + + list_add_tail(&drv->node, &pnpbios_drivers); + spin_lock_irqsave(&pnpbios_devices_lock, flags); + pnpbios_for_each_dev(dev) { + if (!pnpbios_dev_driver(dev)) + count += announce_device(drv, dev); + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); + return count; +} + +EXPORT_SYMBOL(pnpbios_register_driver); + +/** + * pnpbios_unregister_driver - unregister a pci driver + * @drv: the driver structure to unregister + * + * Deletes the driver structure from the list of registered PnPBIOS + * drivers, gives it a chance to clean up by calling its "remove" + * function for each device it was responsible for, and marks those + * devices as driverless. + */ +void pnpbios_unregister_driver(struct pnpbios_driver *drv) +{ + unsigned long flags; + struct pci_dev *dev; + + list_del(&drv->node); + spin_lock_irqsave(&pnpbios_devices_lock, flags); + pnpbios_for_each_dev(dev) { + if (dev->driver == (void *)drv) { + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); +} + +EXPORT_SYMBOL(pnpbios_unregister_driver); + + +/* + * + * RESOURCE RESERVATION FUNCTIONS + * + * + * Used only at init time + * + */ + +static void __init reserve_ioport_range(char *pnpid, int start, int end) +{ + struct resource *res; + char *regionid; + + regionid = pnpbios_kmalloc(16, GFP_KERNEL); + if ( regionid == NULL ) + return; + sprintf(regionid, "PnPBIOS %s", pnpid); + res = request_region(start,end-start+1,regionid); + if ( res == NULL ) + kfree( regionid ); + else + res->flags &= ~IORESOURCE_BUSY; + /* + * Failures at this point are usually harmless. pci quirks for + * example do reserve stuff they know about too, so we may well + * have double reservations. + */ + printk(KERN_INFO + "PnPBIOS: %s: ioport range 0x%x-0x%x %s reserved.\n", + pnpid, start, end, + NULL != res ? "has been" : "could not be" + ); + + return; +} + +static void __init reserve_resources_of_dev( struct pci_dev *dev ) +{ + int i; + + for (i=0;iresource[i].flags & IORESOURCE_UNSET ) + /* end of resources */ + break; + if (dev->resource[i].flags & IORESOURCE_IO) { + /* ioport */ + if ( dev->resource[i].start == 0 ) + /* disabled */ + /* Do nothing */ + continue; + if ( dev->resource[i].start < 0x100 ) + /* + * Below 0x100 is only standard PC hardware + * (pics, kbd, timer, dma, ...) + * We should not get resource conflicts there, + * and the kernel reserves these anyway + * (see arch/i386/kernel/setup.c). + * So, do nothing + */ + continue; + if ( dev->resource[i].end < dev->resource[i].start ) + /* invalid endpoint */ + /* Do nothing */ + continue; + reserve_ioport_range( + dev->slot_name, + dev->resource[i].start, + dev->resource[i].end + ); + } else if (dev->resource[i].flags & IORESOURCE_MEM) { + /* iomem */ + /* For now do nothing */ + continue; + } else { + /* Neither ioport nor iomem */ + /* Do nothing */ + continue; + } + } + + return; +} + +static void __init reserve_resources( void ) +{ + struct pci_dev *dev; + + pnpbios_for_each_dev(dev) { + if ( + 0 != strcmp(dev->slot_name,"PNP0c01") && /* memory controller */ + 0 != strcmp(dev->slot_name,"PNP0c02") /* system peripheral: other */ + ) { + continue; + } + reserve_resources_of_dev(dev); + } + + return; +} + + +/* + * + * INIT AND EXIT + * + */ + +extern int is_sony_vaio_laptop; + +static int pnpbios_disabled; /* = 0 */ +static int dont_reserve_resources; /* = 0 */ +int pnpbios_dont_use_current_config; /* = 0 */ + +#ifndef MODULE +static int __init pnpbios_setup(char *str) +{ + int invert; + + while ((str != NULL) && (*str != '\0')) { + if (strncmp(str, "off", 3) == 0) + pnpbios_disabled=1; + if (strncmp(str, "on", 2) == 0) + pnpbios_disabled=0; + invert = (strncmp(str, "no-", 3) == 0); + if (invert) + str += 3; + if (strncmp(str, "curr", 4) == 0) + pnpbios_dont_use_current_config = invert; + if (strncmp(str, "res", 3) == 0) + dont_reserve_resources = invert; + str = strchr(str, ','); + if (str != NULL) + str += strspn(str, ", \t"); + } + + return 1; +} + +__setup("pnpbios=", pnpbios_setup); +#endif + +void __init pnpbios_init(void) +{ + union pnp_bios_expansion_header *check; + u8 sum; + int i, length; + + spin_lock_init(&pnp_bios_lock); + spin_lock_init(&pnpbios_devices_lock); + + if(pnpbios_disabled) { + printk(KERN_INFO "PnPBIOS: Disabled.\n"); + return; + } + + if ( is_sony_vaio_laptop ) + pnpbios_dont_use_current_config = 1; + + /* + * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS + * structure and, if one is found, sets up the selectors and + * entry points + */ + for (check = (union pnp_bios_expansion_header *) __va(0xf0000); + check < (union pnp_bios_expansion_header *) __va(0xffff0); + ((void *) (check)) += 16) { + if (check->fields.signature != PNP_SIGNATURE) + continue; + length = check->fields.length; + if (!length) + continue; + for (sum = 0, i = 0; i < length; i++) + sum += check->chars[i]; + if (sum) + continue; + if (check->fields.version < 0x10) { + printk(KERN_WARNING "PnPBIOS: PnP BIOS version %d.%d is not supported.\n", + check->fields.version >> 4, + check->fields.version & 15); + continue; + } + printk(KERN_INFO "PnPBIOS: Found PnP BIOS installation structure at 0x%p.\n", check); + printk(KERN_INFO "PnPBIOS: PnP BIOS version %d.%d, entry 0x%x:0x%x, dseg 0x%x.\n", + check->fields.version >> 4, check->fields.version & 15, + check->fields.pm16cseg, check->fields.pm16offset, + check->fields.pm16dseg); + Q2_SET_SEL(PNP_CS32, &pnp_bios_callfunc, 64 * 1024); + Q_SET_SEL(PNP_CS16, check->fields.pm16cseg, 64 * 1024); + Q_SET_SEL(PNP_DS, check->fields.pm16dseg, 64 * 1024); + pnp_bios_callpoint.offset = check->fields.pm16offset; + pnp_bios_callpoint.segment = PNP_CS16; + pnp_bios_hdr = check; + break; + } + build_devlist(); + if ( ! dont_reserve_resources ) + reserve_resources(); +#ifdef CONFIG_PROC_FS + pnpbios_proc_init(); +#endif +} + +static void pnpbios_thread_init(void) +{ +#ifdef CONFIG_HOTPLUG + init_completion(&unload_sem); + if(kernel_thread(pnp_dock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)>0) + unloading = 0; +#endif +} + +#ifndef MODULE + +module_init(pnpbios_thread_init); + +#else + +MODULE_LICENSE("GPL"); + +static int pnpbios_init_all(void) +{ + pnpbios_init(); + if(!pnpbios_present()) + return -ENODEV; + pnpbios_thread_init(); + return 0; +} + +/* We have to run it early and not as a module. */ +module_init(pnpbios_init_all); + +#ifdef CONFIG_HOTPLUG +static void pnpbios_exit(void) +{ + /* free_resources() ought to go here */ + /* pnpbios_proc_done() */ + unloading = 1; + wait_for_completion(&unload_sem); +} + +module_exit(pnpbios_exit); + +#endif +#endif diff -Nru a/drivers/pnp/pnpbios_proc.c b/drivers/pnp/pnpbios_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pnp/pnpbios_proc.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,247 @@ +/* + * pnp_proc.c: /proc/bus/pnp interface for Plug and Play devices + * + * Written by David Hinds, dahinds@users.sourceforge.net + */ + +//#include +#define __NO_VERSION__ +//#include + +#include +#include +#include +#include +#include +#include + +static struct proc_dir_entry *proc_pnp = NULL; +static struct proc_dir_entry *proc_pnp_boot = NULL; +static struct pnp_dev_node_info node_info; + +/* + * Note that we assume that procfs is supplying us with + * a buffer large enough for all our needs. As of writing + * this code, the buffer is PAGE_SIZE in size, which on + * i386 is 4 KiB. + */ + +static int proc_read_pnpconfig(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_isa_config_struc pnps; + + if ( pnp_bios_isapnp_config(&pnps) ) { + printk(KERN_ERR "PNPBIOS: Error reading ISA PNP config\n"); + return 0; + } + + return sprintf(buf, + "structure_revision %d\n" + "number_of_CSNs %d\n" + "ISA_read_data_port 0x%x\n", + pnps.revision, + pnps.no_csns, + pnps.isa_rd_data_port + ); +} + +static int proc_read_legacyres(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + if ( pnp_bios_get_stat_res(buf) ) { + printk(KERN_ERR "PNPBIOS: Error reading static resource allocation table for legacy ISA devices\n"); + return 0; + } + return 4096; // FIXME: Should return actual length +} + +static int proc_read_escdinfo(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct escd_info_struc escd; + + if ( pnp_bios_escd_info(&escd) ) { + printk(KERN_ERR "PNPBIOS: Error reading ESCD info\n"); + return 0; + } + + return sprintf(buf, + "min_ESCD_write_size %d\n" + "ESCD_size %d\n" + "NVRAM_base 0x%x\n", + escd.min_escd_write_size, + escd.escd_size, + escd.nv_storage_base + ); +} + +static int proc_read_escd(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct escd_info_struc escd; + int len; + + if ( pnp_bios_escd_info(&escd) ) { + printk(KERN_ERR "PNPBIOS: Error reading ESCD info\n"); + return 0; + } + + // FIXME: Handle larger sizes by reading into temporary buffer + if (escd.escd_size > 4096) { + printk(KERN_ERR "PNPBIOS: Error: read buffer not large enough for ESCD data\n"); + return 0; + } + + if ( pnp_bios_read_escd(buf, escd.nv_storage_base) ) { + printk(KERN_ERR "PNPBIOS: Error reading ESCD data\n"); + return 0; + } + + len = (unsigned char)(buf[0]) + (unsigned char)(buf[1])*256; + if (len > 4096) { + printk(KERN_ERR "PNPBIOS: Error: ESCD data read overflowed buffer!\n"); + return 0; + } + return len; +} + +static int proc_read_devices(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int i, len; + u8 nodenum; + char *p = buf; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + for (i=0,nodenum=0; i<0xff && nodenum!=0xff; i++) { + if ( pnp_bios_get_dev_node(&nodenum, 1, node) ) + break; + /* + * FIXME: Make sure we don't write beyond end of buf! + * Then we can remove the length check below + */ + p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", + node->handle, node->eisa_id, + node->type_code[0], node->type_code[1], + node->type_code[2], node->flags); + } + kfree(node); + len = p - buf; + if ( len > 4096 ) { + printk(KERN_ERR "PNPBIOS: Error: Device data read overflowed buffer!\n"); + return 0; + } + return len; +} + +static int proc_read_node(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + int len; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if ( pnp_bios_get_dev_node(&nodenum, boot, node) ) + return -EIO; + len = node->size - sizeof(struct pnp_bios_node); + memcpy(buf, node->data, len); + kfree(node); + return len; +} + +static int proc_write_node(struct file *file, const char *buf, + unsigned long count, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if ( pnp_bios_get_dev_node(&nodenum, boot, node) ) + return -EIO; + if (count != node->size - sizeof(struct pnp_bios_node)) + return -EINVAL; + memcpy(node->data, buf, count); + if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) + return -EINVAL; + kfree(node); + return count; +} + +/* + * When this is called, pnpbios functions are assumed to + * work and the pnpbios_dont_use_current_config flag + * should already have been set to the appropriate value + */ +void pnpbios_proc_init( void ) +{ + struct pnp_bios_node *node; + struct proc_dir_entry *ent; + char name[3]; + int i; + u8 nodenum; + + if (pnp_bios_dev_node_info(&node_info) != 0) return; + + proc_pnp = proc_mkdir("pnp", proc_bus); + if (!proc_pnp) return; + proc_pnp_boot = proc_mkdir("boot", proc_pnp); + if (!proc_pnp_boot) return; + create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL); + create_proc_read_entry("configuration_info", 0, proc_pnp, proc_read_pnpconfig, NULL); + create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo, NULL); + create_proc_read_entry("escd", 0, proc_pnp, proc_read_escd, NULL); + create_proc_read_entry("legacy_device_resources", 0, proc_pnp, proc_read_legacyres, NULL); + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return; + for (i=0,nodenum = 0; i<0xff && nodenum != 0xff; i++) { + if (pnp_bios_get_dev_node(&nodenum, 1, node) != 0) + break; + sprintf(name, "%02x", node->handle); + if ( !pnpbios_dont_use_current_config ) { + ent = create_proc_entry(name, 0, proc_pnp); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle); + } + } + ent = create_proc_entry(name, 0, proc_pnp_boot); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle+0x100); + } + } + kfree(node); +} + +void pnpbios_proc_done(void) +{ + int i; + char name[3]; + + if (!proc_pnp) return; + + for (i=0; i<0xff; i++) { + sprintf(name, "%02x", i); + if ( !pnpbios_dont_use_current_config ) + remove_proc_entry(name, proc_pnp); + remove_proc_entry(name, proc_pnp_boot); + } + remove_proc_entry("legacy_device_resources", proc_pnp); + remove_proc_entry("escd", proc_pnp); + remove_proc_entry("escd_info", proc_pnp); + remove_proc_entry("configuration_info", proc_pnp); + remove_proc_entry("devices", proc_pnp); + remove_proc_entry("boot", proc_pnp); + remove_proc_entry("pnp", proc_bus); +} diff -Nru a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h --- a/drivers/s390/block/dasd_int.h Wed Feb 13 20:03:37 2002 +++ b/drivers/s390/block/dasd_int.h Wed Feb 13 20:03:37 2002 @@ -203,14 +203,14 @@ debug_sprintf_event(d_device->debug_area,d_level,\ DASD_DEVICE_FORMAT_STRING d_str "\n",\ d_device, d_data);\ -} while(0); +} while(0) #define DASD_DEVICE_DEBUG_EXCEPTION(d_level, d_device, d_str, d_data...)\ do {\ if ( d_device->debug_area != NULL )\ debug_sprintf_exception(d_device->debug_area,d_level,\ DASD_DEVICE_FORMAT_STRING d_str "\n",\ d_device, d_data);\ -} while(0); +} while(0) #define DASD_DRIVER_FORMAT_STRING "Driver: <[%p]>" #define DASD_DRIVER_DEBUG_EVENT(d_level, d_fn, d_str, d_data...)\ @@ -219,14 +219,14 @@ debug_sprintf_event(dasd_debug_area, d_level,\ DASD_DRIVER_FORMAT_STRING #d_fn ":" d_str "\n",\ d_fn, d_data);\ -} while(0); +} while(0) #define DASD_DRIVER_DEBUG_EXCEPTION(d_level, d_fn, d_str, d_data...)\ do {\ if ( dasd_debug_area != NULL )\ debug_sprintf_exception(dasd_debug_area, d_level,\ DASD_DRIVER_FORMAT_STRING #d_fn ":" d_str "\n",\ d_fn, d_data);\ -} while(0); +} while(0) struct dasd_device_t; struct request; diff -Nru a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h --- a/drivers/scsi/3w-xxxx.h Wed Feb 13 20:03:40 2002 +++ b/drivers/scsi/3w-xxxx.h Wed Feb 13 20:03:40 2002 @@ -213,7 +213,7 @@ #ifdef TW_DEBUG #define dprintk(msg...) printk(msg) #else -#define dprintk(msg...) do { } while(0); +#define dprintk(msg...) do { } while(0) #endif /* Scatter Gather List Entry */ diff -Nru a/drivers/scsi/Config.in b/drivers/scsi/Config.in --- a/drivers/scsi/Config.in Wed Feb 13 20:03:50 2002 +++ b/drivers/scsi/Config.in Wed Feb 13 20:03:50 2002 @@ -16,6 +16,7 @@ bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 fi +dep_tristate ' SCSI media changer support' CONFIG_CHR_DEV_SCH $CONFIG_SCSI dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile --- a/drivers/scsi/Makefile Wed Feb 13 20:03:41 2002 +++ b/drivers/scsi/Makefile Wed Feb 13 20:03:41 2002 @@ -135,6 +135,7 @@ obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o obj-$(CONFIG_CHR_DEV_SG) += sg.o +obj-$(CONFIG_CHR_DEV_SCH) += ch.o list-multi := scsi_mod.o sd_mod.o sr_mod.o initio.o a100u2w.o cpqfc.o scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o \ diff -Nru a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c --- a/drivers/scsi/aacraid/aachba.c Wed Feb 13 20:03:56 2002 +++ b/drivers/scsi/aacraid/aachba.c Wed Feb 13 20:03:56 2002 @@ -494,8 +494,9 @@ scsicmd->use_sg, scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, (u32)scsicmd->SCp.ptr, scsicmd->request_bufflen, - scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); + pci_unmap_single(dev->pdev, (dma_addr_t)(long)scsicmd->SCp.ptr, + scsicmd->request_bufflen, + scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); readreply = (struct aac_read_reply *)fib_data(fibptr); if (le32_to_cpu(readreply->status) == ST_OK) scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD; @@ -537,8 +538,9 @@ scsicmd->use_sg, scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, (u32)scsicmd->SCp.ptr, scsicmd->request_bufflen, - scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); + pci_unmap_single(dev->pdev, (dma_addr_t)(long)scsicmd->SCp.ptr, + scsicmd->request_bufflen, + scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); writereply = (struct aac_write_reply *) fib_data(fibptr); if (le32_to_cpu(writereply->status) == ST_OK) @@ -641,10 +643,10 @@ } else if(scsicmd->request_bufflen) { - u32 addr; + dma_addr_t addr; addr = pci_map_single(dev->pdev, scsicmd->request_buffer, scsicmd->request_bufflen, scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); - scsicmd->SCp.ptr = (void *)addr; + scsicmd->SCp.ptr = (void *)(long)addr; readcmd->sg.sg[0].addr = cpu_to_le32(addr); readcmd->sg.sg[0].count = cpu_to_le32(scsicmd->request_bufflen); @@ -763,14 +765,14 @@ } else if(scsicmd->request_bufflen) { - u32 addr; + dma_addr_t addr; addr = pci_map_single(dev->pdev, scsicmd->request_buffer, scsicmd->request_bufflen, scsi_to_pci_dma_dir(scsicmd->sc_data_direction)); writecmd->sg.sg[0].addr = cpu_to_le32(addr); writecmd->sg.sg[0].count = cpu_to_le32(scsicmd->request_bufflen); - scsicmd->SCp.ptr = (void *)addr; + scsicmd->SCp.ptr = (void *)(long)addr; byte_count = scsicmd->request_bufflen; if (byte_count > (64 * 1024)) diff -Nru a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h --- a/drivers/scsi/aacraid/aacraid.h Wed Feb 13 20:03:56 2002 +++ b/drivers/scsi/aacraid/aacraid.h Wed Feb 13 20:03:56 2002 @@ -1168,7 +1168,17 @@ u32 options; u32 OEM; }; - + +static inline u32 fib2addr(struct hw_fib *hw) +{ + return (u32)hw; +} + +static inline struct hw_fib *addr2fib(u32 addr) +{ + return (struct hw_fib *)addr; +} + const char *aac_driverinfo(struct Scsi_Host *); struct fib *fib_alloc(struct aac_dev *dev); int fib_setup(struct aac_dev *dev); diff -Nru a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c --- a/drivers/scsi/aacraid/comminit.c Wed Feb 13 20:03:30 2002 +++ b/drivers/scsi/aacraid/comminit.c Wed Feb 13 20:03:30 2002 @@ -65,9 +65,9 @@ printk(KERN_ERR "aacraid: unable to create mapping.\n"); return 0; } - dev->comm_addr = (void *)base; + dev->comm_addr = (void *)base; dev->comm_phys = phys; - dev->comm_size = size; + dev->comm_size = size; dev->init = (struct aac_init *)(base + fibsize); dev->init_pa = (struct aac_init *)(phys + fibsize); @@ -82,7 +82,7 @@ * Adapter Fibs are the first thing allocated so that they * start page aligned */ - init->AdapterFibsVirtualAddress = cpu_to_le32(base); + init->AdapterFibsVirtualAddress = cpu_to_le32((long)base); init->AdapterFibsPhysicalAddress = cpu_to_le32(phys); init->AdapterFibsSize = cpu_to_le32(fibsize); init->AdapterFibAlign = cpu_to_le32(sizeof(struct hw_fib)); diff -Nru a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c --- a/drivers/scsi/aacraid/commsup.c Wed Feb 13 20:03:35 2002 +++ b/drivers/scsi/aacraid/commsup.c Wed Feb 13 20:03:35 2002 @@ -445,13 +445,18 @@ fib->header.XferState |= cpu_to_le32(ResponseExpected); FIB_COUNTER_INCREMENT(aac_config.NormalSent); } - fib->header.SenderData = (unsigned long)fibptr; /* for callback */ + /* + * Map the fib into 32bits by using the fib number + */ + fib->header.SenderData = fibptr-&dev->fibs[0]; /* for callback */ /* * Set FIB state to indicate where it came from and if we want a * response from the adapter. Also load the command from the * caller. + * + * Map the hw fib pointer as a 32bit value */ - fib->header.SenderFibAddress = cpu_to_le32((u32)fib); + fib->header.SenderFibAddress = fib2addr(fib); fib->header.Command = cpu_to_le16(command); fib->header.XferState |= cpu_to_le32(SentFromHost); fibptr->fib->header.Flags = 0; /* Zero the flags field - its internal only... */ @@ -756,9 +761,9 @@ if (cp[length] != 0) cp[length] = 0; if (level == LOG_HIGH_ERROR) - printk(KERN_WARNING "aacraid:%s.\n", cp); + printk(KERN_WARNING "aacraid:%s", cp); else - printk(KERN_INFO "aacraid:%s.\n", cp); + printk(KERN_INFO "aacraid:%s", cp); memset(cp, 0, 256); } diff -Nru a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c --- a/drivers/scsi/aacraid/dpcsup.c Wed Feb 13 20:03:54 2002 +++ b/drivers/scsi/aacraid/dpcsup.c Wed Feb 13 20:03:54 2002 @@ -77,9 +77,9 @@ int fast; fast = (int) (entry->addr & 0x01); - fib = (struct hw_fib *) (entry->addr & ~0x01); + fib = addr2fib(entry->addr & ~0x01); aac_consumer_free(dev, q, HostNormRespQueue); - fibctx = (struct fib *)fib->header.SenderData; + fibctx = &dev->fibs[fib->header.SenderData]; /* * Remove this fibctx from the Outstanding I/O queue. * But only if it has not already been timed out. @@ -172,7 +172,7 @@ while(aac_consumer_get(dev, q, &entry)) { struct hw_fib * fib; - fib = (struct hw_fib *)entry->addr; + fib = addr2fib(entry->addr); if (dev->aif_thread) { list_add_tail(&fib->header.FibLinks, &q->cmdq); diff -Nru a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c --- a/drivers/scsi/aacraid/linit.c Wed Feb 13 20:03:30 2002 +++ b/drivers/scsi/aacraid/linit.c Wed Feb 13 20:03:30 2002 @@ -35,7 +35,7 @@ * */ -#define AAC_DRIVER_VERSION "0.9.9ac2-rel" +#define AAC_DRIVER_VERSION "0.9.9ac4-rel" #define AAC_DRIVER_BUILD_DATE __DATE__ #include diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h --- a/drivers/scsi/aic7xxx/aic7xxx_linux_host.h Wed Feb 13 20:03:50 2002 +++ b/drivers/scsi/aic7xxx/aic7xxx_linux_host.h Wed Feb 13 20:03:50 2002 @@ -89,7 +89,8 @@ present: 0, /* number of 7xxx's present */\ unchecked_isa_dma: 0, /* no memory DMA restrictions */\ use_clustering: ENABLE_CLUSTERING, \ - use_new_eh_code: 1 \ + use_new_eh_code: 1, \ + can_do_varyio: 1 \ } #endif /* _AIC7XXX_LINUX_HOST_H_ */ diff -Nru a/drivers/scsi/ch.c b/drivers/scsi/ch.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/scsi/ch.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,862 @@ +/* + * SCSI Media Changer device driver for Linux 2.4 + * + * (c) 1996-2001 Gerd Knorr + * + */ + +#define VERSION "0.18" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* here are all the ioctls */ + +#define MAJOR_NR SCSI_CHANGER_MAJOR +#define DEVICE_NAME "SCSI Changer" +#define DEVICE_ON(device) +#define DEVICE_OFF(device) +#define CH_EXTRA_DEVS 2 + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" +#include "ch.h" +#include + +#define WRITE_TO_USER(user,kernel,size) \ + { if (copy_to_user((user), (kernel), (size))) return -EFAULT; } +#define READ_FROM_USER(kernel,user,size) \ + { if (copy_from_user((kernel), (user), (size))) return -EFAULT; } + +/* insmod options */ +static int check_busy = 1; /* busy check, can be turned off using this */ +static int debug = 0; /* you can turn on misc debug messages with this */ +static int reinit = 1; + +MODULE_SUPPORTED_DEVICE("sch"); +MODULE_DESCRIPTION("device driver for scsi media changer devices"); +MODULE_AUTHOR("Gerd Knorr "); +MODULE_LICENSE("GPL"); + +MODULE_PARM(check_busy,"i"); +MODULE_PARM_DESC(check_busy, \ + "enable/disable busy check for data transfer elements (default: on)"); + +MODULE_PARM(reinit,"i"); +MODULE_PARM_DESC(reinit, \ + "reinitialize element status on unit attention (default: on)"); + +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"enable/disable debug messages (default: off)"); + +/* ------------------------------------------------------------------- */ + +#define MAX_RETRIES 1 +#define CH_TIMEOUT (3600 * HZ) + +static int ch_init(void); +static void ch_finish(void); +static int ch_attach(Scsi_Device *); +static int ch_detect(Scsi_Device *); +static void ch_detach(Scsi_Device *); +static int ch_open(struct inode * inode, struct file * filp); +static int ch_release(struct inode * inode, struct file * filp); +static int ch_ioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg); + +struct Scsi_Device_Template ch_template = +{ + name: "media changer", + tag: "sch", + scsi_type: TYPE_MEDIUM_CHANGER, + major: SCSI_CHANGER_MAJOR, + detect: ch_detect, + init: ch_init, + finish: ch_finish, + attach: ch_attach, + detach: ch_detach +}; + +static struct file_operations changer_fops = +{ + owner: THIS_MODULE, + open: ch_open, + release: ch_release, + ioctl: ch_ioctl, +}; + +static struct { + unsigned char sense; + unsigned char asc; + unsigned char ascq; + int errno; +} error_codes[] = { +/* Just filled in what looks right. Hav'nt checked any standard paper for + these errno assignments, so they may be wrong... */ +{ ILLEGAL_REQUEST, 0x21,0x01/*Invalid element address*/, EBADSLT }, +{ ILLEGAL_REQUEST, 0x28,0x01/*Import or export element accessed*/, EBADE }, +{ ILLEGAL_REQUEST, 0x3B,0x0D/*Medium destination element full*/, EXFULL }, +{ ILLEGAL_REQUEST, 0x3B,0x0E/*Medium source element empty*/, EBADE }, + +{ ILLEGAL_REQUEST, 0x20,0x00/*Invalid command operation code*/, EBADRQC }, +{ 0,0,0,0 } +}; + +Scsi_Changer * scsi_Changers = NULL; + +/* ------------------------------------------------------------------- */ + +static void +ch_request_done (Scsi_Cmnd * SCpnt) +{ + struct request * req; + + req = &SCpnt->request; + req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ + + if (req->waiting != NULL) { + complete(req->waiting); + } +} + +static int +ch_do_cmd(int target, unsigned char * sr_cmd, + void * buffer, unsigned buflength) +{ + static u_char init_cmd[6] = { 0x07 }; /* INITIALIZE ELEMENT STATUS */ + Scsi_Cmnd * SCpnt; + int errno, ua = 0, retries = 0; + + if(!scsi_block_when_processing_errors(scsi_Changers[target].device)) + return -ENXIO; + + SCpnt = scsi_allocate_device(scsi_Changers[target].device, 1, TRUE); +retry: + errno = 0; + { + DECLARE_COMPLETION(wait); + SCpnt->request.waiting = &wait; + if (ua && reinit) { + init_cmd[1] = scsi_Changers[target].device->lun << 5; + scsi_do_cmd(SCpnt, (void *) init_cmd, NULL, 0, + ch_request_done, CH_TIMEOUT, MAX_RETRIES); + wait_for_completion(&wait); + } + scsi_do_cmd(SCpnt, (void *) sr_cmd, buffer, buflength, + ch_request_done, CH_TIMEOUT, MAX_RETRIES); + wait_for_completion(&wait); + SCpnt->request.waiting = NULL; + } + + if(driver_byte(SCpnt->result) != 0) { + if (debug) { + printk(DEVICE_NAME " (debug) error, command: "); + print_command(sr_cmd); + print_sense(DEVICE_NAME " (debug)", SCpnt); + } + /* Check to see if additional sense information is available */ + if(SCpnt->sense_buffer[7] > 5 && SCpnt->sense_buffer[12] != 0) { + int i; + for (i = 0; error_codes[i].errno != 0; i++) + if (error_codes[i].sense == SCpnt->sense_buffer[ 2] && + error_codes[i].asc == SCpnt->sense_buffer[12] && + error_codes[i].ascq == SCpnt->sense_buffer[13]) { + errno = -error_codes[i].errno; + break; + } + } + if (errno == 0) { + errno = -EIO; + switch(SCpnt->sense_buffer[2] & 0xf) { + case UNIT_ATTENTION: + if (debug) + printk(DEVICE_NAME ": got UNIT ATTENTION\n"); + scsi_Changers[target].unit_attention = 1; + ua = 1; + if (retries++ < 3) + goto retry; + break; + case NOT_READY: + printk(DEVICE_NAME " reports NOT READY.\n"); + break; + case ILLEGAL_REQUEST: + printk(DEVICE_NAME " reports ILLEGAL REQUEST.\n"); + break; + default: + printk(DEVICE_NAME " error, command: "); + print_command(sr_cmd); + print_sense(DEVICE_NAME, SCpnt); + } + } + } + + scsi_release_command(SCpnt); + return errno; +} + +/* ------------------------------------------------------------------------ */ + +static Scsi_Device* +find_device(struct Scsi_Host *host, u_char channel, u_char id, u_char lun) +{ + Scsi_Device *ret; + ret = host->host_queue; + + while (ret != NULL && + (ret->host != host || ret->channel != channel || + ret->id != id || ret->lun != lun)) + ret = ret->next; + + return ret; +} + +static int +ch_elem_to_typecode(int target, u_int elem) +{ + int i; + + for (i = 0; i < 4; i++) { + if (elem >= scsi_Changers[target].firsts[i] && + elem < scsi_Changers[target].firsts[i] + + scsi_Changers[target].counts[i]) + return i+1; + } + return 0; +} + +static int +ch_read_element_status(int target, u_int elem, char *data) +{ + u_char cmd[12]; + u_char *buffer; + int result; + + buffer = (unsigned char *) scsi_malloc(512); + if(!buffer) return -ENOMEM; + +retry: + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0xb8; /* READ ELEMENT STATUS */ + cmd[1] = (scsi_Changers[target].device->lun << 5) | + (scsi_Changers[target].voltags ? 0x10 : 0) | + ch_elem_to_typecode(target,elem); + cmd[2] = (elem >> 8) & 0xff; + cmd[3] = elem & 0xff; + cmd[5] = 1; /* # of elements, currently only one */ + cmd[9] = 255; /* alloc length */ + if (0 == (result = ch_do_cmd(target, cmd, buffer, 256))) { + if (((buffer[16] << 8) | buffer[17]) != elem) { + printk(KERN_DEBUG DEVICE_NAME + "Oops: asked for element 0x%02x, got 0x%02x\n", + elem,(buffer[16] << 8) | buffer[17]); + scsi_free(buffer, 512); + return -1; + } + memcpy(data,buffer+16,16); + } else { + if (scsi_Changers[target].voltags) { + scsi_Changers[target].voltags = 0; + printk(KERN_INFO DEVICE_NAME + ": device has no volume tag support\n"); + goto retry; + } + printk(KERN_WARNING DEVICE_NAME + ": READ ELEMENT STATUS for element 0x%x failed\n",elem); + } + + scsi_free(buffer, 512); + return result; +} + +static int +ch_init_elem(int target) +{ + u_char cmd[6]; + + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0x07; /* INITIALIZE ELEMENT STATUS */ + cmd[1] = scsi_Changers[target].device->lun << 5; + return ch_do_cmd(target, cmd, NULL, 0); +} + +static int +ch_readconfig(int target) +{ + u_char cmd[10], data[16]; + u_char *buffer; + int result,i; + u_int elem; + + buffer = (unsigned char *) scsi_malloc(512); + memset(buffer,0,512); + if(!buffer) return -ENOMEM; + + memset(cmd,0,sizeof(cmd)); + cmd[0] = MODE_SENSE; + cmd[1] = scsi_Changers[target].device->lun << 5; + cmd[2] = 0x1d; + cmd[4] = 255; + result = ch_do_cmd(target, cmd, buffer, 255); + if (0 != result) { + cmd[1] |= (1<<3); + result = ch_do_cmd(target, cmd, buffer, 255); + } + if (debug) { + printk("element address assignment page: "); + for (i = 0; i < buffer[0]+buffer[3]; i++) + printk("%02x ",buffer[i]); + printk("\n"); + } + if (0 == result) { + scsi_Changers[target].firsts[CHET_MT] = + (buffer[buffer[3]+ 6] << 8) | buffer[buffer[3]+ 7]; + scsi_Changers[target].counts[CHET_MT] = + (buffer[buffer[3]+ 8] << 8) | buffer[buffer[3]+ 9]; + scsi_Changers[target].firsts[CHET_ST] = + (buffer[buffer[3]+10] << 8) | buffer[buffer[3]+11]; + scsi_Changers[target].counts[CHET_ST] = + (buffer[buffer[3]+12] << 8) | buffer[buffer[3]+13]; + scsi_Changers[target].firsts[CHET_IE] = + (buffer[buffer[3]+14] << 8) | buffer[buffer[3]+15]; + scsi_Changers[target].counts[CHET_IE] = + (buffer[buffer[3]+16] << 8) | buffer[buffer[3]+17]; + scsi_Changers[target].firsts[CHET_DT] = + (buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19]; + scsi_Changers[target].counts[CHET_DT] = + (buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21]; + if (debug) { + printk(DEVICE_NAME + ": mt:0x%x+%d st:0x%x+%d ie:0x%x+%d dt:0x%x+%d\n", + scsi_Changers[target].firsts[CHET_MT], + scsi_Changers[target].counts[CHET_MT], + scsi_Changers[target].firsts[CHET_ST], + scsi_Changers[target].counts[CHET_ST], + scsi_Changers[target].firsts[CHET_IE], + scsi_Changers[target].counts[CHET_IE], + scsi_Changers[target].firsts[CHET_DT], + scsi_Changers[target].counts[CHET_DT]); + } + } else { + printk(KERN_WARNING DEVICE_NAME + ": reading element address assigment page failed!\n"); + } + + /* look up the devices of the data transfer elements */ + scsi_Changers[target].dt = + kmalloc(scsi_Changers[target].counts[CHET_DT]*sizeof(Scsi_Device*), + GFP_ATOMIC); + for (elem = 0; elem < scsi_Changers[target].counts[CHET_DT]; elem++) { + if (0 != ch_read_element_status + (target,elem+scsi_Changers[target].firsts[CHET_DT],data)) { + printk(DEVICE_NAME " Huh?: READ ELEMENT STATUS for " + "data transfer element 0x%x failed\n", + elem+scsi_Changers[target].firsts[CHET_DT]); + } else { + printk(KERN_INFO DEVICE_NAME " data transfer element 0x%x: ", + elem+scsi_Changers[target].firsts[CHET_DT]); + if (data[6] & 0x80) { + printk("not this SCSI bus\n"); + scsi_Changers[target].dt[elem] = NULL; + } else if (0 == (data[6] & 0x30)) { + printk("ID/LUN unknown\n"); + scsi_Changers[target].dt[elem] = NULL; + } else { + int id,lun; + + id = scsi_Changers[target].device->id; + lun = 0; + if (data[6] & 0x20) id = data[7]; + if (data[6] & 0x10) lun = data[6] & 7; + printk("ID %i, LUN %i, ",id,lun); + scsi_Changers[target].dt[elem] = + find_device(scsi_Changers[target].device->host, + scsi_Changers[target].device->channel, + id,lun); + if (!scsi_Changers[target].dt[elem]) { + /* should not happen */ + printk("Huh? device not found !\n"); + } else { + printk("name: %8.8s %16.16s %4.4s\n", + scsi_Changers[target].dt[elem]->vendor, + scsi_Changers[target].dt[elem]->model, + scsi_Changers[target].dt[elem]->rev); + } + } + } + } + scsi_Changers[target].voltags = 1; + scsi_free(buffer, 512); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int +ch_position(int target, u_int trans, u_int elem, int rotate) +{ + u_char cmd[10]; + + if (debug) + printk(KERN_DEBUG DEVICE_NAME " position: 0x%x\n",elem); + if (0 == trans) + trans = scsi_Changers[target].firsts[CHET_MT]; + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0x2b; /* POSITION TO ELEMENT */ + cmd[1] = scsi_Changers[target].device->lun << 5; + cmd[2] = (trans >> 8) & 0xff; + cmd[3] = trans & 0xff; + cmd[4] = (elem >> 8) & 0xff; + cmd[5] = elem & 0xff; + cmd[8] = rotate ? 1 : 0; + return ch_do_cmd(target, cmd, NULL,0); +} + +static int +ch_move(int target, u_int trans, u_int src, u_int dest, int rotate) +{ + u_char cmd[12]; + + if (debug) + printk(KERN_DEBUG DEVICE_NAME " move: 0x%x => 0x%x\n",src,dest); + if (0 == trans) + trans = scsi_Changers[target].firsts[CHET_MT]; + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0xa5; /* MOVE MEDIUM */ + cmd[1] = scsi_Changers[target].device->lun << 5; + cmd[2] = (trans >> 8) & 0xff; + cmd[3] = trans & 0xff; + cmd[4] = (src >> 8) & 0xff; + cmd[5] = src & 0xff; + cmd[6] = (dest >> 8) & 0xff; + cmd[7] = dest & 0xff; + cmd[10] = rotate ? 1 : 0; + return ch_do_cmd(target, cmd, NULL,0); +} + +static int +ch_exchange(int target, u_int trans, u_int src, + u_int dest1, u_int dest2, int rotate1, int rotate2) +{ + u_char cmd[12]; + + if (debug) + printk(KERN_DEBUG DEVICE_NAME " exchange: 0x%x => 0x%x => 0x%x\n", + src,dest1,dest2); + if (0 == trans) + trans = scsi_Changers[target].firsts[CHET_MT]; + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0xa6; /* EXCHANGE MEDIUM */ + cmd[1] = scsi_Changers[target].device->lun << 5; + cmd[2] = (trans >> 8) & 0xff; + cmd[3] = trans & 0xff; + cmd[4] = (src >> 8) & 0xff; + cmd[5] = src & 0xff; + cmd[6] = (dest1 >> 8) & 0xff; + cmd[7] = dest1 & 0xff; + cmd[8] = (dest2 >> 8) & 0xff; + cmd[9] = dest2 & 0xff; + cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0); + + return ch_do_cmd(target, cmd, NULL,0); +} + +/* ------------------------------------------------------------------------ */ + +static int +ch_release(struct inode * inode, struct file * filp) +{ + int minor = MINOR(inode->i_rdev); + + scsi_Changers[minor].device->access_count--; + if (scsi_Changers[minor].device->host->hostt->module) + __MOD_DEC_USE_COUNT(scsi_Changers[minor].device->host->hostt->module); + return 0; +} + +static int +ch_open(struct inode * inode, struct file * filp) +{ + int minor = MINOR(inode->i_rdev); + + if(minor >= ch_template.nr_dev || !scsi_Changers[minor].device) + return -ENXIO; + + scsi_Changers[minor].device->access_count++; + if (scsi_Changers[minor].device->host->hostt->module) + __MOD_INC_USE_COUNT(scsi_Changers[minor].device->host->hostt->module); + + return 0; +} + +static int +ch_checkrange(int target, int type, int unit) +{ + if (type < 0 || type > 3 || unit < 0 || + unit >= scsi_Changers[target].counts[type]) + return -1; + return 0; +} + +/* for data transfer elements: check if they are busy */ +static int +ch_is_busy(int target, int type, int unit) +{ + if (!check_busy) + return 0; + if (type != CHET_DT) + return 0; + if (!scsi_Changers[target].dt[unit]) + return 0; + return scsi_Changers[target].dt[unit]->access_count; +} + +static int +ch_ioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + int target = MINOR(inode->i_rdev); + + if (target >= ch_template.nr_dev || + !scsi_Changers[target].device) return -ENXIO; + + switch (cmd) { + case CHIOGPARAMS: + { + struct changer_params params; + + params.cp_curpicker = 0; + params.cp_npickers = scsi_Changers[target].counts[CHET_MT]; + params.cp_nslots = scsi_Changers[target].counts[CHET_ST]; + params.cp_nportals = scsi_Changers[target].counts[CHET_IE]; + params.cp_ndrives = scsi_Changers[target].counts[CHET_DT]; + + WRITE_TO_USER ((void *) arg, ¶ms, sizeof (struct changer_params)); + return 0; + } + break; + + case CHIOPOSITION: + { + struct changer_position pos; + + READ_FROM_USER (&pos, (void*)arg, sizeof (pos)); + + if (0 != ch_checkrange(target, pos.cp_type, pos.cp_unit)) { + if (debug) + printk(DEVICE_NAME ": CHIOPOSITION: invalid parameter\n"); + return -EBADSLT; + } + return ch_position(target,0, + scsi_Changers[target].firsts[pos.cp_type] + pos.cp_unit, + pos.cp_flags & CP_INVERT); + } + break; + + case CHIOMOVE: + { + struct changer_move mv; + + READ_FROM_USER (&mv, (void*)arg, sizeof (mv)); + + if (0 != ch_checkrange(target, mv.cm_fromtype, mv.cm_fromunit) || + 0 != ch_checkrange(target, mv.cm_totype, mv.cm_tounit )) { + if (debug) + printk(DEVICE_NAME ": CHIOMOVE: invalid parameter\n"); + return -EBADSLT; + } + if (ch_is_busy(target, mv.cm_fromtype, mv.cm_fromunit) || + ch_is_busy(target, mv.cm_totype, mv.cm_tounit )) + return -EBUSY; + + return ch_move(target,0, + scsi_Changers[target].firsts[mv.cm_fromtype] + mv.cm_fromunit, + scsi_Changers[target].firsts[mv.cm_totype] + mv.cm_tounit, + mv.cm_flags & CM_INVERT); + } + break; + +#if 0 + case CHIOINIT: + { + return ch_init_changer(target); + } + break; +#endif + + case CHIOEXCHANGE: + { + struct changer_exchange mv; + + READ_FROM_USER (&mv, (void*)arg, sizeof (mv)); + + if (0 != ch_checkrange(target, mv.ce_srctype, mv.ce_srcunit ) || + 0 != ch_checkrange(target, mv.ce_fdsttype, mv.ce_fdstunit) || + 0 != ch_checkrange(target, mv.ce_sdsttype, mv.ce_sdstunit)) { + if (debug) + printk(DEVICE_NAME ": CHIOEXCHANGE: invalid parameter\n"); + return -EBADSLT; + } + if (0 != ch_is_busy(target, mv.ce_srctype, mv.ce_srcunit ) || + 0 != ch_is_busy(target, mv.ce_fdsttype, mv.ce_fdstunit) || + 0 != ch_is_busy(target, mv.ce_sdsttype, mv.ce_sdstunit)) + return -EBUSY; + + return ch_exchange + (target,0, + scsi_Changers[target].firsts[mv.ce_srctype] + mv.ce_srcunit, + scsi_Changers[target].firsts[mv.ce_fdsttype] + mv.ce_fdstunit, + scsi_Changers[target].firsts[mv.ce_sdsttype] + mv.ce_sdstunit, + mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2); + } + break; + + case CHIOGSTATUS: + { + struct changer_element_status ces; + int i; + u_char data[16]; + + READ_FROM_USER (&ces, (void*)arg, sizeof (ces)); + + if (ces.ces_type < 0 || ces.ces_type > 3) + return -EINVAL; + + for (i = 0; i < scsi_Changers[target].counts[ces.ces_type]; i++) { + if (0 != ch_read_element_status + (target, scsi_Changers[target].firsts[ces.ces_type]+i,data)) + return -EIO; + put_user(data[2], ces.ces_data+i); + if (data[2] & CESTATUS_EXCEPT) + printk(DEVICE_NAME ": element 0x%x: asc=0x%x, ascq=0x%x\n", + scsi_Changers[target].firsts[ces.ces_type]+i, + (int)data[4],(int)data[5]); + if (0 != ch_read_element_status + (target, scsi_Changers[target].firsts[ces.ces_type]+i,data)) + return -EIO; + } + } + break; + + case CHIOGELEM: + { + struct changer_get_element cge; + u_char cmd[12]; + u_char *buffer; + int elem,result,i; + + READ_FROM_USER (&cge, (void*)arg, sizeof (cge)); + + if (0 != ch_checkrange(target, cge.cge_type, cge.cge_unit)) + return -EINVAL; + elem = scsi_Changers[target].firsts[cge.cge_type] + cge.cge_unit; + + buffer = (unsigned char *) scsi_malloc(512); + if(!buffer) return -ENOMEM; + +voltag_retry: + memset(cmd,0,sizeof(cmd)); + cmd[0] = 0xb8; /* READ ELEMENT STATUS */ + cmd[1] = (scsi_Changers[target].device->lun << 5) | + (scsi_Changers[target].voltags ? 0x10 : 0) | + ch_elem_to_typecode(target,elem); + cmd[2] = (elem >> 8) & 0xff; + cmd[3] = elem & 0xff; + cmd[5] = 1; /* # of elements, currently only one */ + cmd[9] = 255; /* alloc length */ + + if (0 == (result = ch_do_cmd(target, cmd, buffer, 256))) { + cge.cge_status = buffer[18]; + cge.cge_flags = 0; + if (buffer[18] & CESTATUS_EXCEPT) { + /* XXX cge_errno */ + } + if (buffer[25] & 0x80) { + cge.cge_flags |= CGE_SRC; + if (buffer[25] & 0x40) + cge.cge_flags |= CGE_INVERT; + elem = (buffer[26]<<8) | buffer[27]; + for (i = 0; i < 4; i++) { + if (elem >= scsi_Changers[target].firsts[i] && + elem < scsi_Changers[target].firsts[i]+ + scsi_Changers[target].counts[i]) { + cge.cge_srctype = i; + cge.cge_srcunit = elem-scsi_Changers[target].firsts[i]; + } + } + } + if ((buffer[22] & 0x30) == 0x30) { + cge.cge_flags |= CGE_IDLUN; + cge.cge_id = buffer[23]; + cge.cge_lun = buffer[22] & 7; + } + if (buffer[9] & 0x80) { + cge.cge_flags |= CGE_PVOLTAG; + memcpy(cge.cge_pvoltag,buffer+28,36); + } + if (buffer[9] & 0x40) { + cge.cge_flags |= CGE_AVOLTAG; + memcpy(cge.cge_avoltag,buffer+64,36); + } + } else if (scsi_Changers[target].voltags) { + scsi_Changers[target].voltags = 0; + printk(KERN_INFO DEVICE_NAME + ": device has no volume tag support\n"); + goto voltag_retry; + } + + scsi_free(buffer, 512); + + WRITE_TO_USER ((void*)arg, &cge, sizeof (cge)); + return result; + break; + } + + default: + return scsi_ioctl(scsi_Changers[target].device, cmd, (void*)arg); + + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static int +ch_detect(Scsi_Device * SDp) +{ + if(SDp->type != TYPE_MEDIUM_CHANGER) + return 0; + + printk("Detected scsi changer ch%d at scsi%d, channel %d, id %d, lun %d\n", + ch_template.dev_noticed++, + SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); + + return 1; +} + +static int ch_attach(Scsi_Device * SDp){ + Scsi_Changer * cpnt; + int i; + + if(SDp->type != TYPE_MEDIUM_CHANGER) + return 1; + + if (ch_template.nr_dev >= ch_template.dev_max) + { + SDp->attached--; + return 1; + } + + for(cpnt = scsi_Changers, i=0; idevice) break; + + if(i >= ch_template.dev_max) panic ("scsi_devicelist corrupt (ch)"); + + scsi_Changers[i].device = SDp; + scsi_Changers[i].de = + devfs_register (scsi_Changers[i].device->de, "changer", + DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFCHR | S_IRUGO | S_IWUGO, + &changer_fops, NULL); + ch_template.nr_dev++; + if(ch_template.nr_dev > ch_template.dev_max) + panic ("scsi_devicelist corrupt (ch)"); + return 0; +} + +static int ch_registered = 0; + +static int +ch_init() +{ + if(ch_template.dev_noticed == 0) return 0; + + if(!ch_registered) { + if (devfs_register_chrdev(MAJOR_NR,"ch",&changer_fops)) { + printk("Unable to get major %d for SCSI-Changer\n",MAJOR_NR); + return 1; + } + ch_registered++; + } + + if (scsi_Changers) return 0; + ch_template.dev_max = + ch_template.dev_noticed + CH_EXTRA_DEVS; + scsi_Changers = (Scsi_Changer *) + kmalloc(ch_template.dev_max * sizeof(Scsi_Changer), GFP_ATOMIC); + memset(scsi_Changers, 0, ch_template.dev_max * sizeof(Scsi_Changer)); + + return 0; +} + +void +ch_finish() +{ + int i; + + for (i = 0; i < ch_template.nr_dev; ++i) + { + if (scsi_Changers[i].device) { + ch_readconfig(i); + ch_init_elem(i); + } + } + + return; +} + +static void ch_detach(Scsi_Device * SDp) +{ + Scsi_Changer * cpnt; + int i; + + for(cpnt = scsi_Changers, i=0; idevice == SDp) { + /* + * Reset things back to a sane state so that one can re-load a new + * driver (perhaps the same one). + */ + devfs_unregister(cpnt->de); + kfree(cpnt->dt); + cpnt->device = NULL; + SDp->attached--; + ch_template.nr_dev--; + ch_template.dev_noticed--; + return; + } + return; +} + +static int __init init_ch_module(void) +{ + printk(KERN_INFO "SCSI Media Changer driver v" VERSION + " for Linux " UTS_RELEASE "\n"); + ch_template.module = THIS_MODULE; + return scsi_register_module(MODULE_SCSI_DEV, &ch_template); +} + +static void __exit exit_ch_module(void) +{ + scsi_unregister_module(MODULE_SCSI_DEV, &ch_template); + devfs_unregister_chrdev(MAJOR_NR, "ch"); + ch_registered--; + if(scsi_Changers != NULL) + kfree((char *) scsi_Changers); + + ch_template.dev_max = 0; +} + +module_init(init_ch_module); +module_exit(exit_ch_module); diff -Nru a/drivers/scsi/ch.h b/drivers/scsi/ch.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/scsi/ch.h Wed Feb 13 20:04:01 2002 @@ -0,0 +1,11 @@ +typedef struct { + Scsi_Device *device; + u_int firsts[4]; + u_int counts[4]; + Scsi_Device **dt; /* ptrs to data transfer elements */ + u_int unit_attention; + u_int voltags; + devfs_handle_t de; +} Scsi_Changer; + +extern Scsi_Changer * scsi_Changers; diff -Nru a/drivers/scsi/eata.c b/drivers/scsi/eata.c --- a/drivers/scsi/eata.c Wed Feb 13 20:03:44 2002 +++ b/drivers/scsi/eata.c Wed Feb 13 20:03:44 2002 @@ -1,6 +1,9 @@ /* * eata.c - Low-level driver for EATA/DMA SCSI host adapters. * + * 01 Jan 2002 Rev. 6.50 for linux 2.4.16 + * + Use the dynamic DMA mapping API. + * * 1 May 2001 Rev. 6.05 for linux 2.4.4 * + Clean up all pci related routines. * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d) @@ -220,7 +223,7 @@ * This driver is based on the CAM (Common Access Method Committee) * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol. * - * Copyright (C) 1994-2001 Dario Ballabio (ballabio_dario@emc.com) + * Copyright (C) 1994-2002 Dario Ballabio (ballabio_dario@emc.com) * * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it * @@ -438,13 +441,6 @@ #include #include -#define SPIN_FLAGS unsigned long spin_flags; -#define SPIN_LOCK spin_lock_irq(&io_request_lock); -#define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags); -#define SPIN_UNLOCK spin_unlock_irq(&io_request_lock); -#define SPIN_UNLOCK_RESTORE \ - spin_unlock_irqrestore(&io_request_lock, spin_flags); - /* Subversion values */ #define ISA 0 #define ESA 1 @@ -638,7 +634,7 @@ u_int32_t data_len; /* If sg=0 Data Length, if sg=1 sglist length */ u_int32_t cpp_index; /* Index of address to be returned in sp */ u_int32_t data_address; /* If sg=0 Data Address, if sg=1 sglist address */ - u_int32_t sp_addr; /* Address where sp is DMA'ed when cp completes */ + u_int32_t sp_dma_addr; /* Address where sp is DMA'ed when cp completes */ u_int32_t sense_addr; /* Address where Sense Data is DMA'ed on error */ /* Additional fields begin here. */ Scsi_Cmnd *SCpnt; @@ -660,7 +656,11 @@ unsigned long last_retried_pid; /* Pid of last retried command */ unsigned char subversion; /* Bus type, either ISA or EISA/PCI */ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */ - struct mssp sp[2]; /* Returned status for this board */ + unsigned char is_pci; /* TRUE is bus type is PCI */ + struct pci_dev *pdev; /* pdev for PCI bus, NULL otherwise */ + struct mssp *sp_cpu_addr; /* cpu addr for DMA buffer sp */ + dma_addr_t sp_dma_addr; /* dma handle for DMA buffer sp */ + struct mssp sp; /* Local copy of sp buffer */ }; static struct Scsi_Host *sh[MAX_BOARDS + 1]; @@ -697,10 +697,11 @@ #define HD(board) ((struct hostdata *) &sh[board]->hostdata) #define BN(board) (HD(board)->board_name) -#define H2DEV(x) htonl(x) -#define DEV2H(x) H2DEV(x) +/* Device is Big Endian */ +#define H2DEV(x) cpu_to_be32(x) +#define DEV2H(x) be32_to_cpu(x) + #define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0) -#define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0) static void do_interrupt_handler(int, void *, struct pt_regs *); static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int); @@ -859,7 +860,7 @@ static inline int port_detect \ (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt) { - unsigned char irq, dma_channel, subversion, i; + unsigned char irq, dma_channel, subversion, i, is_pci = FALSE; unsigned char protocol_rev; struct eata_info info; char *bus_type, dma_name[16], tag_type; @@ -911,10 +912,12 @@ if (!setup_done && j > 0 && j <= MAX_PCI) { bus_type = "PCI"; + is_pci = TRUE; subversion = ESA; } else if (port_base > MAX_EISA_ADDR || (protocol_rev == 'C' && info.pci)) { bus_type = "PCI"; + is_pci = TRUE; subversion = ESA; } else if (port_base >= MIN_EISA_ADDR || (protocol_rev == 'C' && info.eisa)) { @@ -927,6 +930,7 @@ } else if (port_base > MAX_ISA_ADDR) { bus_type = "PCI"; + is_pci = TRUE; subversion = ESA; } else { @@ -967,7 +971,13 @@ printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n", name, irq); - pdev = get_pci_dev(port_base); + if (is_pci) { + pdev = get_pci_dev(port_base); + if (!pdev) + printk("%s: warning, failed to get pci_dev structure.\n", name); + } + else + pdev = NULL; if (pdev && (irq != pdev->irq)) { printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq, pdev->irq); @@ -997,7 +1007,7 @@ /* Set board configuration */ memset((char *)&config, 0, sizeof(struct eata_config)); - config.len = (ushort) htons((ushort)510); + config.len = (ushort) cpu_to_be16((ushort)510); config.ocena = TRUE; if (do_dma(port_base, (unsigned long)&config, SET_CONFIG_DMA)) { @@ -1026,14 +1036,16 @@ sh[j]->n_io_port = REGION_SIZE; sh[j]->dma_channel = dma_channel; sh[j]->irq = irq; - sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size); + sh[j]->sg_tablesize = (ushort) be16_to_cpu(info.scatt_size); sh[j]->this_id = (ushort) info.host_addr[3]; - sh[j]->can_queue = (ushort) ntohs(info.queue_size); + sh[j]->can_queue = (ushort) be16_to_cpu(info.queue_size); sh[j]->cmd_per_lun = MAX_CMD_PER_LUN; sh[j]->select_queue_depths = select_queue_depths; memset(HD(j), 0, sizeof(struct hostdata)); HD(j)->subversion = subversion; HD(j)->protocol_rev = protocol_rev; + HD(j)->is_pci = is_pci; + HD(j)->pdev = pdev; HD(j)->board_number = j; if (HD(j)->subversion == ESA) @@ -1042,14 +1054,14 @@ unsigned long flags; scsi_register_blocked_host(sh[j]); sh[j]->unchecked_isa_dma = TRUE; - + flags=claim_dma_lock(); disable_dma(dma_channel); clear_dma_ff(dma_channel); set_dma_mode(dma_channel, DMA_MODE_CASCADE); enable_dma(dma_channel); release_dma_lock(flags); - + } strcpy(BN(j), name); @@ -1098,6 +1110,13 @@ return FALSE; } + if (! (HD(j)->sp_cpu_addr = pci_alloc_consistent(HD(j)->pdev, + sizeof(struct mssp), &HD(j)->sp_dma_addr))) { + printk("%s: pci_alloc_consistent failed, detaching.\n", BN(j)); + eata2x_release(sh[j]); + return FALSE; + } + if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN) max_queue_depth = MAX_TAGGED_CMD_PER_LUN; @@ -1112,7 +1131,7 @@ else tag_type = 'n'; if (j == 0) { - printk("EATA/DMA 2.0x: Copyright (C) 1994-2001 Dario Ballabio.\n"); + printk("EATA/DMA 2.0x: Copyright (C) 1994-2002 Dario Ballabio.\n"); printk("%s config options -> tc:%c, lc:%c, mq:%d, rs:%c, et:%c.\n", driver_name, tag_type, YESNO(linked_comm), max_queue_depth, YESNO(rev_scan), YESNO(ext_tran)); @@ -1148,7 +1167,11 @@ info.pci, info.eisa, info.raidnum); #endif - if (pdev) pci_set_master(pdev); + if (HD(j)->pdev) { + pci_set_master(HD(j)->pdev); + if (pci_set_dma_mask(HD(j)->pdev, 0xffffffff)) + printk("%s: warning, pci_set_dma_mask failed.\n", BN(j)); + } return TRUE; } @@ -1275,25 +1298,92 @@ return j; } -static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) { - unsigned int k; +static inline void map_dma(unsigned int i, unsigned int j) { + unsigned int k, count, pci_dir; struct scatterlist *sgpnt; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (SCpnt->sense_buffer) + cpp->sense_addr = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->sense_buffer, + sizeof SCpnt->sense_buffer, PCI_DMA_FROMDEVICE)); + + cpp->sense_len = sizeof SCpnt->sense_buffer; + + if (!SCpnt->use_sg) { + + if (!SCpnt->request_bufflen) + cpp->data_address = V2DEV(SCpnt->request_buffer); + + else if (SCpnt->request_buffer) + cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, + SCpnt->request_buffer, SCpnt->request_bufflen, pci_dir)); + + cpp->data_len = H2DEV(SCpnt->request_bufflen); + return; + } sgpnt = (struct scatterlist *) SCpnt->request_buffer; + count = pci_map_sg(HD(j)->pdev, sgpnt, SCpnt->use_sg, pci_dir); - for (k = 0; k < SCpnt->use_sg; k++) { - cpp->sglist[k].address = V2DEV(sgpnt[k].address); - cpp->sglist[k].num_bytes = H2DEV(sgpnt[k].length); + for (k = 0; k < count; k++) { + cpp->sglist[k].address = H2DEV(sg_dma_address(&sgpnt[k])); + cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(&sgpnt[k])); } + cpp->sg = TRUE; cpp->data_address = V2DEV(cpp->sglist); cpp->data_len = H2DEV((SCpnt->use_sg * sizeof(struct sg_list))); } -static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { - unsigned int i, j, k; +static void unmap_dma(unsigned int i, unsigned int j) { + unsigned int pci_dir; struct mscp *cpp; - struct mssp *spp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (DEV2H(cpp->sense_addr)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); + + if (SCpnt->use_sg) + pci_unmap_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); + + else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); + +} + +static void sync_dma(unsigned int i, unsigned int j) { + unsigned int pci_dir; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (DEV2H(cpp->sense_addr)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); + + if (SCpnt->use_sg) + pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + SCpnt->use_sg, pci_dir); + + else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); + +} + +static inline void scsi_to_dev_dir(unsigned int i, unsigned int j) { + unsigned int k; static const unsigned char data_out_cmds[] = { 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e, @@ -1304,9 +1394,53 @@ static const unsigned char data_none_cmds[] = { 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e, 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47, - 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5 + 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00 }; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; + SCpnt = cpp->SCpnt; + + if (SCpnt->sc_data_direction == SCSI_DATA_READ) { + cpp->din = TRUE; + cpp->dout = FALSE; + return; + } + else if (SCpnt->sc_data_direction == SCSI_DATA_WRITE) { + cpp->din = FALSE; + cpp->dout = TRUE; + return; + } + else if (SCpnt->sc_data_direction == SCSI_DATA_NONE) { + cpp->din = FALSE; + cpp->dout = FALSE; + return; + } + + if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) + panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); + + for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) + if (SCpnt->cmnd[0] == data_out_cmds[k]) { + cpp->dout = TRUE; + break; + } + + if ((cpp->din = !cpp->dout)) + for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) + if (SCpnt->cmnd[0] == data_none_cmds[k]) { + cpp->din = FALSE; + break; + } + +} + +static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { + unsigned int i, j, k; + struct mscp *cpp; + /* j is the board number */ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number; @@ -1338,11 +1472,8 @@ memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *)); - /* Set pointer to status packet structure */ - spp = &HD(j)->sp[0]; - - /* The EATA protocol uses Big Endian format */ - cpp->sp_addr = V2DEV(spp); + /* Set pointer to status packet structure, Big Endian format */ + cpp->sp_dma_addr = H2DEV(HD(j)->sp_dma_addr); SCpnt->scsi_done = done; cpp->cpp_index = i; @@ -1352,19 +1483,6 @@ BN(j), i, SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid); - for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) - if (SCpnt->cmnd[0] == data_out_cmds[k]) { - cpp->dout = TRUE; - break; - } - - if ((cpp->din = !cpp->dout)) - for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) - if (SCpnt->cmnd[0] == data_none_cmds[k]) { - cpp->din = FALSE; - break; - } - cpp->reqsen = TRUE; cpp->dispri = TRUE; #if 0 @@ -1375,8 +1493,13 @@ cpp->target = SCpnt->target; cpp->lun = SCpnt->lun; cpp->SCpnt = SCpnt; - cpp->sense_addr = V2DEV(SCpnt->sense_buffer); - cpp->sense_len = sizeof SCpnt->sense_buffer; + memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); + + /* Use data transfer direction SCpnt->sc_data_direction */ + scsi_to_dev_dir(i, j); + + /* Map DMA buffers and SG list */ + map_dma(i, j); if (SCpnt->device->tagged_queue) { @@ -1396,17 +1519,6 @@ cpp->mess[1] = SCpnt->device->current_tag++; } - if (SCpnt->use_sg) { - cpp->sg = TRUE; - build_sg_list(cpp, SCpnt); - } - else { - cpp->data_address = V2DEV(SCpnt->request_buffer); - cpp->data_len = H2DEV(SCpnt->request_bufflen); - } - - memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); - if (linked_comm && SCpnt->device->queue_depth > 2 && TLDEV(SCpnt->device->type)) { HD(j)->cp_stat[i] = READY; @@ -1416,6 +1528,7 @@ /* Send control packet to the board */ if (do_dma(sh[j]->io_port, (unsigned long) cpp, SEND_CP_DMA)) { + unmap_dma(i, j); SCpnt->host_scribble = NULL; printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n", BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid); @@ -1472,6 +1585,7 @@ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i); if (SCarg->eh_state == SCSI_STATE_TIMEOUT) { + unmap_dma(i, j); SCarg->host_scribble = NULL; HD(j)->cp_stat[i] = FREE; printk("%s, abort, mbox %d, eh_state timeout, pid %ld.\n", @@ -1493,6 +1607,7 @@ } if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { + unmap_dma(i, j); SCarg->result = DID_ABORT << 16; SCarg->host_scribble = NULL; HD(j)->cp_stat[i] = FREE; @@ -1589,16 +1704,19 @@ #endif HD(j)->in_reset = TRUE; - SPIN_UNLOCK + + spin_unlock_irq(&io_request_lock); time = jiffies; while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - SPIN_LOCK + spin_lock_irq(&io_request_lock); + printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); for (i = 0; i < sh[j]->can_queue; i++) { if (HD(j)->cp_stat[i] == IN_RESET) { SCpnt = HD(j)->cp[i].SCpnt; + unmap_dma(i, j); SCpnt->result = DID_RESET << 16; SCpnt->host_scribble = NULL; @@ -1611,6 +1729,7 @@ else if (HD(j)->cp_stat[i] == ABORTING) { SCpnt = HD(j)->cp[i].SCpnt; + unmap_dma(i, j); SCpnt->result = DID_RESET << 16; SCpnt->host_scribble = NULL; @@ -1823,7 +1942,7 @@ static inline void ihdlr(int irq, unsigned int j) { Scsi_Cmnd *SCpnt; unsigned int i, k, c, status, tstatus, reg; - struct mssp *dspp, *spp; + struct mssp *spp; struct mscp *cpp; if (sh[j]->irq != irq) @@ -1845,14 +1964,13 @@ return; } - dspp = &HD(j)->sp[0]; - spp = &HD(j)->sp[1]; + spp = &HD(j)->sp; /* Make a local copy just before clearing the interrupt indication */ - memcpy(spp, dspp, sizeof(struct mssp)); + memcpy(spp, HD(j)->sp_cpu_addr, sizeof(struct mssp)); /* Clear the completion flag and cp pointer on the dynamic copy of sp */ - memset(dspp, 0, sizeof(struct mssp)); + memset(HD(j)->sp_cpu_addr, 0, sizeof(struct mssp)); /* Read the status register to clear the interrupt indication */ reg = inb(sh[j]->io_port + REG_STATUS); @@ -1910,6 +2028,8 @@ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n", BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble); + sync_dma(i, j); + if (linked_comm && SCpnt->device->queue_depth > 2 && TLDEV(SCpnt->device->type)) flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE); @@ -1987,6 +2107,7 @@ #else status = DID_BUS_BUSY << 16; #endif + HD(j)->retries++; HD(j)->last_retried_pid = SCpnt->pid; } @@ -2023,6 +2144,8 @@ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, reg, HD(j)->iocount); + unmap_dma(i, j); + /* Set the command state to inactive */ SCpnt->host_scribble = NULL; @@ -2036,14 +2159,14 @@ static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) { unsigned int j; - SPIN_FLAGS + unsigned long spin_flags; /* Check if the interrupt must be processed by this handler */ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return; - SPIN_LOCK_SAVE + spin_lock_irqsave(&io_request_lock, spin_flags); ihdlr(irq, j); - SPIN_UNLOCK_RESTORE + spin_unlock_irqrestore(&io_request_lock, spin_flags); } int eata2x_release(struct Scsi_Host *shpnt) { @@ -2054,13 +2177,15 @@ if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n", driver_name); - if( sh[j]->unchecked_isa_dma ) { - scsi_deregister_blocked_host(sh[j]); - } + if(sh[j]->unchecked_isa_dma) scsi_deregister_blocked_host(sh[j]); for (i = 0; i < sh[j]->can_queue; i++) if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); + if (HD(j)->sp_cpu_addr) + pci_free_consistent(HD(j)->pdev, sizeof(struct mssp), + HD(j)->sp_cpu_addr, HD(j)->sp_dma_addr); + free_irq(sh[j]->irq, &sha[j]); if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel); @@ -2077,4 +2202,4 @@ #ifndef MODULE __setup("eata=", option_setup); #endif /* end MODULE */ -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/scsi/eata.h b/drivers/scsi/eata.h --- a/drivers/scsi/eata.h Wed Feb 13 20:03:37 2002 +++ b/drivers/scsi/eata.h Wed Feb 13 20:03:37 2002 @@ -13,7 +13,7 @@ int eata2x_reset(Scsi_Cmnd *); int eata2x_biosparam(Disk *, kdev_t, int *); -#define EATA_VERSION "6.05.00" +#define EATA_VERSION "6.50.00" #define EATA { \ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \ diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h --- a/drivers/scsi/hosts.h Wed Feb 13 20:03:45 2002 +++ b/drivers/scsi/hosts.h Wed Feb 13 20:03:45 2002 @@ -292,6 +292,11 @@ unsigned emulated:1; /* + * True for drivers which can handle variable length IO + */ + unsigned can_do_varyio:1; + + /* * Name of proc directory */ char *proc_name; diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c --- a/drivers/scsi/ide-scsi.c Wed Feb 13 20:03:30 2002 +++ b/drivers/scsi/ide-scsi.c Wed Feb 13 20:03:30 2002 @@ -529,28 +529,35 @@ return 0; } +int idescsi_reinit(ide_drive_t *drive); + /* * IDE subdriver functions, registered with ide.c */ static ide_driver_t idescsi_driver = { - "ide-scsi", /* name */ - IDESCSI_VERSION, /* version */ - ide_scsi, /* media */ - 0, /* busy */ - 1, /* supports_dma */ - 0, /* supports_dsc_overlap */ - idescsi_cleanup, /* cleanup */ - idescsi_do_request, /* do_request */ - idescsi_end_request, /* end_request */ - NULL, /* ioctl */ - idescsi_open, /* open */ - idescsi_ide_release, /* release */ - NULL, /* media_change */ - NULL, /* revalidate */ - NULL, /* pre_reset */ - NULL, /* capacity */ - NULL, /* special */ - NULL /* proc */ + name: "ide-scsi", + version: IDESCSI_VERSION, + media: ide_scsi, + busy: 0, + supports_dma: 1, + supports_dsc_overlap: 0, + cleanup: idescsi_cleanup, + standby: NULL, + flushcache: NULL, + do_request: idescsi_do_request, + end_request: idescsi_end_request, + ioctl: NULL, + open: idescsi_open, + release: idescsi_ide_release, + media_change: NULL, + revalidate: NULL, + pre_reset: NULL, + capacity: NULL, + special: NULL, + proc: NULL, + reinit: idescsi_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, }; int idescsi_init (void); @@ -561,6 +568,43 @@ NULL }; +int idescsi_reinit (ide_drive_t *drive) +{ +#if 0 + idescsi_scsi_t *scsi; + byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255}; + int i, failed, id; + + if (!idescsi_initialized) + return 0; + for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++) + idescsi_drives[i] = NULL; + + MOD_INC_USE_COUNT; + for (i = 0; media[i] != 255; i++) { + failed = 0; + while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) { + + if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) { + printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idescsi_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (scsi); + continue; + } + for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++); + idescsi_setup (drive, scsi, id); + failed--; + } + } + ide_register_module(&idescsi_module); + MOD_DEC_USE_COUNT; +#endif + return 0; +} + /* * idescsi_init will register the driver for each scsi. */ @@ -591,7 +635,7 @@ continue; } for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++); - idescsi_setup (drive, scsi, id); + idescsi_setup (drive, scsi, id); failed--; } } diff -Nru a/drivers/scsi/ips.c b/drivers/scsi/ips.c --- a/drivers/scsi/ips.c Wed Feb 13 20:03:29 2002 +++ b/drivers/scsi/ips.c Wed Feb 13 20:03:29 2002 @@ -191,6 +191,7 @@ #ifdef MODULE static char *ips = NULL; MODULE_PARM(ips, "s"); + MODULE_LICENSE("GPL"); #endif /* diff -Nru a/drivers/scsi/qlogicisp.h b/drivers/scsi/qlogicisp.h --- a/drivers/scsi/qlogicisp.h Wed Feb 13 20:03:29 2002 +++ b/drivers/scsi/qlogicisp.h Wed Feb 13 20:03:29 2002 @@ -84,7 +84,8 @@ cmd_per_lun: 1, \ present: 0, \ unchecked_isa_dma: 0, \ - use_clustering: DISABLE_CLUSTERING \ + use_clustering: DISABLE_CLUSTERING, \ + can_do_varyio: 1 \ } #endif /* _QLOGICISP_H */ diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c --- a/drivers/scsi/scsi.c Wed Feb 13 20:03:55 2002 +++ b/drivers/scsi/scsi.c Wed Feb 13 20:03:55 2002 @@ -156,7 +156,12 @@ */ extern void scsi_old_done(Scsi_Cmnd * SCpnt); extern void scsi_old_times_out(Scsi_Cmnd * SCpnt); +extern int scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag); +/* + * Private interface into the new error handling code. + */ +extern int scsi_new_reset(Scsi_Cmnd *SCpnt, unsigned int flag); /* * Function: scsi_initialize_queue() @@ -2723,6 +2728,102 @@ */ scsi_release_commandblocks(SDpnt); kfree(SDpnt); +} + +/* + * Function: scsi_reset_provider_done_command + * + * Purpose: Dummy done routine. + * + * Notes: Some low level drivers will call scsi_done and end up here, + * others won't bother. + * We don't want the bogus command used for the bus/device + * reset to find its way into the mid-layer so we intercept + * it here. + */ +static void +scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt) +{ +} + +/* + * Function: scsi_reset_provider + * + * Purpose: Send requested reset to a bus or device at any phase. + * + * Arguments: device - device to send reset to + * flag - reset type (see scsi.h) + * + * Returns: SUCCESS/FAILURE. + * + * Notes: This is used by the SCSI Generic driver to provide + * Bus/Device reset capability. + */ +int +scsi_reset_provider(Scsi_Device *dev, int flag) +{ + Scsi_Cmnd SC, *SCpnt = &SC; + int rtn; + + memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout)); + SCpnt->host = dev->host; + SCpnt->device = dev; + SCpnt->target = dev->id; + SCpnt->lun = dev->lun; + SCpnt->channel = dev->channel; + SCpnt->request.rq_status = RQ_SCSI_BUSY; + SCpnt->request.waiting = NULL; + SCpnt->use_sg = 0; + SCpnt->old_use_sg = 0; + SCpnt->old_cmd_len = 0; + SCpnt->underflow = 0; + SCpnt->transfersize = 0; + SCpnt->resid = 0; + SCpnt->serial_number = 0; + SCpnt->serial_number_at_timeout = 0; + SCpnt->host_scribble = NULL; + SCpnt->next = NULL; + SCpnt->state = SCSI_STATE_INITIALIZING; + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + + memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd)); + + SCpnt->scsi_done = scsi_reset_provider_done_command; + SCpnt->done = NULL; + SCpnt->reset_chain = NULL; + + SCpnt->buffer = NULL; + SCpnt->bufflen = 0; + SCpnt->request_buffer = NULL; + SCpnt->request_bufflen = 0; + + SCpnt->internal_timeout = NORMAL_TIMEOUT; + SCpnt->abort_reason = DID_ABORT; + + SCpnt->cmd_len = 0; + + SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; + SCpnt->sc_request = NULL; + SCpnt->sc_magic = SCSI_CMND_MAGIC; + + /* + * Sometimes the command can get back into the timer chain, + * so use the pid as an identifier. + */ + SCpnt->pid = 0; + + if (dev->host->hostt->use_new_eh_code) { + rtn = scsi_new_reset(SCpnt, flag); + } else { + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + rtn = scsi_old_reset(SCpnt, flag); + spin_unlock_irqrestore(&io_request_lock, flags); + } + + scsi_delete_timer(SCpnt); + return rtn; } /* diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h --- a/drivers/scsi/scsi.h Wed Feb 13 20:03:41 2002 +++ b/drivers/scsi/scsi.h Wed Feb 13 20:03:41 2002 @@ -849,6 +849,16 @@ current->state = TASK_RUNNING; \ }; } +/* + * old style reset request from external source + * (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c) + */ +#define SCSI_TRY_RESET_DEVICE 1 +#define SCSI_TRY_RESET_BUS 2 +#define SCSI_TRY_RESET_HOST 3 + +extern int scsi_reset_provider(Scsi_Device *, int); + #endif /* diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c --- a/drivers/scsi/scsi_error.c Wed Feb 13 20:03:30 2002 +++ b/drivers/scsi/scsi_error.c Wed Feb 13 20:03:30 2002 @@ -984,15 +984,24 @@ case DID_SOFT_ERROR: goto maybe_retry; + case DID_ERROR: + if (msg_byte(SCpnt->result) == COMMAND_COMPLETE && + status_byte(SCpnt->result) == RESERVATION_CONFLICT) + /* + * execute reservation conflict processing code + * lower down + */ + break; + /* FALLTHROUGH */ + case DID_BUS_BUSY: case DID_PARITY: - case DID_ERROR: goto maybe_retry; case DID_TIME_OUT: /* - * When we scan the bus, we get timeout messages for - * these commands if there is no device available. - * Other hosts report DID_NO_CONNECT for the same thing. + * When we scan the bus, we get timeout messages for + * these commands if there is no device available. + * Other hosts report DID_NO_CONNECT for the same thing. */ if ((SCpnt->cmnd[0] == TEST_UNIT_READY || SCpnt->cmnd[0] == INQUIRY)) { @@ -1053,8 +1062,13 @@ */ return SUCCESS; case BUSY: - case RESERVATION_CONFLICT: goto maybe_retry; + + case RESERVATION_CONFLICT: + printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", + SCpnt->host->host_no, SCpnt->channel, + SCpnt->device->id, SCpnt->device->lun); + return SUCCESS; /* causes immediate I/O error */ default: return FAILED; } @@ -1957,6 +1971,45 @@ */ if (host->eh_notify != NULL) up(host->eh_notify); +} + +/* + * Function: scsi_new_reset + * + * Purpose: Send requested reset to a bus or device at any phase. + * + * Arguments: SCpnt - command ptr to send reset with (usually a dummy) + * flag - reset type (see scsi.h) + * + * Returns: SUCCESS/FAILURE. + * + * Notes: This is used by the SCSI Generic driver to provide + * Bus/Device reset capability. + */ +int +scsi_new_reset(Scsi_Cmnd *SCpnt, int flag) +{ + int rtn; + + switch(flag) { + case SCSI_TRY_RESET_DEVICE: + rtn = scsi_try_bus_device_reset(SCpnt, 0); + if (rtn == SUCCESS) + break; + /* FALLTHROUGH */ + case SCSI_TRY_RESET_BUS: + rtn = scsi_try_bus_reset(SCpnt); + if (rtn == SUCCESS) + break; + /* FALLTHROUGH */ + case SCSI_TRY_RESET_HOST: + rtn = scsi_try_host_reset(SCpnt); + break; + default: + rtn = FAILED; + } + + return rtn; } /* diff -Nru a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c --- a/drivers/scsi/scsi_merge.c Wed Feb 13 20:03:49 2002 +++ b/drivers/scsi/scsi_merge.c Wed Feb 13 20:03:49 2002 @@ -150,7 +150,14 @@ panic("DMA pool exhausted"); } -#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering) +/* + * FIXME(eric) - the original disk code disabled clustering for MOD + * devices. I have no idea why we thought this was a good idea - my + * guess is that it was an attempt to limit the size of requests to MOD + * devices. + */ +#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering && \ + SD->type != TYPE_MOD) /* * This entire source file deals with the new queueing code. diff -Nru a/drivers/scsi/scsi_obsolete.c b/drivers/scsi/scsi_obsolete.c --- a/drivers/scsi/scsi_obsolete.c Wed Feb 13 20:03:29 2002 +++ b/drivers/scsi/scsi_obsolete.c Wed Feb 13 20:03:29 2002 @@ -503,11 +503,18 @@ break; case RESERVATION_CONFLICT: - printk("scsi%d, channel %d : RESERVATION CONFLICT performing" - " reset.\n", SCpnt->host->host_no, SCpnt->channel); - scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - status = REDO; + /* + * Most HAs will return an error for + * this, so usually reservation + * conflicts will be processed under + * DID_ERROR code + */ + printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", + SCpnt->host->host_no, SCpnt->channel, + SCpnt->device->id, SCpnt->device->lun); + status = CMD_FINISHED; /* returns I/O error */ break; + default: printk("Internal error %s %d \n" "status byte = %d \n", __FILE__, @@ -557,6 +564,14 @@ exit = (DRIVER_HARD | SUGGEST_ABORT); break; case DID_ERROR: + if (msg_byte(result) == COMMAND_COMPLETE && + status_byte(result) == RESERVATION_CONFLICT) { + printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", + SCpnt->host->host_no, SCpnt->channel, + SCpnt->device->id, SCpnt->device->lun); + status = CMD_FINISHED; /* returns I/O error */ + break; + } status = MAYREDO; exit = (DRIVER_HARD | SUGGEST_ABORT); break; @@ -1098,6 +1113,34 @@ return rtn; } + +/* + * This function exports SCSI Bus, Device or Host reset capability + * and is for use with the SCSI generic driver. + */ +int +scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag) +{ + unsigned int old_flags = SCSI_RESET_SYNCHRONOUS; + + switch(flag) { + case SCSI_TRY_RESET_DEVICE: + /* no suggestion flags to add, device reset is default */ + break; + case SCSI_TRY_RESET_BUS: + old_flags |= SCSI_RESET_SUGGEST_BUS_RESET; + break; + case SCSI_TRY_RESET_HOST: + old_flags |= SCSI_RESET_SUGGEST_HOST_RESET; + break; + default: + return FAILED; + } + + if (scsi_reset(SCpnt, old_flags)) + return FAILED; + return SUCCESS; +} /* * Overrides for Emacs so that we follow Linus's tabbing style. diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c --- a/drivers/scsi/scsi_scan.c Wed Feb 13 20:03:41 2002 +++ b/drivers/scsi/scsi_scan.c Wed Feb 13 20:03:41 2002 @@ -23,21 +23,20 @@ #include #endif -/* The following devices are known not to tolerate a lun != 0 scan for - * one reason or another. Some will respond to all luns, others will - * lock up. +/* + * Flags for irregular SCSI devices that need special treatment */ - -#define BLIST_NOLUN 0x001 -#define BLIST_FORCELUN 0x002 -#define BLIST_BORKEN 0x004 -#define BLIST_KEY 0x008 -#define BLIST_SINGLELUN 0x010 -#define BLIST_NOTQ 0x020 -#define BLIST_SPARSELUN 0x040 -#define BLIST_MAX5LUN 0x080 -#define BLIST_ISDISK 0x100 -#define BLIST_ISROM 0x200 +#define BLIST_NOLUN 0x001 /* Don't scan for LUNs */ +#define BLIST_FORCELUN 0x002 /* Known to have LUNs, force sanning */ +#define BLIST_BORKEN 0x004 /* Flag for broken handshaking */ +#define BLIST_KEY 0x008 /* Needs to be unlocked by special command */ +#define BLIST_SINGLELUN 0x010 /* LUNs should better not be used in parallel */ +#define BLIST_NOTQ 0x020 /* Buggy Tagged Command Queuing */ +#define BLIST_SPARSELUN 0x040 /* Non consecutive LUN numbering */ +#define BLIST_MAX5LUN 0x080 /* Avoid LUNS >= 5 */ +#define BLIST_ISDISK 0x100 /* Treat as (removable) disk */ +#define BLIST_ISROM 0x200 /* Treat as (removable) CD-ROM */ +#define BLIST_LARGELUN 0x400 /* LUNs larger than 7 despite reporting as SCSI 2 */ static void print_inquiry(unsigned char *data); static int scan_scsis_single(unsigned int channel, unsigned int dev, @@ -62,6 +61,10 @@ */ static struct dev_info device_list[] = { +/* The following devices are known not to tolerate a lun != 0 scan for + * one reason or another. Some will respond to all luns, others will + * lock up. + */ {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ @@ -75,7 +78,6 @@ {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */ {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* guess what? */ {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /*Responds to all lun */ - {"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */ {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */ {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* Responds to all lun */ {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ @@ -126,6 +128,7 @@ {"INSITE", "Floptical F*8I", "*", BLIST_KEY}, {"INSITE", "I325VM", "*", BLIST_KEY}, {"LASOUND","CDX7405","3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */ {"NRC", "MBR-7", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"NRC", "MBR-7.4", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"REGAL", "CDC-4X", "*", BLIST_MAX5LUN | BLIST_SINGLELUN}, @@ -152,7 +155,7 @@ {"DELL", "PV660F PSEUDO", "*", BLIST_SPARSELUN}, {"DELL", "PSEUDO DEVICE .", "*", BLIST_SPARSELUN}, // Dell PV 530F {"DELL", "PV530F", "*", BLIST_SPARSELUN}, // Dell PV 530F - {"EMC", "SYMMETRIX", "*", BLIST_SPARSELUN}, + {"EMC", "SYMMETRIX", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"CMD", "CRA-7280", "*", BLIST_SPARSELUN}, // CMD RAID Controller {"CNSI", "G7324", "*", BLIST_SPARSELUN}, // Chaparral G7324 RAID {"CNSi", "G8324", "*", BLIST_SPARSELUN}, // Chaparral G8324 RAID @@ -434,8 +437,13 @@ scsi_result) && !sparse_lun) break; /* break means don't probe further for luns!=0 */ - if (SDpnt && (0 == lun)) - lun0_sl = SDpnt->scsi_level; + if (SDpnt && (0 == lun)) { + int bflags = get_device_flags (scsi_result); + if (bflags & BLIST_LARGELUN) + lun0_sl = SCSI_3; /* treat as SCSI 3 */ + else + lun0_sl = SDpnt->scsi_level; + } } /* for lun ends */ } /* if this_id != id ends */ } /* for dev ends */ diff -Nru a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c --- a/drivers/scsi/scsi_syms.c Wed Feb 13 20:03:54 2002 +++ b/drivers/scsi/scsi_syms.c Wed Feb 13 20:03:54 2002 @@ -84,6 +84,11 @@ EXPORT_SYMBOL(scsi_deregister_blocked_host); /* + * This symbol is for the highlevel drivers (e.g. sg) only. + */ +EXPORT_SYMBOL(scsi_reset_provider); + +/* * These are here only while I debug the rest of the scsi stuff. */ EXPORT_SYMBOL(scsi_hostlist); diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c --- a/drivers/scsi/sd.c Wed Feb 13 20:03:39 2002 +++ b/drivers/scsi/sd.c Wed Feb 13 20:03:39 2002 @@ -91,6 +91,7 @@ static int *sd_blocksizes; static int *sd_hardsizes; /* Hardware sector size */ static int *sd_max_sectors; +static char *sd_varyio; static int check_scsidisk_media_change(kdev_t); static int fop_revalidate_scsidisk(kdev_t); @@ -1111,6 +1112,12 @@ if (!sd_max_sectors) goto cleanup_max_sectors; + sd_varyio = kmalloc((sd_template.dev_max << 4), GFP_ATOMIC); + if (!sd_varyio) + goto cleanup_varyio; + + memset(sd_varyio, 0, (sd_template.dev_max << 4)); + for (i = 0; i < sd_template.dev_max << 4; i++) { sd_blocksizes[i] = 1024; sd_hardsizes[i] = 512; @@ -1181,6 +1188,8 @@ cleanup_sd_gendisks: kfree(sd); cleanup_sd: + kfree(sd_varyio); +cleanup_varyio: kfree(sd_max_sectors); cleanup_max_sectors: kfree(sd_hardsizes); @@ -1245,6 +1254,8 @@ return 1; } +#define SD_DISK_MAJOR(i) SD_MAJOR((i) >> 4) + static int sd_attach(Scsi_Device * SDp) { unsigned int devnum; @@ -1283,6 +1294,14 @@ printk("Attached scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n", SDp->removable ? "removable " : "", nbuff, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); + + if (SDp->host->hostt->can_do_varyio) { + if (blkdev_varyio[SD_DISK_MAJOR(i)] == NULL) { + blkdev_varyio[SD_DISK_MAJOR(i)] = + sd_varyio + ((i / SCSI_DISKS_PER_MAJOR) >> 8); + } + memset(blkdev_varyio[SD_DISK_MAJOR(i)] + (devnum << 4), 1, 16); + } return 0; } @@ -1411,6 +1430,7 @@ kfree(sd_sizes); kfree(sd_blocksizes); kfree(sd_hardsizes); + kfree(sd_varyio); kfree((char *) sd); } for (i = 0; i < N_USED_SD_MAJORS; i++) { diff -Nru a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c --- a/drivers/scsi/u14-34f.c Wed Feb 13 20:03:44 2002 +++ b/drivers/scsi/u14-34f.c Wed Feb 13 20:03:44 2002 @@ -1,6 +1,9 @@ /* * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. * + * 01 Jan 2002 Rev. 6.50 for linux 2.4.16 + * + Use the dynamic DMA mapping API. + * * 1 May 2001 Rev. 6.05 for linux 2.4.4 * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d) * @@ -183,7 +186,7 @@ * * Multiple U14F and/or U34F host adapters are supported. * - * Copyright (C) 1994-2001 Dario Ballabio (ballabio_dario@emc.com) + * Copyright (C) 1994-2002 Dario Ballabio (ballabio_dario@emc.com) * * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it * @@ -334,7 +337,6 @@ * the driver sets host->wish_block = TRUE for all ISA boards. */ -#include #include #ifndef LinuxVersionCode @@ -343,6 +345,9 @@ #define MAX_INT_PARAM 10 +#if defined(MODULE) +#include + MODULE_PARM(boot_options, "s"); MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i"); MODULE_PARM(linked_comm, "i"); @@ -352,6 +357,8 @@ MODULE_PARM(ext_tran, "i"); MODULE_AUTHOR("Dario Ballabio"); +#endif + #include #include #include @@ -370,17 +377,11 @@ #include "u14-34f.h" #include #include +#include #include #include #include -#define SPIN_FLAGS unsigned long spin_flags; -#define SPIN_LOCK spin_lock_irq(&io_request_lock); -#define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags); -#define SPIN_UNLOCK spin_unlock_irq(&io_request_lock); -#define SPIN_UNLOCK_RESTORE \ - spin_unlock_irqrestore(&io_request_lock, spin_flags); - /* Values for the PRODUCT_ID ports for the 14/34F */ #define PRODUCT_ID1 0x56 #define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */ @@ -483,13 +484,13 @@ unsigned char clink_id; /* identifies command in chain */ unsigned char use_sg; /* (if sg is set) 8 bytes per list */ unsigned char sense_len; - unsigned char scsi_cdbs_len; /* 6, 10, or 12 */ - unsigned char scsi_cdbs[12]; /* SCSI commands */ + unsigned char cdb_len; /* 6, 10, or 12 */ + unsigned char cdb[12]; /* SCSI Command Descriptor Block */ unsigned char adapter_status; /* non-zero indicates HA error */ unsigned char target_status; /* non-zero indicates target error */ unsigned int sense_addr PACKED; Scsi_Cmnd *SCpnt; - unsigned int index; /* cp index */ + unsigned int cpp_index; /* cp index */ struct sg_list *sglist; }; @@ -507,6 +508,7 @@ unsigned int retries; /* Number of internal retries */ unsigned long last_retried_pid; /* Pid of last retried command */ unsigned char subversion; /* Bus type, either ISA or ESA */ + struct pci_dev *pdev; /* Always NULL */ unsigned char heads; unsigned char sectors; @@ -537,21 +539,11 @@ #define HD(board) ((struct hostdata *) &sh[board]->hostdata) #define BN(board) (HD(board)->board_name) -#define SWAP_BYTE(x) ((unsigned long)( \ - (((unsigned long)(x) & 0x000000ffU) << 24) | \ - (((unsigned long)(x) & 0x0000ff00U) << 8) | \ - (((unsigned long)(x) & 0x00ff0000U) >> 8) | \ - (((unsigned long)(x) & 0xff000000U) >> 24))) - -#if defined(__BIG_ENDIAN) -#define H2DEV(x) SWAP_BYTE(x) -#else -#define H2DEV(x) (x) -#endif +/* Device is Little Endian */ +#define H2DEV(x) cpu_to_le32(x) +#define DEV2H(x) le32_to_cpu(x) -#define DEV2H(x) H2DEV(x) #define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0) -#define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0) static void do_interrupt_handler(int, void *, struct pt_regs *); static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int); @@ -653,8 +645,8 @@ cpp->xdir = DTD_IN; cpp->data_address = V2DEV(HD(j)->board_id); cpp->data_len = H2DEV(sizeof(HD(j)->board_id)); - cpp->scsi_cdbs_len = 6; - cpp->scsi_cdbs[0] = HA_CMD_INQUIRY; + cpp->cdb_len = 6; + cpp->cdb[0] = HA_CMD_INQUIRY; if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { printk("%s: board_inquiry, adapter busy.\n", BN(j)); @@ -672,10 +664,10 @@ /* Issue OGM interrupt */ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - SPIN_UNLOCK + spin_unlock_irq(&io_request_lock); time = jiffies; while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L); - SPIN_LOCK + spin_lock_irq(&io_request_lock); if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) { HD(j)->cp_stat[0] = FREE; @@ -834,14 +826,14 @@ unsigned long flags; scsi_register_blocked_host(sh[j]); sh[j]->unchecked_isa_dma = TRUE; - + flags=claim_dma_lock(); disable_dma(dma_channel); clear_dma_ff(dma_channel); set_dma_mode(dma_channel, DMA_MODE_CASCADE); enable_dma(dma_channel); release_dma_lock(flags); - + sh[j]->dma_channel = dma_channel; sprintf(BN(j), "U14F%d", j); bus_type = "ISA"; @@ -881,7 +873,7 @@ if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN; if (j == 0) { - printk("UltraStor 14F/34F: Copyright (C) 1994-2001 Dario Ballabio.\n"); + printk("UltraStor 14F/34F: Copyright (C) 1994-2002 Dario Ballabio.\n"); printk("%s config options -> of:%c, lc:%c, mq:%d, et:%c.\n", driver_name, YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth, YESNO(ext_tran)); @@ -951,8 +943,7 @@ return 1; } -int u14_34f_detect(Scsi_Host_Template *tpnt) -{ +int u14_34f_detect(Scsi_Host_Template *tpnt) { unsigned int j = 0, k; tpnt->proc_name = "u14-34f"; @@ -980,26 +971,94 @@ return j; } -static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) { - unsigned int k, data_len = 0; +static inline void map_dma(unsigned int i, unsigned int j) { + unsigned int k, count, data_len = 0, pci_dir; struct scatterlist *sgpnt; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (SCpnt->sense_buffer) + cpp->sense_addr = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->sense_buffer, + sizeof SCpnt->sense_buffer, PCI_DMA_FROMDEVICE)); + + cpp->sense_len = sizeof SCpnt->sense_buffer; + + if (!SCpnt->use_sg) { + + if (!SCpnt->request_bufflen) + cpp->data_address = V2DEV(SCpnt->request_buffer); + + else if (SCpnt->request_buffer) + cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, + SCpnt->request_buffer, SCpnt->request_bufflen, pci_dir)); + + cpp->data_len = H2DEV(SCpnt->request_bufflen); + return; + } sgpnt = (struct scatterlist *) SCpnt->request_buffer; + count = pci_map_sg(HD(j)->pdev, sgpnt, SCpnt->use_sg, pci_dir); - for (k = 0; k < SCpnt->use_sg; k++) { - cpp->sglist[k].address = V2DEV(sgpnt[k].address); - cpp->sglist[k].num_bytes = H2DEV(sgpnt[k].length); + for (k = 0; k < count; k++) { + cpp->sglist[k].address = H2DEV(sg_dma_address(&sgpnt[k])); + cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(&sgpnt[k])); data_len += sgpnt[k].length; } + cpp->sg = TRUE; cpp->use_sg = SCpnt->use_sg; cpp->data_address = V2DEV(cpp->sglist); cpp->data_len = H2DEV(data_len); } -static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { - unsigned int i, j, k; +static void unmap_dma(unsigned int i, unsigned int j) { + unsigned int pci_dir; struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (DEV2H(cpp->sense_addr)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); + + if (SCpnt->use_sg) + pci_unmap_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); + + else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) + pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); + +} + +static void sync_dma(unsigned int i, unsigned int j) { + unsigned int pci_dir; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); + + if (DEV2H(cpp->sense_addr)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); + + if (SCpnt->use_sg) + pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + SCpnt->use_sg, pci_dir); + + else if (DEV2H(cpp->data_address) && DEV2H(cpp->data_len)) + pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + DEV2H(cpp->data_len), pci_dir); + +} + +static inline void scsi_to_dev_dir(unsigned int i, unsigned int j) { + unsigned int k; static const unsigned char data_out_cmds[] = { 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e, @@ -1010,9 +1069,51 @@ static const unsigned char data_none_cmds[] = { 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e, 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47, - 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5 + 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00 }; + struct mscp *cpp; + Scsi_Cmnd *SCpnt; + + cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; + + if (SCpnt->sc_data_direction == SCSI_DATA_READ) { + cpp->xdir = DTD_IN; + return; + } + else if (SCpnt->sc_data_direction == SCSI_DATA_WRITE) { + cpp->xdir = DTD_OUT; + return; + } + else if (SCpnt->sc_data_direction == SCSI_DATA_NONE) { + cpp->xdir = DTD_NONE; + return; + } + + if (SCpnt->sc_data_direction != SCSI_DATA_UNKNOWN) + panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); + + cpp->xdir = DTD_IN; + + for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) + if (SCpnt->cmnd[0] == data_out_cmds[k]) { + cpp->xdir = DTD_OUT; + break; + } + + if (cpp->xdir == DTD_IN) + for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) + if (SCpnt->cmnd[0] == data_none_cmds[k]) { + cpp->xdir = DTD_NONE; + break; + } + +} + +static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { + unsigned int i, j, k; + struct mscp *cpp; + /* j is the board number */ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number; @@ -1044,47 +1145,26 @@ memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *)); SCpnt->scsi_done = done; - cpp->index = i; - SCpnt->host_scribble = (unsigned char *) &cpp->index; + cpp->cpp_index = i; + SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index; if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n", BN(j), i, SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid); - cpp->xdir = DTD_IN; - - for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) - if (SCpnt->cmnd[0] == data_out_cmds[k]) { - cpp->xdir = DTD_OUT; - break; - } - - if (cpp->xdir == DTD_IN) - for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) - if (SCpnt->cmnd[0] == data_none_cmds[k]) { - cpp->xdir = DTD_NONE; - break; - } - cpp->opcode = OP_SCSI; cpp->channel = SCpnt->channel; cpp->target = SCpnt->target; cpp->lun = SCpnt->lun; cpp->SCpnt = SCpnt; - cpp->sense_addr = V2DEV(SCpnt->sense_buffer); - cpp->sense_len = sizeof SCpnt->sense_buffer; + cpp->cdb_len = SCpnt->cmd_len; + memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); - if (SCpnt->use_sg) { - cpp->sg = TRUE; - build_sg_list(cpp, SCpnt); - } - else { - cpp->data_address = V2DEV(SCpnt->request_buffer); - cpp->data_len = H2DEV(SCpnt->request_bufflen); - } + /* Use data transfer direction SCpnt->sc_data_direction */ + scsi_to_dev_dir(i, j); - cpp->scsi_cdbs_len = SCpnt->cmd_len; - memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len); + /* Map DMA buffers and SG list */ + map_dma(i, j); if (linked_comm && SCpnt->device->queue_depth > 2 && TLDEV(SCpnt->device->type)) { @@ -1094,6 +1174,7 @@ } if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { + unmap_dma(i, j); SCpnt->host_scribble = NULL; printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n", BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid); @@ -1156,6 +1237,7 @@ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i); if (SCarg->eh_state == SCSI_STATE_TIMEOUT) { + unmap_dma(i, j); SCarg->host_scribble = NULL; HD(j)->cp_stat[i] = FREE; printk("%s, abort, mbox %d, eh_state timeout, pid %ld.\n", @@ -1177,6 +1259,7 @@ } if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { + unmap_dma(i, j); SCarg->result = DID_ABORT << 16; SCarg->host_scribble = NULL; HD(j)->cp_stat[i] = FREE; @@ -1274,16 +1357,19 @@ #endif HD(j)->in_reset = TRUE; - SPIN_UNLOCK + + spin_unlock_irq(&io_request_lock); time = jiffies; while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - SPIN_LOCK + spin_lock_irq(&io_request_lock); + printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); for (i = 0; i < sh[j]->can_queue; i++) { if (HD(j)->cp_stat[i] == IN_RESET) { SCpnt = HD(j)->cp[i].SCpnt; + unmap_dma(i, j); SCpnt->result = DID_RESET << 16; SCpnt->host_scribble = NULL; @@ -1296,6 +1382,7 @@ else if (HD(j)->cp_stat[i] == ABORTING) { SCpnt = HD(j)->cp[i].SCpnt; + unmap_dma(i, j); SCpnt->result = DID_RESET << 16; SCpnt->host_scribble = NULL; @@ -1536,23 +1623,25 @@ return; } - spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM)); - cpp = spp; + ret = inl(sh[j]->io_port + REG_ICM); /* Clear interrupt pending flag */ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); -#if defined(DEBUG_GENERATE_ABORTS) - if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) return; -#endif - /* Find the mailbox to be serviced on this board */ - i = cpp - HD(j)->cp; + for (i = 0; i < sh[j]->can_queue; i++) + if (V2DEV(&(HD(j)->cp[i])) == ret) break; - if (cpp < HD(j)->cp || cpp >= HD(j)->cp + sh[j]->can_queue - || i >= sh[j]->can_queue) + if (i >= sh[j]->can_queue) panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j), - (void *)ret, HD(j)->cp); + (void *)ret, (void *)V2DEV(HD(j)->cp)); + + cpp = &(HD(j)->cp[i]); + spp = cpp; + +#if defined(DEBUG_GENERATE_ABORTS) + if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) return; +#endif if (HD(j)->cp_stat[i] == IGNORE) { HD(j)->cp_stat[i] = FREE; @@ -1588,6 +1677,8 @@ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n", BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble); + sync_dma(i, j); + if (linked_comm && SCpnt->device->queue_depth > 2 && TLDEV(SCpnt->device->type)) flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE); @@ -1705,6 +1796,8 @@ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, reg, HD(j)->iocount); + unmap_dma(i, j); + /* Set the command state to inactive */ SCpnt->host_scribble = NULL; @@ -1718,14 +1811,14 @@ static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) { unsigned int j; - SPIN_FLAGS + unsigned long spin_flags; /* Check if the interrupt must be processed by this handler */ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return; - SPIN_LOCK_SAVE + spin_lock_irqsave(&io_request_lock, spin_flags); ihdlr(irq, j); - SPIN_UNLOCK_RESTORE + spin_unlock_irqrestore(&io_request_lock, spin_flags); } int u14_34f_release(struct Scsi_Host *shpnt) { @@ -1736,9 +1829,7 @@ if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n", driver_name); - if( sh[j]->unchecked_isa_dma ) { - scsi_deregister_blocked_host(sh[j]); - } + if(sh[j]->unchecked_isa_dma) scsi_deregister_blocked_host(sh[j]); for (i = 0; i < sh[j]->can_queue; i++) if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist); @@ -1752,7 +1843,6 @@ return FALSE; } -MODULE_LICENSE("BSD without advertisement clause"); static Scsi_Host_Template driver_template = ULTRASTOR_14_34F; #include "scsi_module.c" @@ -1760,3 +1850,4 @@ #ifndef MODULE __setup("u14-34f=", option_setup); #endif /* end MODULE */ +MODULE_LICENSE("GPL"); diff -Nru a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h --- a/drivers/scsi/u14-34f.h Wed Feb 13 20:03:47 2002 +++ b/drivers/scsi/u14-34f.h Wed Feb 13 20:03:47 2002 @@ -13,7 +13,7 @@ int u14_34f_reset(Scsi_Cmnd *); int u14_34f_biosparam(Disk *, kdev_t, int *); -#define U14_34F_VERSION "6.05.00" +#define U14_34F_VERSION "6.50.00" #define ULTRASTOR_14_34F { \ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ diff -Nru a/drivers/sensors/Config.in b/drivers/sensors/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/Config.in Wed Feb 13 20:03:57 2002 @@ -0,0 +1,45 @@ +# +# Sensor device configuration +# All depend on CONFIG_I2C_PROC. +# ISA-only devices depend on CONFIG_I2C_ISA also. +# + +if [ "$CONFIG_I2C" = "m" -o "$CONFIG_I2C" = "y" ] ; then +if [ "$CONFIG_I2C_PROC" = "m" -o "$CONFIG_I2C_PROC" = "y" ] ; then + mainmenu_option next_comment + comment 'Hardware sensors support' + + dep_mbool 'Hardware sensors support' CONFIG_SENSORS $CONFIG_I2C $CONFIG_I2C_PROC + + if [ "$CONFIG_SENSORS" != "n" ]; then + dep_tristate ' Analog Devices ADM1021 and compatibles' CONFIG_SENSORS_ADM1021 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM1024' CONFIG_SENSORS_ADM1024 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM1025' CONFIG_SENSORS_ADM1025 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM9240 and compatibles' CONFIG_SENSORS_ADM9240 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Dallas DS1621 and DS1625' CONFIG_SENSORS_DS1621 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Fujitsu-Siemens Poseidon' CONFIG_SENSORS_FSCPOS $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Genesys Logic GL518SM' CONFIG_SENSORS_GL518SM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Genesys Logic GL520SM' CONFIG_SENSORS_GL520SM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' HP Maxilife' CONFIG_SENSORS_MAXILIFE $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' ITE 8705/8712, SiS950' CONFIG_SENSORS_IT87 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Myson MTP008' CONFIG_SENSORS_MTP008 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM75 and compatibles' CONFIG_SENSORS_LM75 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM78' CONFIG_SENSORS_LM78 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM80' CONFIG_SENSORS_LM80 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM87' CONFIG_SENSORS_LM87 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Silicon Integrated Systems Corp. SiS5595' CONFIG_SENSORS_SIS5595 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA + dep_tristate ' Texas Instruments THMC50 and compatibles' CONFIG_SENSORS_THMC50 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' VIA 686a Integrated Hardware Monitor' CONFIG_SENSORS_VIA686A $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA + dep_tristate ' Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F' CONFIG_SENSORS_W83781D $CONFIG_I2C $CONFIG_I2C_PROC + bool 'Other I2C devices' CONFIG_SENSORS_OTHER + if [ "$CONFIG_SENSORS_OTHER" = "y" ] ; then + dep_tristate ' Brooktree BT869 Video Modulator' CONFIG_SENSORS_BT869 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' DDC Monitor EDID EEPROM' CONFIG_SENSORS_DDCMON $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' EEprom (DIMM) reader ' CONFIG_SENSORS_EEPROM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Matrix-Orbital LCD Displays' CONFIG_SENSORS_MATORB $CONFIG_I2C $CONFIG_I2C_PROC + fi + fi + endmenu +fi +fi + diff -Nru a/drivers/sensors/Makefile b/drivers/sensors/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/Makefile Wed Feb 13 20:03:56 2002 @@ -0,0 +1,35 @@ +# +# Makefile for the kernel hardware sensors drivers. +# + +MOD_LIST_NAME := SENSORS_MODULES +O_TARGET := sensor.o + +export-objs := sensors.o + +obj-$(CONFIG_SENSORS) += sensors.o +obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o +obj-$(CONFIG_SENSORS_ADM1024) += adm1024.o +obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o +obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_BT869) += bt869.o +obj-$(CONFIG_SENSORS_DDCMON) += ddcmon.o +obj-$(CONFIG_SENSORS_DS1621) += ds1621.o +obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o +obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o +obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_IT87) += it87.o +obj-$(CONFIG_SENSORS_LM75) += lm75.o +obj-$(CONFIG_SENSORS_LM78) += lm78.o +obj-$(CONFIG_SENSORS_LM80) += lm80.o +obj-$(CONFIG_SENSORS_LM87) += lm87.o +obj-$(CONFIG_SENSORS_MAXILIFE) += maxilife.o +obj-$(CONFIG_SENSORS_MTP008) += mtp008.o +obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o +obj-$(CONFIG_SENSORS_THMC50) += thmc50.o +obj-$(CONFIG_SENSORS_VIA686A) += via686a.o +obj-$(CONFIG_SENSORS_W83781D) += w83781d.o + +include $(TOPDIR)/Rules.make + diff -Nru a/drivers/sensors/adm1021.c b/drivers/sensors/adm1021.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/adm1021.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,655 @@ +/* + adm1021.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, + 0x4c, 0x4e, SENSORS_I2C_END +}; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_7(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm); + +/* adm1021 constants specified below */ + +/* The adm1021 registers */ +/* Read-only */ +#define ADM1021_REG_TEMP 0x00 +#define ADM1021_REG_REMOTE_TEMP 0x01 +#define ADM1021_REG_STATUS 0x02 +#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys */ +#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +/* These use different addresses for reading/writing */ +#define ADM1021_REG_CONFIG_R 0x03 +#define ADM1021_REG_CONFIG_W 0x09 +#define ADM1021_REG_CONV_RATE_R 0x04 +#define ADM1021_REG_CONV_RATE_W 0x0A +/* These are for the ADM1023's additional precision on the remote temp sensor */ +#define ADM1021_REG_REM_TEMP_PREC 0x010 +#define ADM1021_REG_REM_OFFSET 0x011 +#define ADM1021_REG_REM_OFFSET_PREC 0x012 +#define ADM1021_REG_REM_TOS_PREC 0x013 +#define ADM1021_REG_REM_THYST_PREC 0x014 +/* limits */ +#define ADM1021_REG_TOS_R 0x05 +#define ADM1021_REG_TOS_W 0x0B +#define ADM1021_REG_REMOTE_TOS_R 0x07 +#define ADM1021_REG_REMOTE_TOS_W 0x0D +#define ADM1021_REG_THYST_R 0x06 +#define ADM1021_REG_THYST_W 0x0C +#define ADM1021_REG_REMOTE_THYST_R 0x08 +#define ADM1021_REG_REMOTE_THYST_W 0x0E +/* write-only */ +#define ADM1021_REG_ONESHOT 0x0F + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +/* Conversions note: 1021 uses normal integer signed-byte format*/ +#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) + +/* Initial values */ + +/* Note: Eventhough I left the low and high limits named os and hyst, +they don't quite work like a thermostat the way the LM75 does. I.e., +a lower temp than THYST actuall triggers an alarm instead of +clearing it. Weird, ey? --Phil */ +#define adm1021_INIT_TOS 60 +#define adm1021_INIT_THYST 20 +#define adm1021_INIT_REMOTE_TOS 60 +#define adm1021_INIT_REMOTE_THYST 20 + +/* Each client has this additional data */ +struct adm1021_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 temp, temp_os, temp_hyst; /* Register values */ + u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code; + /* Special values for ADM1023 only */ + u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, + remote_temp_offset, remote_temp_offset_prec; +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1021_init(void); +static int __init adm1021_cleanup(void); +static int adm1021_attach_adapter(struct i2c_adapter *adapter); +static int adm1021_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void adm1021_init_client(struct i2c_client *client); +static int adm1021_detach_client(struct i2c_client *client); +static int adm1021_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1021_inc_use(struct i2c_client *client); +static void adm1021_dec_use(struct i2c_client *client); +static int adm1021_read_value(struct i2c_client *client, u8 reg); +static int adm1021_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void adm1021_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm1021_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver adm1021_driver = { + /* name */ "ADM1021, MAX1617 sensor driver", + /* id */ I2C_DRIVERID_ADM1021, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1021_attach_adapter, + /* detach_client */ &adm1021_detach_client, + /* command */ &adm1021_command, + /* inc_use */ &adm1021_inc_use, + /* dec_use */ &adm1021_dec_use +}; + +/* These files are created for each detected adm1021. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1021_dir_table_template[] = { + {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_temp}, + {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_remote_temp}, + {ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_die_code}, + {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_alarms}, + {0} +}; + +static ctl_table adm1021_max_dir_table_template[] = { + {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_temp}, + {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_remote_temp}, + {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_alarms}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata adm1021_initialized = 0; + +/* I choose here for semi-static allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1021_id = 0; + +int adm1021_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1021_detect); +} + +static int adm1021_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1021_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1021_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1021_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1021_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1021_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + (adm1021_read_value(new_client, ADM1021_REG_STATUS) & + 0x03) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + + if (kind <= 0) { + i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); + if (i == 0x41) + if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) + kind = adm1023; + else + kind = adm1021; + else if (i == 0x49) + kind = thmc10; + else if (i == 0x23) + kind = gl523sm; + else if ((i == 0x4d) && + (adm1021_read_value + (new_client, ADM1021_REG_DEV_ID) == 0x01)) + kind = max1617a; + /* LM84 Mfr ID in a different place */ + else + if (adm1021_read_value + (new_client, ADM1021_REG_CONV_RATE_R) == 0x00) + kind = lm84; + else + kind = max1617; + } + + if (kind == max1617) { + type_name = "max1617"; + client_name = "MAX1617 chip"; + } else if (kind == max1617a) { + type_name = "max1617a"; + client_name = "MAX1617A chip"; + } else if (kind == adm1021) { + type_name = "adm1021"; + client_name = "ADM1021 chip"; + } else if (kind == adm1023) { + type_name = "adm1023"; + client_name = "ADM1023 chip"; + } else if (kind == thmc10) { + type_name = "thmc10"; + client_name = "THMC10 chip"; + } else if (kind == lm84) { + type_name = "lm84"; + client_name = "LM84 chip"; + } else if (kind == gl523sm) { + type_name = "gl523sm"; + client_name = "GL523SM chip"; + } else { +#ifdef DEBUG + printk("adm1021.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1021_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + data->type == + adm1021 ? + adm1021_dir_table_template : + adm1021_max_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1021 chip */ + adm1021_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +void adm1021_init_client(struct i2c_client *client) +{ + /* Initialize the adm1021 chip */ + adm1021_write_value(client, ADM1021_REG_TOS_W, + TEMP_TO_REG(adm1021_INIT_TOS)); + adm1021_write_value(client, ADM1021_REG_THYST_W, + TEMP_TO_REG(adm1021_INIT_THYST)); + adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W, + TEMP_TO_REG(adm1021_INIT_REMOTE_TOS)); + adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W, + TEMP_TO_REG(adm1021_INIT_REMOTE_THYST)); + /* Enable ADC and disable suspend mode */ + adm1021_write_value(client, ADM1021_REG_CONFIG_W, 0); + /* Set Conversion rate to 1/sec (this can be tinkered with) */ + adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); +} + +int adm1021_detach_client(struct i2c_client *client) +{ + + int err; + + i2c_deregister_entry(((struct adm1021_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1021.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + + +/* No commands defined yet */ +int adm1021_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1021_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1021_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are byte-sized */ +int adm1021_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +void adm1021_update_client(struct i2c_client *client) +{ + struct adm1021_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1021 update\n"); +#endif + + data->temp = adm1021_read_value(client, ADM1021_REG_TEMP); + data->temp_os = + adm1021_read_value(client, ADM1021_REG_TOS_R); + data->temp_hyst = + adm1021_read_value(client, ADM1021_REG_THYST_R); + data->remote_temp = + adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); + data->remote_temp_os = + adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); + data->remote_temp_hyst = + adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); + data->alarms = + adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec; + if (data->type == adm1021) + data->die_code = + adm1021_read_value(client, + ADM1021_REG_DIE_CODE); + if (data->type == adm1023) { + data->remote_temp_prec = + adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); + data->remote_temp_os_prec = + adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); + data->remote_temp_hyst_prec = + adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); + data->remote_temp_offset = + adm1021_read_value(client, ADM1021_REG_REM_OFFSET); + data->remote_temp_offset_prec = + adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void adm1021_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + adm1021_write_value(client, ADM1021_REG_TOS_W, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + adm1021_write_value(client, ADM1021_REG_THYST_W, + data->temp_hyst); + } + } +} + +void adm1021_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ +int prec=0; + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + if (data->type == adm1023) { *nrels_mag = 3; } + else { *nrels_mag = 0; } + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[1] = TEMP_FROM_REG(data->remote_temp_hyst); + results[2] = TEMP_FROM_REG(data->remote_temp); + if (data->type == adm1023) { + results[0]=results[0]*1000 + + ((data->remote_temp_os_prec >> 5) * 125); + results[1]=results[1]*1000 + + ((data->remote_temp_hyst_prec >> 5) * 125); + results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + + ((data->remote_temp_offset_prec >> 5) * 125); + results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + + ((data->remote_temp_prec >> 5) * 125); + *nrels_mag = 4; + } else { + *nrels_mag = 3; + } + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (data->type == adm1023) { + prec=((results[0]-((results[0]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_TOS_PREC, + prec); + results[0]=results[0]/1000; + data->remote_temp_os_prec=prec; + } + data->remote_temp_os = TEMP_TO_REG(results[0]); + adm1021_write_value(client, + ADM1021_REG_REMOTE_TOS_W, + data->remote_temp_os); + } + if (*nrels_mag >= 2) { + if (data->type == adm1023) { + prec=((results[1]-((results[1]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_THYST_PREC, + prec); + results[1]=results[1]/1000; + data->remote_temp_hyst_prec=prec; + } + data->remote_temp_hyst = TEMP_TO_REG(results[1]); + adm1021_write_value(client, + ADM1021_REG_REMOTE_THYST_W, + data->remote_temp_hyst); + } + if (*nrels_mag >= 3) { + if (data->type == adm1023) { + prec=((results[2]-((results[2]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_OFFSET_PREC, + prec); + results[2]=results[2]/1000; + data->remote_temp_offset_prec=prec; + data->remote_temp_offset=results[2]; + adm1021_write_value(client, + ADM1021_REG_REM_OFFSET, + data->remote_temp_offset); + } + } + } +} + +void adm1021_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = data->die_code; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* Can't write to it */ + } +} + +void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = data->alarms; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* Can't write to it */ + } +} + +int __init sensors_adm1021_init(void) +{ + int res; + + printk("adm1021.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1021_initialized = 0; + if ((res = i2c_add_driver(&adm1021_driver))) { + printk + ("adm1021.o: Driver registration failed, module not inserted.\n"); + adm1021_cleanup(); + return res; + } + adm1021_initialized++; + return 0; +} + +int __init adm1021_cleanup(void) +{ + int res; + + if (adm1021_initialized >= 1) { + if ((res = i2c_del_driver(&adm1021_driver))) { + printk + ("adm1021.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1021_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("adm1021 driver"); + +int init_module(void) +{ + return sensors_adm1021_init(); +} + +int cleanup_module(void) +{ + return adm1021_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/adm1024.c b/drivers/sensors/adm1024.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/adm1024.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,933 @@ +/* + adm1024.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Add by Ken Bowley from the adm1025.c written by + Gordon Wu and from adm9240.c written by + Copyright (c) 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Supports the Analog Devices ADM1024. See doc/chips/adm1024 for details */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(adm1024); + +/* Many ADM1024 constants specified below */ + +#define ADM1024_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM1024_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM1024_REG_IN(nr) (0x20 + (nr)) + +/* The ADM1024 registers */ +#define ADM1024_REG_INT_TEMP_TRIP_SET 0x13 +#define ADM1024_REG_EXT_TEMP_TRIP_SET 0x14 +#define ADM1024_REG_TEST 0x15 +#define ADM1024_REG_CHANNEL_MODE 0x16 +#define ADM1024_REG_INT_TEMP_TRIP 0x17 /* read only */ +#define ADM1024_REG_EXT_TEMP_TRIP 0x18 /* read only */ +#define ADM1024_REG_ANALOG_OUT 0x19 +#define ADM1024_REG_AIN1_LOW_LIMIT 0x1A +#define ADM1024_REG_AIN2_LOW_LIMIT 0x1B +/* These are all read-only */ +#define ADM1024_REG_2_5V 0x20 /* 2.5V Measured Value/EXT Temp 2 */ +#define ADM1024_REG_VCCP1 0x21 +#define ADM1024_REG_3_3V 0x22 /* VCC Measured Value */ +#define ADM1024_REG_5V 0x23 +#define ADM1024_REG_12V 0x24 +#define ADM1024_REG_VCCP2 0x25 +#define ADM1024_REG_EXT_TEMP1 0x26 +#define ADM1024_REG_TEMP 0x27 +#define ADM1024_REG_FAN1 0x28 /* FAN1/AIN1 Value */ +#define ADM1024_REG_FAN2 0x29 /* FAN2/AIN2 Value */ +#define ADM1024_REG_COMPANY_ID 0x3E /* 0x41 for ADM1024 */ +#define ADM1024_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM1024_REG_2_5V_HIGH 0x2B /* 2.5V/Ext Temp2 High Limit */ +#define ADM1024_REG_2_5V_LOW 0x2C /* 2.5V/Ext Temp2 Low Limit */ +#define ADM1024_REG_VCCP1_HIGH 0x2D +#define ADM1024_REG_VCCP1_LOW 0x2E +#define ADM1024_REG_3_3V_HIGH 0x2F /* VCC High Limit */ +#define ADM1024_REG_3_3V_LOW 0x30 /* VCC Low Limit */ +#define ADM1024_REG_5V_HIGH 0x31 +#define ADM1024_REG_5V_LOW 0x32 +#define ADM1024_REG_12V_HIGH 0x33 +#define ADM1024_REG_12V_LOW 0x34 +#define ADM1024_REG_VCCP2_HIGH 0x35 +#define ADM1024_REG_VCCP2_LOW 0x36 +#define ADM1024_REG_EXT_TEMP1_HIGH 0x37 +#define ADM1024_REG_EXT_TEMP1_LOW 0x38 +#define ADM1024_REG_TOS 0x39 +#define ADM1024_REG_THYST 0x3A +#define ADM1024_REG_FAN1_MIN 0x3B +#define ADM1024_REG_FAN2_MIN 0x3C + +#define ADM1024_REG_CONFIG 0x40 +#define ADM1024_REG_INT1_STAT 0x41 +#define ADM1024_REG_INT2_STAT 0x42 +#define ADM1024_REG_INT1_MASK 0x43 +#define ADM1024_REG_INT2_MASK 0x44 + +#define ADM1024_REG_CHASSIS_CLEAR 0x46 +#define ADM1024_REG_VID_FAN_DIV 0x47 +#define ADM1024_REG_I2C_ADDR 0x48 +#define ADM1024_REG_VID4 0x49 +#define ADM1024_REG_CONFIG2 0x4A +#define ADM1024_REG_TEMP_CONFIG 0x4B +#define ADM1024_REG_EXTMODE1 0x4C /* Interupt Status Register Mirror No. 1 */ +#define ADM1024_REG_EXTMODE2 0x4D /* Interupt Status Register Mirror No. 2 */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define EXT_TEMP_FROM_REG(temp) (((temp)>0x80?(temp)-0x100:(temp))*10) + + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM1024_INIT_IN_0 190 +#define ADM1024_INIT_IN_1 190 +#define ADM1024_INIT_IN_2 190 +#define ADM1024_INIT_IN_3 190 +#define ADM1024_INIT_IN_4 190 +#define ADM1024_INIT_IN_5 190 + +#define ADM1024_INIT_IN_PERCENTAGE 10 + +#define ADM1024_INIT_IN_MIN_0 \ + (ADM1024_INIT_IN_0 - ADM1024_INIT_IN_0 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_0 \ + (ADM1024_INIT_IN_0 + ADM1024_INIT_IN_0 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_1 \ + (ADM1024_INIT_IN_1 - ADM1024_INIT_IN_1 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_1 \ + (ADM1024_INIT_IN_1 + ADM1024_INIT_IN_1 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_2 \ + (ADM1024_INIT_IN_2 - ADM1024_INIT_IN_2 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_2 \ + (ADM1024_INIT_IN_2 + ADM1024_INIT_IN_2 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_3 \ + (ADM1024_INIT_IN_3 - ADM1024_INIT_IN_3 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_3 \ + (ADM1024_INIT_IN_3 + ADM1024_INIT_IN_3 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_4 \ + (ADM1024_INIT_IN_4 - ADM1024_INIT_IN_4 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_4 \ + (ADM1024_INIT_IN_4 + ADM1024_INIT_IN_4 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_5 \ + (ADM1024_INIT_IN_5 - ADM1024_INIT_IN_5 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_5 \ + (ADM1024_INIT_IN_5 + ADM1024_INIT_IN_5 * ADM1024_INIT_IN_PERCENTAGE / 100) + +#define ADM1024_INIT_FAN_MIN_1 3000 +#define ADM1024_INIT_FAN_MIN_2 3000 + +#define ADM1024_INIT_TEMP_OS_MAX 600 +#define ADM1024_INIT_TEMP_OS_HYST 500 +#define ADM1024_INIT_TEMP_HOT_MAX 700 +#define ADM1024_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM1024, we need to keep some data in memory. That + data is pointed to by adm1024_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm1024 client is + allocated. */ +struct adm1024_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + int temp; /* Temp, shifted right */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + int temp1; /* Ext Temp 1 */ + u8 temp1_os_max; + u8 temp1_os_hyst; + int temp2; /* Ext Temp 2 */ + u8 temp2_os_max; + u8 temp2_os_hyst; + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1024_init(void); +static int __init adm1024_cleanup(void); + +static int adm1024_attach_adapter(struct i2c_adapter *adapter); +static int adm1024_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm1024_detach_client(struct i2c_client *client); +static int adm1024_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1024_inc_use(struct i2c_client *client); +static void adm1024_dec_use(struct i2c_client *client); + +static int adm1024_read_value(struct i2c_client *client, u8 register); +static int adm1024_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm1024_update_client(struct i2c_client *client); +static void adm1024_init_client(struct i2c_client *client); + + +static void adm1024_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp1(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp2(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm1024_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM1024 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1024_id = 0; + +static struct i2c_driver adm1024_driver = { + /* name */ "ADM1024 sensor driver", + /* id */ I2C_DRIVERID_ADM1024, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1024_attach_adapter, + /* detach_client */ &adm1024_detach_client, + /* command */ &adm1024_command, + /* inc_use */ &adm1024_inc_use, + /* dec_use */ &adm1024_dec_use +}; + +/* Used by adm1024_init/cleanup */ +static int __initdata adm1024_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM1024. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1024_dir_table_template[] = { + {ADM1024_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan}, + {ADM1024_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan}, + {ADM1024_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp}, + {ADM1024_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp1}, + {ADM1024_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp2}, + {ADM1024_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan_div}, + {ADM1024_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_alarms}, + {ADM1024_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_analog_out}, + {ADM1024_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_vid}, + {0} +}; + +int adm1024_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1024_detect); +} + +static int adm1024_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1024_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1024.o: adm1024_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1024_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1024_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1024_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1024_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if((adm1024_read_value(new_client, ADM1024_REG_CONFIG) & 0x80) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm1024_read_value(new_client, ADM1024_REG_COMPANY_ID); + if (i == 0x41) + kind = adm1024; + else { + if (kind == 0) + printk + ("adm1024.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm1024) { + type_name = "adm1024"; + client_name = "ADM1024 chip"; + } else { +#ifdef DEBUG + printk("adm1024.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1024_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm1024_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1024 chip */ + adm1024_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm1024_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm1024_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1024.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm1024_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1024_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1024_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm1024_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm1024_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM1024. It should set limits, etc. */ +void adm1024_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm1024_write_value(client, ADM1024_REG_CONFIG, 0x80); + + adm1024_write_value(client, ADM1024_REG_IN_MIN(0), + IN_TO_REG(ADM1024_INIT_IN_MIN_0, 0)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(0), + IN_TO_REG(ADM1024_INIT_IN_MAX_0, 0)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(1), + IN_TO_REG(ADM1024_INIT_IN_MIN_1, 1)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(1), + IN_TO_REG(ADM1024_INIT_IN_MAX_1, 1)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(2), + IN_TO_REG(ADM1024_INIT_IN_MIN_2, 2)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(2), + IN_TO_REG(ADM1024_INIT_IN_MAX_2, 2)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(3), + IN_TO_REG(ADM1024_INIT_IN_MIN_3, 3)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(3), + IN_TO_REG(ADM1024_INIT_IN_MAX_3, 3)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(4), + IN_TO_REG(ADM1024_INIT_IN_MIN_4, 4)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(4), + IN_TO_REG(ADM1024_INIT_IN_MAX_4, 4)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(5), + IN_TO_REG(ADM1024_INIT_IN_MIN_5, 5)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(5), + IN_TO_REG(ADM1024_INIT_IN_MAX_5, 5)); + adm1024_write_value(client, ADM1024_REG_FAN1_MIN, + FAN_TO_REG(ADM1024_INIT_FAN_MIN_1, 2)); + adm1024_write_value(client, ADM1024_REG_FAN2_MIN, + FAN_TO_REG(ADM1024_INIT_FAN_MIN_2, 2)); + adm1024_write_value(client, ADM1024_REG_TOS, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_THYST, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_2_5V_HIGH, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_2_5V_LOW, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_TEMP_CONFIG, 0x00); + + /* Enable temperature channel 2 */ + adm1024_write_value(client, ADM1024_REG_CHANNEL_MODE, adm1024_read_value(client, ADM1024_REG_CHANNEL_MODE) | 0x04); + + /* Start monitoring */ + adm1024_write_value(client, ADM1024_REG_CONFIG, 0x07); +} + +void adm1024_update_client(struct i2c_client *client) +{ + struct adm1024_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm1024 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1024 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm1024_read_value(client, ADM1024_REG_IN(i)); + data->in_min[i] = + adm1024_read_value(client, + ADM1024_REG_IN_MIN(i)); + data->in_max[i] = + adm1024_read_value(client, + ADM1024_REG_IN_MAX(i)); + } + data->fan[0] = + adm1024_read_value(client, ADM1024_REG_FAN1); + data->fan_min[0] = + adm1024_read_value(client, ADM1024_REG_FAN1_MIN); + data->fan[1] = + adm1024_read_value(client, ADM1024_REG_FAN2); + data->fan_min[1] = + adm1024_read_value(client, ADM1024_REG_FAN2_MIN); + data->temp = + (adm1024_read_value(client, ADM1024_REG_TEMP) << 1) + + ((adm1024_read_value + (client, ADM1024_REG_TEMP_CONFIG) & 0x80) >> 7); + data->temp_os_max = + adm1024_read_value(client, ADM1024_REG_TOS); + data->temp_os_hyst = + adm1024_read_value(client, ADM1024_REG_THYST); + data->temp1 = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1); + data->temp1_os_max = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_HIGH); + data->temp1_os_hyst = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_LOW); + data->temp2 = + adm1024_read_value(client, ADM1024_REG_2_5V); + data->temp2_os_max = + adm1024_read_value(client, ADM1024_REG_2_5V_HIGH); + data->temp2_os_hyst = + adm1024_read_value(client, ADM1024_REG_2_5V_LOW); + + i = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (adm1024_read_value(client, ADM1024_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm1024_read_value(client, + ADM1024_REG_INT1_STAT) + + (adm1024_read_value(client, ADM1024_REG_INT2_STAT) << + 8); + data->analog_out = + adm1024_read_value(client, ADM1024_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm1024_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 225, 330, 500, 1200, 270 }; + + struct adm1024_data *data = client->data; + int nr = ctl_name - ADM1024_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm1024_write_value(client, ADM1024_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm1024_write_value(client, ADM1024_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void adm1024_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + int nr = ctl_name - ADM1024_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + adm1024_write_value(client, + nr == + 1 ? ADM1024_REG_FAN1_MIN : + ADM1024_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void adm1024_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_TOS, + data->temp_os_max); + } + if (*nrels_mag >= 2) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_THYST, + data->temp_os_hyst); + } + } +} + +void adm1024_temp1(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp1_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp1_os_hyst); + results[2] = EXT_TEMP_FROM_REG(data->temp1); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp1_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH, + data->temp1_os_max); + } + if (*nrels_mag >= 2) { + data->temp1_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW, + data->temp1_os_hyst); + } + } +} + +void adm1024_temp2(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp2_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp2_os_hyst); + results[2] = EXT_TEMP_FROM_REG(data->temp2); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp2_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_2_5V_HIGH, + data->temp2_os_max); + } + if (*nrels_mag >= 2) { + data->temp2_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_2_5V_LOW, + data->temp2_os_hyst); + } + } +} + +void adm1024_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void adm1024_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + adm1024_write_value(client, + ADM1024_REG_VID_FAN_DIV, old); + } + } +} + +void adm1024_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm1024_write_value(client, ADM1024_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void adm1024_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm1024_init(void) +{ + int res; + + printk("adm1024.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1024_initialized = 0; + + if ((res = i2c_add_driver(&adm1024_driver))) { + printk + ("adm1024.o: Driver registration failed, module not inserted.\n"); + adm1024_cleanup(); + return res; + } + adm1024_initialized++; + return 0; +} + +int __init adm1024_cleanup(void) +{ + int res; + + if (adm1024_initialized >= 1) { + if ((res = i2c_del_driver(&adm1024_driver))) { + printk + ("adm1024.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1024_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM1024 driver"); + +int init_module(void) +{ + return sensors_adm1024_init(); +} + +int cleanup_module(void) +{ + return adm1024_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/adm1025.c b/drivers/sensors/adm1025.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/adm1025.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,756 @@ +/* + adm1025.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Add by Gordon Wu according to the adm9240.c written by + Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Supports the Analog Devices ADM1025. See doc/chips/adm1025 for details */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(adm1025); + +/* Many ADM1025 constants specified below */ + +#define ADM1025_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM1025_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM1025_REG_IN(nr) (0x20 + (nr)) + +/* The ADM1025 registers */ +#define ADM1025_REG_TEST 0x15 +/* These are all read-only */ +#define ADM1025_REG_2_5V 0x20 +#define ADM1025_REG_VCCP1 0x21 +#define ADM1025_REG_3_3V 0x22 +#define ADM1025_REG_5V 0x23 +#define ADM1025_REG_12V 0x24 +#define ADM1025_REG_VCC 0x25 +#define ADM1025_REG_RTEMP 0x26 +#define ADM1025_REG_TEMP 0x27 +#define ADM1025_REG_COMPANY_ID 0x3E /* 0x41 for ADM1025 */ +#define ADM1025_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM1025_REG_2_5V_HIGH 0x2B +#define ADM1025_REG_2_5V_LOW 0x2C +#define ADM1025_REG_VCCP1_HIGH 0x2D +#define ADM1025_REG_VCCP1_LOW 0x2E +#define ADM1025_REG_3_3V_HIGH 0x2F +#define ADM1025_REG_3_3V_LOW 0x30 +#define ADM1025_REG_5V_HIGH 0x31 +#define ADM1025_REG_5V_LOW 0x32 +#define ADM1025_REG_12V_HIGH 0x33 +#define ADM1025_REG_12V_LOW 0x34 +#define ADM1025_REG_VCC_HIGH 0x35 +#define ADM1025_REG_VCC_LOW 0x36 +#define ADM1025_REG_RTEMP_HIGH 0x37 +#define ADM1025_REG_RTEMP_LOW 0x38 +#define ADM1025_REG_TEMP_HIGH 0x39 +#define ADM1025_REG_TEMP_LOW 0x3A + +#define ADM1025_REG_CONFIG 0x40 +#define ADM1025_REG_INT1_STAT 0x41 +#define ADM1025_REG_INT2_STAT 0x42 + +#define ADM1025_REG_VID 0x47 +#define ADM1025_REG_VID4 0x49 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) +#define TEMP_LIMIT_FROM_REG(val) TEMP_FROM_REG(val) +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), 0, 255) + +#define ALARMS_FROM_REG(val) (val) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM1025_INIT_IN_0 190 +#define ADM1025_INIT_IN_1 190 +#define ADM1025_INIT_IN_2 190 +#define ADM1025_INIT_IN_3 190 +#define ADM1025_INIT_IN_4 190 +#define ADM1025_INIT_IN_5 190 + +#define ADM1025_INIT_IN_PERCENTAGE 10 + +#define ADM1025_INIT_IN_MIN_0 \ + (ADM1025_INIT_IN_0 - ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_0 \ + (ADM1025_INIT_IN_0 + ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_1 \ + (ADM1025_INIT_IN_1 - ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_1 \ + (ADM1025_INIT_IN_1 + ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_2 \ + (ADM1025_INIT_IN_2 - ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_2 \ + (ADM1025_INIT_IN_2 + ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_3 \ + (ADM1025_INIT_IN_3 - ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_3 \ + (ADM1025_INIT_IN_3 + ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_4 \ + (ADM1025_INIT_IN_4 - ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_4 \ + (ADM1025_INIT_IN_4 + ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_5 \ + (ADM1025_INIT_IN_5 - ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_5 \ + (ADM1025_INIT_IN_5 + ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100) + +#define ADM1025_INIT_RTEMP_MAX 600 +#define ADM1025_INIT_RTEMP_MIN 0 +#define ADM1025_INIT_TEMP_MAX 600 +#define ADM1025_INIT_TEMP_MIN 0 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM1025, we need to keep some data in memory. That + data is pointed to by adm1025_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm1025 client is + allocated. */ +struct adm1025_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 rtemp; /* Register value */ + u8 rtemp_max; /* Register value */ + u8 rtemp_min; /* Register value */ + u8 temp; /* Register value */ + u8 temp_max; /* Register value */ + u8 temp_min; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1025_init(void); +static int __init adm1025_cleanup(void); + +static int adm1025_attach_adapter(struct i2c_adapter *adapter); +static int adm1025_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm1025_detach_client(struct i2c_client *client); +static int adm1025_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1025_inc_use(struct i2c_client *client); +static void adm1025_dec_use(struct i2c_client *client); + +static int adm1025_read_value(struct i2c_client *client, u8 register); +static int adm1025_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm1025_update_client(struct i2c_client *client); +static void adm1025_init_client(struct i2c_client *client); + + +static void adm1025_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_rm_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +/*static void adm1025_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results);*/ +static void adm1025_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM1025 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1025_id = 0; + +static struct i2c_driver adm1025_driver = { + /* name */ "ADM1025 sensor driver", + /* id */ I2C_DRIVERID_ADM1025, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1025_attach_adapter, + /* detach_client */ &adm1025_detach_client, + /* command */ &adm1025_command, + /* inc_use */ &adm1025_inc_use, + /* dec_use */ &adm1025_dec_use +}; + +/* Used by adm1025_init/cleanup */ +static int __initdata adm1025_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM1025. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1025_dir_table_template[] = { + {ADM1025_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_RTEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_rm_temp}, + {ADM1025_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_temp}, + {ADM1025_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_alarms}, +/* {ADM1025_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_analog_out},*/ + {ADM1025_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_vid}, + {0} +}; + +int adm1025_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1025_detect); +} + +static int adm1025_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1025_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1025.o: adm1025_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1025_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1025_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1025_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1025_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if((adm1025_read_value(new_client,ADM1025_REG_CONFIG) & 0x80) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm1025_read_value(new_client, ADM1025_REG_COMPANY_ID); + if (i == 0x41) + kind = adm1025; + else { + if (kind == 0) + printk + ("adm1025.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm1025) { + type_name = "adm1025"; + client_name = "ADM1025 chip"; + } else { +#ifdef DEBUG + printk("adm1025.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1025_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm1025_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1025 chip */ + adm1025_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm1025_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm1025_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1025.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm1025_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1025_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1025_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm1025_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm1025_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM1025. It should set limits, etc. */ +void adm1025_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm1025_write_value(client, ADM1025_REG_CONFIG, 0x80); + + adm1025_write_value(client, ADM1025_REG_IN_MIN(0), + IN_TO_REG(ADM1025_INIT_IN_MIN_0, 0)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(0), + IN_TO_REG(ADM1025_INIT_IN_MAX_0, 0)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(1), + IN_TO_REG(ADM1025_INIT_IN_MIN_1, 1)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(1), + IN_TO_REG(ADM1025_INIT_IN_MAX_1, 1)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(2), + IN_TO_REG(ADM1025_INIT_IN_MIN_2, 2)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(2), + IN_TO_REG(ADM1025_INIT_IN_MAX_2, 2)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(3), + IN_TO_REG(ADM1025_INIT_IN_MIN_3, 3)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(3), + IN_TO_REG(ADM1025_INIT_IN_MAX_3, 3)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(4), + IN_TO_REG(ADM1025_INIT_IN_MIN_4, 4)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(4), + IN_TO_REG(ADM1025_INIT_IN_MAX_4, 4)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(5), + IN_TO_REG(ADM1025_INIT_IN_MIN_5, 5)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(5), + IN_TO_REG(ADM1025_INIT_IN_MAX_5, 5)); + + adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH, + TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MAX)); + adm1025_write_value(client, ADM1025_REG_RTEMP_LOW, + TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MIN)); + adm1025_write_value(client, ADM1025_REG_TEMP_HIGH, + TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MAX)); + adm1025_write_value(client, ADM1025_REG_TEMP_LOW, + TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MIN)); + + /* Start monitoring */ + adm1025_write_value(client, ADM1025_REG_CONFIG, 0x01); +} + +void adm1025_update_client(struct i2c_client *client) +{ + struct adm1025_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm1025 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1025 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm1025_read_value(client, ADM1025_REG_IN(i)); + data->in_min[i] = + adm1025_read_value(client, + ADM1025_REG_IN_MIN(i)); + data->in_max[i] = + adm1025_read_value(client, + ADM1025_REG_IN_MAX(i)); + } + data->temp = + adm1025_read_value(client, ADM1025_REG_TEMP); + data->rtemp = + adm1025_read_value(client, ADM1025_REG_RTEMP); +#ifdef DEBUG + printk("The temp is %2x\n",data->temp); +#endif + data->temp_max = + adm1025_read_value(client, ADM1025_REG_TEMP_HIGH); + data->temp_min = + adm1025_read_value(client, ADM1025_REG_TEMP_LOW); + data->rtemp_max = + adm1025_read_value(client, ADM1025_REG_RTEMP_HIGH); + data->rtemp_min = + adm1025_read_value(client, ADM1025_REG_RTEMP_LOW); + + i = adm1025_read_value(client, ADM1025_REG_VID); + data->vid = i & 0x0f; + data->vid |= + (adm1025_read_value(client, ADM1025_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm1025_read_value(client, + ADM1025_REG_INT1_STAT) + + (adm1025_read_value(client, ADM1025_REG_INT2_STAT) << + 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm1025_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 225, 330, 500, 1200, 330 }; + + struct adm1025_data *data = client->data; + int nr = ctl_name - ADM1025_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm1025_write_value(client, ADM1025_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm1025_write_value(client, ADM1025_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + + +void adm1025_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_min); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_max = TEMP_LIMIT_TO_REG(results[0]); + adm1025_write_value(client, ADM1025_REG_TEMP_HIGH, + data->temp_max); + } + if (*nrels_mag >= 2) { + data->temp_min = TEMP_LIMIT_TO_REG(results[1]); + adm1025_write_value(client, ADM1025_REG_TEMP_LOW, + data->temp_min); + } + } +} + +void adm1025_rm_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->rtemp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->rtemp_min); + results[2] = TEMP_FROM_REG(data->rtemp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->rtemp_max = TEMP_LIMIT_TO_REG(results[0]); + adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH, + data->rtemp_max); + } + if (*nrels_mag >= 2) { + data->rtemp_min = TEMP_LIMIT_TO_REG(results[1]); + adm1025_write_value(client, ADM1025_REG_RTEMP_LOW, + data->rtemp_min); + } + } +} + +void adm1025_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} +/* +void adm1025_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm1025_write_value(client, ADM1025_REG_ANALOG_OUT, + data->analog_out); + } + } +} +*/ + +void adm1025_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm1025_init(void) +{ + int res; + + printk("adm1025.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1025_initialized = 0; + + if ((res = i2c_add_driver(&adm1025_driver))) { + printk + ("adm1025.o: Driver registration failed, module not inserted.\n"); + adm1025_cleanup(); + return res; + } + adm1025_initialized++; + return 0; +} + +int __init adm1025_cleanup(void) +{ + int res; + + if (adm1025_initialized >= 1) { + if ((res = i2c_del_driver(&adm1025_driver))) { + printk + ("adm1025.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1025_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM1025 driver"); + +int init_module(void) +{ + return sensors_adm1025_init(); +} + +int cleanup_module(void) +{ + return adm1025_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/adm9240.c b/drivers/sensors/adm9240.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/adm9240.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,878 @@ +/* + adm9240.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Supports ADM9240, DS1780, and LM81. See doc/chips/adm9240 for details */ + +/* + A couple notes about the ADM9240: + +* It claims to be 'LM7x' register compatible. This must be in reference + to only the LM78, because it is missing stuff to emulate LM75's as well. + (like the Winbond W83781 does) + +* This driver was written from rev. 0 of the PDF, but it seems well + written and complete (unlike the W83781 which is horrible and has + supposidly gone through a few revisions.. rev 0 of that one must + have been in crayon on construction paper...) + +* All analog inputs can range from 0 to 2.5, eventhough some inputs are + marked as being 5V, 12V, etc. I don't have any real voltages going + into my prototype, so I'm not sure that things are computed right, + but at least the limits seem to be working OK. + +* Another curiousity is that the fan_div seems to be read-only. I.e., + any written value to it doesn't seem to make any difference. The + fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases). + + + --Phil + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(adm9240, ds1780, lm81); + +/* Many ADM9240 constants specified below */ + +#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM9240_REG_IN(nr) (0x20 + (nr)) + +/* The ADM9240 registers */ +#define ADM9240_REG_TEST 0x15 +#define ADM9240_REG_ANALOG_OUT 0x19 +/* These are all read-only */ +#define ADM9240_REG_2_5V 0x20 +#define ADM9240_REG_VCCP1 0x21 +#define ADM9240_REG_3_3V 0x22 +#define ADM9240_REG_5V 0x23 +#define ADM9240_REG_12V 0x24 +#define ADM9240_REG_VCCP2 0x25 +#define ADM9240_REG_TEMP 0x27 +#define ADM9240_REG_FAN1 0x28 +#define ADM9240_REG_FAN2 0x29 +#define ADM9240_REG_COMPANY_ID 0x3E /* 0x23 for ADM9240; 0xDA for DS1780 */ + /* 0x01 for LM81 */ +#define ADM9240_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM9240_REG_2_5V_HIGH 0x2B +#define ADM9240_REG_2_5V_LOW 0x2C +#define ADM9240_REG_VCCP1_HIGH 0x2D +#define ADM9240_REG_VCCP1_LOW 0x2E +#define ADM9240_REG_3_3V_HIGH 0x2F +#define ADM9240_REG_3_3V_LOW 0x30 +#define ADM9240_REG_5V_HIGH 0x31 +#define ADM9240_REG_5V_LOW 0x32 +#define ADM9240_REG_12V_HIGH 0x33 +#define ADM9240_REG_12V_LOW 0x34 +#define ADM9240_REG_VCCP2_HIGH 0x35 +#define ADM9240_REG_VCCP2_LOW 0x36 +#define ADM9240_REG_TCRIT_LIMIT 0x37 /* LM81 only - not supported */ +#define ADM9240_REG_LOW_LIMIT 0x38 /* LM81 only - not supported */ +#define ADM9240_REG_TOS 0x39 +#define ADM9240_REG_THYST 0x3A +#define ADM9240_REG_FAN1_MIN 0x3B +#define ADM9240_REG_FAN2_MIN 0x3C + +#define ADM9240_REG_CONFIG 0x40 +#define ADM9240_REG_INT1_STAT 0x41 +#define ADM9240_REG_INT2_STAT 0x42 +#define ADM9240_REG_INT1_MASK 0x43 +#define ADM9240_REG_INT2_MASK 0x44 + +#define ADM9240_REG_COMPAT 0x45 /* dummy compat. register for other drivers? */ +#define ADM9240_REG_CHASSIS_CLEAR 0x46 +#define ADM9240_REG_VID_FAN_DIV 0x47 +#define ADM9240_REG_I2C_ADDR 0x48 +#define ADM9240_REG_VID4 0x49 +#define ADM9240_REG_TEMP_CONFIG 0x4B +#define ADM9240_REG_EXTMODE1 0x4C /* LM81 only - not supported */ +#define ADM9240_REG_EXTMODE2 0x4D /* LM81 only - not supported */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM9240_INIT_IN_0 190 +#define ADM9240_INIT_IN_1 190 +#define ADM9240_INIT_IN_2 190 +#define ADM9240_INIT_IN_3 190 +#define ADM9240_INIT_IN_4 190 +#define ADM9240_INIT_IN_5 190 + +#define ADM9240_INIT_IN_PERCENTAGE 10 + +#define ADM9240_INIT_IN_MIN_0 \ + (ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_0 \ + (ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_1 \ + (ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_1 \ + (ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_2 \ + (ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_2 \ + (ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_3 \ + (ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_3 \ + (ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_4 \ + (ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_4 \ + (ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_5 \ + (ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_5 \ + (ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100) + +#define ADM9240_INIT_FAN_MIN_1 3000 +#define ADM9240_INIT_FAN_MIN_2 3000 + +#define ADM9240_INIT_TEMP_OS_MAX 600 +#define ADM9240_INIT_TEMP_OS_HYST 500 +#define ADM9240_INIT_TEMP_HOT_MAX 700 +#define ADM9240_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM9240, we need to keep some data in memory. That + data is pointed to by adm9240_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm9240 client is + allocated. */ +struct adm9240_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + int temp; /* Temp, shifted right */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm9240_init(void); +static int __init adm9240_cleanup(void); + +static int adm9240_attach_adapter(struct i2c_adapter *adapter); +static int adm9240_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm9240_detach_client(struct i2c_client *client); +static int adm9240_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm9240_inc_use(struct i2c_client *client); +static void adm9240_dec_use(struct i2c_client *client); + +static int adm9240_read_value(struct i2c_client *client, u8 register); +static int adm9240_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm9240_update_client(struct i2c_client *client); +static void adm9240_init_client(struct i2c_client *client); + + +static void adm9240_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm9240_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM9240 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm9240_id = 0; + +static struct i2c_driver adm9240_driver = { + /* name */ "ADM9240 sensor driver", + /* id */ I2C_DRIVERID_ADM9240, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm9240_attach_adapter, + /* detach_client */ &adm9240_detach_client, + /* command */ &adm9240_command, + /* inc_use */ &adm9240_inc_use, + /* dec_use */ &adm9240_dec_use +}; + +/* Used by adm9240_init/cleanup */ +static int __initdata adm9240_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM9240. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm9240_dir_table_template[] = { + {ADM9240_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan}, + {ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan}, + {ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_temp}, + {ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan_div}, + {ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_alarms}, + {ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_analog_out}, + {ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_vid}, + {0} +}; + +int adm9240_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm9240_detect); +} + +static int adm9240_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm9240_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm9240.o: adm9240_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm9240_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm9240_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm9240_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm9240_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + ((adm9240_read_value + (new_client, ADM9240_REG_CONFIG) & 0x80) != 0x00) + || + (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR) + != address)) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm9240_read_value(new_client, ADM9240_REG_COMPANY_ID); + if (i == 0x23) + kind = adm9240; + else if (i == 0xda) + kind = ds1780; + else if (i == 0x01) + kind = lm81; + else { + if (kind == 0) + printk + ("adm9240.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm9240) { + type_name = "adm9240"; + client_name = "ADM9240 chip"; + } else if (kind == ds1780) { + type_name = "ds1780"; + client_name = "DS1780 chip"; + } else if (kind == lm81) { + type_name = "lm81"; + client_name = "LM81 chip"; + } else { +#ifdef DEBUG + printk("adm9240.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm9240_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm9240_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM9240 chip */ + adm9240_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm9240_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm9240_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm9240.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm9240_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm9240_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm9240_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM9240. It should set limits, etc. */ +void adm9240_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm9240_write_value(client, ADM9240_REG_CONFIG, 0x80); + + adm9240_write_value(client, ADM9240_REG_IN_MIN(0), + IN_TO_REG(ADM9240_INIT_IN_MIN_0, 0)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(0), + IN_TO_REG(ADM9240_INIT_IN_MAX_0, 0)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(1), + IN_TO_REG(ADM9240_INIT_IN_MIN_1, 1)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(1), + IN_TO_REG(ADM9240_INIT_IN_MAX_1, 1)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(2), + IN_TO_REG(ADM9240_INIT_IN_MIN_2, 2)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(2), + IN_TO_REG(ADM9240_INIT_IN_MAX_2, 2)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(3), + IN_TO_REG(ADM9240_INIT_IN_MIN_3, 3)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(3), + IN_TO_REG(ADM9240_INIT_IN_MAX_3, 3)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(4), + IN_TO_REG(ADM9240_INIT_IN_MIN_4, 4)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(4), + IN_TO_REG(ADM9240_INIT_IN_MAX_4, 4)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(5), + IN_TO_REG(ADM9240_INIT_IN_MIN_5, 5)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(5), + IN_TO_REG(ADM9240_INIT_IN_MAX_5, 5)); + adm9240_write_value(client, ADM9240_REG_FAN1_MIN, + FAN_TO_REG(ADM9240_INIT_FAN_MIN_1, 2)); + adm9240_write_value(client, ADM9240_REG_FAN2_MIN, + FAN_TO_REG(ADM9240_INIT_FAN_MIN_2, 2)); + adm9240_write_value(client, ADM9240_REG_TOS, + TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX)); + adm9240_write_value(client, ADM9240_REG_THYST, + TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST)); + adm9240_write_value(client, ADM9240_REG_TEMP_CONFIG, 0x00); + + /* Start monitoring */ + adm9240_write_value(client, ADM9240_REG_CONFIG, 0x01); +} + +void adm9240_update_client(struct i2c_client *client) +{ + struct adm9240_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm9240 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm9240 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm9240_read_value(client, ADM9240_REG_IN(i)); + data->in_min[i] = + adm9240_read_value(client, + ADM9240_REG_IN_MIN(i)); + data->in_max[i] = + adm9240_read_value(client, + ADM9240_REG_IN_MAX(i)); + } + data->fan[0] = + adm9240_read_value(client, ADM9240_REG_FAN1); + data->fan_min[0] = + adm9240_read_value(client, ADM9240_REG_FAN1_MIN); + data->fan[1] = + adm9240_read_value(client, ADM9240_REG_FAN2); + data->fan_min[1] = + adm9240_read_value(client, ADM9240_REG_FAN2_MIN); + data->temp = + (adm9240_read_value(client, ADM9240_REG_TEMP) << 1) + + ((adm9240_read_value + (client, ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7); + data->temp_os_max = + adm9240_read_value(client, ADM9240_REG_TOS); + data->temp_os_hyst = + adm9240_read_value(client, ADM9240_REG_THYST); + + i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (adm9240_read_value(client, ADM9240_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm9240_read_value(client, + ADM9240_REG_INT1_STAT) + + (adm9240_read_value(client, ADM9240_REG_INT2_STAT) << + 8); + data->analog_out = + adm9240_read_value(client, ADM9240_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm9240_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 270, 330, 500, 1200, 270 }; + + struct adm9240_data *data = client->data; + int nr = ctl_name - ADM9240_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void adm9240_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + adm9240_write_value(client, + nr == + 1 ? ADM9240_REG_FAN1_MIN : + ADM9240_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void adm9240_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm9240_write_value(client, ADM9240_REG_TOS, + data->temp_os_max); + } + if (*nrels_mag >= 2) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm9240_write_value(client, ADM9240_REG_THYST, + data->temp_os_hyst); + } + } +} + +void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void adm9240_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + adm9240_write_value(client, + ADM9240_REG_VID_FAN_DIV, old); + } + } +} + +void adm9240_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void adm9240_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm9240_init(void) +{ + int res; + + printk("adm9240.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm9240_initialized = 0; + + if ((res = i2c_add_driver(&adm9240_driver))) { + printk + ("adm9240.o: Driver registration failed, module not inserted.\n"); + adm9240_cleanup(); + return res; + } + adm9240_initialized++; + return 0; +} + +int __init adm9240_cleanup(void) +{ + int res; + + if (adm9240_initialized >= 1) { + if ((res = i2c_del_driver(&adm9240_driver))) { + printk + ("adm9240.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm9240_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM9240 driver"); + +int init_module(void) +{ + return sensors_adm9240_init(); +} + +int cleanup_module(void) +{ + return adm9240_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/bt869.c b/drivers/sensors/bt869.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/bt869.c Wed Feb 13 20:04:00 2002 @@ -0,0 +1,580 @@ +/* + bt869.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; + +/* found only at 0x44 or 0x45 */ +static unsigned short normal_i2c_range[] = { 0x44, 0x45, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(bt869); + +/* Many bt869 constants specified below */ + +/* The bt869 registers */ +/* Coming soon: Many, many registers */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + + /*none */ + +/* Initial values */ +/*none*/ + +/* Each client has this additional data */ +struct bt869_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 status[3]; /* Register values */ + u16 res[2]; /* Resolution XxY */ + u8 ntsc; /* 1=NTSC, 0=PAL */ + u8 half; /* go half res */ + u8 depth; /* screen depth */ + u8 colorbars; /* turn on/off colorbar calibration screen */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_bt869_init(void); +static int __init bt869_cleanup(void); +static int bt869_attach_adapter(struct i2c_adapter *adapter); +static int bt869_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void bt869_init_client(struct i2c_client *client); +static int bt869_detach_client(struct i2c_client *client); +static int bt869_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void bt869_inc_use(struct i2c_client *client); +static void bt869_dec_use(struct i2c_client *client); +static int bt869_read_value(struct i2c_client *client, u8 reg); +static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value); +static void bt869_status(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_ntsc(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_res(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_half(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_colorbars(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_depth(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver bt869_driver = { + /* name */ "BT869 video-output chip driver", + /* id */ I2C_DRIVERID_BT869, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &bt869_attach_adapter, + /* detach_client */ &bt869_detach_client, + /* command */ &bt869_command, + /* inc_use */ &bt869_inc_use, + /* dec_use */ &bt869_dec_use +}; + +/* These files are created for each detected bt869. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table bt869_dir_table_template[] = { + {BT869_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_status}, + {BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_ntsc}, + {BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_res}, + {BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_half}, + {BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_colorbars}, + {BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_depth}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata bt869_initialized = 0; + +int bt869_id = 0; + +int bt869_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, bt869_detect); +} + +/* This function is called by i2c_detect */ +int bt869_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct bt869_data *data; + int err = 0; + const char *type_name, *client_name; + + + printk("bt869.o: probing address %d .\n", address); + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("bt869.o: bt869_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access bt869_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct bt869_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = + (struct bt869_data *) (((struct i2c_client *) new_client) + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &bt869_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + i2c_smbus_write_byte_data(new_client, 0xC4, 0); /* set status bank 0 */ + cur = i2c_smbus_read_byte(new_client); + printk("bt869.o: address 0x%X testing-->0x%X\n", address, cur); + if ((cur & 0xE0) != 0x20) + goto ERROR1; + + /* Determine the chip type */ + kind = ((cur & 0x20) >> 5); + + if (kind) { + type_name = "bt869"; + client_name = "bt869 chip"; + printk("bt869.o: BT869 detected\n"); + } else { + type_name = "bt868"; + client_name = "bt868 chip"; + printk("bt869.o: BT868 detected\n"); + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = bt869_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + bt869_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + bt869_init_client((struct i2c_client *) new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int bt869_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct bt869_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("bt869.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int bt869_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void bt869_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void bt869_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are byte-sized. + bt869 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int bt869_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte(client); +} + +/* All registers are byte-sized. + bt869 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int bt869_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +void bt869_init_client(struct i2c_client *client) +{ + struct bt869_data *data = client->data; + + /* Initialize the bt869 chip */ + bt869_write_value(client, 0x0ba, 0x80); + // bt869_write_value(client,0x0D6, 0x00); + /* Be a slave to the clock on the Voodoo3 */ + bt869_write_value(client, 0xa0, 0x80); + bt869_write_value(client, 0xba, 0x20); + /* depth =16bpp */ + bt869_write_value(client, 0x0C6, 0x001); + bt869_write_value(client, 0xC4, 1); + /* Flicker free enable and config */ + bt869_write_value(client, 0xC8, 0); + data->res[0] = 640; + data->res[1] = 480; + data->ntsc = 1; + data->half = 0; + data->colorbars = 0; + data->depth = 16; + +} + +void bt869_update_client(struct i2c_client *client) +{ + struct bt869_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { +#ifdef DEBUG + printk("Starting bt869 update\n"); +#endif +/* Set values of device */ + if ((data->res[0] == 640) && (data->res[1] == 480)) { + bt869_write_value(client, 0xB8, (!data->ntsc)); + bt869_write_value(client, 0xa0, 0x80 + 0x0C); + printk("bt869.o: writing into config -->0x%X\n", + (0 + (!data->ntsc))); + } else if ((data->res[0] == 800) && (data->res[1] == 600)) { + bt869_write_value(client, 0xB8, + (2 + (!data->ntsc))); + bt869_write_value(client, 0xa0, 0x80 + 0x11); + printk("bt869.o: writing into config -->0x%X\n", + (2 + (!data->ntsc))); + } else { + bt869_write_value(client, 0xB8, (!data->ntsc)); + bt869_write_value(client, 0xa0, 0x80 + 0x0C); + printk("bt869.o: writing into config -->0x%X\n", + (0 + (!data->ntsc))); + printk + ("bt869.o: Warning: arbitrary resolutions not supported yet. Using 640x480.\n"); + data->res[0] = 640; + data->res[1] = 480; + } + if ((data->depth != 24) && (data->depth != 16)) + data->depth = 16; + if (data->depth == 16) + bt869_write_value(client, 0x0C6, 0x001); + if (data->depth == 24) + bt869_write_value(client, 0x0C6, 0x000); + bt869_write_value(client, 0xD4, data->half << 6); + /* Be a slave to the clock on the Voodoo3 */ + bt869_write_value(client, 0xba, 0x20); + /* depth =16bpp */ + bt869_write_value(client, 0x0C6, 0x001); + bt869_write_value(client, 0xC4, 1); + +/* Get status */ + bt869_write_value(client, 0xC4, + 1 | (data->colorbars << 2)); + data->status[0] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x41 | (data->colorbars << 2)); + data->status[1] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x81 | (data->colorbars << 2)); + data->status[2] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x0C1 | (data->colorbars << 2)); + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + + +void bt869_status(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->status[0]; + results[1] = data->status[1]; + results[2] = data->status[2]; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk + ("bt869.o: Warning: write was requested on read-only proc file: status\n"); + } +} + + +void bt869_ntsc(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->ntsc; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->ntsc = (results[0] > 0); + } + bt869_update_client(client); + } +} + + +void bt869_res(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->res[0]; + results[1] = data->res[1]; + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->res[0] = results[0]; + } + if (*nrels_mag >= 2) { + data->res[1] = results[1]; + } + bt869_update_client(client); + } +} + + +void bt869_half(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->half; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->half = (results[0] > 0); + bt869_update_client(client); + } + } +} + +void bt869_colorbars(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->colorbars; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->colorbars = (results[0] > 0); + bt869_update_client(client); + } + } +} + +void bt869_depth(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->depth; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->depth = results[0]; + bt869_update_client(client); + } + } +} + +int __init sensors_bt869_init(void) +{ + int res; + + printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE); + bt869_initialized = 0; + if ((res = i2c_add_driver(&bt869_driver))) { + printk + ("bt869.o: Driver registration failed, module not inserted.\n"); + bt869_cleanup(); + return res; + } + bt869_initialized++; + return 0; +} + +int __init bt869_cleanup(void) +{ + int res; + + if (bt869_initialized >= 1) { + if ((res = i2c_del_driver(&bt869_driver))) { + printk + ("bt869.o: Driver deregistration failed, module not removed.\n"); + return res; + } + bt869_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("bt869 driver"); + +int init_module(void) +{ + return sensors_bt869_init(); +} + +int cleanup_module(void) +{ + return bt869_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/ddcmon.c b/drivers/sensors/ddcmon.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/ddcmon.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,443 @@ +/* + ddcmon.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999, 2000 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x50, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(ddcmon); + +/* Many constants specified below */ + +/* DDCMON registers */ +#define DDCMON_REG_ID 0x08 +#define DDCMON_REG_SERIAL 0x0C +#define DDCMON_REG_HORSIZE 0x15 +#define DDCMON_REG_VERSIZE 0x16 +#define DDCMON_REG_TIMINGS 0x23 +#define DDCMON_REG_TIMBASE 0x36 +#define DDCMON_REG_TIMINCR 18 +#define DDCMON_REG_TIMNUM 4 +#define DDCMON_REG_TIMOFFSET 5 +#define DDCMON_REG_CHECKSUM 0x7f + +/* Size of DDCMON in bytes */ +#define DDCMON_SIZE 128 + +/* Each client has this additional data */ +struct ddcmon_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 data[DDCMON_SIZE]; /* Register values */ + int memtype; +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_ddcmon_init(void); +static int __init ddcmon_cleanup(void); + +static int ddcmon_attach_adapter(struct i2c_adapter *adapter); +static int ddcmon_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int ddcmon_detach_client(struct i2c_client *client); +static int ddcmon_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +static void ddcmon_inc_use(struct i2c_client *client); +static void ddcmon_dec_use(struct i2c_client *client); + +static void ddcmon_idcall(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_size(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_sync(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_timings(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_serial(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver ddcmon_driver = { + /* name */ "DDCMON READER", + /* id */ I2C_DRIVERID_DDCMON, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &ddcmon_attach_adapter, + /* detach_client */ &ddcmon_detach_client, + /* command */ &ddcmon_command, + /* inc_use */ &ddcmon_inc_use, + /* dec_use */ &ddcmon_dec_use +}; + +/* These files are created for each detected DDCMON. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table ddcmon_dir_table_template[] = { + {DDCMON_SYSCTL_ID, "ID", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &ddcmon_idcall}, + {DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &ddcmon_size}, + {DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_sync}, + {DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_timings}, + {DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_serial}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata ddcmon_initialized = 0; + +static int ddcmon_id = 0; + +int ddcmon_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ddcmon_detect); +} + +/* This function is called by i2c_detect */ +int ddcmon_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cs; + struct i2c_client *new_client; + struct ddcmon_data *data; + int err = 0; + const char *type_name, *client_name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access ddcmon_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct ddcmon_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct ddcmon_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &ddcmon_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + /* Verify the first 8 locations 0x00FFFFFFFFFFFF00 */ + /* Allow force and force_ddcmon arguments */ + if(kind < 0) + { + for(i = 0; i < 8; i++) { + cs = i2c_smbus_read_byte_data(new_client, i); + if(i == 0 || i == 7) { + if(cs != 0) + goto ERROR1; + } else if(cs != 0xff) + goto ERROR1; + } + } + + type_name = "ddcmon"; + client_name = "DDC Monitor"; + + /* Fill in the remaining client fields and put it in the global list */ + strcpy(new_client->name, client_name); + + new_client->id = ddcmon_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + ddcmon_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int ddcmon_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct ddcmon_data *) (client->data))-> + sysctl_id); + if ((err = i2c_detach_client(client))) { + printk + ("ddcmon.o: Client deregistration failed, client not detached.\n"); + return err; + } + kfree(client); + return 0; +} + +/* No commands defined yet */ +int ddcmon_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void ddcmon_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void ddcmon_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +void ddcmon_update_client(struct i2c_client *client) +{ + struct ddcmon_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 300 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + if (i2c_smbus_write_byte(client, 0)) { +#ifdef DEBUG + printk("ddcmon read start has failed!\n"); +#endif + } + for (i = 0; i < DDCMON_SIZE; i++) { + data->data[i] = (u8) i2c_smbus_read_byte(client); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void ddcmon_idcall(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_ID + 1] | + (data->data[DDCMON_REG_ID] << 8) | + (data->data[DDCMON_REG_ID + 3] << 16) | + (data->data[DDCMON_REG_ID + 2] << 24); + *nrels_mag = 1; + } +} + +void ddcmon_size(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_VERSIZE]; + results[1] = data->data[DDCMON_REG_HORSIZE]; + *nrels_mag = 2; + } +} + +void ddcmon_sync(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + int i, j; + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + *nrels_mag = 4; + /* look for sync entry */ + for(i = DDCMON_REG_TIMBASE; + i < DDCMON_REG_TIMBASE + + (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR); + i += DDCMON_REG_TIMINCR) { + if(data->data[i] == 0) { + for(j = 0; j < 4; j++) + results[j] = data->data[i + j + + DDCMON_REG_TIMOFFSET]; + return; + } + } + for(j = 0; j < 4; j++) + results[j] = 0; + } +} + +void ddcmon_timings(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_TIMINGS] | + (data->data[DDCMON_REG_TIMINGS + 1] << 8) | + (data->data[DDCMON_REG_TIMINGS + 2] << 16); + *nrels_mag = 1; + } +} + +void ddcmon_serial(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_SERIAL] | + (data->data[DDCMON_REG_SERIAL + 1] << 8) | + (data->data[DDCMON_REG_SERIAL + 2] << 16) | + (data->data[DDCMON_REG_SERIAL + 3] << 24); + *nrels_mag = 1; + } +} + +int __init sensors_ddcmon_init(void) +{ + int res; + + printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE); + ddcmon_initialized = 0; + if ((res = i2c_add_driver(&ddcmon_driver))) { + printk + ("ddcmon.o: Driver registration failed, module not inserted.\n"); + ddcmon_cleanup(); + return res; + } + ddcmon_initialized++; + return 0; +} + +int __init ddcmon_cleanup(void) +{ + int res; + + if (ddcmon_initialized >= 1) { + if ((res = i2c_del_driver(&ddcmon_driver))) { + printk + ("ddcmon.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } else + ddcmon_initialized--; + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("DDCMON driver"); + +int init_module(void) +{ + return sensors_ddcmon_init(); +} + +int cleanup_module(void) +{ + return ddcmon_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/ds1621.c b/drivers/sensors/ds1621.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/ds1621.c Wed Feb 13 20:03:58 2002 @@ -0,0 +1,621 @@ +/* + ds1621.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Christian W. Zuckschwerdt 2000-11-23 + based on lm75.c by Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Supports DS1621. See doc/chips/ds1621 for details */ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(ds1621); + +/* Many DS1621 constants specified below */ + +/* Config register used for detection */ +/* 7 6 5 4 3 2 1 0 */ +/* |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT| */ +#define DS1621_REG_CONFIG_MASK 0x0C +#define DS1621_REG_CONFIG_VAL 0x08 +#define DS1621_REG_CONFIG_POLARITY 0x02 +#define DS1621_REG_CONFIG_1SHOT 0x01 +#define DS1621_REG_CONFIG_DONE 0x80 + +/* Note: the done bit is always unset if continuous conversion is in progress. + We need to stop the continuous conversion or switch to single shot + before this bit becomes available! + */ + +/* The DS1621 registers */ +#define DS1621_REG_TEMP 0xAA /* word, RO */ +#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */ +#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */ +#define DS1621_REG_CONF 0xAC /* byte, RW */ +#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */ +#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */ +#define DS1621_COM_START 0xEE /* no data */ +#define DS1621_COM_STOP 0x22 /* no data */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | \ + ((val & 0x8000)?-256:0)) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0 ? (0x200+((val)/5))<<7 : \ + (((val) + 2) / 5) << 7),0,0xffff)) +#define ALARMS_FROM_REG(val) ((val) & \ + (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) +#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \ + ((val & 0x8000)?-256:0)) + +/* Initial values */ +#define DS1621_INIT_TEMP_OVER 600 +#define DS1621_INIT_TEMP_HYST 0 /* 500 would cause an alarm at room temp. */ + +/* Each client has this additional data */ +struct ds1621_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_over, temp_hyst; /* Register values, word */ + u8 conf; /* Register encoding, combined */ + + char enable; /* !=0 if we're expected to restart the conversion */ + u8 temp_int, temp_counter, temp_slope; /* Register values, byte */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_ds1621_init(void); +static int __init ds1621_cleanup(void); +static int ds1621_attach_adapter(struct i2c_adapter *adapter); +static int ds1621_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void ds1621_init_client(struct i2c_client *client); +static int ds1621_detach_client(struct i2c_client *client); +static int ds1621_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void ds1621_inc_use(struct i2c_client *client); +static void ds1621_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int ds1621_read_value(struct i2c_client *client, u8 reg); +static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value); +static void ds1621_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_enable(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_continuous(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_polarity(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver ds1621_driver = { + /* name */ "DS1621 sensor driver", + /* id */ I2C_DRIVERID_DS1621, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &ds1621_attach_adapter, + /* detach_client */ &ds1621_detach_client, + /* command */ &ds1621_command, + /* inc_use */ &ds1621_inc_use, + /* dec_use */ &ds1621_dec_use +}; + +/* These files are created for each detected DS1621. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table ds1621_dir_table_template[] = { + {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp}, + {DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms}, + {DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable}, + {DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous}, + {DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata ds1621_initialized = 0; + +static int ds1621_id = 0; + +int ds1621_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ds1621_detect); +} + +/* This function is called by i2c_detect */ +int ds1621_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, conf; + struct i2c_client *new_client; + struct ds1621_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access ds1621_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct ds1621_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct ds1621_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &ds1621_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (kind < 0) { + conf = i2c_smbus_read_byte_data(new_client, + DS1621_REG_CONF); + if ((conf & DS1621_REG_CONFIG_MASK) + != DS1621_REG_CONFIG_VAL) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = ds1621; + + if (kind == ds1621) { + type_name = "ds1621"; + client_name = "DS1621 chip"; + } else { +#ifdef DEBUG + printk("ds1621.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = ds1621_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + ds1621_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + ds1621_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int ds1621_detach_client(struct i2c_client *client) +{ + int err; + +#ifdef MODULE + if (MOD_IN_USE) + return -EBUSY; +#endif + + + i2c_deregister_entry(((struct ds1621_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("ds1621.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int ds1621_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void ds1621_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void ds1621_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int ds1621_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) + || (reg == DS1621_REG_TEMP_SLOPE)) + return i2c_smbus_read_byte_data(client, reg); + else + return swap_bytes(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) + || (reg == DS1621_REG_TEMP_SLOPE)) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); +} + +void ds1621_init_client(struct i2c_client *client) +{ + /* Initialize the DS1621 chip */ + ds1621_write_value(client, DS1621_REG_TEMP_OVER, + TEMP_TO_REG(DS1621_INIT_TEMP_OVER)); + ds1621_write_value(client, DS1621_REG_TEMP_HYST, + TEMP_TO_REG(DS1621_INIT_TEMP_HYST)); + ds1621_write_value(client, DS1621_REG_CONF, 0); + + /* perhaps we should start the continous conversion? For now */ + /* you got to do that yourself using the "enable" in proc */ +} + +void ds1621_update_client(struct i2c_client *client) +{ + struct ds1621_data *data = client->data; + u8 new_conf; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting ds1621 update\n"); +#endif + + data->conf = ds1621_read_value(client, DS1621_REG_CONF); + + data->temp = ds1621_read_value(client, + DS1621_REG_TEMP); + data->temp_over = ds1621_read_value(client, + DS1621_REG_TEMP_OVER); + data->temp_hyst = ds1621_read_value(client, + DS1621_REG_TEMP_HYST); + + /* wait for the DONE bit before reading extended values */ + + if (data->conf & DS1621_REG_CONFIG_DONE) { + data->temp_counter = ds1621_read_value(client, + DS1621_REG_TEMP_COUNTER); + data->temp_slope = ds1621_read_value(client, + DS1621_REG_TEMP_SLOPE); + data->temp_int = ITEMP_FROM_REG(data->temp); + /* restart the conversion */ + if (data->enable) + ds1621_read_value(client, DS1621_COM_START); + } + + /* reset alarms if neccessary */ + new_conf = data->conf; + if (data->temp < data->temp_over) + new_conf &= ~DS1621_ALARM_TEMP_HIGH; + if (data->temp > data->temp_hyst) + new_conf &= ~DS1621_ALARM_TEMP_LOW; + if (data->conf != new_conf) + ds1621_write_value(client, DS1621_REG_CONF, + new_conf); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void ds1621_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + *nrels_mag = 1; + } else { + *nrels_mag = 2; + } + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + /* decide wether to calculate more precise temp */ + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + } else { + results[0] = TEMP_FROM_REG(data->temp_over)*10; + results[1] = TEMP_FROM_REG(data->temp_hyst)*10; + results[2] = data->temp_int * 100 - 25 + + ((data->temp_slope - data->temp_counter) * + 100 / data->temp_slope); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + ds1621_write_value(client, DS1621_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + ds1621_write_value(client, DS1621_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = ALARMS_FROM_REG(data->conf); + *nrels_mag = 1; + } +} + +void ds1621_enable(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + /* If you really screw up your chip (like I did) this is */ + /* sometimes needed to (re)start the continous conversion */ + /* there is no data to read so this might hang your SMBus! */ + + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_DONE); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_read_value(client, DS1621_COM_START); + data->enable=1; + } else { + ds1621_read_value(client, DS1621_COM_STOP); + data->enable=0; + } + } else { + ds1621_read_value(client, DS1621_COM_START); + data->enable=1; + } + } +} + +void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_1SHOT); + } + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } + } +} + +void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY)); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_POLARITY); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_POLARITY); + } + } + } +} + +int __init sensors_ds1621_init(void) +{ + int res; + + printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE); + ds1621_initialized = 0; + if ((res = i2c_add_driver(&ds1621_driver))) { + printk + ("ds1621.o: Driver registration failed, module not inserted.\n"); + ds1621_cleanup(); + return res; + } + ds1621_initialized++; + return 0; +} + +int __init ds1621_cleanup(void) +{ + int res; + + if (ds1621_initialized >= 1) { + if ((res = i2c_del_driver(&ds1621_driver))) { + printk + ("ds1621.o: Driver deregistration failed, module not removed.\n"); + return res; + } + ds1621_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Christian W. Zuckschwerdt "); +MODULE_DESCRIPTION("DS1621 driver"); + +int init_module(void) +{ + return sensors_ds1621_init(); +} + +int cleanup_module(void) +{ + return ds1621_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/eeprom.c b/drivers/sensors/eeprom.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/eeprom.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,452 @@ +/* + eeprom.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x50, 0x57, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(eeprom); + +static int checksum = 0; +MODULE_PARM(checksum, "i"); +MODULE_PARM_DESC(checksum, + "Only accept eeproms whose checksum is correct"); + + +/* Many constants specified below */ + +/* EEPROM registers */ +#define EEPROM_REG_CHECKSUM 0x3f + +/* EEPROM memory types: */ +#define ONE_K 1 +#define TWO_K 2 +#define FOUR_K 3 +#define EIGHT_K 4 +#define SIXTEEN_K 5 + +/* Conversions */ +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 128 + +/* Each client has this additional data */ +struct eeprom_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 data[EEPROM_SIZE]; /* Register values */ + int memtype; +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_eeprom_init(void); +static int __init eeprom_cleanup(void); + +static int eeprom_attach_adapter(struct i2c_adapter *adapter); +static int eeprom_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int eeprom_detach_client(struct i2c_client *client); +static int eeprom_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +static void eeprom_inc_use(struct i2c_client *client); +static void eeprom_dec_use(struct i2c_client *client); + +#if 0 +static int eeprom_write_value(struct i2c_client *client, u8 reg, + u16 value); +#endif + +static void eeprom_contents(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void eeprom_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver eeprom_driver = { + /* name */ "EEPROM READER", + /* id */ I2C_DRIVERID_EEPROM, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &eeprom_attach_adapter, + /* detach_client */ &eeprom_detach_client, + /* command */ &eeprom_command, + /* inc_use */ &eeprom_inc_use, + /* dec_use */ &eeprom_dec_use +}; + +/* These files are created for each detected EEPROM. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table eeprom_dir_table_template[] = { + {EEPROM_SYSCTL1, "data0-15", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL2, "data16-31", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL3, "data32-47", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL4, "data48-63", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL5, "data64-79", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL6, "data80-95", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL7, "data96-111", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL8, "data112-127", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata eeprom_initialized = 0; + +static int eeprom_id = 0; + +int eeprom_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, eeprom_detect); +} + +/* This function is called by i2c_detect */ +int eeprom_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cs; + struct i2c_client *new_client; + struct eeprom_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("eeprom.o: eeprom_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access eeprom_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct eeprom_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct eeprom_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &eeprom_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is not there, unless you force + the checksum to work out. */ + if (checksum) { + cs = 0; + for (i = 0; i <= 0x3e; i++) + cs += i2c_smbus_read_byte_data(new_client, i); + cs &= 0xff; + if (i2c_smbus_read_byte_data + (new_client, EEPROM_REG_CHECKSUM) != cs) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = eeprom; + + if (kind == eeprom) { + type_name = "eeprom"; + client_name = "EEPROM chip"; + } else { +#ifdef DEBUG + printk("eeprom.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = eeprom_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + eeprom_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int eeprom_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct eeprom_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("eeprom.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int eeprom_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void eeprom_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void eeprom_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +#if 0 +/* No writes yet (PAE) */ +int eeprom_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == EEPROM_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, value); + */return 0; +} +#endif + +void eeprom_update_client(struct i2c_client *client) +{ + struct eeprom_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 300 * HZ) | + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting eeprom update\n"); +#endif + + if (i2c_smbus_write_byte(client, 0)) { +#ifdef DEBUG + printk("eeprom read start has failed!\n"); +#endif + } + for (i = 0; i < EEPROM_SIZE; i++) { + data->data[i] = (u8) i2c_smbus_read_byte(client); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void eeprom_contents(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + int i; + int base = 0; + struct eeprom_data *data = client->data; + + if (ctl_name == EEPROM_SYSCTL2) { + base = 16; + } + if (ctl_name == EEPROM_SYSCTL3) { + base = 32; + } + if (ctl_name == EEPROM_SYSCTL4) { + base = 48; + } + if (ctl_name == EEPROM_SYSCTL5) { + base = 64; + } + if (ctl_name == EEPROM_SYSCTL6) { + base = 80; + } + if (ctl_name == EEPROM_SYSCTL7) { + base = 96; + } + if (ctl_name == EEPROM_SYSCTL8) { + base = 112; + } + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + eeprom_update_client(client); + for (i = 0; i < 16; i++) { + results[i] = data->data[i + base]; + } +#ifdef DEBUG + printk("eeprom.o: 0x%X EEPROM Contents (base %d): ", + client->addr, base); + for (i = 0; i < 16; i++) { + printk(" 0x%X", data->data[i + base]); + } + printk(" .\n"); +#endif + *nrels_mag = 16; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + +/* No writes to the EEPROM (yet, anyway) (PAE) */ + printk("eeprom.o: No writes to EEPROMs supported!\n"); + } +} + +int __init sensors_eeprom_init(void) +{ + int res; + + printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE); + eeprom_initialized = 0; + if ((res = i2c_add_driver(&eeprom_driver))) { + printk + ("eeprom.o: Driver registration failed, module not inserted.\n"); + eeprom_cleanup(); + return res; + } + eeprom_initialized++; + return 0; +} + +int __init eeprom_cleanup(void) +{ + int res; + + if (eeprom_initialized >= 1) { + if ((res = i2c_del_driver(&eeprom_driver))) { + printk + ("eeprom.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } else + eeprom_initialized--; + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("EEPROM driver"); + +int init_module(void) +{ + return sensors_eeprom_init(); +} + +int cleanup_module(void) +{ + return eeprom_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/fscpos.c b/drivers/sensors/fscpos.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/fscpos.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,777 @@ +/* + fscpos.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Hermann Jung + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + fujitsu siemens poseidon chip, + module based on lm80.c + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +/* temp. because wasn't in kernel 2.4.13 patch */ +#ifndef I2C_DRIVERID_FSCPOS +#define I2C_DRIVERID_FSCPOS 1028 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(fscpos); + +/* The FSCPOS registers */ + +/* chip identification */ +#define FSCPOS_REG_IDENT_0 0x00 +#define FSCPOS_REG_IDENT_1 0x01 +#define FSCPOS_REG_IDENT_2 0x02 +#define FSCPOS_REG_REVISION 0x03 + +/* global control and status */ +#define FSCPOS_REG_EVENT_STATE 0x04 +#define FSCPOS_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCPOS_REG_WDOG_PRESET 0x28 +#define FSCPOS_REG_WDOG_STATE 0x23 +#define FSCPOS_REG_WDOG_CONTROL 0x21 + +/* fan 0 */ +#define FSCPOS_REG_FAN0_MIN 0x55 +#define FSCPOS_REG_FAN0_ACT 0x0e +#define FSCPOS_REG_FAN0_STATE 0x0d +#define FSCPOS_REG_FAN0_RIPPLE 0x0f + +/* fan 1 */ +#define FSCPOS_REG_FAN1_MIN 0x65 +#define FSCPOS_REG_FAN1_ACT 0x6b +#define FSCPOS_REG_FAN1_STATE 0x62 +#define FSCPOS_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +/* min speed fan2 not supported */ +#define FSCPOS_REG_FAN2_ACT 0xab +#define FSCPOS_REG_FAN2_STATE 0xa2 +#define FSCPOS_REG_FAN2_RIPPLE 0x0af + +/* voltage supervision */ +#define FSCPOS_REG_VOLT_12 0x45 +#define FSCPOS_REG_VOLT_5 0x42 +#define FSCPOS_REG_VOLT_BATT 0x48 + +/* temperatures */ +/* sensor 0 */ +#define FSCPOS_REG_TEMP0_ACT 0x64 +#define FSCPOS_REG_TEMP0_STATE 0x71 + +/* sensor 1 */ +#define FSCPOS_REG_TEMP1_ACT 0x32 +#define FSCPOS_REG_TEMP1_STATE 0x81 + +/* sensor 2 */ +#define FSCPOS_REG_TEMP2_ACT 0x35 +#define FSCPOS_REG_TEMP2_STATE 0x91 + + + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) +#define IN_FROM_REG(val,nr) (val) + +/* Initial limits */ + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered FSCPOS, we need to keep some data in memory. That + data is pointed to by fscpos_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new fscpos client is + allocated. */ +struct fscpos_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery current */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 fan_min[3]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_fscpos_init(void); +static int __init fscpos_cleanup(void); + +static int fscpos_attach_adapter(struct i2c_adapter *adapter); +static int fscpos_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int fscpos_detach_client(struct i2c_client *client); +static int fscpos_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void fscpos_inc_use(struct i2c_client *client); +static void fscpos_dec_use(struct i2c_client *client); + +static int fscpos_read_value(struct i2c_client *client, u8 register); +static int fscpos_write_value(struct i2c_client *client, u8 register, + u8 value); +static void fscpos_update_client(struct i2c_client *client); +static void fscpos_init_client(struct i2c_client *client); + + +static void fscpos_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void fscpos_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_fan_internal(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results, + int nr, int reg_state, int reg_min, int res_ripple); +static void fscpos_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_volt(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_wdog(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int fscpos_id = 0; + +static struct i2c_driver fscpos_driver = { + /* name */ "FSCPOS sensor driver", + /* id */ I2C_DRIVERID_FSCPOS, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &fscpos_attach_adapter, + /* detach_client */ &fscpos_detach_client, + /* command */ &fscpos_command, + /* inc_use */ &fscpos_inc_use, + /* dec_use */ &fscpos_dec_use +}; + +/* Used by fscpos_init/cleanup */ +static int __initdata fscpos_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected FSCPOS. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table fscpos_dir_table_template[] = { + {FSCPOS_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_wdog}, + {0} +}; + +int fscpos_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, fscpos_detect); +} + +int fscpos_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct fscpos_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("fscpos.o: fscpos_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access fscpos_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct fscpos_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct fscpos_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &fscpos_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscpos parameter */ + if (kind < 0) { + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) != 0x50) + goto ERROR1; + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) != 0x45) + goto ERROR1; + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) != 0x47) + goto ERROR1; + } + + kind = fscpos; + + type_name = "fscpos"; + client_name = "fsc poseidon chip"; + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = fscpos_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + fscpos_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + fscpos_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int fscpos_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct fscpos_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("fscpos.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int fscpos_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void fscpos_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void fscpos_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +int fscpos_read_value(struct i2c_client *client, u8 reg) +{ +#ifdef DEBUG + printk("fscpos: read reg 0x%02x\n",reg); +#endif + return i2c_smbus_read_byte_data(client, reg); +} + +int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) +{ +#ifdef DEBUG + printk("fscpos: write reg 0x%02x, val 0x%02x\n",reg, value); +#endif + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSCPOS. It should set limits, etc. */ +void fscpos_init_client(struct i2c_client *client) +{ + struct fscpos_data *data = client->data; + + /* read revision from chip */ + data->revision = fscpos_read_value(client,FSCPOS_REG_REVISION); + /* setup missing fan2_min value */ + data->fan_min[2] = 0xff; +} + +void fscpos_update_client(struct i2c_client *client) +{ + struct fscpos_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting fscpos update\n"); +#endif + data->temp_act[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_ACT); + data->temp_act[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_ACT); + data->temp_act[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_ACT); + data->temp_status[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_STATE); + data->temp_status[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_STATE); + data->temp_status[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_STATE); + + data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); + data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); + data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); + + data->fan_act[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_ACT); + data->fan_act[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_ACT); + data->fan_act[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_ACT); + data->fan_status[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_STATE); + data->fan_status[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_STATE); + data->fan_status[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_STATE); + data->fan_min[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_MIN); + data->fan_min[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_MIN); + /* fan2_min is not supported */ + data->fan_ripple[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_RIPPLE); + + data->watchdog[0] = fscpos_read_value(client, FSCPOS_REG_WDOG_PRESET); + data->watchdog[1] = fscpos_read_value(client, FSCPOS_REG_WDOG_STATE); + data->watchdog[2] = fscpos_read_value(client, FSCPOS_REG_WDOG_CONTROL); + + data->global_event = fscpos_read_value(client, FSCPOS_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void fscpos_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_REV: + results[0] = data->revision ; + break; + case FSCPOS_SYSCTL_EVENT: + results[0] = data->global_event & 0x1f; + break; + case FSCPOS_SYSCTL_CONTROL: + results[0] = data->global_control & 0x01; + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if((ctl_name == FSCPOS_SYSCTL_CONTROL) && (*nrels_mag >= 1)) { + data->global_control = (results[0] & 0x01); + printk("fscpos: writing 0x%02x to global_control\n", + data->global_control); + fscpos_write_value(client,FSCPOS_REG_CONTROL, + data->global_control); + } + else + printk("fscpos: writing to chip not supported\n"); + } +} + +#define TEMP_FROM_REG(val) (val-128) + + +void fscpos_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_TEMP0: + results[0] = data->temp_status[0] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[0]); + break; + case FSCPOS_SYSCTL_TEMP1: + results[0] = data->temp_status[1] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[1]); + break; + case FSCPOS_SYSCTL_TEMP2: + results[0] = data->temp_status[2] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[2]); + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + switch(ctl_name) { + case FSCPOS_SYSCTL_TEMP0: + data->temp_status[0] = + (data->temp_status[0] & ~0x02) + | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x " + "to temp0_status\n", + data->temp_status[0]); + fscpos_write_value(client, + FSCPOS_REG_TEMP0_STATE, + data->temp_status[0] & 0x02); + break; + case FSCPOS_SYSCTL_TEMP1: + data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x to temp1_status\n", data->temp_status[1]); + fscpos_write_value(client,FSCPOS_REG_TEMP1_STATE, + data->temp_status[1] & 0x02); + break; + case FSCPOS_SYSCTL_TEMP2: + data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x to temp2_status\n", data->temp_status[2]); + fscpos_write_value(client,FSCPOS_REG_TEMP2_STATE, + data->temp_status[2] & 0x02); + break; + default: + printk("fscpos: ctl_name %d not supported\n",ctl_name); + } + } + else + printk("fscpos: writing to chip not supported\n"); + } +} + +#define VOLT_FROM_REG(val,mult) (val*mult/255) + +void fscpos_volt(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_VOLT0: + results[0] = VOLT_FROM_REG(data->volt[0],1420); + break; + case FSCPOS_SYSCTL_VOLT1: + results[0] = VOLT_FROM_REG(data->volt[1],660); + break; + case FSCPOS_SYSCTL_VOLT2: + results[0] = VOLT_FROM_REG(data->volt[2],330); + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("fscpos: writing to chip not supported\n"); + } +} + +void fscpos_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + switch(ctl_name) { + case FSCPOS_SYSCTL_FAN0: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 0,FSCPOS_REG_FAN0_STATE,FSCPOS_REG_FAN0_MIN, + FSCPOS_REG_FAN0_RIPPLE); + break; + case FSCPOS_SYSCTL_FAN1: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 1,FSCPOS_REG_FAN1_STATE,FSCPOS_REG_FAN1_MIN, + FSCPOS_REG_FAN1_RIPPLE); + break; + case FSCPOS_SYSCTL_FAN2: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 2,FSCPOS_REG_FAN2_STATE,0xff, + FSCPOS_REG_FAN2_RIPPLE); + break; + default: + printk("fscpos: illegal fan nr %d\n",ctl_name); + } +} + +#define RPM_FROM_REG(val) (val*60) + +void fscpos_fan_internal(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results, int nr, + int reg_state, int reg_min, int reg_ripple ) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + results[0] = data->fan_status[nr] & 0x04; + results[1] = data->fan_min[nr]; + results[2] = data->fan_ripple[nr] & 0x03; + results[3] = RPM_FROM_REG(data->fan_act[nr]); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + data->fan_status[nr] = results[0] & 0x04; + printk("fscpos: writing value 0x%02x to fan%d_status\n", + data->fan_status[nr],nr); + fscpos_write_value(client,reg_state, + data->fan_status[nr]); + } + if((*nrels_mag >= 2) && (nr < 2)) { + /* minimal speed for fan2 not supported */ + data->fan_min[nr] = results[1]; + printk("fscpos: writing value 0x%02x to fan%d_min\n", + data->fan_min[nr],nr); + fscpos_write_value(client,reg_min, + data->fan_min[nr]); + } + if(*nrels_mag >= 3) { + if((results[2] & 0x03) == 0) { + printk("fscpos: fan%d ripple 0 not allowed\n",nr); + return; + } + data->fan_ripple[nr] = results[2] & 0x03; + printk("fscpos: writing value 0x%02x to fan%d_ripple\n", + data->fan_ripple[nr],nr); + fscpos_write_value(client,reg_ripple, + data->fan_ripple[nr]); + } + } +} + +void fscpos_wdog(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + results[0] = data->watchdog[0] ; + results[1] = data->watchdog[1] & 0x02; + results[2] = data->watchdog[2] & 0xb0; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->watchdog[0] = results[0] & 0xff; + printk("fscpos: writing value 0x%02x to wdog_preset\n", + data->watchdog[0]); + fscpos_write_value(client,FSCPOS_REG_WDOG_PRESET, + data->watchdog[0]); + } + if (*nrels_mag >= 2) { + data->watchdog[1] = results[1] & 0x02; + printk("fscpos: writing value 0x%02x to wdog_state\n", + data->watchdog[1]); + fscpos_write_value(client,FSCPOS_REG_WDOG_STATE, + data->watchdog[1]); + } + if (*nrels_mag >= 3) { + data->watchdog[2] = results[2] & 0xb0; + printk("fscpos: writing value 0x%02x to wdog_control\n", + data->watchdog[2]); + fscpos_write_value(client,FSCPOS_REG_WDOG_CONTROL, + data->watchdog[2]); + } + } +} + +int __init sensors_fscpos_init(void) +{ + int res; + + printk("fscpos.o version %s (%s)\n", LM_VERSION, LM_DATE); + fscpos_initialized = 0; + + if ((res = i2c_add_driver(&fscpos_driver))) { + printk + ("fscpos.o: Driver registration failed, module not inserted.\n"); + fscpos_cleanup(); + return res; + } + fscpos_initialized++; + return 0; +} + +int __init fscpos_cleanup(void) +{ + int res; + + if (fscpos_initialized >= 1) { + if ((res = i2c_del_driver(&fscpos_driver))) { + printk + ("fscpos.o: Driver deregistration failed, module not removed.\n"); + return res; + } + fscpos_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Hermann Jung based on work from Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +int init_module(void) +{ + return sensors_fscpos_init(); +} + +int cleanup_module(void) +{ + return fscpos_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/gl518sm.c b/drivers/sensors/gl518sm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/gl518sm.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,1125 @@ +/* + gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#ifdef __SMP__ +#include +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); + +/* Defining this will enable debug messages for the voltage iteration + code used with rev 0 ICs */ +#undef DEBUG_VIN + +/* Many GL518 constants specified below */ + +/* The GL518 registers */ +#define GL518_REG_CHIP_ID 0x00 +#define GL518_REG_REVISION 0x01 +#define GL518_REG_VENDOR_ID 0x02 +#define GL518_REG_CONF 0x03 +#define GL518_REG_TEMP 0x04 +#define GL518_REG_TEMP_OVER 0x05 +#define GL518_REG_TEMP_HYST 0x06 +#define GL518_REG_FAN_COUNT 0x07 +#define GL518_REG_FAN_LIMIT 0x08 +#define GL518_REG_VIN1_LIMIT 0x09 +#define GL518_REG_VIN2_LIMIT 0x0a +#define GL518_REG_VIN3_LIMIT 0x0b +#define GL518_REG_VDD_LIMIT 0x0c +#define GL518_REG_VIN3 0x0d +#define GL518_REG_MISC 0x0f +#define GL518_REG_ALARM 0x10 +#define GL518_REG_MASK 0x11 +#define GL518_REG_INT 0x12 +#define GL518_REG_VIN2 0x13 +#define GL518_REG_VIN1 0x14 +#define GL518_REG_VDD 0x15 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+119),\ + 0,255)) +#define TEMP_FROM_REG(val) (((val) - 119) * 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) \ + ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) +#define IN_FROM_REG(val) (((val)*19)/10) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) +#define VDD_FROM_REG(val) (((val)*23)/10) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +#define ALARMS_FROM_REG(val) val + +#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) +#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) + +#define BEEPS_TO_REG(val) ((val) & 0x7f) +#define BEEPS_FROM_REG(val) ((val) & 0x7f) + +/* Initial values */ +#define GL518_INIT_TEMP_OVER 600 +#define GL518_INIT_TEMP_HYST 500 +#define GL518_INIT_FAN_MIN_1 3000 +#define GL518_INIT_FAN_MIN_2 3000 + +/* These are somewhat sane */ +#define GL518_INIT_VIN_1 330 /* 3.3 V */ +#define GL518_INIT_VIN_2 286 /* 12 V */ +#define GL518_INIT_VIN_3 260 /* Vcore */ +#define GL518_INIT_VDD 500 /* 5 V */ + +#define GL518_INIT_PERCENTAGE 10 + +#define GL518_INIT_VIN_MIN_1 \ + (GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_1 \ + (GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MIN_2 \ + (GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_2 \ + (GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MIN_3 \ + (GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_3 \ + (GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VDD_MIN \ + (GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VDD_MAX \ + (GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100) + + +/* Each client has this additional data */ +struct gl518_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + + int iterate_lock; + int quit_thread; + struct task_struct *thread; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + wait_queue_head_t wq; +#else + struct wait_queue *wq; +#endif + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_updated_v00; + /* In jiffies (used only by rev00 chips) */ + + u8 voltage[4]; /* Register values; [0] = VDD */ + u8 voltage_min[4]; /* Register values; [0] = VDD */ + u8 voltage_max[4]; /* Register values; [0] = VDD */ + u8 iter_voltage[4]; /* Register values; [0] = VDD */ + u8 fan[2]; + u8 fan_min[2]; + u8 temp; /* Register values */ + u8 temp_over; /* Register values */ + u8 temp_hyst; /* Register values */ + u8 alarms, beeps; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 beep_enable; /* Boolean */ + u8 iterate; /* Voltage iteration mode */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_gl518sm_init(void); +static int __init gl518_cleanup(void); +static int gl518_attach_adapter(struct i2c_adapter *adapter); +static int gl518_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void gl518_init_client(struct i2c_client *client); +static int gl518_detach_client(struct i2c_client *client); +static int gl518_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void gl518_inc_use(struct i2c_client *client); +static void gl518_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int gl518_read_value(struct i2c_client *client, u8 reg); +static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value); +static void gl518_update_client(struct i2c_client *client); + +static void gl518_update_client_rev00(struct i2c_client *client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,68) +static int gl518_update_thread(void *data); +#endif +static void gl518_update_iterate(struct i2c_client *client); + +static void gl518_vin(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan1off(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_iterate(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl518_driver = { + /* name */ "GL518SM sensor chip driver", + /* id */ I2C_DRIVERID_GL518, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &gl518_attach_adapter, + /* detach_client */ &gl518_detach_client, + /* command */ &gl518_command, + /* inc_use */ &gl518_inc_use, + /* dec_use */ &gl518_dec_use +}; + +/* These files are created for each detected GL518. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table gl518_dir_table_template[] = { + {GL518_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan}, + {GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan}, + {GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_temp}, + {GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan_div}, + {GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_alarms}, + {GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_beep}, + {GL518_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan1off}, + {GL518_SYSCTL_ITERATE, "iterate", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_iterate}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata gl518_initialized = 0; + +/* I choose here for semi-static GL518SM allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +#define MAX_GL518_NR 4 +static struct i2c_client *gl518_list[MAX_GL518_NR]; + +int gl518_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, gl518_detect); +} + +static int gl518_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl518_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("gl518sm.o: gl518_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl518_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct gl518_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct gl518_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &gl518_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + (gl518_read_value(new_client, GL518_REG_CHIP_ID) != + 0x80) + || (gl518_read_value(new_client, GL518_REG_CONF) & + 0x80)) goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = gl518_read_value(new_client, GL518_REG_REVISION); + if (i == 0x00) + kind = gl518sm_r00; + else if (i == 0x80) + kind = gl518sm_r80; + else { + if (kind == 0) + printk + ("gl518sm.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == gl518sm_r00) { + type_name = "gl518sm"; + client_name = "GL518SM Revision 0x00 chip"; + } else if (kind == gl518sm_r80) { + type_name = "gl518sm"; + client_name = "GL518SM Revision 0x80 chip"; + } else { +#ifdef DEBUG + printk("gl518sm.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + for (i = 0; i < MAX_GL518_NR; i++) + if (!gl518_list[i]) + break; + if (i == MAX_GL518_NR) { + printk + ("gl518sm.o: No empty slots left, recompile and heighten " + "MAX_GL518_NR!\n"); + err = -ENOMEM; + goto ERROR2; + } + gl518_list[i] = new_client; + new_client->id = i; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + gl518_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the GL518SM chip */ + data->iterate = 0; + data->iterate_lock = 0; + data->quit_thread = 0; + data->thread = NULL; + data->alarm_mask = 0xff; + data->voltage[0]=data->voltage[1]=data->voltage[2]=0; + gl518_init_client((struct i2c_client *) new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + for (i = 0; i < MAX_GL518_NR; i++) + if (new_client == gl518_list[i]) + gl518_list[i] = NULL; + ERROR2: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + + +/* Called when we have found a new GL518SM. It should set limits, etc. */ +void gl518_init_client(struct i2c_client *client) +{ + /* Power-on defaults (bit 7=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x80); + + /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0), + standby mode (bit6=0) */ + gl518_write_value(client, GL518_REG_CONF, 0x04); + + /* Never interrupts */ + gl518_write_value(client, GL518_REG_MASK, 0x00); + + gl518_write_value(client, GL518_REG_TEMP_HYST, + TEMP_TO_REG(GL518_INIT_TEMP_HYST)); + gl518_write_value(client, GL518_REG_TEMP_OVER, + TEMP_TO_REG(GL518_INIT_TEMP_OVER)); + gl518_write_value(client, GL518_REG_MISC, (DIV_TO_REG(2) << 6) | + (DIV_TO_REG(2) << 4)); + gl518_write_value(client, GL518_REG_FAN_LIMIT, + (FAN_TO_REG(GL518_INIT_FAN_MIN_1, 2) << 8) | + FAN_TO_REG(GL518_INIT_FAN_MIN_2, 2)); + gl518_write_value(client, GL518_REG_VIN1_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_1) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_1)); + gl518_write_value(client, GL518_REG_VIN2_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_2) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_2)); + gl518_write_value(client, GL518_REG_VIN3_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_3) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_3)); + gl518_write_value(client, GL518_REG_VDD_LIMIT, + (VDD_TO_REG(GL518_INIT_VDD_MAX) << 8) | + VDD_TO_REG(GL518_INIT_VDD_MIN)); + + /* Clear status register (bit 5=1), start (bit6=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x24); + gl518_write_value(client, GL518_REG_CONF, 0x44); +} + +int gl518_detach_client(struct i2c_client *client) +{ + int err, i; + struct gl518_data *data = client->data; + + i2c_deregister_entry(((struct gl518_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("gl518sm.o: Client deregistration failed, client not detached.\n"); + return err; + } + + for (i = 0; i < MAX_GL518_NR; i++) + if (client == gl518_list[i]) + break; + if ((i == MAX_GL518_NR)) { + printk("gl518sm.o: Client to detach not found.\n"); + return -ENOENT; + } + gl518_list[i] = NULL; + + if (data->thread) { + data->quit_thread = 1; + wake_up_interruptible(&data->wq); + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void gl518_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void gl518_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl518_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return swap_bytes(i2c_smbus_read_word_data(client, reg)); + else + return i2c_smbus_read_byte_data(client, reg); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + +void gl518_update_client(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting gl518 update\n"); +#endif + + data->alarms = gl518_read_value(client, GL518_REG_INT); + data->beeps = gl518_read_value(client, GL518_REG_ALARM); + + val = gl518_read_value(client, GL518_REG_VDD_LIMIT); + data->voltage_min[0] = val & 0xff; + data->voltage_max[0] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN1_LIMIT); + data->voltage_min[1] = val & 0xff; + data->voltage_max[1] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN2_LIMIT); + data->voltage_min[2] = val & 0xff; + data->voltage_max[2] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN3_LIMIT); + data->voltage_min[3] = val & 0xff; + data->voltage_max[3] = (val >> 8) & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_COUNT); + data->fan[0] = (val >> 8) & 0xff; + data->fan[1] = val & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_LIMIT); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp = gl518_read_value(client, GL518_REG_TEMP); + data->temp_over = + gl518_read_value(client, GL518_REG_TEMP_OVER); + data->temp_hyst = + gl518_read_value(client, GL518_REG_TEMP_HYST); + + val = gl518_read_value(client, GL518_REG_MISC); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + + data->alarms &= data->alarm_mask; + + val = gl518_read_value(client, GL518_REG_CONF); + data->beep_enable = (val >> 2) & 1; + +#ifndef DEBUG_VIN + if (data->type != gl518sm_r00) { + data->voltage[0] = + gl518_read_value(client, GL518_REG_VDD); + data->voltage[1] = + gl518_read_value(client, GL518_REG_VIN1); + data->voltage[2] = + gl518_read_value(client, GL518_REG_VIN2); + data->voltage[3] = + gl518_read_value(client, GL518_REG_VIN3); + } else + gl518_update_client_rev00(client); +#else + gl518_update_client_rev00(client); +#endif + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +/* Here we decide how to run the iteration code. + When called, we trigger the iteration and report the last + measured voltage. No delay for user apps */ +void gl518_update_client_rev00(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int i; + + if (data->iterate == 1) { /* 10 sec delay */ + /* as that update is slow, we consider the data valid for 30 seconds */ + if ( + ((jiffies - data->last_updated_v00 > 30 * HZ) + || (data->alarms & 7) + || (!data->valid)) && (!data->iterate_lock)) { + data->iterate_lock = 1; + gl518_update_iterate(client); + data->iterate_lock = 0; + } + for (i = 0; i < 4; i++) + data->voltage[i] = data->iter_voltage[i]; + } else if (data->iterate == 2) { /* show results of last iteration */ + for (i = 0; i < 4; i++) + data->voltage[i] = data->iter_voltage[i]; + wake_up_interruptible(&data->wq); + } else { /* no iteration */ + data->voltage[3] = + gl518_read_value(client, GL518_REG_VIN3); + } +} + +int gl518_update_thread(void *c) +{ + struct i2c_client *client = c; + struct gl518_data *data = client->data; + +#ifdef __SMP__ + lock_kernel(); +#endif + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm, "gl518sm"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + init_waitqueue_head(&(data->wq)); +#else + data->wq = NULL; +#endif + data->thread = current; + +#ifdef __SMP__ + unlock_kernel(); +#endif + + for (;;) { + if (!data->iterate_lock) { + data->iterate_lock = 1; + gl518_update_iterate(client); + data->iterate_lock = 0; + } + + if ((data->quit_thread) || signal_pending(current)) + break; + interruptible_sleep_on(&data->wq); + } + + data->thread = NULL; + data->quit_thread = 0; + return 0; +} + +/* This updates vdd, vin1, vin2 values by doing slow and multiple + comparisons for the GL518SM rev 00 that lacks support for direct + reading of these values. Values are kept in iter_voltage */ + +void gl518_update_iterate(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int i, j, loop_more = 1, min[3], max[3], delta[3]; + int alarm, beeps, irqs; + +#define VIN_REG(c) c==0?GL518_REG_VDD_LIMIT:\ + c==1?GL518_REG_VIN1_LIMIT:\ + GL518_REG_VIN2_LIMIT + + /* disable beeps & irqs for vin0-2 */ + beeps = gl518_read_value(client, GL518_REG_ALARM); + irqs = gl518_read_value(client, GL518_REG_MASK); + gl518_write_value(client, GL518_REG_ALARM, beeps & ~0x7); + gl518_write_value(client, GL518_REG_MASK, irqs & ~0x7); + + alarm = data->alarms; + + for (i = 0; i < 3; i++) { + if (alarm & (1 << i)) { + min[i] = 0; + max[i] = 127; + } else { + min[i] = data->voltage_min[i]; + max[i] = + (data->voltage_max[i] + + data->voltage_min[i]) / 2; + } + delta[i] = (max[i] - min[i]) / 2; + } + + for (j = 0; (j < 10 && loop_more); j++) { + + for (i = 0; i < 3; i++) + gl518_write_value(client, VIN_REG(i), + max[i] << 8 | min[i]); + + if ((data->thread) && + ((data->quit_thread) || signal_pending(current))) + goto finish; + + /* we wait now 1.5 seconds before comparing */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ + HZ / 2); + + alarm = gl518_read_value(client, GL518_REG_INT); + +#ifdef DEBUG_VIN + printk("gl518sm: iteration %2d: %4d%c %4d%c %4d%c\n", j, + max[0], (alarm & 1) ? '!' : ' ', + max[1], (alarm & 2) ? '!' : ' ', + max[2], (alarm & 4) ? '!' : ' '); +#endif + + for (loop_more = 0, i = 0; i < 3; i++) { + if (alarm & (1 << i)) + max[i] += delta[i]; + else + max[i] -= delta[i]; + + if (delta[i]) + loop_more++; + delta[i] >>= 1; + } + + } + + for (i = 0; i < 3; i++) + if (alarm & (1 << i)) + max[i]++; + +#ifdef DEBUG_VIN + printk("gl518sm: final :%5d %5d %5d\n", max[0], max[1], + max[2]); + printk("gl518sm: meter :%5d %5d %5d\n", data->voltage[0], + data->voltage[1], data->voltage[2]); +#endif + + /* update values, including vin3 */ + for (i = 0; i < 3; i++) { + data->iter_voltage[i] = max[i]; + } + data->iter_voltage[3] = gl518_read_value(client, GL518_REG_VIN3); + data->last_updated_v00 = jiffies; + + finish: + + /* reset values */ + for (i = 0; i < 3; i++) { + gl518_write_value(client, VIN_REG(i), + data->voltage_max[i] << 8 | data-> + voltage_min[i]); + } + + gl518_write_value(client, GL518_REG_ALARM, beeps); + gl518_write_value(client, GL518_REG_MASK, irqs); + +#undef VIN_REG +} + +void gl518_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + gl518_write_value(client, GL518_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + gl518_write_value(client, GL518_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void gl518_vin(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int nr = ctl_name - GL518_SYSCTL_VDD; + int regnr, old = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : + VDD_FROM_REG(data->voltage_min[nr]); + results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : + VDD_FROM_REG(data->voltage_max[nr]); + results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : + VDD_FROM_REG(data->voltage[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + regnr = + nr == 0 ? GL518_REG_VDD_LIMIT : nr == + 1 ? GL518_REG_VIN1_LIMIT : nr == + 2 ? GL518_REG_VIN2_LIMIT : GL518_REG_VIN3_LIMIT; + if (*nrels_mag == 1) + old = gl518_read_value(client, regnr) & 0xff00; + if (*nrels_mag >= 2) { + data->voltage_max[nr] = + nr ? IN_TO_REG(results[1]) : + VDD_TO_REG(results[1]); + old = data->voltage_max[nr] << 8; + } + if (*nrels_mag >= 1) { + data->voltage_min[nr] = + nr ? IN_TO_REG(results[0]) : + VDD_TO_REG(results[0]); + old |= data->voltage_min[nr]; + gl518_write_value(client, regnr, old); + } + } +} + + +void gl518_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int nr = ctl_name - GL518_SYSCTL_FAN1; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div + [nr])); + old = + gl518_read_value(client, GL518_REG_FAN_LIMIT); + + if (nr == 0) { + old = + (old & 0x00ff) | (data-> + fan_min[0] << 8); + if (results[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + } else { + old = (old & 0xff00) | data->fan_min[1]; + if (results[0] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + } + gl518_write_value(client, GL518_REG_FAN_LIMIT, + old); + } + } +} + + +void gl518_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void gl518_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + gl518_write_value(client, GL518_REG_CONF, + (gl518_read_value(client, + GL518_REG_CONF) + & 0xfb) | (data-> + beep_enable << 2)); + } + if (*nrels_mag >= 2) { + data->beeps = + BEEPS_TO_REG(results[1]) & data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, + data->beeps); + } + } +} + + +void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = gl518_read_value(client, GL518_REG_MISC); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0x3f) | (data->fan_div[0] << 6); + } + gl518_write_value(client, GL518_REG_MISC, old); + } +} + +void gl518_fan1off(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl518_read_value(client, GL518_REG_MISC) & 0x08) != + 0); + results[1] = + ((gl518_read_value(client, GL518_REG_CONF) & 0x10) != + 0); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl518_read_value(client, + GL518_REG_MISC) & 0xf7; + if (results[0]) + old |= 0x08; + gl518_write_value(client, GL518_REG_MISC, old); + } + if (*nrels_mag >= 2) { + old = + gl518_read_value(client, + GL518_REG_CONF) & 0xef; + if (results[1]) + old |= 0x10; + gl518_write_value(client, GL518_REG_CONF, old); + } + } +} + +void gl518_iterate(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int i; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = data->iterate; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if ((*nrels_mag >= 1) && (data->iterate != results[0])) { + data->iterate = results[0]; + for (i = 0; i < 4; i++) { + data->voltage[i] = 0; + data->iter_voltage[i] = 0; + } + data->valid = 0; + + if ((data->iterate != 2) && (data->thread)) { + data->quit_thread = 1; + wake_up_interruptible(&data->wq); + } else if ((data->iterate == 2) && (!data->thread)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + init_waitqueue_head(&(data->wq)); +#else + data->wq = NULL; +#endif + kernel_thread(gl518_update_thread, + (void *) client, 0); + } + } + } +} + +int __init sensors_gl518sm_init(void) +{ + int res; + + printk("gl518sm.o version %s (%s)\n", LM_VERSION, LM_DATE); + gl518_initialized = 0; + if ((res = i2c_add_driver(&gl518_driver))) { + printk + ("gl518sm.o: Driver registration failed, module not inserted.\n"); + gl518_cleanup(); + return res; + } + gl518_initialized++; + return 0; +} + +int __init gl518_cleanup(void) +{ + int res; + + if (gl518_initialized >= 1) { + if ((res = i2c_del_driver(&gl518_driver))) { + printk + ("gl518.o: Driver deregistration failed, module not removed.\n"); + return res; + } + gl518_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Kyösti Mälkki "); +MODULE_DESCRIPTION("GL518SM driver"); + +int init_module(void) +{ + return sensors_gl518sm_init(); +} + +int cleanup_module(void) +{ + return gl518_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/gl520sm.c b/drivers/sensors/gl520sm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/gl520sm.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,928 @@ +/* + gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(gl520sm); + +/* Many GL520 constants specified below +One of the inputs can be configured as either temp or voltage. +That's why _TEMP2 and _VIN4 access the same register +*/ + +/* The GL520 registers */ +#define GL520_REG_CHIP_ID 0x00 +#define GL520_REG_REVISION 0x01 +#define GL520_REG_VID 0x02 +#define GL520_REG_CONF 0x03 +#define GL520_REG_TEMP1 0x04 +#define GL520_REG_TEMP1_OVER 0x05 +#define GL520_REG_TEMP1_HYST 0x06 +#define GL520_REG_FAN_COUNT 0x07 +#define GL520_REG_FAN_LIMIT 0x08 +#define GL520_REG_VIN1_LIMIT 0x09 +#define GL520_REG_VIN2_LIMIT 0x0a +#define GL520_REG_VIN3_LIMIT 0x0b +#define GL520_REG_VDD_LIMIT 0x0c +#define GL520_REG_VIN3 0x0d +#define GL520_REG_VIN4 0x0e +#define GL520_REG_TEMP2 0x0e +#define GL520_REG_MISC 0x0f +#define GL520_REG_ALARM 0x10 +#define GL520_REG_MASK 0x11 +#define GL520_REG_INT 0x12 +#define GL520_REG_VIN2 0x13 +#define GL520_REG_VIN1 0x14 +#define GL520_REG_VDD 0x15 +#define GL520_REG_TEMP2_OVER 0x17 +#define GL520_REG_VIN4_MAX 0x17 +#define GL520_REG_TEMP2_HYST 0x18 +#define GL520_REG_VIN4_MIN 0x18 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+130),\ + 0,255)) +#define TEMP_FROM_REG(val) (((val) - 130) * 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) \ + ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) +#define IN_FROM_REG(val) (((val)*19)/10) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) +#define VDD_FROM_REG(val) (((val)*23)/10) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +#define ALARMS_FROM_REG(val) val + +#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) +#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) + +#define BEEPS_TO_REG(val) (val) +#define BEEPS_FROM_REG(val) (val) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial values */ +#define GL520_INIT_TEMP_OVER 600 +#define GL520_INIT_TEMP_HYST 500 +#define GL520_INIT_FAN_MIN_1 3000 +#define GL520_INIT_FAN_MIN_2 3000 + +/* These are somewhat sane */ +#define GL520_INIT_VIN_1 330 /* 3.3 V */ +#define GL520_INIT_VIN_2 286 /* 12 V */ +#define GL520_INIT_VIN_3 260 /* Vcore */ +#define GL520_INIT_VIN_4 160 /* -12 V */ +#define GL520_INIT_VDD 500 /* 5 V */ + +#define GL520_INIT_PERCENTAGE 10 + +#define GL520_INIT_VIN_MIN_1 \ + (GL520_INIT_VIN_1 - GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_1 \ + (GL520_INIT_VIN_1 + GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MIN_2 \ + (GL520_INIT_VIN_2 - GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_2 \ + (GL520_INIT_VIN_2 + GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MIN_3 \ + (GL520_INIT_VIN_3 - GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_3 \ + (GL520_INIT_VIN_3 + GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VDD_MIN \ + (GL520_INIT_VDD - GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VDD_MAX \ + (GL520_INIT_VDD + GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100) + + +/* Each client has this additional data */ +struct gl520_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_updated_v00; + /* In jiffies (used only by rev00 chips) */ + + u8 voltage[5]; /* Register values; [0] = VDD */ + u8 voltage_min[5]; /* Register values; [0] = VDD */ + u8 voltage_max[5]; /* Register values; [0] = VDD */ + u8 fan[2]; + u8 fan_min[2]; + u8 temp[2]; /* Register values */ + u8 temp_over[2]; /* Register values */ + u8 temp_hyst[2]; /* Register values */ + u8 alarms, beeps, vid; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 beep_enable; /* Boolean */ + u8 two_temps; /* Boolean */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_gl520_init(void); +static int __init gl520_cleanup(void); +static int gl520_attach_adapter(struct i2c_adapter *adapter); +static int gl520_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void gl520_init_client(struct i2c_client *client); +static int gl520_detach_client(struct i2c_client *client); +static int gl520_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void gl520_inc_use(struct i2c_client *client); +static void gl520_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int gl520_read_value(struct i2c_client *client, u8 reg); +static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value); +static void gl520_update_client(struct i2c_client *client); + +static void gl520_vin(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan1off(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_config(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl520_driver = { + /* name */ "GL520SM sensor chip driver", + /* id */ I2C_DRIVERID_GL520, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &gl520_attach_adapter, + /* detach_client */ &gl520_detach_client, + /* command */ &gl520_command, + /* inc_use */ &gl520_inc_use, + /* dec_use */ &gl520_dec_use +}; + +/* These files are created for each detected GL520. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table gl520_dir_table_template[] = { + {GL520_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN4, "vin4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vid}, + {GL520_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan}, + {GL520_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan}, + {GL520_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_temp}, + {GL520_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_temp}, + {GL520_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan_div}, + {GL520_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_alarms}, + {GL520_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_beep}, + {GL520_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan1off}, + {GL520_SYSCTL_CONFIG, "config", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_config}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata gl520_initialized = 0; + +/* I choose here for semi-static GL520SM allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int gl520_id = 0; + +int gl520_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, gl520_detect); +} + +static int gl520_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl520_data *data; + int err = 0; + const char *type_name = ""; + char client_name[32]; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("gl520sm.o: gl520_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl520_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct gl520_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct gl520_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &gl520_driver; + new_client->flags = 0; + + /* Determine the chip type. */ + + if (gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) { + printk + ("gl520sm.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } else { + kind = gl520sm; + } + + i = gl520_read_value(new_client, GL520_REG_REVISION); + if (kind == gl520sm) { + type_name = "gl520sm"; + sprintf(client_name, "GL520SM Revision %02x chip", i); + } else { +#ifdef DEBUG + printk("gl520sm.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = gl520_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + gl520_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the GL520SM chip */ + data->two_temps = 1; + data->alarm_mask = 0xff; + gl520_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + + +/* Called when we have found a new GL520SM. It should set limits, etc. */ +void gl520_init_client(struct i2c_client *client) +{ + /* Power-on defaults (bit 7=1) */ + gl520_write_value(client, GL520_REG_CONF, 0x80); + + /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0), + standby mode (bit6=0) */ + gl520_write_value(client, GL520_REG_CONF, 0x04); + + /* Never interrupts */ + gl520_write_value(client, GL520_REG_MASK, 0x00); + + gl520_write_value(client, GL520_REG_TEMP1_HYST, + TEMP_TO_REG(GL520_INIT_TEMP_HYST)); + gl520_write_value(client, GL520_REG_TEMP1_OVER, + TEMP_TO_REG(GL520_INIT_TEMP_OVER)); + + /* We set Temp2, but not Vin4. */ + gl520_write_value(client, GL520_REG_TEMP2_HYST, + TEMP_TO_REG(GL520_INIT_TEMP_HYST)); + gl520_write_value(client, GL520_REG_TEMP2_OVER, + TEMP_TO_REG(GL520_INIT_TEMP_OVER)); + + gl520_write_value(client, GL520_REG_MISC, (DIV_TO_REG(2) << 6) | + (DIV_TO_REG(2) << 4)); + gl520_write_value(client, GL520_REG_FAN_LIMIT, + (FAN_TO_REG(GL520_INIT_FAN_MIN_1, 2) << 8) | + FAN_TO_REG(GL520_INIT_FAN_MIN_2, 2)); + + gl520_write_value(client, GL520_REG_VIN1_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_1) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_1)); + gl520_write_value(client, GL520_REG_VIN2_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_2) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_2)); + gl520_write_value(client, GL520_REG_VIN3_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_3) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_3)); + gl520_write_value(client, GL520_REG_VDD_LIMIT, + (VDD_TO_REG(GL520_INIT_VDD_MAX) << 8) | + VDD_TO_REG(GL520_INIT_VDD_MIN)); + + /* Clear status register (bit 5=1), start (bit6=1) */ + gl520_write_value(client, GL520_REG_CONF, 0x24); + gl520_write_value(client, GL520_REG_CONF, 0x44); +} + +int gl520_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct gl520_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("gl520sm.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int gl520_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void gl520_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void gl520_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL520 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl520_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return swap_bytes(i2c_smbus_read_word_data(client, reg)); + else + return i2c_smbus_read_byte_data(client, reg); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL520 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + +void gl520_update_client(struct i2c_client *client) +{ + struct gl520_data *data = client->data; + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting gl520 update\n"); +#endif + + data->alarms = gl520_read_value(client, GL520_REG_INT); + data->beeps = gl520_read_value(client, GL520_REG_ALARM); + data->vid = gl520_read_value(client, GL520_REG_VID) & 0x1f; + + val = gl520_read_value(client, GL520_REG_VDD_LIMIT); + data->voltage_min[0] = val & 0xff; + data->voltage_max[0] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN1_LIMIT); + data->voltage_min[1] = val & 0xff; + data->voltage_max[1] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN2_LIMIT); + data->voltage_min[2] = val & 0xff; + data->voltage_max[2] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN3_LIMIT); + data->voltage_min[3] = val & 0xff; + data->voltage_max[3] = (val >> 8) & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_COUNT); + data->fan[0] = (val >> 8) & 0xff; + data->fan[1] = val & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_LIMIT); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp[0] = gl520_read_value(client, GL520_REG_TEMP1); + data->temp_over[0] = + gl520_read_value(client, GL520_REG_TEMP1_OVER); + data->temp_hyst[0] = + gl520_read_value(client, GL520_REG_TEMP1_HYST); + + val = gl520_read_value(client, GL520_REG_MISC); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + + data->alarms &= data->alarm_mask; + + val = gl520_read_value(client, GL520_REG_CONF); + data->beep_enable = (val >> 2) & 1; + + data->voltage[0] = gl520_read_value(client, GL520_REG_VDD); + data->voltage[1] = + gl520_read_value(client, GL520_REG_VIN1); + data->voltage[2] = + gl520_read_value(client, GL520_REG_VIN2); + data->voltage[3] = + gl520_read_value(client, GL520_REG_VIN3); + + /* Temp1 and Vin4 are the same input */ + data->temp[1] = gl520_read_value(client, GL520_REG_TEMP2); + data->temp_over[1] = + gl520_read_value(client, GL520_REG_TEMP2_OVER); + data->temp_hyst[1] = + gl520_read_value(client, GL520_REG_TEMP2_HYST); + + data->voltage[4] = + gl520_read_value(client, GL520_REG_VIN4); + data->voltage_min[4] = + gl520_read_value(client, GL520_REG_VIN4_MIN); + data->voltage_max[4] = + gl520_read_value(client, GL520_REG_VIN4_MAX); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +void gl520_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_TEMP1; + int regnr; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over[nr]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); + results[2] = TEMP_FROM_REG(data->temp[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if ((nr == 0) && (!data->two_temps)) + return; + regnr = + nr == 0 ? GL520_REG_TEMP1_OVER : GL520_REG_TEMP2_OVER; + if (*nrels_mag >= 1) { + data->temp_over[nr] = TEMP_TO_REG(results[nr]); + gl520_write_value(client, regnr, + data->temp_over[nr]); + } + regnr = + nr == 0 ? GL520_REG_TEMP1_HYST : GL520_REG_TEMP2_HYST; + if (*nrels_mag >= 2) { + data->temp_hyst[nr] = TEMP_TO_REG(results[nr]); + gl520_write_value(client, regnr, + data->temp_hyst[nr]); + } + } +} + +void gl520_vin(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_VDD; + int regnr, old = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : + VDD_FROM_REG(data->voltage_min[nr]); + results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : + VDD_FROM_REG(data->voltage_max[nr]); + results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : + VDD_FROM_REG(data->voltage[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (nr != 4) { + regnr = + nr == 0 ? GL520_REG_VDD_LIMIT : nr == + 1 ? GL520_REG_VIN1_LIMIT : nr == + 2 ? GL520_REG_VIN2_LIMIT : + GL520_REG_VIN3_LIMIT; + if (*nrels_mag == 1) + old = + gl520_read_value(client, + regnr) & 0xff00; + if (*nrels_mag >= 2) { + data->voltage_max[nr] = + nr ? IN_TO_REG(results[1]) : + VDD_TO_REG(results[1]); + old = data->voltage_max[nr] << 8; + } + if (*nrels_mag >= 1) { + data->voltage_min[nr] = + nr ? IN_TO_REG(results[0]) : + VDD_TO_REG(results[0]); + old |= data->voltage_min[nr]; + gl520_write_value(client, regnr, old); + } + } else if (!data->two_temps) { + if (*nrels_mag == 1) + gl520_write_value(client, + GL520_REG_VIN4_MIN, + IN_TO_REG(results[0])); + if (*nrels_mag >= 2) + gl520_write_value(client, + GL520_REG_VIN4_MAX, + IN_TO_REG(results[1])); + } + } +} + + +void gl520_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_FAN1; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div + [nr])); + old = + gl520_read_value(client, GL520_REG_FAN_LIMIT); + + if (nr == 0) { + old = + (old & 0x00ff) | (data-> + fan_min[nr] << 8); + if (results[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + } else { + old = (old & 0xff00) | data->fan_min[nr]; + if (results[0] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + } + gl520_write_value(client, GL520_REG_FAN_LIMIT, + old); + } + } +} + + +void gl520_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void gl520_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + gl520_write_value(client, GL520_REG_CONF, + (gl520_read_value(client, + GL520_REG_CONF) + & 0xfb) | (data-> + beep_enable << 2)); + } + if (*nrels_mag >= 2) { + data->beeps = + BEEPS_TO_REG(results[1]) & data->alarm_mask; + gl520_write_value(client, GL520_REG_ALARM, + data->beeps); + } + } +} + + +void gl520_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = gl520_read_value(client, GL520_REG_MISC); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0x3f) | (data->fan_div[0] << 6); + } + gl520_write_value(client, GL520_REG_MISC, old); + } +} + +void gl520_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void gl520_fan1off(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl520_read_value(client, GL520_REG_MISC) & 0x04) != + 0); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl520_read_value(client, + GL520_REG_MISC) & 0xfb; + if (results[0]) + old |= 0x04; + gl520_write_value(client, GL520_REG_MISC, old); + } + } +} + +void gl520_config(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl520_read_value(client, GL520_REG_CONF) & 0x10) == + 0); + data->two_temps = results[0]; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl520_read_value(client, + GL520_REG_CONF) & 0xef; + if (!results[1]) { + old |= 0x10; + data->two_temps = 0; + } else { + data->two_temps = 1; + } + gl520_write_value(client, GL520_REG_CONF, old); + } + } +} + +int __init sensors_gl520_init(void) +{ + int res; + + printk("gl520sm.o version %s (%s)\n", LM_VERSION, LM_DATE); + gl520_initialized = 0; + if ((res = i2c_add_driver(&gl520_driver))) { + printk + ("gl520sm.o: Driver registration failed, module not inserted.\n"); + gl520_cleanup(); + return res; + } + gl520_initialized++; + return 0; +} + +int __init gl520_cleanup(void) +{ + int res; + + if (gl520_initialized >= 1) { + if ((res = i2c_del_driver(&gl520_driver))) { + printk + ("gl520.o: Driver deregistration failed, module not removed.\n"); + return res; + } + gl520_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Kyösti Mälkki "); +MODULE_DESCRIPTION("GL520SM driver"); + +int init_module(void) +{ + return sensors_gl520_init(); +} + +int cleanup_module(void) +{ + return gl520_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/it87.c b/drivers/sensors/it87.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/it87.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,941 @@ +/* + it87.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring. + + Supports: IT8705F Super I/O chip w/LPC interface + IT8712F Super I/O chup w/LPC interface & SMbus + Sis950 A clone of the IT8705F + + Copyright (c) 2001 Chris Gauthron + Largely inspired by lm78.c of the same package + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + djg@pdp8.net David Gesswein 7/18/01 + Modified to fix bug with not all alarms enabled. + Added ability to read battery voltage and select temperature sensor + type at module load time. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_4(it87, it8705, it8712, sis950); + + +/* Update battery voltage after every reading if true */ +static int update_vbat = 0; + + +/* Enable Temp1 as thermal resistor */ +/* Enable Temp2 as thermal diode */ +/* Enable Temp3 as thermal resistor */ +static int temp_type = 0x2a; + +/* Many IT87 constants specified below */ + +/* Length of ISA address segment */ +#define IT87_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define IT87_ADDR_REG_OFFSET 5 +#define IT87_DATA_REG_OFFSET 6 + +/*----- The IT87 registers -----*/ + +#define IT87_REG_CONFIG 0x00 + +#define IT87_REG_ALARM1 0x01 +#define IT87_REG_ALARM2 0x02 +#define IT87_REG_ALARM3 0x03 + +#define IT87_REG_VID 0x0a +#define IT87_REG_FAN_DIV 0x0b + +/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ + +#define IT87_REG_FAN(nr) (0x0c + (nr)) +#define IT87_REG_FAN_MIN(nr) (0x0f + (nr)) +#define IT87_REG_FAN_CTRL 0x13 + +#define IT87_REG_VIN(nr) (0x20 + (nr)) +#define IT87_REG_TEMP(nr) (0x28 + (nr)) + +#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) +#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) +#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2) +#define IT87_REG_TEMP_LOW(nr) (0x3f + (nr) * 2) + +#define IT87_REG_I2C_ADDR 0x48 + +#define IT87_REG_VIN_ENABLE 0x50 +#define IT87_REG_TEMP_ENABLE 0x51 + +#define IT87_REG_CHIPID 0x58 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +/* Initial limits. Use the config file to set better limits. */ +#define IT87_INIT_IN_0 170 +#define IT87_INIT_IN_1 250 +#define IT87_INIT_IN_2 (330 / 2) +#define IT87_INIT_IN_3 (((500) * 100)/168) +#define IT87_INIT_IN_4 (((1200) * 10)/38) +#define IT87_INIT_IN_5 (((1200) * 10)/72) +#define IT87_INIT_IN_6 (((500) * 10)/56) +#define IT87_INIT_IN_7 (((500) * 100)/168) + +#define IT87_INIT_IN_PERCENTAGE 10 + +#define IT87_INIT_IN_MIN_0 \ + (IT87_INIT_IN_0 - IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_0 \ + (IT87_INIT_IN_0 + IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_1 \ + (IT87_INIT_IN_1 - IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_1 \ + (IT87_INIT_IN_1 + IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_2 \ + (IT87_INIT_IN_2 - IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_2 \ + (IT87_INIT_IN_2 + IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_3 \ + (IT87_INIT_IN_3 - IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_3 \ + (IT87_INIT_IN_3 + IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_4 \ + (IT87_INIT_IN_4 - IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_4 \ + (IT87_INIT_IN_4 + IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_5 \ + (IT87_INIT_IN_5 - IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_5 \ + (IT87_INIT_IN_5 + IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_6 \ + (IT87_INIT_IN_6 - IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_6 \ + (IT87_INIT_IN_6 + IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_7 \ + (IT87_INIT_IN_7 - IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_7 \ + (IT87_INIT_IN_7 + IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100) + +#define IT87_INIT_FAN_MIN_1 3000 +#define IT87_INIT_FAN_MIN_2 3000 +#define IT87_INIT_FAN_MIN_3 3000 + +#define IT87_INIT_TEMP_HIGH_1 600 +#define IT87_INIT_TEMP_LOW_1 200 +#define IT87_INIT_TEMP_HIGH_2 600 +#define IT87_INIT_TEMP_LOW_2 200 +#define IT87_INIT_TEMP_HIGH_3 600 +#define IT87_INIT_TEMP_LOW_3 200 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered IT87, we need to keep some data in memory. That + data is pointed to by it87_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new it87 client is + allocated. */ +struct it87_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[9]; /* Register value */ + u8 in_max[9]; /* Register value */ + u8 in_min[9]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp[3]; /* Register value */ + u8 temp_high[3]; /* Register value */ + u8 temp_low[3]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_it87_init(void); +static int __init it87_cleanup(void); + +static int it87_attach_adapter(struct i2c_adapter *adapter); +static int it87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int it87_detach_client(struct i2c_client *client); +static int it87_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void it87_inc_use(struct i2c_client *client); +static void it87_dec_use(struct i2c_client *client); + +static int it87_read_value(struct i2c_client *client, u8 register); +static int it87_write_value(struct i2c_client *client, u8 register, + u8 value); +static void it87_update_client(struct i2c_client *client); +static void it87_init_client(struct i2c_client *client); + + +static void it87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void it87_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static struct i2c_driver it87_driver = { + /* name */ "IT87xx sensor driver", + /* id */ I2C_DRIVERID_IT87, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &it87_attach_adapter, + /* detach_client */ &it87_detach_client, + /* command */ &it87_command, + /* inc_use */ &it87_inc_use, + /* dec_use */ &it87_dec_use +}; + +/* Used by it87_init/cleanup */ +static int __initdata it87_initialized = 0; + +static int it87_id = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected IT87. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table it87_dir_table_template[] = { + {IT87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_vid}, + {IT87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan_div}, + {IT87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_alarms}, + {0} +}; + + +/* This function is called when: + * it87_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and it87_driver is still present) */ +int it87_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, it87_detect); +} + +/* This function is called by i2c_detect */ +int it87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct it87_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, IT87_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some IT87-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access it87_{read,write}_value. */ + + if (!(new_client = kmalloc((sizeof(struct i2c_client)) + + sizeof(struct it87_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct it87_data *) (new_client + 1); + if (is_isa) + init_MUTEX(&data->lock); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &it87_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) + goto ERROR1; + if (!is_isa + && (it87_read_value(new_client, IT87_REG_I2C_ADDR) != + address)) goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = it87_read_value(new_client, IT87_REG_CHIPID); + if (i == 0x90) { + kind = it87; + } + else { + if (kind == 0) + printk + ("it87.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == it87) { + type_name = "it87"; + client_name = "IT87 chip"; + } /* else if (kind == it8712) { + type_name = "it8712"; + client_name = "IT87-J chip"; + } */ else { +#ifdef DEBUG + printk("it87.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, IT87_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = it87_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + it87_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the IT87 chip */ + it87_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, IT87_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int it87_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct it87_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("it87.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, IT87_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int it87_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void it87_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void it87_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int it87_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct it87_data *) (client->data))->lock)); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + res = inb_p(client->addr + IT87_DATA_REG_OFFSET); + up(&(((struct it87_data *) (client->data))->lock)); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int it87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down(&(((struct it87_data *) (client->data))->lock)); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + outb_p(value, client->addr + IT87_DATA_REG_OFFSET); + up(&(((struct it87_data *) (client->data))->lock)); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new IT87. It should set limits, etc. */ +void it87_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + it87_write_value(client, IT87_REG_CONFIG, 0x80); + it87_write_value(client, IT87_REG_VIN_MIN(0), + IN_TO_REG(IT87_INIT_IN_MIN_0)); + it87_write_value(client, IT87_REG_VIN_MAX(0), + IN_TO_REG(IT87_INIT_IN_MAX_0)); + it87_write_value(client, IT87_REG_VIN_MIN(1), + IN_TO_REG(IT87_INIT_IN_MIN_1)); + it87_write_value(client, IT87_REG_VIN_MAX(1), + IN_TO_REG(IT87_INIT_IN_MAX_1)); + it87_write_value(client, IT87_REG_VIN_MIN(2), + IN_TO_REG(IT87_INIT_IN_MIN_2)); + it87_write_value(client, IT87_REG_VIN_MAX(2), + IN_TO_REG(IT87_INIT_IN_MAX_2)); + it87_write_value(client, IT87_REG_VIN_MIN(3), + IN_TO_REG(IT87_INIT_IN_MIN_3)); + it87_write_value(client, IT87_REG_VIN_MAX(3), + IN_TO_REG(IT87_INIT_IN_MAX_3)); + it87_write_value(client, IT87_REG_VIN_MIN(4), + IN_TO_REG(IT87_INIT_IN_MIN_4)); + it87_write_value(client, IT87_REG_VIN_MAX(4), + IN_TO_REG(IT87_INIT_IN_MAX_4)); + it87_write_value(client, IT87_REG_VIN_MIN(5), + IN_TO_REG(IT87_INIT_IN_MIN_5)); + it87_write_value(client, IT87_REG_VIN_MAX(5), + IN_TO_REG(IT87_INIT_IN_MAX_5)); + it87_write_value(client, IT87_REG_VIN_MIN(6), + IN_TO_REG(IT87_INIT_IN_MIN_6)); + it87_write_value(client, IT87_REG_VIN_MAX(6), + IN_TO_REG(IT87_INIT_IN_MAX_6)); + it87_write_value(client, IT87_REG_VIN_MIN(7), + IN_TO_REG(IT87_INIT_IN_MIN_7)); + it87_write_value(client, IT87_REG_VIN_MAX(7), + IN_TO_REG(IT87_INIT_IN_MAX_7)); + /* Note: Battery voltage does not have limit registers */ + it87_write_value(client, IT87_REG_FAN_MIN(1), + FAN_TO_REG(IT87_INIT_FAN_MIN_1, 2)); + it87_write_value(client, IT87_REG_FAN_MIN(2), + FAN_TO_REG(IT87_INIT_FAN_MIN_2, 2)); + it87_write_value(client, IT87_REG_FAN_MIN(3), + FAN_TO_REG(IT87_INIT_FAN_MIN_3, 2)); + it87_write_value(client, IT87_REG_TEMP_HIGH(1), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_1)); + it87_write_value(client, IT87_REG_TEMP_LOW(1), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_1)); + it87_write_value(client, IT87_REG_TEMP_HIGH(2), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_2)); + it87_write_value(client, IT87_REG_TEMP_LOW(2), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_2)); + it87_write_value(client, IT87_REG_TEMP_HIGH(3), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_3)); + it87_write_value(client, IT87_REG_TEMP_LOW(3), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_3)); + + /* Enable voltage monitors */ + it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff); + + /* Enable Temp1-Temp3 */ + it87_write_value(client, IT87_REG_TEMP_ENABLE, + (it87_read_value(client, IT87_REG_TEMP_ENABLE) & 0xc0) + | (temp_type & 0x3f)); + + /* Enable fans */ + it87_write_value(client, IT87_REG_FAN_CTRL, + (it87_read_value(client, IT87_REG_FAN_CTRL) & 0x8f) + | 0x70); + + /* Start monitoring */ + it87_write_value(client, IT87_REG_CONFIG, + (it87_read_value(client, IT87_REG_CONFIG) & 0xb7) + | (update_vbat ? 0x41 : 0x01)); +} + +void it87_update_client(struct i2c_client *client) +{ + struct it87_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + if (update_vbat) { + /* Cleared after each update, so reenable. Value + returned by this read will be previous value */ + it87_write_value(client, IT87_REG_CONFIG, + it87_read_value(client, IT87_REG_CONFIG) | 0x40); + } + for (i = 0; i <= 7; i++) { + data->in[i] = + it87_read_value(client, IT87_REG_VIN(i)); + data->in_min[i] = + it87_read_value(client, IT87_REG_VIN_MIN(i)); + data->in_max[i] = + it87_read_value(client, IT87_REG_VIN_MAX(i)); + } + data->in[8] = + it87_read_value(client, IT87_REG_VIN(8)); + /* Temperature sensor doesn't have limit registers, set + to min and max value */ + data->in_min[8] = 0; + data->in_max[8] = 255; + + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + it87_read_value(client, IT87_REG_FAN(i)); + data->fan_min[i - 1] = + it87_read_value(client, IT87_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + data->temp[i - 1] = + it87_read_value(client, IT87_REG_TEMP(i)); + data->temp_high[i - 1] = + it87_read_value(client, IT87_REG_TEMP_HIGH(i)); + data->temp_low[i - 1] = + it87_read_value(client, IT87_REG_TEMP_LOW(i)); + } + + /* The 8705 does not have VID capability */ + /*if (data->type == it8712) { + data->vid = it87_read_value(client, IT87_REG_VID); + data->vid &= 0x1f; + } + else */ { + data->vid = 0x1f; + } + + i = it87_read_value(client, IT87_REG_FAN_DIV); + data->fan_div[0] = i & 0x07; + data->fan_div[1] = (i >> 3) & 0x07; + data->fan_div[2] = 1; + + data->alarms = + it87_read_value(client, IT87_REG_ALARM1) | + (it87_read_value(client, IT87_REG_ALARM2) << 8) | + (it87_read_value(client, IT87_REG_ALARM3) << 16); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + - Each function must return the magnitude (power of 10 to divide the + data with) if it is called with operation==SENSORS_PROC_REAL_INFO. + - It must put a maximum of *nrels elements in results reflecting the + data of this file, and set *nrels to the number it actually put + in it, if operation==SENSORS_PROC_REAL_READ. + - Finally, it must get upto *nrels elements from results and write them + to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void it87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + it87_write_value(client, IT87_REG_VIN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + it87_write_value(client, IT87_REG_VIN_MAX(nr), + data->in_max[nr]); + } + } +} + +void it87_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + it87_write_value(client, IT87_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +void it87_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_TEMP1 + 1; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_high[nr - 1]); + results[1] = TEMP_FROM_REG(data->temp_low[nr - 1]); + results[2] = TEMP_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_high[nr - 1] = TEMP_TO_REG(results[0]); + it87_write_value(client, IT87_REG_TEMP_HIGH(nr), + data->temp_high[nr - 1]); + } + if (*nrels_mag >= 2) { + data->temp_low[nr - 1] = TEMP_TO_REG(results[1]); + it87_write_value(client, IT87_REG_TEMP_LOW(nr), + data->temp_low[nr - 1]); + } + } +} + +void it87_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void it87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void it87_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = it87_read_value(client, IT87_REG_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xc3) | (data->fan_div[1] << 3); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xf8) | data->fan_div[0]; + it87_write_value(client, IT87_REG_FAN_DIV, old); + } + } +} + +int __init sensors_it87_init(void) +{ + int res; + + printk("it87.o version %s (%s)\n", LM_VERSION, LM_DATE); + it87_initialized = 0; + + if ((res = i2c_add_driver(&it87_driver))) { + printk + ("it87.o: Driver registration failed, module not inserted.\n"); + it87_cleanup(); + return res; + } + it87_initialized++; + return 0; +} + +int __init it87_cleanup(void) +{ + int res; + + if (it87_initialized >= 1) { + if ((res = i2c_del_driver(&it87_driver))) { + printk + ("it87.o: Driver deregistration failed, module not removed.\n"); + return res; + } + it87_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Chris Gauthron "); +MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); +MODULE_PARM(update_vbat, "i"); +MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); +MODULE_PARM(temp_type, "i"); +MODULE_PARM_DESC(temp_type, "Temperature sensor type, normally leave unset"); + +int init_module(void) +{ + return sensors_it87_init(); +} + +int cleanup_module(void) +{ + return it87_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/lm75.c b/drivers/sensors/lm75.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/lm75.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,439 @@ +/* + lm75.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm75); + +/* Many LM75 constants specified below */ + +/* The LM75 registers */ +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_TEMP_HYST 0x02 +#define LM75_REG_TEMP_OS 0x03 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) + +/* Initial values */ +#define LM75_INIT_TEMP_OS 600 +#define LM75_INIT_TEMP_HYST 500 + +/* Each client has this additional data */ +struct lm75_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_os, temp_hyst; /* Register values */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm75_init(void); +static int __init lm75_cleanup(void); +static int lm75_attach_adapter(struct i2c_adapter *adapter); +static int lm75_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void lm75_init_client(struct i2c_client *client); +static int lm75_detach_client(struct i2c_client *client); +static int lm75_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm75_inc_use(struct i2c_client *client); +static void lm75_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int lm75_read_value(struct i2c_client *client, u8 reg); +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); +static void lm75_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm75_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver lm75_driver = { + /* name */ "LM75 sensor chip driver", + /* id */ I2C_DRIVERID_LM75, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm75_attach_adapter, + /* detach_client */ &lm75_detach_client, + /* command */ &lm75_command, + /* inc_use */ &lm75_inc_use, + /* dec_use */ &lm75_dec_use +}; + +/* These files are created for each detected LM75. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm75_dir_table_template[] = { + {LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm75_temp}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata lm75_initialized = 0; + +static int lm75_id = 0; + +int lm75_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm75_detect); +} + +/* This function is called by i2c_detect */ +int lm75_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur, conf, hyst, os; + struct i2c_client *new_client; + struct lm75_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm75_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm75_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm75_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm75_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (kind < 0) { + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + os = i2c_smbus_read_word_data(new_client, 3); + for (i = 0; i <= 0x1f; i++) + if ( + (i2c_smbus_read_byte_data + (new_client, i * 8 + 1) != conf) + || + (i2c_smbus_read_word_data + (new_client, i * 8 + 2) != hyst) + || + (i2c_smbus_read_word_data + (new_client, i * 8 + 3) != os)) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm75; + + if (kind == lm75) { + type_name = "lm75"; + client_name = "LM75 chip"; + } else { +#ifdef DEBUG + printk("lm75.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = lm75_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + lm75_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + lm75_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm75_detach_client(struct i2c_client *client) +{ + int err; + +#ifdef MODULE + if (MOD_IN_USE) + return -EBUSY; +#endif + + + i2c_deregister_entry(((struct lm75_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm75.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int lm75_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void lm75_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void lm75_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int lm75_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return swap_bytes(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); +} + +void lm75_init_client(struct i2c_client *client) +{ + /* Initialize the LM75 chip */ + lm75_write_value(client, LM75_REG_TEMP_OS, + TEMP_TO_REG(LM75_INIT_TEMP_OS)); + lm75_write_value(client, LM75_REG_TEMP_HYST, + TEMP_TO_REG(LM75_INIT_TEMP_HYST)); + lm75_write_value(client, LM75_REG_CONF, 0); +} + +void lm75_update_client(struct i2c_client *client) +{ + struct lm75_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm75 update\n"); +#endif + + data->temp = lm75_read_value(client, LM75_REG_TEMP); + data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS); + data->temp_hyst = + lm75_read_value(client, LM75_REG_TEMP_HYST); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void lm75_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm75_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm75_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + lm75_write_value(client, LM75_REG_TEMP_OS, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm75_write_value(client, LM75_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +int __init sensors_lm75_init(void) +{ + int res; + + printk("lm75.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm75_initialized = 0; + if ((res = i2c_add_driver(&lm75_driver))) { + printk + ("lm75.o: Driver registration failed, module not inserted.\n"); + lm75_cleanup(); + return res; + } + lm75_initialized++; + return 0; +} + +int __init lm75_cleanup(void) +{ + int res; + + if (lm75_initialized >= 1) { + if ((res = i2c_del_driver(&lm75_driver))) { + printk + ("lm75.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm75_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM75 driver"); + +int init_module(void) +{ + return sensors_lm75_init(); +} + +int cleanup_module(void) +{ + return lm75_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/lm78.c b/drivers/sensors/lm78.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/lm78.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,873 @@ +/* + lm78.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(lm78, lm78j, lm79); + +/* Many LM78 constants specified below */ + +/* Length of ISA address segment */ +#define LM78_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define LM78_ADDR_REG_OFFSET 5 +#define LM78_DATA_REG_OFFSET 6 + +/* The LM78 registers */ +#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define LM78_REG_IN(nr) (0x20 + (nr)) + +#define LM78_REG_FAN_MIN(nr) (0x3a + (nr)) +#define LM78_REG_FAN(nr) (0x27 + (nr)) + +#define LM78_REG_TEMP 0x27 +#define LM78_REG_TEMP_OVER 0x39 +#define LM78_REG_TEMP_HYST 0x3a + +#define LM78_REG_ALARM1 0x41 +#define LM78_REG_ALARM2 0x42 + +#define LM78_REG_VID_FANDIV 0x47 + +#define LM78_REG_CONFIG 0x40 +#define LM78_REG_CHIPID 0x49 +#define LM78_REG_I2C_ADDR 0x48 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +/* Initial limits. To keep them sane, we use the 'standard' translation as + specified in the LM78 sheet. Use the config file to set better limits. */ +#define LM78_INIT_IN_0 (vid==350?280:vid) +#define LM78_INIT_IN_1 (vid==350?280:vid) +#define LM78_INIT_IN_2 330 +#define LM78_INIT_IN_3 (((500) * 100)/168) +#define LM78_INIT_IN_4 (((1200) * 10)/38) +#define LM78_INIT_IN_5 (((-1200) * -604)/2100) +#define LM78_INIT_IN_6 (((-500) * -604)/909) + +#define LM78_INIT_IN_PERCENTAGE 10 + +#define LM78_INIT_IN_MIN_0 \ + (LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_0 \ + (LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_1 \ + (LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_1 \ + (LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_2 \ + (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_2 \ + (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_3 \ + (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_3 \ + (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_4 \ + (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_4 \ + (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_5 \ + (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_5 \ + (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_6 \ + (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_6 \ + (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) + +#define LM78_INIT_FAN_MIN_1 3000 +#define LM78_INIT_FAN_MIN_2 3000 +#define LM78_INIT_FAN_MIN_3 3000 + +#define LM78_INIT_TEMP_OVER 600 +#define LM78_INIT_TEMP_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, LM78 chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + LM78 chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the LM78 at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered LM78, we need to keep some data in memory. That + data is pointed to by lm78_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm78 client is + allocated. */ +struct lm78_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_over; /* Register value */ + u8 temp_hyst; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm78_init(void); +static int __init lm78_cleanup(void); + +static int lm78_attach_adapter(struct i2c_adapter *adapter); +static int lm78_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm78_detach_client(struct i2c_client *client); +static int lm78_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm78_inc_use(struct i2c_client *client); +static void lm78_dec_use(struct i2c_client *client); + +static int lm78_read_value(struct i2c_client *client, u8 register); +static int lm78_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm78_update_client(struct i2c_client *client); +static void lm78_init_client(struct i2c_client *client); + + +static void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void lm78_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static struct i2c_driver lm78_driver = { + /* name */ "LM78(-J) and LM79 sensor driver", + /* id */ I2C_DRIVERID_LM78, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm78_attach_adapter, + /* detach_client */ &lm78_detach_client, + /* command */ &lm78_command, + /* inc_use */ &lm78_inc_use, + /* dec_use */ &lm78_dec_use +}; + +/* Used by lm78_init/cleanup */ +static int __initdata lm78_initialized = 0; + +static int lm78_id = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM78. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm78_dir_table_template[] = { + {LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_temp}, + {LM78_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_vid}, + {LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan_div}, + {LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_alarms}, + {0} +}; + + +/* This function is called when: + * lm78_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and lm78_driver is still present) */ +int lm78_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm78_detect); +} + +/* This function is called by i2c_detect */ +int lm78_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct lm78_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, LM78_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm78_{read,write}_value. */ + + if (!(new_client = kmalloc((sizeof(struct i2c_client)) + + sizeof(struct lm78_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm78_data *) (new_client + 1); + if (is_isa) + init_MUTEX(&data->lock); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm78_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) + goto ERROR1; + if (!is_isa + && (lm78_read_value(new_client, LM78_REG_I2C_ADDR) != + address)) goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = lm78_read_value(new_client, LM78_REG_CHIPID); + if (i == 0x00 || i == 0x20) + kind = lm78; + else if (i == 0x40) + kind = lm78j; + else if ((i & 0xfe) == 0xc0) + kind = lm79; + else { + if (kind == 0) + printk + ("lm78.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == lm78) { + type_name = "lm78"; + client_name = "LM78 chip"; + } else if (kind == lm78j) { + type_name = "lm78-j"; + client_name = "LM78-J chip"; + } else if (kind == lm79) { + type_name = "lm79"; + client_name = "LM79 chip"; + } else { +#ifdef DEBUG + printk("lm78.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, LM78_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = lm78_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + lm78_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the LM78 chip */ + lm78_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, LM78_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm78_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm78_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm78.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, LM78_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void lm78_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void lm78_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct lm78_data *) (client->data))->lock)); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + up(&(((struct lm78_data *) (client->data))->lock)); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down(&(((struct lm78_data *) (client->data))->lock)); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + up(&(((struct lm78_data *) (client->data))->lock)); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM78. It should set limits, etc. */ +void lm78_init_client(struct i2c_client *client) +{ + int vid; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + lm78_write_value(client, LM78_REG_CONFIG, 0x80); + + vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f; + if (((struct lm78_data *) (client->data))->type == lm79) + vid |= + (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4; + else + vid |= 0x10; + vid = VID_FROM_REG(vid); + + lm78_write_value(client, LM78_REG_IN_MIN(0), + IN_TO_REG(LM78_INIT_IN_MIN_0)); + lm78_write_value(client, LM78_REG_IN_MAX(0), + IN_TO_REG(LM78_INIT_IN_MAX_0)); + lm78_write_value(client, LM78_REG_IN_MIN(1), + IN_TO_REG(LM78_INIT_IN_MIN_1)); + lm78_write_value(client, LM78_REG_IN_MAX(1), + IN_TO_REG(LM78_INIT_IN_MAX_1)); + lm78_write_value(client, LM78_REG_IN_MIN(2), + IN_TO_REG(LM78_INIT_IN_MIN_2)); + lm78_write_value(client, LM78_REG_IN_MAX(2), + IN_TO_REG(LM78_INIT_IN_MAX_2)); + lm78_write_value(client, LM78_REG_IN_MIN(3), + IN_TO_REG(LM78_INIT_IN_MIN_3)); + lm78_write_value(client, LM78_REG_IN_MAX(3), + IN_TO_REG(LM78_INIT_IN_MAX_3)); + lm78_write_value(client, LM78_REG_IN_MIN(4), + IN_TO_REG(LM78_INIT_IN_MIN_4)); + lm78_write_value(client, LM78_REG_IN_MAX(4), + IN_TO_REG(LM78_INIT_IN_MAX_4)); + lm78_write_value(client, LM78_REG_IN_MIN(5), + IN_TO_REG(LM78_INIT_IN_MIN_5)); + lm78_write_value(client, LM78_REG_IN_MAX(5), + IN_TO_REG(LM78_INIT_IN_MAX_5)); + lm78_write_value(client, LM78_REG_IN_MIN(6), + IN_TO_REG(LM78_INIT_IN_MIN_6)); + lm78_write_value(client, LM78_REG_IN_MAX(6), + IN_TO_REG(LM78_INIT_IN_MAX_6)); + lm78_write_value(client, LM78_REG_FAN_MIN(1), + FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2)); + lm78_write_value(client, LM78_REG_FAN_MIN(2), + FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2)); + lm78_write_value(client, LM78_REG_FAN_MIN(3), + FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2)); + lm78_write_value(client, LM78_REG_TEMP_OVER, + TEMP_TO_REG(LM78_INIT_TEMP_OVER)); + lm78_write_value(client, LM78_REG_TEMP_HYST, + TEMP_TO_REG(LM78_INIT_TEMP_HYST)); + + /* Start monitoring */ + lm78_write_value(client, LM78_REG_CONFIG, + (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7) + | 0x01); + +} + +void lm78_update_client(struct i2c_client *client) +{ + struct lm78_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm78 update\n"); +#endif + for (i = 0; i <= 6; i++) { + data->in[i] = + lm78_read_value(client, LM78_REG_IN(i)); + data->in_min[i] = + lm78_read_value(client, LM78_REG_IN_MIN(i)); + data->in_max[i] = + lm78_read_value(client, LM78_REG_IN_MAX(i)); + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + lm78_read_value(client, LM78_REG_FAN(i)); + data->fan_min[i - 1] = + lm78_read_value(client, LM78_REG_FAN_MIN(i)); + } + data->temp = lm78_read_value(client, LM78_REG_TEMP); + data->temp_over = + lm78_read_value(client, LM78_REG_TEMP_OVER); + data->temp_hyst = + lm78_read_value(client, LM78_REG_TEMP_HYST); + i = lm78_read_value(client, LM78_REG_VID_FANDIV); + data->vid = i & 0x0f; + if (data->type == lm79) + data->vid |= + (lm78_read_value(client, LM78_REG_CHIPID) & + 0x01) << 4; + else + data->vid |= 0x10; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + + (lm78_read_value(client, LM78_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + + data->fan_div[2] = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + lm78_write_value(client, LM78_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + lm78_write_value(client, LM78_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + lm78_write_value(client, LM78_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + lm78_write_value(client, LM78_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm78_write_value(client, LM78_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm78_read_value(client, LM78_REG_VID_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + lm78_write_value(client, LM78_REG_VID_FANDIV, old); + } + } +} + +int __init sensors_lm78_init(void) +{ + int res; + + printk("lm78.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm78_initialized = 0; + + if ((res = i2c_add_driver(&lm78_driver))) { + printk + ("lm78.o: Driver registration failed, module not inserted.\n"); + lm78_cleanup(); + return res; + } + lm78_initialized++; + return 0; +} + +int __init lm78_cleanup(void) +{ + int res; + + if (lm78_initialized >= 1) { + if ((res = i2c_del_driver(&lm78_driver))) { + printk + ("lm78.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm78_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); + +int init_module(void) +{ + return sensors_lm78_init(); +} + +int cleanup_module(void) +{ + return lm78_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/lm80.c b/drivers/sensors/lm80.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/lm80.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,761 @@ +/* + lm80.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm80); + +/* Many LM80 constants specified below */ + +/* The LM80 registers */ +#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define LM80_REG_IN(nr) (0x20 + (nr)) + +#define LM80_REG_FAN1_MIN 0x3c +#define LM80_REG_FAN2_MIN 0x3d +#define LM80_REG_FAN1 0x28 +#define LM80_REG_FAN2 0x29 + +#define LM80_REG_TEMP 0x27 +#define LM80_REG_TEMP_HOT_MAX 0x38 +#define LM80_REG_TEMP_HOT_HYST 0x39 +#define LM80_REG_TEMP_OS_MAX 0x3a +#define LM80_REG_TEMP_OS_HYST 0x3b + +#define LM80_REG_CONFIG 0x00 +#define LM80_REG_ALARM1 0x01 +#define LM80_REG_ALARM2 0x02 +#define LM80_REG_MASK1 0x03 +#define LM80_REG_MASK2 0x04 +#define LM80_REG_FANDIV 0x05 +#define LM80_REG_RES 0x06 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +extern inline long TEMP_FROM_REG(u16 temp) +{ + long res; + + temp = temp >> 4; + if (temp < 0x0800) { + res = (625 * (long) temp); + } else { + res = ((long) temp - 0x01000) * 625; + } + return res / 100; +} + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-50)/100):\ + ((val)+50)/100), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits */ +#define LM80_INIT_IN_0 190 +#define LM80_INIT_IN_1 190 +#define LM80_INIT_IN_2 190 +#define LM80_INIT_IN_3 190 +#define LM80_INIT_IN_4 190 +#define LM80_INIT_IN_5 190 +#define LM80_INIT_IN_6 190 + +#define LM80_INIT_IN_PERCENTAGE 10 + +#define LM80_INIT_IN_MIN_0 \ + (LM80_INIT_IN_0 - LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_0 \ + (LM80_INIT_IN_0 + LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_1 \ + (LM80_INIT_IN_1 - LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_1 \ + (LM80_INIT_IN_1 + LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_2 \ + (LM80_INIT_IN_2 - LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_2 \ + (LM80_INIT_IN_2 + LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_3 \ + (LM80_INIT_IN_3 - LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_3 \ + (LM80_INIT_IN_3 + LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_4 \ + (LM80_INIT_IN_4 - LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_4 \ + (LM80_INIT_IN_4 + LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_5 \ + (LM80_INIT_IN_5 - LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_5 \ + (LM80_INIT_IN_5 + LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_6 \ + (LM80_INIT_IN_6 - LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_6 \ + (LM80_INIT_IN_6 + LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100) + +#define LM80_INIT_FAN_MIN_1 3000 +#define LM80_INIT_FAN_MIN_2 3000 + +#define LM80_INIT_TEMP_OS_MAX 600 +#define LM80_INIT_TEMP_OS_HYST 500 +#define LM80_INIT_TEMP_HOT_MAX 700 +#define LM80_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered LM80, we need to keep some data in memory. That + data is pointed to by lm80_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm80 client is + allocated. */ +struct lm80_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 temp; /* Register values, shifted right */ + u8 temp_hot_max; /* Register value */ + u8 temp_hot_hyst; /* Register value */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm80_init(void); +static int __init lm80_cleanup(void); + +static int lm80_attach_adapter(struct i2c_adapter *adapter); +static int lm80_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm80_detach_client(struct i2c_client *client); +static int lm80_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm80_inc_use(struct i2c_client *client); +static void lm80_dec_use(struct i2c_client *client); + +static int lm80_read_value(struct i2c_client *client, u8 register); +static int lm80_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm80_update_client(struct i2c_client *client); +static void lm80_init_client(struct i2c_client *client); + + +static void lm80_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void lm80_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int lm80_id = 0; + +static struct i2c_driver lm80_driver = { + /* name */ "LM80 sensor driver", + /* id */ I2C_DRIVERID_LM80, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm80_attach_adapter, + /* detach_client */ &lm80_detach_client, + /* command */ &lm80_command, + /* inc_use */ &lm80_inc_use, + /* dec_use */ &lm80_dec_use +}; + +/* Used by lm80_init/cleanup */ +static int __initdata lm80_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM80. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm80_dir_table_template[] = { + {LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan}, + {LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan}, + {LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_temp}, + {LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan_div}, + {LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_alarms}, + {0} +}; + +int lm80_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm80_detect); +} + +int lm80_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct lm80_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("lm80.o: lm80_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm80_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm80_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm80_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm80_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0) + goto ERROR1; + for (i = 0x2a; i <= 0x3d; i++) { + cur = i2c_smbus_read_byte_data(new_client, i); + if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur) + || (i2c_smbus_read_byte_data(new_client, i + 0x80) != + cur) + || (i2c_smbus_read_byte_data(new_client, i + 0xc0) != + cur)) goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm80; + + if (kind == lm80) { + type_name = "lm80"; + client_name = "LM80 chip"; + } else { +#ifdef DEBUG + printk("lm80.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = lm80_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + lm80_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + lm80_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm80_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm80_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm80.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int lm80_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void lm80_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void lm80_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +int lm80_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM80. It should set limits, etc. */ +void lm80_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + lm80_write_value(client, LM80_REG_CONFIG, 0x80); + /* Set 11-bit temperature resolution */ + lm80_write_value(client, LM80_REG_RES, 0x08); + + lm80_write_value(client, LM80_REG_IN_MIN(0), + IN_TO_REG(LM80_INIT_IN_MIN_0, 0)); + lm80_write_value(client, LM80_REG_IN_MAX(0), + IN_TO_REG(LM80_INIT_IN_MAX_0, 0)); + lm80_write_value(client, LM80_REG_IN_MIN(1), + IN_TO_REG(LM80_INIT_IN_MIN_1, 1)); + lm80_write_value(client, LM80_REG_IN_MAX(1), + IN_TO_REG(LM80_INIT_IN_MAX_1, 1)); + lm80_write_value(client, LM80_REG_IN_MIN(2), + IN_TO_REG(LM80_INIT_IN_MIN_2, 2)); + lm80_write_value(client, LM80_REG_IN_MAX(2), + IN_TO_REG(LM80_INIT_IN_MAX_2, 2)); + lm80_write_value(client, LM80_REG_IN_MIN(3), + IN_TO_REG(LM80_INIT_IN_MIN_3, 3)); + lm80_write_value(client, LM80_REG_IN_MAX(3), + IN_TO_REG(LM80_INIT_IN_MAX_3, 3)); + lm80_write_value(client, LM80_REG_IN_MIN(4), + IN_TO_REG(LM80_INIT_IN_MIN_4, 4)); + lm80_write_value(client, LM80_REG_IN_MAX(4), + IN_TO_REG(LM80_INIT_IN_MAX_4, 4)); + lm80_write_value(client, LM80_REG_IN_MIN(5), + IN_TO_REG(LM80_INIT_IN_MIN_5, 5)); + lm80_write_value(client, LM80_REG_IN_MAX(5), + IN_TO_REG(LM80_INIT_IN_MAX_5, 5)); + lm80_write_value(client, LM80_REG_IN_MIN(6), + IN_TO_REG(LM80_INIT_IN_MIN_6, 6)); + lm80_write_value(client, LM80_REG_IN_MAX(6), + IN_TO_REG(LM80_INIT_IN_MAX_6, 6)); + lm80_write_value(client, LM80_REG_FAN1_MIN, + FAN_TO_REG(LM80_INIT_FAN_MIN_1, 2)); + lm80_write_value(client, LM80_REG_FAN2_MIN, + FAN_TO_REG(LM80_INIT_FAN_MIN_2, 2)); + lm80_write_value(client, LM80_REG_TEMP_HOT_MAX, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX)); + lm80_write_value(client, LM80_REG_TEMP_HOT_HYST, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST)); + lm80_write_value(client, LM80_REG_TEMP_OS_MAX, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX)); + lm80_write_value(client, LM80_REG_TEMP_OS_HYST, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST)); + + /* Start monitoring */ + lm80_write_value(client, LM80_REG_CONFIG, 0x01); +} + +void lm80_update_client(struct i2c_client *client) +{ + struct lm80_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm80 update\n"); +#endif + for (i = 0; i <= 6; i++) { + data->in[i] = + lm80_read_value(client, LM80_REG_IN(i)); + data->in_min[i] = + lm80_read_value(client, LM80_REG_IN_MIN(i)); + data->in_max[i] = + lm80_read_value(client, LM80_REG_IN_MAX(i)); + } + data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); + data->fan_min[0] = + lm80_read_value(client, LM80_REG_FAN1_MIN); + data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); + data->fan_min[1] = + lm80_read_value(client, LM80_REG_FAN2_MIN); + + data->temp = + (lm80_read_value(client, LM80_REG_TEMP) << 8) | + (lm80_read_value(client, LM80_REG_RES) & 0xf0); + data->temp_os_max = + lm80_read_value(client, LM80_REG_TEMP_OS_MAX); + data->temp_os_hyst = + lm80_read_value(client, LM80_REG_TEMP_OS_HYST); + data->temp_hot_max = + lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); + data->temp_hot_hyst = + lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); + + i = lm80_read_value(client, LM80_REG_FANDIV); + data->fan_div[0] = (i >> 2) & 0x03; + data->fan_div[1] = (i >> 4) & 0x03; + data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + + (lm80_read_value(client, LM80_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm80_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int nr = ctl_name - LM80_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr], nr); + results[1] = IN_FROM_REG(data->in_max[nr], nr); + results[2] = IN_FROM_REG(data->in[nr], nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0], nr); + lm80_write_value(client, LM80_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1], nr); + lm80_write_value(client, LM80_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void lm80_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int nr = ctl_name - LM80_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + lm80_write_value(client, + nr == + 1 ? LM80_REG_FAN1_MIN : + LM80_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void lm80_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst); + results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[4] = TEMP_FROM_REG(data->temp); + *nrels_mag = 5; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]); + lm80_write_value(client, LM80_REG_TEMP_HOT_MAX, + data->temp_hot_max); + } + if (*nrels_mag >= 2) { + data->temp_hot_hyst = + TEMP_LIMIT_TO_REG(results[1]); + lm80_write_value(client, LM80_REG_TEMP_HOT_HYST, + data->temp_hot_hyst); + } + if (*nrels_mag >= 3) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]); + lm80_write_value(client, LM80_REG_TEMP_OS_MAX, + data->temp_os_max); + } + if (*nrels_mag >= 4) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]); + lm80_write_value(client, LM80_REG_TEMP_OS_HYST, + data->temp_os_hyst); + } + } +} + +void lm80_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm80_read_value(client, LM80_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xf3) | (data->fan_div[0] << 2); + lm80_write_value(client, LM80_REG_FANDIV, old); + } + } +} + +int __init sensors_lm80_init(void) +{ + int res; + + printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm80_initialized = 0; + + if ((res = i2c_add_driver(&lm80_driver))) { + printk + ("lm80.o: Driver registration failed, module not inserted.\n"); + lm80_cleanup(); + return res; + } + lm80_initialized++; + return 0; +} + +int __init lm80_cleanup(void) +{ + int res; + + if (lm80_initialized >= 1) { + if ((res = i2c_del_driver(&lm80_driver))) { + printk + ("lm80.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm80_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("LM80 driver"); + +int init_module(void) +{ + return sensors_lm80_init(); +} + +int cleanup_module(void) +{ + return lm80_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/lm87.c b/drivers/sensors/lm87.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/lm87.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,1090 @@ +/* + LM87.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2000 Frodo Looijaard + Philip Edelbrock + Stephen Rousset + Dan Eaton + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* Chip configuration settings. These should be set to reflect the +HARDWARE configuration of your chip. By default (read: when all of +these are left commented out), this driver assumes that the +configuration is the same as National's defaults for the Channel Mode +register. + +Set to '1' the appropriate defines, as nessesary: + + - External temp sensors 2 (possible second CPU temp) + This will disable the 2.5V and Vccp2 readings. + Ironicly, National decided that you can read the + temperature of a second CPU or it's core voltage, + but not both! Comment out if FAULT is reported. */ + +/* #define LM87_EXT2 1 */ + +/* Aux analog input. When enabled, the Fan 1 reading + will be disabled */ + +/* #define LM87_AIN1 1 */ + +/* Aux analog input 2. When enabled, the Fan 2 reading + will be disabled */ + +/* #define LM87_AIN2 1 */ + +/* Internal Vcc is 5V instead of 3.3V */ + +/* #define LM87_5V_VCC 1 */ + +/* That's the end of the hardware config defines. I would have made + them insmod params, but it would be too much work. ;') */ + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm87); + +/* The following is the calculation for the register offset + * for the monitored items minimum and maximum locations. + */ +#define LM87_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define LM87_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define LM87_REG_IN(nr) (0x20 + (nr)) + +/* Initial limits */ + +/* + * LM87 register definition + * + */ + + /* The LM87 registers */ +#define LM87_INT_TEMP_HI_LIMIT_LOCKABLE 0x13 +#define LM87_EXT_TEMP_HI_LIMIT_LOCKABLE 0x14 +#define LM87_REG_TEST 0x15 +#define LM87_REG_CHANNEL_MODE 0x16 +#define LM87_REG_INT_TEMP_HI_LIMIT 0x17 +#define LM87_REG_EXT_TEMP_HI_LIMIT 0x18 +#define LM87_REG_ANALOG_OUT 0x19 + + /* These are all read-only */ +#define LM87_REG_2_5V_EXT_TEMP_2 0x20 +#define LM87_REG_VCCP1 0x21 +#define LM87_REG_3_3V 0x22 +#define LM87_REG_5V 0x23 +#define LM87_REG_12V 0x24 +#define LM87_REG_VCCP2 0x25 +#define LM87_REG_EXT_TEMP_1 0x26 +#define LM87_REG_INT_TEMP 0x27 /* LM87 temp. */ +#define LM87_REG_FAN1_AIN1 0x28 +#define LM87_REG_FAN2_AIN2 0x29 + +/* These are read/write */ +#define LM87_REG_AIN1_LOW 0x1A +#define LM87_REG_AIN2_LOW 0x1B +#define LM87_REG_2_5V_EXT_TEMP_2_HIGH 0x2B +#define LM87_REG_2_5V_EXT_TEMP_2_LOW 0x2C +#define LM87_REG_VCCP1_HIGH 0x2D +#define LM87_REG_VCCP1_LOW 0x2E +#define LM87_REG_3_3V_HIGH 0x2F +#define LM87_REG_3_3V_LOW 0x30 +#define LM87_REG_5V_HIGH 0x31 +#define LM87_REG_5V_LOW 0x32 +#define LM87_REG_12V_HIGH 0x33 +#define LM87_REG_12V_LOW 0x34 +#define LM87_REG_VCCP2_HIGH 0x35 +#define LM87_REG_VCCP2_LOW 0x36 +#define LM87_REG_EXT_TEMP_1_HIGH 0x37 +#define LM87_REG_EXT_TEMP_1_LOW 0x38 +#define LM87_REG_INT_TEMP_HIGH 0x39 +#define LM87_REG_INT_TEMP_LOW 0x3A +#define LM87_REG_FAN1_AIN1_LIMIT 0x3B +#define LM87_REG_FAN2_AIN2_LIMIT 0x3C +#define LM87_REG_COMPANY_ID 0x3E +#define LM87_REG_DIE_REV 0x3F + +#define LM87_REG_CONFIG 0x40 +#define LM87_REG_INT1_STAT 0x41 +#define LM87_REG_INT2_STAT 0x42 +#define LM87_REG_INT1_MASK 0x43 +#define LM87_REG_INT2_MASK 0x44 +#define LM87_REG_CHASSIS_CLEAR 0x46 +#define LM87_REG_VID_FAN_DIV 0x47 +#define LM87_REG_VID4 0x49 +#define LM87_REG_CONFIG_2 0x4A +#define LM87_REG_INTRPT_STATUS_1_MIRROR 0x4C +#define LM87_REG_INTRPT_STATUS_2_MIRROR 0x4D +#define LM87_REG_SMBALERT_NUM_ENABLE 0x80 + + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) (temp * 10) + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255) +#if 0 +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define TEMP_LIMIT_FROM_REG(val) (val) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val),0,255) +#endif + + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +#define LM87_INIT_FAN_MIN 3000 + +#define LM87_INIT_EXT_TEMP_MAX 600 +#define LM87_INIT_EXT_TEMP_MIN 100 +#define LM87_INIT_INT_TEMP_MAX 600 +#define LM87_INIT_INT_TEMP_MIN 100 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered LM87, we need to keep some data in memory. That + data is pointed to by LM87_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new LM87 client is + allocated. */ +struct lm87_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 ain1; /* Register value */ + u8 ain1_min; /* Register value */ + u8 ain1_max; /* Register value */ + u8 ain2; /* Register value */ + u8 ain2_min; /* Register value */ + u8 ain2_max; /* Register value */ + u8 fan; /* Register value */ + u8 fan_min; /* Register value */ + u8 fan_div; /* Register encoding, shifted right */ + u8 fan2; /* Register value */ + u8 fan2_min; /* Register value */ + u8 fan2_div; /* Register encoding, shifted right */ + int ext2_temp; /* Temp, shifted right */ + int ext_temp; /* Temp, shifted right */ + int int_temp; /* Temp, shifted right */ + u8 ext_temp_max; /* Register value */ + u8 ext_temp_min; /* Register value */ + u8 ext2_temp_max; /* Register value */ + u8 ext2_temp_min; /* Register value */ + u8 int_temp_max; /* Register value */ + u8 int_temp_min; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm87_init(void); +static int __init lm87_cleanup(void); + +static int lm87_attach_adapter(struct i2c_adapter *adapter); +static int lm87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm87_detach_client(struct i2c_client *client); +static int lm87_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm87_inc_use(struct i2c_client *client); +static void lm87_dec_use(struct i2c_client *client); + +static int lm87_read_value(struct i2c_client *client, u8 register); +static int lm87_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm87_update_client(struct i2c_client *client); +static void lm87_init_client(struct i2c_client *client); + + +static void lm87_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#if defined (LM87_AIN1) || defined (LM87_AIN2) +static void lm87_ain(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#endif +static void lm87_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void lm87_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static LM87 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int lm87_id = 0; + +static struct i2c_driver LM87_driver = { + /* name */ "LM87 sensor driver", + /* id */ I2C_DRIVERID_LM87, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm87_attach_adapter, + /* detach_client */ &lm87_detach_client, + /* command */ &lm87_command, + /* inc_use */ &lm87_inc_use, + /* dec_use */ &lm87_dec_use +}; + +/* Used by LM87_init/cleanup */ +static int __initdata lm87_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM87. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +static ctl_table LM87_dir_table_template[] = { +#ifdef LM87_AIN1 + {LM87_SYSCTL_AIN1, "ain1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_ain}, +#endif +#ifdef LM87_AIN2 + {LM87_SYSCTL_AIN2, "ain2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_ain}, +#endif +#ifndef LM87_EXT2 + {LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, +#endif + {LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, +#ifndef LM87_AIN1 + {LM87_SYSCTL_FAN1, "fan", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan}, + {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan_div}, +#define LM87_FANDIV_FLAG +#endif +#ifndef LM87_AIN2 + {LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan}, +#ifndef LM87_FANDIV_FLAG + {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan_div}, +#endif /* LM87_FANDIV_FLAG */ +#endif /* LM87_AIN2 */ +#ifdef LM87_EXT2 + {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, +#endif + {LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, + {LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, + {LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_alarms}, + {LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_analog_out}, + {LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_vid}, + {0} +}; + +int lm87_attach_adapter(struct i2c_adapter *adapter) +{ + int error; + struct i2c_client_address_data lm87_client_data; + + lm87_client_data.normal_i2c = addr_data.normal_i2c; + lm87_client_data.normal_i2c_range = addr_data.normal_i2c_range; + lm87_client_data.probe = addr_data.probe; + lm87_client_data.probe_range = addr_data.probe_range; + lm87_client_data.ignore = addr_data.ignore; + lm87_client_data.ignore_range = addr_data.ignore_range; + lm87_client_data.force = addr_data.forces->force; + + error = i2c_probe(adapter, &lm87_client_data, lm87_detect); + i2c_detect(adapter, &addr_data, lm87_detect); + + return error; +} + +static int lm87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct lm87_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access LM87_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm87_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm87_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &LM87_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (((lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + != 0x00) || + (lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02)) + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + type_name = "lm87"; + client_name = "LM87 chip"; + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = lm87_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + LM87_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the LM87 chip */ + lm87_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm87_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm87_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm87.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int lm87_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void lm87_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void lm87_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int lm87_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM87. It should set limits, etc. */ +void lm87_init_client(struct i2c_client *client) +{ + long vid; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + lm87_write_value(client, LM87_REG_CONFIG, 0x80); + + /* Setup Channel Mode register for configuration of monitoring + * Default is 00000000b + * bit 0 - Configures Fan 1/AIN 1 input (1 = AIN) + * bit 1 - Configures Fan 2/AIN 2 input (1 = AIN) + * bit 2 - Configures 2.5V&Vccp2/D2 input (1 = 2nd Therm.) + * bit 3 - Configures Vcc for 5V/3.3V reading (0 = 3.3V) + * bit 4 - Configures IRQ0 Enable if = 1 + * bit 5 - Configures IRQ1 Enable if = 1 + * bit 6 - Configures IRQ2 Enable if = 1 + * bit 7 - Configures VID/IRQ input as interrupts if = 1 + */ + +/* I know, not clean, but it works. :'p */ + lm87_write_value(client, LM87_REG_CHANNEL_MODE, +#ifdef LM87_AIN1 + 0x01 +#else +0 +#endif + | +#ifdef LM87_AIN2 + 0x02 +#else +0 +#endif + | +#ifdef LM87_EXT2 + 0x04 +#else +0 +#endif + | +#ifdef LM87_5V_VCC +0x08 +#else +0 +#endif + ); + + /* Set IN (voltage) initial limits to sane values +/- 5% */ + lm87_write_value(client, LM87_REG_IN_MIN(1),182); + lm87_write_value(client, LM87_REG_IN_MAX(1),202); + lm87_write_value(client, LM87_REG_IN_MIN(2),182); + lm87_write_value(client, LM87_REG_IN_MAX(2),202); + lm87_write_value(client, LM87_REG_IN_MIN(3),182); + lm87_write_value(client, LM87_REG_IN_MAX(3),202); + lm87_write_value(client, LM87_REG_IN_MIN(4),182); + lm87_write_value(client, LM87_REG_IN_MAX(4),202); + + /* Set CPU core voltage limits relative to vid readings */ + vid = (lm87_read_value(client, LM87_REG_VID_FAN_DIV) & 0x0f) + | ((lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4 ); + if ((vid == 0x1f) || (vid == 0x0f)) { + vid = 0; + } else if (vid > 0x0f) { + vid = (1275 - ((vid - 0x10) * 25))/10; + } else { + vid = 200 - (vid * 5); + } + lm87_write_value(client, LM87_REG_IN_MIN(0), + (u8)(0x0FF & (((1920/270)*(vid*95))/1000) )); + lm87_write_value(client, LM87_REG_IN_MAX(0), + (u8)(0x0FF & (((1920/270)*(vid*105))/1000) )); + lm87_write_value(client, LM87_REG_IN_MIN(5), + (u8)(0x0FF & (((1920/270)*(vid*95))/1000) )); + lm87_write_value(client, LM87_REG_IN_MAX(5), + (u8)(0x0FF & (((1920/270)*(vid*105))/1000) )); + + /* Set Temp initial limits to sane values */ + lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN)); +#ifdef LM87_EXT2 + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN)); +#endif + lm87_write_value(client, LM87_REG_INT_TEMP_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_INT_TEMP_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MIN)); + +#ifndef LM87_AIN1 + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + FAN_TO_REG(LM87_INIT_FAN_MIN, 2)); +#endif +#ifndef LM87_AIN2 + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + FAN_TO_REG(LM87_INIT_FAN_MIN, 2)); +#endif + + /* Start monitoring */ + lm87_write_value(client, LM87_REG_CONFIG, 0x01); +} + +void lm87_update_client(struct i2c_client *client) +{ + struct lm87_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ) || /* 1 sec cache */ + (jiffies < data->last_updated) || + !data->valid) { + for (i = 0; i <= 5; i++) { + data->in[i] = + lm87_read_value(client,LM87_REG_IN(i)); + data->in_min[i] = + lm87_read_value(client,LM87_REG_IN_MIN(i)); + data->in_max[i] = + lm87_read_value(client,LM87_REG_IN_MAX(i)); + } + data->ain1 = + lm87_read_value(client,LM87_REG_FAN1_AIN1); + data->ain1_min = + lm87_read_value(client,LM87_REG_AIN1_LOW); + data->ain1_max = + lm87_read_value(client,LM87_REG_FAN1_AIN1_LIMIT); + data->ain2 = + lm87_read_value(client,LM87_REG_FAN2_AIN2); + data->ain2_min = + lm87_read_value(client,LM87_REG_AIN2_LOW); + data->ain2_max = + lm87_read_value(client,LM87_REG_FAN2_AIN2_LIMIT); + + data->fan = + lm87_read_value(client, LM87_REG_FAN1_AIN1); + data->fan_min = + lm87_read_value(client, LM87_REG_FAN1_AIN1_LIMIT); + data->fan2 = + lm87_read_value(client, LM87_REG_FAN2_AIN2); + data->fan2_min = + lm87_read_value(client, LM87_REG_FAN2_AIN2_LIMIT); + + data->ext2_temp = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2); + data->ext_temp = + lm87_read_value(client, LM87_REG_EXT_TEMP_1); + data->int_temp = + lm87_read_value(client, LM87_REG_INT_TEMP); + + data->ext2_temp_max = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH); + data->ext2_temp_min = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW); + + data->ext_temp_max = + lm87_read_value(client, LM87_REG_EXT_TEMP_1_HIGH); + data->ext_temp_min = + lm87_read_value(client, LM87_REG_EXT_TEMP_1_LOW); + + data->int_temp_max = + lm87_read_value(client, LM87_REG_INT_TEMP_HIGH); + data->int_temp_min = + lm87_read_value(client, LM87_REG_INT_TEMP_LOW); + + i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + data->fan_div = (i >> 4) & 0x03; + data->fan2_div = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4; + data->alarms = + lm87_read_value(client, LM87_REG_INT1_STAT) + + (lm87_read_value(client, LM87_REG_INT2_STAT) << + 8); + data->analog_out = + lm87_read_value(client, LM87_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + long scales[6] = { 250, 270, +#ifdef LM87_5V_VCC +500, +#else +330, +#endif + 500, 1200, 270 }; + + struct lm87_data *data = client->data; + int nr = ctl_name - LM87_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = + ((long)data->in_min[nr] * scales[nr]) / 192; + results[1] = + ((long)data->in_max[nr] * scales[nr]) / 192; + results[2] = + ((long)data->in[nr] * scales[nr]) / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + (results[0] * 192) / scales[nr]; + lm87_write_value(client, LM87_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + (results[1] * 192) / scales[nr]; + lm87_write_value(client, LM87_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +#if defined (LM87_AIN1) || defined (LM87_AIN2) +void lm87_ain(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + if (ctl_name == LM87_SYSCTL_AIN1) { + results[0] = data->ain1_min; + results[1] = data->ain1_max; + results[2] = data->ain1; + } else { + results[0] = data->ain2_min; + results[1] = data->ain2_max; + results[2] = data->ain2; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (ctl_name == LM87_SYSCTL_AIN1) { + data->ain1_min = results[0]; + lm87_write_value(client, LM87_REG_AIN1_LOW, + data->ain1_min); + } else { + data->ain2_min = results[0]; + lm87_write_value(client, LM87_REG_AIN2_LOW, + data->ain2_min); + } + } + if (*nrels_mag >= 2) { + if (ctl_name == LM87_SYSCTL_AIN1) { + data->ain1_max = results[1]; + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + data->ain1_max); + } else { + data->ain2_max = results[1]; + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + data->ain2_max); + } + } + } +} +#endif + +void lm87_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + if (ctl_name == LM87_SYSCTL_FAN1) { + results[0] = FAN_FROM_REG(data->fan_min, + DIV_FROM_REG(data->fan_div)); + results[1] = FAN_FROM_REG(data->fan, + DIV_FROM_REG(data->fan_div)); + } else { + results[0] = FAN_FROM_REG(data->fan2_min, + DIV_FROM_REG(data->fan2_div)); + results[1] = FAN_FROM_REG(data->fan2, + DIV_FROM_REG(data->fan2_div)); + } + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 0) { + if (ctl_name == LM87_SYSCTL_FAN1) { + data->fan_min = FAN_TO_REG(results[0], + DIV_FROM_REG + (data->fan_div)); + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + data->fan_min); + } else { + data->fan2_min = FAN_TO_REG(results[0], + DIV_FROM_REG + (data->fan2_div)); + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + data->fan2_min); + } + } + } +} + + +void lm87_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) + { + lm87_update_client(client); + + /* find out which temp. is being requested */ + if (ctl_name == LM87_SYSCTL_TEMP3) + { + results[0] = TEMP_LIMIT_FROM_REG(data->ext2_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->ext2_temp_min); + results[2] = TEMP_FROM_REG(data->ext2_temp); + } + else if(ctl_name == LM87_SYSCTL_TEMP2) + { + results[0] = TEMP_LIMIT_FROM_REG(data->ext_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->ext_temp_min); + results[2] = TEMP_FROM_REG(data->ext_temp); + } + else if(ctl_name == LM87_SYSCTL_TEMP1) + { + results[0] = TEMP_LIMIT_FROM_REG(data->int_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->int_temp_min); + results[2] = TEMP_FROM_REG(data->int_temp); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (ctl_name == LM87_SYSCTL_TEMP3) { + data->ext2_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH, + data->ext2_temp_max); + } + if (ctl_name == LM87_SYSCTL_TEMP2) { + data->ext_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH, + data->int_temp_max); + } + if (ctl_name == LM87_SYSCTL_TEMP1) { + data->int_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_INT_TEMP_HIGH, + data->int_temp_max); + } + } + if (*nrels_mag >= 2) { + if (ctl_name == LM87_SYSCTL_TEMP3) { + data->ext2_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW, + data->ext2_temp_min); + } + if (ctl_name == LM87_SYSCTL_TEMP2) { + data->ext_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW, + data->int_temp_min); + } + if (ctl_name == LM87_SYSCTL_TEMP1) { + data->int_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_INT_TEMP_LOW, + data->int_temp_min); + } + } + } +} + +void lm87_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ +/* This gets a little hairy depending on the hardware config */ + + struct lm87_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); +#ifndef LM87_AIN1 + results[0] = DIV_FROM_REG(data->fan_div); +# ifndef LM87_AIN2 + results[1] = DIV_FROM_REG(data->fan2_div); + *nrels_mag = 2; +# else + *nrels_mag = 1; +# endif +#else /* Must be referring to fan 2 */ + results[0] = DIV_FROM_REG(data->fan2_div); + *nrels_mag = 1; +#endif + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm87_read_value(client, LM87_REG_VID_FAN_DIV); +/* Note: it's OK to change fan2 div even if fan2 isn't enabled */ +#ifndef LM87_AIN1 + if (*nrels_mag >= 2) { + data->fan2_div = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan2_div << 6); + } + if (*nrels_mag >= 1) { + data->fan_div = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div << 4); + lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); + } +#else /* Must be referring to fan 2 */ + if (*nrels_mag >= 1) { + data->fan2_div = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan2_div << 6); + lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); + } +#endif + } +} + +void lm87_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + lm87_write_value(client, LM87_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void lm87_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_lm87_init(void) +{ + int res; + + printk("lm87.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm87_initialized = 0; + + if ((res = i2c_add_driver(&LM87_driver))) { + printk + ("lm87.o: Driver registration failed, module not inserted.\n"); + lm87_cleanup(); + return res; + } + lm87_initialized++; + return 0; +} + +int __init lm87_cleanup(void) +{ + int res; + + if (lm87_initialized >= 1) { + if ((res = i2c_del_driver(&LM87_driver))) { + printk + ("lm87.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm87_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker , + and Stephen Rousset "); + +MODULE_DESCRIPTION("LM87 driver"); + +int init_module(void) +{ + return sensors_lm87_init(); +} + +int cleanup_module(void) +{ + return lm87_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/matorb.c b/drivers/sensors/matorb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/matorb.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,371 @@ +/* + matorb.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2E, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(matorb); + +/* Many MATORB constants specified below */ + + +/* Each client has this additional data */ +struct matorb_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_matorb_init(void); +static int __init matorb_cleanup(void); +static int matorb_attach_adapter(struct i2c_adapter *adapter); +static int matorb_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void matorb_init_client(struct i2c_client *client); +static int matorb_detach_client(struct i2c_client *client); +static int matorb_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void matorb_inc_use(struct i2c_client *client); +static void matorb_dec_use(struct i2c_client *client); +static int matorb_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void matorb_disp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void matorb_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver matorb_driver = { + /* name */ "Matrix Orbital LCD driver", + /* id */ I2C_DRIVERID_MATORB, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &matorb_attach_adapter, + /* detach_client */ &matorb_detach_client, + /* command */ &matorb_command, + /* inc_use */ &matorb_inc_use, + /* dec_use */ &matorb_dec_use +}; + +/* These files are created for each detected MATORB. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table matorb_dir_table_template[] = { + {MATORB_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &matorb_disp}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata matorb_initialized = 0; + +static int matorb_id = 0; + +int matorb_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, matorb_detect); +} + +/* This function is called by i2c_detect */ +int matorb_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct matorb_data *data; + int err = 0; + const char *type_name = "matorb"; + const char *client_name = "matorb"; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("matorb.o: matorb_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + goto ERROR0; + + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access matorb_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct matorb_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct matorb_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &matorb_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + cur = i2c_smbus_write_byte_data(new_client, 0x0FE, 0x58); /* clear screen */ + + printk("matorb.o: debug detect 0x%X\n", cur); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = matorb_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + matorb_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + matorb_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + kfree(new_client); + ERROR0: + return err; +} + +int matorb_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct matorb_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("matorb.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int matorb_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void matorb_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void matorb_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +#if 0 +/* All registers are word-sized, except for the configuration register. + MATORB uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int matorb_read_value(struct i2c_client *client, u8 reg) +{ + return -1; /* Doesn't support reads */ +} +#endif + +/* All registers are word-sized, except for the configuration register. + MATORB uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int matorb_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == 0) { + return i2c_smbus_write_byte(client, value); + } else { + return i2c_smbus_write_byte_data(client, reg, value); + } +} + +void matorb_init_client(struct i2c_client *client) +{ + /* Initialize the MATORB chip */ +} + +void matorb_update_client(struct i2c_client *client) +{ + struct matorb_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting matorb update\n"); +#endif + +/* nothing yet */ + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void matorb_disp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int i; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + matorb_update_client(client); + results[0] = 0; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + for (i = 1; i <= *nrels_mag; i++) { + matorb_write_value(client, 0, results[i - 1]); + } + } +} + +int __init sensors_matorb_init(void) +{ + int res; + + printk("matorb.o version %s (%s)\n", LM_VERSION, LM_DATE); + matorb_initialized = 0; + if ((res = i2c_add_driver(&matorb_driver))) { + printk + ("matorb.o: Driver registration failed, module not inserted.\n"); + matorb_cleanup(); + return res; + } + matorb_initialized++; + return 0; +} + +int __init matorb_cleanup(void) +{ + int res; + + if (matorb_initialized >= 1) { + if ((res = i2c_del_driver(&matorb_driver))) { + printk + ("matorb.o: Driver deregistration failed, module not removed.\n"); + return res; + } + matorb_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("MATORB driver"); + +int init_module(void) +{ + return sensors_matorb_init(); +} + +int cleanup_module(void) +{ + return matorb_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/maxilife.c b/drivers/sensors/maxilife.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/maxilife.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,1442 @@ +/* + maxilife.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999-2000 Fons Rademakers + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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 is the driver for the HP MaxiLife Health monitoring system + as used in the line of HP Kayak Workstation PC's. + + The driver supports the following MaxiLife firmware versions: + + 0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath) + 1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + 2) HP KAYAK XA (Pentium II Slot 1, monoprocessor) + + Currently firmware auto detection is not implemented. To use the + driver load it with the correct option for you Kayak. For example: + + insmod maxilife.o maxi_version=0 | 1 | 2 + + maxi_version=0 is the default + + This version of MaxiLife is called MaxiLife'98 and has been + succeeded by MaxiLife'99, see below. + + The new version of the driver also supports MaxiLife NBA (New BIOS + Architecture). This new MaxiLife controller provides a much cleaner + machine independent abstraction layer to the MaxiLife controller. + Instead of accessing directly registers (different for each revision) + one now accesses the sensors via unique mailbox tokens that do not + change between revisions. Also the quantities are already in physical + units (degrees, rpms, voltages, etc.) and don't need special conversion + formulas. This new MaxiLife is available on the new 2000 machines, + like the Kayak XU800 and XM600. This hardware is also autodetected. +*/ + +static const char *version_str = "2.00 29/2/2000 Fons Rademakers"; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + + +#undef AUTODETECT /* try to autodetect MaxiLife version */ +/*#define AUTODETECT*/ +#define NOWRITE /* don't allow writing to MaxiLife registers */ + +#ifdef AUTODETECT +#include +#include +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x10, 0x14, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(maxilife); + +/* Macro definitions */ +#define LOW(MyWord) ((u8) (MyWord)) +#define HIGH(MyWord) ((u8) (((u16)(MyWord) >> 8) & 0xFF)) + +/*----------------- MaxiLife'98 registers and conversion formulas ------------*/ +#define MAXI_REG_TEMP(nr) (0x60 + (nr)) + +#define MAXI_REG_FAN(nr) (0x65 + (nr)) +#define MAXI_REG_FAN_MIN(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab) +#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3) +#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9) + +#define MAXI_REG_PLL 0xb9 +#define MAXI_REG_PLL_MIN 0xba +#define MAXI_REG_PLL_MAX 0xbb + +#define MAXI_REG_VID(nr) ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \ + (nr)==2 ? 0xd4 : 0xc5) +#define MAXI_REG_VID_MIN(nr) MAXI_REG_VID(nr)+1 +#define MAXI_REG_VID_MAX(nr) MAXI_REG_VID(nr)+2 + +#define MAXI_REG_DIAG_RT1 0x2c +#define MAXI_REG_DIAG_RT2 0x2d + +#define MAXI_REG_BIOS_CTRL 0x2a + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + + /* 0xfe: fan off, 0xff: stopped (alarm) */ + /* 19531 / val * 60 == 1171860 / val */ +#define FAN_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : \ + (val)==0x00 ? -1 : (1171860 / (val))) + +extern inline u8 FAN_TO_REG(long rpm) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1171860 + rpm / 2) / (rpm), 1, 254); +} + +#define TEMP_FROM_REG(val) ((val) * 5) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val+2) / 5),0,0xff) +#define PLL_FROM_REG(val) (((val) * 1000) / 32) +#define PLL_TO_REG(val) (SENSORS_LIMIT((((val) * 32 + 500) / 1000),\ + 0,0xff)) +#define VID_FROM_REG(val) ((val) ? (((val) * 27390) / 256) + 3208 : 0) +#define VID_TO_REG(val) (SENSORS_LIMIT((((val) - 3208) * 256) / 27390, \ + 0,255)) +#define ALARMS_FROM_REG(val) (val) + +/*----------------- MaxiLife'99 mailbox and token definitions ----------------*/ +/* MaxiLife mailbox data register map */ +#define MAXI_REG_MBX_STATUS 0x5a +#define MAXI_REG_MBX_CMD 0x5b +#define MAXI_REG_MBX_TOKEN_H 0x5c +#define MAXI_REG_MBX_TOKEN_L 0x5d +#define MAXI_REG_MBX_DATA 0x60 + +/* Mailbox status register definition */ +#define MAXI_STAT_IDLE 0xff +#define MAXI_STAT_OK 0x00 +#define MAXI_STAT_BUSY 0x0b +/* other values not used */ + +/* Mailbox command register opcodes */ +#define MAXI_CMD_READ 0x02 +#define MAXI_CMD_WRITE 0x03 +/* other values not used */ + +/* MaxiLife NBA Hardware monitoring tokens */ + +/* Alarm tokens (0x1xxx) */ +#define MAXI_TOK_ALARM(nr) (0x1000 + (nr)) +#define MAXI_TOK_ALARM_EVENT 0x1000 +#define MAXI_TOK_ALARM_FAN 0x1001 +#define MAXI_TOK_ALARM_TEMP 0x1002 +#define MAXI_TOK_ALARM_VID 0x1003 /* voltages */ +#define MAXI_TOK_ALARM_AVID 0x1004 /* additional voltages */ +#define MAXI_TOK_ALARM_PWR 0x1101 /* power supply glitch */ + +/* Fan status tokens (0x20xx) */ +#define MAXI_TOK_FAN(nr) (0x2000 + (nr)) +#define MAXI_TOK_FAN_CPU 0x2000 +#define MAXI_TOK_FAN_PCI 0x2001 +#define MAXI_TOK_FAN_HDD 0x2002 /* hard disk bay fan */ +#define MAXI_TOK_FAN_SINK 0x2003 /* heatsink */ + +/* Temperature status tokens (0x21xx) */ +#define MAXI_TOK_TEMP(nr) (0x2100 + (nr)) +#define MAXI_TOK_TEMP_CPU1 0x2100 +#define MAXI_TOK_TEMP_CPU2 0x2101 +#define MAXI_TOK_TEMP_PCI 0x2102 /* PCI/ambient temp */ +#define MAXI_TOK_TEMP_HDD 0x2103 /* hard disk bay temp */ +#define MAXI_TOK_TEMP_MEM 0x2104 /* mother board temp */ +#define MAXI_TOK_TEMP_CPU 0x2105 /* CPU reference temp */ + +/* Voltage status tokens (0x22xx) */ +#define MAXI_TOK_VID(nr) (0x2200 + (nr)) +#define MAXI_TOK_VID_12 0x2200 /* +12 volt */ +#define MAXI_TOK_VID_CPU1 0x2201 /* cpu 1 voltage */ +#define MAXI_TOK_VID_CPU2 0x2202 /* cpu 2 voltage */ +#define MAXI_TOK_VID_L2 0x2203 /* level 2 cache voltage */ +#define MAXI_TOK_VID_M12 0x2204 /* -12 volt */ + +/* Additive voltage status tokens (0x23xx) */ +#define MAXI_TOK_AVID(nr) (0x2300 + (nr)) +#define MAXI_TOK_AVID_15 0x2300 /* 1.5 volt */ +#define MAXI_TOK_AVID_18 0x2301 /* 1.8 volt */ +#define MAXI_TOK_AVID_25 0x2302 /* 2.5 volt */ +#define MAXI_TOK_AVID_33 0x2303 /* 3.3 volt */ +#define MAXI_TOK_AVID_5 0x2304 /* 5 volt */ +#define MAXI_TOK_AVID_M5 0x2305 /* -5 volt */ +#define MAXI_TOK_AVID_BAT 0x2306 /* battery voltage */ + +/* Threshold tokens (0x3xxx) */ +#define MAXI_TOK_MIN(token) ((token) + 0x1000) +#define MAXI_TOK_MAX(token) ((token) + 0x1800) + +/* LCD Panel (0x4xxx) */ +#define MAXI_TOK_LCD(nr) (0x4000 + (nr)) +#define MAXI_TOK_LCD_LINE1 0x4000 +#define MAXI_TOK_LCD_LINE2 0x4001 +#define MAXI_TOK_LCD_LINE3 0x4002 +#define MAXI_TOK_LCD_LINE4 0x4003 + + /* 0xfe: fan off, 0xff: stopped (alarm) */ + /* or not available */ +#define FAN99_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : ((val)*39)) + + /* when no CPU2 temp is 127 (0x7f) */ +#define TEMP99_FROM_REG(val) ((val)==0x7f ? -1 : (val)==0xff ? -1 : (val)) + +#define VID99_FROM_REG(nr,val) ((val)==0xff ? 0 : \ + (nr)==1 ? ((val) * 608) : \ + (nr)==2 ? ((val) * 160) : \ + (nr)==3 ? ((val) * 160) : \ + (nr)==4 ? (val) /* no formula spcified */ : \ + (nr)==5 ? ((val) * 823 - 149140) : 0) + + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* The following product codenames apply: + Cristal/Geronimo: HP KAYAK XU/XAs + (Dual Pentium II Slot 1, Deschutes/Klamath) + Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor) + NBA: New BIOS Architecture, Kayak XU800, XM600, ... */ + +enum maxi_type { cristal, cognac, ashaki, nba }; +enum sensor_type { fan, temp, vid, pll, lcd, alarm }; + +/* For each registered MaxiLife controller, we need to keep some data in + memory. That data is pointed to by maxi_list[NR]->data. The structure + itself is dynamically allocated, at the same time when a new MaxiLife + client is allocated. We assume MaxiLife will only be present on the + SMBus and not on the ISA bus. */ +struct maxi_data { + struct semaphore lock; + int sysctl_id; + enum maxi_type type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 fan[4]; /* Register value */ + u8 fan_min[4]; /* Register value */ + u8 fan_speed[4]; /* Register value */ + u8 fan_div[4]; /* Static value */ + u8 temp[6]; /* Register value */ + u8 temp_max[6]; /* Static value */ + u8 temp_hyst[6]; /* Static value */ + u8 pll; /* Register value */ + u8 pll_min; /* Register value */ + u8 pll_max; /* register value */ + u8 vid[5]; /* Register value */ + u8 vid_min[5]; /* Register value */ + u8 vid_max[5]; /* Register value */ + u8 lcd[4][17]; /* Four LCD lines */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_maxi_init(void); +static int __init maxi_cleanup(void); + +static int maxi_attach_adapter(struct i2c_adapter *adapter); +static int maxi_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int maxi_detach_client(struct i2c_client *client); +static int maxi_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void maxi_inc_use(struct i2c_client *client); +static void maxi_dec_use(struct i2c_client *client); + +static int maxi_read_value(struct i2c_client *client, u8 register); +static int maxi_read_token(struct i2c_client *client, u16 token); +#ifndef NOWRITE +static int maxi_write_value(struct i2c_client *client, u8 register, + u8 value); +#endif +static int maxi_write_token_loop(struct i2c_client *client, u16 token, + u8 len, u8 * values); + +static void maxi_update_client(struct i2c_client *client); +static void maxi99_update_client(struct i2c_client *client, + enum sensor_type sensor, int which); +static void maxi_init_client(struct i2c_client *client); + +static void maxi_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_pll(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_lcd(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* The driver. I choose to use type i2c_driver, as at is identical to + the smbus_driver. */ +static struct i2c_driver maxi_driver = { + /* name */ "HP MaxiLife driver", + /* id */ I2C_DRIVERID_MAXILIFE, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &maxi_attach_adapter, + /* detach_client */ &maxi_detach_client, + /* command */ &maxi_command, + /* inc_use */ &maxi_inc_use, + /* dec_use */ &maxi_dec_use +}; + +/* Used by maxi_init/cleanup */ +static int __initdata maxi_initialized = 0; + +static int maxi_id = 0; + +/* Default firmware version. Use module option "maxi_version" + to set desired version. Auto detect is not yet working */ +static int maxi_version = cristal; + +/* The /proc/sys entries */ +/* These files are created for each detected MaxiLife processor. + This is just a template; though at first sight, you might think we + could use a statically allocated list, we need some way to get back + to the parent - which is done through one of the 'extra' fields + which are initialized when a new copy is allocated. */ +static ctl_table maxi_dir_table_template[] = { + {MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_pll}, + {MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_alarms}, + {0} +}; + +/* This function is called when: + - maxi_driver is inserted (when this module is loaded), for each + available adapter + - when a new adapter is inserted (and maxi_driver is still present) */ +int maxi_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, maxi_detect); +} + +/* This function is called by i2c_detect */ +int maxi_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + struct i2c_client *new_client; + struct maxi_data *data; + enum maxi_type type; + int i, j, err = 0; + const char *type_name, *client_name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access maxi_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct maxi_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + /* Fill the new client structure with data */ + data = (struct maxi_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &maxi_driver; + new_client->flags = 0; + + /* Now we do the remaining detection. */ + if (kind < 0) { + if (i2c_smbus_read_byte_data + (new_client, MAXI_REG_MBX_STATUS) < 0) + goto ERROR2; + } + + /* Determine the chip type - only one kind supported */ + if (kind <= 0) + kind = maxilife; + + if (kind == maxilife) { + /* Detect if the machine has a MaxiLife NBA controller. + The right way to perform this check is to do a read/modify/write + on register MbxStatus (5A): + - Read 5A (value 0 for non-NBA firmware, FF (MbxIdle on NBA-firmware) + - Write 55 on 5A, then read back 5A + Non-NBA firmware: value is 55 (reg 5A is a standard writable reg) + NBA firmaware: value is FF (write-protect on MbxStatus active) */ + int stat; + i2c_smbus_write_byte_data(new_client, MAXI_REG_MBX_STATUS, + 0x55); + stat = + i2c_smbus_read_byte_data(new_client, + MAXI_REG_MBX_STATUS); + + /*if (stat == MAXI_STAT_IDLE || stat == MAXI_STAT_OK) */ + if (stat != 0x55) + maxi_version = nba; +#ifdef AUTODETECT + else { + /* The right way to get the platform info is to read the firmware + revision from serial EEPROM (addr=0x54), at offset 0x0045. + This is a string as: + "CG 00.04" -> Cristal [XU] / Geronimo [XAs] + "CO 00.03" -> Cognac [XU] + "AS 00.01" -> Ashaki [XA] */ +#if 0 + int biosctl; + biosctl = + i2c_smbus_read_byte_data(new_client, + MAXI_REG_BIOS_CTRL); + i2c_smbus_write_byte_data(new_client, + MAXI_REG_BIOS_CTRL, + biosctl | 4); + err = eeprom_read_byte_data(adapter, 0x54, 0x45); + i2c_smbus_write_byte_data(new_client, + MAXI_REG_BIOS_CTRL, + biosctl); +#endif + int i; + char *biosmem, *bm; + bm = biosmem = ioremap(0xe0000, 0x20000); + if (biosmem) { + printk("begin of bios search\n"); + for (i = 0; i < 0x20000; i++) { + if (*bm == 'C') { + char *s = bm; + while (s && isprint(*s)) { + printk("%c", *s); + s++; + } + printk("\n"); + if (!strncmp + (bm, "CG 00.04", 8)) { + maxi_version = + cristal; + printk + ("maxilife: found MaxiLife Rev CG 00.04\n"); + break; + } + if (!strncmp + (bm, "CO 00.03", 8)) { + maxi_version = + cognac; + printk + ("maxilife: found MaxiLife Rev CO 00.03\n"); + break; + } + } + if (*bm == 'A' && *(bm + 1) == 'S') { + char *s = bm; + while (s && isprint(*s)) { + printk("%c", *s); + s++; + } + printk("\n"); + if (!strncmp + (bm, "AS 00.01", 8)) { + maxi_version = + ashaki; + printk + ("maxilife: found MaxiLife Rev AS 00.01\n"); + break; + } + } + bm++; + } + printk("end of bios search\n"); + } else + printk("could not map bios memory\n"); + } +#endif + + if (maxi_version == cristal) { + type = cristal; + type_name = "maxilife-cg"; + client_name = "HP MaxiLife Rev CG 00.04"; + printk + ("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n"); + } else if (maxi_version == cognac) { + type = cognac; + type_name = "maxilife-co"; + client_name = "HP MaxiLife Rev CO 00.03"; + printk + ("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n"); + } else if (maxi_version == ashaki) { + type = ashaki; + type_name = "maxilife-as"; + client_name = "HP MaxiLife Rev AS 00.01"; + printk + ("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n"); + } else if (maxi_version == nba) { + type = nba; + type_name = "maxilife-nba"; + client_name = "HP MaxiLife NBA"; + printk("maxilife: HP KAYAK XU800/XM600\n"); + } else { +#ifdef AUTODETECT + printk + ("maxilife: Warning: probed non-maxilife chip?!? (%x)\n", + err); +#else + printk + ("maxilife: Error: specified wrong maxi_version (%d)\n", + maxi_version); +#endif + goto ERROR2; + } + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + ((struct maxi_data *) (new_client->data))->type = type; + + for (i = 0; i < 4; i++) + for (j = 0; j < 17; j++) + ((struct maxi_data *) (new_client->data))-> + lcd[i][j] = (u8) 0; + + new_client->id = maxi_id++; + + data->valid = 0; + init_MUTEX(&data->lock); + init_MUTEX(&data->update_lock); + + /* Tell i2c-core that a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* Register a new directory entry with module sensors */ + if ((err = i2c_register_entry(new_client, type_name, + maxi_dir_table_template, + THIS_MODULE)) < 0) + goto ERROR4; + data->sysctl_id = err; + + /* Initialize the MaxiLife chip */ + maxi_init_client(new_client); + return 0; + + /* OK, this is not exactly good programming practice, usually. + But it is very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR2: + kfree(new_client); + ERROR0: + return err; +} + +/* This function is called whenever a client should be removed: + - maxi_driver is removed (when this module is unloaded) + - when an adapter is removed which has a maxi client (and maxi_driver + is still present). */ +int maxi_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct maxi_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("maxilife: Client deregistration failed, client not detached.\n"); + return err; + } + kfree(client); + return 0; +} + +/* No commands defined yet */ +int maxi_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void maxi_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void maxi_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Read byte from specified register (-1 in case of error, value otherwise). */ +int maxi_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* Read the byte value for a MaxiLife token (-1 in case of error, value otherwise */ +int maxi_read_token(struct i2c_client *client, u16 token) +{ + u8 lowToken, highToken; + int error, value; + + lowToken = LOW(token); + highToken = HIGH(token); + + /* Set mailbox status register to idle state. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + /* Check for mailbox idle state. */ + error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + if (error != MAXI_STAT_IDLE) + return -1; + + /* Write the most significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, + highToken); + if (error < 0) + return error; + + /* Write the least significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, + lowToken); + if (error < 0) + return error; + + /* Write the read token opcode to the mailbox. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, + MAXI_CMD_READ); + if (error < 0) + return error; + + /* Check for transaction completion */ + do { + error = + i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + } while (error == MAXI_STAT_BUSY); + if (error != MAXI_STAT_OK) + return -1; + + /* Read the value of the token. */ + value = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_DATA); + if (value == -1) + return -1; + + /* set mailbox status to idle to complete transaction. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + return value; +} + +#ifndef NOWRITE +/* Write byte to specified register (-1 in case of error, 0 otherwise). */ +int maxi_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} +#endif + +/* Write a set of len byte values to MaxiLife token (-1 in case of error, 0 otherwise). */ +int maxi_write_token_loop(struct i2c_client *client, u16 token, u8 len, + u8 * values) +{ + u8 lowToken, highToken, bCounter; + int error; + + lowToken = LOW(token); + highToken = HIGH(token); + + /* Set mailbox status register to idle state. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + /* Check for mailbox idle state. */ + error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + if (error != MAXI_STAT_IDLE) + return -1; + + for (bCounter = 0; (bCounter < len && bCounter < 32); bCounter++) { + error = + i2c_smbus_write_byte_data(client, + (u8) (MAXI_REG_MBX_DATA + + bCounter), + values[bCounter]); + if (error < 0) + return error; + } + + /* Write the most significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, + highToken); + if (error < 0) + return error; + + /* Write the least significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, + lowToken); + if (error < 0) + return error; + + /* Write the write token opcode to the mailbox. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, + MAXI_CMD_WRITE); + if (error < 0) + return error; + + /* Check for transaction completion */ + do { + error = + i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + } while (error == MAXI_STAT_BUSY); + if (error != MAXI_STAT_OK) + return -1; + + /* set mailbox status to idle to complete transaction. */ + return i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); +} + +/* Called when we have found a new MaxiLife. It should set limits, etc. */ +void maxi_init_client(struct i2c_client *client) +{ + struct maxi_data *data = client->data; + + if (data->type == nba) { + strcpy(data->lcd[2], " Linux MaxiLife"); + maxi_write_token_loop(client, MAXI_TOK_LCD(2), + strlen(data->lcd[2]) + 1, + data->lcd[2]); + } +} + +void maxi_update_client(struct i2c_client *client) +{ + struct maxi_data *data = client->data; + int i; + + if (data->type == nba) { + printk + ("maxi_update_client should never be called by nba\n"); + return; + } + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("maxilife: Starting MaxiLife update\n"); +#endif + for (i = 0; i < 5; i++) + data->temp[i] = + maxi_read_value(client, MAXI_REG_TEMP(i)); + switch (data->type) { + case cristal: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max BX chipset temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + case cognac: + data->temp_max[0] = 120; /* max CPU1 temp */ + data->temp_hyst[0] = 110; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max CPU2 temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max reference CPU temp */ + data->temp_hyst[4] = 110; + break; + + case ashaki: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp[2] = 0; /* not valid */ + data->temp_max[2] = 0; + data->temp_hyst[2] = 0; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + data->temp[5] = 0; /* only used by MaxiLife'99 */ + data->temp_max[5] = 0; + data->temp_hyst[5] = 0; + + for (i = 0; i < 3; i++) { + data->fan[i] = + maxi_read_value(client, MAXI_REG_FAN(i)); + data->fan_speed[i] = + maxi_read_value(client, MAXI_REG_FAN_SPEED(i)); + data->fan_div[i] = 4; + if (data->type == ashaki) + data->fan_min[i] = + maxi_read_value(client, + MAXI_REG_FAN_MINAS(i)); + else + data->fan_min[i] = + maxi_read_value(client, + MAXI_REG_FAN_MIN(i)); + } + data->fan[3] = 0xff; /* only used by MaxiLife'99 */ + data->fan_speed[3] = 0; + data->fan_div[3] = 4; /* avoid possible /0 */ + data->fan_min[3] = 0; + + data->pll = maxi_read_value(client, MAXI_REG_PLL); + data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN); + data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX); + + for (i = 0; i < 4; i++) { + data->vid[i] = + maxi_read_value(client, MAXI_REG_VID(i)); + data->vid_min[i] = + maxi_read_value(client, MAXI_REG_VID_MIN(i)); + data->vid_max[i] = + maxi_read_value(client, MAXI_REG_VID_MAX(i)); + } + switch (data->type) { + case cristal: + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + case cognac: + break; + + case ashaki: + data->vid[1] = 0; /* no voltage CPU 2 */ + data->vid_min[1] = 0; + data->vid_max[1] = 0; + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + data->vid[4] = 0; /* only used by MaxliLife'99 */ + data->vid_min[4] = 0; + data->vid_max[4] = 0; + + data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) + + (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +void maxi99_update_client(struct i2c_client *client, + enum sensor_type sensor, int which) +{ + static unsigned long last_updated[6][6]; /* sensor, which */ + struct maxi_data *data = client->data; + + down(&data->update_lock); + + /*maxi_write_token_loop(client, MAXI_TOK_LCD_LINE3, 13, "Linux 2.2.13"); */ + + if ((jiffies - last_updated[sensor][which] > 2 * HZ) || + (jiffies < last_updated[sensor][which] + || !last_updated[sensor][which])) { + + int tmp, i; + + switch (sensor) { + case fan: + for (i = 0; i < 4; i++) { + if (i == which) { + tmp = + maxi_read_token(client, + MAXI_TOK_FAN + (i)); + data->fan[i] = + maxi_read_token(client, + MAXI_TOK_FAN + (i)); + data->fan_speed[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_FAN + (i))); + data->fan_div[i] = 1; + data->fan_min[i] = 0; + } + } + break; + + case temp: + for (i = 0; i < 6; i++) { + if (i == which) { + data->temp[i] = + maxi_read_token(client, + MAXI_TOK_TEMP + (i)); + data->temp_max[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_TEMP + (i))); + data->temp_hyst[i] = + data->temp_max[i] - 5; + } + } + break; + + case vid: + for (i = 0; i < 5; i++) { + if (i == which) { + data->vid[i] = + maxi_read_token(client, + MAXI_TOK_VID + (i)); + data->vid_min[i] = + maxi_read_token(client, + MAXI_TOK_MIN + (MAXI_TOK_VID + (i))); + data->vid_max[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_VID + (i))); + } + } + break; + + case pll: + data->pll = 0; + data->pll_min = 0; + data->pll_max = 0; + break; + + case alarm: + data->alarms = + (maxi_read_token(client, MAXI_TOK_ALARM_EVENT) + << 8); + if (data->alarms) + data->alarms += + data->alarms == + (1 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_FAN) + : data->alarms == + (2 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_VID) + : data->alarms == + (4 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_TEMP) + : data->alarms == + (8 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_FAN) + : 0; + break; + + default: + printk("maxilife: Unknown sensor type\n"); + } + + last_updated[sensor][which] = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the data + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void maxi_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_fan(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1]); + results[1] = data->fan_div[nr - 1]; + results[2] = FAN_FROM_REG(data->fan[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } +#endif + } +} + +void maxi99_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, fan, nr - 1); + results[0] = FAN99_FROM_REG(data->fan_min[nr - 1]); /* min rpm */ + results[1] = data->fan_div[nr - 1]; /* divisor */ + results[2] = FAN99_FROM_REG(data->fan[nr - 1]); /* rpm */ + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + /* still to do */ + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } +#endif + } +} + +void maxi_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_temp(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_max[nr - 1]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr - 1]); + results[2] = TEMP_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* temperature range can not be changed */ + } +} + +void maxi99_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, temp, nr - 1); + results[0] = TEMP99_FROM_REG(data->temp_max[nr - 1]); + results[1] = TEMP99_FROM_REG(data->temp_hyst[nr - 1]); + results[2] = TEMP99_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* temperature range can not be changed */ + } +} + +void maxi_pll(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + if (data->type == nba) + maxi99_update_client(client, pll, 0); + else + maxi_update_client(client); + results[0] = PLL_FROM_REG(data->pll_min); + results[1] = PLL_FROM_REG(data->pll_max); + results[2] = PLL_FROM_REG(data->pll); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->pll_min = PLL_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_PLL_MIN, + data->pll_min); + } + if (*nrels_mag >= 2) { + data->pll_max = PLL_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_PLL_MAX, + data->pll_max); + } +#endif + } +} + +void maxi_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_vid(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_VID1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 4; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = VID_FROM_REG(data->vid_min[nr - 1]); + results[1] = VID_FROM_REG(data->vid_max[nr - 1]); + results[2] = VID_FROM_REG(data->vid[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->vid_min[nr - 1] = VID_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_VID_MIN(nr), + data->vid_min[nr - 1]); + } + if (*nrels_mag >= 2) { + data->vid_max[nr - 1] = VID_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_VID_MAX(nr), + data->vid_max[nr - 1]); + } +#endif + } +} + +void maxi99_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr = ctl_name - MAXI_SYSCTL_VID1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 4; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, vid, nr - 1); + results[0] = VID99_FROM_REG(nr, data->vid_min[nr - 1]); + results[1] = VID99_FROM_REG(nr, data->vid_max[nr - 1]); + results[2] = VID99_FROM_REG(nr, data->vid[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + /* still to do */ + if (*nrels_mag >= 1) { + data->vid_min[nr - 1] = VID_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_VID_MIN(nr), + data->vid_min[nr - 1]); + } + if (*nrels_mag >= 2) { + data->vid_max[nr - 1] = VID_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_VID_MAX(nr), + data->vid_max[nr - 1]); + } +#endif + } +} + +void maxi_lcd(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + /* Allows writing and reading from LCD display */ + + struct maxi_data *data = client->data; + int nr; + + if (data->type != nba) + return; + + nr = ctl_name - MAXI_SYSCTL_LCD1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = *((long *) &data->lcd[nr - 1][0]); + results[1] = *((long *) &data->lcd[nr - 1][4]); + results[2] = *((long *) &data->lcd[nr - 1][8]); + results[3] = *((long *) &data->lcd[nr - 1][12]); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* + Writing a string to line 3 of the LCD can be done like: + echo -n "Linux MaxiLife" | od -A n -l > \ + /proc/sys/dev/sensors/maxilife-nba-i2c-0-14/lcd3 + */ + if (*nrels_mag >= 1) + *((long *) &data->lcd[nr - 1][0]) = results[0]; + if (*nrels_mag >= 2) + *((long *) &data->lcd[nr - 1][4]) = results[1]; + if (*nrels_mag >= 3) + *((long *) &data->lcd[nr - 1][8]) = results[2]; + if (*nrels_mag >= 4) + *((long *) &data->lcd[nr - 1][12]) = results[3]; + maxi_write_token_loop(client, MAXI_TOK_LCD(nr - 1), + strlen(data->lcd[nr - 1]) + 1, + data->lcd[nr - 1]); +#if 0 + if (*nrels_mag >= 1) + printk("nr=%d, result[0] = %.4s\n", nr, + (char *) &results[0]); + if (*nrels_mag >= 2) + printk("nr=%d, result[1] = %.4s\n", nr, + (char *) &results[1]); + if (*nrels_mag >= 3) + printk("nr=%d, result[2] = %.4s\n", nr, + (char *) &results[2]); + if (*nrels_mag >= 4) + printk("nr=%d, result[3] = %.4s\n", nr, + (char *) &results[3]); +#endif + } + +} + +void maxi_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + if (data->type == nba) + maxi99_update_client(client, alarm, 0); + else + maxi_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +int __init sensors_maxi_init(void) +{ + int res; + + printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str, + LM_VERSION, LM_DATE); + maxi_initialized = 0; + + if ((res = i2c_add_driver(&maxi_driver))) { + printk + ("maxilife: Driver registration failed, module not inserted.\n"); + maxi_cleanup(); + return res; + } + maxi_initialized++; + return 0; +} + +int __init maxi_cleanup(void) +{ + int res; + + if (maxi_initialized >= 1) { + if ((res = i2c_del_driver(&maxi_driver))) { + printk + ("maxilife: Driver deregistration failed, module not removed.\n"); + return res; + } + maxi_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Fons Rademakers "); +MODULE_DESCRIPTION("HP MaxiLife driver"); +MODULE_PARM(maxi_version, "i"); +MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version"); + +int init_module(void) +{ + return sensors_maxi_init(); +} + +int cleanup_module(void) +{ + return maxi_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/mtp008.c b/drivers/sensors/mtp008.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/mtp008.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,1248 @@ +/* + mtp008.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Kris Van Hees + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {SENSORS_I2C_END}; +static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END}; +static unsigned int normal_isa[] = {SENSORS_ISA_END}; +static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; + +/* Insmod parameters */ +SENSORS_INSMOD_1(mtp008); + +/* The MTP008 registers */ +/* in0 .. in6 */ +#define MTP008_REG_IN(nr) (0x20 + (nr)) +#define MTP008_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define MTP008_REG_IN_MIN(nr) (0x2c + (nr) * 2) + +/* temp1 */ +#define MTP008_REG_TEMP 0x27 +#define MTP008_REG_TEMP_MAX 0x39 +#define MTP008_REG_TEMP_MIN 0x3a + +/* fan1 .. fan3 */ +#define MTP008_REG_FAN(nr) (0x27 + (nr)) +#define MTP008_REG_FAN_MIN(nr) (0x3a + (nr)) + +#define MTP008_REG_CONFIG 0x40 +#define MTP008_REG_INT_STAT1 0x41 +#define MTP008_REG_INT_STAT2 0x42 + +#define MTP008_REG_SMI_MASK1 0x43 +#define MTP008_REG_SMI_MASK2 0x44 + +#define MTP008_REG_NMI_MASK1 0x45 +#define MTP008_REG_NMI_MASK2 0x46 + +#define MTP008_REG_VID_FANDIV 0x47 + +#define MTP008_REG_I2C_ADDR 0x48 + +#define MTP008_REG_RESET_VID4 0x49 + +#define MTP008_REG_OVT_PROP 0x50 + +#define MTP008_REG_BEEP_CTRL1 0x51 +#define MTP008_REG_BEEP_CTRL2 0x52 + +/* pwm1 .. pwm3 nr range 1-3 */ +#define MTP008_REG_PWM_CTRL(nr) (0x52 + (nr)) + +#define MTP008_REG_PIN_CTRL1 0x56 +#define MTP008_REG_PIN_CTRL2 0x57 + +#define MTP008_REG_CHIPID 0x58 + +/* + * Pin control register configuration constants. + */ +#define MTP008_CFG_VT1_PII 0x08 +#define MTP008_CFG_VT2_AIN 0x00 +#define MTP008_CFG_VT2_VT 0x03 +#define MTP008_CFG_VT2_PII 0x04 +#define MTP008_CFG_VT2_MASK 0x06 +#define MTP008_CFG_VT3_VT 0x01 + +/* sensor pin types */ +#define VOLTAGE 1 +#define THERMISTOR 2 +#define PIIDIODE 3 + +/* + * Conversion routines and macros. Rounding and limit checking is only done on + * the TO_REG variants. + * + * Note that IN values are expressed as 100 times the actual voltage to avoid + * having to use floating point values. As such, IN values are between 0 and + * 409 (0V to 4.096V). + */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +/* + * The fan cotation count (as stored in the register) is calculated using the + * following formula: + * count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div) + * and the rpm is therefore: + * rpm = 1350000 / (count * div) + */ +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + + return SENSORS_LIMIT( + (1350000 + rpm * div / 2) / (rpm * div), + 1, 254 + ); +} + +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 \ + : (val) == 255 ? 0 \ + : 1350000 / \ + ((val) * (div)) \ + ) + +/* + * Temperatures are stored as two's complement values of the Celsius value. It + * actually uses 10 times the Celsius value to avoid using floating point + * values. + */ +#define TEMP_TO_REG(val) ( \ + (val) < 0 \ + ? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \ + : SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \ + ) +#define TEMP_FROM_REG(val) ( \ + ( \ + (val) > 0x80 ? (val) - 0x100 \ + : (val) \ + ) * 10 \ + ) + +/* + * VCORE voltage: + * 0x00 to 0x0f = 2.05 to 1.30 (0.05 per unit) + * 0x10 to 0x1e = 3.50 to 2.10 (0.10 per unit) + * 0x1f = No CPU + */ +#define VID_FROM_REG(val) ((val) == 0x1f \ + ? 0 \ + : (val) < 0x10 ? 205 - (val) * 5 \ + : 510 - (val) * 10) + +/* + * Fan divider. + */ +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val) == 8 ? 3 \ + : (val) == 4 ? 2 \ + : (val) == 2 ? 1 \ + : 0) + +/* + * Alarms (interrupt status). + */ +#define ALARMS_FROM_REG(val) (val) + +/* + * Beep controls. + */ +#define BEEPS_FROM_REG(val) (val) +#define BEEPS_TO_REG(val) (val) + +/* + * PWM control. nr range 1 to 3 + */ +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (val) +#define PWMENABLE_FROM_REG(nr, val) (((val) >> ((nr) + 3)) & 1) + +/* Initial limits */ +#define MTP008_INIT_IN_0 (vid) /* VCore 1 */ +#define MTP008_INIT_IN_1 330 /* +3.3V */ +#define MTP008_INIT_IN_2 (1200 * 10 / 38) /* +12V */ +#define MTP008_INIT_IN_3 (vid) /* VCore 2 */ +#define MTP008_INIT_IN_5 ((11861 + 7 * (-1200)) / 36) /* -12V */ +#define MTP008_INIT_IN_6 150 /* Vtt */ + +#define MTP008_INIT_IN_PCT 10 + +#define MTP008_INIT_IN_MIN_0 (MTP008_INIT_IN_0 - \ + MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_0 (MTP008_INIT_IN_0 + \ + MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_1 (MTP008_INIT_IN_1 - \ + MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_1 (MTP008_INIT_IN_1 + \ + MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_2 (MTP008_INIT_IN_2 - \ + MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_2 (MTP008_INIT_IN_2 + \ + MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_3 (MTP008_INIT_IN_3 - \ + MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_3 (MTP008_INIT_IN_3 + \ + MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100) + +#define MTP008_INIT_IN_MIN_5 (MTP008_INIT_IN_5 - \ + MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_5 (MTP008_INIT_IN_5 + \ + MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_6 (MTP008_INIT_IN_6 - \ + MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_6 (MTP008_INIT_IN_6 + \ + MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100) + +#define MTP008_INIT_FAN_MIN_1 3000 +#define MTP008_INIT_FAN_MIN_2 3000 +#define MTP008_INIT_FAN_MIN_3 3000 + +#define MTP008_INIT_TEMP_OVER 700 /* 70 Celsius */ +#define MTP008_INIT_TEMP_HYST 500 /* 50 Celsius */ +#define MTP008_INIT_TEMP2_OVER 700 /* 70 Celsius */ +#define MTP008_INIT_TEMP2_HYST 500 /* 50 Celsius */ + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* + * For each registered MTP008, we need to keep some data in memory. The + * structure itself is dynamically allocated, at the same time when a new + * mtp008 client is allocated. + */ +struct mtp008_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_max; /* Register value */ + u8 temp_min; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 vid; /* Register encoding */ + u8 fan_div[3]; /* Register encoding */ + u16 alarms; /* Register encoding */ + u16 beeps; /* Register encoding */ + u8 pwm[4]; /* Register value */ + u8 sens[3]; /* 1 = Analog input, + 2 = Thermistor, + 3 = PII/Celeron diode */ + u8 pwmenable; /* Register 0x57 value */ +}; + +#ifdef MODULE +static int __init sensors_mtp008_init(void); +#else +extern int __init sensors_mtp008_init(void); +#endif +static int __init mtp008_cleanup(void); + +static int mtp008_attach_adapter(struct i2c_adapter *adapter); +static int mtp008_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int mtp008_detach_client(struct i2c_client *client); +static int mtp008_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void mtp008_inc_use(struct i2c_client *client); +static void mtp008_dec_use(struct i2c_client *client); + +static int mtp008_read_value(struct i2c_client *client, u8 register); +static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value); +static void mtp008_update_client(struct i2c_client *client); +static void mtp008_init_client(struct i2c_client *client); + +static void mtp008_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_pwm(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_sens(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_getsensortype(struct mtp008_data *data, u8 inp); + +static int mtp008_id = 0; + +static struct i2c_driver mtp008_driver = +{ + /* name */ "MTP008 sensor driver", + /* id */ I2C_DRIVERID_MTP008, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &mtp008_attach_adapter, + /* detach_client */ &mtp008_detach_client, + /* command */ &mtp008_command, + /* inc_use */ &mtp008_inc_use, + /* dec_use */ &mtp008_dec_use +}; + +/* Used by mtp008_init/cleanup */ +static int __initdata mtp008_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected chip. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +static ctl_table mtp008_dir_table_template[] = +{ + {MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp}, + {MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, + {MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, + {MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid}, + {MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div}, + {MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms}, + {MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep}, + {MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {0} +}; + +/* This function is called when: + * mtp008_driver is inserted (when this module is loaded), for each available + * adapter when a new adapter is inserted (and mtp008_driver is still present) + */ +int mtp008_attach_adapter(struct i2c_adapter *adapter) +{ + struct i2c_client_address_data mtp008_addr_data; + + mtp008_addr_data.normal_i2c = addr_data.normal_i2c; + mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range; + mtp008_addr_data.probe = addr_data.probe; + mtp008_addr_data.probe_range = addr_data.probe_range; + mtp008_addr_data.ignore = addr_data.ignore; + mtp008_addr_data.ignore_range = addr_data.ignore_range; + mtp008_addr_data.force = addr_data.forces->force; + + return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect); +} + +int mtp008_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + const char *type_name = ""; + const char *client_name = ""; + int is_isa, err, sysid; + struct i2c_client *new_client; + struct mtp008_data *data; + + err = 0; + + is_isa = i2c_is_isa_adapter(adapter); + if (is_isa || + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* + * We presume we have a valid client. We now create the client + * structure, even though we cannot fill it completely yet. But it + * allows us to use mtp008_(read|write)_value(). + */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct mtp008_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + data = (struct mtp008_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &mtp008_driver; + new_client->flags = 0; + + /* + * Remaining detection. + */ + if (kind < 0) { + if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac) + goto ERROR1; + } + /* + * Fill in the remaining client fields and put it into the global list. + */ + type_name = "mtp008"; + client_name = "MTP008 chip"; + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = mtp008_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* + * Tell the I2C layer that a new client has arrived. + */ + if ((err = i2c_attach_client(new_client))) + goto ERROR1; + + /* + * Register a new directory entry with the sensors module. + */ + if ((sysid = i2c_register_entry(new_client, type_name, + mtp008_dir_table_template, + THIS_MODULE)) < 0) { + err = sysid; + goto ERROR2; + } + data->sysctl_id = sysid; + + /* + * Initialize the MTP008 chip. + */ + mtp008_init_client(new_client); + + return 0; + + /* + * Error handling. Bad programming practise but very code efficient. + */ + ERROR2: + i2c_detach_client(new_client); + ERROR1: + kfree(new_client); + + ERROR0: + return err; +} + +int mtp008_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry( + ((struct mtp008_data *) (client->data))->sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk("mtp008.o: Deregistration failed, " + "client not detached.\n"); + return err; + } + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int mtp008_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void mtp008_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void mtp008_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int mtp008_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg) & 0xff; +} + +int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new MTP008. It should set limits, etc. */ +void mtp008_init_client(struct i2c_client *client) +{ + int vid; + u8 save1, save2; + struct mtp008_data *data; + + data = client->data; + + /* + * Initialize the Myson MTP008 hardware monitoring chip. + * Save the pin settings that the BIOS hopefully set. + */ + save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); + save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + mtp008_write_value(client, MTP008_REG_CONFIG, + (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80); + mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1); + mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2); + + mtp008_getsensortype(data, save2); + + /* + * Retrieve the VID setting (needed for the default limits). + */ + vid = mtp008_read_value(client, MTP008_REG_VID_FANDIV) & 0x0f; + vid |= (mtp008_read_value(client, MTP008_REG_RESET_VID4) & 0x01) << 4; + vid = VID_FROM_REG(vid); + + /* + * Set the default limits. + * + * Setting temp sensors is done as follows: + * + * Register 0x57: 0 0 0 0 x x x x + * | \ / +-- AIN5/VT3 + * | +----- AIN4/VT2/PII + * +-------- VT1/PII + */ + + mtp008_write_value(client, MTP008_REG_IN_MAX(0), + IN_TO_REG(MTP008_INIT_IN_MAX_0)); + mtp008_write_value(client, MTP008_REG_IN_MIN(0), + IN_TO_REG(MTP008_INIT_IN_MIN_0)); + mtp008_write_value(client, MTP008_REG_IN_MAX(1), + IN_TO_REG(MTP008_INIT_IN_MAX_1)); + mtp008_write_value(client, MTP008_REG_IN_MIN(1), + IN_TO_REG(MTP008_INIT_IN_MIN_1)); + mtp008_write_value(client, MTP008_REG_IN_MAX(2), + IN_TO_REG(MTP008_INIT_IN_MAX_2)); + mtp008_write_value(client, MTP008_REG_IN_MIN(2), + IN_TO_REG(MTP008_INIT_IN_MIN_2)); + mtp008_write_value(client, MTP008_REG_IN_MAX(3), + IN_TO_REG(MTP008_INIT_IN_MAX_3)); + mtp008_write_value(client, MTP008_REG_IN_MIN(3), + IN_TO_REG(MTP008_INIT_IN_MIN_3)); + + mtp008_write_value(client, MTP008_REG_IN_MAX(5), + IN_TO_REG(MTP008_INIT_IN_MAX_5)); + mtp008_write_value(client, MTP008_REG_IN_MIN(5), + IN_TO_REG(MTP008_INIT_IN_MIN_5)); + mtp008_write_value(client, MTP008_REG_IN_MAX(6), + IN_TO_REG(MTP008_INIT_IN_MAX_6)); + mtp008_write_value(client, MTP008_REG_IN_MIN(6), + IN_TO_REG(MTP008_INIT_IN_MIN_6)); + + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + TEMP_TO_REG(MTP008_INIT_TEMP_OVER)); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + TEMP_TO_REG(MTP008_INIT_TEMP_HYST)); + mtp008_write_value(client, MTP008_REG_IN_MAX(4), + TEMP_TO_REG(MTP008_INIT_TEMP2_OVER)); + mtp008_write_value(client, MTP008_REG_IN_MIN(4), + TEMP_TO_REG(MTP008_INIT_TEMP2_HYST)); + + mtp008_write_value(client, MTP008_REG_FAN_MIN(1), + FAN_TO_REG(MTP008_INIT_FAN_MIN_1, 2)); + mtp008_write_value(client, MTP008_REG_FAN_MIN(2), + FAN_TO_REG(MTP008_INIT_FAN_MIN_2, 2)); + mtp008_write_value(client, MTP008_REG_FAN_MIN(3), + FAN_TO_REG(MTP008_INIT_FAN_MIN_3, 2)); + + /* + * Start monitoring. + */ + mtp008_write_value( + client, MTP008_REG_CONFIG, + (mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01 + ); +} + +void mtp008_update_client(struct i2c_client *client) +{ + int i; + u8 inp; + struct mtp008_data *data; + + data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { +#ifdef DEBUG + printk("Starting MTP008 update\n"); +#endif + + /* + * Read in the analog inputs. We're reading AIN4 and AIN5 as + * regular analog inputs, even though they may have been + * configured as temperature readings instead. Interpretation + * of these values is done elsewhere. + */ + for (i = 0; i < 7; i++) { + data->in[i] = + mtp008_read_value(client, MTP008_REG_IN(i)); + data->in_max[i] = + mtp008_read_value(client, MTP008_REG_IN_MAX(i)); + data->in_min[i] = + mtp008_read_value(client, MTP008_REG_IN_MIN(i)); + } + + /* + * Read the temperature sensor. + */ + data->temp = mtp008_read_value(client, MTP008_REG_TEMP); + data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX); + data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN); + + /* + * Read the first 2 fan dividers and the VID setting. Read the + * third fan divider from a different register. + */ + inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV); + data->vid = inp & 0x0f; + data->vid |= (mtp008_read_value(client, + MTP008_REG_RESET_VID4) & 0x01) << 4; + + data->fan_div[0] = (inp >> 4) & 0x03; + data->fan_div[1] = inp >> 6; + data->fan_div[2] = + mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6; + + /* + * Read the interrupt status registers. + */ + data->alarms = + (mtp008_read_value(client, + MTP008_REG_INT_STAT1) & 0xdf) | + (mtp008_read_value(client, + MTP008_REG_INT_STAT2) & 0x0f) << 8; + + /* + * Read the beep control registers. + */ + data->beeps = + (mtp008_read_value(client, + MTP008_REG_BEEP_CTRL1) & 0xdf) | + (mtp008_read_value(client, + MTP008_REG_BEEP_CTRL2) & 0x8f) << 8; + + /* + * Read the sensor configuration. + */ + inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + mtp008_getsensortype(data, inp); + + /* + * Read the PWM registers if enabled. + */ + for (i = 1; i <= 3; i++) + { + if(PWMENABLE_FROM_REG(i, inp)) + data->pwm[i-1] = mtp008_read_value(client, + MTP008_REG_PWM_CTRL(i)); + else + data->pwm[i-1] = 255; + } + + /* + * Read the fan sensors. Skip 3 if PWM3 enabled. + */ + for (i = 1; i <= 3; i++) { + if(i == 3 && PWMENABLE_FROM_REG(3, inp)) { + data->fan[2] = 0; + data->fan_min[2] = 0; + } else { + data->fan[i-1] = mtp008_read_value(client, + MTP008_REG_FAN(i)); + data->fan_min[i-1] = mtp008_read_value(client, + MTP008_REG_FAN_MIN(i)); + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + +void mtp008_getsensortype(struct mtp008_data *data, u8 inp) +{ + inp &= 0x0f; + data->sens[0] = (inp >> 3) + 2; /* 2 or 3 */ + data->sens[1] = ((inp >> 1) & 0x03) + 1; /* 1, 2 or 3 */ + data->sens[2] = (inp & 0x01) + 1; /* 1 or 2 */ +} + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void mtp008_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_IN0; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_IN_MAX(nr), + data->in_max[nr]); + } + } + } +} + +void mtp008_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_FAN1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->fan_min[nr] = + FAN_TO_REG(results[0], + DIV_FROM_REG(data->fan_div[nr])); + mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1), + data->fan_min[nr]); + } + } +} + +void mtp008_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = TEMP_FROM_REG(data->temp_max); + results[1] = TEMP_FROM_REG(data->temp_min); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->temp_max = TEMP_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + data->temp_max); + } + if (*nrels_mag >= 2) { + data->temp_min = TEMP_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + data->temp_min); + } + } +} + +void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1; /* AIN4 or AIN5 */ + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + if(data->sens[nr - 3] != VOLTAGE) { + results[0] = TEMP_FROM_REG(data->in_max[nr]); + results[1] = TEMP_FROM_REG(data->in_min[nr]); + results[2] = TEMP_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if(data->sens[nr - 3] != VOLTAGE) { + if (*nrels_mag >= 1) { + data->in_max[nr] = TEMP_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + data->in_max[nr]); + } + if (*nrels_mag >= 2) { + data->in_min[nr] = TEMP_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + data->in_min[nr]); + } + } + } +} + +void mtp008_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = VID_FROM_REG(data->vid); + + *nrels_mag = 1; + } +} + +void mtp008_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct mtp008_data *data; + u8 val; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = DIV_FROM_REG(data->fan_div[2]); + + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 3) { + data->fan_div[2] = DIV_TO_REG(results[2]); + val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); + val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6; + mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val); + } + if (*nrels_mag >= 1) { + val = mtp008_read_value(client, MTP008_REG_VID_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + val = (val & 0x3f) | + (data->fan_div[1] & 0x03) << 6; + } + data->fan_div[0] = DIV_TO_REG(results[0]); + val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4; + mtp008_write_value(client, MTP008_REG_VID_FANDIV, val); + } + } +} + +void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = ALARMS_FROM_REG(data->alarms); + + *nrels_mag = 1; + } +} + +void mtp008_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = BEEPS_FROM_REG(data->beeps); + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f; + + mtp008_write_value(client, MTP008_REG_BEEP_CTRL1, + data->beeps & 0xff); + mtp008_write_value(client, MTP008_REG_BEEP_CTRL2, + data->beeps >> 8); + } + } +} + +void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_PWM1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = PWM_FROM_REG(data->pwm[nr]); + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->pwm[nr] = PWM_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr), + data->pwm[nr]); + } + } +} + +void mtp008_sens(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + const char *opts = ""; + int nr; + u8 tmp; + struct mtp008_data *data; + + nr = 1 + ctl_name - MTP008_SYSCTL_SENS1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + results[0] = data->sens[nr - 1]; + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + + switch (nr) { + case 1: /* VT or PII */ + opts = "2 or 3"; + + switch (results[0]) { + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp & ~MTP008_CFG_VT1_PII); + data->sens[0] = 2; + return; + case PIIDIODE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT1_PII); + data->sens[0] = 3; + return; + } + + break; + case 2: /* AIN, VT or PII */ + tmp &= ~MTP008_CFG_VT2_MASK; + opts = "1, 2 or 3"; + + switch (results[0]) { + case VOLTAGE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_AIN); + data->sens[1] = 1; + return; + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_VT); + data->sens[1] = 2; + return; + case PIIDIODE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_PII); + data->sens[1] = 3; + return; + } + + break; + case 3: /* AIN or VT */ + opts = "1 or 2"; + + switch (results[0]) { + case VOLTAGE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp & ~MTP008_CFG_VT3_VT); + data->sens[2] = 1; + return; + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT3_VT); + data->sens[2] = 2; + return; + } + + break; + } + + printk("mtp008.o: Invalid sensor type %ld " + "for sensor %d; must be %s.\n", + results[0], nr, opts); + } + } +} + +int __init sensors_mtp008_init(void) +{ + int res; + + printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE); + mtp008_initialized = 0; + + if ((res = i2c_add_driver(&mtp008_driver))) { + printk("mtp008.o: Driver registration failed, " + "module not inserted.\n"); + mtp008_cleanup(); + return res; + } + mtp008_initialized++; + + return 0; +} + +int __init mtp008_cleanup(void) +{ + int res; + + if (mtp008_initialized >= 1) { + if ((res = i2c_del_driver(&mtp008_driver))) { + printk("mtp008.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + mtp008_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Kris Van Hees "); +MODULE_DESCRIPTION("MTP008 driver"); + +int init_module(void) +{ + return sensors_mtp008_init(); +} + +int cleanup_module(void) +{ + return mtp008_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/sensors.c b/drivers/sensors/sensors.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/sensors.c Wed Feb 13 20:04:02 2002 @@ -0,0 +1,149 @@ +/* + sensors.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* Not configurable as a module */ + +#include + +#ifdef CONFIG_SENSORS_ADM1021 +extern int sensors_adm1021_init(void); +#endif +#ifdef CONFIG_SENSORS_ADM1025 +extern int sensors_adm1025_init(void); +#endif +#ifdef CONFIG_SENSORS_ADM9240 +extern int sensors_adm9240_init(void); +#endif +#ifdef CONFIG_SENSORS_BT869 +extern int sensors_bt869_init(void); +#endif +#ifdef CONFIG_SENSORS_DDCMON +extern int sensors_ddcmon_init(void); +#endif +#ifdef CONFIG_SENSORS_DS1621 +extern int sensors_ds1621_init(void); +#endif +#ifdef CONFIG_SENSORS_GL518SM +extern int sensors_gl518sm_init(void); +#endif +#ifdef CONFIG_SENSORS_GL520SM +extern int sensors_gl520_init(void); +#endif +#ifdef CONFIG_SENSORS_LM75 +extern int sensors_lm75_init(void); +#endif +#ifdef CONFIG_SENSORS_LM78 +extern int sensors_lm78_init(void); +#endif +#ifdef CONFIG_SENSORS_LM80 +extern int sensors_lm80_init(void); +#endif +#ifdef CONFIG_SENSORS_LM87 +extern int sensors_lm87_init(void); +#endif +#ifdef CONFIG_SENSORS_MTP008 +extern int sensors_mtp008_init(void); +#endif +#ifdef CONFIG_SENSORS_SIS5595 +extern int sensors_sis5595_init(void); +#endif +#ifdef CONFIG_SENSORS_THMC50 +extern int sensors_thmc50_init(void); +#endif +#ifdef CONFIG_SENSORS_VIA686A +extern int sensors_via686a_init(void); +#endif +#ifdef CONFIG_SENSORS_W83781D +extern int sensors_w83781d_init(void); +#endif +#ifdef CONFIG_SENSORS_EEPROM +extern int sensors_eeprom_init(void); +#endif +#ifdef CONFIG_SENSORS_LTC1710 +extern int sensors_ltc1710_init(void); +#endif +#ifdef CONFIG_SENSORS_IT87 +extern int sensors_it87_init(void); +#endif + +int __init sensors_init_all(void) +{ +#ifdef CONFIG_SENSORS_ADM1021 + sensors_adm1021_init(); +#endif +#ifdef CONFIG_SENSORS_ADM1025 + sensors_adm1025_init(); +#endif +#ifdef CONFIG_SENSORS_ADM9240 + sensors_adm9240_init(); +#endif +#ifdef CONFIG_SENSORS_BT869 + sensors_bt869_init(); +#endif +#ifdef CONFIG_SENSORS_DDCMON + sensors_ddcmon_init(); +#endif +#ifdef CONFIG_SENSORS_DS1621 + sensors_ds1621_init(); +#endif +#ifdef CONFIG_SENSORS_GL518SM + sensors_gl518sm_init(); +#endif +#ifdef CONFIG_SENSORS_GL520SM + sensors_gl520_init(); +#endif +#ifdef CONFIG_SENSORS_LM75 + sensors_lm75_init(); +#endif +#ifdef CONFIG_SENSORS_LM78 + sensors_lm78_init(); +#endif +#ifdef CONFIG_SENSORS_LM80 + sensors_lm80_init(); +#endif +#ifdef CONFIG_SENSORS_LM87 + sensors_lm87_init(); +#endif +#ifdef CONFIG_SENSORS_MTP008 + sensors_mtp008_init(); +#endif +#ifdef CONFIG_SENSORS_SIS5595 + sensors_sis5595_init(); +#endif +#ifdef CONFIG_SENSORS_THMC50 + sensors_thmc50_init(); +#endif +#ifdef CONFIG_SENSORS_VIA686A + sensors_via686a_init(); +#endif +#ifdef CONFIG_SENSORS_W83781D + sensors_w83781d_init(); +#endif +#ifdef CONFIG_SENSORS_EEPROM + sensors_eeprom_init(); +#endif +#ifdef CONFIG_SENSORS_LTC1710 + sensors_ltc1710_init(); +#endif +#ifdef CONFIG_SENSORS_IT87 + sensors_it87_init(); +#endif + return 0; +} diff -Nru a/drivers/sensors/sis5595.c b/drivers/sensors/sis5595.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/sis5595.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,886 @@ +/* + sis5595.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2001 Frodo Looijaard , + Kyösti Mälkki , and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports following revisions: + Version PCI ID PCI Revision + 1 1039/0008 AF or less + 2 1039/0008 B0 or greater + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 730 0008 0730 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(sis5595); + +#ifndef PCI_DEVICE_ID_SI_540 +#define PCI_DEVICE_ID_SI_540 0x0540 +#endif +#ifndef PCI_DEVICE_ID_SI_550 +#define PCI_DEVICE_ID_SI_550 0x0550 +#endif +#ifndef PCI_DEVICE_ID_SI_630 +#define PCI_DEVICE_ID_SI_630 0x0630 +#endif +#ifndef PCI_DEVICE_ID_SI_730 +#define PCI_DEVICE_ID_SI_730 0x0730 +#endif +#ifndef PCI_DEVICE_ID_SI_5598 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#endif + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but + that ID shows up in other chips so we + use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0 }; +/* + SiS southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many SIS5595 constants specified below */ + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* PCI Config Registers */ +#define SIS5595_REVISION_REG 0x08 +#define SIS5595_BASE_REG 0x68 +#define SIS5595_PIN_REG 0x7A +#define SIS5595_ENABLE_REG 0x7B + +/* Where are the ISA address/data registers relative to the base address */ +#define SIS5595_ADDR_REG_OFFSET 5 +#define SIS5595_DATA_REG_OFFSET 6 + +/* The SIS5595 registers */ +#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define SIS5595_REG_IN(nr) (0x20 + (nr)) + +#define SIS5595_REG_FAN_MIN(nr) (0x3a + (nr)) +#define SIS5595_REG_FAN(nr) (0x27 + (nr)) + +/* On the first version of the chip, the temp registers are separate. + On the second version, + TEMP pin is shared with IN4, configured in PCI register 0x7A. + The registers are the same as well. + OVER and HYST are really MAX and MIN. */ + +#define REV2MIN 0xb0 +#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN(4) : 0x27 +#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MAX(4) : 0x39 +#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MIN(4) : 0x3a + +#define SIS5595_REG_CONFIG 0x40 +#define SIS5595_REG_ALARM1 0x41 +#define SIS5595_REG_ALARM2 0x42 +#define SIS5595_REG_FANDIV 0x47 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +/* Version 1 datasheet temp=.83*reg + 52.12 */ +#define TEMP_FROM_REG(val) (((((val)>=0x80?(val)-0x100:(val))*83)+5212)/10) +/* inverse 1.20*val - 62.77 */ +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?\ + ((((val)*12)-6327)/100):\ + ((((val)*12)-6227)/100)),0,255)) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits. To keep them sane, we use the 'standard' translation as + specified in the SIS5595 sheet. Use the config file to set better limits. */ +#define SIS5595_INIT_IN_0 (((1200) * 10)/38) +#define SIS5595_INIT_IN_1 (((500) * 100)/168) +#define SIS5595_INIT_IN_2 330 +#define SIS5595_INIT_IN_3 250 +#define SIS5595_INIT_IN_4 250 + +#define SIS5595_INIT_IN_PERCENTAGE 10 + +#define SIS5595_INIT_IN_MIN_0 \ + (SIS5595_INIT_IN_0 - SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_0 \ + (SIS5595_INIT_IN_0 + SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_1 \ + (SIS5595_INIT_IN_1 - SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_1 \ + (SIS5595_INIT_IN_1 + SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_2 \ + (SIS5595_INIT_IN_2 - SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_2 \ + (SIS5595_INIT_IN_2 + SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_3 \ + (SIS5595_INIT_IN_3 - SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_3 \ + (SIS5595_INIT_IN_3 + SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_4 \ + (SIS5595_INIT_IN_4 - SIS5595_INIT_IN_4 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_4 \ + (SIS5595_INIT_IN_4 + SIS5595_INIT_IN_4 * SIS5595_INIT_IN_PERCENTAGE / 100) + +#define SIS5595_INIT_FAN_MIN_1 3000 +#define SIS5595_INIT_FAN_MIN_2 3000 + +#define SIS5595_INIT_TEMP_OVER 600 +#define SIS5595_INIT_TEMP_HYST 100 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For the SIS5595, we need to keep some data in memory. That + data is pointed to by sis5595_list[NR]->data. The structure itself is + dynamically allocated, at the time when the new sis5595 client is + allocated. */ +struct sis5595_data { + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + char maxins; /* == 3 if temp enabled, otherwise == 4 */ + u8 revision; /* Reg. value */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_over; /* Register value - really max */ + u8 temp_hyst; /* Register value - really min */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_sis5595_init(void); +static int __init sis5595_cleanup(void); + +static int sis5595_attach_adapter(struct i2c_adapter *adapter); +static int sis5595_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int sis5595_detach_client(struct i2c_client *client); +static int sis5595_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void sis5595_inc_use(struct i2c_client *client); +static void sis5595_dec_use(struct i2c_client *client); + +static int sis5595_read_value(struct i2c_client *client, u8 register); +static int sis5595_write_value(struct i2c_client *client, u8 register, + u8 value); +static void sis5595_update_client(struct i2c_client *client); +static void sis5595_init_client(struct i2c_client *client); +static int sis5595_find_sis(int *address); + + +static void sis5595_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int sis5595_id = 0; + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver sis5595_driver = { + /* name */ "SiS 5595", + /* id */ I2C_DRIVERID_SIS5595, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &sis5595_attach_adapter, + /* detach_client */ &sis5595_detach_client, + /* command */ &sis5595_command, + /* inc_use */ &sis5595_inc_use, + /* dec_use */ &sis5595_dec_use +}; + +/* Used by sis5595_init/cleanup */ +static int __initdata sis5595_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected SIS5595. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table sis5595_dir_table_template[] = { + {SIS5595_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan}, + {SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan}, + {SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_temp}, + {SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan_div}, + {SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_alarms}, + {0} +}; + +/* This is called when the module is loaded */ +int sis5595_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, sis5595_detect); +} + +/* Locate SiS bridge and correct base address for SIS5595 */ +int sis5595_find_sis(int *address) +{ + u16 val; + int *i; + + if (!pci_present()) + return -ENODEV; + + if (!(s_bridge = + pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, + NULL))) + return -ENODEV; + + /* Look for imposters */ + for(i = blacklist; *i != 0; i++) { + if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { + printk("sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val)) + return -ENODEV; + + *address = val & ~(SIS5595_EXTENT - 1); + if (*address == 0 && force_addr == 0) { + printk("sis5595.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + *address = force_addr; /* so detect will get called */ + + return 0; +} + +int sis5595_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct sis5595_data *data; + int err = 0; + const char *type_name = "sis5595"; + const char *client_name = "SIS5595 chip"; + char val; + u16 a; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + printk + ("sis5595.o: sis5595_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + if(force_addr) + address = force_addr & ~(SIS5595_EXTENT - 1); + if (check_region(address, SIS5595_EXTENT)) { + printk("sis5595.o: region 0x%x already in use!\n", address); + return -ENODEV; + } + if(force_addr) { + printk("sis5595.o: forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) + return -ENODEV; + if ((a & ~(SIS5595_EXTENT - 1)) != address) { + /* doesn't work for some chips? */ + printk("sis5595.o: force address failed\n"); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { + printk("sis5595.o: enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, + val | 0x80)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { /* doesn't work for some chips! */ + printk("sis5595.o: sensors enable failed - not supported?\n"); + return -ENODEV; + } + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct sis5595_data), + GFP_KERNEL))) { + return -ENOMEM; + } + + data = (struct sis5595_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &sis5595_driver; + new_client->flags = 0; + + /* Reserve the ISA region */ + request_region(address, SIS5595_EXTENT, type_name); + + /* Check revision and pin registers to determine whether 3 or 4 voltages */ + pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); + if(data->revision < REV2MIN) { + data->maxins = 3; + } else { + pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); + if(val & 0x80) + /* 3 voltages, 1 temp */ + data->maxins = 3; + else + /* 4 voltages, no temps */ + data->maxins = 4; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = sis5595_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + sis5595_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the SIS5595 chip */ + sis5595_init_client(new_client); + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + release_region(address, SIS5595_EXTENT); + kfree(new_client); + return err; +} + +int sis5595_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct sis5595_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("sis5595.o: Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, SIS5595_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int sis5595_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void sis5595_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void sis5595_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ISA access must be locked explicitly. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +int sis5595_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + down(&(((struct sis5595_data *) (client->data))->lock)); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); + up(&(((struct sis5595_data *) (client->data))->lock)); + return res; +} + +int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + down(&(((struct sis5595_data *) (client->data))->lock)); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); + up(&(((struct sis5595_data *) (client->data))->lock)); + return 0; +} + +/* Called when we have found a new SIS5595. It should set limits, etc. */ +void sis5595_init_client(struct i2c_client *client) +{ + struct sis5595_data *data = client->data; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + sis5595_write_value(client, SIS5595_REG_CONFIG, 0x80); + + sis5595_write_value(client, SIS5595_REG_IN_MIN(0), + IN_TO_REG(SIS5595_INIT_IN_MIN_0)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(0), + IN_TO_REG(SIS5595_INIT_IN_MAX_0)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(1), + IN_TO_REG(SIS5595_INIT_IN_MIN_1)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(1), + IN_TO_REG(SIS5595_INIT_IN_MAX_1)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(2), + IN_TO_REG(SIS5595_INIT_IN_MIN_2)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(2), + IN_TO_REG(SIS5595_INIT_IN_MAX_2)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(3), + IN_TO_REG(SIS5595_INIT_IN_MIN_3)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(3), + IN_TO_REG(SIS5595_INIT_IN_MAX_3)); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(1), + FAN_TO_REG(SIS5595_INIT_FAN_MIN_1, 2)); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(2), + FAN_TO_REG(SIS5595_INIT_FAN_MIN_2, 2)); + if(data->maxins == 4) { + sis5595_write_value(client, SIS5595_REG_IN_MIN(4), + IN_TO_REG(SIS5595_INIT_IN_MIN_4)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(4), + IN_TO_REG(SIS5595_INIT_IN_MAX_4)); + } else { + sis5595_write_value(client, SIS5595_REG_TEMP_OVER, + TEMP_TO_REG(SIS5595_INIT_TEMP_OVER)); + sis5595_write_value(client, SIS5595_REG_TEMP_HYST, + TEMP_TO_REG(SIS5595_INIT_TEMP_HYST)); + } + + /* Start monitoring */ + sis5595_write_value(client, SIS5595_REG_CONFIG, + (sis5595_read_value(client, SIS5595_REG_CONFIG) + & 0xf7) | 0x01); + +} + +void sis5595_update_client(struct i2c_client *client) +{ + struct sis5595_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + for (i = 0; i <= data->maxins; i++) { + data->in[i] = + sis5595_read_value(client, SIS5595_REG_IN(i)); + data->in_min[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MIN(i)); + data->in_max[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + sis5595_read_value(client, SIS5595_REG_FAN(i)); + data->fan_min[i - 1] = + sis5595_read_value(client, + SIS5595_REG_FAN_MIN(i)); + } + if(data->maxins == 3) { + data->temp = + sis5595_read_value(client, SIS5595_REG_TEMP); + data->temp_over = + sis5595_read_value(client, SIS5595_REG_TEMP_OVER); + data->temp_hyst = + sis5595_read_value(client, SIS5595_REG_TEMP_HYST); + } + i = sis5595_read_value(client, SIS5595_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + sis5595_read_value(client, SIS5595_REG_ALARM1) | + (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ + +/* Return 0 for in4 and disallow writes if pin used for temp */ +void sis5595_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int nr = ctl_name - SIS5595_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + if(nr <= 3 || data->maxins == 4) { + sis5595_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(nr <= 3 || data->maxins == 4) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + sis5595_write_value(client, + SIS5595_REG_IN_MIN(nr), data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + sis5595_write_value(client, + SIS5595_REG_IN_MAX(nr), data->in_max[nr]); + } + } + } +} + +void sis5595_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int nr = ctl_name - SIS5595_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + results[1] = FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr-1])); + sis5595_write_value(client, + SIS5595_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +/* Return 0 for temp and disallow writes if pin used for in4 */ +void sis5595_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + if(data->maxins == 3) { + sis5595_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(data->maxins == 3) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + sis5595_write_value(client, + SIS5595_REG_TEMP_OVER, data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + sis5595_write_value(client, + SIS5595_REG_TEMP_HYST, data->temp_hyst); + } + } + } +} + +void sis5595_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void sis5595_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = sis5595_read_value(client, SIS5595_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + sis5595_write_value(client, SIS5595_REG_FANDIV, old); + } + } +} + +int __init sensors_sis5595_init(void) +{ + int res, addr; + + printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); + sis5595_initialized = 0; + + if (sis5595_find_sis(&addr)) { + printk("sis5595.o: SIS5595 not detected, module not inserted.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + if ((res = i2c_add_driver(&sis5595_driver))) { + printk + ("sis5595.o: Driver registration failed, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + return 0; +} + +int __init sis5595_cleanup(void) +{ + int res; + + if (sis5595_initialized >= 1) { + if ((res = i2c_del_driver(&sis5595_driver))) { + printk + ("sis5595.o: Driver deregistration failed, module not removed.\n"); + return res; + } + sis5595_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Kyösti Mälkki "); +MODULE_DESCRIPTION("SiS 5595 Sensor device"); + +int init_module(void) +{ + return sensors_sis5595_init(); +} + +int cleanup_module(void) +{ + return sis5595_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/thmc50.c b/drivers/sensors/thmc50.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/thmc50.c Wed Feb 13 20:04:00 2002 @@ -0,0 +1,580 @@ +/* + thmc50.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2D, 0x2F, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(thmc50); + +/* Many THMC50 constants specified below */ + +/* The THMC50 registers */ +#define THMC50_REG_TEMP 0x27 +#define THMC50_REG_CONF 0x40 +#define THMC50_REG_TEMP_HYST 0x3A +#define THMC50_REG_TEMP_OS 0x39 + +#define THMC50_REG_TEMP_TRIP 0x13 +#define THMC50_REG_TEMP_REMOTE_TRIP 0x14 +#define THMC50_REG_TEMP_DEFAULT_TRIP 0x17 +#define THMC50_REG_TEMP_REMOTE_DEFAULT_TRIP 0x18 +#define THMC50_REG_ANALOG_OUT 0x19 +#define THMC50_REG_REMOTE_TEMP 0x26 +#define THMC50_REG_REMOTE_TEMP_HYST 0x38 +#define THMC50_REG_REMOTE_TEMP_OS 0x37 + +#define THMC50_REG_INTER 0x41 +#define THMC50_REG_INTER_MIRROR 0x4C +#define THMC50_REG_INTER_MASK 0x43 + +#define THMC50_REG_COMPANY_ID 0x3E +#define THMC50_REG_DIE_CODE 0x3F + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define TEMP_FROM_REG(val) ((val>127)?val - 0x0100:val) +#define TEMP_TO_REG(val) ((val<0)?0x0100+val:val) + +/* Initial values */ +#define THMC50_INIT_TEMP_OS 60 +#define THMC50_INIT_TEMP_HYST 50 + +/* Each client has this additional data */ +struct thmc50_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_os, temp_hyst, + remote_temp, remote_temp_os, remote_temp_hyst, + inter, inter_mask, die_code, analog_out; /* Register values */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_thmc50_init(void); +static int __init thmc50_cleanup(void); +static int thmc50_attach_adapter(struct i2c_adapter *adapter); +static int thmc50_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void thmc50_init_client(struct i2c_client *client); +static int thmc50_detach_client(struct i2c_client *client); +static int thmc50_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void thmc50_inc_use(struct i2c_client *client); +static void thmc50_dec_use(struct i2c_client *client); +static int thmc50_read_value(struct i2c_client *client, u8 reg); +static int thmc50_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void thmc50_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void thmc50_inter(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_inter_mask(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver thmc50_driver = { + /* name */ "THMC50 sensor chip driver", + /* id */ I2C_DRIVERID_THMC50, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &thmc50_attach_adapter, + /* detach_client */ &thmc50_detach_client, + /* command */ &thmc50_command, + /* inc_use */ &thmc50_inc_use, + /* dec_use */ &thmc50_dec_use +}; + +/* These files are created for each detected THMC50. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table thmc50_dir_table_template[] = { + {THMC50_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_temp}, + {THMC50_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_remote_temp}, + {THMC50_SYSCTL_INTER, "inter", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_inter}, + {THMC50_SYSCTL_INTER_MASK, "inter_mask", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_inter_mask}, + {THMC50_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_die_code}, + {THMC50_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_analog_out}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata thmc50_initialized = 0; + +static int thmc50_id = 0; + +int thmc50_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, thmc50_detect); +} + +/* This function is called by i2c_detect */ +int thmc50_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int company, i; + struct i2c_client *new_client; + struct thmc50_data *data; + int err = 0; + const char *type_name, *client_name; + +#ifdef DEBUG + printk("thmc50.o: Probing for THMC50 at 0x%2X on bus %d\n", + address, adapter->id); +#endif + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("thmc50.o: thmc50_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access thmc50_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct thmc50_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = + (struct thmc50_data *) (((struct i2c_client *) new_client) + + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &thmc50_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + company = + i2c_smbus_read_byte_data(new_client, THMC50_REG_COMPANY_ID); + + if (company != 0x49) { +#ifdef DEBUG + printk + ("thmc50.o: Detect of THMC50 failed (reg 3E: 0x%X)\n", + company); +#endif + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + kind = thmc50; + + if (kind == thmc50) { + type_name = "thmc50"; + client_name = "THMC50 chip"; + } else { +#ifdef DEBUG + printk("thmc50.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = thmc50_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + thmc50_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + thmc50_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int thmc50_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct thmc50_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("thmc50.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int thmc50_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void thmc50_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void thmc50_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are word-sized, except for the configuration register. + THMC50 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int thmc50_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* All registers are word-sized, except for the configuration register. + THMC50 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int thmc50_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +void thmc50_init_client(struct i2c_client *client) +{ + /* Initialize the THMC50 chip */ + thmc50_write_value(client, THMC50_REG_TEMP_OS, + TEMP_TO_REG(THMC50_INIT_TEMP_OS)); + thmc50_write_value(client, THMC50_REG_TEMP_HYST, + TEMP_TO_REG(THMC50_INIT_TEMP_HYST)); + thmc50_write_value(client, THMC50_REG_CONF, 1); +} + +void thmc50_update_client(struct i2c_client *client) +{ + struct thmc50_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting thmc50 update\n"); +#endif + + data->temp = thmc50_read_value(client, THMC50_REG_TEMP); + data->temp_os = + thmc50_read_value(client, THMC50_REG_TEMP_OS); + data->temp_hyst = + thmc50_read_value(client, THMC50_REG_TEMP_HYST); + data->remote_temp = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP); + data->remote_temp_os = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_OS); + data->remote_temp_hyst = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_HYST); + data->inter = thmc50_read_value(client, THMC50_REG_INTER); + data->inter_mask = + thmc50_read_value(client, THMC50_REG_INTER_MASK); + data->die_code = + thmc50_read_value(client, THMC50_REG_DIE_CODE); + data->analog_out = + thmc50_read_value(client, THMC50_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void thmc50_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + thmc50_write_value(client, THMC50_REG_TEMP_OS, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + thmc50_write_value(client, THMC50_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + + +void thmc50_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[1] = TEMP_FROM_REG(data->remote_temp_hyst); + results[2] = TEMP_FROM_REG(data->remote_temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->remote_temp_os = TEMP_TO_REG(results[0]); + thmc50_write_value(client, + THMC50_REG_REMOTE_TEMP_OS, + data->remote_temp_os); + } + if (*nrels_mag >= 2) { + data->remote_temp_hyst = TEMP_TO_REG(results[1]); + thmc50_write_value(client, + THMC50_REG_REMOTE_TEMP_HYST, + data->remote_temp_hyst); + } + } +} + + +void thmc50_inter(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->inter; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("thmc50.o: No writes to Interrupt register!\n"); + } +} + + +void thmc50_inter_mask(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->inter_mask; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->inter_mask = results[0]; + thmc50_write_value(client, THMC50_REG_INTER_MASK, + data->inter_mask); + } + } +} + + +void thmc50_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->die_code; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("thmc50.o: No writes to Die-Code register!\n"); + } +} + + +void thmc50_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + thmc50_write_value(client, THMC50_REG_ANALOG_OUT, + data->analog_out); + } + } +} + + + + +int __init sensors_thmc50_init(void) +{ + int res; + + printk("thmc50.o version %s (%s)\n", LM_VERSION, LM_DATE); + thmc50_initialized = 0; + if ((res = i2c_add_driver(&thmc50_driver))) { + printk + ("thmc50.o: Driver registration failed, module not inserted.\n"); + thmc50_cleanup(); + return res; + } + thmc50_initialized++; + return 0; +} + +int __init thmc50_cleanup(void) +{ + int res; + + if (thmc50_initialized >= 1) { + if ((res = i2c_del_driver(&thmc50_driver))) { + printk + ("thmc50.o: Driver deregistration failed, module not removed.\n"); + return res; + } + thmc50_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("THMC50 driver"); + +int init_module(void) +{ + return sensors_thmc50_init(); +} + +int cleanup_module(void) +{ + return thmc50_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/via686a.c b/drivers/sensors/via686a.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/via686a.c Wed Feb 13 20:03:59 2002 @@ -0,0 +1,997 @@ +/* + via686a.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2001 Frodo Looijaard , + Kyösti Mälkki , + Mark Studebaker , + and Bob Dougherty + (Some conversion-factor data were contributed by Jonathan Teh Soon Yew + and Alex van Kaam .) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports the Via VT82C686A and VT82C686B south bridges. + Reports either as a 686A. + See doc/chips/via686a for details. + Warning - only supports a single device. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_VIA_82C686_4 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) +#define THIS_MODULE NULL +#endif + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(via686a); + +/* + The Via 686a southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many VIA686A constants specified below */ + +/* Length of ISA address segment */ +#define VIA686A_EXTENT 0x80 +#define VIA686A_BASE_REG 0x70 +#define VIA686A_ENABLE_REG 0x74 + +/* The VIA686A registers */ +/* ins numbered 0-4 */ +#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define VIA686A_REG_IN(nr) (0x22 + (nr)) + +/* fans numbered 1-2 */ +#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) +#define VIA686A_REG_FAN(nr) (0x28 + (nr)) + +// the following values are as speced by VIA: +static const u8 regtemp[] = { 0x20, 0x21, 0x1f }; +static const u8 regover[] = { 0x39, 0x3d, 0x1d }; +static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e }; + +/* temps numbered 1-3 */ +#define VIA686A_REG_TEMP(nr) (regtemp[(nr) - 1]) +#define VIA686A_REG_TEMP_OVER(nr) (regover[(nr) - 1]) +#define VIA686A_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) +#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6 +#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6 + +#define VIA686A_REG_ALARM1 0x41 +#define VIA686A_REG_ALARM2 0x42 +#define VIA686A_REG_FANDIV 0x47 +#define VIA686A_REG_CONFIG 0x40 +// The following register sets temp interrupt mode (bits 1-0 for temp1, +// 3-2 for temp2, 5-4 for temp3). Modes are: +// 00 interrupt stays as long as value is out-of-range +// 01 interrupt is cleared once register is read (default) +// 10 comparator mode- like 00, but ignores hysteresis +// 11 same as 00 +#define VIA686A_REG_TEMP_MODE 0x4b +// We'll just assume that you want to set all 3 simulataneously: +#define VIA686A_TEMP_MODE_MASK 0x3F +#define VIA686A_TEMP_MODE_CONTINUOUS (0x00) + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. */ + +/********* VOLTAGE CONVERSIONS (Bob Dougherty) ********/ +// From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): +// voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp +// voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V +// voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V +// voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V +// voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V +// in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; +// That is: +// volts = (25*regVal+133)*factor +// regVal = (volts/factor-133)/25 +// (These conversions were contributed by Jonathan Teh Soon Yew +// ) +// +// These get us close, but they don't completely agree with what my BIOS +// says- they are all a bit low. But, it all we have to go on... +extern inline u8 IN_TO_REG(long val, int inNum) +{ + // to avoid floating point, we multiply everything by 100. + // val is guaranteed to be positive, so we can achieve the effect of + // rounding by (...*10+5)/10. Note that the *10 is hidden in the + // /250 (which should really be /2500). + // At the end, we need to /100 because we *100 everything and we need + // to /10 because of the rounding thing, so we /1000. + if (inNum <= 1) + return (u8) + SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 2) + return (u8) + SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 3) + return (u8) + SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000, + 0, 255); + else + return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5) + / 1000, 0, 255); +} + +extern inline long IN_FROM_REG(u8 val, int inNum) +{ + // to avoid floating point, we multiply everything by 100. + // val is guaranteed to be positive, so we can achieve the effect of + // rounding by adding 0.5. Or, to avoid fp math, we do (...*10+5)/10. + // We need to scale with *100 anyway, so no need to /100 at the end. + if (inNum <= 1) + return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10); + else if (inNum == 2) + return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10); + else if (inNum == 3) + return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10); + else + return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10); +} + +/********* FAN RPM CONVERSIONS ********/ +// Higher register values = slower fans (the fan's strobe gates a counter). +// But this chip saturates back at 0, not at 255 like all the other chips. +// So, 0 means 0 RPM +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 0; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); +} + +#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) + +/******** TEMP CONVERSIONS (Bob Dougherty) *********/ +// linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) +// if(temp<169) +// return double(temp)*0.427-32.08; +// else if(temp>=169 && temp<=202) +// return double(temp)*0.582-58.16; +// else +// return double(temp)*0.924-127.33; +// +// A fifth-order polynomial fits the unofficial data (provided by Alex van +// Kaam ) a bit better. It also give more reasonable +// numbers on my machine (ie. they agree with what my BIOS tells me). +// Here's the fifth-order fit to the 8-bit data: +// temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - +// 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. +// +// (2000-10-25- RFD: thanks to Uwe Andersen for +// finding my typos in this formula!) +// +// Alas, none of the elegant function-fit solutions will work because we +// aren't allowed to use floating point in the kernel and doing it with +// integers doesn't rpovide enough precision. So we'll do boring old +// look-up table stuff. The unofficial data (see below) have effectively +// 7-bit resolution (they are rounded to the nearest degree). I'm assuming +// that the transfer function of the device is monotonic and smooth, so a +// smooth function fit to the data will allow us to get better precision. +// I used the 5th-order poly fit described above and solved for +// VIA register values 0-255. I *10 before rounding, so we get tenth-degree +// precision. (I could have done all 1024 values for our 10-bit readings, +// but the function is very linear in the useful range (0-80 deg C), so +// we'll just use linear interpolation for 10-bit readings.) So, tempLUT +// is the temp at via register values 0-255: +static const long tempLUT[] = + { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, + -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, + -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, + -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, + -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, + -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, + -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, + 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, + 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, + 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, + 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, + 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, + 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, + 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, + 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, + 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, + 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, + 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, + 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, + 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, + 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, + 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 +}; + +/* the original LUT values from Alex van Kaam + (for via register values 12-240): +{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, +-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, +-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, +-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, +12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, +22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, +33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, +45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, +61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, +85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; +*/ + +// Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed +// an extra term for a good fit to these inverse data!) and then +// solving for each temp value from -50 to 110 (the useable range for +// this chip). Here's the fit: +// viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 +// - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) +// Note that n=161: +static const u8 viaLUT[] = + { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, + 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, + 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, + 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, + 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, + 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, + 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, + 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, + 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, + 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, + 239, 240 +}; + +/* Converting temps to (8-bit) hyst and over registers */ +// No interpolation here. Just check the limits and go. +// The +5 effectively rounds off properly and the +50 is because +// the temps start at -50 +extern inline u8 TEMP_TO_REG(long val) +{ + return (u8) + SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 : + ((val + 5) / 10 + 50))], 0, 255); +} + +/* for 8-bit temperature hyst and over registers */ +// The temp values are already *10, so we don't need to do that. +// But we _will_ round these off to the nearest degree with (...*10+5)/10 +#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10) + +/* for 10-bit temperature readings */ +// You might _think_ this is too long to inline, but's it's really only +// called once... +extern inline long TEMP_FROM_REG10(u16 val) +{ + // the temp values are already *10, so we don't need to do that. + long temp; + u16 eightBits = val >> 2; + u16 twoBits = val & 3; + + // handle the extremes first (they won't interpolate well! ;-) + if (val == 0) + return (long) tempLUT[0]; + if (val == 1023) + return (long) tempLUT[255]; + + if (twoBits == 0) + return (long) tempLUT[eightBits]; + else { + // do some interpolation by multipying the lower and upper + // bounds by 25, 50 or 75, then /100. + temp = ((25 * (4 - twoBits)) * tempLUT[eightBits] + + (25 * twoBits) * tempLUT[eightBits + 1]); + // increase the magnitude by 50 to achieve rounding. + if (temp > 0) + temp += 50; + else + temp -= 50; + return (temp / 100); + } +} + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits */ +#define VIA686A_INIT_IN_0 200 +#define VIA686A_INIT_IN_1 250 +#define VIA686A_INIT_IN_2 330 +#define VIA686A_INIT_IN_3 500 +#define VIA686A_INIT_IN_4 1200 + +#define VIA686A_INIT_IN_PERCENTAGE 10 + +#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) + +#define VIA686A_INIT_FAN_MIN 3000 + +#define VIA686A_INIT_TEMP_OVER 600 +#define VIA686A_INIT_TEMP_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For the VIA686A, we need to keep some data in memory. That + data is pointed to by via686a_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new via686a client is + allocated. */ +struct via686a_data { + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u16 temp[3]; /* Register value 10 bit */ + u8 temp_over[3]; /* Register value */ + u8 temp_hyst[3]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_via686a_init(void); +static int __init via686a_cleanup(void); + +static int via686a_attach_adapter(struct i2c_adapter *adapter); +static int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int via686a_detach_client(struct i2c_client *client); +static int via686a_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void via686a_inc_use(struct i2c_client *client); +static void via686a_dec_use(struct i2c_client *client); + +static int via686a_read_value(struct i2c_client *client, u8 register); +static void via686a_write_value(struct i2c_client *client, u8 register, + u8 value); +static void via686a_update_client(struct i2c_client *client); +static void via686a_init_client(struct i2c_client *client); +static int via686a_find(int *address); + + +static void via686a_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int via686a_id = 0; + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver via686a_driver = { + /* name */ "VIA 686A", + /* id */ I2C_DRIVERID_VIA686A, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &via686a_attach_adapter, + /* detach_client */ &via686a_detach_client, + /* command */ &via686a_command, + /* inc_use */ &via686a_inc_use, + /* dec_use */ &via686a_dec_use +}; + +/* Used by via686a_init/cleanup */ +static int __initdata via686a_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected VIA686A. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table via686a_dir_table_template[] = { + {VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div}, + {VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms}, + {0} +}; + +static inline int via686a_read_value(struct i2c_client *client, u8 reg) +{ + return (inb_p(client->addr + reg)); +} + +static inline void via686a_write_value(struct i2c_client *client, u8 reg, + u8 value) +{ + outb_p(value, client->addr + reg); +} + +/* This is called when the module is loaded */ +int via686a_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, via686a_detect); +} + +/* Locate chip and get correct base address */ +int via686a_find(int *address) +{ + u16 val; + + if (!pci_present()) + return -ENODEV; + + if (!(s_bridge = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C686_4, + NULL))) return -ENODEV; + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_BASE_REG, &val)) + return -ENODEV; + *address = val & ~(VIA686A_EXTENT - 1); + if (*address == 0 && force_addr == 0) { + printk("via686a.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + *address = force_addr; /* so detect will get called */ + + return 0; +} + +int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct via686a_data *data; + int err = 0; + const char *type_name = "via686a"; + u16 val; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + printk + ("via686a.o: via686a_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + if(force_addr) + address = force_addr & ~(VIA686A_EXTENT - 1); + if (check_region(address, VIA686A_EXTENT)) { + printk("via686a.o: region 0x%x already in use!\n", + address); + return -ENODEV; + } + + if(force_addr) { + printk("via686a.o: forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) + return -ENODEV; + } + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) + return -ENODEV; + if (!(val & 0x0001)) { + printk("via686a.o: enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, + val | 0x0001)) + return -ENODEV; + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct via686a_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct via686a_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &via686a_driver; + new_client->flags = 0; + + /* Reserve the ISA region */ + request_region(address, VIA686A_EXTENT, "via686a-sensors"); + + /* Fill in the remaining client fields and put into the global list */ + strcpy(new_client->name, "Via 686A Integrated Sensors"); + + new_client->id = via686a_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + via686a_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the VIA686A chip */ + via686a_init_client(new_client); + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + release_region(address, VIA686A_EXTENT); + kfree(new_client); + ERROR0: + return err; +} + +int via686a_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct via686a_data *) + (client->data))->sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("via686a.o: Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, VIA686A_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int via686a_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void via686a_inc_use(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; +} + +void via686a_dec_use(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; +} + +/* Called when we have found a new VIA686A. Set limits, etc. */ +void via686a_init_client(struct i2c_client *client) +{ + int i; + + /* Reset the device */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x80); + + /* Have to wait for reset to complete or else the following + initializations won't work reliably. The delay was arrived at + empirically, the datasheet doesn't tell you. + Waiting for the reset bit to clear doesn't work, it + clears in about 2-4 udelays and that isn't nearly enough. */ + udelay(50); + + via686a_write_value(client, VIA686A_REG_IN_MIN(0), + IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MAX(0), + IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MIN(1), + IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MAX(1), + IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MIN(2), + IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MAX(2), + IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MIN(3), + IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MAX(3), + IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MIN(4), + IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4)); + via686a_write_value(client, VIA686A_REG_IN_MAX(4), + IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(1), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(2), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + for (i = 1; i <= 3; i++) { + via686a_write_value(client, VIA686A_REG_TEMP_OVER(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_OVER)); + via686a_write_value(client, VIA686A_REG_TEMP_HYST(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_HYST)); + } + + /* Start monitoring */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x01); + + /* Cofigure temp interrupt mode for continuous-interrupt operation */ + via686a_write_value(client, VIA686A_REG_TEMP_MODE, + via686a_read_value(client, VIA686A_REG_TEMP_MODE) & + !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); +} + +void via686a_update_client(struct i2c_client *client) +{ + struct via686a_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(client, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(client, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(client, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(client, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(client, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + data->temp[i - 1] = via686a_read_value(client, + VIA686A_REG_TEMP(i)) << 2; + data->temp_over[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_OVER(i)); + data->temp_hyst[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_HYST(i)); + } + /* add in lower 2 bits + temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(client, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + i = via686a_read_value(client, VIA686A_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + via686a_read_value(client, + VIA686A_REG_ALARM1) | + (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void via686a_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr], nr); + results[1] = IN_FROM_REG(data->in_max[nr], nr); + results[2] = IN_FROM_REG(data->in[nr], nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0], nr); + via686a_write_value(client, VIA686A_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1], nr); + via686a_write_value(client, VIA686A_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void via686a_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div + [nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div[nr -1])); + via686a_write_value(client, + VIA686A_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + +void via686a_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_TEMP; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over[nr]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); + results[2] = TEMP_FROM_REG10(data->temp[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over[nr] = TEMP_TO_REG(results[0]); + via686a_write_value(client, + VIA686A_REG_TEMP_OVER(nr + 1), + data->temp_over[nr]); + } + if (*nrels_mag >= 2) { + data->temp_hyst[nr] = TEMP_TO_REG(results[1]); + via686a_write_value(client, + VIA686A_REG_TEMP_HYST(nr + 1), + data->temp_hyst[nr]); + } + } +} + +void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = via686a_read_value(client, VIA686A_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + via686a_write_value(client, VIA686A_REG_FANDIV, + old); + } + } +} + +int __init sensors_via686a_init(void) +{ + int res, addr; + + printk("via686a.o version %s (%s)\n", LM_VERSION, LM_DATE); + via686a_initialized = 0; + + if (via686a_find(&addr)) { + printk("via686a.o: No Via 686A sensors found.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + if ((res = i2c_add_driver(&via686a_driver))) { + printk("via686a.o: Driver registration failed.\n"); + via686a_cleanup(); + return res; + } + via686a_initialized++; + return 0; +} + +int __init via686a_cleanup(void) +{ + int res; + + if (via686a_initialized >= 1) { + if ((res = i2c_del_driver(&via686a_driver))) { + printk + ("via686a.o: Driver deregistration failed.\n"); + return res; + } + via686a_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Kyösti Mälkki , Mark Studebaker , Bob Dougherty "); +MODULE_DESCRIPTION("VIA 686A Sensor device"); + +int init_module(void) +{ + return sensors_via686a_init(); +} + +int cleanup_module(void) +{ + return via686a_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sensors/w83781d.c b/drivers/sensors/w83781d.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/sensors/w83781d.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,1942 @@ +/* + w83781d.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999, 2000 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + as99127f 7 3 1? 3 0x20 0x12c3 yes no + w83781d 7 3 0 3 0x10 0x5ca3 yes yes + w83627hf 9 3 2 3 0x20 0x5ca3 yes yes(LPC) + w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes + w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20011118" +#define LM_VERSION "2.6.2" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* RT Table support #defined so we can take it out if it gets bothersome */ +#define W83781D_RT 1 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_5(w83781d, w83782d, w83783s, w83627hf, as99127f); + +static int init = 1; +MODULE_PARM(init, "i"); +MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); + +/* Many W83781D constants specified below */ + +/* Length of ISA address segment */ +#define W83781D_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define W83781D_ADDR_REG_OFFSET 5 +#define W83781D_DATA_REG_OFFSET 6 + +/* The W83781D registers */ +/* The W83782D registers for nr=7,8 are in bank 5 */ +#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + +#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) +#define W83781D_REG_FAN(nr) (0x27 + (nr)) + +#define W83781D_REG_TEMP2 0x0150 +#define W83781D_REG_TEMP3 0x0250 +#define W83781D_REG_TEMP2_HYST 0x153 +#define W83781D_REG_TEMP3_HYST 0x253 +#define W83781D_REG_TEMP2_CONFIG 0x152 +#define W83781D_REG_TEMP3_CONFIG 0x252 +#define W83781D_REG_TEMP2_OVER 0x155 +#define W83781D_REG_TEMP3_OVER 0x255 + +#define W83781D_REG_TEMP 0x27 +#define W83781D_REG_TEMP_OVER 0x39 +#define W83781D_REG_TEMP_HYST 0x3A +#define W83781D_REG_BANK 0x4E + +#define W83781D_REG_CONFIG 0x40 +#define W83781D_REG_ALARM1 0x41 +#define W83781D_REG_ALARM2 0x42 +#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ + +#define W83781D_REG_BEEP_CONFIG 0x4D +#define W83781D_REG_BEEP_INTS1 0x56 +#define W83781D_REG_BEEP_INTS2 0x57 +#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ + +#define W83781D_REG_VID_FANDIV 0x47 + +#define W83781D_REG_CHIPID 0x49 +#define W83781D_REG_WCHIPID 0x58 +#define W83781D_REG_CHIPMAN 0x4F +#define W83781D_REG_PIN 0x4B + +/* 782D/783S only */ +#define W83781D_REG_VBAT 0x5D + +/* PWM 782D (1-4) and 783S (1-2) only */ +#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ + /* on which is which; */ +#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ + /* However 782d is probably wrong. */ +#define W83781D_REG_PWM3 0x5E +#define W83781D_REG_PWM4 0x5F +#define W83781D_REG_PWMCLK12 0x5C +#define W83781D_REG_PWMCLK34 0x45C +static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, + W83781D_REG_PWM3, W83781D_REG_PWM4 +}; +#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1]) + +#define W83781D_REG_I2C_ADDR 0x48 +#define W83781D_REG_I2C_SUBADDR 0x4A + +/* The following are undocumented in the data sheets however we + received the information in an email from Winbond tech support */ +/* Sensor selection - not on 781d */ +#define W83781D_REG_SCFG1 0x5D +static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; +#define W83781D_REG_SCFG2 0x59 +static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; +#define W83781D_DEFAULT_BETA 3435 + +/* RT Table registers */ +#define W83781D_REG_RT_IDX 0x50 +#define W83781D_REG_RT_VAL 0x51 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_ADD_TO_REG(val) (SENSORS_LIMIT(((((val) + 2) / 5) << 7),\ + 0,0xffff)) +#define TEMP_ADD_FROM_REG(val) (((val) >> 7) * 5) + +#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val) + 2)*4)/10) \ + << 7),0,0xffff)) +#define AS99127_TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 10) / 4) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) +#define BEEPS_FROM_REG(val) (val) +#define BEEPS_TO_REG(val) ((val) & 0xffffff) + +#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) +#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) + +#define DIV_FROM_REG(val) (1 << (val)) + +extern inline u8 DIV_TO_REG(long val, enum chips type) +{ + int i; + val = SENSORS_LIMIT(val, 1, + ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1; + for (i = 0; i < 6; i++) { + if (val == 0) + break; + val >>= 1; + } + return ((u8) i); +} + +/* Initial limits */ +#define W83781D_INIT_IN_0 (vid==350?280:vid) +#define W83781D_INIT_IN_1 (vid==350?280:vid) +#define W83781D_INIT_IN_2 330 +#define W83781D_INIT_IN_3 (((500) * 100)/168) +#define W83781D_INIT_IN_4 (((1200) * 10)/38) +#define W83781D_INIT_IN_5 (((-1200) * -604)/2100) +#define W83781D_INIT_IN_6 (((-500) * -604)/909) +#define W83781D_INIT_IN_7 (((500) * 100)/168) +#define W83781D_INIT_IN_8 300 +/* Initial limits for 782d/783s negative voltages */ +/* Note level shift. Change min/max below if you change these. */ +#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514) +#define W83782D_INIT_IN_6 ((( (-500) + 771) * 100)/314) + +#define W83781D_INIT_IN_PERCENTAGE 10 + +#define W83781D_INIT_IN_MIN_0 \ + (W83781D_INIT_IN_0 - W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_0 \ + (W83781D_INIT_IN_0 + W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_1 \ + (W83781D_INIT_IN_1 - W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_1 \ + (W83781D_INIT_IN_1 + W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_2 \ + (W83781D_INIT_IN_2 - W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_2 \ + (W83781D_INIT_IN_2 + W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_3 \ + (W83781D_INIT_IN_3 - W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_3 \ + (W83781D_INIT_IN_3 + W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_4 \ + (W83781D_INIT_IN_4 - W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_4 \ + (W83781D_INIT_IN_4 + W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_5 \ + (W83781D_INIT_IN_5 - W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_5 \ + (W83781D_INIT_IN_5 + W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_6 \ + (W83781D_INIT_IN_6 - W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_6 \ + (W83781D_INIT_IN_6 + W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_7 \ + (W83781D_INIT_IN_7 - W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_7 \ + (W83781D_INIT_IN_7 + W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_8 \ + (W83781D_INIT_IN_8 - W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_8 \ + (W83781D_INIT_IN_8 + W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +/* Initial limits for 782d/783s negative voltages */ +/* These aren't direct multiples because of level shift */ +/* Beware going negative - check */ +#define W83782D_INIT_IN_MIN_5_TMP \ + (((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_5 \ + ((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0) +#define W83782D_INIT_IN_MAX_5 \ + (((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_6_TMP \ + ((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) +#define W83782D_INIT_IN_MIN_6 \ + ((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0) +#define W83782D_INIT_IN_MAX_6 \ + ((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) + +#define W83781D_INIT_FAN_MIN_1 3000 +#define W83781D_INIT_FAN_MIN_2 3000 +#define W83781D_INIT_FAN_MIN_3 3000 + +#define W83781D_INIT_TEMP_OVER 600 +#define W83781D_INIT_TEMP_HYST 500 +#define W83781D_INIT_TEMP2_OVER 600 +#define W83781D_INIT_TEMP2_HYST 500 +#define W83781D_INIT_TEMP3_OVER 600 +#define W83781D_INIT_TEMP3_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, W83781D chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + W83781D chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the W83781D at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered W83781D, we need to keep some data in memory. That + data is pointed to by w83781d_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new w83781d client is + allocated. */ +struct w83781d_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + struct i2c_client *lm75; /* for secondary I2C addresses */ + /* pointer to array of 2 subclients */ + + u8 in[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; + u8 temp_over; /* Register value */ + u8 temp_hyst; /* Register value */ + u16 temp_add[2]; /* Register value */ + u16 temp_add_over[2]; /* Register value */ + u16 temp_add_hyst[2]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u32 beeps; /* Register encoding, combined */ + u8 beep_enable; /* Boolean */ + u8 pwm[4]; /* Register value */ + u16 sens[3]; /* 782D/783S only. + 1 = pentium diode; 2 = 3904 diode; + 3000-5000 = thermistor beta. + Default = 3435. + Other Betas unimplemented */ +#ifdef W83781D_RT + u8 rt[3][32]; /* Register value */ +#endif +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_w83781d_init(void); +static int __init w83781d_cleanup(void); + +static int w83781d_attach_adapter(struct i2c_adapter *adapter); +static int w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int w83781d_detach_client(struct i2c_client *client); +static int w83781d_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void w83781d_inc_use(struct i2c_client *client); +static void w83781d_dec_use(struct i2c_client *client); + +static int w83781d_read_value(struct i2c_client *client, u16 register); +static int w83781d_write_value(struct i2c_client *client, u16 register, + u16 value); +static void w83781d_update_client(struct i2c_client *client); +static void w83781d_init_client(struct i2c_client *client); + + +static void w83781d_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_pwm(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_sens(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#ifdef W83781D_RT +static void w83781d_rt(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#endif +static u16 swap_bytes(u16 val); + +static int w83781d_id = 0; + +static struct i2c_driver w83781d_driver = { + /* name */ "W83781D sensor driver", + /* id */ I2C_DRIVERID_W83781D, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &w83781d_attach_adapter, + /* detach_client */ &w83781d_detach_client, + /* command */ &w83781d_command, + /* inc_use */ &w83781d_inc_use, + /* dec_use */ &w83781d_dec_use +}; + +/* Used by w83781d_init/cleanup */ +static int __initdata w83781d_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected chip. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +/* just a guess - no datasheet */ +static ctl_table as99127f_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {0} +}; + +static ctl_table w83781d_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, +#ifdef W83781D_RT + {W83781D_SYSCTL_RT1, "rt1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, + {W83781D_SYSCTL_RT2, "rt2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, + {W83781D_SYSCTL_RT3, "rt3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, +#endif + {0} +}; + +/* without pwm3-4 */ +static ctl_table w83782d_isa_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + +/* with pwm3-4 */ +static ctl_table w83782d_i2c_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + +static ctl_table w83783s_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + /* no in1 to maintain compatibility with 781d and 782d. */ + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + + +/* This function is called when: + * w83781d_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83781d_driver is still present) */ +int w83781d_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, w83781d_detect); +} + +int w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, val1, val2; + struct i2c_client *new_client; + struct w83781d_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + enum vendor { winbond, asus } vendid; + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, W83781D_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83781d_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct w83781d_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct w83781d_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &w83781d_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + /* The w8378?d may be stuck in some other bank than bank 0. This may + make reading other information impossible. Specify a force=... or + force_*=... parameter, and the Winbond will be reset to the right + bank. */ + if (kind < 0) { + if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & + 0x80) goto ERROR1; + val1 = w83781d_read_value(new_client, W83781D_REG_BANK); + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + /* Check for Winbond or Asus ID if in bank 0 */ + if ((!(val1 & 0x07)) && + (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) + || ((val1 & 0x80) && (val2 != 0x5c) + && (val2 != 0x12)))) goto ERROR1; + /* If Winbond SMBus, check address at 0x48. Asus doesn't support */ + if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || + ((val1 & 0x80) && (val2 == 0x5c)))) { + if (w83781d_read_value + (new_client, W83781D_REG_I2C_ADDR) != address) + goto ERROR1; + } + } + + /* We have either had a force parameter, or we have already detected the + Winbond. Put it now into bank 0 and Vendor ID High Byte */ + w83781d_write_value(new_client, W83781D_REG_BANK, + (w83781d_read_value(new_client, + W83781D_REG_BANK) & 0x78) | + 0x80); + + /* Determine the chip type. */ + if (kind <= 0) { + /* get vendor ID */ + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if (val2 == 0x12) + vendid = asus; + else + goto ERROR1; + /* mask off lower bit, not reliable */ + val1 = + w83781d_read_value(new_client, + W83781D_REG_WCHIPID) & 0xfe; + if (val1 == 0x10 && vendid == winbond) + kind = w83781d; + else if (val1 == 0x30 && vendid == winbond) + kind = w83782d; + else if (val1 == 0x40 && vendid == winbond && !is_isa) + kind = w83783s; + else if (val1 == 0x20 && vendid == winbond) + kind = w83627hf; + else if (val1 == 0x30 && vendid == asus && !is_isa) + kind = as99127f; + else { + if (kind == 0) + printk + ("w83781d.o: Ignoring 'force' parameter for unknown chip at" + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == w83781d) { + type_name = "w83781d"; + client_name = "W83781D chip"; + } else if (kind == w83782d) { + type_name = "w83782d"; + client_name = "W83782D chip"; + } else if (kind == w83783s) { + type_name = "w83783s"; + client_name = "W83783S chip"; + } else if (kind == w83627hf) { + type_name = "w83627hf"; + client_name = "W83627HF chip"; + } else if (kind == as99127f) { + type_name = "as99127f"; + client_name = "AS99127F chip"; + } else { +#ifdef DEBUG + printk("w83781d.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, W83781D_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = w83781d_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* attach secondary i2c lm75-like clients */ + if (!is_isa) { + if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR4; + } + val1 = w83781d_read_value(new_client, + W83781D_REG_I2C_SUBADDR); + data->lm75[0].addr = 0x48 + (val1 & 0x07); + if (kind != w83783s) { + data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07); + if(data->lm75[0].addr == data->lm75[1].addr) { + printk("w83781d.o: Duplicate addresses 0x%x for subclients.\n", + data->lm75[0].addr); + goto ERROR5; + } + } + if (kind == w83781d) + client_name = "W83781D subclient"; + else if (kind == w83782d) + client_name = "W83782D subclient"; + else if (kind == w83783s) + client_name = "W83783S subclient"; + else if (kind == w83627hf) + client_name = "W83627HF subclient"; + else if (kind == as99127f) + client_name = "AS99127F subclient"; + + for (i = 0; i <= 1; i++) { + data->lm75[i].data = NULL; /* store all data in w83781d */ + data->lm75[i].adapter = adapter; + data->lm75[i].driver = &w83781d_driver; + data->lm75[i].flags = 0; + strcpy(data->lm75[i].name, client_name); + data->lm75[i].id = w83781d_id++; + if ((err = i2c_attach_client(&(data->lm75[i])))) { + printk("w83781d.o: Subclient %d registration at address 0x%x failed.\n", + i, data->lm75[i].addr); + if (i == 1) + goto ERROR6; + else + goto ERROR5; + } + if (kind == w83783s) + break; + } + } else { + data->lm75 = NULL; + } + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + kind == as99127f ? + as99127f_dir_table_template : + kind == w83781d ? + w83781d_dir_table_template : + kind == w83783s ? + w83783s_dir_table_template : + (is_isa || kind == w83627hf) ? + w83782d_isa_dir_table_template : + w83782d_i2c_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR7; + } + data->sysctl_id = i; + + /* Initialize the Winbond chip */ + w83781d_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR7: + if (!is_isa) + i2c_detach_client(& + (((struct + w83781d_data *) (new_client->data))-> + lm75[1])); + ERROR6: + if (!is_isa) + i2c_detach_client(& + (((struct + w83781d_data *) (new_client->data))-> + lm75[0])); + ERROR5: + if (!is_isa) + kfree(((struct w83781d_data *) (new_client->data))->lm75); + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, W83781D_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int w83781d_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct w83781d_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("w83781d.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) { + release_region(client->addr, W83781D_EXTENT); + } else { + i2c_detach_client(& + (((struct + w83781d_data *) (client->data))-> + lm75[0])); + if((((struct w83781d_data *) (client->data))->type) != w83783s) + i2c_detach_client(& + (((struct + w83781d_data *) (client->data))-> + lm75[1])); + kfree(((struct w83781d_data *) (client->data))->lm75); + } + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int w83781d_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void w83781d_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void w83781d_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +int w83781d_read_value(struct i2c_client *client, u16 reg) +{ + int res, word_sized, bank; + struct i2c_client *cl; + + down(&(((struct w83781d_data *) (client->data))->lock)); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); + } else { + /* switch to subclient */ + cl = + &(((struct w83781d_data *) (client->data))-> + lm75[bank - 1]); + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 3)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + 0); + } + up(&(((struct w83781d_data *) (client->data))->lock)); + return res; +} + +int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + int word_sized, bank; + struct i2c_client *cl; + + down(&(((struct w83781d_data *) (client->data))->lock)); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, + client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); + } else { + /* switch to subclient */ + cl = &(((struct w83781d_data *) (client->data))-> + lm75[bank - 1]); + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, + value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, + swap_bytes(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, + swap_bytes(value)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + 0); + } + up(&(((struct w83781d_data *) (client->data))->lock)); + return 0; +} + +/* Called when we have found a new W83781D. It should set limits, etc. */ +void w83781d_init_client(struct i2c_client *client) +{ + struct w83781d_data *data = client->data; + int vid, i; + int type = data->type; + u8 tmp; + + if(init && type != as99127f) { /* this resets registers we don't have + documentation for on the as99127f */ + /* save this register */ + i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); + /* Restore the register and disable power-on abnormal beep. + This saves FAN 1/2/3 input/output values set by BIOS. */ + w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + /* Disable master beep-enable (reset turns it on). + Individual beeps should be reset to off but for some reason + disabling this bit helps some people not get beeped */ + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); + } + + vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f; + vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) << 4; + vid = VID_FROM_REG(vid); + + if ((type != w83781d) && (type != as99127f)) { + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = W83781D_DEFAULT_BETA; + } else { + if (w83781d_read_value + (client, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83783s) && (i == 2)) + break; + } + } +#ifdef W83781D_RT +/* + Fill up the RT Tables. + We assume that they are 32 bytes long, in order for temp 1-3. + Data sheet documentation is sparse. + We also assume that it is only for the 781D although I suspect + that the others support it as well.... +*/ + + if (init && type == w83781d) { + u16 k = 0; +/* + Auto-indexing doesn't seem to work... + w83781d_write_value(client,W83781D_REG_RT_IDX,0); +*/ + for (i = 0; i < 3; i++) { + int j; + for (j = 0; j < 32; j++) { + w83781d_write_value(client, + W83781D_REG_RT_IDX, + k++); + data->rt[i][j] = + w83781d_read_value(client, + W83781D_REG_RT_VAL); + } + } + } +#endif /* W83781D_RT */ + + if(init) { + w83781d_write_value(client, W83781D_REG_IN_MIN(0), + IN_TO_REG(W83781D_INIT_IN_MIN_0)); + w83781d_write_value(client, W83781D_REG_IN_MAX(0), + IN_TO_REG(W83781D_INIT_IN_MAX_0)); + if (type != w83783s) { + w83781d_write_value(client, W83781D_REG_IN_MIN(1), + IN_TO_REG(W83781D_INIT_IN_MIN_1)); + w83781d_write_value(client, W83781D_REG_IN_MAX(1), + IN_TO_REG(W83781D_INIT_IN_MAX_1)); + } + + w83781d_write_value(client, W83781D_REG_IN_MIN(2), + IN_TO_REG(W83781D_INIT_IN_MIN_2)); + w83781d_write_value(client, W83781D_REG_IN_MAX(2), + IN_TO_REG(W83781D_INIT_IN_MAX_2)); + w83781d_write_value(client, W83781D_REG_IN_MIN(3), + IN_TO_REG(W83781D_INIT_IN_MIN_3)); + w83781d_write_value(client, W83781D_REG_IN_MAX(3), + IN_TO_REG(W83781D_INIT_IN_MAX_3)); + w83781d_write_value(client, W83781D_REG_IN_MIN(4), + IN_TO_REG(W83781D_INIT_IN_MIN_4)); + w83781d_write_value(client, W83781D_REG_IN_MAX(4), + IN_TO_REG(W83781D_INIT_IN_MAX_4)); + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83781D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83781D_INIT_IN_MAX_5)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83782D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83782D_INIT_IN_MAX_5)); + } + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83781D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83781D_INIT_IN_MAX_6)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83782D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83782D_INIT_IN_MAX_6)); + } + if ((type == w83782d) || (type == w83627hf)) { + w83781d_write_value(client, W83781D_REG_IN_MIN(7), + IN_TO_REG(W83781D_INIT_IN_MIN_7)); + w83781d_write_value(client, W83781D_REG_IN_MAX(7), + IN_TO_REG(W83781D_INIT_IN_MAX_7)); + w83781d_write_value(client, W83781D_REG_IN_MIN(8), + IN_TO_REG(W83781D_INIT_IN_MIN_8)); + w83781d_write_value(client, W83781D_REG_IN_MAX(8), + IN_TO_REG(W83781D_INIT_IN_MAX_8)); + w83781d_write_value(client, W83781D_REG_VBAT, + (w83781d_read_value(client, W83781D_REG_VBAT) | 0x01)); + } + w83781d_write_value(client, W83781D_REG_FAN_MIN(1), + FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2)); + w83781d_write_value(client, W83781D_REG_FAN_MIN(2), + FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2)); + w83781d_write_value(client, W83781D_REG_FAN_MIN(3), + FAN_TO_REG(W83781D_INIT_FAN_MIN_3, 2)); + + w83781d_write_value(client, W83781D_REG_TEMP_OVER, + TEMP_TO_REG(W83781D_INIT_TEMP_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST, + TEMP_TO_REG(W83781D_INIT_TEMP_HYST)); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP2_OVER, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP2_HYST, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } else { + w83781d_write_value(client, W83781D_REG_TEMP2_OVER, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP2_HYST, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } + w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP3_OVER, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP3_HYST, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } else if (type != w83783s) { + w83781d_write_value(client, W83781D_REG_TEMP3_OVER, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP3_HYST, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } + if (type != w83783s) { + w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG, + 0x00); + } + /* enable PWM2 control (can't hurt since PWM reg should have + been reset to 0xff) */ + if (type != w83781d) { + w83781d_write_value(client, W83781D_REG_PWMCLK12, 0x19); + } + } + + /* Start monitoring */ + w83781d_write_value(client, W83781D_REG_CONFIG, + (w83781d_read_value(client, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); +} + +void w83781d_update_client(struct i2c_client *client) +{ + struct w83781d_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting w83781d update\n"); +#endif + for (i = 0; i <= 8; i++) { + if ((data->type == w83783s) && (i == 1)) + continue; /* 783S has no in1 */ + data->in[i] = + w83781d_read_value(client, W83781D_REG_IN(i)); + data->in_min[i] = + w83781d_read_value(client, + W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83781d_read_value(client, + W83781D_REG_IN_MAX(i)); + if ((data->type != w83782d) + && (data->type != w83627hf) && (i == 6)) + break; + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN(i)); + data->fan_min[i - 1] = + w83781d_read_value(client, + W83781D_REG_FAN_MIN(i)); + } + if (data->type != w83781d) { + for (i = 1; i <= 4; i++) { + data->pwm[i - 1] = + w83781d_read_value(client, + W83781D_REG_PWM(i)); + if (((data->type == w83783s) + || (data->type == w83627hf) + || (data->type == as99127f) + || ((data->type == w83782d) + && i2c_is_isa_client(client))) + && i == 2) + break; + } + } + + data->temp = w83781d_read_value(client, W83781D_REG_TEMP); + data->temp_over = + w83781d_read_value(client, W83781D_REG_TEMP_OVER); + data->temp_hyst = + w83781d_read_value(client, W83781D_REG_TEMP_HYST); + data->temp_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP2); + data->temp_add_over[0] = + w83781d_read_value(client, W83781D_REG_TEMP2_OVER); + data->temp_add_hyst[0] = + w83781d_read_value(client, W83781D_REG_TEMP2_HYST); + if (data->type != w83783s) { + data->temp_add[1] = + w83781d_read_value(client, W83781D_REG_TEMP3); + data->temp_add_over[1] = + w83781d_read_value(client, W83781D_REG_TEMP3_OVER); + data->temp_add_hyst[1] = + w83781d_read_value(client, W83781D_REG_TEMP3_HYST); + } + i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + data->vid = i & 0x0f; + data->vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) + << 4; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->fan_div[2] = (w83781d_read_value(client, + W83781D_REG_PIN) >> + 6) & 0x03; + if ((data->type != w83781d) && (data->type != as99127f)) { + i = w83781d_read_value(client, W83781D_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + data->fan_div[2] |= (i >> 5) & 0x04; + } + data->alarms = + w83781d_read_value(client, + W83781D_REG_ALARM1) + + (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); + if ((data->type == w83782d) || (data->type == w83627hf)) { + data->alarms |= + w83781d_read_value(client, + W83781D_REG_ALARM3) << 16; + } + i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); + data->beep_enable = i >> 7; + data->beeps = ((i & 0x7f) << 8) + + w83781d_read_value(client, W83781D_REG_BEEP_INTS1); + if ((data->type != w83781d) && (data->type != as99127f)) { + data->beeps |= + w83781d_read_value(client, + W83781D_REG_BEEP_INTS3) << 16; + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void w83781d_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void w83781d_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + results[1] = FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = + FAN_TO_REG(results[0], + DIV_FROM_REG(data->fan_div[nr-1])); + w83781d_write_value(client, + W83781D_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + +void w83781d_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void w83781d_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_TEMP2; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + if (data->type == as99127f) { + results[0] = + AS99127_TEMP_ADD_FROM_REG(data-> + temp_add_over[nr]); + results[1] = + AS99127_TEMP_ADD_FROM_REG(data-> + temp_add_hyst[nr]); + results[2] = + AS99127_TEMP_ADD_FROM_REG(data->temp_add[nr]); + } else { + results[0] = + TEMP_ADD_FROM_REG(data->temp_add_over[nr]); + results[1] = + TEMP_ADD_FROM_REG(data->temp_add_hyst[nr]); + results[2] = TEMP_ADD_FROM_REG(data->temp_add[nr]); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (data->type == as99127f) + data->temp_add_over[nr] = + AS99127_TEMP_ADD_TO_REG(results[0]); + else + data->temp_add_over[nr] = + TEMP_ADD_TO_REG(results[0]); + w83781d_write_value(client, + nr ? W83781D_REG_TEMP3_OVER : + W83781D_REG_TEMP2_OVER, + data->temp_add_over[nr]); + } + if (*nrels_mag >= 2) { + if (data->type == as99127f) + data->temp_add_hyst[nr] = + AS99127_TEMP_ADD_TO_REG(results[1]); + else + data->temp_add_hyst[nr] = + TEMP_ADD_TO_REG(results[1]); + w83781d_write_value(client, + nr ? W83781D_REG_TEMP3_HYST : + W83781D_REG_TEMP2_HYST, + data->temp_add_hyst[nr]); + } + } +} + + +void w83781d_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void w83781d_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void w83781d_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int val; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 2) { + data->beeps = BEEPS_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_BEEP_INTS1, + data->beeps & 0xff); + if ((data->type != w83781d) && + (data->type != as99127f)) { + w83781d_write_value(client, + W83781D_REG_BEEP_INTS3, + ((data-> beeps) >> 16) & + 0xff); + } + val = (data->beeps >> 8) & 0x7f; + } else if (*nrels_mag >= 1) + val = + w83781d_read_value(client, + W83781D_REG_BEEP_INTS2) & + 0x7f; + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, + val | data->beep_enable << 7); + } + } +} + +void w83781d_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int old, old2, old3 = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = DIV_FROM_REG(data->fan_div[2]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + /* w83781d and as99127f don't have extended divisor bits */ + if ((data->type != w83781d) && data->type != as99127f) { + old3 = + w83781d_read_value(client, W83781D_REG_VBAT); + } + if (*nrels_mag >= 3) { + data->fan_div[2] = + DIV_TO_REG(results[2], data->type); + old2 = w83781d_read_value(client, W83781D_REG_PIN); + old2 = + (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); + w83781d_write_value(client, W83781D_REG_PIN, old2); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0x7f) | + ((data->fan_div[2] & 0x04) << 5); + } + } + if (*nrels_mag >= 2) { + data->fan_div[1] = + DIV_TO_REG(results[1], data->type); + old = + (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0xbf) | + ((data->fan_div[1] & 0x04) << 4); + } + } + if (*nrels_mag >= 1) { + data->fan_div[0] = + DIV_TO_REG(results[0], data->type); + old = + (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); + w83781d_write_value(client, W83781D_REG_VID_FANDIV, + old); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0xdf) | + ((data->fan_div[0] & 0x04) << 3); + w83781d_write_value(client, + W83781D_REG_VBAT, + old3); + } + } + } +} + +void w83781d_pwm(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = PWM_FROM_REG(data->pwm[nr - 1]); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->pwm[nr - 1] = PWM_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_PWM(nr), + data->pwm[nr - 1]); + } + } +} + +void w83781d_sens(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1; + u8 tmp; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = data->sens[nr - 1]; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + switch (results[0]) { + case 1: /* PII/Celeron diode */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - + 1]); + tmp = w83781d_read_value(client, + W83781D_REG_SCFG2); + w83781d_write_value(client, + W83781D_REG_SCFG2, + tmp | BIT_SCFG2[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + case 2: /* 3904 */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - + 1]); + tmp = w83781d_read_value(client, + W83781D_REG_SCFG2); + w83781d_write_value(client, + W83781D_REG_SCFG2, + tmp & ~BIT_SCFG2[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + case W83781D_DEFAULT_BETA: /* thermistor */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp & ~BIT_SCFG1[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + default: + printk + ("w83781d.o: Invalid sensor type %ld; must be 1, 2, or %d\n", + results[0], W83781D_DEFAULT_BETA); + break; + } + } + } +} + +#ifdef W83781D_RT +void w83781d_rt(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_RT1; + int i; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + for (i = 0; i < 32; i++) { + results[i] = data->rt[nr - 1][i]; + } + *nrels_mag = 32; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag > 32) + *nrels_mag = 32; + for (i = 0; i < *nrels_mag; i++) { + /* fixme: no bounds checking 0-255 */ + data->rt[nr - 1][i] = results[i]; + w83781d_write_value(client, W83781D_REG_RT_IDX, i); + w83781d_write_value(client, W83781D_REG_RT_VAL, + data->rt[nr - 1][i]); + } + } +} +#endif + +int __init sensors_w83781d_init(void) +{ + int res; + + printk("w83781d.o version %s (%s)\n", LM_VERSION, LM_DATE); + w83781d_initialized = 0; + + if ((res = i2c_add_driver(&w83781d_driver))) { + printk + ("w83781d.o: Driver registration failed, module not inserted.\n"); + w83781d_cleanup(); + return res; + } + w83781d_initialized++; + return 0; +} + +int __init w83781d_cleanup(void) +{ + int res; + + if (w83781d_initialized >= 1) { + if ((res = i2c_del_driver(&w83781d_driver))) { + printk + ("w83781d.o: Driver deregistration failed, module not removed.\n"); + return res; + } + w83781d_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("W83781D driver"); + +int init_module(void) +{ + return sensors_w83781d_init(); +} + +int cleanup_module(void) +{ + return w83781d_cleanup(); +} + +#endif /* MODULE */ diff -Nru a/drivers/sound/btaudio.c b/drivers/sound/btaudio.c --- a/drivers/sound/btaudio.c Wed Feb 13 20:03:40 2002 +++ b/drivers/sound/btaudio.c Wed Feb 13 20:03:40 2002 @@ -1,7 +1,7 @@ /* btaudio - bt878 audio dma driver for linux 2.4.x - (c) 2000 Gerd Knorr + (c) 2000-2002 Gerd Knorr This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -300,7 +301,7 @@ static int btaudio_mixer_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct btaudio *bta; for (bta = btaudios; bta != NULL; bta = bta->next) @@ -459,7 +460,7 @@ static int btaudio_dsp_open_digital(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct btaudio *bta; for (bta = btaudios; bta != NULL; bta = bta->next) @@ -475,7 +476,7 @@ static int btaudio_dsp_open_analog(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct btaudio *bta; for (bta = btaudios; bta != NULL; bta = bta->next) @@ -682,7 +683,7 @@ return put_user((bta->channels)-1, (int *)arg); case SNDCTL_DSP_CHANNELS: - if (!analog) { + if (!bta->analog) { if (get_user(val, (int*)arg)) return -EFAULT; bta->channels = (val > 1) ? 2 : 1; @@ -697,7 +698,7 @@ return put_user(bta->channels, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - if (analog) + if (bta->analog) return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg); else return put_user(AFMT_S16_LE, (int*)arg); @@ -706,7 +707,7 @@ if (get_user(val, (int*)arg)) return -EFAULT; if (val != AFMT_QUERY) { - if (analog) + if (bta->analog) bta->bits = (val == AFMT_S8) ? 8 : 16; else bta->bits = 16; @@ -783,7 +784,7 @@ poll_wait(file, &bta->readq, wait); - if (0 == bta->read_count) + if (0 != bta->read_count) mask |= (POLLIN | POLLRDNORM); return mask; @@ -886,10 +887,8 @@ bta = kmalloc(sizeof(*bta),GFP_ATOMIC); if (!bta) { - printk(KERN_WARNING - "btaudio: not enough memory\n"); rc = -ENOMEM; - goto fail1; + goto fail0; } memset(bta,0,sizeof(*bta)); @@ -942,6 +941,8 @@ "btaudio: can't register digital dsp (rc=%d)\n",rc); goto fail2; } + printk(KERN_INFO "btaudio: registered device dsp%d [digital]\n", + bta->dsp_digital >> 4); } if (analog) { rc = bta->dsp_analog = @@ -951,16 +952,17 @@ "btaudio: can't register analog dsp (rc=%d)\n",rc); goto fail3; } + printk(KERN_INFO "btaudio: registered device dsp%d [analog]\n", + bta->dsp_analog >> 4); rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer); if (rc < 0) { printk(KERN_WARNING "btaudio: can't register mixer (rc=%d)\n",rc); goto fail4; } + printk(KERN_INFO "btaudio: registered device mixer%d\n", + bta->mixer_dev >> 4); } - if (debug) - printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n", - bta->dsp_digital, bta->dsp_analog, bta->mixer_dev); /* hook into linked list */ bta->next = btaudios; @@ -977,9 +979,10 @@ fail2: free_irq(bta->irq,bta); fail1: + kfree(bta); + fail0: release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); - kfree(bta); return rc; } @@ -1039,16 +1042,16 @@ remove: __devexit_p(btaudio_remove), }; -int btaudio_init_module(void) +static int btaudio_init_module(void) { printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n", - analog ? "analog" : "", + digital ? "digital" : "", analog && digital ? "+" : "", - digital ? "digital" : ""); + analog ? "analog" : ""); return pci_module_init(&btaudio_pci_driver); } -void btaudio_cleanup_module(void) +static void btaudio_cleanup_module(void) { pci_unregister_driver(&btaudio_pci_driver); return; diff -Nru a/drivers/sound/es1370.c b/drivers/sound/es1370.c --- a/drivers/sound/es1370.c Wed Feb 13 20:03:51 2002 +++ b/drivers/sound/es1370.c Wed Feb 13 20:03:51 2002 @@ -397,7 +397,7 @@ * so that it cannot wreak havoc. The attribute makes sure it doesn't * cross a page boundary and ensures dword alignment for the DMA engine */ -static unsigned char bugbuf[16] __attribute__ ((aligned (16))); +static unsigned char *bugbuf; // [16] __attribute__ ((aligned (16))); /* --------------------------------------------------------------------- */ @@ -2564,6 +2564,11 @@ mm_segment_t fs; int i, val, ret; + if (bugbuf == NULL) + bugbuf = kmalloc(16, GFP_KERNEL); + if (bugbuf == NULL) + return -ENOMEM; + if ((ret=pci_enable_device(pcidev))) return ret; @@ -2747,6 +2752,8 @@ { printk(KERN_INFO "es1370: unloading\n"); pci_unregister_driver(&es1370_driver); + if(bugbuf) + kfree(bugbuf); } module_init(init_es1370); diff -Nru a/drivers/sound/i810_audio.c b/drivers/sound/i810_audio.c --- a/drivers/sound/i810_audio.c Wed Feb 13 20:03:40 2002 +++ b/drivers/sound/i810_audio.c Wed Feb 13 20:03:40 2002 @@ -110,6 +110,9 @@ #ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 #endif +#ifndef PCI_DEVICE_ID_AMD_768_AUDIO +#define PCI_DEVICE_ID_AMD_768_AUDIO 0x7445 +#endif static int ftsodell=0; static int strict_clocking=0; @@ -231,7 +234,8 @@ INTELICH2, INTELICH3, SI7012, - NVIDIA_NFORCE + NVIDIA_NFORCE, + AMD768 }; static char * card_names[] = { @@ -241,7 +245,8 @@ "Intel ICH2", "Intel ICH3", "SiS 7012", - "NVIDIA nForce Audio" + "NVIDIA nForce Audio", + "AMD 768" }; static struct pci_device_id i810_pci_tbl [] __initdata = { @@ -259,6 +264,8 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_768_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768}, {0,} }; diff -Nru a/drivers/sound/nm256_audio.c b/drivers/sound/nm256_audio.c --- a/drivers/sound/nm256_audio.c Wed Feb 13 20:03:50 2002 +++ b/drivers/sound/nm256_audio.c Wed Feb 13 20:03:50 2002 @@ -896,7 +896,7 @@ /* Reset the mixer. 'Tis magic! */ nm256_writePort8 (card, 2, 0x6c0, 1); - nm256_writePort8 (card, 2, 0x6cc, 0x87); +// nm256_writePort8 (card, 2, 0x6cc, 0x87); /* This crashes Dell latitudes */ nm256_writePort8 (card, 2, 0x6cc, 0x80); nm256_writePort8 (card, 2, 0x6cc, 0x0); diff -Nru a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c --- a/drivers/sound/sb_card.c Wed Feb 13 20:03:34 2002 +++ b/drivers/sound/sb_card.c Wed Feb 13 20:03:34 2002 @@ -373,6 +373,11 @@ 0,0,0,0, 0,1,1,-1}, {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0046), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0,0,0,0, diff -Nru a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c --- a/drivers/sound/sound_core.c Wed Feb 13 20:03:50 2002 +++ b/drivers/sound/sound_core.c Wed Feb 13 20:03:50 2002 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff -Nru a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c --- a/drivers/sound/via82cxxx_audio.c Wed Feb 13 20:03:43 2002 +++ b/drivers/sound/via82cxxx_audio.c Wed Feb 13 20:03:43 2002 @@ -365,7 +365,7 @@ name: VIA_MODULE_NAME, id_table: via_pci_tbl, probe: via_init_one, - remove: via_remove_one, + remove: __devexit_p(via_remove_one), }; @@ -3271,7 +3271,7 @@ } -static void __exit via_remove_one (struct pci_dev *pdev) +static void __devexit via_remove_one (struct pci_dev *pdev) { struct via_info *card; diff -Nru a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c --- a/drivers/telephony/ixj.c Wed Feb 13 20:03:30 2002 +++ b/drivers/telephony/ixj.c Wed Feb 13 20:03:31 2002 @@ -387,7 +387,7 @@ #ifdef PERFMON_STATS #define ixj_perfmon(x) ((x)++) #else -#define ixj_perfmon(x) do {} while(0); +#define ixj_perfmon(x) do { } while(0) #endif static int ixj_convert_loaded; diff -Nru a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c --- a/drivers/usb/pegasus.c Wed Feb 13 20:03:51 2002 +++ b/drivers/usb/pegasus.c Wed Feb 13 20:03:51 2002 @@ -53,7 +53,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.4.22 (2001/12/07)" +#define DRIVER_VERSION "v0.4.23 (2002/02/01)" #define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -94,7 +94,7 @@ static int update_eth_regs_async( pegasus_t * ); /* Aargh!!! I _really_ hate such tweaks */ -static void ctrl_callback( urb_t *urb ) +static void ctrl_callback( struct urb *urb ) { pegasus_t *pegasus = urb->context; @@ -102,7 +102,7 @@ return; switch ( urb->status ) { - case USB_ST_NOERROR: + case 0: if ( pegasus->flags & ETH_REGS_CHANGE ) { pegasus->flags &= ~ETH_REGS_CHANGE; pegasus->flags |= ETH_REGS_CHANGED; @@ -110,12 +110,12 @@ return; } break; - case USB_ST_URB_PENDING: + case -EINPROGRESS: return; - case USB_ST_URB_KILLED: + case -ENOENT: break; default: - warn( __FUNCTION__ " status %d", urb->status); + warn("%s: status %d", __FUNCTION__, urb->status); } pegasus->flags &= ~ETH_REGS_CHANGED; wake_up(&pegasus->ctrl_wait ); @@ -147,9 +147,9 @@ pegasus->dr.value = cpu_to_le16 (0); pegasus->dr.index = cpu_to_le16p(&indx); pegasus->dr.length = cpu_to_le16p(&size); - pegasus->ctrl_urb.transfer_buffer_length = size; + pegasus->ctrl_urb->transfer_buffer_length = size; - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, buffer, size, ctrl_callback, pegasus ); @@ -157,8 +157,8 @@ add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_UNINTERRUPTIBLE ); - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { - err( __FUNCTION__ " BAD CTRLs %d", ret); + if ( (ret = usb_submit_urb( pegasus->ctrl_urb )) ) { + err("%s: BAD CTRLs %d", __FUNCTION__, ret); goto out; } @@ -197,9 +197,9 @@ pegasus->dr.value = cpu_to_le16 (0); pegasus->dr.index = cpu_to_le16p( &indx ); pegasus->dr.length = cpu_to_le16p( &size ); - pegasus->ctrl_urb.transfer_buffer_length = size; + pegasus->ctrl_urb->transfer_buffer_length = size; - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, buffer, size, ctrl_callback, pegasus ); @@ -207,8 +207,8 @@ add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_UNINTERRUPTIBLE ); - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { - err( __FUNCTION__ " BAD CTRL %d", ret); + if ( (ret = usb_submit_urb( pegasus->ctrl_urb )) ) { + err("%s: BAD CTRL %d", __FUNCTION__, ret); goto out; } @@ -247,9 +247,9 @@ pegasus->dr.value = cpu_to_le16p( &dat); pegasus->dr.index = cpu_to_le16p( &indx ); pegasus->dr.length = cpu_to_le16( 1 ); - pegasus->ctrl_urb.transfer_buffer_length = 1; + pegasus->ctrl_urb->transfer_buffer_length = 1; - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, buffer, 1, ctrl_callback, pegasus ); @@ -257,8 +257,8 @@ add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_UNINTERRUPTIBLE ); - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { - err( __FUNCTION__ " BAD CTRL %d", ret); + if ( (ret = usb_submit_urb( pegasus->ctrl_urb )) ) { + err("%s: BAD CTRL %d", __FUNCTION__, ret); goto out; } @@ -280,15 +280,15 @@ pegasus->dr.value = 0; pegasus->dr.index = cpu_to_le16(EthCtrl0); pegasus->dr.length = cpu_to_le16(3); - pegasus->ctrl_urb.transfer_buffer_length = 3; + pegasus->ctrl_urb->transfer_buffer_length = 3; - FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, pegasus->eth_regs, 3, ctrl_callback, pegasus ); - if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) - err( __FUNCTION__ " BAD CTRL %d, flags %x",ret,pegasus->flags ); + if ( (ret = usb_submit_urb( pegasus->ctrl_urb )) ) + err("%s: BAD CTRL %d, flgs %x",__FUNCTION__,ret,pegasus->flags); return ret; } @@ -313,7 +313,7 @@ *regd = le16_to_cpu(regdi); return 0; } - warn( __FUNCTION__ " failed" ); + warn("%s: failed", __FUNCTION__); return 1; } @@ -335,7 +335,7 @@ } if ( i < REG_TIMEOUT ) return 0; - warn( __FUNCTION__ " failed" ); + warn("%s: failed", __FUNCTION__); return 1; } @@ -361,7 +361,7 @@ *retdata = le16_to_cpu (retdatai); return 0; } - warn( __FUNCTION__ " failed" ); + warn("%s: failed", __FUNCTION__); return -1; } @@ -405,7 +405,7 @@ disable_eprom_write( pegasus ); if ( i < REG_TIMEOUT ) return 0; - warn( __FUNCTION__ " failed" ); + warn("%s: failed", __FUNCTION__); return -1; } #endif /* PEGASUS_WRITE_EEPROM */ @@ -526,9 +526,9 @@ pegasus->flags |= PEGASUS_RX_BUSY; switch ( urb->status ) { - case USB_ST_NOERROR: + case 0: break; - case USB_ST_NORESPONSE: + case -ETIMEDOUT: dbg( "reset MAC" ); pegasus->flags &= ~PEGASUS_RX_BUSY; break; @@ -569,12 +569,12 @@ pegasus->stats.rx_bytes += pkt_len; goon: - FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb, + FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, read_bulk_callback, pegasus ); - if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) - warn( __FUNCTION__ " failed submint rx_urb %d", res); + if ( (res = usb_submit_urb(pegasus->rx_urb)) ) + warn("%s: failed submint rx_urb %d", __FUNCTION__, res); pegasus->flags &= ~PEGASUS_RX_BUSY; } @@ -607,9 +607,9 @@ return; switch ( urb->status ) { - case USB_ST_NOERROR: + case 0: break; - case USB_ST_URB_KILLED: + case -ENOENT: return; default: info("intr status %d", urb->status); @@ -639,8 +639,8 @@ return; warn("%s: Tx timed out.", net->name); - pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; - usb_unlink_urb( &pegasus->tx_urb ); + pegasus->tx_urb->transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb( pegasus->tx_urb ); pegasus->stats.tx_errors++; } @@ -656,12 +656,12 @@ ((__u16 *)pegasus->tx_buff)[0] = cpu_to_le16( l16 ); memcpy(pegasus->tx_buff+2, skb->data, skb->len); - FILL_BULK_URB( &pegasus->tx_urb, pegasus->usb, + FILL_BULK_URB( pegasus->tx_urb, pegasus->usb, usb_sndbulkpipe(pegasus->usb, 2), pegasus->tx_buff, PEGASUS_MAX_MTU, write_bulk_callback, pegasus ); - pegasus->tx_urb.transfer_buffer_length = count; - if ((res = usb_submit_urb(&pegasus->tx_urb))) { + 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 ); @@ -717,19 +717,19 @@ err("can't enable_net_traffic() - %d", res); return -EIO; } - FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb, + FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, read_bulk_callback, pegasus ); - if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) - warn( __FUNCTION__ " failed rx_urb %d", res ); + if ( (res = usb_submit_urb(pegasus->rx_urb)) ) + warn("%s: failed rx_urb %d", __FUNCTION__, res); #ifdef PEGASUS_USE_INTR - FILL_INT_URB( &pegasus->intr_urb, pegasus->usb, + FILL_INT_URB( pegasus->intr_urb, pegasus->usb, usb_rcvintpipe(pegasus->usb, 3), pegasus->intr_buff, sizeof(pegasus->intr_buff), intr_callback, pegasus, pegasus->intr_interval ); - if ( (res = usb_submit_urb(&pegasus->intr_urb)) ) - warn( __FUNCTION__ " failed intr_urb %d", res); + if ( (res = usb_submit_urb(pegasus->intr_urb)) ) + warn("%s: failed intr_urb %d", __FUNCTION__, res); #endif netif_start_queue( net ); pegasus->flags |= PEGASUS_RUNNING; @@ -747,11 +747,11 @@ if ( !(pegasus->flags & PEGASUS_UNPLUG) ) disable_net_traffic( pegasus ); - usb_unlink_urb( &pegasus->rx_urb ); - usb_unlink_urb( &pegasus->tx_urb ); - usb_unlink_urb( &pegasus->ctrl_urb ); + usb_unlink_urb( pegasus->rx_urb ); + usb_unlink_urb( pegasus->tx_urb ); + usb_unlink_urb( pegasus->ctrl_urb ); #ifdef PEGASUS_USE_INTR - usb_unlink_urb( &pegasus->intr_urb ); + usb_unlink_urb( pegasus->intr_urb ); #endif return 0; @@ -800,7 +800,7 @@ } pegasus->flags |= ETH_REGS_CHANGE; - ctrl_callback( &pegasus->ctrl_urb ); + ctrl_callback( pegasus->ctrl_urb ); netif_wake_queue(net); } @@ -856,8 +856,38 @@ pegasus->dev_index = dev_index; init_waitqueue_head( &pegasus->ctrl_wait ); + pegasus->ctrl_urb = usb_alloc_urb(0); + if (!pegasus->ctrl_urb) { + kfree (pegasus); + return NULL; + } + pegasus->rx_urb = usb_alloc_urb(0); + if (!pegasus->rx_urb) { + usb_free_urb (pegasus->ctrl_urb); + kfree (pegasus); + return NULL; + } + pegasus->tx_urb = usb_alloc_urb(0); + if (!pegasus->tx_urb) { + usb_free_urb (pegasus->rx_urb); + usb_free_urb (pegasus->ctrl_urb); + kfree (pegasus); + return NULL; + } + pegasus->intr_urb = usb_alloc_urb(0); + if (!pegasus->intr_urb) { + usb_free_urb (pegasus->tx_urb); + usb_free_urb (pegasus->rx_urb); + usb_free_urb (pegasus->ctrl_urb); + kfree (pegasus); + return NULL; + } + net = init_etherdev( NULL, 0 ); if ( !net ) { + usb_free_urb (pegasus->tx_urb); + usb_free_urb (pegasus->rx_urb); + usb_free_urb (pegasus->ctrl_urb); kfree( pegasus ); return NULL; } @@ -883,6 +913,9 @@ if ( reset_mac(pegasus) ) { err("can't reset MAC"); unregister_netdev( pegasus->net ); + usb_free_urb (pegasus->tx_urb); + usb_free_urb (pegasus->rx_urb); + usb_free_urb (pegasus->ctrl_urb); kfree(pegasus->net); kfree(pegasus); pegasus = NULL; @@ -920,6 +953,14 @@ pegasus->flags |= PEGASUS_UNPLUG; unregister_netdev( pegasus->net ); usb_dec_dev_use( dev ); + usb_unlink_urb(pegasus->intr_urb); + usb_unlink_urb(pegasus->tx_urb); + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->ctrl_urb); + usb_free_urb(pegasus->intr_urb); + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); kfree( pegasus->net ); kfree( pegasus ); pegasus = NULL; diff -Nru a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h --- a/drivers/usb/pegasus.h Wed Feb 13 20:03:50 2002 +++ b/drivers/usb/pegasus.h Wed Feb 13 20:03:51 2002 @@ -107,7 +107,7 @@ unsigned features; int dev_index; int intr_interval; - struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; + struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; devrequest dr; wait_queue_head_t ctrl_wait; struct semaphore ctrl_sem; @@ -134,9 +134,11 @@ #define VENDOR_ALLIEDTEL 0x07c9 #define VENDOR_BELKIN 0x050d #define VENDOR_BILLIONTON 0x08dd +#define VENDOR_COMPAQ 0x049f #define VENDOR_COREGA 0x07aa #define VENDOR_DLINK 0x2001 #define VENDOR_ELSA 0x05cc +#define VENDOR_HAWKING 0x0e66 #define VENDOR_IODATA 0x04bb #define VENDOR_KINGSTON 0x0951 #define VENDOR_LANEED 0x056e @@ -145,6 +147,7 @@ #define VENDOR_SMARTBRIDGES 0x08d1 #define VENDOR_SMC 0x0707 #define VENDOR_SOHOWARE 0x15e8 +#define VENDOR_SIEMENS 0x067c #else /* PEGASUS_DEV */ @@ -173,6 +176,8 @@ DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_ACCTON, 0x5046, + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", VENDOR_ADMTEK, 0x8511, DEFAULT_GPIO_RESET | PEGASUS_II ) @@ -187,6 +192,8 @@ DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "iPAQ Networking 10/100 USB", VENDOR_COMPAQ, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, @@ -209,8 +216,12 @@ DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, DEFAULT_GPIO_RESET) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, @@ -244,6 +255,8 @@ PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, DEFAULT_GPIO_RESET ) diff -Nru a/drivers/video/Config.in b/drivers/video/Config.in --- a/drivers/video/Config.in Wed Feb 13 20:03:55 2002 +++ b/drivers/video/Config.in Wed Feb 13 20:03:55 2002 @@ -128,7 +128,8 @@ dep_tristate ' G400 second head support' CONFIG_FB_MATROX_MAVEN $CONFIG_FB_MATROX_I2C fi fi - dep_tristate ' G450/G550 second head support' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100 + dep_tristate ' G450/G550 second head support (mandatory for G550)' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100 + dep_tristate ' Matrox /proc interface' CONFIG_FB_MATROX_PROC $CONFIG_FB_MATROX bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD fi tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY @@ -143,6 +144,7 @@ bool ' SIS 630/540/730 support' CONFIG_FB_SIS_300 bool ' SIS 315H/315 support' CONFIG_FB_SIS_315 fi + tristate ' NeoMagic display support (EXPERIMENTAL)' CONFIG_FB_NEOMAGIC tristate ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX tristate ' 3Dfx Voodoo Graphics (sst1) support (EXPERIMENTAL)' CONFIG_FB_VOODOO1 tristate ' Trident support (EXPERIMENTAL)' CONFIG_FB_TRIDENT @@ -269,7 +271,7 @@ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ "$CONFIG_FB_PMAG_BA" = "y" -o "$CONFIG_FB_PMAGB_B" = "y" -o \ "$CONFIG_FB_MAXINE" = "y" -o "$CONFIG_FB_TX3912" = "y" -o \ - "$CONFIG_FB_SIS" = "y" ]; then + "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" ]; then define_tristate CONFIG_FBCON_CFB8 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ @@ -289,7 +291,7 @@ "$CONFIG_FB_PMAG_BA" = "m" -o "$CONFIG_FB_PMAGB_B" = "m" -o \ "$CONFIG_FB_MAXINE" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ - "$CONFIG_FB_TX3912" = "m" ]; then + "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_NEOMAGIC" = "m" ]; then define_tristate CONFIG_FBCON_CFB8 m fi fi @@ -305,7 +307,8 @@ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_SA1100" = "y" -o \ - "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" ]; then + "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" -o \ + "$CONFIG_FB_NEOMAGIC" = "y" ]; then define_tristate CONFIG_FBCON_CFB16 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -320,7 +323,8 @@ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ - "$CONFIG_FB_PVR2" = "m" -o "$CONFIG_FB_VOODOO1" = "m" ]; then + "$CONFIG_FB_PVR2" = "m" -o "$CONFIG_FB_VOODOO1" = "m" -o \ + "$CONFIG_FB_NEOMAGIC" = "m" ]; then define_tristate CONFIG_FBCON_CFB16 m fi fi @@ -329,7 +333,7 @@ "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_ATY128" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_PVR2" = "y" -o \ - "$CONFIG_FB_VOODOO1" = "y" ]; then + "$CONFIG_FB_VOODOO1" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" ]; then define_tristate CONFIG_FBCON_CFB24 y else if [ "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ @@ -337,7 +341,7 @@ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_ATY128" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_PVR2" = "m" -o \ - "$CONFIG_FB_VOODOO1" = "m" ]; then + "$CONFIG_FB_VOODOO1" = "m" -o "$CONFIG_FB_NEOMAGIC" = "y" ]; then define_tristate CONFIG_FBCON_CFB24 m fi fi diff -Nru a/drivers/video/Makefile b/drivers/video/Makefile --- a/drivers/video/Makefile Wed Feb 13 20:03:35 2002 +++ b/drivers/video/Makefile Wed Feb 13 20:03:35 2002 @@ -49,6 +49,7 @@ obj-$(CONFIG_FB_ATARI) += atafb.o obj-$(CONFIG_FB_ATY128) += aty128fb.o obj-$(CONFIG_FB_RADEON) += radeonfb.o +obj-$(CONFIG_FB_NEOMAGIC) += neofb.o obj-$(CONFIG_FB_IGA) += igafb.o obj-$(CONFIG_FB_CONTROL) += controlfb.o obj-$(CONFIG_FB_PLATINUM) += platinumfb.o diff -Nru a/drivers/video/acornfb.c b/drivers/video/acornfb.c --- a/drivers/video/acornfb.c Wed Feb 13 20:03:50 2002 +++ b/drivers/video/acornfb.c Wed Feb 13 20:03:50 2002 @@ -1139,9 +1139,6 @@ off += start; vma->vm_pgoff = off >> PAGE_SHIFT; - /* This is an IO map - tell maydump to skip this VMA */ - vma->vm_flags |= VM_IO; - #ifdef CONFIG_CPU_32 pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE; #endif diff -Nru a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c --- a/drivers/video/chipsfb.c Wed Feb 13 20:03:51 2002 +++ b/drivers/video/chipsfb.c Wed Feb 13 20:03:51 2002 @@ -79,7 +79,7 @@ } while (0) #define read_ind(num, var, ap, dp) do { \ outb((num), (ap)); var = inb((dp)); \ -} while (0); +} while (0) /* extension registers */ #define write_xr(num, val) write_ind(num, val, 0x3d6, 0x3d7) diff -Nru a/drivers/video/fbcon-cfb24.c b/drivers/video/fbcon-cfb24.c --- a/drivers/video/fbcon-cfb24.c Wed Feb 13 20:03:41 2002 +++ b/drivers/video/fbcon-cfb24.c Wed Feb 13 20:03:41 2002 @@ -76,14 +76,14 @@ out1 = (in1<<8) | (in2>>16); \ out2 = (in2<<16) | (in3>>8); \ out3 = (in3<<24) | in4; \ - } while (0); + } while (0) #elif defined(__LITTLE_ENDIAN) #define convert4to3(in1, in2, in3, in4, out1, out2, out3) \ do { \ out1 = in1 | (in2<<24); \ out2 = (in2>> 8) | (in3<<16); \ out3 = (in3>>16) | (in4<< 8); \ - } while (0); + } while (0) #else #error FIXME: No endianness?? #endif diff -Nru a/drivers/video/fbmem.c b/drivers/video/fbmem.c --- a/drivers/video/fbmem.c Wed Feb 13 20:03:29 2002 +++ b/drivers/video/fbmem.c Wed Feb 13 20:03:29 2002 @@ -72,6 +72,8 @@ extern int atyfb_setup(char*); extern int aty128fb_init(void); extern int aty128fb_setup(char*); +extern int neofb_init(void); +extern int neofb_setup(char*); extern int igafb_init(void); extern int igafb_setup(char*); extern int imsttfb_init(void); @@ -174,6 +176,9 @@ #ifdef CONFIG_FB_ATY128 { "aty128fb", aty128fb_init, aty128fb_setup }, #endif +#ifdef CONFIG_FB_NEOMAGIC + { "neo", neofb_init, neofb_setup }, +#endif #ifdef CONFIG_FB_VIRGE { "virge", virgefb_init, virgefb_setup }, #endif @@ -548,6 +553,8 @@ lock_kernel(); res = fb->fb_mmap(info, file, vma); unlock_kernel(); + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO; return res; } @@ -581,12 +588,13 @@ return -EINVAL; off += start; vma->vm_pgoff = off >> PAGE_SHIFT; + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO; #if defined(__sparc_v9__) vma->vm_flags |= (VM_SHM | VM_LOCKED); if (io_remap_page_range(vma->vm_start, off, vma->vm_end - vma->vm_start, vma->vm_page_prot, 0)) return -EAGAIN; - vma->vm_flags |= VM_IO; #else #if defined(__mc68000__) #if defined(CONFIG_SUN3) @@ -612,8 +620,6 @@ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; #elif defined(__arm__) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - /* This is an IO map - tell maydump to skip this VMA */ - vma->vm_flags |= VM_IO; #elif defined(__sh__) pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE; #else diff -Nru a/drivers/video/igafb.c b/drivers/video/igafb.c --- a/drivers/video/igafb.c Wed Feb 13 20:03:30 2002 +++ b/drivers/video/igafb.c Wed Feb 13 20:03:30 2002 @@ -293,8 +293,6 @@ if (!map_size) return -EINVAL; - vma->vm_flags |= VM_IO; - if (!fb->mmaped) { int lastconsole = 0; diff -Nru a/drivers/video/matrox/Makefile b/drivers/video/matrox/Makefile --- a/drivers/video/matrox/Makefile Wed Feb 13 20:03:42 2002 +++ b/drivers/video/matrox/Makefile Wed Feb 13 20:03:42 2002 @@ -7,14 +7,17 @@ # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o +export-objs := matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o g450_pll.o # Each configuration option enables a list of files. -obj-$(CONFIG_FB_MATROX) += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o +my-obj-$(CONFIG_FB_MATROX_G100) := g450_pll.o + +obj-$(CONFIG_FB_MATROX) += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o $(my-obj-y) obj-$(CONFIG_FB_MATROX_I2C) += i2c-matroxfb.o obj-$(CONFIG_FB_MATROX_MAVEN) += matroxfb_maven.o matroxfb_crtc2.o obj-$(CONFIG_FB_MATROX_G450) += matroxfb_g450.o matroxfb_crtc2.o +obj-$(CONFIG_FB_MATROX_PROC) += matroxfb_proc.o include $(TOPDIR)/Rules.make diff -Nru a/drivers/video/matrox/g450_pll.c b/drivers/video/matrox/g450_pll.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/matrox/g450_pll.c Wed Feb 13 20:03:57 2002 @@ -0,0 +1,472 @@ +/* + * + * Hardware accelerated Matrox PCI cards - G450/G550 PLL control. + * + * (c) 2001 Petr Vandrovec + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ + +#include "g450_pll.h" +#include "matroxfb_DAC1064.h" + +static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) { + return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1); +} + +static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { + return (p & 0x40) ? fin : fin << ((p & 3) + 1); +} + +static unsigned int g450_mnp2vco(CPMINFO_ unsigned int mnp) { + unsigned int m, n; + + m = ((mnp >> 16) & 0x0FF) + 1; + n = ((mnp >> 7) & 0x1FE) + 4; + return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m; +} + +static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { + if (f2 < f1) { + f2 = f1 - f2; + } else { + f2 = f2 - f1; + } + return f2; +} + +#define NO_MORE_MNP 0x01FFFFFF +#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ + +static unsigned int g450_nextpll(CPMINFO_ const struct matrox_pll_limits* pi, unsigned int* fvco, unsigned int mnp) { + unsigned int m, n, p; + unsigned int tvco = *fvco; + + m = (mnp >> 16) & 0xFF; + p = mnp & 0xFF; + + if (m == 0 || m == 0xFF) { + if (m == 0) { + if (p & 0x40) { + return NO_MORE_MNP; + } + if (p & 3) { + p--; + } else { + p = 0x40; + } + tvco >>= 1; + if (tvco < pi->vcomin) { + return NO_MORE_MNP; + } + *fvco = tvco; + } + + p &= 0x43; + if (tvco < 550000) { +/* p |= 0x00; */ + } else if (tvco < 700000) { + p |= 0x08; + } else if (tvco < 1000000) { + p |= 0x10; + } else if (tvco < 1150000) { + p |= 0x18; + } else { + p |= 0x20; + } + m = 9; + } else { + m--; + } + n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2; + return (m << 16) | (n << 8) | p; +} + +static unsigned int g450_firstpll(CPMINFO_ const struct matrox_pll_limits* pi, unsigned int* vco, unsigned int fout) { + unsigned int p; + unsigned int vcomax; + + vcomax = pi->vcomax; + if (fout > (vcomax / 2)) { + if (fout > vcomax) { + *vco = vcomax; + } else { + *vco = fout; + } + p = 0x40; + } else { + unsigned int tvco; + + p = 3; + tvco = g450_f2vco(p, fout); + while (p && (tvco > vcomax)) { + p--; + tvco >>= 1; + } + if (tvco < pi->vcomin) { + tvco = pi->vcomin; + } + *vco = tvco; + } + return g450_nextpll(PMINFO_ pi, vco, 0xFF0000 | p); +} + +static inline unsigned int g450_setpll(CPMINFO_ unsigned int mnp, unsigned int pll) { + switch (pll) { + case M_PIXEL_PLL_A: + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLAM, mnp >> 16); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLAN, mnp >> 8); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLAP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_B: + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLBM, mnp >> 16); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLBN, mnp >> 8); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLBP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_C: + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLCM, mnp >> 16); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLCN, mnp >> 8); + matroxfb_DAC_out(PMINFO_ M1064_XPIXPLLCP, mnp); + return M1064_XPIXPLLSTAT; + + case M_SYSTEM_PLL: + matroxfb_DAC_out(PMINFO_ DAC1064_XSYSPLLM, mnp >> 16); + matroxfb_DAC_out(PMINFO_ DAC1064_XSYSPLLN, mnp >> 8); + matroxfb_DAC_out(PMINFO_ DAC1064_XSYSPLLP, mnp); + return DAC1064_XSYSPLLSTAT; + + case M_VIDEO_PLL: + matroxfb_DAC_out(PMINFO_ M1064_XVIDPLLM, mnp >> 16); + matroxfb_DAC_out(PMINFO_ M1064_XVIDPLLN, mnp >> 8); + matroxfb_DAC_out(PMINFO_ M1064_XVIDPLLP, mnp); + return M1064_XVIDPLLSTAT; + } + return 0; +} + +static inline unsigned int g450_cmppll(CPMINFO_ unsigned int mnp, unsigned int pll) { + unsigned char m = mnp >> 16; + unsigned char n = mnp >> 8; + unsigned char p = mnp; + + switch (pll) { + case M_PIXEL_PLL_A: + return (matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLAM) != m || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLAN) != n || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLAP) != p); + + case M_PIXEL_PLL_B: + return (matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLBM) != m || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLBN) != n || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLBP) != p); + + case M_PIXEL_PLL_C: + return (matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLCM) != m || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLCN) != n || + matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLCP) != p); + + case M_SYSTEM_PLL: + return (matroxfb_DAC_in(PMINFO_ DAC1064_XSYSPLLM) != m || + matroxfb_DAC_in(PMINFO_ DAC1064_XSYSPLLN) != n || + matroxfb_DAC_in(PMINFO_ DAC1064_XSYSPLLP) != p); + + case M_VIDEO_PLL: + return (matroxfb_DAC_in(PMINFO_ M1064_XVIDPLLM) != m || + matroxfb_DAC_in(PMINFO_ M1064_XVIDPLLN) != n || + matroxfb_DAC_in(PMINFO_ M1064_XVIDPLLP) != p); + } + return 1; +} + +static inline int g450_isplllocked(CPMINFO_ unsigned int regidx) { + unsigned int j; + + for (j = 0; j < 1000; j++) { + if (matroxfb_DAC_in(PMINFO_ regidx) & 0x40) { + unsigned int r = 0; + int i; + + for (i = 0; i < 100; i++) { + r += matroxfb_DAC_in(PMINFO_ regidx) & 0x40; + } + return r >= (90 * 0x40); + } + /* udelay(1)... but DAC_in is much slower... */ + } + return 0; +} + +static int g450_testpll(CPMINFO_ unsigned int mnp, unsigned int pll) { + return g450_isplllocked(PMINFO_ g450_setpll(PMINFO_ mnp, pll)); +} + +static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { + switch (pll) { + case M_SYSTEM_PLL: + hw->DACclk[3] = mnp >> 16; + hw->DACclk[4] = mnp >> 8; + hw->DACclk[5] = mnp; + break; + } +} + +static inline void g450_setpll_cond(WPMINFO_ unsigned int mnp, unsigned int pll) { + if (g450_cmppll(PMINFO_ mnp, pll)) { + g450_setpll(PMINFO_ mnp, pll); + } +} + +static inline unsigned int g450_findworkingpll(WPMINFO_ unsigned int pll, unsigned int* mnparray, unsigned int mnpcount) { + unsigned int found = 0; + unsigned int idx; + unsigned int mnpfound = mnparray[0]; + + for (idx = 0; idx < mnpcount; idx++) { + unsigned int sarray[3]; + unsigned int *sptr; + { + unsigned int mnp; + + sptr = sarray; + mnp = mnparray[idx]; + if (mnp & 0x38) { + *sptr++ = mnp - 8; + } + if ((mnp & 0x38) != 0x38) { + *sptr++ = mnp + 8; + } + *sptr = mnp; + } + while (sptr >= sarray) { + unsigned int mnp = *sptr--; + + if (g450_testpll(PMINFO_ mnp - 0x0300, pll) && + g450_testpll(PMINFO_ mnp + 0x0300, pll) && + g450_testpll(PMINFO_ mnp - 0x0200, pll) && + g450_testpll(PMINFO_ mnp + 0x0200, pll) && + g450_testpll(PMINFO_ mnp - 0x0100, pll) && + g450_testpll(PMINFO_ mnp + 0x0100, pll)) { + if (g450_testpll(PMINFO_ mnp, pll)) { + return mnp; + } + } else if (!found && g450_testpll(PMINFO_ mnp, pll)) { + mnpfound = mnp; + found = 1; + } + } + } + g450_setpll(PMINFO_ mnpfound, pll); + return mnpfound; +} + +static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) { + if (++ci->valid > ARRAY_SIZE(ci->data)) { + ci->valid = ARRAY_SIZE(ci->data); + } + memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS; + ci->data[0].mnp_value = mnp_value; +} + +static int g450_checkcache(WPMINFO_ struct matrox_pll_cache* ci, unsigned int mnp_key) { + unsigned int i; + + mnp_key &= G450_MNP_FREQBITS; + for (i = 0; i < ci->valid; i++) { + if (ci->data[i].mnp_key == mnp_key) { + unsigned int mnp; + + mnp = ci->data[i].mnp_value; + if (i) { + memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key; + ci->data[0].mnp_value = mnp; + } + return mnp; + } + } + return NO_MORE_MNP; +} + +static int __g450_setclk(WPMINFO_ unsigned int fout, unsigned int pll, + unsigned int* mnparray, unsigned int* deltaarray) { + unsigned int mnpcount; + unsigned int pixel_vco; + const struct matrox_pll_limits* pi; + struct matrox_pll_cache* ci; + + pixel_vco = 0; + switch (pll) { + case M_PIXEL_PLL_A: + case M_PIXEL_PLL_B: + case M_PIXEL_PLL_C: + { + u_int8_t tmp; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + tmp = matroxfb_DAC_in(PMINFO_ M1064_XPIXCLKCTRL); + if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { + matroxfb_DAC_out(PMINFO_ M1064_XPIXCLKCTRL, tmp | M1064_XPIXCLKCTRL_PLL_UP); + } + matroxfb_DAC_unlock_irqrestore(flags); + } + { + u_int8_t misc; + + misc = mga_inb(M_MISC_REG_READ) & ~0x0C; + switch (pll) { + case M_PIXEL_PLL_A: + break; + case M_PIXEL_PLL_B: + misc |= 0x04; + break; + default: + misc |= 0x0C; + break; + } + mga_outb(M_MISC_REG, misc); + } + pi = &ACCESS_FBINFO(limits.pixel); + ci = &ACCESS_FBINFO(cache.pixel); + break; + case M_SYSTEM_PLL: + { + u_int32_t opt; + + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, &opt); + if (!(opt & 0x20)) { + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, opt | 0x20); + } + } + pi = &ACCESS_FBINFO(limits.system); + ci = &ACCESS_FBINFO(cache.system); + break; + case M_VIDEO_PLL: + { + u_int8_t tmp; + unsigned int mnp; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + tmp = matroxfb_DAC_in(PMINFO_ M1064_XPWRCTRL); + if (!(tmp & 2)) { + matroxfb_DAC_out(PMINFO_ M1064_XPWRCTRL, tmp | 2); + } + + mnp = matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLCM) << 16; + mnp |= matroxfb_DAC_in(PMINFO_ M1064_XPIXPLLCN) << 8; + pixel_vco = g450_mnp2vco(PMINFO_ mnp); + matroxfb_DAC_unlock_irqrestore(flags); + } + pi = &ACCESS_FBINFO(limits.video); + ci = &ACCESS_FBINFO(cache.video); + break; + default: + return -EINVAL; + } + + mnpcount = 0; + { + unsigned int mnp; + unsigned int xvco; + + for(mnp = g450_firstpll(PMINFO_ pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(PMINFO_ pi, &xvco, mnp)) { + unsigned int idx; + unsigned int vco; + unsigned int delta; + + if ((mnp & 0xFF00) < 0x0300 || (mnp & 0xFF00) > 0x7A00) { + continue; + } + vco = g450_mnp2vco(PMINFO_ mnp); + if (pll == M_VIDEO_PLL) { + unsigned int big, small; + + if (vco < pixel_vco) { + small = vco; + big = pixel_vco; + } else { + small = pixel_vco; + big = vco; + } + while (big > small) { + big >>= 1; + } + if (big == small) { + continue; + } + } + delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); + for (idx = mnpcount; idx > 0; idx--) { + /* == is important; due to nextpll algorithm we get + sorted equally good frequencies from lower VCO + frequency to higher - with <= lowest wins, while + with < highest one wins */ + if (delta <= deltaarray[idx-1]) { + mnparray[idx] = mnparray[idx-1]; + deltaarray[idx] = deltaarray[idx-1]; + } else { + break; + } + } + mnparray[idx] = mnp; + deltaarray[idx] = delta; + mnpcount++; + } + } + /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */ + if (!mnpcount) { + return 1; + } + { + unsigned long flags; + unsigned int mnp; + + matroxfb_DAC_lock_irqsave(flags); + mnp = g450_checkcache(PMINFO_ ci, mnparray[0]); + if (mnp != NO_MORE_MNP) { + g450_setpll_cond(PMINFO_ mnp, pll); + } else { + mnp = g450_findworkingpll(PMINFO_ pll, mnparray, mnpcount); + g450_addcache(ci, mnparray[0], mnp); + } + updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll); + matroxfb_DAC_unlock_irqrestore(flags); + } + return 0; +} + +/* It must be greater than number of possible PLL values. + * Currently there is 5(p) * 10(m) = 50 possible values. */ +#define MNP_TABLE_SIZE 64 + +int matroxfb_g450_setclk(WPMINFO_ unsigned int fout, unsigned int pll) { + unsigned int* arr; + + arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); + if (arr) { + int r; + + r = __g450_setclk(PMINFO_ fout, pll, arr, arr + MNP_TABLE_SIZE); + kfree(arr); + return r; + } + return -ENOMEM; +} + +EXPORT_SYMBOL(matroxfb_g450_setclk); + +MODULE_AUTHOR("(c) 2001 Petr Vandrovec "); +MODULE_DESCRIPTION("Matrox G450/G550 PLL driver"); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/video/matrox/g450_pll.h b/drivers/video/matrox/g450_pll.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/matrox/g450_pll.h Wed Feb 13 20:03:56 2002 @@ -0,0 +1,8 @@ +#ifndef __G450_PLL_H__ +#define __G450_PLL_H__ + +#include "matroxfb_base.h" + +int matroxfb_g450_setclk(WPMINFO_ unsigned int fout, unsigned int pll); + +#endif /* __G450_PLL_H__ */ diff -Nru a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c --- a/drivers/video/matrox/i2c-matroxfb.c Wed Feb 13 20:03:51 2002 +++ b/drivers/video/matrox/i2c-matroxfb.c Wed Feb 13 20:03:51 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/01/19 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -35,20 +37,20 @@ int v; matroxfb_DAC_lock_irqsave(flags); - v = matroxfb_DAC_in(PMINFO DAC_XGENIODATA); + v = matroxfb_DAC_in(PMINFO_ DAC_XGENIODATA); matroxfb_DAC_unlock_irqrestore(flags); return v; } -static inline void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { +static inline void matroxfb_set_gpio(WPMINFO_ int mask, int val) { unsigned long flags; int v; matroxfb_DAC_lock_irqsave(flags); - v = (matroxfb_DAC_in(PMINFO DAC_XGENIOCTRL) & mask) | val; - matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, v); + v = (matroxfb_DAC_in(PMINFO_ DAC_XGENIOCTRL) & mask) | val; + matroxfb_DAC_out(PMINFO_ DAC_XGENIOCTRL, v); /* We must reset GENIODATA very often... XFree plays with this register */ - matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0x00); + matroxfb_DAC_out(PMINFO_ DAC_XGENIODATA, 0x00); matroxfb_DAC_unlock_irqrestore(flags); } @@ -58,7 +60,7 @@ state = 0; else state = mask; - matroxfb_set_gpio(minfo, ~mask, state); + matroxfb_set_gpio(PMINFO_ ~mask, state); } static void matroxfb_maven_setsda(void* data, int state) { @@ -294,8 +296,8 @@ return NULL; matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0xFF); - matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, 0x00); + matroxfb_DAC_out(PMINFO_ DAC_XGENIODATA, 0xFF); + matroxfb_DAC_out(PMINFO_ DAC_XGENIOCTRL, 0x00); matroxfb_DAC_unlock_irqrestore(flags); memset(m2info, 0, sizeof(*m2info)); diff -Nru a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c --- a/drivers/video/matrox/matroxfb_DAC1064.c Wed Feb 13 20:03:51 2002 +++ b/drivers/video/matrox/matroxfb_DAC1064.c Wed Feb 13 20:03:51 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -16,6 +18,7 @@ #include "matroxfb_DAC1064.h" #include "matroxfb_misc.h" #include "matroxfb_accel.h" +#include "g450_pll.h" #include #ifdef NEED_DAC1064 @@ -37,14 +40,14 @@ #define minfo ((struct matrox_fb_info*)ptr) matroxfb_DAC_lock_irqsave(flags); - outDAC1064(PMINFO M1064_XCURCTRL, inDAC1064(PMINFO M1064_XCURCTRL) ^ M1064_XCURCTRL_DIS ^ M1064_XCURCTRL_XGA); + outDAC1064(PMINFO_ M1064_XCURCTRL, inDAC1064(PMINFO_ M1064_XCURCTRL) ^ M1064_XCURCTRL_DIS ^ M1064_XCURCTRL_XGA); ACCESS_FBINFO(cursor.timer.expires) = jiffies + HZ/2; add_timer(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_unlock_irqrestore(flags); #undef minfo } -static void matroxfb_DAC1064_createcursor(WPMINFO struct display* p) { +static void matroxfb_DAC1064_createcursor(WPMINFO_ struct display* p) { vaddr_t cursorbase; u_int32_t xline; unsigned int i; @@ -54,7 +57,7 @@ if (ACCESS_FBINFO(currcon_display) != p) return; - matroxfb_createcursorshape(PMINFO p, p->var.vmode); + matroxfb_createcursorshape(PMINFO_ p, p->var.vmode); xline = (~0) << (32 - ACCESS_FBINFO(cursor.w)); cursorbase = ACCESS_FBINFO(video.vbase); @@ -108,13 +111,13 @@ del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); ACCESS_FBINFO(cursor.state) = CM_ERASE; - outDAC1064(PMINFO M1064_XCURCTRL, M1064_XCURCTRL_DIS); + outDAC1064(PMINFO_ M1064_XCURCTRL, M1064_XCURCTRL_DIS); matroxfb_DAC_unlock_irqrestore(flags); } return; } if ((p->conp->vc_cursor_type & CUR_HWMASK) != ACCESS_FBINFO(cursor.type)) - matroxfb_DAC1064_createcursor(PMINFO p); + matroxfb_DAC1064_createcursor(PMINFO_ p); x *= fontwidth(p); y *= fontheight(p); y -= p->var.yoffset; @@ -128,7 +131,7 @@ ACCESS_FBINFO(cursor.y) = y; x += 64; y += 64; - outDAC1064(PMINFO M1064_XCURCTRL, M1064_XCURCTRL_DIS); + outDAC1064(PMINFO_ M1064_XCURCTRL, M1064_XCURCTRL_DIS); mga_outb(M_RAMDAC_BASE+M1064_CURPOSXL, x); mga_outb(M_RAMDAC_BASE+M1064_CURPOSXH, x >> 8); mga_outb(M_RAMDAC_BASE+M1064_CURPOSYL, y); @@ -137,7 +140,7 @@ ACCESS_FBINFO(cursor.state) = CM_DRAW; if (ACCESS_FBINFO(devflags.blink)) mod_timer(&ACCESS_FBINFO(cursor.timer), jiffies + HZ/2); - outDAC1064(PMINFO M1064_XCURCTRL, M1064_XCURCTRL_XGA); + outDAC1064(PMINFO_ M1064_XCURCTRL, M1064_XCURCTRL_XGA); matroxfb_DAC_unlock_irqrestore(flags); } @@ -147,19 +150,19 @@ return 0; } -static int DAC1064_selhwcursor(WPMINFO struct display* p) { +static int DAC1064_selhwcursor(WPMINFO_ struct display* p) { ACCESS_FBINFO(dispsw.cursor) = matroxfb_DAC1064_cursor; ACCESS_FBINFO(dispsw.set_font) = matroxfb_DAC1064_setfont; return 0; } -static void DAC1064_calcclock(CPMINFO unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post) { +static void DAC1064_calcclock(CPMINFO_ unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post) { unsigned int fvco; unsigned int p; DBG("DAC1064_calcclock") - fvco = PLL_calcclock(PMINFO freq, fmax, in, feed, &p); + fvco = PLL_calcclock(PMINFO_ freq, fmax, in, feed, &p); if (ACCESS_FBINFO(devflags.g450dac)) { if (fvco <= 300000) /* 276-324 */ @@ -217,30 +220,30 @@ 0x00, 0x00, 0x00, 0xFF, 0xFF}; -static void DAC1064_setpclk(CPMINFO struct matrox_hw_state* hw, unsigned long fout) { +static void DAC1064_setpclk(WPMINFO_ unsigned long fout) { unsigned int m, n, p; DBG("DAC1064_setpclk") - DAC1064_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - hw->DACclk[0] = m; - hw->DACclk[1] = n; - hw->DACclk[2] = p; + DAC1064_calcclock(PMINFO_ fout, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); + ACCESS_FBINFO(hw).DACclk[0] = m; + ACCESS_FBINFO(hw).DACclk[1] = n; + ACCESS_FBINFO(hw).DACclk[2] = p; } -static void DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem){ +static void DAC1064_setmclk(WPMINFO_ int oscinfo, unsigned long fmem) { u_int32_t mx; DBG("DAC1064_setmclk") if (ACCESS_FBINFO(devflags.noinit)) { /* read MCLK and give up... */ - hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM); - hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN); - hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP); + ACCESS_FBINFO(hw).DACclk[3] = inDAC1064(PMINFO_ DAC1064_XSYSPLLM); + ACCESS_FBINFO(hw).DACclk[4] = inDAC1064(PMINFO_ DAC1064_XSYSPLLN); + ACCESS_FBINFO(hw).DACclk[5] = inDAC1064(PMINFO_ DAC1064_XSYSPLLP); return; } - mx = hw->MXoptionReg | 0x00000004; + mx = ACCESS_FBINFO(hw).MXoptionReg | 0x00000004; pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); mx &= ~0x000000BB; if (oscinfo & DAC1064_OPT_GDIV1) @@ -267,12 +270,12 @@ perfect... */ /* (bit 2 of PCI_OPTION_REG must be 0... and bits 0,1 must not select PLL... because of PLL can be stopped at this time) */ - DAC1064_calcclock(PMINFO fmem, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3] = m); - outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4] = n); - outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5] = p); + DAC1064_calcclock(PMINFO_ fmem, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); + outDAC1064(PMINFO_ DAC1064_XSYSPLLM, ACCESS_FBINFO(hw).DACclk[3] = m); + outDAC1064(PMINFO_ DAC1064_XSYSPLLN, ACCESS_FBINFO(hw).DACclk[4] = n); + outDAC1064(PMINFO_ DAC1064_XSYSPLLP, ACCESS_FBINFO(hw).DACclk[5] = p); for (clk = 65536; clk; --clk) { - if (inDAC1064(PMINFO DAC1064_XSYSPLLSTAT) & 0x40) + if (inDAC1064(PMINFO_ DAC1064_XSYSPLLSTAT) & 0x40) break; } if (!clk) @@ -286,89 +289,89 @@ pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); mx &= ~0x00000004; pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); - hw->MXoptionReg = mx; + ACCESS_FBINFO(hw).MXoptionReg = mx; } -void DAC1064_global_init(CPMINFO struct matrox_hw_state* hw) { - hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; - hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; - hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; - hw->DACreg[POS1064_XOUTPUTCONN] = 0x01; /* output #1 enabled */ +void DAC1064_global_init(WPMINFO) { + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; + ACCESS_FBINFO(hw).DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; + ACCESS_FBINFO(hw).DACreg[POS1064_XOUTPUTCONN] = 0x01; /* output #1 enabled */ if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { if (ACCESS_FBINFO(devflags.g450dac)) { - hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL2; - hw->DACreg[POS1064_XOUTPUTCONN] = 0x05; /* output #1 enabled; CRTC1 connected to output #2 */ + ACCESS_FBINFO(hw).DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL2; + ACCESS_FBINFO(hw).DACreg[POS1064_XOUTPUTCONN] = 0x05; /* output #1 enabled; CRTC1 connected to output #2 */ } else { - hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; - hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; + ACCESS_FBINFO(hw).DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; } } else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { - hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; - hw->DACreg[POS1064_XOUTPUTCONN] = 0x09; /* output #1 enabled; CRTC2 connected to output #2 */ + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; + ACCESS_FBINFO(hw).DACreg[POS1064_XOUTPUTCONN] = 0x09; /* output #1 enabled; CRTC2 connected to output #2 */ } else if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) - hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12; + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12; else - hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS; + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS; if ((ACCESS_FBINFO(output.ph) | ACCESS_FBINFO(output.sh)) & MATROXFB_OUTPUT_CONN_PRIMARY) - hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; } -void DAC1064_global_restore(CPMINFO const struct matrox_hw_state* hw) { - outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); - outDAC1064(PMINFO M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]); +void DAC1064_global_restore(WPMINFO) { + outDAC1064(PMINFO_ M1064_XPIXCLKCTRL, ACCESS_FBINFO(hw).DACreg[POS1064_XPIXCLKCTRL]); + outDAC1064(PMINFO_ M1064_XMISCCTRL, ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL]); if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { - outDAC1064(PMINFO 0x20, 0x04); - outDAC1064(PMINFO 0x1F, ACCESS_FBINFO(devflags.dfp_type)); + outDAC1064(PMINFO_ 0x20, 0x04); + outDAC1064(PMINFO_ 0x1F, ACCESS_FBINFO(devflags.dfp_type)); if (ACCESS_FBINFO(devflags.g450dac)) { - outDAC1064(PMINFO M1064_XSYNCCTRL, 0xCC); /* only matrox know... */ - outDAC1064(PMINFO M1064_XPWRCTRL, 0x1F); /* powerup everything */ - outDAC1064(PMINFO M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]); + outDAC1064(PMINFO_ M1064_XSYNCCTRL, 0xCC); /* only matrox know... */ + outDAC1064(PMINFO_ M1064_XPWRCTRL, 0x1F); /* powerup everything */ + outDAC1064(PMINFO_ M1064_XOUTPUTCONN, ACCESS_FBINFO(hw).DACreg[POS1064_XOUTPUTCONN]); } } } -static int DAC1064_init_1(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display *p) { +static int DAC1064_init_1(WPMINFO_ struct my_timming* m, struct display *p) { DBG("DAC1064_init_1") - memcpy(hw->DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs)); + memcpy(ACCESS_FBINFO(hw).DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs)); if (p->type == FB_TYPE_TEXT) { - hw->DACreg[POS1064_XMISCCTRL] = M1064_XMISCCTRL_DAC_6BIT; - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP + ACCESS_FBINFO(hw).DACreg[POS1064_XMISCCTRL] = M1064_XMISCCTRL_DAC_6BIT; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; } else { switch (p->var.bits_per_pixel) { /* case 4: not supported by MGA1064 DAC */ case 8: - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; break; case 16: if (p->var.green.length == 5) - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_15BPP_1BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_15BPP_1BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; else - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_16BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_16BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; break; case 24: - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_24BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_24BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; break; case 32: - hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_32BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + ACCESS_FBINFO(hw).DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_32BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; break; default: return 1; /* unsupported depth */ } } - DAC1064_global_init(PMINFO hw); - hw->DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl); - hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK; - hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN; - hw->DACreg[POS1064_XCURADDL] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 10; - hw->DACreg[POS1064_XCURADDH] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 18; + DAC1064_global_init(PMINFO); + ACCESS_FBINFO(hw).DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl); + ACCESS_FBINFO(hw).DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK; + ACCESS_FBINFO(hw).DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN; + ACCESS_FBINFO(hw).DACreg[POS1064_XCURADDL] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 10; + ACCESS_FBINFO(hw).DACreg[POS1064_XCURADDH] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 18; return 0; } -static int DAC1064_init_2(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int DAC1064_init_2(WPMINFO_ struct my_timming* m, struct display* p) { DBG("DAC1064_init_2") @@ -376,9 +379,9 @@ int i; for (i = 0; i < 256; i++) { - hw->DACpal[i * 3 + 0] = i; - hw->DACpal[i * 3 + 1] = i; - hw->DACpal[i * 3 + 2] = i; + ACCESS_FBINFO(hw).DACpal[i * 3 + 0] = i; + ACCESS_FBINFO(hw).DACpal[i * 3 + 1] = i; + ACCESS_FBINFO(hw).DACpal[i * 3 + 2] = i; } } else if (p->var.bits_per_pixel > 8) { if (p->var.green.length == 5) { /* 0..31, 128..159 */ @@ -386,111 +389,113 @@ for (i = 0; i < 32; i++) { /* with p15 == 0 */ - hw->DACpal[i * 3 + 0] = i << 3; - hw->DACpal[i * 3 + 1] = i << 3; - hw->DACpal[i * 3 + 2] = i << 3; + ACCESS_FBINFO(hw).DACpal[i * 3 + 0] = i << 3; + ACCESS_FBINFO(hw).DACpal[i * 3 + 1] = i << 3; + ACCESS_FBINFO(hw).DACpal[i * 3 + 2] = i << 3; /* with p15 == 1 */ - hw->DACpal[(i + 128) * 3 + 0] = i << 3; - hw->DACpal[(i + 128) * 3 + 1] = i << 3; - hw->DACpal[(i + 128) * 3 + 2] = i << 3; + ACCESS_FBINFO(hw).DACpal[(i + 128) * 3 + 0] = i << 3; + ACCESS_FBINFO(hw).DACpal[(i + 128) * 3 + 1] = i << 3; + ACCESS_FBINFO(hw).DACpal[(i + 128) * 3 + 2] = i << 3; } } else { int i; for (i = 0; i < 64; i++) { /* 0..63 */ - hw->DACpal[i * 3 + 0] = i << 3; - hw->DACpal[i * 3 + 1] = i << 2; - hw->DACpal[i * 3 + 2] = i << 3; + ACCESS_FBINFO(hw).DACpal[i * 3 + 0] = i << 3; + ACCESS_FBINFO(hw).DACpal[i * 3 + 1] = i << 2; + ACCESS_FBINFO(hw).DACpal[i * 3 + 2] = i << 3; } } } else { - memset(hw->DACpal, 0, 768); + memset(ACCESS_FBINFO(hw).DACpal, 0, 768); } return 0; } -static void DAC1064_restore_1(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw) { +static void DAC1064_restore_1(WPMINFO) { CRITFLAGS DBG("DAC1064_restore_1") CRITBEGIN - outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]); - outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]); - outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]); - /* - * We must ALWAYS reprogram hardware due to broken XF4 matrox drivers... - * - * if (!oldhw || memcmp(hw->DACreg, oldhw->DACreg, sizeof(MGA1064_DAC_regs))) - */ + if ((inDAC1064(PMINFO_ DAC1064_XSYSPLLM) != ACCESS_FBINFO(hw).DACclk[3]) || + (inDAC1064(PMINFO_ DAC1064_XSYSPLLN) != ACCESS_FBINFO(hw).DACclk[4]) || + (inDAC1064(PMINFO_ DAC1064_XSYSPLLP) != ACCESS_FBINFO(hw).DACclk[5])) { + outDAC1064(PMINFO_ DAC1064_XSYSPLLM, ACCESS_FBINFO(hw).DACclk[3]); + outDAC1064(PMINFO_ DAC1064_XSYSPLLN, ACCESS_FBINFO(hw).DACclk[4]); + outDAC1064(PMINFO_ DAC1064_XSYSPLLP, ACCESS_FBINFO(hw).DACclk[5]); + } { unsigned int i; for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { if ((i != POS1064_XPIXCLKCTRL) && (i != POS1064_XMISCCTRL)) - outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]); + outDAC1064(PMINFO_ MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]); } } - DAC1064_global_restore(PMINFO hw); + DAC1064_global_restore(PMINFO); CRITEND }; -static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw, struct display* p) { +static void DAC1064_restore_2(WPMINFO_ struct display* p) { #ifdef DEBUG unsigned int i; #endif DBG("DAC1064_restore_2") - matrox_init_putc(PMINFO p, matroxfb_DAC1064_createcursor); + matrox_init_putc(PMINFO_ p, matroxfb_DAC1064_createcursor); #ifdef DEBUG dprintk(KERN_DEBUG "DAC1064regs "); for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { - dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], hw->DACreg[i]); + dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]); if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... "); } dprintk("\n" KERN_DEBUG "DAC1064clk "); for (i = 0; i < 6; i++) - dprintk("C%02X=%02X ", i, hw->DACclk[i]); + dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]); dprintk("\n"); #endif } -static int m1064_compute(void* outdev, struct my_timming* m, struct matrox_hw_state* hw) { +static int m1064_compute(void* outdev, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)outdev) - DAC1064_setpclk(PMINFO hw, m->pixclock); -#undef minfo - return 0; -} - -static int m1064_program(void* outdev, const struct matrox_hw_state* hw) { -#define minfo ((struct matrox_fb_info*)outdev) - int i; - int tmout; - CRITFLAGS + if (ACCESS_FBINFO(devflags.g450dac)) { + matroxfb_g450_setclk(PMINFO_ m->pixclock, M_PIXEL_PLL_C); + } else { + int i; + int tmout; + CRITFLAGS - CRITBEGIN + DAC1064_setpclk(PMINFO_ m->pixclock); - for (i = 0; i < 3; i++) - outDAC1064(PMINFO M1064_XPIXPLLCM + i, hw->DACclk[i]); - for (tmout = 500000; tmout; tmout--) { - if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) - break; - udelay(10); - }; + CRITBEGIN - CRITEND + for (i = 0; i < 3; i++) + outDAC1064(PMINFO_ M1064_XPIXPLLCM + i, ACCESS_FBINFO(hw).DACclk[i]); + for (tmout = 500000; tmout; tmout--) { + if (inDAC1064(PMINFO_ M1064_XPIXPLLSTAT) & 0x40) + break; + udelay(10); + }; - if (!tmout) - printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + CRITEND + if (!tmout) + printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + } #undef minfo return 0; } +static int m1064_program(void* outdev) { + /* nothing, hardware is set in m1064_compute */ + return 0; +} + static int m1064_start(void* outdev) { /* nothing */ return 0; @@ -522,50 +527,50 @@ #endif /* NEED_DAC1064 */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int MGA1064_init(WPMINFO_ struct my_timming* m, struct display* p) { DBG("MGA1064_init") - if (DAC1064_init_1(PMINFO hw, m, p)) return 1; - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (DAC1064_init_1(PMINFO_ m, p)) return 1; + if (matroxfb_vgaHWinit(PMINFO_ m, p)) return 1; - hw->MiscOutReg = 0xCB; + ACCESS_FBINFO(hw).MiscOutReg = 0xCB; if (m->sync & FB_SYNC_HOR_HIGH_ACT) - hw->MiscOutReg &= ~0x40; + ACCESS_FBINFO(hw).MiscOutReg &= ~0x40; if (m->sync & FB_SYNC_VERT_HIGH_ACT) - hw->MiscOutReg &= ~0x80; + ACCESS_FBINFO(hw).MiscOutReg &= ~0x80; if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ - hw->CRTCEXT[3] |= 0x40; + ACCESS_FBINFO(hw).CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO hw, m, p)) return 1; + if (DAC1064_init_2(PMINFO_ m, p)) return 1; return 0; } #endif #ifdef CONFIG_FB_MATROX_G100 -static int MGAG100_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int MGAG100_init(WPMINFO_ struct my_timming* m, struct display* p) { DBG("MGAG100_init") - if (DAC1064_init_1(PMINFO hw, m, p)) return 1; - hw->MXoptionReg &= ~0x2000; - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (DAC1064_init_1(PMINFO_ m, p)) return 1; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x2000; + if (matroxfb_vgaHWinit(PMINFO_ m, p)) return 1; - hw->MiscOutReg = 0xEF; + ACCESS_FBINFO(hw).MiscOutReg = 0xEF; if (m->sync & FB_SYNC_HOR_HIGH_ACT) - hw->MiscOutReg &= ~0x40; + ACCESS_FBINFO(hw).MiscOutReg &= ~0x40; if (m->sync & FB_SYNC_VERT_HIGH_ACT) - hw->MiscOutReg &= ~0x80; + ACCESS_FBINFO(hw).MiscOutReg &= ~0x80; if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ - hw->CRTCEXT[3] |= 0x40; + ACCESS_FBINFO(hw).CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO hw, m, p)) return 1; + if (DAC1064_init_2(PMINFO_ m, p)) return 1; return 0; } #endif /* G100 */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw){ +static void MGA1064_ramdac_init(WPMINFO) { DBG("MGA1064_ramdac_init"); @@ -579,7 +584,7 @@ ACCESS_FBINFO(features.pll.post_shift_max) = 3; ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_EXTERNAL; /* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */ - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); + DAC1064_setmclk(PMINFO_ DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); } #endif @@ -591,23 +596,23 @@ static int def50 = 0; /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */ #endif -static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p){ +static void MGAG100_progPixClock(CPMINFO_ int flags, int m, int n, int p) { int reg; int selClk; int clk; DBG("MGAG100_progPixClock") - outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS | + outDAC1064(PMINFO_ M1064_XPIXCLKCTRL, inDAC1064(PMINFO_ M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS | M1064_XPIXCLKCTRL_PLL_UP); switch (flags & 3) { case 0: reg = M1064_XPIXPLLAM; break; case 1: reg = M1064_XPIXPLLBM; break; default: reg = M1064_XPIXPLLCM; break; } - outDAC1064(PMINFO reg++, m); - outDAC1064(PMINFO reg++, n); - outDAC1064(PMINFO reg, p); + outDAC1064(PMINFO_ reg++, m); + outDAC1064(PMINFO_ reg++, n); + outDAC1064(PMINFO_ reg, p); selClk = mga_inb(M_MISC_REG_READ) & ~0xC; /* there should be flags & 0x03 & case 0/1/else */ /* and we should first select source and after that we should wait for PLL */ @@ -619,34 +624,34 @@ } mga_outb(M_MISC_REG, selClk); for (clk = 500000; clk; clk--) { - if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) + if (inDAC1064(PMINFO_ M1064_XPIXPLLSTAT) & 0x40) break; udelay(10); }; if (!clk) printk(KERN_ERR "matroxfb: Pixel PLL%c not locked after usual time\n", (reg-M1064_XPIXPLLAM-2)/4 + 'A'); - selClk = inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK; + selClk = inDAC1064(PMINFO_ M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK; switch (flags & 0x0C) { case 0x00: selClk |= M1064_XPIXCLKCTRL_SRC_PCI; break; case 0x04: selClk |= M1064_XPIXCLKCTRL_SRC_PLL; break; default: selClk |= M1064_XPIXCLKCTRL_SRC_EXT; break; } - outDAC1064(PMINFO M1064_XPIXCLKCTRL, selClk); - outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); + outDAC1064(PMINFO_ M1064_XPIXCLKCTRL, selClk); + outDAC1064(PMINFO_ M1064_XPIXCLKCTRL, inDAC1064(PMINFO_ M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); } -static void MGAG100_setPixClock(CPMINFO int flags, int freq){ +static void MGAG100_setPixClock(CPMINFO_ int flags, int freq) { unsigned int m, n, p; DBG("MGAG100_setPixClock") - DAC1064_calcclock(PMINFO freq, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - MGAG100_progPixClock(PMINFO flags, m, n, p); + DAC1064_calcclock(PMINFO_ freq, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); + MGAG100_progPixClock(PMINFO_ flags, m, n, p); } #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_preinit(WPMINFO struct matrox_hw_state* hw){ +static int MGA1064_preinit(WPMINFO) { static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; @@ -662,15 +667,15 @@ if (ACCESS_FBINFO(devflags.noinit)) return 0; /* do not modify settings */ - hw->MXoptionReg &= 0xC0000100; - hw->MXoptionReg |= 0x00094E20; + ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00094E20; if (ACCESS_FBINFO(devflags.novga)) - hw->MXoptionReg &= ~0x00000100; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; if (ACCESS_FBINFO(devflags.nobios)) - hw->MXoptionReg &= ~0x40000000; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; if (ACCESS_FBINFO(devflags.nopciretry)) - hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); mga_setr(M_SEQ_INDEX, 0x01, 0x20); mga_outl(M_CTLWTST, 0x00000000); udelay(200); @@ -680,20 +685,133 @@ return 0; } -static void MGA1064_reset(WPMINFO struct matrox_hw_state* hw){ +static void MGA1064_reset(WPMINFO) { DBG("MGA1064_reset"); ACCESS_FBINFO(features.DAC1064.cursorimage) = ACCESS_FBINFO(video.len_usable) - 1024; if (ACCESS_FBINFO(devflags.hwcursor)) ACCESS_FBINFO(video.len_usable) -= 1024; - matroxfb_fastfont_init(MINFO); - MGA1064_ramdac_init(PMINFO hw); + matroxfb_fastfont_init(PMINFO); + MGA1064_ramdac_init(PMINFO); } #endif #ifdef CONFIG_FB_MATROX_G100 -static int MGAG100_preinit(WPMINFO struct matrox_hw_state* hw){ +static void g450_mclk_init(WPMINFO) { + /* switch all clocks to PCI source */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3 & ~0x00300C03); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + if (((ACCESS_FBINFO(values).reg.opt3 & 0x000003) == 0x000003) || + ((ACCESS_FBINFO(values).reg.opt3 & 0x000C00) == 0x000C00) || + ((ACCESS_FBINFO(values).reg.opt3 & 0x300000) == 0x300000)) { + matroxfb_g450_setclk(PMINFO_ ACCESS_FBINFO(values.pll.video), M_VIDEO_PLL); + } else { + /* slow down video clocks... */ + matroxfb_g450_setclk(PMINFO_ 0, M_VIDEO_PLL); + } + matroxfb_g450_setclk(PMINFO_ ACCESS_FBINFO(values.pll.system), M_SYSTEM_PLL); + + /* switch clocks to their real PLL source(s) */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + +} + +static void g450_memory_init(WPMINFO) { + /* disable memory refresh */ + ACCESS_FBINFO(hw).MXoptionReg &= ~0x001F8000; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* set memory interface parameters */ + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00207E00; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00207E00 & ACCESS_FBINFO(values).reg.opt; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ACCESS_FBINFO(values).reg.opt2); + + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + + /* first set up memory interface with disabled memory interface clocks */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc & ~0x80000000U); + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess); + /* start memory clocks */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc | 0x80000000U); + + udelay(200); + + if (ACCESS_FBINFO(values).memory.ddr && (!ACCESS_FBINFO(values).memory.emrswen || !ACCESS_FBINFO(values).memory.dll)) { + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk & ~0x1000); + } + mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess | 0x8000); + + udelay(200); + + ACCESS_FBINFO(hw).MXoptionReg |= 0x001F8000 & ACCESS_FBINFO(values).reg.opt; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* value is written to memory chips only if old != new */ + mga_outl(M_PLNWT, 0); + mga_outl(M_PLNWT, ~0); + + if (ACCESS_FBINFO(values).reg.mctlwtst != ACCESS_FBINFO(values).reg.mctlwtst_core) { + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst_core); + } + +} + +static void g450_preinit(WPMINFO) { + u_int32_t c2ctl; + u_int8_t curctl; + u_int8_t c1ctl; + + /* ACCESS_FBINFO(hw).MXoptionReg = minfo->values.reg.opt; */ + ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00000020; + if (ACCESS_FBINFO(devflags.novga)) + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; + if (ACCESS_FBINFO(devflags.nobios)) + ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; + if (ACCESS_FBINFO(devflags.nopciretry)) + ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; + ACCESS_FBINFO(hw).MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x03400040; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + + /* Init system clocks */ + + /* stop crtc2 */ + c2ctl = mga_inl(M_C2CTL); + mga_outl(M_C2CTL, c2ctl & ~1); + /* stop cursor */ + curctl = inDAC1064(PMINFO_ M1064_XCURCTRL); + outDAC1064(PMINFO_ M1064_XCURCTRL, 0); + /* stop crtc1 */ + c1ctl = mga_readr(M_SEQ_INDEX, 1); + mga_setr(M_SEQ_INDEX, 1, c1ctl | 0x20); + + g450_mclk_init(PMINFO); + g450_memory_init(PMINFO); + + /* set legacy VGA clock sources for DOSEmu or VMware... */ + matroxfb_g450_setclk(PMINFO_ 25175, M_PIXEL_PLL_A); + matroxfb_g450_setclk(PMINFO_ 28322, M_PIXEL_PLL_B); + + /* restore crtc1 */ + mga_setr(M_SEQ_INDEX, 1, c1ctl); + + /* restore cursor */ + outDAC1064(PMINFO_ M1064_XCURCTRL, curctl); + + /* restore crtc2 */ + mga_outl(M_C2CTL, c2ctl); + + return; +} + +static int MGAG100_preinit(WPMINFO) { static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; @@ -710,7 +828,9 @@ } else { ACCESS_FBINFO(features.pll.vco_freq_min) = 62000; } - ACCESS_FBINFO(features.pll.ref_freq) = 27000; + if (!ACCESS_FBINFO(features.pll.ref_freq)) { + ACCESS_FBINFO(features.pll.ref_freq) = 27000; + } ACCESS_FBINFO(features.pll.feed_div_min) = 7; ACCESS_FBINFO(features.pll.feed_div_max) = 127; ACCESS_FBINFO(features.pll.in_div_min) = 1; @@ -734,24 +854,28 @@ } if (ACCESS_FBINFO(devflags.noinit)) return 0; - hw->MXoptionReg &= 0xC0000100; - hw->MXoptionReg |= 0x00000020; + if (ACCESS_FBINFO(devflags.g450dac)) { + g450_preinit(PMINFO); + return 0; + } + ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00000020; if (ACCESS_FBINFO(devflags.novga)) - hw->MXoptionReg &= ~0x00000100; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; if (ACCESS_FBINFO(devflags.nobios)) - hw->MXoptionReg &= ~0x40000000; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; if (ACCESS_FBINFO(devflags.nopciretry)) - hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); + ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + DAC1064_setmclk(PMINFO_ DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); - hw->MXoptionReg |= 0x1080; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg |= 0x1080; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); mga_outl(M_CTLWTST, 0x00000300); /* mga_outl(M_CTLWTST, 0x03258A31); */ udelay(100); @@ -763,7 +887,7 @@ udelay(100); reg50 &= ~0xFF; reg50 |= 0x07; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); /* it should help with G100 */ mga_outb(M_GRAPHICS_INDEX, 6); mga_outb(M_GRAPHICS_DATA, (mga_inb(M_GRAPHICS_DATA) & 3) | 4); @@ -774,39 +898,40 @@ mga_writeb(ACCESS_FBINFO(video.vbase), 0x4000, 0x55); #if 0 if (mga_readb(ACCESS_FBINFO(video.vbase), 0x0000) != 0xAA) { - hw->MXoptionReg &= ~0x1000; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x1000; } #endif - hw->MXoptionReg |= 0x00078020; - } else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + ACCESS_FBINFO(hw).MXoptionReg |= 0x00078020; + } else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) { + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); if (ACCESS_FBINFO(devflags.memtype) == -1) - ACCESS_FBINFO(devflags.memtype) = 3; - hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; + ACCESS_FBINFO(hw).MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00; + else + ACCESS_FBINFO(hw).MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; if (ACCESS_FBINFO(devflags.sgram)) - hw->MXoptionReg |= 0x4000; - mga_outl(M_CTLWTST, 0x042450A1); - mga_outl(M_MEMRDBK, 0x00000108); + ACCESS_FBINFO(hw).MXoptionReg |= 0x4000; + mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); udelay(200); mga_outl(M_MACCESS, 0x00000000); mga_outl(M_MACCESS, 0x00008000); udelay(100); - mga_outw(M_MEMRDBK, 0x00000108); - hw->MXoptionReg |= 0x00078020; + mga_outw(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + ACCESS_FBINFO(hw).MXoptionReg |= 0x00078020; } else { - pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, ®50); + pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); reg50 &= ~0x00000100; reg50 |= 0x00000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); if (ACCESS_FBINFO(devflags.memtype) == -1) ACCESS_FBINFO(devflags.memtype) = 0; - hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; + ACCESS_FBINFO(hw).MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; if (ACCESS_FBINFO(devflags.sgram)) - hw->MXoptionReg |= 0x4000; + ACCESS_FBINFO(hw).MXoptionReg |= 0x4000; mga_outl(M_CTLWTST, 0x042450A1); mga_outl(M_MEMRDBK, 0x00000108); udelay(200); @@ -814,13 +939,13 @@ mga_outl(M_MACCESS, 0x00008000); udelay(100); mga_outl(M_MEMRDBK, 0x00000108); - hw->MXoptionReg |= 0x00040020; + ACCESS_FBINFO(hw).MXoptionReg |= 0x00040020; } - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); return 0; } -static void MGAG100_reset(WPMINFO struct matrox_hw_state* hw){ +static void MGAG100_reset(WPMINFO) { u_int8_t b; DBG("MGAG100_reset") @@ -828,7 +953,7 @@ ACCESS_FBINFO(features.DAC1064.cursorimage) = ACCESS_FBINFO(video.len_usable) - 1024; if (ACCESS_FBINFO(devflags.hwcursor)) ACCESS_FBINFO(video.len_usable) -= 1024; - matroxfb_fastfont_init(MINFO); + matroxfb_fastfont_init(PMINFO); { #ifdef G100_BROKEN_IBM_82351 @@ -845,33 +970,43 @@ #endif if (!ACCESS_FBINFO(devflags.noinit)) { if (x7AF4 & 8) { - hw->MXoptionReg |= 0x40; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg |= 0x40; /* FIXME... */ + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); } mga_setr(M_EXTVGA_INDEX, 0x06, 0x50); } } - DAC1064_setmclk(PMINFO hw, DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + if (ACCESS_FBINFO(devflags.g450dac)) { + /* either leave MCLK as is... or they were set in preinit */ + ACCESS_FBINFO(hw).DACclk[3] = inDAC1064(PMINFO_ DAC1064_XSYSPLLM); + ACCESS_FBINFO(hw).DACclk[4] = inDAC1064(PMINFO_ DAC1064_XSYSPLLN); + ACCESS_FBINFO(hw).DACclk[5] = inDAC1064(PMINFO_ DAC1064_XSYSPLLP); + } else { + DAC1064_setmclk(PMINFO_ DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + } if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { if (ACCESS_FBINFO(devflags.dfp_type) == -1) { - ACCESS_FBINFO(devflags.dfp_type) = inDAC1064(PMINFO 0x1F); + ACCESS_FBINFO(devflags.dfp_type) = inDAC1064(PMINFO_ 0x1F); } } if (ACCESS_FBINFO(devflags.noinit)) return; - MGAG100_setPixClock(PMINFO 4, 25175); - MGAG100_setPixClock(PMINFO 5, 28322); - if (x7AF4 & 0x10) { - b = inDAC1064(PMINFO M1064_XGENIODATA) & ~1; - outDAC1064(PMINFO M1064_XGENIODATA, b); - b = inDAC1064(PMINFO M1064_XGENIOCTRL) | 1; - outDAC1064(PMINFO M1064_XGENIOCTRL, b); + if (ACCESS_FBINFO(devflags.g450dac)) { + } else { + MGAG100_setPixClock(PMINFO_ 4, 25175); + MGAG100_setPixClock(PMINFO_ 5, 28322); + if (x7AF4 & 0x10) { + b = inDAC1064(PMINFO_ M1064_XGENIODATA) & ~1; + outDAC1064(PMINFO_ M1064_XGENIODATA, b); + b = inDAC1064(PMINFO_ M1064_XGENIOCTRL) | 1; + outDAC1064(PMINFO_ M1064_XGENIOCTRL, b); + } } } #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void MGA1064_restore(WPMINFO_ struct display* p) { int i; CRITFLAGS @@ -879,22 +1014,22 @@ CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); mga_outb(M_IEN, 0x00); mga_outb(M_CACHEFLUSH, 0x00); CRITEND - DAC1064_restore_1(PMINFO hw, oldhw); - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + DAC1064_restore_1(PMINFO); + matroxfb_vgaHWrestore(PMINFO); for (i = 0; i < 6; i++) - mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO hw, oldhw, p); + mga_setr(M_EXTVGA_INDEX, i, ACCESS_FBINFO(hw).CRTCEXT[i]); + DAC1064_restore_2(PMINFO_ p); } #endif #ifdef CONFIG_FB_MATROX_G100 -static void MGAG100_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void MGAG100_restore(WPMINFO_ struct display* p) { int i; CRITFLAGS @@ -902,18 +1037,18 @@ CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); CRITEND - DAC1064_restore_1(PMINFO hw, oldhw); - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + DAC1064_restore_1(PMINFO); + matroxfb_vgaHWrestore(PMINFO); #ifdef CONFIG_FB_MATROX_32MB if (ACCESS_FBINFO(devflags.support32MB)) - mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]); + mga_setr(M_EXTVGA_INDEX, 8, ACCESS_FBINFO(hw).CRTCEXT[8]); #endif for (i = 0; i < 6; i++) - mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO hw, oldhw, p); + mga_setr(M_EXTVGA_INDEX, i, ACCESS_FBINFO(hw).CRTCEXT[i]); + DAC1064_restore_2(PMINFO_ p); } #endif diff -Nru a/drivers/video/matrox/matroxfb_DAC1064.h b/drivers/video/matrox/matroxfb_DAC1064.h --- a/drivers/video/matrox/matroxfb_DAC1064.h Wed Feb 13 20:03:50 2002 +++ b/drivers/video/matrox/matroxfb_DAC1064.h Wed Feb 13 20:03:50 2002 @@ -13,8 +13,8 @@ extern struct matrox_switch matrox_G100; #endif #ifdef NEED_DAC1064 -void DAC1064_global_init(CPMINFO struct matrox_hw_state*); -void DAC1064_global_restore(CPMINFO const struct matrox_hw_state*); +void DAC1064_global_init(WPMINFO); +void DAC1064_global_restore(WPMINFO); #endif #define M1064_INDEX 0x00 @@ -139,10 +139,10 @@ #define M1064_XOUTPUTCONN 0x8A #define M1064_XSYNCCTRL 0x8B -#define M1064_XPIXPLL2STAT 0x8C -#define M1064_XPIXPLL2P 0x8D -#define M1064_XPIXPLL2N 0x8E -#define M1064_XPIXPLL2M 0x8F +#define M1064_XVIDPLLSTAT 0x8C +#define M1064_XVIDPLLP 0x8D +#define M1064_XVIDPLLM 0x8E +#define M1064_XVIDPLLN 0x8F #define M1064_XPWRCTRL 0xA0 diff -Nru a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c --- a/drivers/video/matrox/matroxfb_Ti3026.c Wed Feb 13 20:03:30 2002 +++ b/drivers/video/matrox/matroxfb_Ti3026.c Wed Feb 13 20:03:30 2002 @@ -4,7 +4,9 @@ * * (c) 1998,1999,2000 Petr Vandrovec * - * Version: 1.50 2000/08/10 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2000/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -283,14 +285,14 @@ #define minfo ((struct matrox_fb_info*)ptr) matroxfb_DAC_lock_irqsave(flags); - outTi3026(PMINFO TVP3026_XCURCTRL, inTi3026(PMINFO TVP3026_XCURCTRL) ^ TVP3026_XCURCTRL_DIS ^ TVP3026_XCURCTRL_XGA); + outTi3026(PMINFO_ TVP3026_XCURCTRL, inTi3026(PMINFO_ TVP3026_XCURCTRL) ^ TVP3026_XCURCTRL_DIS ^ TVP3026_XCURCTRL_XGA); ACCESS_FBINFO(cursor.timer.expires) = jiffies + HZ/2; add_timer(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_unlock_irqrestore(flags); #undef minfo } -static void matroxfb_ti3026_createcursor(WPMINFO struct display* p) { +static void matroxfb_ti3026_createcursor(WPMINFO_ struct display* p) { unsigned long flags; u_int32_t xline; unsigned int i; @@ -301,7 +303,7 @@ DBG("matroxfb_ti3026_createcursor"); - matroxfb_createcursorshape(PMINFO p, p->var.vmode); + matroxfb_createcursorshape(PMINFO_ p, p->var.vmode); xline = (~0) << (32 - ACCESS_FBINFO(cursor.w)); matroxfb_DAC_lock_irqsave(flags); @@ -357,13 +359,13 @@ del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); ACCESS_FBINFO(cursor.state) = CM_ERASE; - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL])); + outTi3026(PMINFO_ TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL])); matroxfb_DAC_unlock_irqrestore(flags); } return; } if ((p->conp->vc_cursor_type & CUR_HWMASK) != ACCESS_FBINFO(cursor.type)) - matroxfb_ti3026_createcursor(PMINFO p); + matroxfb_ti3026_createcursor(PMINFO_ p); x *= fontwidth(p); y *= fontheight(p); y -= p->var.yoffset; @@ -377,7 +379,7 @@ ACCESS_FBINFO(cursor.y) = y; x += 64; y += 64; - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL])); + outTi3026(PMINFO_ TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL])); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSXL, x); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSXH, x >> 8); mga_outb(M_RAMDAC_BASE+TVP3026_CURPOSYL, y); @@ -386,7 +388,7 @@ ACCESS_FBINFO(cursor.state) = CM_DRAW; if (ACCESS_FBINFO(devflags.blink)) mod_timer(&ACCESS_FBINFO(cursor.timer), jiffies + HZ/2); - outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL]) | TVP3026_XCURCTRL_XGA); + outTi3026(PMINFO_ TVP3026_XCURCTRL, ACCESS_FBINFO(hw.DACreg[POS3026_XCURCTRL]) | TVP3026_XCURCTRL_XGA); matroxfb_DAC_unlock_irqrestore(flags); } @@ -399,42 +401,42 @@ return 0; } -static int matroxfb_ti3026_selhwcursor(WPMINFO struct display* p) { +static int matroxfb_ti3026_selhwcursor(WPMINFO_ struct display* p) { ACCESS_FBINFO(dispsw.cursor) = matroxfb_ti3026_cursor; ACCESS_FBINFO(dispsw.set_font) = matroxfb_ti3026_setfont; return 0; } -static int Ti3026_calcclock(CPMINFO unsigned int freq, unsigned int fmax, int* in, int* feed, int* post) { +static int Ti3026_calcclock(CPMINFO_ unsigned int freq, unsigned int fmax, int* in, int* feed, int* post) { unsigned int fvco; unsigned int lin, lfeed, lpost; DBG("Ti3026_calcclock") - fvco = PLL_calcclock(PMINFO freq, fmax, &lin, &lfeed, &lpost); + fvco = PLL_calcclock(PMINFO_ freq, fmax, &lin, &lfeed, &lpost); fvco >>= (*post = lpost); *in = 64 - lin; *feed = 64 - lfeed; return fvco; } -static int Ti3026_setpclk(CPMINFO struct matrox_hw_state* hw, int clk, struct display* p) { +static int Ti3026_setpclk(WPMINFO_ int clk, struct display* p) { unsigned int f_pll; unsigned int pixfeed, pixin, pixpost; DBG("Ti3026_setpclk") - f_pll = Ti3026_calcclock(PMINFO clk, ACCESS_FBINFO(max_pixel_clock), &pixin, &pixfeed, &pixpost); + f_pll = Ti3026_calcclock(PMINFO_ clk, ACCESS_FBINFO(max_pixel_clock), &pixin, &pixfeed, &pixpost); - hw->DACclk[0] = pixin | 0xC0; - hw->DACclk[1] = pixfeed; - hw->DACclk[2] = pixpost | 0xB0; + ACCESS_FBINFO(hw).DACclk[0] = pixin | 0xC0; + ACCESS_FBINFO(hw).DACclk[1] = pixfeed; + ACCESS_FBINFO(hw).DACclk[2] = pixpost | 0xB0; if (p->type == FB_TYPE_TEXT) { - hw->DACreg[POS3026_XMEMPLLCTRL] = TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_PIXPLL; - hw->DACclk[3] = 0xFD; - hw->DACclk[4] = 0x3D; - hw->DACclk[5] = 0x70; + ACCESS_FBINFO(hw).DACreg[POS3026_XMEMPLLCTRL] = TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_PIXPLL; + ACCESS_FBINFO(hw).DACclk[3] = 0xFD; + ACCESS_FBINFO(hw).DACclk[4] = 0x3D; + ACCESS_FBINFO(hw).DACclk[5] = 0x70; } else { unsigned int loopfeed, loopin, looppost, loopdiv, z; unsigned int Bpp; @@ -461,117 +463,117 @@ loopdiv = z/16; } if (p->var.bits_per_pixel == 24) { - hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; - hw->DACclk[4] = (65 - loopfeed) | 0x80; + ACCESS_FBINFO(hw).DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; + ACCESS_FBINFO(hw).DACclk[4] = (65 - loopfeed) | 0x80; if (ACCESS_FBINFO(accel.ramdac_rev) > 0x20) { if (isInterleave(MINFO)) - hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_8_3; + ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_8_3; else { - hw->DACclk[4] &= ~0xC0; - hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_4_3; + ACCESS_FBINFO(hw).DACclk[4] &= ~0xC0; + ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_4_3; } } else { if (isInterleave(MINFO)) ; /* default... */ else { - hw->DACclk[4] ^= 0xC0; /* change from 0x80 to 0x40 */ - hw->DACreg[POS3026_XLATCHCTRL] = TVP3026A_XLATCHCTRL_4_3; + ACCESS_FBINFO(hw).DACclk[4] ^= 0xC0; /* change from 0x80 to 0x40 */ + ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026A_XLATCHCTRL_4_3; } } - hw->DACclk[5] = looppost | 0xF8; + ACCESS_FBINFO(hw).DACclk[5] = looppost | 0xF8; if (ACCESS_FBINFO(devflags.mga_24bpp_fix)) - hw->DACclk[5] ^= 0x40; + ACCESS_FBINFO(hw).DACclk[5] ^= 0x40; } else { - hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; - hw->DACclk[4] = 65 - loopfeed; - hw->DACclk[5] = looppost | 0xF0; + ACCESS_FBINFO(hw).DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; + ACCESS_FBINFO(hw).DACclk[4] = 65 - loopfeed; + ACCESS_FBINFO(hw).DACclk[5] = looppost | 0xF0; } - hw->DACreg[POS3026_XMEMPLLCTRL] = loopdiv | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL; + ACCESS_FBINFO(hw).DACreg[POS3026_XMEMPLLCTRL] = loopdiv | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL; } return 0; } -static int Ti3026_init(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +static int Ti3026_init(WPMINFO_ struct my_timming* m, struct display* p) { u_int8_t muxctrl = isInterleave(MINFO) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT; DBG("Ti3026_init") - memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg)); + memcpy(ACCESS_FBINFO(hw).DACreg, MGADACbpp32, sizeof(ACCESS_FBINFO(hw).DACreg)); if (p->type == FB_TYPE_TEXT) { - hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; - hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; - hw->DACreg[POS3026_XMUXCTRL] = TVP3026_XMUXCTRL_VGA; - hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | + ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; + ACCESS_FBINFO(hw).DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = TVP3026_XMUXCTRL_VGA; + ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; - hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_6BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; + ACCESS_FBINFO(hw).DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_6BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; } else { switch (p->var.bits_per_pixel) { - case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */ - hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; - hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_4BIT; - hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV8; - hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; + case 4: ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */ + ACCESS_FBINFO(hw).DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_4BIT; + ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV8; + ACCESS_FBINFO(hw).DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; break; - case 8: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; /* or _4_1, they are same */ - hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; - hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_8BIT; - hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; - hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; + case 8: ACCESS_FBINFO(hw).DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; /* or _4_1, they are same */ + ACCESS_FBINFO(hw).DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_8BIT; + ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; + ACCESS_FBINFO(hw).DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; break; case 16: /* XLATCHCTRL should be _4_1 / _2_1... Why is not? (_2_1 is used everytime) */ - hw->DACreg[POS3026_XTRUECOLORCTRL] = (p->var.green.length == 5)? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555 ) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565); - hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_16BIT; - hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV2; + ACCESS_FBINFO(hw).DACreg[POS3026_XTRUECOLORCTRL] = (p->var.green.length == 5)? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555 ) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565); + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_16BIT; + ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV2; break; case 24: /* XLATCHCTRL is: for (A) use _4_3 (?_8_3 is same? TBD), for (B) it is set in setpclk */ - hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_888; - hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; - hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; + ACCESS_FBINFO(hw).DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_888; + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; + ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; break; case 32: /* XLATCHCTRL should be _2_1 / _1_1... Why is not? (_2_1 is used everytime) */ - hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; + ACCESS_FBINFO(hw).DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; break; default: return 1; /* TODO: failed */ } } - if (matroxfb_vgaHWinit(PMINFO hw, m, p)) return 1; + if (matroxfb_vgaHWinit(PMINFO_ m, p)) return 1; /* set SYNC */ - hw->MiscOutReg = 0xCB; + ACCESS_FBINFO(hw).MiscOutReg = 0xCB; if (m->sync & FB_SYNC_HOR_HIGH_ACT) - hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_HSYNC_NEG; + ACCESS_FBINFO(hw).DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_HSYNC_NEG; if (m->sync & FB_SYNC_VERT_HIGH_ACT) - hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_VSYNC_NEG; + ACCESS_FBINFO(hw).DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_VSYNC_NEG; if (m->sync & FB_SYNC_ON_GREEN) - hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_SYNC_ON_GREEN; + ACCESS_FBINFO(hw).DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_SYNC_ON_GREEN; /* set DELAY */ if (ACCESS_FBINFO(video.len) < 0x400000) - hw->CRTCEXT[3] |= 0x08; + ACCESS_FBINFO(hw).CRTCEXT[3] |= 0x08; else if (ACCESS_FBINFO(video.len) > 0x400000) - hw->CRTCEXT[3] |= 0x10; + ACCESS_FBINFO(hw).CRTCEXT[3] |= 0x10; /* set HWCURSOR */ if (m->interlaced) { - hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_INTERLACED; + ACCESS_FBINFO(hw).DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_INTERLACED; } if (m->HTotal >= 1536) - hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_BLANK4096; + ACCESS_FBINFO(hw).DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_BLANK4096; /* set interleaving */ - hw->MXoptionReg &= ~0x00001000; - if ((p->type != FB_TYPE_TEXT) && isInterleave(MINFO)) hw->MXoptionReg |= 0x00001000; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00001000; + if ((p->type != FB_TYPE_TEXT) && isInterleave(MINFO)) ACCESS_FBINFO(hw).MXoptionReg |= 0x00001000; /* set DAC */ - Ti3026_setpclk(PMINFO hw, m->pixclock, p); + Ti3026_setpclk(PMINFO_ m->pixclock, p); return 0; } -static void ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout){ +static void ti3026_setMCLK(WPMINFO_ int fout){ unsigned int f_pll; unsigned int pclk_m, pclk_n, pclk_p; unsigned int mclk_m, mclk_n, mclk_p; @@ -580,29 +582,29 @@ DBG("ti3026_setMCLK") - f_pll = Ti3026_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &mclk_n, &mclk_m, &mclk_p); + f_pll = Ti3026_calcclock(PMINFO_ fout, ACCESS_FBINFO(max_pixel_clock), &mclk_n, &mclk_m, &mclk_p); /* save pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - pclk_n = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFD); - pclk_m = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - pclk_p = inTi3026(PMINFO TVP3026_XPIXPLLDATA); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFC); + pclk_n = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFD); + pclk_m = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFE); + pclk_p = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); /* stop pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFE); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, 0x00); /* set pclk to new mclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_n | 0xC0); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_m); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_p | 0xB0); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFC); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, mclk_n | 0xC0); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, mclk_m); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, mclk_p | 0xB0); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(PMINFO_ TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); }; @@ -610,23 +612,23 @@ printk(KERN_ERR "matroxfb: Temporary pixel PLL not locked after 5 secs\n"); /* output pclk on mclk pin */ - mclk_ctl = inTi3026(PMINFO TVP3026_XMEMPLLCTRL); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4); + mclk_ctl = inTi3026(PMINFO_ TVP3026_XMEMPLLCTRL); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4); /* stop MCLK */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFB); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, 0x00); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFB); + outTi3026(PMINFO_ TVP3026_XMEMPLLDATA, 0x00); /* set mclk to new freq */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xF3); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_n | 0xC0); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_m); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_p | 0xB0); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xF3); + outTi3026(PMINFO_ TVP3026_XMEMPLLDATA, mclk_n | 0xC0); + outTi3026(PMINFO_ TVP3026_XMEMPLLDATA, mclk_m); + outTi3026(PMINFO_ TVP3026_XMEMPLLDATA, mclk_p | 0xB0); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XMEMPLLDATA) & 0x40) + if (inTi3026(PMINFO_ TVP3026_XMEMPLLDATA) & 0x40) break; udelay(10); } @@ -643,26 +645,26 @@ if (rfhcnt > 15) rfhcnt = 0; } - hw->MXoptionReg = (hw->MXoptionReg & ~0x000F0000) | (rfhcnt << 16); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg = (ACCESS_FBINFO(hw).MXoptionReg & ~0x000F0000) | (rfhcnt << 16); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); /* output MCLK to MCLK pin */ - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4); /* stop PCLK */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFE); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, 0x00); /* restore pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_n); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_m); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_p); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0xFC); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, pclk_n); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, pclk_m); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, pclk_p); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(PMINFO_ TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); } @@ -670,7 +672,7 @@ printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); } -static void ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw){ +static void ti3026_ramdac_init(WPMINFO) { DBG("ti3026_ramdac_init") @@ -683,11 +685,12 @@ ACCESS_FBINFO(features.pll.post_shift_max) = 3; if (ACCESS_FBINFO(devflags.noinit)) return; - ti3026_setMCLK(PMINFO hw, 60000); + ti3026_setMCLK(PMINFO_ 60000); } -static void Ti3026_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw, struct display* p) { +static void Ti3026_restore(WPMINFO_ struct display* p) { int i; + unsigned char progdac[6]; CRITFLAGS DBG("Ti3026_restore") @@ -695,58 +698,58 @@ #ifdef DEBUG dprintk(KERN_INFO "EXTVGA regs: "); for (i = 0; i < 6; i++) - dprintk("%02X:", hw->CRTCEXT[i]); + dprintk("%02X:", ACCESS_FBINFO(hw).CRTCEXT[i]); dprintk("\n"); #endif CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); CRITEND - matroxfb_vgaHWrestore(PMINFO hw, oldhw); + matroxfb_vgaHWrestore(PMINFO); CRITBEGIN for (i = 0; i < 6; i++) - mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); + mga_setr(M_EXTVGA_INDEX, i, ACCESS_FBINFO(hw).CRTCEXT[i]); for (i = 0; i < 21; i++) { - outTi3026(PMINFO DACseq[i], hw->DACreg[i]); - } - if (oldhw) { - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); - oldhw->DACclk[0] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[3] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x15); - oldhw->DACclk[1] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[4] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - oldhw->DACclk[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - oldhw->DACclk[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); + outTi3026(PMINFO_ DACseq[i], ACCESS_FBINFO(hw).DACreg[i]); } + + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x00); + progdac[0] = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); + progdac[3] = inTi3026(PMINFO_ TVP3026_XLOOPPLLDATA); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x15); + progdac[1] = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); + progdac[4] = inTi3026(PMINFO_ TVP3026_XLOOPPLLDATA); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x2A); + progdac[2] = inTi3026(PMINFO_ TVP3026_XPIXPLLDATA); + progdac[5] = inTi3026(PMINFO_ TVP3026_XLOOPPLLDATA); + CRITEND - if (!oldhw || memcmp(hw->DACclk, oldhw->DACclk, 6)) { + if (memcmp(ACCESS_FBINFO(hw).DACclk, progdac, 6)) { /* agrhh... setting up PLL is very slow on Millennium... */ /* Mystique PLL is locked in few ms, but Millennium PLL lock takes about 0.15 s... */ /* Maybe even we should call schedule() ? */ CRITBEGIN - outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0); + outTi3026(PMINFO_ TVP3026_XCLKCTRL, ACCESS_FBINFO(hw).DACreg[POS3026_XCLKCTRL]); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x2A); + outTi3026(PMINFO_ TVP3026_XLOOPPLLDATA, 0); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, 0); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x00); for (i = 0; i < 3; i++) - outTi3026(PMINFO TVP3026_XPIXPLLDATA, hw->DACclk[i]); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, ACCESS_FBINFO(hw).DACclk[i]); /* wait for PLL only if PLL clock requested (always for PowerMode, never for VGA) */ - if (hw->MiscOutReg & 0x08) { + if (ACCESS_FBINFO(hw).MiscOutReg & 0x08) { int tmout; - outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x3F); for (tmout = 500000; tmout; --tmout) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(PMINFO_ TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); } @@ -759,18 +762,18 @@ dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout); CRITBEGIN } - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, ACCESS_FBINFO(hw).DACreg[POS3026_XMEMPLLCTRL]); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x00); for (i = 3; i < 6; i++) - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]); + outTi3026(PMINFO_ TVP3026_XLOOPPLLDATA, ACCESS_FBINFO(hw).DACclk[i]); CRITEND - if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) { + if ((ACCESS_FBINFO(hw).MiscOutReg & 0x08) && ((ACCESS_FBINFO(hw).DACclk[5] & 0x80) == 0x80)) { int tmout; CRITBEGIN - outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F); + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x3F); for (tmout = 500000; tmout; --tmout) { - if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40) + if (inTi3026(PMINFO_ TVP3026_XLOOPPLLDATA) & 0x40) break; udelay(10); } @@ -781,31 +784,31 @@ dprintk(KERN_INFO "LoopPLL: %d\n", 500000-tmout); } } - matrox_init_putc(PMINFO p, matroxfb_ti3026_createcursor); + matrox_init_putc(PMINFO_ p, matroxfb_ti3026_createcursor); #ifdef DEBUG dprintk(KERN_DEBUG "3026DACregs "); for (i = 0; i < 21; i++) { - dprintk("R%02X=%02X ", DACseq[i], hw->DACreg[i]); + dprintk("R%02X=%02X ", DACseq[i], ACCESS_FBINFO(hw).DACreg[i]); if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... "); } dprintk("\n" KERN_DEBUG "DACclk "); for (i = 0; i < 6; i++) - dprintk("C%02X=%02X ", i, hw->DACclk[i]); + dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]); dprintk("\n"); #endif } -static void Ti3026_reset(WPMINFO struct matrox_hw_state* hw){ +static void Ti3026_reset(WPMINFO) { DBG("Ti3026_reset") - matroxfb_fastfont_init(MINFO); + matroxfb_fastfont_init(PMINFO); - ti3026_ramdac_init(PMINFO hw); + ti3026_ramdac_init(PMINFO); } -static int Ti3026_preinit(WPMINFO struct matrox_hw_state* hw){ +static int Ti3026_preinit(WPMINFO) { static const int vxres_mill2[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; @@ -825,29 +828,29 @@ if (ACCESS_FBINFO(devflags.noinit)) return 0; /* preserve VGA I/O, BIOS and PPC */ - hw->MXoptionReg &= 0xC0000100; - hw->MXoptionReg |= 0x002C0000; + ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; + ACCESS_FBINFO(hw).MXoptionReg |= 0x002C0000; if (ACCESS_FBINFO(devflags.novga)) - hw->MXoptionReg &= ~0x00000100; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; if (ACCESS_FBINFO(devflags.nobios)) - hw->MXoptionReg &= ~0x40000000; + ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; if (ACCESS_FBINFO(devflags.nopciretry)) - hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; + pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); - ACCESS_FBINFO(accel.ramdac_rev) = inTi3026(PMINFO TVP3026_XSILICONREV); + ACCESS_FBINFO(accel.ramdac_rev) = inTi3026(PMINFO_ TVP3026_XSILICONREV); - outTi3026(PMINFO TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED); - outTi3026(PMINFO TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR); - outTi3026(PMINFO TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA); - - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0x00); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(PMINFO_ TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED); + outTi3026(PMINFO_ TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR); + outTi3026(PMINFO_ TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA); + + outTi3026(PMINFO_ TVP3026_XPLLADDR, 0x2A); + outTi3026(PMINFO_ TVP3026_XLOOPPLLDATA, 0x00); + outTi3026(PMINFO_ TVP3026_XPIXPLLDATA, 0x00); mga_outb(M_MISC_REG, 0x67); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + outTi3026(PMINFO_ TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); mga_outl(M_RESET, 1); udelay(250); diff -Nru a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c --- a/drivers/video/matrox/matroxfb_accel.c Wed Feb 13 20:03:30 2002 +++ b/drivers/video/matrox/matroxfb_accel.c Wed Feb 13 20:03:30 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/06/18 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -85,7 +87,7 @@ #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) -void matrox_cfbX_init(WPMINFO struct display* p) { +void matrox_cfbX_init(WPMINFO_ struct display* p) { u_int32_t maccess; u_int32_t mpitch; u_int32_t mopmode; @@ -242,7 +244,7 @@ } #endif -static void matroxfb_accel_clear(WPMINFO u_int32_t color, int sy, int sx, int height, +static void matroxfb_accel_clear(WPMINFO_ u_int32_t color, int sy, int sx, int height, int width) { CRITFLAGS @@ -943,13 +945,13 @@ CRITEND } -void matrox_text_createcursor(WPMINFO struct display* p) { +void matrox_text_createcursor(WPMINFO_ struct display* p) { CRITFLAGS if (ACCESS_FBINFO(currcon_display) != p) return; - matroxfb_createcursorshape(PMINFO p, 0); + matroxfb_createcursorshape(PMINFO_ p, 0); CRITBEGIN @@ -981,7 +983,7 @@ return; } if ((p->conp->vc_cursor_type & CUR_HWMASK) != ACCESS_FBINFO(cursor.type)) - matrox_text_createcursor(PMINFO p); + matrox_text_createcursor(PMINFO_ p); /* DO NOT CHECK cursor.x != x because of matroxfb_vgaHWinit moves cursor to 0,0 */ ACCESS_FBINFO(cursor.x) = x; @@ -1000,7 +1002,7 @@ ACCESS_FBINFO(cursor.state) = CM_DRAW; } -void matrox_text_round(CPMINFO struct fb_var_screeninfo* var, struct display* p) { +void matrox_text_round(CPMINFO_ struct fb_var_screeninfo* var, struct display* p) { unsigned hf; unsigned vf; unsigned vxres; @@ -1035,11 +1037,11 @@ if (p) { MINFO_FROM_DISP(p); - matrox_text_round(PMINFO &p->var, p); + matrox_text_round(PMINFO_ &p->var, p); p->next_line = p->line_length = ((p->var.xres_virtual / (fontwidth(p)?fontwidth(p):8)) * ACCESS_FBINFO(devflags.textstep)); if (p->conp) - matrox_text_createcursor(PMINFO p); + matrox_text_createcursor(PMINFO_ p); } return 0; } @@ -1130,7 +1132,7 @@ }; #endif -void initMatrox(WPMINFO struct display* p) { +void initMatrox(WPMINFO_ struct display* p) { struct display_switch *swtmp; DBG("initMatrox") @@ -1219,21 +1221,21 @@ memcpy(&ACCESS_FBINFO(dispsw), swtmp, sizeof(ACCESS_FBINFO(dispsw))); p->dispsw = &ACCESS_FBINFO(dispsw); if ((p->type != FB_TYPE_TEXT) && ACCESS_FBINFO(devflags.hwcursor)) { - ACCESS_FBINFO(hw_switch)->selhwcursor(PMINFO p); + ACCESS_FBINFO(hw_switch)->selhwcursor(PMINFO_ p); } } -void matrox_init_putc(WPMINFO struct display* p, void (*dac_createcursor)(WPMINFO struct display* p)) { +void matrox_init_putc(WPMINFO_ struct display* p, void (*dac_createcursor)(WPMINFO_ struct display* p)) { int i; if (p && p->conp) { if (p->type == FB_TYPE_TEXT) { - matrox_text_createcursor(PMINFO p); - matrox_text_loadfont(PMINFO p); + matrox_text_createcursor(PMINFO_ p); + matrox_text_loadfont(PMINFO_ p); i = 0; } else { - dac_createcursor(PMINFO p); - i = matroxfb_fastfont_tryset(PMINFO p); + dac_createcursor(PMINFO_ p); + i = matroxfb_fastfont_tryset(PMINFO_ p); } } else i = 0; diff -Nru a/drivers/video/matrox/matroxfb_accel.h b/drivers/video/matrox/matroxfb_accel.h --- a/drivers/video/matrox/matroxfb_accel.h Wed Feb 13 20:03:40 2002 +++ b/drivers/video/matrox/matroxfb_accel.h Wed Feb 13 20:03:40 2002 @@ -3,10 +3,10 @@ #include "matroxfb_base.h" -void matrox_init_putc(WPMINFO struct display* p, void (*)(WPMINFO struct display *p)); -void matrox_cfbX_init(WPMINFO struct display* p); -void matrox_text_createcursor(WPMINFO struct display* p); -void matrox_text_round(CPMINFO struct fb_var_screeninfo* var, struct display* p); -void initMatrox(WPMINFO struct display* p); +void matrox_init_putc(WPMINFO_ struct display* p, void (*)(WPMINFO_ struct display *p)); +void matrox_cfbX_init(WPMINFO_ struct display* p); +void matrox_text_createcursor(WPMINFO_ struct display* p); +void matrox_text_round(CPMINFO_ struct fb_var_screeninfo* var, struct display* p); +void initMatrox(WPMINFO_ struct display* p); #endif diff -Nru a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c --- a/drivers/video/matrox/matroxfb_base.c Wed Feb 13 20:03:35 2002 +++ b/drivers/video/matrox/matroxfb_base.c Wed Feb 13 20:03:35 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -141,7 +143,7 @@ /* --------------------------------------------------------------------- */ -static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) { +static void matrox_pan_var(WPMINFO_ struct fb_var_screeninfo *var) { unsigned int pos; unsigned short p0, p1, p2; #ifdef CONFIG_FB_MATROX_32MB @@ -162,11 +164,11 @@ pos = (var->yoffset * var->xres_virtual + var->xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32; pos += ACCESS_FBINFO(curr.ydstorg.chunks); } - p0 = ACCESS_FBINFO(currenthw)->CRTC[0x0D] = pos & 0xFF; - p1 = ACCESS_FBINFO(currenthw)->CRTC[0x0C] = (pos & 0xFF00) >> 8; - p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + p0 = ACCESS_FBINFO(hw).CRTC[0x0D] = pos & 0xFF; + p1 = ACCESS_FBINFO(hw).CRTC[0x0C] = (pos & 0xFF00) >> 8; + p2 = ACCESS_FBINFO(hw).CRTCEXT[0] = (ACCESS_FBINFO(hw).CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); #ifdef CONFIG_FB_MATROX_32MB - p3 = ACCESS_FBINFO(currenthw)->CRTCEXT[8] = pos >> 21; + p3 = ACCESS_FBINFO(hw).CRTCEXT[8] = pos >> 21; #endif CRITBEGIN @@ -182,7 +184,7 @@ CRITEND } -static void matroxfb_remove(WPMINFO int dummy) { +static void matroxfb_remove(WPMINFO_ int dummy) { /* Currently we are holding big kernel lock on all dead & usecount updates. * Destroy everything after all users release it. Especially do not unregister * framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check @@ -236,7 +238,7 @@ DBG_LOOP("matroxfb_release") if (!(--ACCESS_FBINFO(usecount)) && ACCESS_FBINFO(dead)) { - matroxfb_remove(PMINFO 0); + matroxfb_remove(PMINFO_ 0); } #undef minfo return(0); @@ -257,7 +259,7 @@ return -EINVAL; } if (con == ACCESS_FBINFO(currcon)) - matrox_pan_var(PMINFO var); + matrox_pan_var(PMINFO_ var); fb_display[con].var.xoffset = var->xoffset; fb_display[con].var.yoffset = var->yoffset; if (var->vmode & FB_VMODE_YWRAP) @@ -273,12 +275,12 @@ #define minfo ((struct matrox_fb_info*)info) DBG("matroxfb_updatevar"); - matrox_pan_var(PMINFO &fb_display[con].var); + matrox_pan_var(PMINFO_ &fb_display[con].var); return 0; #undef minfo } -static int matroxfb_get_final_bppShift(CPMINFO int bpp) { +static int matroxfb_get_final_bppShift(CPMINFO_ int bpp) { int bppshft2; DBG("matroxfb_get_final_bppShift") @@ -294,7 +296,7 @@ return bppshft2; } -static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) { +static int matroxfb_test_and_set_rounding(CPMINFO_ int xres, int bpp) { int over; int rounding; @@ -325,7 +327,7 @@ return xres; } -static int matroxfb_pitch_adjust(CPMINFO int xres, int bpp) { +static int matroxfb_pitch_adjust(CPMINFO_ int xres, int bpp) { const int* width; int xres_new; @@ -337,14 +339,14 @@ if (ACCESS_FBINFO(devflags.precise_width)) { while (*width) { - if ((*width >= xres) && (matroxfb_test_and_set_rounding(PMINFO *width, bpp) == *width)) { + if ((*width >= xres) && (matroxfb_test_and_set_rounding(PMINFO_ *width, bpp) == *width)) { break; } width++; } xres_new = *width; } else { - xres_new = matroxfb_test_and_set_rounding(PMINFO xres, bpp); + xres_new = matroxfb_test_and_set_rounding(PMINFO_ xres, bpp); } if (!xres_new) return 0; if (xres != xres_new) { @@ -389,7 +391,7 @@ return 16; /* return something reasonable... or panic()? */ } -static int matroxfb_decode_var(CPMINFO struct display* p, struct fb_var_screeninfo *var, int *visual, int *video_cmap_len, unsigned int* ydstorg) { +static int matroxfb_decode_var(CPMINFO_ struct display* p, struct fb_var_screeninfo *var, int *visual, int *video_cmap_len, unsigned int* ydstorg) { unsigned int vramlen; unsigned int memlen; @@ -425,7 +427,7 @@ if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->bits_per_pixel) { - var->xres_virtual = matroxfb_pitch_adjust(PMINFO var->xres_virtual, var->bits_per_pixel); + var->xres_virtual = matroxfb_pitch_adjust(PMINFO_ var->xres_virtual, var->bits_per_pixel); memlen = var->xres_virtual * var->bits_per_pixel * var->yres_virtual / 8; if (memlen > vramlen) { var->yres_virtual = vramlen * 8 / (var->xres_virtual * var->bits_per_pixel); @@ -459,7 +461,7 @@ } } } else { - matrox_text_round(PMINFO var, p); + matrox_text_round(PMINFO_ var, p); #if 0 /* we must limit pixclock by mclk... Millennium I: 66 MHz = 15000 @@ -655,7 +657,7 @@ return 0; } -static void do_install_cmap(WPMINFO struct display* dsp) +static void do_install_cmap(WPMINFO_ struct display* dsp) { DBG("do_install_cmap") @@ -737,7 +739,7 @@ display = fb_display + con; else display = ACCESS_FBINFO(fbcon.disp); - if ((err = matroxfb_decode_var(PMINFO display, var, &visual, &cmap_len, &ydstorg)) != 0) + if ((err = matroxfb_decode_var(PMINFO_ display, var, &visual, &cmap_len, &ydstorg)) != 0) return err; switch (var->activate & FB_ACTIVATE_MASK) { case FB_ACTIVATE_TEST: return 0; @@ -776,7 +778,7 @@ display->inverse = ACCESS_FBINFO(devflags.inverse); /* conp, fb_info, vrows, cursor_x, cursor_y, fgcol, bgcol */ /* next_plane, fontdata, _font*, userfont */ - initMatrox(PMINFO display); /* dispsw */ + initMatrox(PMINFO_ display); /* dispsw */ /* dispsw, scrollmode, yscroll */ /* fgshift, bgshift, charmask */ if (chgvar && info && info->changevar) @@ -799,7 +801,7 @@ else ACCESS_FBINFO(curr.ydstorg.pixels) = (ydstorg * 8) / var->bits_per_pixel; } - ACCESS_FBINFO(curr.final_bppShift) = matroxfb_get_final_bppShift(PMINFO var->bits_per_pixel); + ACCESS_FBINFO(curr.final_bppShift) = matroxfb_get_final_bppShift(PMINFO_ var->bits_per_pixel); if (visual == MX_VISUAL_PSEUDOCOLOR) { int i; @@ -814,8 +816,6 @@ } { struct my_timming mt; - struct matrox_hw_state* hw; - struct matrox_hw_state* ohw; matroxfb_var2my(var, &mt); /* CRTC1 delays */ @@ -827,16 +827,10 @@ default: mt.delay = 31 + 8; break; } - hw = ACCESS_FBINFO(newhw); - ohw = ACCESS_FBINFO(currenthw); - - /* copy last setting... */ - memcpy(hw, ohw, sizeof(*hw)); - del_timer_sync(&ACCESS_FBINFO(cursor.timer)); ACCESS_FBINFO(cursor.state) = CM_ERASE; - ACCESS_FBINFO(hw_switch->init(PMINFO hw, &mt, display)); + ACCESS_FBINFO(hw_switch->init(PMINFO_ &mt, display)); if (display->type == FB_TYPE_TEXT) { if (fontheight(display)) pos = var->yoffset / fontheight(display) * display->next_line / ACCESS_FBINFO(devflags.textstep) + var->xoffset / (fontwidth(display)?fontwidth(display):8); @@ -847,34 +841,32 @@ pos += ACCESS_FBINFO(curr.ydstorg.chunks); } - hw->CRTC[0x0D] = pos & 0xFF; - hw->CRTC[0x0C] = (pos & 0xFF00) >> 8; - hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); - hw->CRTCEXT[8] = pos >> 21; + ACCESS_FBINFO(hw).CRTC[0x0D] = pos & 0xFF; + ACCESS_FBINFO(hw).CRTC[0x0C] = (pos & 0xFF00) >> 8; + ACCESS_FBINFO(hw).CRTCEXT[0] = (ACCESS_FBINFO(hw).CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + ACCESS_FBINFO(hw).CRTCEXT[8] = pos >> 21; if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); + ACCESS_FBINFO(primout)->compute(MINFO, &mt); } if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw); + ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt); up_read(&ACCESS_FBINFO(altout.lock)); } - ACCESS_FBINFO(hw_switch->restore(PMINFO hw, ohw, display)); + ACCESS_FBINFO(hw_switch->restore(PMINFO_ display)); if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->program(MINFO, hw); + ACCESS_FBINFO(primout)->program(MINFO); } if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw); + ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device)); up_read(&ACCESS_FBINFO(altout.lock)); } ACCESS_FBINFO(cursor.redraw) = 1; - ACCESS_FBINFO(currenthw) = hw; - ACCESS_FBINFO(newhw) = ohw; if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) ACCESS_FBINFO(primout)->start(MINFO); @@ -885,8 +877,8 @@ ACCESS_FBINFO(altout.output)->start(ACCESS_FBINFO(altout.device)); up_read(&ACCESS_FBINFO(altout.lock)); } - matrox_cfbX_init(PMINFO display); - do_install_cmap(PMINFO display); + matrox_cfbX_init(PMINFO_ display); + do_install_cmap(PMINFO_ display); #if defined(CONFIG_FB_COMPAT_XPMAC) if (console_fb_info == &ACCESS_FBINFO(fbcon)) { int vmode, cmode; @@ -989,7 +981,7 @@ #undef minfo } -static int matroxfb_get_vblank(CPMINFO struct fb_vblank *vblank) +static int matroxfb_get_vblank(CPMINFO_ struct fb_vblank *vblank) { unsigned int sts1; @@ -1005,7 +997,7 @@ vblank->flags |= FB_VBLANK_HBLANKING; if (sts1 & 8) vblank->flags |= FB_VBLANK_VSYNCING; - if (vblank->count >= ACCESS_FBINFO(currcon_display)->var.yres) + if (vblank->vcount >= ACCESS_FBINFO(currcon_display)->var.yres) vblank->flags |= FB_VBLANK_VBLANKING; vblank->hcount = 0; vblank->count = 0; @@ -1029,7 +1021,7 @@ struct fb_vblank vblank; int err; - err = matroxfb_get_vblank(PMINFO &vblank); + err = matroxfb_get_vblank(PMINFO_ &vblank); if (err) return err; if (copy_to_user((struct fb_vblank*)arg, &vblank, sizeof(vblank))) @@ -1333,7 +1325,7 @@ static char videomode[64]; /* "matrox:mode:xxxxx" or "matrox:xxxxx" */ #endif -static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSize){ +static int matroxfb_getmemory(WPMINFO_ unsigned int maxSize, unsigned int *realSize){ vaddr_t vm; unsigned int offs; unsigned int offs2; @@ -1417,7 +1409,7 @@ #define DEVF_CROSS4MB 0x0010 #define DEVF_TEXT4B 0x0020 #define DEVF_DDC_8_2 0x0040 -#define DEVF_G550DAC 0x0080 +/* #define DEVF_recycled 0x0080 */ #define DEVF_SUPPORT32MB 0x0100 #define DEVF_ANY_VXRES 0x0200 #define DEVF_TEXT16B 0x0400 @@ -1433,12 +1425,13 @@ #define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2) /* if you'll find how to drive DFP... */ #define DEVF_G450 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG) -#define DEVF_G550 (DEVF_G450 | DEVF_G550DAC | DEVF_BOTHDACS) +#define DEVF_G550 (DEVF_G450 | DEVF_BOTHDACS) static struct board { unsigned short vendor, device, rev, svid, sid; unsigned int flags; unsigned int maxclk; + enum mga_chip chip; struct video_board* base; const char* name; } dev_list[] = { @@ -1447,18 +1440,21 @@ 0, 0, DEVF_TEXT4B, 230000, + MGA_2064, &vbMillennium, "Millennium (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, 0xFF, 0, 0, DEVF_SWAPS, 220000, + MGA_2164, &vbMillennium2, "Millennium II (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, 0xFF, 0, 0, DEVF_SWAPS, 250000, + MGA_2164, &vbMillennium2A, "Millennium II (AGP)"}, #endif @@ -1467,122 +1463,107 @@ 0, 0, DEVF_VIDEO64BIT | DEVF_CROSS4MB, 180000, + MGA_1064, &vbMystique, "Mystique (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0xFF, 0, 0, DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB, 220000, + MGA_1164, &vbMystique, "Mystique 220 (PCI)"}, #endif #ifdef CONFIG_FB_MATROX_G100 {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MGA_G100_PCI, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (PCI)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, 0, 0, DEVF_G100, 230000, + MGA_G100, &vbG100, - "unknown G100 (PCI)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MGA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "MGA-G100 (AGP)"}, - {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, - PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP, - DEVF_G100, - 230000, - &vbG100, - "Productiva G100 (AGP)"}, + "MGA-G100 (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, 0, 0, DEVF_G100, 230000, + MGA_G100, &vbG100, - "unknown G100 (AGP)"}, + "MGA-G100 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, 0xFF, 0, 0, DEVF_G200, 250000, + MGA_G200, &vbG200, - "unknown G200 (PCI)"}, + "MGA-G200 (PCI)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC, DEVF_G200, 220000, + MGA_G200, &vbG200, "MGA-G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "Mystique G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENIUM_G200_AGP, DEVF_G200, 250000, + MGA_G200, &vbG200, "Millennium G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MARVEL_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "Marvel G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G200_AGP, DEVF_G200, 230000, + MGA_G200, &vbG200, "MGA-G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, 0, 0, DEVF_G200, 230000, + MGA_G200, &vbG200, "G200 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP, DEVF_G400, 360000, + MGA_G400, &vbG400, "Millennium G400 MAX (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, 0, 0, DEVF_G400, 300000, + MGA_G400, &vbG400, "G400 (AGP)"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0xFF, 0, 0, DEVF_G450, - 500000, /* ??? vco goes up to 900MHz... */ + 360000, + MGA_G450, &vbG400, "G450"}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, 0xFF, 0, 0, DEVF_G550, - 500000, + 360000, + MGA_G550, &vbG400, "G550"}, #endif @@ -1590,6 +1571,7 @@ 0, 0, 0, 0, + 0, NULL, NULL}}; @@ -1603,11 +1585,10 @@ static int hotplug = 0; -static int initMatrox2(WPMINFO struct display* d, struct board* b){ +static int initMatrox2(WPMINFO_ struct display* d, struct board* b){ unsigned long ctrlptr_phys = 0; unsigned long video_base_phys = 0; unsigned int memsize; - struct matrox_hw_state* hw = ACCESS_FBINFO(currenthw); int err; DBG("initMatrox2") @@ -1621,6 +1602,7 @@ printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name); ACCESS_FBINFO(capable.plnwt) = 1; + ACCESS_FBINFO(chip) = b->chip; ACCESS_FBINFO(capable.srcorg) = b->flags & DEVF_SRCORG; ACCESS_FBINFO(devflags.video64bits) = b->flags & DEVF_VIDEO64BIT; if (b->flags & DEVF_TEXT4B) { @@ -1658,7 +1640,6 @@ } ACCESS_FBINFO(devflags.dfp_type) = dfp_type; ACCESS_FBINFO(devflags.g450dac) = b->flags & DEVF_G450DAC; - ACCESS_FBINFO(devflags.g550dac) = b->flags & DEVF_G550DAC; ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode); ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode); @@ -1667,9 +1648,11 @@ if (b->flags & DEVF_SWAPS) { ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); + ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_0; } else { ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); + ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_1; } err = -EINVAL; if (!ctrlptr_phys) { @@ -1726,7 +1709,7 @@ } pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, cmd); pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mga_option); - hw->MXoptionReg = mga_option; + ACCESS_FBINFO(hw).MXoptionReg = mga_option; /* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */ /* maybe preinit() candidate, but it is same... for all devices... at this time... */ @@ -1734,12 +1717,13 @@ } err = -ENXIO; - if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO hw)) { + matroxfb_read_pins(PMINFO); + if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO)) { goto failVideoIO; } err = -ENOMEM; - if (!matroxfb_getmemory(PMINFO memsize, &ACCESS_FBINFO(video.len)) || !ACCESS_FBINFO(video.len)) { + if (!matroxfb_getmemory(PMINFO_ memsize, &ACCESS_FBINFO(video.len)) || !ACCESS_FBINFO(video.len)) { printk(KERN_ERR "matroxfb: cannot determine memory size\n"); goto failVideoIO; } @@ -1747,13 +1731,7 @@ ACCESS_FBINFO(currcon) = -1; ACCESS_FBINFO(currcon_display) = d; - mga_iounmap(ACCESS_FBINFO(video.vbase)); ACCESS_FBINFO(video.base) = video_base_phys; - if (mga_ioremap(video_base_phys, ACCESS_FBINFO(video.len), MGA_IOREMAP_FB, &ACCESS_FBINFO(video.vbase))) { - printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n", - video_base_phys, ACCESS_FBINFO(video.len)); - goto failCtrlIO; - } ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len); if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable) ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable; @@ -1767,7 +1745,7 @@ if (!ACCESS_FBINFO(devflags.novga)) request_region(0x3C0, 32, "matrox"); - ACCESS_FBINFO(hw_switch->reset(PMINFO hw)); + ACCESS_FBINFO(hw_switch->reset(PMINFO)); ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0; ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh; @@ -1849,9 +1827,9 @@ + vesafb_defined.right_margin + vesafb_defined.hsync_len); if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp; } - maxclk = (maxclk + 499) / 500; - if (maxclk) { - tmp = (2000000000 + maxclk) / maxclk; + tmp = (maxclk + 499) / 500; + if (tmp) { + tmp = (2000000000 + tmp) / tmp; if (tmp > pixclock) pixclock = tmp; } } @@ -2071,8 +2049,6 @@ memset(MINFO, 0, sizeof(*MINFO)); memset(d, 0, sizeof(*d)); - ACCESS_FBINFO(currenthw) = &ACCESS_FBINFO(hw1); - ACCESS_FBINFO(newhw) = &ACCESS_FBINFO(hw2); ACCESS_FBINFO(pcidev) = pdev; ACCESS_FBINFO(dead) = 0; ACCESS_FBINFO(usecount) = 0; @@ -2120,7 +2096,7 @@ ACCESS_FBINFO(output.ph) = MATROXFB_OUTPUT_CONN_PRIMARY; ACCESS_FBINFO(output.sh) = 0; - err = initMatrox2(PMINFO d, b); + err = initMatrox2(PMINFO_ d, b); if (!err) { #ifndef CONFIG_FB_MATROX_MULTIHEAD registered = 1; @@ -2139,7 +2115,7 @@ struct matrox_fb_info* minfo; minfo = pci_get_drvdata(pdev); - matroxfb_remove(PMINFO 1); + matroxfb_remove(PMINFO_ 1); } static struct pci_device_id matroxfb_devices[] __devinitdata = { diff -Nru a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h --- a/drivers/video/matrox/matroxfb_base.h Wed Feb 13 20:03:55 2002 +++ b/drivers/video/matrox/matroxfb_base.h Wed Feb 13 20:03:55 2002 @@ -166,7 +166,7 @@ #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) -/* G100, G200 and Mystique have (almost) same DAC */ +/* G-series and Mystique have (almost) same DAC */ #undef NEED_DAC1064 #if defined(CONFIG_FB_MATROX_MYSTIQUE) || defined(CONFIG_FB_MATROX_G100) #define NEED_DAC1064 1 @@ -309,6 +309,21 @@ unsigned int delay; /* CRTC delay */ }; +enum { M_SYSTEM_PLL, M_PIXEL_PLL_A, M_PIXEL_PLL_B, M_PIXEL_PLL_C, M_VIDEO_PLL }; + +struct matrox_pll_cache { + unsigned int valid; + struct { + unsigned int mnp_key; + unsigned int mnp_value; + } data[4]; +}; + +struct matrox_pll_limits { + unsigned int vcomin; + unsigned int vcomax; +}; + struct matrox_pll_features { unsigned int vco_freq_min; unsigned int ref_freq; @@ -375,6 +390,8 @@ /* CRTC2 only */ /* u_int32_t TBD */ + + unsigned int vidclk; }; struct matrox_accel_data { @@ -386,8 +403,8 @@ }; struct matrox_altout { - int (*compute)(void* altout_dev, struct my_timming* input, struct matrox_hw_state* state); - int (*program)(void* altout_dev, const struct matrox_hw_state* state); + int (*compute)(void* altout_dev, struct my_timming* input); + int (*program)(void* altout_dev); int (*start)(void* altout_dev); void (*incuse)(void* altout_dev); void (*decuse)(void* altout_dev); @@ -395,6 +412,20 @@ int (*getmode)(void* altout_dev, u_int32_t* mode); }; +enum mga_chip { MGA_2064, MGA_2164, MGA_1064, MGA_1164, MGA_G100, MGA_G200, MGA_G400, MGA_G450, MGA_G550 }; + +struct matrox_bios { + unsigned int bios_valid : 1; + unsigned int pins_len; + unsigned char pins[128]; + struct { + unsigned char vMaj, vMin, vRev; + } version; + struct { + unsigned char state, tvout; + } output; +}; + struct matrox_switch; struct matroxfb_driver; @@ -408,10 +439,7 @@ unsigned int usecount; struct matroxfb_par curr; - struct matrox_hw_state hw1; - struct matrox_hw_state hw2; - struct matrox_hw_state* newhw; - struct matrox_hw_state* currenthw; + struct matrox_hw_state hw; struct matrox_accel_data accel; @@ -468,6 +496,8 @@ spinlock_t accel; } lock; + enum mga_chip chip; + int interleave; int millenium; int milleniumII; @@ -518,8 +548,8 @@ /* 0 except for 6MB Millenium */ int memtype; int g450dac; - int g550dac; int dfp_type; + unsigned int fbResource; } devflags; struct display_switch dispsw; struct { @@ -533,6 +563,54 @@ int redraw; struct timer_list timer; } cursor; + struct matrox_bios bios; + struct { + struct matrox_pll_limits pixel; + struct matrox_pll_limits system; + struct matrox_pll_limits video; +#if 0 + struct { + unsigned int b8; + unsigned int b16; + unsigned int b24; + unsigned int b32; + } c1; + struct { + unsigned int b16; + unsigned int b32; + } c2; + struct { + unsigned int dac1; + unsigned int dac2; + } out; +#endif + } limits; + struct { + struct matrox_pll_cache pixel; + struct matrox_pll_cache system; + struct matrox_pll_cache video; + } cache; + struct { + struct { + unsigned int video; + unsigned int system; + } pll; + struct { + u_int32_t opt; + u_int32_t opt2; + u_int32_t opt3; + u_int32_t mctlwtst; + u_int32_t mctlwtst_core; + u_int32_t memmisc; + u_int32_t memrdbk; + u_int32_t maccess; + } reg; + struct { + unsigned int ddr:1, + emrswen:1, + dll:1; + } memory; + } values; struct { unsigned red, green, blue, transp; } palette[256]; #if defined(CONFIG_FB_COMPAT_XPMAC) char matrox_name[32]; @@ -559,12 +637,12 @@ #define MINFO minfo -#define WPMINFO2 struct matrox_fb_info* minfo -#define WPMINFO WPMINFO2 , -#define CPMINFO2 const struct matrox_fb_info* minfo -#define CPMINFO CPMINFO2 , -#define PMINFO2 minfo -#define PMINFO PMINFO2 , +#define WPMINFO struct matrox_fb_info* minfo +#define WPMINFO_ WPMINFO , +#define CPMINFO const struct matrox_fb_info* minfo +#define CPMINFO_ CPMINFO , +#define PMINFO minfo +#define PMINFO_ PMINFO , static inline struct matrox_fb_info* mxinfo(const struct display* p) { return (struct matrox_fb_info*)p->fb_info; @@ -583,18 +661,12 @@ #define MINFO (&matroxfb_global_mxinfo) -#define WPMINFO2 void -#define WPMINFO -#define CPMINFO2 void -#define CPMINFO -#define PMINFO2 +#define WPMINFO void +#define WPMINFO_ +#define CPMINFO void +#define CPMINFO_ #define PMINFO - -#if 0 -static inline struct matrox_fb_info* mxinfo(const struct display* p) { - return &matroxfb_global_mxinfo; -} -#endif +#define PMINFO_ #define PMXINFO(p) #define MINFO_FROM(x) @@ -603,11 +675,11 @@ #endif struct matrox_switch { - int (*preinit)(WPMINFO struct matrox_hw_state*); - void (*reset)(WPMINFO struct matrox_hw_state*); - int (*init)(CPMINFO struct matrox_hw_state*, struct my_timming*, struct display*); - void (*restore)(WPMINFO struct matrox_hw_state*, struct matrox_hw_state*, struct display*); - int (*selhwcursor)(WPMINFO struct display*); + int (*preinit)(WPMINFO); + void (*reset)(WPMINFO); + int (*init)(WPMINFO_ struct my_timming*, struct display*); + void (*restore)(WPMINFO_ struct display*); + int (*selhwcursor)(WPMINFO_ struct display*); }; struct matroxfb_driver { @@ -621,8 +693,13 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define PCI_OPTION_REG 0x40 +#define PCI_OPTION_ENABLE_ROM 0x40000000 + #define PCI_MGA_INDEX 0x44 #define PCI_MGA_DATA 0x48 +#define PCI_OPTION2_REG 0x50 +#define PCI_OPTION3_REG 0x54 +#define PCI_MEMMISC_REG 0x58 #define M_DWGCTL 0x1C00 #define M_MACCESS 0x1C04 @@ -741,6 +818,8 @@ #define DAC_XGENIOCTRL 0x2A #define DAC_XGENIODATA 0x2B +#define M_C2CTL 0x3E10 + #ifdef __LITTLE_ENDIAN #define MX_OPTION_BSWAP 0x00000000 @@ -794,10 +873,11 @@ #define matroxfb_DAC_unlock() spin_unlock(&ACCESS_FBINFO(lock.DAC)) #define matroxfb_DAC_lock_irqsave(flags) spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC),flags) #define matroxfb_DAC_unlock_irqrestore(flags) spin_unlock_irqrestore(&ACCESS_FBINFO(lock.DAC),flags) -extern void matroxfb_DAC_out(CPMINFO int reg, int val); -extern int matroxfb_DAC_in(CPMINFO int reg); +extern void matroxfb_DAC_out(CPMINFO_ int reg, int val); +extern int matroxfb_DAC_in(CPMINFO_ int reg); extern struct list_head matroxfb_list; extern void matroxfb_var2my(struct fb_var_screeninfo* fvsi, struct my_timming* mt); +extern int matroxfb_switch(int con, struct fb_info *); #ifdef MATROXFB_USE_SPINLOCKS #define CRITBEGIN spin_lock_irqsave(&ACCESS_FBINFO(lock.accel), critflags); diff -Nru a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c --- a/drivers/video/matrox/matroxfb_crtc2.c Wed Feb 13 20:03:43 2002 +++ b/drivers/video/matrox/matroxfb_crtc2.c Wed Feb 13 20:03:43 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.52 2001/05/25 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * */ @@ -402,43 +404,35 @@ info->changevar(con); if (con == m2info->currcon) { struct my_timming mt; - struct matrox_hw_state* hw; - struct matrox_hw_state* ohw; unsigned int pos; matroxfb_var2my(var, &mt); /* CRTC2 delay */ mt.delay = 34; - hw = ACCESS_FBINFO(newhw); - ohw = ACCESS_FBINFO(currenthw); - - /* copy last setting... */ - memcpy(hw, ohw, sizeof(*hw)); - pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3; pos += m2info->video.offbase; - DAC1064_global_init(PMINFO hw); + DAC1064_global_init(PMINFO); if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); + ACCESS_FBINFO(primout)->compute(MINFO, &mt); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw); + ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt); up_read(&ACCESS_FBINFO(altout.lock)); } matroxfb_dh_restore(m2info, &mt, p, mode, pos); - DAC1064_global_restore(PMINFO hw); + DAC1064_global_restore(PMINFO); if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { if (ACCESS_FBINFO(primout)) - ACCESS_FBINFO(primout)->program(MINFO, hw); + ACCESS_FBINFO(primout)->program(MINFO); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { down_read(&ACCESS_FBINFO(altout.lock)); if (ACCESS_FBINFO(altout.output)) - ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw); + ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device)); up_read(&ACCESS_FBINFO(altout.lock)); } if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { @@ -679,7 +673,7 @@ {0,0,0,0,0,0} }; -static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { +static int matroxfb_dh_regit(CPMINFO_ struct matroxfb_dh_fb_info* m2info) { #define minfo (m2info->primary_dev) struct display* d; void* oldcrtc2; @@ -759,7 +753,7 @@ static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { #define minfo (m2info->primary_dev) - if (matroxfb_dh_regit(PMINFO m2info)) { + if (matroxfb_dh_regit(PMINFO_ m2info)) { printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); return -1; } diff -Nru a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c --- a/drivers/video/matrox/matroxfb_g450.c Wed Feb 13 20:03:42 2002 +++ b/drivers/video/matrox/matroxfb_g450.c Wed Feb 13 20:03:42 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/01/19 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -13,119 +15,28 @@ #include "matroxfb_g450.h" #include "matroxfb_misc.h" #include "matroxfb_DAC1064.h" +#include "g450_pll.h" #include #include -static int matroxfb_g450_get_reg(WPMINFO int reg) { - int val; - unsigned long flags; - - matroxfb_DAC_lock_irqsave(flags); - val = matroxfb_DAC_in(PMINFO reg); - matroxfb_DAC_unlock_irqrestore(flags); - return val; -} - -static int matroxfb_g450_set_reg(WPMINFO int reg, int val) { - unsigned long flags; - - matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO reg, val); - matroxfb_DAC_unlock_irqrestore(flags); +static int matroxfb_g450_compute(void* md, struct my_timming* mt) { +#define m2info ((struct matroxfb_g450_info*)md) +#define minfo (m2info->primary_dev) + ACCESS_FBINFO(hw).vidclk = mt->pixclock; +#undef minfo +#undef m2info return 0; } -static const struct matrox_pll_features maven_pll = { - 110000, - 27000, - 4, 127, - 2, 31, - 3 -}; - -static const struct matrox_pll_features g550_pll = { - 135000, - 27000, - 4, 127, - 0, 9, - 3 -}; - -static void DAC1064_calcclock(unsigned int freq, unsigned int fmax, - unsigned int* in, unsigned int* feed, unsigned int* post, - unsigned int timmings) { - unsigned int fvco; - unsigned int p; - - switch (timmings) { - default: - fvco = matroxfb_PLL_calcclock(&maven_pll, freq, fmax, in, feed, &p); - /* 0 => 100 ... 275 MHz - 1 => 243 ... 367 MHz - 2 => 320 ... 475 MHz - 3 => 453 ... 556 MHz - 4 => 540 ... 594 MHz - 5 => 588 ... 621 MHz - 6 => 626 ... 637 MHz - 7 => 631 ... 642 MHz - - As you can see, never choose frequency > 621 MHz, there is unavailable gap... - Just to be sure, currently driver uses 110 ... 500 MHz range. - */ - if (fvco <= 260000) - ; - else if (fvco <= 350000) - p |= 0x08; - else if (fvco <= 460000) - p |= 0x10; - else if (fvco <= 550000) - p |= 0x18; - else if (fvco <= 590000) - p |= 0x20; - else - p |= 0x28; - break; - case 1: - fvco = matroxfb_PLL_calcclock(&g550_pll, freq, fmax, in, feed, &p); - /* p |= 0x00; */ - break; - } - *post = p; - return; -} - -static inline int matroxfb_g450_compute_timming(struct matroxfb_g450_info* m2info, - struct my_timming* mt, - struct mavenregs* m) { - unsigned int a, b, c; - - DAC1064_calcclock(mt->pixclock, 300000, &a, &b, &c, m2info->timmings); - m->regs[0x80] = a; - m->regs[0x81] = b; - m->regs[0x82] = c; - printk(KERN_DEBUG "PLL: %02X %02X %02X\n", a, b, c); +static int matroxfb_g450_program(void* md) { +#define m2info ((struct matroxfb_g450_info*)md) +#define minfo (m2info->primary_dev) + matroxfb_g450_setclk(PMINFO_ ACCESS_FBINFO(hw).vidclk, M_VIDEO_PLL); +#undef minfo +#undef m2info return 0; } -static inline int matroxfb_g450_program_timming(struct matroxfb_g450_info* m2info, const struct mavenregs* m) { - MINFO_FROM(m2info->primary_dev); - - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2M, m->regs[0x81]); - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2N, m->regs[0x80]); - matroxfb_g450_set_reg(PMINFO M1064_XPIXPLL2P, m->regs[0x82]); - return 0; -} - -/******************************************************/ - -static int matroxfb_g450_compute(void* md, struct my_timming* mt, struct matrox_hw_state* mr) { - return matroxfb_g450_compute_timming(md, mt, &mr->maven); -} - -static int matroxfb_g450_program(void* md, const struct matrox_hw_state* mr) { - return matroxfb_g450_program_timming(md, &mr->maven); -} - static int matroxfb_g450_start(void* md) { return 0; } @@ -191,7 +102,7 @@ static void* matroxfb_g450_probe(struct matrox_fb_info* minfo) { struct matroxfb_g450_info* m2info; - /* hardware is not G450 incapable... */ + /* hardware is not G450... */ if (!ACCESS_FBINFO(devflags.g450dac)) return NULL; m2info = (struct matroxfb_g450_info*)kmalloc(sizeof(*m2info), GFP_KERNEL); @@ -201,11 +112,6 @@ } memset(m2info, 0, sizeof(*m2info)); m2info->primary_dev = MINFO; - if (ACCESS_FBINFO(devflags.g550dac)) { - m2info->timmings = 1; - } else { - m2info->timmings = 0; - } if (matroxfb_g450_connect(m2info)) { kfree(m2info); printk(KERN_ERR "matroxfb_g450: G450 DAC failed to initialize\n"); @@ -220,9 +126,9 @@ } static struct matroxfb_driver g450 = { - name: "Matrox G450 output #2", - probe: matroxfb_g450_probe, - remove: matroxfb_g450_remove }; + name: "Matrox G450 output #2", + probe: matroxfb_g450_probe, + remove: matroxfb_g450_remove }; static int matroxfb_g450_init(void) { matroxfb_register_driver(&g450); @@ -236,5 +142,6 @@ MODULE_AUTHOR("(c) 2000-2001 Petr Vandrovec "); MODULE_DESCRIPTION("Matrox G450 secondary output driver"); MODULE_LICENSE("GPL"); + module_init(matroxfb_g450_init); module_exit(matroxfb_g450_exit); diff -Nru a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c --- a/drivers/video/matrox/matroxfb_maven.c Wed Feb 13 20:03:29 2002 +++ b/drivers/video/matrox/matroxfb_maven.c Wed Feb 13 20:03:29 2002 @@ -4,7 +4,9 @@ * * (c) 1998-2001 Petr Vandrovec * - * Version: 1.51 2001/01/19 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * See matroxfb_base.c for contributors. * @@ -25,10 +27,14 @@ #define MODE_TV(x) (((x) == MODE_PAL) || ((x) == MODE_NTSC)) #define MODE_MONITOR MATROXFB_OUTPUT_MODE_MONITOR +#define MGATVO_B 1 +#define MGATVO_C 2 + struct maven_data { struct matrox_fb_info* primary_head; struct i2c_client* client; int mode; + int version; }; static int maven_get_reg(struct i2c_client* c, char reg) { @@ -623,8 +629,13 @@ m->regs[0xA2] = mt->VTotal - mt->VSyncStart - 1; /* stop vblanking */ m->regs[0xA3] = (mt->VTotal - mt->VSyncStart - 1) >> 8; /* something end... [A6]+1..[A8] */ - m->regs[0xA4] = 0x01; - m->regs[0xA5] = 0x00; + if (md->version == MGATVO_B) { + m->regs[0xA4] = 0x04; + m->regs[0xA5] = 0x00; + } else { + m->regs[0xA4] = 0x01; + m->regs[0xA5] = 0x00; + } /* something start... 0..[A4]-1 */ m->regs[0xA6] = 0x00; m->regs[0xA7] = 0x00; @@ -862,12 +873,20 @@ /******************************************************/ -static int maven_out_compute(void* md, struct my_timming* mt, struct matrox_hw_state* mr) { - return maven_compute_timming(md, mt, &mr->maven); +static int maven_out_compute(void* md, struct my_timming* mt) { +#define mdinfo ((struct maven_data*)md) +#define minfo (mdinfo->primary_head) + return maven_compute_timming(md, mt, &ACCESS_FBINFO(hw).maven); +#undef minfo +#undef mdinfo } -static int maven_out_program(void* md, const struct matrox_hw_state* mr) { - return maven_program_timming(md, &mr->maven); +static int maven_out_program(void* md) { +#define mdinfo ((struct maven_data*)md) +#define minfo (mdinfo->primary_head) + return maven_program_timming(md, &ACCESS_FBINFO(hw).maven); +#undef minfo +#undef mdinfo } static int maven_out_start(void* md) { @@ -916,6 +935,11 @@ ACCESS_FBINFO(altout.output) = &maven_altout; up_write(&ACCESS_FBINFO(altout.lock)); ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY; + if (maven_get_reg(clnt, 0xB2) < 0x14) { + md->version = MGATVO_B; + } else { + md->version = MGATVO_C; + } return 0; } diff -Nru a/drivers/video/matrox/matroxfb_misc.c b/drivers/video/matrox/matroxfb_misc.c --- a/drivers/video/matrox/matroxfb_misc.c Wed Feb 13 20:03:35 2002 +++ b/drivers/video/matrox/matroxfb_misc.c Wed Feb 13 20:03:35 2002 @@ -2,9 +2,11 @@ * * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 * - * (c) 1998,1999,2000 Petr Vandrovec + * (c) 1998,1999,2000,2001 Petr Vandrovec * - * Version: 1.54 2001/09/09 + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.62 2001/11/29 * * MTRR stuff: 1998 Tom Rini * @@ -86,7 +88,7 @@ #include #include -void matroxfb_createcursorshape(WPMINFO struct display* p, int vmode) { +void matroxfb_createcursorshape(WPMINFO_ struct display* p, int vmode) { unsigned int h; unsigned int cu, cd; @@ -124,13 +126,13 @@ ACCESS_FBINFO(cursor.d) = cd; } -void matroxfb_DAC_out(CPMINFO int reg, int val) { +void matroxfb_DAC_out(CPMINFO_ int reg, int val) { DBG_REG("outDAC"); mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); mga_outb(M_RAMDAC_BASE+M_X_DATAREG, val); } -int matroxfb_DAC_in(CPMINFO int reg) { +int matroxfb_DAC_in(CPMINFO_ int reg) { DBG_REG("inDAC"); mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); return mga_inb(M_RAMDAC_BASE+M_X_DATAREG); @@ -218,7 +220,7 @@ return bestvco; } -int matroxfb_vgaHWinit(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p) { +int matroxfb_vgaHWinit(WPMINFO_ struct my_timming* m, struct display* p) { unsigned int hd, hs, he, hbe, ht; unsigned int vd, vs, ve, vt; unsigned int wd; @@ -235,17 +237,17 @@ DBG("vgaHWinit") - hw->SEQ[0] = 0x00; + ACCESS_FBINFO(hw).SEQ[0] = 0x00; if (fwidth == 9) - hw->SEQ[1] = 0x00; + ACCESS_FBINFO(hw).SEQ[1] = 0x00; else - hw->SEQ[1] = 0x01; /* or 0x09 */ - hw->SEQ[2] = 0x0F; /* bitplanes */ - hw->SEQ[3] = 0x00; + ACCESS_FBINFO(hw).SEQ[1] = 0x01; /* or 0x09 */ + ACCESS_FBINFO(hw).SEQ[2] = 0x0F; /* bitplanes */ + ACCESS_FBINFO(hw).SEQ[3] = 0x00; if (text) - hw->SEQ[4] = 0x02; + ACCESS_FBINFO(hw).SEQ[4] = 0x02; else - hw->SEQ[4] = 0x0E; + ACCESS_FBINFO(hw).SEQ[4] = 0x0E; /* CRTC 0..7, 9, 16..19, 21, 22 are reprogrammed by Matrox Millennium code... Hope that by MGA1064 too */ if (m->dblscan) { m->VTotal <<= 1; @@ -261,36 +263,36 @@ } /* GCTL is ignored when not using 0xA0000 aperture */ - hw->GCTL[0] = 0x00; - hw->GCTL[1] = 0x00; - hw->GCTL[2] = 0x00; - hw->GCTL[3] = 0x00; - hw->GCTL[4] = 0x00; + ACCESS_FBINFO(hw).GCTL[0] = 0x00; + ACCESS_FBINFO(hw).GCTL[1] = 0x00; + ACCESS_FBINFO(hw).GCTL[2] = 0x00; + ACCESS_FBINFO(hw).GCTL[3] = 0x00; + ACCESS_FBINFO(hw).GCTL[4] = 0x00; if (text) { - hw->GCTL[5] = 0x10; - hw->GCTL[6] = 0x02; + ACCESS_FBINFO(hw).GCTL[5] = 0x10; + ACCESS_FBINFO(hw).GCTL[6] = 0x02; } else { - hw->GCTL[5] = 0x40; - hw->GCTL[6] = 0x05; + ACCESS_FBINFO(hw).GCTL[5] = 0x40; + ACCESS_FBINFO(hw).GCTL[6] = 0x05; } - hw->GCTL[7] = 0x0F; - hw->GCTL[8] = 0xFF; + ACCESS_FBINFO(hw).GCTL[7] = 0x0F; + ACCESS_FBINFO(hw).GCTL[8] = 0xFF; /* Whole ATTR is ignored in PowerGraphics mode */ for (i = 0; i < 16; i++) - hw->ATTR[i] = i; + ACCESS_FBINFO(hw).ATTR[i] = i; if (text) { - hw->ATTR[16] = 0x04; + ACCESS_FBINFO(hw).ATTR[16] = 0x04; } else { - hw->ATTR[16] = 0x41; + ACCESS_FBINFO(hw).ATTR[16] = 0x41; } - hw->ATTR[17] = 0xFF; - hw->ATTR[18] = 0x0F; + ACCESS_FBINFO(hw).ATTR[17] = 0xFF; + ACCESS_FBINFO(hw).ATTR[18] = 0x0F; if (fwidth == 9) - hw->ATTR[19] = 0x08; + ACCESS_FBINFO(hw).ATTR[19] = 0x08; else - hw->ATTR[19] = 0x00; - hw->ATTR[20] = 0x00; + ACCESS_FBINFO(hw).ATTR[19] = 0x00; + ACCESS_FBINFO(hw).ATTR[20] = 0x00; if (text) { hd = m->HDisplay / fwidth; @@ -343,42 +345,42 @@ wd = p->var.xres_virtual * ACCESS_FBINFO(curr.final_bppShift) / 64; } - hw->CRTCEXT[0] = 0; - hw->CRTCEXT[5] = 0; + ACCESS_FBINFO(hw).CRTCEXT[0] = 0; + ACCESS_FBINFO(hw).CRTCEXT[5] = 0; if (m->interlaced) { - hw->CRTCEXT[0] = 0x80; - hw->CRTCEXT[5] = (hs + he - ht) >> 1; + ACCESS_FBINFO(hw).CRTCEXT[0] = 0x80; + ACCESS_FBINFO(hw).CRTCEXT[5] = (hs + he - ht) >> 1; if (!m->dblscan) wd <<= 1; vt &= ~1; } - hw->CRTCEXT[0] |= (wd & 0x300) >> 4; - hw->CRTCEXT[1] = (((ht - 4) & 0x100) >> 8) | + ACCESS_FBINFO(hw).CRTCEXT[0] |= (wd & 0x300) >> 4; + ACCESS_FBINFO(hw).CRTCEXT[1] = (((ht - 4) & 0x100) >> 8) | ((hd & 0x100) >> 7) | /* blanking */ ((hs & 0x100) >> 6) | /* sync start */ (hbe & 0x040); /* end hor. blanking */ if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) - hw->CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */ - hw->CRTCEXT[2] = ((vt & 0xC00) >> 10) | + ACCESS_FBINFO(hw).CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */ + ACCESS_FBINFO(hw).CRTCEXT[2] = ((vt & 0xC00) >> 10) | ((vd & 0x400) >> 8) | /* disp end */ ((vd & 0xC00) >> 7) | /* vblanking start */ ((vs & 0xC00) >> 5); if (text) - hw->CRTCEXT[3] = 0x00; + ACCESS_FBINFO(hw).CRTCEXT[3] = 0x00; else - hw->CRTCEXT[3] = (divider - 1) | 0x80; - hw->CRTCEXT[4] = 0; + ACCESS_FBINFO(hw).CRTCEXT[3] = (divider - 1) | 0x80; + ACCESS_FBINFO(hw).CRTCEXT[4] = 0; - hw->CRTC[0] = ht-4; - hw->CRTC[1] = hd; - hw->CRTC[2] = hd; - hw->CRTC[3] = (hbe & 0x1F) | 0x80; - hw->CRTC[4] = hs; - hw->CRTC[5] = ((hbe & 0x20) << 2) | (he & 0x1F); + ACCESS_FBINFO(hw).CRTC[0] = ht-4; + ACCESS_FBINFO(hw).CRTC[1] = hd; + ACCESS_FBINFO(hw).CRTC[2] = hd; + ACCESS_FBINFO(hw).CRTC[3] = (hbe & 0x1F) | 0x80; + ACCESS_FBINFO(hw).CRTC[4] = hs; + ACCESS_FBINFO(hw).CRTC[5] = ((hbe & 0x20) << 2) | (he & 0x1F); if (text) - hw->CRTC[5] |= 0x60; /* delay sync for 3 clocks (to same picture position on MGA and VGA) */ - hw->CRTC[6] = vt & 0xFF; - hw->CRTC[7] = ((vt & 0x100) >> 8) | + ACCESS_FBINFO(hw).CRTC[5] |= 0x60; /* delay sync for 3 clocks (to same picture position on MGA and VGA) */ + ACCESS_FBINFO(hw).CRTC[6] = vt & 0xFF; + ACCESS_FBINFO(hw).CRTC[7] = ((vt & 0x100) >> 8) | ((vd & 0x100) >> 7) | ((vs & 0x100) >> 6) | ((vd & 0x100) >> 5) | @@ -386,88 +388,88 @@ ((vt & 0x200) >> 4) | ((vd & 0x200) >> 3) | ((vs & 0x200) >> 2); - hw->CRTC[8] = 0x00; - hw->CRTC[9] = ((vd & 0x200) >> 4) | 0x40; + ACCESS_FBINFO(hw).CRTC[8] = 0x00; + ACCESS_FBINFO(hw).CRTC[9] = ((vd & 0x200) >> 4) | 0x40; if (text) - hw->CRTC[9] |= fontheight(p) - 1; + ACCESS_FBINFO(hw).CRTC[9] |= fontheight(p) - 1; if (m->dblscan && !m->interlaced) - hw->CRTC[9] |= 0x80; + ACCESS_FBINFO(hw).CRTC[9] |= 0x80; for (i = 10; i < 16; i++) - hw->CRTC[i] = 0x00; - hw->CRTC[16] = vs /* & 0xFF */; - hw->CRTC[17] = (ve & 0x0F) | 0x20; - hw->CRTC[18] = vd /* & 0xFF */; - hw->CRTC[19] = wd /* & 0xFF */; - hw->CRTC[20] = 0x00; - hw->CRTC[21] = vd /* & 0xFF */; - hw->CRTC[22] = (vt + 1) /* & 0xFF */; + ACCESS_FBINFO(hw).CRTC[i] = 0x00; + ACCESS_FBINFO(hw).CRTC[16] = vs /* & 0xFF */; + ACCESS_FBINFO(hw).CRTC[17] = (ve & 0x0F) | 0x20; + ACCESS_FBINFO(hw).CRTC[18] = vd /* & 0xFF */; + ACCESS_FBINFO(hw).CRTC[19] = wd /* & 0xFF */; + ACCESS_FBINFO(hw).CRTC[20] = 0x00; + ACCESS_FBINFO(hw).CRTC[21] = vd /* & 0xFF */; + ACCESS_FBINFO(hw).CRTC[22] = (vt + 1) /* & 0xFF */; if (text) { if (ACCESS_FBINFO(devflags.textmode) == 1) - hw->CRTC[23] = 0xC3; + ACCESS_FBINFO(hw).CRTC[23] = 0xC3; else - hw->CRTC[23] = 0xA3; + ACCESS_FBINFO(hw).CRTC[23] = 0xA3; if (ACCESS_FBINFO(devflags.textmode) == 4) - hw->CRTC[20] = 0x5F; + ACCESS_FBINFO(hw).CRTC[20] = 0x5F; else - hw->CRTC[20] = 0x1F; + ACCESS_FBINFO(hw).CRTC[20] = 0x1F; } else - hw->CRTC[23] = 0xC3; - hw->CRTC[24] = 0xFF; + ACCESS_FBINFO(hw).CRTC[23] = 0xC3; + ACCESS_FBINFO(hw).CRTC[24] = 0xFF; return 0; }; -void matroxfb_vgaHWrestore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw) { +void matroxfb_vgaHWrestore(WPMINFO) { int i; CRITFLAGS DBG("vgaHWrestore") - dprintk(KERN_INFO "MiscOutReg: %02X\n", hw->MiscOutReg); + dprintk(KERN_INFO "MiscOutReg: %02X\n", ACCESS_FBINFO(hw).MiscOutReg); dprintk(KERN_INFO "SEQ regs: "); for (i = 0; i < 5; i++) - dprintk("%02X:", hw->SEQ[i]); + dprintk("%02X:", ACCESS_FBINFO(hw).SEQ[i]); dprintk("\n"); dprintk(KERN_INFO "GDC regs: "); for (i = 0; i < 9; i++) - dprintk("%02X:", hw->GCTL[i]); + dprintk("%02X:", ACCESS_FBINFO(hw).GCTL[i]); dprintk("\n"); dprintk(KERN_INFO "CRTC regs: "); for (i = 0; i < 25; i++) - dprintk("%02X:", hw->CRTC[i]); + dprintk("%02X:", ACCESS_FBINFO(hw).CRTC[i]); dprintk("\n"); dprintk(KERN_INFO "ATTR regs: "); for (i = 0; i < 21; i++) - dprintk("%02X:", hw->ATTR[i]); + dprintk("%02X:", ACCESS_FBINFO(hw).ATTR[i]); dprintk("\n"); CRITBEGIN mga_inb(M_ATTR_RESET); mga_outb(M_ATTR_INDEX, 0); - mga_outb(M_MISC_REG, hw->MiscOutReg); + mga_outb(M_MISC_REG, ACCESS_FBINFO(hw).MiscOutReg); for (i = 1; i < 5; i++) - mga_setr(M_SEQ_INDEX, i, hw->SEQ[i]); - mga_setr(M_CRTC_INDEX, 17, hw->CRTC[17] & 0x7F); + mga_setr(M_SEQ_INDEX, i, ACCESS_FBINFO(hw).SEQ[i]); + mga_setr(M_CRTC_INDEX, 17, ACCESS_FBINFO(hw).CRTC[17] & 0x7F); for (i = 0; i < 25; i++) - mga_setr(M_CRTC_INDEX, i, hw->CRTC[i]); + mga_setr(M_CRTC_INDEX, i, ACCESS_FBINFO(hw).CRTC[i]); for (i = 0; i < 9; i++) - mga_setr(M_GRAPHICS_INDEX, i, hw->GCTL[i]); + mga_setr(M_GRAPHICS_INDEX, i, ACCESS_FBINFO(hw).GCTL[i]); for (i = 0; i < 21; i++) { mga_inb(M_ATTR_RESET); mga_outb(M_ATTR_INDEX, i); - mga_outb(M_ATTR_INDEX, hw->ATTR[i]); + mga_outb(M_ATTR_INDEX, ACCESS_FBINFO(hw).ATTR[i]); } mga_outb(M_PALETTE_MASK, 0xFF); mga_outb(M_DAC_REG, 0x00); for (i = 0; i < 768; i++) - mga_outb(M_DAC_VAL, hw->DACpal[i]); + mga_outb(M_DAC_VAL, ACCESS_FBINFO(hw).DACpal[i]); mga_inb(M_ATTR_RESET); mga_outb(M_ATTR_INDEX, 0x20); CRITEND } -void matroxfb_fastfont_init(struct matrox_fb_info* minfo){ +void matroxfb_fastfont_init(WPMINFO) { unsigned int size; size = ACCESS_FBINFO(fastfont.size); @@ -494,7 +496,7 @@ #define FNTCHARCNT(fd) (((int *)(fd))[-3]) #endif -int matrox_text_loadfont(WPMINFO struct display* p) { +int matrox_text_loadfont(WPMINFO_ struct display* p) { unsigned int fsize; unsigned int width; vaddr_t dst; @@ -531,7 +533,7 @@ return 1; } -int matroxfb_fastfont_tryset(WPMINFO struct display* p) { +int matroxfb_fastfont_tryset(WPMINFO_ struct display* p) { unsigned int fsize; unsigned int width; CRITFLAGS @@ -643,6 +645,396 @@ return 1; } +static void get_pins(unsigned char* pins, struct matrox_bios* bd) { + unsigned int b0 = readb(pins); + + if (b0 == 0x2E && readb(pins+1) == 0x41) { + unsigned int pins_len = readb(pins+2); + unsigned int i; + unsigned char cksum; + unsigned char* dst = bd->pins; + + if (pins_len < 3 || pins_len > 128) { + return; + } + *dst++ = 0x2E; + *dst++ = 0x41; + *dst++ = pins_len; + cksum = 0x2E + 0x41 + pins_len; + for (i = 3; i < pins_len; i++) { + cksum += *dst++ = readb(pins+i); + } + if (cksum) { + return; + } + bd->pins_len = pins_len; + } else if (b0 == 0x40 && readb(pins+1) == 0x00) { + unsigned int i; + unsigned char* dst = bd->pins; + + *dst++ = 0x40; + *dst++ = 0; + for (i = 2; i < 0x40; i++) { + *dst++ = readb(pins+i); + } + bd->pins_len = 0x40; + } +} + +static void get_bios_version(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int pcir_offset; + + pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8); + if (pcir_offset >= 26 && pcir_offset < 0xFFE0 && + readb(vbios + pcir_offset ) == 'P' && + readb(vbios + pcir_offset + 1) == 'C' && + readb(vbios + pcir_offset + 2) == 'I' && + readb(vbios + pcir_offset + 3) == 'R') { + unsigned char h; + + h = readb(vbios + pcir_offset + 0x12); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = readb(vbios + pcir_offset + 0x13); + } else { + unsigned char h; + + h = readb(vbios + 5); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = 0; + } +} + +static void get_bios_output(unsigned char* vbios, struct matrox_bios* bd) { + unsigned char b; + + b = readb(vbios + 0x7FF1); + if (b == 0xFF) { + b = 0; + } + bd->output.state = b; +} + +static void get_bios_tvout(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int i; + + /* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */ + bd->output.tvout = 0; + if (readb(vbios + 0x1D) != 'I' || + readb(vbios + 0x1E) != 'B' || + readb(vbios + 0x1F) != 'M' || + readb(vbios + 0x20) != ' ') { + return; + } + for (i = 0x2D; i < 0x2D + 128; i++) { + unsigned char b = readb(vbios + i); + + if (b == '(' && readb(vbios + i + 1) == 'V') { + if (readb(vbios + i + 6) == 'T' && + readb(vbios + i + 7) == 'V' && + readb(vbios + i + 8) == 'O') { + bd->output.tvout = 1; + } + return; + } + if (b == 0) + break; + } +} + +static void parse_bios(unsigned char* vbios, struct matrox_bios* bd) { + unsigned int pins_offset; + + if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) { + return; + } + bd->bios_valid = 1; + get_bios_version(vbios, bd); + get_bios_output(vbios, bd); + get_bios_tvout(vbios, bd); + pins_offset = readb(vbios + 0x7FFC) | (readb(vbios + 0x7FFD) << 8); + if (pins_offset <= 0xFF80) { + get_pins(vbios + pins_offset, bd); + } +} + +#define get_u16(x) (le16_to_cpu(get_unaligned((__u16*)(x)))) +#define get_u32(x) (le32_to_cpu(get_unaligned((__u32*)(x)))) +static int parse_pins1(WPMINFO_ const struct matrox_bios* bd) { + unsigned int maxdac; + + switch (bd->pins[22]) { + case 0: maxdac = 175000; break; + case 1: maxdac = 220000; break; + default: maxdac = 240000; break; + } + if (get_u16(bd->pins + 24)) { + maxdac = get_u16(bd->pins + 24) * 10; + } + MINFO->limits.pixel.vcomax = maxdac; + MINFO->values.pll.system = get_u16(bd->pins + 28) ? get_u16(bd->pins + 28) * 10 : 50000; + /* ignore 4MB, 8MB, module clocks */ + MINFO->features.pll.ref_freq = 14318; + MINFO->values.reg.mctlwtst = 0x00030101; + return 0; +} + +static void default_pins1(WPMINFO) { + /* Millennium */ +// MINFO->limits.out.dac1 = + MINFO->limits.pixel.vcomax = 220000; + MINFO->values.pll.system = 50000; + MINFO->features.pll.ref_freq = 14318; + MINFO->values.reg.mctlwtst = 0x00030101; +} + +static int parse_pins2(WPMINFO_ const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = +// MINFO->limits.out.dac1 = + MINFO->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); +// MINFO->limits.c1.b8 = +// MINFO->limits.c1.b16 = +// MINFO->limits.c1.b24 = +// MINFO->limits.c1.b32 = (bd->pins[42] == 0xFF) ? MINFO->limits.out.dac1 : ((bd->pins[42] + 100) * 1000); + MINFO->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | + ((bd->pins[51] & 0x02) ? 0x00000100 : 0) | + ((bd->pins[51] & 0x04) ? 0x00010000 : 0) | + ((bd->pins[51] & 0x08) ? 0x00020000 : 0); + MINFO->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); + MINFO->features.pll.ref_freq = 14318; + return 0; +} + +static void default_pins2(WPMINFO) { + /* Millennium II, Mystique */ + MINFO->limits.pixel.vcomax = +// MINFO->limits.out.dac1 = + MINFO->limits.system.vcomax = +// MINFO->limits.c1.b8 = +// MINFO->limits.c1.b16 = +// MINFO->limits.c1.b24 = +// MINFO->limits.c1.b32 = + 230000; + MINFO->values.reg.mctlwtst = 0x00030101; + MINFO->values.pll.system = 50000; + MINFO->features.pll.ref_freq = 14318; +} + +static int parse_pins3(WPMINFO_ const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = +// MINFO->limits.out.dac1 = + MINFO->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); +// MINFO->limits.c1.b8 = (bd->pins[37] == 0xFF) ? MINFO->limits.pixel.vcomax : ((bd->pins[37] + 100) * 1000); +// MINFO->limits.c1.b16 = (bd->pins[38] == 0xFF) ? MINFO->limits.pixel.vcomax : ((bd->pins[38] + 100) * 1000); +// MINFO->limits.c1.b24 = (bd->pins[39] == 0xFF) ? MINFO->limits.pixel.vcomax : ((bd->pins[39] + 100) * 1000); +// MINFO->limits.c1.b32 = (bd->pins[40] == 0xFF) ? MINFO->limits.pixel.vcomax : ((bd->pins[40] + 100) * 1000); + MINFO->values.reg.mctlwtst = get_u32(bd->pins + 48) == 0xFFFFFFFF ? 0x01250A21 : get_u32(bd->pins + 48); +// MINFO->values.reg.crtcext6 = (bd->pins[55] == 0xFF) ? 0x00020002 : +// (((bd->pins[55] << 13) & 0x00070000) | (bd->pins[55] & 7)); + /* memory config */ + MINFO->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | + ((bd->pins[57] << 22) & 0x00C00000) | + ((bd->pins[56] << 1) & 0x000001E0) | + ( bd->pins[56] & 0x0000000F); + MINFO->values.reg.opt = (bd->pins[54] & 7) << 10; + MINFO->values.reg.opt2 = bd->pins[58] << 12; + MINFO->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; + return 0; +} + +static void default_pins3(WPMINFO) { + /* G100, G200 */ + MINFO->limits.pixel.vcomax = +// MINFO->limits.out.dac1 = + MINFO->limits.system.vcomax = +// MINFO->limits.c1.b8 = +// MINFO->limits.c1.b16 = +// MINFO->limits.c1.b24 = +// MINFO->limits.c1.b32 = + 230000; + MINFO->values.reg.mctlwtst = 0x01250A21; +// MINFO->values.reg.crtcext6 = 0x00020002; + MINFO->values.reg.memrdbk = 0x00000000; + MINFO->values.reg.opt = 0x00000C00; + MINFO->values.reg.opt2 = 0x00000000; + MINFO->features.pll.ref_freq = 27000; +} + +static int parse_pins4(WPMINFO_ const struct matrox_bios* bd) { + MINFO->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; + MINFO->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 38] * 4000; + MINFO->values.reg.mctlwtst = get_u32(bd->pins + 71); + MINFO->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | + ((bd->pins[87] << 22) & 0x00C00000) | + ((bd->pins[86] << 1) & 0x000001E0) | + ( bd->pins[86] & 0x0000000F); + MINFO->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | + ((bd->pins[53] << 22) & 0x10000000) | + ((bd->pins[53] << 10) & 0x00001C00); + MINFO->values.reg.opt3 = get_u32(bd->pins + 67); + MINFO->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; + MINFO->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; + return 0; +} + +static void default_pins4(WPMINFO) { + /* G400 */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = 252000; + MINFO->values.reg.mctlwtst = 0x04A450A1; + MINFO->values.reg.memrdbk = 0x000000E7; + MINFO->values.reg.opt = 0x10000400; + MINFO->values.reg.opt3 = 0x0190A419; + MINFO->values.pll.system = 200000; + MINFO->features.pll.ref_freq = 27000; +} + +static int parse_pins5(WPMINFO_ const struct matrox_bios* bd) { + unsigned int mult; + + mult = bd->pins[4]?8000:6000; + + MINFO->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; + MINFO->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 39] * mult; + MINFO->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax : bd->pins[ 37] * mult; + MINFO->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; + MINFO->limits.system.vcomin = (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin : bd->pins[121] * mult; + MINFO->limits.video.vcomin = (bd->pins[122] == 0xFF) ? MINFO->limits.system.vcomin : bd->pins[122] * mult; +// MINFO->limits.c1.b8 = (bd->pins[ 39] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 39] * 4000; +// MINFO->limits.c1.b16 = (bd->pins[ 40] == 0xFF) ? MINFO->limits.c1.b8 : bd->pins[ 40] * 4000; +// MINFO->limits.c1.b24 = (bd->pins[ 41] == 0xFF) ? MINFO->limits.c1.b16 : bd->pins[ 41] * 4000; +// MINFO->limits.c1.b32 = (bd->pins[124] == 0xFF) ? MINFO->limits.c1.b24 : bd->pins[124] * 4000; +// MINFO->limits.c2.b16 = (bd->pins[ 43] == 0xFF) ? MINFO->limits.video.vcomax : bd->pins[ 43] * 4000; +// MINFO->limits.c2.b32 = (bd->pins[125] == 0xFF) ? MINFO->limits.c2.b16 : bd->pins[125] * 4000; +// MINFO->limits.out.dac1 = (bd->pins[118] == 0xFF) ? MINFO->limits.c1.b8 : bd->pins[118] * 4000; +// MINFO->limits.out.dac2 = (bd->pins[119] == 0xFF) ? MINFO->limits.out.dac1 : bd->pins[119] * 4000; + MINFO->values.pll.system = + MINFO->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; + MINFO->values.reg.opt = get_u32(bd->pins+ 48); + MINFO->values.reg.opt2 = get_u32(bd->pins+ 52); + MINFO->values.reg.opt3 = get_u32(bd->pins+ 94); + MINFO->values.reg.mctlwtst = get_u32(bd->pins+ 98); + MINFO->values.reg.memmisc = get_u32(bd->pins+102); + MINFO->values.reg.memrdbk = get_u32(bd->pins+106); + MINFO->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; + MINFO->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; + MINFO->values.memory.dll = (bd->pins[115] & 0x02) != 0; + MINFO->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; + MINFO->values.reg.maccess = MINFO->values.memory.emrswen ? 0x00004000 : 0x00000000; + if (bd->pins[115] & 4) { + MINFO->values.reg.mctlwtst_core = MINFO->values.reg.mctlwtst; + } else { + u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; + MINFO->values.reg.mctlwtst_core = (MINFO->values.reg.mctlwtst & ~7) | + wtst_xlat[MINFO->values.reg.mctlwtst & 7]; + } + return 0; +} + +static void default_pins5(WPMINFO) { + /* Mine 16MB G450 with SDRAM DDR */ + MINFO->limits.pixel.vcomax = + MINFO->limits.system.vcomax = + MINFO->limits.video.vcomax = 600000; + MINFO->limits.pixel.vcomin = + MINFO->limits.system.vcomin = + MINFO->limits.video.vcomin = 256000; +// MINFO->limits.c1.b8 = +// MINFO->limits.c1.b16 = +// MINFO->limits.c1.b24 = 360000; +// MINFO->limits.c1.b32 = 232000; +// MINFO->limits.c2.b16 = 232000; +// MINFO->limits.c2.b32 = 204000; +// MINFO->limits.out.dac1 = +// MINFO->limits.out.dac2 = 360000; + MINFO->values.pll.system = + MINFO->values.pll.video = 284000; + MINFO->values.reg.opt = 0x404A1160; + MINFO->values.reg.opt2 = 0x0000AC00; + MINFO->values.reg.opt3 = 0x0090A409; + MINFO->values.reg.mctlwtst_core = + MINFO->values.reg.mctlwtst = 0x0C81462B; + MINFO->values.reg.memmisc = 0x80000004; + MINFO->values.reg.memrdbk = 0x01001103; + MINFO->features.pll.ref_freq = 27000; + MINFO->values.memory.ddr = 1; + MINFO->values.memory.dll = 1; + MINFO->values.memory.emrswen = 1; + MINFO->values.reg.maccess = 0x00004000; +} + +static int matroxfb_set_limits(WPMINFO_ const struct matrox_bios* bd) { + unsigned int pins_version; + static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 }; + + switch (ACCESS_FBINFO(chip)) { + case MGA_2064: default_pins1(PMINFO); break; + case MGA_2164: + case MGA_1064: + case MGA_1164: default_pins2(PMINFO); break; + case MGA_G100: + case MGA_G200: default_pins3(PMINFO); break; + case MGA_G400: default_pins4(PMINFO); break; + case MGA_G450: + case MGA_G550: default_pins5(PMINFO); break; + } + if (!bd->bios_valid) { + printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n"); + return -1; + } + if (bd->pins_len < 64) { + printk(KERN_INFO "matroxfb: BIOS on your Matrox device does not contain powerup info\n"); + return -1; + } + if (bd->pins[0] == 0x2E && bd->pins[1] == 0x41) { + pins_version = bd->pins[5]; + if (pins_version < 2 || pins_version > 5) { + printk(KERN_INFO "matroxfb: Unknown version (%u) of powerup info\n", pins_version); + return -1; + } + } else { + pins_version = 1; + } + if (bd->pins_len != pinslen[pins_version - 1]) { + printk(KERN_INFO "matroxfb: Invalid powerup info\n"); + return -1; + } + switch (pins_version) { + case 1: + return parse_pins1(PMINFO_ bd); + case 2: + return parse_pins2(PMINFO_ bd); + case 3: + return parse_pins3(PMINFO_ bd); + case 4: + return parse_pins4(PMINFO_ bd); + case 5: + return parse_pins5(PMINFO_ bd); + default: + printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version); + return -1; + } +} + +void matroxfb_read_pins(WPMINFO) { + u32 opt; + u32 biosbase; + u32 fbbase; + struct pci_dev* pdev = ACCESS_FBINFO(pcidev); + + memset(&ACCESS_FBINFO(bios), 0, sizeof(ACCESS_FBINFO(bios))); + pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase); + pci_read_config_dword(pdev, ACCESS_FBINFO(devflags.fbResource), &fbbase); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE); + parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios)); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt); + matroxfb_set_limits(PMINFO_ &ACCESS_FBINFO(bios)); +} + EXPORT_SYMBOL(matroxfb_DAC_in); EXPORT_SYMBOL(matroxfb_DAC_out); EXPORT_SYMBOL(matroxfb_var2my); @@ -651,12 +1043,13 @@ struct matrox_fb_info matroxfb_global_mxinfo; EXPORT_SYMBOL(matroxfb_global_mxinfo); #endif -EXPORT_SYMBOL(matrox_text_loadfont); /* for matroxfb_accel */ +EXPORT_SYMBOL(matrox_text_loadfont); /* for matroxfb_accel */ EXPORT_SYMBOL(matroxfb_createcursorshape); /* accel, DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_fastfont_tryset); /* accel */ EXPORT_SYMBOL(matroxfb_fastfont_init); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */ +EXPORT_SYMBOL(matroxfb_read_pins); MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec "); MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards"); diff -Nru a/drivers/video/matrox/matroxfb_misc.h b/drivers/video/matrox/matroxfb_misc.h --- a/drivers/video/matrox/matroxfb_misc.h Wed Feb 13 20:03:33 2002 +++ b/drivers/video/matrox/matroxfb_misc.h Wed Feb 13 20:03:33 2002 @@ -6,16 +6,17 @@ /* also for modules */ int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post); -static inline int PLL_calcclock(CPMINFO unsigned int freq, unsigned int fmax, +static inline int PLL_calcclock(CPMINFO_ unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post) { return matroxfb_PLL_calcclock(&ACCESS_FBINFO(features.pll), freq, fmax, in, feed, post); } -void matroxfb_createcursorshape(WPMINFO struct display* p, int vmode); -int matroxfb_vgaHWinit(CPMINFO struct matrox_hw_state* hw, struct my_timming* m, struct display* p); -void matroxfb_vgaHWrestore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_state* oldhw); -void matroxfb_fastfont_init(struct matrox_fb_info* minfo); -int matrox_text_loadfont(WPMINFO struct display* p); -int matroxfb_fastfont_tryset(WPMINFO struct display* p); +void matroxfb_createcursorshape(WPMINFO_ struct display* p, int vmode); +int matroxfb_vgaHWinit(WPMINFO_ struct my_timming* m, struct display* p); +void matroxfb_vgaHWrestore(WPMINFO); +void matroxfb_fastfont_init(WPMINFO); +int matrox_text_loadfont(WPMINFO_ struct display* p); +int matroxfb_fastfont_tryset(WPMINFO_ struct display* p); +void matroxfb_read_pins(WPMINFO); #endif /* __MATROXFB_MISC_H__ */ diff -Nru a/drivers/video/matrox/matroxfb_proc.c b/drivers/video/matrox/matroxfb_proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/matrox/matroxfb_proc.c Wed Feb 13 20:04:01 2002 @@ -0,0 +1,151 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. + * + * (c) 1998-2001 Petr Vandrovec + * + * Version: 1.62 2001/11/29 + * + */ + +#include "matroxfb_base.h" +#include + +static struct proc_dir_entry* mga_pde; + +struct procinfo { + struct matrox_fb_info* info; + struct proc_dir_entry* pde; +}; + +static inline void remove_pde(struct proc_dir_entry* pde) { + if (pde) { + remove_proc_entry(pde->name, pde->parent); + } +} + +#ifndef CONFIG_PROC_FS +static int bios_read_proc(char* buffer, char** start, off_t offset, + int size, int *eof, void *data) { + return 0; +} + +static int pins_read_proc(char* buffer, char** start, off_t offset, + int size, int *eof, void *data) { + return 0; +} +#else +/* This macro frees the machine specific function from bounds checking and + * this like that... */ +#define PRINT_PROC(fmt,args...) \ + do { \ + len += sprintf(buffer+len, fmt, ##args ); \ + if (begin + len > offset + size) \ + break; \ + if (begin + len < offset) { \ + begin += len; \ + len = 0; \ + } \ + } while(0) + +static int bios_read_proc(char* buffer, char** start, off_t offset, + int size, int *eof, void *data) { + int len = 0; + off_t begin = 0; + struct matrox_bios* bd = data; + + do { + *eof = 0; + if (bd->bios_valid) { + PRINT_PROC("BIOS: %u.%u.%u\n", bd->version.vMaj, bd->version.vMin, bd->version.vRev); + PRINT_PROC("Output: 0x%02X\n", bd->output.state); + PRINT_PROC("TVOut: %s\n", bd->output.tvout?"yes":"no"); + PRINT_PROC("PINS: %s\n", bd->pins_len ? "found" : "not found"); + PRINT_PROC("Info: %p\n", bd); + } else { + PRINT_PROC("BIOS: Invalid\n"); + } + *eof = 1; + } while (0); + if (offset >= begin + len) + return 0; + *start = buffer + (offset - begin); + return size < begin + len - offset ? size : begin + len - offset; +} + +static int pins_read_proc(char* buffer, char** start, off_t offset, + int size, int *eof, void *data) { + struct matrox_bios* bd = data; + + if (offset >= bd->pins_len) { + *eof = 1; + return 0; + } + if (offset + size >= bd->pins_len) { + size = bd->pins_len - offset; + *eof = 1; + } + memcpy(buffer, bd->pins + offset, size); + *start = buffer; + return size; +} +#endif /* CONFIG_PROC_FS */ + +static void* matroxfb_proc_probe(struct matrox_fb_info* minfo) { + struct procinfo* binfo; + char b[10]; + + binfo = (struct procinfo*)kmalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + printk(KERN_ERR "matroxfb_proc: Not enough memory for /proc control structs\n"); + return NULL; + } + binfo->info = minfo; + sprintf(b, "fb%u", GET_FB_IDX(minfo->fbcon.node)); + binfo->pde = proc_mkdir(b, mga_pde); + if (binfo->pde) { + create_proc_read_entry("bios", 0, binfo->pde, bios_read_proc, &minfo->bios); + if (minfo->bios.pins_len) { + struct proc_dir_entry* p = create_proc_read_entry("pins", 0, binfo->pde, pins_read_proc, &minfo->bios); + if (p) { + p->size = minfo->bios.pins_len; + } + } + } + return binfo; +} + +static void matroxfb_proc_remove(struct matrox_fb_info* minfo, void* binfoI) { + struct procinfo* binfo = binfoI; + + if (binfo->pde) { + remove_proc_entry("pins", binfo->pde); + remove_proc_entry("bios", binfo->pde); + remove_pde(binfo->pde); + } + kfree(binfo); +} + +static struct matroxfb_driver procfn = { + name: "Matrox /proc driver", + probe: matroxfb_proc_probe, + remove: matroxfb_proc_remove +}; + +static int matroxfb_proc_init(void) { + mga_pde = proc_mkdir("driver/mga", NULL); + matroxfb_register_driver(&procfn); + return 0; +} + +static void matroxfb_proc_exit(void) { + matroxfb_unregister_driver(&procfn); + remove_pde(mga_pde); +} + +MODULE_AUTHOR("(c) 2001 Petr Vandrovec "); +MODULE_DESCRIPTION("Matrox /proc driver"); +MODULE_LICENSE("GPL"); +module_init(matroxfb_proc_init); +module_exit(matroxfb_proc_exit); +/* we do not have __setup() */ diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/video/neofb.c Wed Feb 13 20:03:56 2002 @@ -0,0 +1,2416 @@ +/* + * linux/drivers/video/neofb.c -- NeoMagic Framebuffer Driver + * + * Copyright (c) 2001 Denis Oliver Kropp + * + * + * Card specific code is based on XFree86's neomagic driver. + * Framebuffer framework code is based on code of cyber2000fb. + * + * 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. + * + * + * 0.3 + * - hardware accelerated clear and move for 2200 and above + * - maximum allowed dotclock is handled now + * + * 0.2.1 + * - correct panning after X usage (dok) + * - added module and kernel parameters (dok) + * - no stretching if external display is enabled (dok) + * + * 0.2 + * - initial version (dok) + * + * + * TODO + * - ioctl for internal/external switching + * - blanking + * - 32bit depth support, maybe impossible + * - disable automatic pan on sync, need specs + * + * BUGS + * - white margin on bootup (colormap problem?) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif + +#include