summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile17
-rw-r--r--drivers/block/Config.in209
-rw-r--r--drivers/block/DAC960.c10
-rw-r--r--drivers/block/Makefile216
-rw-r--r--drivers/block/blkpg.c7
-rw-r--r--drivers/block/elevator.c150
-rw-r--r--drivers/block/ll_rw_blk.c341
-rw-r--r--drivers/cdrom/aztcd.c7
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/bttv.c8
-rw-r--r--drivers/char/bttv.h2
-rw-r--r--drivers/char/console.c2
-rw-r--r--drivers/char/drm/auth.c7
-rw-r--r--drivers/char/drm/bufs.c8
-rw-r--r--drivers/char/drm/context.c5
-rw-r--r--drivers/char/drm/dma.c5
-rw-r--r--drivers/char/drm/drawable.c5
-rw-r--r--drivers/char/drm/drm.h5
-rw-r--r--drivers/char/drm/drmP.h10
-rw-r--r--drivers/char/drm/fops.c18
-rw-r--r--drivers/char/drm/gamma_dma.c5
-rw-r--r--drivers/char/drm/gamma_drv.c9
-rw-r--r--drivers/char/drm/gamma_drv.h5
-rw-r--r--drivers/char/drm/init.c5
-rw-r--r--drivers/char/drm/ioctl.c5
-rw-r--r--drivers/char/drm/lists.c7
-rw-r--r--drivers/char/drm/lock.c5
-rw-r--r--drivers/char/drm/memory.c5
-rw-r--r--drivers/char/drm/proc.c21
-rw-r--r--drivers/char/drm/tdfx_context.c5
-rw-r--r--drivers/char/drm/tdfx_drv.c25
-rw-r--r--drivers/char/drm/tdfx_drv.h6
-rw-r--r--drivers/char/drm/vm.c5
-rw-r--r--drivers/char/generic_serial.c168
-rw-r--r--drivers/char/generic_serial.h104
-rw-r--r--drivers/char/mem.c8
-rw-r--r--drivers/char/misc.c26
-rw-r--r--drivers/char/mixcomwd.c58
-rw-r--r--drivers/char/n_tty.c50
-rw-r--r--drivers/char/ppdev.c11
-rw-r--r--drivers/char/pty.c4
-rw-r--r--drivers/char/radio-gemtek.c4
-rw-r--r--drivers/char/serial.c35
-rw-r--r--drivers/char/sx.c437
-rw-r--r--drivers/char/sx.h38
-rw-r--r--drivers/char/sxboards.h1
-rw-r--r--drivers/char/tty_io.c4
-rw-r--r--drivers/char/videodev.c72
-rw-r--r--drivers/ide/Config.in153
-rw-r--r--drivers/ide/Makefile238
-rw-r--r--drivers/ide/aec6210.c (renamed from drivers/block/aec6210.c)0
-rw-r--r--drivers/ide/ali14xx.c (renamed from drivers/block/ali14xx.c)0
-rw-r--r--drivers/ide/alim15x3.c (renamed from drivers/block/alim15x3.c)0
-rw-r--r--drivers/ide/amd7409.c (renamed from drivers/block/amd7409.c)0
-rw-r--r--drivers/ide/buddha.c (renamed from drivers/block/buddha.c)0
-rw-r--r--drivers/ide/cmd640.c (renamed from drivers/block/cmd640.c)0
-rw-r--r--drivers/ide/cmd64x.c (renamed from drivers/block/cmd64x.c)0
-rw-r--r--drivers/ide/cs5530.c (renamed from drivers/block/cs5530.c)0
-rw-r--r--drivers/ide/cy82c693.c (renamed from drivers/block/cy82c693.c)0
-rw-r--r--drivers/ide/dtc2278.c (renamed from drivers/block/dtc2278.c)0
-rw-r--r--drivers/ide/falconide.c (renamed from drivers/block/falconide.c)0
-rw-r--r--drivers/ide/gayle.c (renamed from drivers/block/gayle.c)0
-rw-r--r--drivers/ide/hd.c (renamed from drivers/block/hd.c)0
-rw-r--r--drivers/ide/hpt34x.c (renamed from drivers/block/hpt34x.c)0
-rw-r--r--drivers/ide/hpt366.c (renamed from drivers/block/hpt366.c)0
-rw-r--r--drivers/ide/ht6560b.c (renamed from drivers/block/ht6560b.c)0
-rw-r--r--drivers/ide/icside.c (renamed from drivers/block/icside.c)0
-rw-r--r--drivers/ide/ide-cd.c (renamed from drivers/block/ide-cd.c)0
-rw-r--r--drivers/ide/ide-cd.h (renamed from drivers/block/ide-cd.h)0
-rw-r--r--drivers/ide/ide-cs.c (renamed from drivers/block/ide-cs.c)0
-rw-r--r--drivers/ide/ide-disk.c (renamed from drivers/block/ide-disk.c)0
-rw-r--r--drivers/ide/ide-dma.c (renamed from drivers/block/ide-dma.c)0
-rw-r--r--drivers/ide/ide-features.c (renamed from drivers/block/ide-features.c)0
-rw-r--r--drivers/ide/ide-floppy.c (renamed from drivers/block/ide-floppy.c)8
-rw-r--r--drivers/ide/ide-geometry.c (renamed from drivers/block/ide-geometry.c)0
-rw-r--r--drivers/ide/ide-pci.c (renamed from drivers/block/ide-pci.c)0
-rw-r--r--drivers/ide/ide-pmac.c (renamed from drivers/block/ide-pmac.c)0
-rw-r--r--drivers/ide/ide-pnp.c (renamed from drivers/block/ide-pnp.c)0
-rw-r--r--drivers/ide/ide-probe.c (renamed from drivers/block/ide-probe.c)0
-rw-r--r--drivers/ide/ide-proc.c (renamed from drivers/block/ide-proc.c)0
-rw-r--r--drivers/ide/ide-tape.c (renamed from drivers/block/ide-tape.c)0
-rw-r--r--drivers/ide/ide.c (renamed from drivers/block/ide.c)2
-rw-r--r--drivers/ide/ide_modes.h (renamed from drivers/block/ide_modes.h)0
-rw-r--r--drivers/ide/macide.c (renamed from drivers/block/macide.c)0
-rw-r--r--drivers/ide/ns87415.c (renamed from drivers/block/ns87415.c)0
-rw-r--r--drivers/ide/opti621.c (renamed from drivers/block/opti621.c)0
-rw-r--r--drivers/ide/pdc202xx.c (renamed from drivers/block/pdc202xx.c)0
-rw-r--r--drivers/ide/pdc4030.c (renamed from drivers/block/pdc4030.c)0
-rw-r--r--drivers/ide/pdc4030.h (renamed from drivers/block/pdc4030.h)0
-rw-r--r--drivers/ide/piix.c (renamed from drivers/block/piix.c)0
-rw-r--r--drivers/ide/q40ide.c (renamed from drivers/block/q40ide.c)0
-rw-r--r--drivers/ide/qd6580.c (renamed from drivers/block/qd6580.c)0
-rw-r--r--drivers/ide/rapide.c (renamed from drivers/block/rapide.c)0
-rw-r--r--drivers/ide/rz1000.c (renamed from drivers/block/rz1000.c)0
-rw-r--r--drivers/ide/sis5513.c (renamed from drivers/block/sis5513.c)0
-rw-r--r--drivers/ide/sl82c105.c (renamed from drivers/block/sl82c105.c)0
-rw-r--r--drivers/ide/trm290.c (renamed from drivers/block/trm290.c)0
-rw-r--r--drivers/ide/umc8672.c (renamed from drivers/block/umc8672.c)0
-rw-r--r--drivers/ide/via82cxxx.c (renamed from drivers/block/via82cxxx.c)0
-rw-r--r--drivers/net/3c527.c52
-rw-r--r--drivers/net/3c59x.c2
-rw-r--r--drivers/net/Config.in12
-rw-r--r--drivers/net/aironet4500.h2
-rw-r--r--drivers/net/appletalk/Config.in31
-rw-r--r--drivers/net/bsd_comp.c42
-rw-r--r--drivers/net/dgrs.c5
-rw-r--r--drivers/net/dmfe.c586
-rw-r--r--drivers/net/eexpress.c1
-rw-r--r--drivers/net/epic100.c2
-rw-r--r--drivers/net/eql.c4
-rw-r--r--drivers/net/ewrk3.c4
-rw-r--r--drivers/net/hamradio/baycom_epp.c8
-rw-r--r--drivers/net/hamradio/baycom_par.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c2
-rw-r--r--drivers/net/hamradio/bpqether.c2
-rw-r--r--drivers/net/hamradio/hdlcdrv.c6
-rw-r--r--drivers/net/hamradio/pi2.c4
-rw-r--r--drivers/net/hamradio/pt.c4
-rw-r--r--drivers/net/hamradio/scc.c12
-rw-r--r--drivers/net/hamradio/soundmodem/sm.c2
-rw-r--r--drivers/net/hamradio/soundmodem/sm_sbc.c4
-rw-r--r--drivers/net/hamradio/soundmodem/sm_wss.c2
-rw-r--r--drivers/net/hamradio/yam.c6
-rw-r--r--drivers/net/lance.c46
-rw-r--r--drivers/net/pcmcia/3c574_cs.c12
-rw-r--r--drivers/net/pcmcia/Config.in1
-rw-r--r--drivers/net/pcmcia/Makefile2
-rw-r--r--drivers/net/pcmcia/ray_cs.c2
-rw-r--r--drivers/net/pcmcia/tulip_cb.c3150
-rw-r--r--drivers/net/pcnet32.c2
-rw-r--r--drivers/net/plip.c59
-rw-r--r--drivers/net/ppp_async.c627
-rw-r--r--drivers/net/ppp_deflate.c49
-rw-r--r--drivers/net/ppp_generic.c1510
-rw-r--r--drivers/net/ppp_synctty.c2
-rw-r--r--drivers/net/rcpci45.c15
-rw-r--r--drivers/net/rrunner.c8
-rw-r--r--drivers/net/sb1000.c4
-rw-r--r--drivers/net/setup.c5
-rw-r--r--drivers/net/shaper.c11
-rw-r--r--drivers/net/sis900.c2
-rw-r--r--drivers/net/skfp/drvfbi.c2
-rw-r--r--drivers/net/skfp/skfddi.c2
-rw-r--r--drivers/net/sunhme.c4
-rw-r--r--drivers/net/tokenring/Config.in1
-rw-r--r--drivers/net/tokenring/Makefile1
-rw-r--r--drivers/net/tokenring/lanstreamer.c1776
-rw-r--r--drivers/net/tokenring/lanstreamer.h319
-rw-r--r--drivers/net/via-rhine.c2
-rw-r--r--drivers/net/wan/Config.in19
-rw-r--r--drivers/net/wan/Makefile56
-rw-r--r--drivers/net/wan/comx-hw-comx.c1426
-rw-r--r--drivers/net/wan/comx-hw-locomx.c496
-rw-r--r--drivers/net/wan/comx-hw-mixcom.c948
-rw-r--r--drivers/net/wan/comx-proto-fr.c1006
-rw-r--r--drivers/net/wan/comx-proto-lapb.c548
-rw-r--r--drivers/net/wan/comx-proto-ppp.c269
-rw-r--r--drivers/net/wan/comx.c1238
-rw-r--r--drivers/net/wan/comx.h240
-rw-r--r--drivers/net/wan/comxhw.h113
-rw-r--r--drivers/net/wan/cosa.c10
-rw-r--r--drivers/net/wan/hscx.h103
-rw-r--r--drivers/net/wan/mixcom.h35
-rw-r--r--drivers/net/wan/sbni.c6
-rw-r--r--drivers/net/wan/sdla.c2
-rw-r--r--drivers/net/wan/syncppp.c142
-rw-r--r--drivers/net/wan/syncppp.h4
-rw-r--r--drivers/net/wan/z85230.c94
-rw-r--r--drivers/net/wavelan.c6
-rw-r--r--drivers/net/wavelan.h6
-rw-r--r--drivers/net/yellowfin.c1
-rw-r--r--drivers/parport/ChangeLog21
-rw-r--r--drivers/parport/Config.in3
-rw-r--r--drivers/parport/parport_pc.c119
-rw-r--r--drivers/pcmcia/yenta.c25
-rw-r--r--drivers/sbus/audio/audio.c410
-rw-r--r--drivers/sbus/char/sab82532.c8
-rw-r--r--drivers/sbus/char/su.c8
-rw-r--r--drivers/sbus/char/zs.c8
-rw-r--r--drivers/scsi/ChangeLog.ncr53c8xx17
-rw-r--r--drivers/scsi/ChangeLog.sym53c8xx9
-rw-r--r--drivers/scsi/Config.in4
-rw-r--r--drivers/scsi/README.st8
-rw-r--r--drivers/scsi/atp870u.c5
-rw-r--r--drivers/scsi/constants.c563
-rw-r--r--drivers/scsi/eata_dma_proc.c18
-rw-r--r--drivers/scsi/hosts.h28
-rw-r--r--drivers/scsi/ncr53c8xx.c2600
-rw-r--r--drivers/scsi/scsi.c18
-rw-r--r--drivers/scsi/scsi.h11
-rw-r--r--drivers/scsi/scsi_debug.c12
-rw-r--r--drivers/scsi/scsi_ioctl.c64
-rw-r--r--drivers/scsi/scsi_lib.c9
-rw-r--r--drivers/scsi/scsi_merge.c8
-rw-r--r--drivers/scsi/scsi_scan.c80
-rw-r--r--drivers/scsi/scsi_syms.c1
-rw-r--r--drivers/scsi/sd.c74
-rw-r--r--drivers/scsi/sr.c4
-rw-r--r--drivers/scsi/st.c738
-rw-r--r--drivers/scsi/st.h11
-rw-r--r--drivers/scsi/st_options.h9
-rw-r--r--drivers/scsi/sym53c8xx.c175
-rw-r--r--drivers/scsi/sym53c8xx_comm.h2863
-rw-r--r--drivers/sound/Config.in5
-rw-r--r--drivers/sound/Makefile37
-rw-r--r--drivers/sound/dev_table.c2
-rw-r--r--drivers/sound/dev_table.h36
-rw-r--r--drivers/sound/dmabuf.c8
-rw-r--r--drivers/sound/miroaci.h1
-rw-r--r--drivers/sound/mpu401.c21
-rw-r--r--drivers/sound/sb_card.c2
-rw-r--r--drivers/sound/sound_core.c112
-rw-r--r--drivers/sound/sound_firmware.c18
-rw-r--r--drivers/sound/soundcard.c57
-rw-r--r--drivers/sound/waveartist.c12
-rw-r--r--drivers/telephony/ixj.c29
-rw-r--r--drivers/telephony/phonedev.c5
-rw-r--r--drivers/usb/Config.in4
-rw-r--r--drivers/usb/Makefile18
-rw-r--r--drivers/usb/dsbr100.c353
-rw-r--r--drivers/usb/inode.c1
-rw-r--r--drivers/usb/ov511.c484
-rw-r--r--drivers/usb/ov511.h9
-rw-r--r--drivers/usb/pegasus.c714
-rw-r--r--drivers/usb/serial/Makefile9
-rw-r--r--drivers/usb/uhci.c56
-rw-r--r--drivers/usb/uhci.h6
-rw-r--r--drivers/usb/usb-core.c7
-rw-r--r--drivers/usb/usb-storage.c1124
-rw-r--r--drivers/usb/usb-storage.h24
-rw-r--r--drivers/usb/usb-uhci-debug.h79
-rw-r--r--drivers/usb/usb-uhci.c975
-rw-r--r--drivers/usb/usb-uhci.h18
-rw-r--r--drivers/video/aty128.h2
-rw-r--r--drivers/video/aty128fb.c351
-rw-r--r--drivers/video/atyfb.c2
-rw-r--r--drivers/video/cyber2000fb.c106
-rw-r--r--drivers/video/fbcon.c101
-rw-r--r--drivers/video/fbmem.c84
-rw-r--r--drivers/video/hgafb.c86
241 files changed, 19632 insertions, 11001 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 1d4653233..eca864b62 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -9,9 +9,9 @@
SUB_DIRS := block char net parport sound misc
MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o ieee1394 \
- macintosh video dio zorro fc4 usb \
- nubus tc atm pcmcia i2c telephony
+ALL_SUB_DIRS := $(SUB_DIRS) pci sgi ide scsi sbus cdrom isdn pnp i2o \
+ ieee1394 macintosh video dio zorro fc4 \
+ usb nubus tc atm pcmcia i2c telephony
ifdef CONFIG_DIO
SUB_DIRS += dio
@@ -94,6 +94,17 @@ else
endif
endif
+# If CONFIG_IDE is set, the core of ATA support will be added to the kernel,
+# but some of the low-level things may also be modules.
+ifeq ($(CONFIG_IDE),y)
+SUB_DIRS += ide
+MOD_SUB_DIRS += ide
+else
+ ifeq ($(CONFIG_IDE),m)
+ MOD_SUB_DIRS += ide
+ endif
+endif
+
# If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel,
# but some of the low-level things may also be modules.
ifeq ($(CONFIG_SCSI),y)
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index 0e29e3a2e..51a6db89c 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -16,172 +16,6 @@ if [ "$CONFIG_MAC" = "y" ]; then
bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP
fi
fi
-
-tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
-comment 'Please see Documentation/ide.txt for help/info on IDE drives'
-if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
- bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
-else
- bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
- dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
- if [ "$CONFIG_BLK_DEV_IDEDISK" != "n" ]; then
- bool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE
- fi
- dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
- dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
- dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
- dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
- comment 'IDE chipset support/bugfixes'
- if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
- bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
- if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
- bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED
- fi
- if [ "$CONFIG_ISAPNP" = "y" ]; then
- bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP
- fi
- if [ "$CONFIG_PCI" = "y" ]; then
- bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
- bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
- if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
- bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
- bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Good-Bad DMA Model-Firmware (EXPERIMENTAL)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
- define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL y
- else
- define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL n
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP
- fi
- bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
- bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AEC6210" = "y" ]; then
- bool ' AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3
- bool ' AMD Viper support' CONFIG_BLK_DEV_AMD7409
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_AMD7409" = "y" ]; then
- bool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE
- fi
- fi
- bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_CMD64X" = "y" ]; then
- bool ' CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693
- fi
- bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then
- bool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA
- fi
- bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" -a "$CONFIG_BLK_DEV_HPT366" = "y" ]; then
- bool ' HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP
- bool ' HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3
- fi
- if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
- bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX
- if [ "$CONFIG_BLK_DEV_PIIX" = "y" -a "$CONFIG_IDEDMA_PCI_AUTO" = "y" ]; then
- bool ' PIIXn Tuning support' CONFIG_PIIX_TUNING
- fi
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
- fi
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
- bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX
- if [ "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then
- bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST
- if [ "$CONFIG_IDEDMA_PCI_WIP" = "y" ]; then
- bool ' Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER
- fi
- fi
- if [ "$CONFIG_X86" = "y" ]; then
- bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513
- fi
- fi
- if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then
- bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
- bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX
- fi
- fi
- if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
- bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
- fi
- fi
- if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
- bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
- if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then
- bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC
- if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then
- bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO
- fi
- fi
- fi
- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
- bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
- if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then
- bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS
- if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
- bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO
- fi
- fi
- bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
- fi
- if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
- "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
- "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
- define_bool CONFIG_BLK_DEV_IDEDMA y
- if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
- "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
- "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
- define_bool CONFIG_IDEDMA_AUTO y
- fi
- fi
- bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
- if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
- comment 'Note: most of these also require special kernel boot parameters'
- bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
- bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
- bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
- bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
- if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
- fi
- bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
- bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
- fi
- if [ "$CONFIG_AMIGA" = "y" ]; then
- bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE
- fi
- fi
- if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA
- fi
- if [ "$CONFIG_ATARI" = "y" ]; then
- bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE
- fi
- if [ "$CONFIG_MAC" = "y" ]; then
- bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE
- fi
- fi
-fi
if [ "$CONFIG_MCA" = "y" ]; then
tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2
fi
@@ -196,8 +30,15 @@ if [ "$CONFIG_ATARI" = "y" ]; then
tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM
fi
fi
+tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+ source drivers/block/paride/Config.in
+fi
+
if [ "$CONFIG_PCI" = "y" ]; then
tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+ tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
fi
comment 'Additional Block Devices'
@@ -220,41 +61,5 @@ tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
fi
-tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
-if [ "$CONFIG_PCI" = "y" ]; then
- tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
-fi
-
-dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
-if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
- source drivers/block/paride/Config.in
-fi
-
-if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
- "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
- "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
- "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
- "$CONFIG_BLK_DEV_CMD640" = "y" -o \
- "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
- "$CONFIG_BLK_DEV_CS5530" = "y" -o \
- "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
- "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
- "$CONFIG_BLK_DEV_HPT366" = "y" -o \
- "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
- "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
- "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
- "$CONFIG_BLK_DEV_PIIX" = "y" -o \
- "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
- "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
- define_bool CONFIG_BLK_DEV_IDE_MODES y
-else
- define_bool CONFIG_BLK_DEV_IDE_MODES n
-fi
-
-if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
- define_bool CONFIG_BLK_DEV_HD y
-else
- define_bool CONFIG_BLK_DEV_HD n
-fi
endmenu
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index e969e2dd7..87c597c31 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -1021,7 +1021,7 @@ static inline int DAC_new_segment(request_queue_t *q, struct request *req,
if (req->nr_segments < max_segments) {
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
return 0;
@@ -1051,23 +1051,23 @@ static int DAC_merge_requests_fn(request_queue_t *q,
int max_segments;
DAC960_Controller_T * Controller = q->queuedata;
int total_segments = req->nr_segments + next->nr_segments;
- int same_segment;
+ int same_segment;
max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
if (__max_segments < max_segments)
max_segments = __max_segments;
- same_segment = 0;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
{
total_segments--;
- same_segment = 1;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
- q->nr_segments -= same_segment;
+ q->elevator.nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 6882d03f3..9f5c813d7 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -20,7 +20,7 @@ ALL_SUB_DIRS := $(SUB_DIRS) paride
L_TARGET := block.a
-L_OBJS := genhd.o ide-geometry.o
+L_OBJS := genhd.o elevator.o
M_OBJS :=
MOD_LIST_NAME := BLOCK_MODULES
LX_OBJS := ll_rw_blk.o blkpg.o
@@ -98,214 +98,6 @@ else
endif
endif
-#
-# IDE-STUFF
-#
-
-ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
-IDE_OBJS += aec6210.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
-IDE_OBJS += ali14xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
-IDE_OBJS += alim15x3.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
-IDE_OBJS += amd7409.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
-IDE_OBJS += buddha.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD640),y)
-IDE_OBJS += cmd640.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
-IDE_OBJS += cmd64x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CS5530),y)
-IDE_OBJS += cs5530.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
-IDE_OBJS += cy82c693.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
-IDE_OBJS += dtc2278.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
-IDE_OBJS += falconide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
-IDE_OBJS += gayle.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
-IDE_OBJS += q40ide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HD),y)
-L_OBJS += hd.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
-IDE_OBJS += hpt34x.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HPT366),y)
-IDE_OBJS += hpt366.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
-IDE_OBJS += ht6560b.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
-IDE_OBJS += icside.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
-IDE_OBJS += ide-dma.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
-IDE_OBJS += ide-pci.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
-IDE_OBJS += ide-pnp.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
-IDE_OBJS += ide-pmac.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
-IDE_OBJS += macide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_NS87415),y)
-IDE_OBJS += ns87415.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
-IDE_OBJS += opti621.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
-IDE_OBJS += pdc202xx.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
-IDE_OBJS += pdc4030.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_PIIX),y)
-IDE_OBJS += piix.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_QD6580),y)
-IDE_OBJS += qd6580.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
-IDE_OBJS += rapide.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
-IDE_OBJS += rz1000.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
-IDE_OBJS += sis5513.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
-IDE_OBJS += sl82c105.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_TRM290),y)
-IDE_OBJS += trm290.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
-IDE_OBJS += umc8672.o
-endif
-
-ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
-IDE_OBJS += via82cxxx.o
-endif
-
-### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
-
-ifeq ($(CONFIG_PROC_FS),y)
-IDE_OBJS += ide-proc.o
-endif
-
-###Collect
-
-ifeq ($(CONFIG_BLK_DEV_IDE),y)
- LX_OBJS += ide.o ide-features.o
- L_OBJS += ide-probe.o $(IDE_OBJS)
-else
- ifeq ($(CONFIG_BLK_DEV_IDE),m)
- MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
- M_OBJS += ide-mod.o ide-probe-mod.o
- endif
-endif
-
-############
-
-ifeq ($(CONFIG_BLK_DEV_IDECS),y)
-L_OBJS += ide-cs.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDECS),m)
- M_OBJS += ide-cs.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
-L_OBJS += ide-disk.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
- M_OBJS += ide-disk.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDECD),y)
-L_OBJS += ide-cd.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDECD),m)
- M_OBJS += ide-cd.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
-L_OBJS += ide-tape.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
- M_OBJS += ide-tape.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
-L_OBJS += ide-floppy.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
- M_OBJS += ide-floppy.o
- endif
-endif
-
ifeq ($(CONFIG_BLK_DEV_PS2),y)
L_OBJS += ps2esdi.o
else
@@ -418,11 +210,5 @@ endif
include $(TOPDIR)/Rules.make
-ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
-
-ide-probe-mod.o: ide-probe.o ide-geometry.o
- $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
-
lvm-mod.o: lvm.o lvm-snap.o
$(LD) -r -o $@ lvm.o lvm-snap.o
diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c
index 591ff9310..c709ebdc9 100644
--- a/drivers/block/blkpg.c
+++ b/drivers/block/blkpg.c
@@ -268,6 +268,13 @@ int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
case BLKPG:
return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg);
+ case BLKELVGET:
+ return blkelvget_ioctl(&blk_get_queue(dev)->elevator,
+ (blkelv_ioctl_arg_t *) arg);
+ case BLKELVSET:
+ return blkelvset_ioctl(&blk_get_queue(dev)->elevator,
+ (blkelv_ioctl_arg_t *) arg);
+
default:
return -EINVAL;
}
diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c
new file mode 100644
index 000000000..11532f8fd
--- /dev/null
+++ b/drivers/block/elevator.c
@@ -0,0 +1,150 @@
+/*
+ * linux/drivers/block/elevator.c
+ *
+ * Block device elevator/IO-scheduler.
+ *
+ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+#include <linux/blk.h>
+#include <asm/uaccess.h>
+
+static void elevator_default(struct request * req, elevator_t * elevator,
+ struct list_head * real_head,
+ struct list_head * head, int orig_latency)
+{
+ struct list_head * entry = real_head, * point = NULL;
+ struct request * tmp;
+ int sequence = elevator->sequence;
+ int latency = orig_latency -= elevator->nr_segments, pass = 0;
+ int point_latency = 0xbeefbeef;
+
+ while ((entry = entry->prev) != head) {
+ if (!point && latency >= 0) {
+ point = entry;
+ point_latency = latency;
+ }
+ tmp = blkdev_entry_to_request(entry);
+ if (elevator_sequence_before(tmp->elevator_sequence, sequence) || !tmp->q)
+ break;
+ if (latency >= 0) {
+ if (IN_ORDER(tmp, req) ||
+ (pass && !IN_ORDER(tmp, blkdev_next_request(tmp))))
+ goto link;
+ }
+ latency += tmp->nr_segments;
+ pass = 1;
+ }
+
+ if (point) {
+ entry = point;
+ latency = point_latency;
+ }
+
+ link:
+ list_add(&req->queue, entry);
+ req->elevator_sequence = elevator_sequence(elevator, latency);
+}
+
+#ifdef ELEVATOR_DEBUG
+void elevator_debug(request_queue_t * q, kdev_t dev)
+{
+ int read_pendings = 0, nr_segments = 0;
+ elevator_t * elevator = &q->elevator;
+ struct list_head * entry = &q->queue_head;
+ static int counter;
+
+ if (counter++ % 100)
+ return;
+
+ while ((entry = entry->prev) != &q->queue_head)
+ {
+ struct request * req;
+
+ req = blkdev_entry_to_request(entry);
+ if (req->cmd != READ && req->cmd != WRITE && (req->q || req->nr_segments))
+ printk(KERN_WARNING
+ "%s: elevator req->cmd %d req->nr_segments %u req->q %p\n",
+ kdevname(dev), req->cmd, req->nr_segments, req->q);
+ if (!req->q) {
+ if (req->nr_segments)
+ printk(KERN_WARNING
+ "%s: elevator req->q NULL req->nr_segments %u\n",
+ kdevname(dev), req->nr_segments);
+ continue;
+ }
+ if (req->cmd == READ)
+ read_pendings++;
+ nr_segments += req->nr_segments;
+ }
+
+ if (read_pendings != elevator->read_pendings)
+ {
+ printk(KERN_WARNING
+ "%s: elevator read_pendings %d should be %d\n",
+ kdevname(dev), elevator->read_pendings,
+ read_pendings);
+ elevator->read_pendings = read_pendings;
+ }
+ if (nr_segments != elevator->nr_segments)
+ {
+ printk(KERN_WARNING
+ "%s: elevator nr_segments %d should be %d\n",
+ kdevname(dev), elevator->nr_segments,
+ nr_segments);
+ elevator->nr_segments = nr_segments;
+ }
+}
+#endif
+
+int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
+{
+ int ret;
+ blkelv_ioctl_arg_t output;
+
+ output.queue_ID = elevator;
+ output.read_latency = elevator->read_latency;
+ output.write_latency = elevator->write_latency;
+ output.max_bomb_segments = elevator->max_bomb_segments;
+
+ ret = -EFAULT;
+ if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
+ goto out;
+ ret = 0;
+ out:
+ return ret;
+}
+
+int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
+{
+ blkelv_ioctl_arg_t input;
+ int ret;
+
+ ret = -EFAULT;
+ if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
+ goto out;
+
+ ret = -EINVAL;
+ if (input.read_latency < 0)
+ goto out;
+ if (input.write_latency < 0)
+ goto out;
+ if (input.max_bomb_segments <= 0)
+ goto out;
+
+ elevator->read_latency = input.read_latency;
+ elevator->write_latency = input.write_latency;
+ elevator->max_bomb_segments = input.max_bomb_segments;
+
+ ret = 0;
+ out:
+ return ret;
+}
+
+void elevator_init(elevator_t * elevator)
+{
+ *elevator = ELEVATOR_DEFAULTS;
+}
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index c14591b92..63c5e4b85 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -28,8 +28,6 @@
#include <linux/module.h>
-#define DEBUG_ELEVATOR
-
/*
* MAC Floppy IWM hooks
*/
@@ -150,18 +148,6 @@ request_queue_t * blk_get_queue (kdev_t dev)
return ret;
}
-static inline int get_request_latency(elevator_t * elevator, int rw)
-{
- int latency;
-
- if (rw != READ)
- latency = elevator->write_latency;
- else
- latency = elevator->read_latency;
-
- return latency;
-}
-
void blk_cleanup_queue(request_queue_t * q)
{
memset(q, 0, sizeof(*q));
@@ -186,7 +172,7 @@ static inline int ll_new_segment(request_queue_t *q, struct request *req, int ma
{
if (req->nr_segments < max_segments) {
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
return 0;
@@ -212,18 +198,18 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req,
struct request *next, int max_segments)
{
int total_segments = req->nr_segments + next->nr_segments;
- int same_segment;
+ int same_segment;
- same_segment = 0;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
total_segments--;
- same_segment = 1;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
- q->nr_segments -= same_segment;
+ q->elevator.nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
@@ -254,7 +240,7 @@ static void generic_plug_device (request_queue_t *q, kdev_t dev)
void blk_init_queue(request_queue_t * q, request_fn_proc * rfn)
{
INIT_LIST_HEAD(&q->queue_head);
- q->elevator = ELEVATOR_DEFAULTS;
+ elevator_init(&q->elevator);
q->request_fn = rfn;
q->back_merge_fn = ll_back_merge_fn;
q->front_merge_fn = ll_front_merge_fn;
@@ -425,113 +411,6 @@ static inline void drive_stat_acct(struct request *req,
printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
}
-/* elevator */
-
-#define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0)
-#define elevator_sequence_before(a,b) elevator_sequence_after(b,a)
-#define elevator_sequence_after_eq(a,b) ((int)((b)-(a)) <= 0)
-#define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a)
-
-static inline struct list_head * seek_to_not_starving_chunk(request_queue_t * q,
- int * lat, int * starving)
-{
- int sequence = q->elevator.sequence;
- struct list_head * entry = q->queue_head.prev;
- int pos = 0;
-
- do {
- struct request * req = blkdev_entry_to_request(entry);
- if (elevator_sequence_before(req->elevator_sequence, sequence)) {
- *lat -= q->nr_segments - pos;
- *starving = 1;
- return entry;
- }
- pos += req->nr_segments;
- } while ((entry = entry->prev) != &q->queue_head);
-
- *starving = 0;
-
- return entry->next;
-}
-
-static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next)
-{
- if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence))
- req->elevator_sequence = next->elevator_sequence;
- if (req->cmd == READ)
- e->read_pendings--;
-
-}
-
-static inline int elevator_sequence(elevator_t * e, int latency)
-{
- return latency + e->sequence;
-}
-
-#define elevator_merge_before(q, req, lat) __elevator_merge((q), (req), (lat), 0)
-#define elevator_merge_after(q, req, lat) __elevator_merge((q), (req), (lat), 1)
-static inline void __elevator_merge(request_queue_t * q, struct request * req, int latency, int after)
-{
- int sequence = elevator_sequence(&q->elevator, latency);
- if (after)
- sequence -= req->nr_segments;
- if (elevator_sequence_before(sequence, req->elevator_sequence))
- req->elevator_sequence = sequence;
-}
-
-static inline void elevator_queue(request_queue_t * q,
- struct request * req,
- struct list_head * entry,
- int latency, int starving)
-{
- struct request * tmp, * __tmp;
- int __latency = latency;
-
- __tmp = tmp = blkdev_entry_to_request(entry);
-
- for (;; tmp = blkdev_next_request(tmp))
- {
- if ((latency -= tmp->nr_segments) <= 0)
- {
- tmp = __tmp;
- latency = __latency;
-
- if (starving)
- break;
-
- if (q->head_active && !q->plugged)
- {
- latency -= tmp->nr_segments;
- break;
- }
-
- list_add(&req->queue, &q->queue_head);
- goto after_link;
- }
-
- if (tmp->queue.next == &q->queue_head)
- break;
-
- {
- const int after_current = IN_ORDER(tmp,req);
- const int before_next = IN_ORDER(req,blkdev_next_request(tmp));
-
- if (!IN_ORDER(tmp,blkdev_next_request(tmp))) {
- if (after_current || before_next)
- break;
- } else {
- if (after_current && before_next)
- break;
- }
- }
- }
-
- list_add(&req->queue, &tmp->queue);
-
- after_link:
- req->elevator_sequence = elevator_sequence(&q->elevator, latency);
-}
-
/*
* add-request adds a request to the linked list.
* It disables interrupts (aquires the request spinlock) so that it can muck
@@ -542,20 +421,19 @@ static inline void elevator_queue(request_queue_t * q,
* which is important for drive_stat_acct() above.
*/
-static inline void __add_request(request_queue_t * q, struct request * req,
- int empty, struct list_head * entry,
- int latency, int starving)
+static inline void add_request(request_queue_t * q, struct request * req,
+ struct list_head * head, int latency)
{
int major;
drive_stat_acct(req, req->nr_sectors, 1);
- if (empty) {
+ if (list_empty(head)) {
req->elevator_sequence = elevator_sequence(&q->elevator, latency);
list_add(&req->queue, &q->queue_head);
return;
}
- elevator_queue(q, req, entry, latency, starving);
+ q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency);
/*
* FIXME(eric) I don't understand why there is a need for this
@@ -579,19 +457,17 @@ static inline void __add_request(request_queue_t * q, struct request * req,
/*
* Has to be called with the request spinlock aquired
*/
-static inline void attempt_merge (request_queue_t * q,
- struct request *req,
- int max_sectors,
- int max_segments)
+static void attempt_merge(request_queue_t * q,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
struct request *next;
- if (req->queue.next == &q->queue_head)
- return;
next = blkdev_next_request(req);
if (req->sector + req->nr_sectors != next->sector)
return;
- if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors)
+ if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem)
return;
/*
* If we are not allowed to merge these requests, then
@@ -611,54 +487,28 @@ static inline void attempt_merge (request_queue_t * q,
wake_up (&wait_for_request);
}
-static inline void elevator_debug(request_queue_t * q, kdev_t dev)
+static inline void attempt_back_merge(request_queue_t * q,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
-#ifdef DEBUG_ELEVATOR
- int read_pendings = 0, nr_segments = 0;
- elevator_t * elevator = &q->elevator;
- struct list_head * entry = &q->queue_head;
- static int counter;
-
- if (counter++ % 100)
+ if (&req->queue == q->queue_head.prev)
return;
-
- while ((entry = entry->next) != &q->queue_head)
- {
- struct request * req;
-
- req = blkdev_entry_to_request(entry);
- if (!req->q)
- continue;
- if (req->cmd == READ)
- read_pendings++;
- nr_segments += req->nr_segments;
- }
-
- if (read_pendings != elevator->read_pendings)
- {
- printk(KERN_WARNING
- "%s: elevator read_pendings %d should be %d\n",
- kdevname(dev), elevator->read_pendings,
- read_pendings);
- elevator->read_pendings = read_pendings;
- }
- if (nr_segments != q->nr_segments)
- {
- printk(KERN_WARNING
- "%s: elevator nr_segments %d should be %d\n",
- kdevname(dev), q->nr_segments,
- nr_segments);
- q->nr_segments = nr_segments;
- }
-#endif
+ attempt_merge(q, req, max_sectors, max_segments);
}
-static inline void elevator_account_request(request_queue_t * q, struct request * req)
+static inline void attempt_front_merge(request_queue_t * q,
+ struct list_head * head,
+ struct request *req,
+ int max_sectors,
+ int max_segments)
{
- q->elevator.sequence++;
- if (req->cmd == READ)
- q->elevator.read_pendings++;
- q->nr_segments++;
+ struct list_head * prev;
+
+ prev = req->queue.prev;
+ if (head == prev)
+ return;
+ attempt_merge(q, blkdev_entry_to_request(prev), max_sectors, max_segments);
}
static inline void __make_request(request_queue_t * q, int rw,
@@ -667,11 +517,13 @@ static inline void __make_request(request_queue_t * q, int rw,
int major = MAJOR(bh->b_rdev);
unsigned int sector, count;
int max_segments = MAX_SEGMENTS;
- struct request * req, * prev;
+ struct request * req;
int rw_ahead, max_req, max_sectors;
unsigned long flags;
- int orig_latency, latency, __latency, starving, __starving, empty;
- struct list_head * entry, * __entry = NULL;
+
+ int orig_latency, latency, starving, sequence;
+ struct list_head * entry, * head = &q->queue_head;
+ elevator_t * elevator;
count = bh->b_size >> 9;
sector = bh->b_rsector;
@@ -758,7 +610,8 @@ static inline void __make_request(request_queue_t * q, int rw,
*/
max_sectors = get_max_sectors(bh->b_rdev);
- __latency = orig_latency = get_request_latency(&q->elevator, rw);
+ elevator = &q->elevator;
+ orig_latency = elevator_request_latency(elevator, rw);
/*
* Now we acquire the request spinlock, we have to be mega careful
@@ -767,46 +620,42 @@ static inline void __make_request(request_queue_t * q, int rw,
spin_lock_irqsave(&io_request_lock,flags);
elevator_debug(q, bh->b_rdev);
- empty = 0;
- if (list_empty(&q->queue_head)) {
- empty = 1;
+ if (list_empty(head)) {
q->plug_device_fn(q, bh->b_rdev); /* is atomic */
goto get_rq;
}
/* avoid write-bombs to not hurt iteractiveness of reads */
- if (rw != READ && q->elevator.read_pendings)
- max_segments = q->elevator.max_bomb_segments;
-
- entry = seek_to_not_starving_chunk(q, &__latency, &starving);
+ if (rw != READ && elevator->read_pendings)
+ max_segments = elevator->max_bomb_segments;
- __entry = entry;
- __starving = starving;
+ sequence = elevator->sequence;
+ latency = orig_latency - elevator->nr_segments;
+ starving = 0;
+ entry = head;
- latency = __latency;
-
- if (q->head_active && !q->plugged) {
- /*
- * The scsi disk and cdrom drivers completely remove the request
- * from the queue when they start processing an entry. For this
- * reason it is safe to continue to add links to the top entry
- * for those devices.
- *
- * All other drivers need to jump over the first entry, as that
- * entry may be busy being processed and we thus can't change
- * it.
- */
- if (entry == q->queue_head.next) {
- latency -= blkdev_entry_to_request(entry)->nr_segments;
- if ((entry = entry->next) == &q->queue_head)
- goto get_rq;
- starving = 0;
- }
- }
+ /*
+ * The scsi disk and cdrom drivers completely remove the request
+ * from the queue when they start processing an entry. For this
+ * reason it is safe to continue to add links to the top entry
+ * for those devices.
+ *
+ * All other drivers need to jump over the first entry, as that
+ * entry may be busy being processed and we thus can't change
+ * it.
+ */
+ if (q->head_active && !q->plugged)
+ head = head->next;
- prev = NULL;
- do {
+ while ((entry = entry->prev) != head && !starving) {
req = blkdev_entry_to_request(entry);
+ if (!req->q)
+ break;
+ latency += req->nr_segments;
+ if (elevator_sequence_before(req->elevator_sequence, sequence))
+ starving = 1;
+ if (latency < 0)
+ continue;
if (req->sem)
continue;
@@ -833,20 +682,20 @@ static inline void __make_request(request_queue_t * q, int rw,
* this
*/
if(!(q->back_merge_fn)(q, req, bh, max_segments))
- continue;
+ break;
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
req->nr_sectors += count;
drive_stat_acct(req, count, 0);
- elevator_merge_after(q, req, latency);
+ elevator_merge_after(elevator, req, latency);
/* Can we now merge this req with the next? */
- attempt_merge(q, req, max_sectors, max_segments);
+ attempt_back_merge(q, req, max_sectors, max_segments);
/* or to the beginning? */
} else if (req->sector - count == sector) {
- if (!prev && starving)
- continue;
+ if (starving)
+ break;
/*
* The merge_fn is a more advanced way
* of accomplishing the same task. Instead
@@ -860,7 +709,7 @@ static inline void __make_request(request_queue_t * q, int rw,
* this
*/
if(!(q->front_merge_fn)(q, req, bh, max_segments))
- continue;
+ break;
bh->b_reqnext = req->bh;
req->bh = bh;
req->buffer = bh->b_data;
@@ -869,10 +718,9 @@ static inline void __make_request(request_queue_t * q, int rw,
req->nr_sectors += count;
drive_stat_acct(req, count, 0);
- elevator_merge_before(q, req, latency);
+ elevator_merge_before(elevator, req, latency);
- if (prev)
- attempt_merge(q, prev, max_sectors, max_segments);
+ attempt_front_merge(q, head, req, max_sectors, max_segments);
} else
continue;
@@ -880,9 +728,7 @@ static inline void __make_request(request_queue_t * q, int rw,
spin_unlock_irqrestore(&io_request_lock,flags);
return;
- } while (prev = req,
- (latency -= req->nr_segments) >= 0 &&
- (entry = entry->next) != &q->queue_head);
+ }
/* find an unused request. */
get_rq:
@@ -899,31 +745,10 @@ get_rq:
req = __get_request_wait(max_req, bh->b_rdev);
spin_lock_irqsave(&io_request_lock,flags);
- /* lock got dropped so revalidate elevator */
- empty = 1;
- if (!list_empty(&q->queue_head)) {
- empty = 0;
- __latency = orig_latency;
- __entry = seek_to_not_starving_chunk(q, &__latency, &__starving);
- }
- }
- /*
- * Dont start the IO if the buffer has been
- * invalidated meanwhile. (we have to do this
- * within the io request lock and atomically
- * before adding the request, see buffer.c's
- * insert_into_queues_exclusive() function.
- */
- if (!test_bit(BH_Req, &bh->b_state)) {
- req->rq_status = RQ_INACTIVE;
- spin_unlock_irqrestore(&io_request_lock,flags);
- /*
- * A fake 'everything went ok' completion event.
- * The bh doesnt matter anymore, but we should not
- * signal errors to RAID levels.
- */
- bh->b_end_io(bh, 1);
- return;
+ /* revalidate elevator */
+ head = &q->queue_head;
+ if (q->head_active && !q->plugged)
+ head = head->next;
}
/* fill up the request-info, and add it to the queue */
@@ -939,8 +764,8 @@ get_rq:
req->bh = bh;
req->bhtail = bh;
req->q = q;
- __add_request(q, req, empty, __entry, __latency, __starving);
- elevator_account_request(q, req);
+ add_request(q, req, head, orig_latency);
+ elevator_account_request(elevator, req);
spin_unlock_irqrestore(&io_request_lock, flags);
return;
@@ -1166,10 +991,10 @@ int __init blk_dev_init(void)
#ifdef CONFIG_ISP16_CDI
isp16_init();
#endif CONFIG_ISP16_CDI
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_IDE)
ide_init(); /* this MUST precede hd_init */
#endif
-#ifdef CONFIG_BLK_DEV_HD
+#if defined(CONFIG_IDE) && defined(CONFIG_BLK_DEV_HD)
hd_init();
#endif
#ifdef CONFIG_BLK_DEV_PS2
diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c
index e7df93ac6..fe0d33a77 100644
--- a/drivers/cdrom/aztcd.c
+++ b/drivers/cdrom/aztcd.c
@@ -1726,10 +1726,9 @@ int __init aztcd_init(void)
{ printk("aztcd: no AZTECH CD-ROM drive found\n");
return -EIO;
}
- for (count = 0; count < AZT_TIMEOUT; count++);
- { count=count*2; /* delay a bit */
- count=count/2;
- }
+
+ for (count = 0; count < AZT_TIMEOUT; count++);
+
if ((st=getAztStatus())==-1)
{ printk("aztcd: Drive Status Error Status=%x\n",st);
return -EIO;
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 18f867c04..21720c81a 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -219,7 +219,7 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
fi
dep_tristate ' Zoran ZR36057/36060 Video For Linux' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV $CONFIG_PCI
dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN
- dep_tristate ' Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI
+ dep_tristate ' Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C
fi
endmenu
diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c
index 19e252968..66dfc0fe5 100644
--- a/drivers/char/bttv.c
+++ b/drivers/char/bttv.c
@@ -719,12 +719,14 @@ static void init_PXC200(struct bttv *btv)
/* ----------------------------------------------------------------------- */
+/* for some vendors it is just the PCI ID */
static struct VENDOR {
int id;
char *name;
} vendors[] = {
{ 0x0001, "ATI Technologies Inc" },
{ 0x10b4, "STB Systems Inc" },
+ { 0x1118, "Terratec" },
{ 0x13eb, "Hauppauge Computer Works Inc" },
{ 0x1461, "Avermedia" },
{ 0x1850, "Chronos" },
@@ -743,6 +745,7 @@ static struct CARD {
} cards[] = {
{ 0x0001, 0x1002, BTTV_HAUPPAUGE878, "TV Wonder" },
{ 0x10b4, 0x2636, BTTV_HAUPPAUGE878, "???" },
+ { 0x1118, 0x153b, BTTV_TERRATVALUE, "TV Value" },
{ 0x13eb, 0x0070, BTTV_HAUPPAUGE878, "WinTV" },
{ 0x1461, 0x0002, BTTV_AVERMEDIA98, "TVCapture 98" },
{ 0x1850, 0x1851, BTTV_CHRONOS_VS2, "Video Shuttle II" },
@@ -2018,6 +2021,7 @@ static int bttv_open(struct video_device *dev, int flags)
btv->gbuf[i].stat = GBUFFER_UNUSED;
burst(0);
+ set_pll(btv);
btv->user++;
up(&btv->lock);
MOD_INC_USE_COUNT;
@@ -2235,6 +2239,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
if (btv->win.norm != v.mode) {
btv->win.norm = v.mode;
down(&btv->lock);
+ set_pll(btv);
make_vbitab(btv);
bt848_set_winsize(btv);
up(&btv->lock);
@@ -2866,6 +2871,7 @@ static struct video_device vbi_template=
static int radio_open(struct video_device *dev, int flags)
{
struct bttv *btv = (struct bttv *)(dev-1);
+ unsigned long v;
down(&btv->lock);
if (btv->user)
@@ -2873,6 +2879,8 @@ static int radio_open(struct video_device *dev, int flags)
btv->user++;
btv->radio = 1;
+ v = 400*16;
+ call_i2c_clients(btv,VIDIOCSFREQ,&v);
call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
bt848_muxsel(btv,0);
up(&btv->lock);
diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h
index ab3d88ab5..3a2cd7e21 100644
--- a/drivers/char/bttv.h
+++ b/drivers/char/bttv.h
@@ -21,7 +21,7 @@
#ifndef _BTTV_H_
#define _BTTV_H_
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,21)
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,22)
#include <linux/types.h>
#include <linux/wait.h>
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 9d8bf6cb0..93e710188 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -369,7 +369,7 @@ static u8 build_attr(int currcons, u8 _color, u8 _intensity, u8 _blink, u8 _unde
static void update_attr(int currcons)
{
attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
- video_erase_char = (build_attr(currcons, color, 1, 0, 0, decscnm) << 8) | ' ';
+ video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
}
/* Note: inverting the screen twice should revert to the original state */
diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c
index 865681956..ebf0671f4 100644
--- a/drivers/char/drm/auth.c
+++ b/drivers/char/drm/auth.c
@@ -1,6 +1,5 @@
/* auth.c -- IOCTLs for authentication -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -23,9 +22,9 @@
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c
index a71d6dde9..1bb7bd612 100644
--- a/drivers/char/drm/bufs.c
+++ b/drivers/char/drm/bufs.c
@@ -1,8 +1,7 @@
/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 12:11:11 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,13 +23,12 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
#define __NO_VERSION__
-#include <linux/config.h>
#include "drmP.h"
#include "linux/un.h"
diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c
index d7f8bdf2b..a8919d83d 100644
--- a/drivers/char/drm/context.c
+++ b/drivers/char/drm/context.c
@@ -1,6 +1,5 @@
/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c
index ea08a859e..0ec14ede5 100644
--- a/drivers/char/drm/dma.c
+++ b/drivers/char/drm/dma.c
@@ -1,6 +1,5 @@
/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:39 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.7 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c
index c26953c1d..19e5da3b7 100644
--- a/drivers/char/drm/drawable.c
+++ b/drivers/char/drm/drawable.c
@@ -1,6 +1,5 @@
/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h
index 320db51eb..fe0f8defe 100644
--- a/drivers/char/drm/drm.h
+++ b/drivers/char/drm/drm.h
@@ -1,6 +1,5 @@
/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 17:11:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
* Acknowledgements:
* Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index fce2df7ec..3a371c23d 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -1,6 +1,5 @@
/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:06:49 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -50,6 +49,10 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
#include "drm.h"
#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then
@@ -475,6 +478,7 @@ extern int drm_fasync(int fd, struct file *filp, int on);
extern ssize_t drm_read(struct file *filp, char *buf, size_t count,
loff_t *off);
extern int drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
/* Mapping support (vm.c) */
#if LINUX_VERSION_CODE < 0x020317
diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c
index 24b17356b..a823db356 100644
--- a/drivers/char/drm/fops.c
+++ b/drivers/char/drm/fops.c
@@ -1,8 +1,7 @@
/* fops.c -- File operations for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 10:26:26 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +23,9 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
@@ -222,3 +222,13 @@ int drm_write_string(drm_device_t *dev, const char *s)
wake_up_interruptible(&dev->buf_readers);
return 0;
}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+
+ poll_wait(filp, &dev->buf_readers, wait);
+ if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+ return 0;
+}
diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c
index 3b1592180..1f8c0a7de 100644
--- a/drivers/char/drm/gamma_dma.c
+++ b/drivers/char/drm/gamma_dma.c
@@ -1,6 +1,5 @@
/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:37 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.9 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c
index 028772f26..6df4440f3 100644
--- a/drivers/char/drm/gamma_drv.c
+++ b/drivers/char/drm/gamma_drv.c
@@ -1,8 +1,7 @@
/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,12 +23,11 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "gamma_drv.h"
EXPORT_SYMBOL(gamma_init);
@@ -52,6 +50,7 @@ static struct file_operations gamma_fops = {
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice gamma_misc = {
diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h
index 15e77dca9..a87655cb9 100644
--- a/drivers/char/drm/gamma_drv.h
+++ b/drivers/char/drm/gamma_drv.h
@@ -1,6 +1,5 @@
/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c
index f416a99af..7a0115e86 100644
--- a/drivers/char/drm/init.c
+++ b/drivers/char/drm/init.c
@@ -1,6 +1,5 @@
/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c
index 886ef661c..13bb60659 100644
--- a/drivers/char/drm/ioctl.c
+++ b/drivers/char/drm/ioctl.c
@@ -1,6 +1,5 @@
/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
* Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c
index b84561f2e..212ed18ed 100644
--- a/drivers/char/drm/lists.c
+++ b/drivers/char/drm/lists.c
@@ -1,6 +1,5 @@
/* lists.c -- Buffer list handling routines -*- linux-c -*-
* Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -154,7 +153,7 @@ int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
buf->list = DRM_LIST_FREE;
do {
old = bl->next;
- bl->next = old;
+ buf->next = old;
prev = cmpxchg(&bl->next, old, buf);
if (++count > DRM_LOOPING_LIMIT) {
DRM_ERROR("Looping\n");
diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c
index e8c1eff10..2523eb21a 100644
--- a/drivers/char/drm/lock.c
+++ b/drivers/char/drm/lock.c
@@ -1,6 +1,5 @@
/* lock.c -- IOCTLs for locking -*- linux-c -*-
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:04:44 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c
index af8d510b5..a778a1539 100644
--- a/drivers/char/drm/memory.c
+++ b/drivers/char/drm/memory.c
@@ -1,6 +1,5 @@
/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
* Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 10:28:18 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c
index 33a5b20e8..4d5d1a964 100644
--- a/drivers/char/drm/proc.c
+++ b/drivers/char/drm/proc.c
@@ -1,6 +1,5 @@
/* proc.c -- /proc support for DRM -*- linux-c -*-
* Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
- * Revised: Fri Dec 3 09:44:16 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
@@ -79,26 +78,26 @@ int drm_proc_init(drm_device_t *dev)
struct proc_dir_entry *ent;
int i, j;
- drm_root = create_proc_entry("graphics", S_IFDIR, NULL);
+ drm_root = create_proc_entry("dri", S_IFDIR, NULL);
if (!drm_root) {
- DRM_ERROR("Cannot create /proc/graphics\n");
+ DRM_ERROR("Cannot create /proc/dri\n");
return -1;
}
/* Instead of doing this search, we should
- add some global support for /proc/graphics. */
+ add some global support for /proc/dri. */
for (i = 0; i < 8; i++) {
- sprintf(drm_slot_name, "graphics/%d", i);
+ sprintf(drm_slot_name, "dri/%d", i);
drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
if (!drm_dev_root) {
DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
}
if (drm_dev_root->nlink == 2) break;
drm_dev_root = NULL;
}
if (!drm_dev_root) {
- DRM_ERROR("Cannot find slot in /proc/graphics\n");
+ DRM_ERROR("Cannot find slot in /proc/dri\n");
return -1;
}
@@ -112,7 +111,7 @@ int drm_proc_init(drm_device_t *dev)
remove_proc_entry(drm_proc_list[i].name,
drm_dev_root);
remove_proc_entry(drm_slot_name, NULL);
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
return -1;
}
ent->read_proc = drm_proc_list[i].f;
@@ -135,7 +134,7 @@ int drm_proc_cleanup(void)
}
remove_proc_entry(drm_slot_name, NULL);
}
- remove_proc_entry("graphics", NULL);
+ remove_proc_entry("dri", NULL);
remove_proc_entry(DRM_NAME, NULL);
}
drm_root = drm_dev_root = NULL;
diff --git a/drivers/char/drm/tdfx_context.c b/drivers/char/drm/tdfx_context.c
index 0c3c541d0..22bf59ff3 100644
--- a/drivers/char/drm/tdfx_context.c
+++ b/drivers/char/drm/tdfx_context.c
@@ -1,6 +1,5 @@
/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
* Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:39:56 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c
index f56e2af95..82b2ac9a2 100644
--- a/drivers/char/drm/tdfx_drv.c
+++ b/drivers/char/drm/tdfx_drv.c
@@ -1,8 +1,7 @@
/* tdfx.c -- tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:35 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -23,17 +22,15 @@
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * $PI$
- * $XFree86$
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
-#include <linux/config.h>
#include "drmP.h"
#include "tdfx_drv.h"
-EXPORT_SYMBOL(tdfx_init);
-EXPORT_SYMBOL(tdfx_cleanup);
#define TDFX_NAME "tdfx"
#define TDFX_DESC "tdfx"
@@ -53,6 +50,7 @@ static struct file_operations tdfx_fops = {
mmap: drm_mmap,
read: drm_read,
fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice tdfx_misc = {
@@ -542,6 +540,12 @@ int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
#endif
}
}
+
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY/4;
+ }
+
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
#if DRM_DMA_HISTOGRAM
@@ -582,6 +586,11 @@ int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
}
}
+ if (lock.context != tdfx_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY;
+ }
+
return 0;
}
diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h
index bdff05ee1..4c0c3282b 100644
--- a/drivers/char/drm/tdfx_drv.h
+++ b/drivers/char/drm/tdfx_drv.h
@@ -1,6 +1,5 @@
/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com
- * Revised: Sat Oct 9 23:38:19 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All rights reserved.
@@ -24,8 +23,9 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI$
- * $XFree86$
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Daryll Strauss <daryll@precisioninsight.com>
*
*/
diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c
index d649a6e75..b4c4c5bbf 100644
--- a/drivers/char/drm/vm.c
+++ b/drivers/char/drm/vm.c
@@ -1,6 +1,5 @@
/* vm.c -- Memory mapping for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Mon Dec 6 16:54:35 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* All Rights Reserved.
@@ -24,8 +23,8 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
*/
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 95a6c4370..212525df7 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -18,90 +18,12 @@
#include <linux/serial.h>
#include <linux/mm.h>
#include <asm/semaphore.h>
-#include <linux/version.h>
-
-
-#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300 /* less than 2.2.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source
- work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2,
- and provide for compatibility stuff here if possible. */
-
-/* Some 200 days (on intel) */
-#define MAX_SCHEDULE_TIMEOUT ((long)(~0UL>>1))
-
-
-#ifndef MODULE
-
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c)
-{
- memcpy_fromfs(to, from, c);
- return 0;
-}
-
-
-#define capable(x) suser()
-
-#define queue_task queue_task_irq_off
-#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current) (current->signal & ~current->blocked)
-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2) (((long)t1-t2) > 0)
-
-#define test_and_set_bit(nr, addr) set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI -1
-#define ASYNC_SPD_WARP -1
-
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
- to the "name" field that does exist. As long as the assignments are
- done in the right order, there is nothing to worry about. */
-#define driver_name name
-
-
-/* Should be in a header somewhere. */
-#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN 15 /* in hardware - output and input */
-#endif
-
-#endif
-
-#ifndef TWO_ZERO
-/* This include is new with 2.2 (and required!) */
#include <asm/uaccess.h>
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is
- the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#include "generic_serial.h"
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/generic_serial.h>
+#define DEBUG
static char * tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);
@@ -118,8 +40,6 @@ int gs_debug = 0;
#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n")
#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n")
-
-
#if NEW_WRITE_LOCKING
#define DECL /* Nothing */
#define LOCKIT down (& port->port_write_sem);
@@ -130,6 +50,28 @@ int gs_debug = 0;
#define RELEASEIT restore_flags (flags)
#endif
+#define RS_EVENT_WRITE_WAKEUP 1
+
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+ int i, j, ch;
+
+ for (i=0;i<len;i+=16) {
+ printk ("%08x ", (int) addr+i);
+ for (j=0;j<16;j++) {
+ printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+ }
+ for (j=0;j<16;j++) {
+ ch = addr[j+i];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ }
+}
+#else
+#define my_hd(addr,len)
+#endif
void gs_put_char(struct tty_struct * tty, unsigned char ch)
@@ -393,16 +335,19 @@ int gs_real_chars_in_buffer(struct tty_struct *tty)
if (!tty) return 0;
port = tty->driver_data;
+ if (!port->rd) return 0;
+ if (!port->rd->chars_in_buffer) return 0;
+
func_exit ();
return port->xmit_cnt + port->rd->chars_in_buffer (port);
}
-static void gs_wait_tx_flushed (void * ptr, int timeout)
+static int gs_wait_tx_flushed (void * ptr, int timeout)
{
struct gs_port *port = ptr;
long end_jiffies;
- int jiffies_to_transmit, charsleft;
+ int jiffies_to_transmit, charsleft = 0, rv = 0;
int to, rcib;
func_enter();
@@ -416,7 +361,7 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
func_exit();
- return; /* This is an error which we don't know how to handle. */
+ return -EINVAL; /* This is an error which we don't know how to handle. */
}
gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 1\n");
@@ -426,8 +371,8 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
if(rcib <= 0) {
gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
- func_exit();
- return;
+ func_exit();
+ return rv;
}
gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 3\n");
@@ -460,14 +405,18 @@ static void gs_wait_tx_flushed (void * ptr, int timeout)
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(jiffies_to_transmit);
- if (signal_pending (current))
+ if (signal_pending (current)) {
+ gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
+ rv = -EINTR;
break;
+ }
}
gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft);
current->state = TASK_RUNNING;
func_exit();
+ return rv;
}
@@ -541,7 +490,7 @@ void gs_start(struct tty_struct * tty)
void gs_shutdown_port (struct gs_port *port)
{
long flags;
-
+ func_enter();
if (!(port->flags & ASYNC_INITIALIZED))
return;
@@ -560,6 +509,7 @@ void gs_shutdown_port (struct gs_port *port)
port->flags &= ~ASYNC_INITIALIZED;
restore_flags (flags);
+ func_exit();
}
@@ -746,8 +696,10 @@ void gs_close(struct tty_struct * tty, struct file * filp)
func_exit();
return;
}
+
if (!port->tty) {
- printk (KERN_WARNING "gs: Odd: port->tty is NULL\n");
+ /* This seems to happen when this is called from vhangup. */
+ gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n");
port->tty = tty;
}
@@ -771,6 +723,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
port->count = 0;
}
if (port->count) {
+ gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count);
restore_flags(flags);
func_exit ();
return;
@@ -802,6 +755,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
port->rd->disable_rx_interrupts (port);
+ /* close has no way of returning "EINTR", so discard return value */
if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
gs_wait_tx_flushed (port, port->closing_wait);
@@ -812,8 +766,12 @@ void gs_close(struct tty_struct * tty, struct file * filp)
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
+
port->event = 0;
+ port->rd->close (port);
+ port->rd->shutdown_port (port);
port->tty = 0;
+
if (port->blocked_open) {
if (port->close_delay) {
current->state = TASK_INTERRUPTIBLE;
@@ -825,8 +783,6 @@ void gs_close(struct tty_struct * tty, struct file * filp)
ASYNC_CLOSING | ASYNC_INITIALIZED);
wake_up_interruptible(&port->close_wait);
- port->rd->close (port);
- port->rd->shutdown_port (port);
restore_flags(flags);
func_exit ();
}
@@ -842,7 +798,7 @@ void gs_set_termios (struct tty_struct * tty,
struct termios * old_termios)
{
struct gs_port *port = tty->driver_data;
- int baudrate, tmp;
+ int baudrate, tmp, rv;
struct termios *tiosp;
func_enter();
@@ -867,7 +823,7 @@ void gs_set_termios (struct tty_struct * tty,
&& (tiosp->c_line == old_termios->c_line)
&& (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) {
gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n");
- return;
+ return /* 0 */;
}
} else
gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: "
@@ -923,9 +879,11 @@ void gs_set_termios (struct tty_struct * tty,
/* We should really wait for the characters to be all sent before
changing the settings. -- CAL */
- gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ if (rv < 0) return /* rv */;
- port->rd->set_real_termios(port);
+ rv = port->rd->set_real_termios(port);
+ if (rv < 0) return /* rv */;
if ((!old_termios ||
(old_termios->c_cflag & CRTSCTS)) &&
@@ -943,7 +901,7 @@ void gs_set_termios (struct tty_struct * tty,
#endif
func_exit();
- return;
+ return /* 0 */;
}
@@ -1067,3 +1025,15 @@ void gs_getserial(struct gs_port *port, struct serial_struct *sp)
copy_to_user(sp, &sio, sizeof(struct serial_struct));
}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return 0;
+}
+
+int cleanup_module (void)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/char/generic_serial.h b/drivers/char/generic_serial.h
deleted file mode 100644
index 2e44bee95..000000000
--- a/drivers/char/generic_serial.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * generic_serial.h
- *
- * Copyright (C) 1998 R.E.Wolff@BitWizard.nl
- *
- * written for the SX serial driver.
- * Contains the code that should be shared over all the serial drivers.
- *
- * Version 0.1 -- December, 1998.
- */
-
-#ifndef GENERIC_SERIAL_H
-#define GENERIC_SERIAL_H
-
-#define RS_EVENT_WRITE_WAKEUP 0
-
-struct real_driver {
- void (*disable_tx_interrupts) (void *);
- void (*enable_tx_interrupts) (void *);
- void (*disable_rx_interrupts) (void *);
- void (*enable_rx_interrupts) (void *);
- int (*get_CD) (void *);
- void (*shutdown_port) (void*);
- void (*set_real_termios) (void*);
- int (*chars_in_buffer) (void*);
- void (*close) (void*);
- void (*hungup) (void*);
- void (*getserial) (void*, struct serial_struct *sp);
-};
-
-
-
-struct gs_port {
- int magic;
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- /* struct semaphore port_write_sem; */
- int flags;
- struct termios normal_termios;
- struct termios callout_termios;
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
- long session;
- long pgrp;
- int count;
- int blocked_open;
- struct tty_struct *tty;
- int event;
- unsigned short closing_wait;
- int close_delay;
- struct real_driver *rd;
- int wakeup_chars;
- int baud_base;
- int baud;
- int custom_divisor;
-};
-
-
-/* Flags */
-/* Warning: serial.h defines some ASYNC_ flags, they say they are "only"
- used in serial.c, but they are also used in all other serial drivers.
- Make sure they don't clash with these here... */
-#define GS_TX_INTEN 0x00800000
-#define GS_RX_INTEN 0x00400000
-#define GS_ACTIVE 0x00200000
-
-
-
-#define GS_TYPE_NORMAL 1
-#define GS_TYPE_CALLOUT 2
-
-
-#define GS_DEBUG_FLUSH 0x00000001
-#define GS_DEBUG_BTR 0x00000002
-#define GS_DEBUG_TERMIOS 0x00000004
-#define GS_DEBUG_STUFF 0x00000008
-#define GS_DEBUG_CLOSE 0x00000010
-#define GS_DEBUG_FLOW 0x00000020
-
-
-void gs_put_char(struct tty_struct *tty, unsigned char ch);
-int gs_write(struct tty_struct *tty, int from_user,
- const unsigned char *buf, int count);
-int gs_write_room(struct tty_struct *tty);
-int gs_chars_in_buffer(struct tty_struct *tty);
-void gs_flush_buffer(struct tty_struct *tty);
-void gs_flush_chars(struct tty_struct *tty);
-void gs_stop(struct tty_struct *tty);
-void gs_start(struct tty_struct *tty);
-void gs_hangup(struct tty_struct *tty);
-void gs_do_softint(void *private_);
-int block_til_ready(void *port, struct file *filp);
-void gs_close(struct tty_struct *tty, struct file *filp);
-void gs_set_termios (struct tty_struct * tty,
- struct termios * old_termios);
-int gs_init_port(struct gs_port *port);
-int gs_setserial(struct gs_port *port, struct serial_struct *sp);
-void gs_getserial(struct gs_port *port, struct serial_struct *sp);
-
-extern int gs_debug;
-
-#endif
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 3bf3fbf28..e9a34fbf3 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -26,9 +26,6 @@
#include <asm/io.h>
#include <asm/pgalloc.h>
-#ifdef CONFIG_VIDEO_BT848
-extern int i2c_init(void);
-#endif
#ifdef CONFIG_I2C
extern int i2c_init_all(void);
#endif
@@ -52,7 +49,6 @@ extern int videodev_init(void);
#endif
#ifdef CONFIG_FB
extern void fbmem_init(void);
-extern void fbconsole_init(void);
#endif
#ifdef CONFIG_PROM_CONSOLE
extern void prom_con_init(void);
@@ -621,7 +617,6 @@ int __init chr_dev_init(void)
#endif
#if defined (CONFIG_FB)
fbmem_init();
- fbconsole_init();
#endif
#if defined (CONFIG_PROM_CONSOLE)
prom_con_init();
@@ -665,9 +660,6 @@ int __init chr_dev_init(void)
#ifdef CONFIG_FTAPE
ftape_init();
#endif
-#ifdef CONFIG_VIDEO_BT848
- i2c_init();
-#endif
#if defined(CONFIG_ADB)
adbdev_init();
#endif
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index d3687d9f6..53939959f 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -123,6 +123,22 @@ static struct file_operations misc_fops = {
open: misc_open,
};
+/**
+ * misc_register - register a miscellaneous device
+ * @misc: device structure
+ *
+ * Register a miscellaneous device with the kernel. If the minor
+ * number is set to MISC_DYNAMIC_MINOR a minor number is assigned
+ * and placed in the minor field of the structure. For other cases
+ * the minor number requested is used.
+ *
+ * The structure passed is linked into the kernel and may not be
+ * destroyed until it has been unregistered
+ *
+ * A zero is returned on success and a negative errno code for
+ * failure.
+ */
+
int misc_register(struct miscdevice * misc)
{
static devfs_handle_t devfs_handle = NULL;
@@ -158,6 +174,16 @@ int misc_register(struct miscdevice * misc)
return 0;
}
+/**
+ * misc_deregister - unregister a miscellaneous device
+ * @misc: device to unregister
+ *
+ * Unregister a miscellaneous device that was previously
+ * successfully registered with misc_register. Success
+ * is indicated by a zero return, a negative errno code
+ * indicates an error.
+ */
+
int misc_deregister(struct miscdevice * misc)
{
int i = misc->minor;
diff --git a/drivers/char/mixcomwd.c b/drivers/char/mixcomwd.c
index 199818ebf..5894353e3 100644
--- a/drivers/char/mixcomwd.c
+++ b/drivers/char/mixcomwd.c
@@ -24,10 +24,13 @@
* Version 0.3.1 (99/06/22):
* - allow module removal while internal timer is active,
* print warning about probable reset
+ *
+ * Version 0.4 (99/11/15):
+ * - support for one more type board
*
*/
-#define VERSION "0.3.1"
+#define VERSION "0.4"
#include <linux/module.h>
#include <linux/config.h>
@@ -46,11 +49,13 @@
static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
#define MIXCOM_WATCHDOG_OFFSET 0xc10
-#define MIXCOM_ID1 0x11
-#define MIXCOM_ID2 0x13
+#define MIXCOM_ID 0x11
+#define FLASHCOM_WATCHDOG_OFFSET 0x4
+#define FLASHCOM_ID 0x18
static int mixcomwd_opened;
-static int mixcomwd_port;
+
+static int watchdog_port;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
static int mixcomwd_timer_alive;
@@ -59,7 +64,7 @@ static struct timer_list mixcomwd_timer;
static void mixcomwd_ping(void)
{
- outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET);
+ outb_p(55,watchdog_port);
return;
}
@@ -183,40 +188,61 @@ static int __init mixcomwd_checkcard(int port)
{
int id;
- if(check_region(port,1)) {
+ if(check_region(port+MIXCOM_WATCHDOG_OFFSET,1)) {
return 0;
}
id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
- if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) {
+ if(id!=MIXCOM_ID) {
return 0;
}
return 1;
}
-
+static int __init flashcom_checkcard(int port)
+{
+ int id;
+
+ if(check_region(port + FLASHCOM_WATCHDOG_OFFSET,1)) {
+ return 0;
+ }
+
+ id=inb_p(port + FLASHCOM_WATCHDOG_OFFSET);
+ if(id!=FLASHCOM_ID) {
+ return 0;
+ }
+ return 1;
+ }
+
void __init mixcomwd_init(void)
{
int i;
int found=0;
- for (i = 0; mixcomwd_ioports[i] != 0; i++) {
+ for (i = 0; !found && mixcomwd_ioports[i] != 0; i++) {
if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
found = 1;
- mixcomwd_port = mixcomwd_ioports[i];
- break;
+ watchdog_port = mixcomwd_ioports[i] + MIXCOM_WATCHDOG_OFFSET;
}
}
-
+
+ /* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */
+ for (i = 0x300; !found && i < 0x380; i+=0x8) {
+ if (flashcom_checkcard(i)) {
+ found = 1;
+ watchdog_port = i + FLASHCOM_WATCHDOG_OFFSET;
+ }
+ }
+
if (!found) {
printk("mixcomwd: No card detected, or port not available.\n");
return;
}
- request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog");
-
+ request_region(watchdog_port,1,"MixCOM watchdog");
+
misc_register(&mixcomwd_miscdev);
- printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port);
+ printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",VERSION,watchdog_port);
}
#ifdef MODULE
@@ -236,7 +262,7 @@ void cleanup_module(void)
mixcomwd_timer_alive=0;
}
#endif
- release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1);
+ release_region(watchdog_port,1);
misc_deregister(&mixcomwd_miscdev);
}
#endif
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 0da69c55c..f447dbbbc 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -17,6 +17,10 @@
*
* This file may be redistributed under the terms of the GNU Public
* License.
+ *
+ * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of
+ * the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
+ * who actually finally proved there really was a race.
*/
#include <linux/types.h>
@@ -59,11 +63,18 @@
static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
+ unsigned long flags;
+ /*
+ * The problem of stomping on the buffers ends here.
+ * Why didn't anyone see this one comming? --AJK
+ */
+ spin_lock_irqsave(&tty->read_lock, flags);
if (tty->read_cnt < N_TTY_BUF_SIZE) {
tty->read_buf[tty->read_head] = c;
tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
tty->read_cnt++;
}
+ spin_unlock_irqrestore(&tty->read_lock, flags);
}
/*
@@ -86,7 +97,11 @@ static void check_unthrottle(struct tty_struct * tty)
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
check_unthrottle(tty);
@@ -114,14 +129,19 @@ void n_tty_flush_buffer(struct tty_struct * tty)
*/
ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
- if (tty->icanon) {
- if (!tty->canon_data) return 0;
+ unsigned long flags;
+ ssize_t n = 0;
- return (tty->canon_head > tty->read_tail) ?
+ spin_lock_irqsave(&tty->read_lock, flags);
+ if (!tty->icanon) {
+ n = tty->read_cnt;
+ } else if (tty->canon_data) {
+ n = (tty->canon_head > tty->read_tail) ?
tty->canon_head - tty->read_tail :
tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
}
- return tty->read_cnt;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
+ return n;
}
/*
@@ -283,6 +303,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
{
enum { ERASE, WERASE, KILL } kill_type;
int head, seen_alnums;
+ unsigned long flags;
if (tty->read_head == tty->canon_head) {
/* opost('\a', tty); */ /* what do you think? */
@@ -294,15 +315,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
finish_erasing(tty);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -324,8 +349,10 @@ static void eraser(unsigned char c, struct tty_struct *tty)
else if (seen_alnums)
break;
}
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = head;
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!tty->erasing) {
@@ -658,11 +685,13 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
+ unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
+ spin_lock_irqsave(&tty->read_lock, cpuflags);
i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head));
memcpy(tty->read_buf + tty->read_head, cp, i);
@@ -676,6 +705,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
+ spin_unlock_irqrestore(&tty->read_lock, cpuflags);
} else {
for (i=count, p = cp, f = fp; i; i--, p++) {
if (f)
@@ -850,15 +880,20 @@ static inline int copy_from_read_buf(struct tty_struct *tty,
{
int retval;
ssize_t n;
+ unsigned long flags;
retval = 0;
+ spin_lock_irqsave(&tty->read_lock, flags);
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
mb();
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
@@ -875,6 +910,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
ssize_t retval = 0;
ssize_t size;
long timeout;
+ unsigned long flags;
do_it_again:
@@ -993,9 +1029,11 @@ do_it_again:
eol = test_and_clear_bit(tty->read_tail,
&tty->read_flags);
c = tty->read_buf[tty->read_tail];
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (!eol || (c != __DISABLED_CHAR)) {
put_user(c, b++);
@@ -1094,7 +1132,9 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
nr -= num;
if (nr == 0)
break;
+ current->state = TASK_RUNNING;
get_user(c, b);
+ current->state = TASK_INTERRUPTIBLE;
if (opost(c, tty) < 0)
break;
b++; nr--;
@@ -1102,7 +1142,9 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
} else {
+ current->state = TASK_RUNNING;
c = tty->driver.write(tty, 1, b, nr);
+ current->state = TASK_INTERRUPTIBLE;
if (c < 0) {
retval = c;
goto break_out;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 59853da1f..4336009fc 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -537,6 +537,17 @@ static int pp_release (struct inode * inode, struct file * file)
unsigned int minor = MINOR (inode->i_rdev);
struct pp_struct *pp = file->private_data;
+ if (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
+ if (!(pp->flags & PP_CLAIMED)) {
+ parport_claim_or_block (pp->pdev);
+ pp->flags |= PP_CLAIMED;
+ }
+ parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
+ printk (KERN_DEBUG CHRDEV
+ "%x: negotiated back to compatibility mode because "
+ "user-space forgot\n", minor);
+ }
+
if (pp->flags & PP_CLAIMED) {
parport_release (pp->pdev);
printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 8f25926e9..9331852e1 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -174,7 +174,9 @@ static int pty_write(struct tty_struct * tty, int from_user,
}
up(&tty->flip.pty_sem);
} else {
- c = MIN(count, to->ldisc.receive_room(to));
+ c = to->ldisc.receive_room(to);
+ if (c > count)
+ c = count;
to->ldisc.receive_buf(to, buf, 0, c);
}
diff --git a/drivers/char/radio-gemtek.c b/drivers/char/radio-gemtek.c
index 523ac0955..8b53dbd0d 100644
--- a/drivers/char/radio-gemtek.c
+++ b/drivers/char/radio-gemtek.c
@@ -266,7 +266,7 @@ static int __init gemtek_init(void)
{
if(io==-1)
{
- printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n");
+ printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
return -EINVAL;
}
@@ -299,7 +299,7 @@ static int __init gemtek_init(void)
MODULE_AUTHOR("Jonas Munsin");
MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
MODULE_PARM(io, "i");
-MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (or 0x248 for the combined sound/radiocard))");
+MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
EXPORT_NO_SYMBOLS;
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index 9c3f2e2e5..2d160acfd 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -1648,8 +1648,13 @@ static void change_speed(struct async_struct *info,
serial_outp(info, UART_FCR, fcr); /* set fcr */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
info->LCR = cval; /* Save LCR */
- if (info->state->type != PORT_16750)
+ if (info->state->type != PORT_16750) {
+ if (fcr & UART_FCR_ENABLE_FIFO) {
+ /* emulated UARTs (Lucent Venus 167x) need two steps */
+ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
serial_outp(info, UART_FCR, fcr); /* set fcr */
+ }
restore_flags(flags);
}
@@ -4515,9 +4520,24 @@ int __init rs_init(void)
}
/*
- * register_serial and unregister_serial allows for serial ports to be
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
+
+/**
+ * register_serial - configure a 16x50 serial port at runtime
+ * @req: request structure
+ *
+ * Configure the serial port specified by the request. If the
+ * port exists and is in use an error is returned. If the port
+ * is not currently in the table it is added.
+ *
+ * The port is then probed and if neccessary the IRQ is autodetected
+ * If this fails an error is returned.
+ *
+ * On success the port is ready to use and the line number is returned.
+ */
+
int register_serial(struct serial_struct *req)
{
int i;
@@ -4575,7 +4595,7 @@ int register_serial(struct serial_struct *req)
if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
state->irq = detect_uart_irq(state);
- printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+ printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
state->line + SERIAL_DEV_OFFSET,
state->iomem_base ? "iomem" : "port",
state->iomem_base ? (unsigned long)state->iomem_base :
@@ -4588,6 +4608,15 @@ int register_serial(struct serial_struct *req)
return state->line + SERIAL_DEV_OFFSET;
}
+/**
+ * unregister_serial - deconfigure a 16x50 serial port
+ * @line: line to deconfigure
+ *
+ * The port specified is deconfigured and its resources are freed. Any
+ * user of the port is disconnected as if carrier was dropped. Line is
+ * the port number returned by register_serial.
+ */
+
void unregister_serial(int line)
{
unsigned long flags;
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index 7caf1058a..1b8583034 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -4,7 +4,7 @@
* This driver will also support the older SI, and XIO cards.
*
*
- * (C) 1998 R.E.Wolff@BitWizard.nl
+ * (C) 1998 - 2000 R.E.Wolff@BitWizard.nl
*
* Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous
* version of this driver. Some fragments may have been copied. (none
@@ -33,6 +33,16 @@
*
* Revision history:
* $Log: sx.c,v $
+ * Revision 1.32 2000/03/07 90:00:00 wolff,pvdl
+ * - Fixed some sx_dprintk typos
+ * - added detection for an invalid board/module configuration
+ *
+ * Revision 1.31 2000/03/06 12:00:00 wolff,pvdl
+ * - Added support for EISA
+ *
+ * Revision 1.30 2000/01/21 17:43:06 wolff
+ * - Added support for SX+
+ *
* Revision 1.26 1999/08/05 15:22:14 wolff
* - Port to 2.3.x
* - Reformatted to Linus' liking.
@@ -185,8 +195,8 @@
* */
-#define RCS_ID "$Id: sx.c,v 1.26 1999/08/05 15:22:14 wolff Exp $"
-#define RCS_REV "$Revision: 1.26 $"
+#define RCS_ID "$Id: sx.c,v 1.32 2000/03/07 17:01:02 wolff, pvdl Exp $"
+#define RCS_REV "$Revision: 1.32 $"
#include <linux/module.h>
@@ -221,6 +231,9 @@
#include "sxboards.h"
#include "sxwindow.h"
+#include <linux/generic_serial.h>
+#include <linux/compatmac.h>
+#include "sx.h"
/* I don't think that this driver can handle more than 256 ports on
@@ -228,149 +241,16 @@
if you want more than 4 boards. */
-/* ************************************************************** */
-/* * This section can be removed when 2.0 becomes outdated.... * */
-/* ************************************************************** */
-
-
-#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */
-#define TWO_ZERO
-#else
-#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */
-#warning "Please use a 2.2.x kernel. "
-#else
-#if LINUX_VERSION_CODE < 0x020300 /* less than 2.3.x */
-#define TWO_TWO
-#else
-#define TWO_THREE
-#endif
-#endif
-#endif
-
-#ifdef TWO_ZERO
-
-/* Here is the section that makes the 2.2 compatible driver source
- work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2,
- and provide for compatibility stuff here if possible. */
-
-#include <linux/bios32.h>
-
-#define Get_user(a,b) a = get_user(b)
-#define Put_user(a,b) 0,put_user(a,b)
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-
-static inline int copy_from_user(void *to,const void *from, int c)
-{
- memcpy_fromfs(to, from, c);
- return 0;
-}
-
-#define pci_present pcibios_present
-#define pci_read_config_word pcibios_read_config_word
-#define pci_read_config_dword pcibios_read_config_dword
-
-static inline unsigned char get_irq (unsigned char bus, unsigned char fn)
-{
- unsigned char t;
- pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t);
- return t;
-}
-
-static inline void *ioremap(unsigned long base, long length)
-{
- if (base < 0x100000) return (void *)base;
- return vremap (base, length);
-}
-
-#define my_iounmap(x, b) (((long)x<0x100000)?0:vfree ((void*)x))
-
-#define capable(x) suser()
-
-#define queue_task queue_task_irq_off
-#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current) (current->signal & ~current->blocked)
-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2) (((long)t1-t2) > 0)
-
-
-#define test_and_set_bit(nr, addr) set_bit(nr, addr)
-#define test_and_clear_bit(nr, addr) clear_bit(nr, addr)
-
-/* Not yet implemented on 2.0 */
-#define ASYNC_SPD_SHI -1
-#define ASYNC_SPD_WARP -1
-
-
-/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it
- to the "name" field that does exist. As long as the assignments are
- done in the right order, there is nothing to worry about. */
-#define driver_name name
-
-/* Should be in a header somewhere. They are in tty.h on 2.2 */
-#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */
-#define TTY_HW_COOK_IN 15 /* in hardware - output and input */
-
-/* The return type of a "close" routine. */
-#define INT void
-#define NO_ERROR /* Nothing */
-
-#else
-
-/* The 2.2.x compatibility section. */
-#include <asm/uaccess.h>
-
-
-#define Get_user(a,b) get_user(a,b)
-#define Put_user(a,b) put_user(a,b)
-#define get_irq(pdev) pdev->irq
-
-#define INT int
-#define NO_ERROR 0
-
-#define my_iounmap(x,b) (iounmap((char *)(b)))
-
-#endif
-
-#ifndef TWO_THREE
-/* These are new in 2.3. The source now uses 2.3 syntax, and here is
- the compatibility define... */
-#define wait_queue_head_t struct wait_queue *
-#define DECLARE_MUTEX(name) struct semaphore name = MUTEX
-#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
-
-#endif
-
-#undef RS_EVENT_WRITE_WAKEUP
-#define RS_EVENT_WRITE_WAKEUP 0
-
-
-#include "generic_serial.h"
-#include "sx.h"
-
-
-/* ************************************************************** */
-/* * End of compatibility section.. * */
-/* ************************************************************** */
-
-
/* Why the hell am I defining these here? */
#define SX_TYPE_NORMAL 1
#define SX_TYPE_CALLOUT 2
-#ifndef SX_NORMAL_MAJOR
-/* This allows overriding on the compiler commandline, or in a "major.h"
- include or something like that */
-#define SX_NORMAL_MAJOR 32
-#define SX_CALLOUT_MAJOR 33
-#endif
-
#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8
#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
#endif
-
/* Configurable options:
(Don't be too sure that it'll work if you toggle them) */
@@ -410,19 +290,17 @@ static void sx_disable_rx_interrupts (void * ptr);
static void sx_enable_rx_interrupts (void * ptr);
static int sx_get_CD (void * ptr);
static void sx_shutdown_port (void * ptr);
-static void sx_set_real_termios (void *ptr);
+static int sx_set_real_termios (void *ptr);
static void sx_hungup (void *ptr);
static void sx_close (void *ptr);
static int sx_chars_in_buffer (void * ptr);
static int sx_init_board (struct sx_board *board);
static int sx_init_portstructs (int nboards, int nports);
static int sx_fw_ioctl (struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
+ unsigned int cmd, unsigned long arg);
static int sx_fw_open(struct inode *inode, struct file *filp);
static INT sx_fw_release(struct inode *inode, struct file *filp);
static int sx_init_drivers(void);
-void my_hd (unsigned char *addr, int len);
-
static struct tty_driver sx_driver, sx_callout_driver;
@@ -458,11 +336,13 @@ int sx_slowpoll = 0;
int sx_maxints = 100;
/* These are the only open spaces in my computer. Yours may have more
- or less.... */
+ or less.... -- REW
+ duh: Card at 0xa0000 is possible on HP Netserver?? -- pvdl
+*/
int sx_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000,
0xc8000, 0xd8000, 0xe8000};
int si_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000,
- 0xc8000, 0xd8000, 0xe8000};
+ 0xc8000, 0xd8000, 0xe8000, 0xa0000};
#define NR_SX_ADDRS (sizeof(sx_probe_addrs)/sizeof (int))
#define NR_SI_ADDRS (sizeof(si_probe_addrs)/sizeof (int))
@@ -474,6 +354,8 @@ int sx_irqmask = -1;
#ifndef TWO_ZERO
#ifdef MODULE
+MODULE_PARM(sx_probe_addrs, "i");
+MODULE_PARM(si_probe_addrs, "i");
MODULE_PARM(sx_poll, "i");
MODULE_PARM(sx_slowpoll, "i");
MODULE_PARM(sx_maxints, "i");
@@ -580,6 +462,27 @@ static inline int sx_paranoia_check(struct sx_port const * port,
#define TIMEOUT_2 1000000
+#ifdef DEBUG
+static void my_hd (unsigned char *addr, int len)
+{
+ int i, j, ch;
+
+ for (i=0;i<len;i+=16) {
+ printk ("%08x ", (int) addr+i);
+ for (j=0;j<16;j++) {
+ printk ("%02x %s", addr[j+i], (j==7)?" ":"");
+ }
+ for (j=0;j<16;j++) {
+ ch = addr[j+i];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ }
+}
+#endif
+
+
+
/* This needs redoing for Alpha -- REW -- Done. */
inline void write_sx_byte (struct sx_board *board, int offset, u8 byte)
@@ -675,6 +578,8 @@ int sx_reset (struct sx_board *board)
printk (KERN_INFO "sx: Card doesn't respond to reset....\n");
return 0;
}
+ } else if (IS_EISA_BOARD(board)) {
+ outb(board->irq<<4, board->eisa_base+0xc02);
} else {
/* Gory details of the SI/ISA board */
write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_SET);
@@ -746,9 +651,12 @@ int sx_start_board (struct sx_board *board)
{
if (IS_SX_BOARD (board)) {
write_sx_byte (board, SX_CONFIG, SX_CONF_BUSEN);
+ } else if (IS_EISA_BOARD(board)) {
+ write_sx_byte(board, SI2_EISA_OFF, SI2_EISA_VAL);
+ outb((board->irq<<4)|4, board->eisa_base+0xc02);
} else {
/* Don't bug me about the clear_set.
- I haven't the foggiest idea what it's about -- REW*/
+ I haven't the foggiest idea what it's about -- REW */
write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_CLEAR);
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
}
@@ -769,6 +677,8 @@ int sx_start_interrupts (struct sx_board *board)
write_sx_byte (board, SX_CONFIG, SX_IRQ_REG_VAL (board) |
SX_CONF_BUSEN |
SX_CONF_HOSTIRQ);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base+0xc03);
} else {
switch (board->irq) {
case 11:write_sx_byte (board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET);break;
@@ -834,6 +744,18 @@ int mod_compat_type (int module_type)
return module_type >> 4;
}
+static void sx_reconfigure_port(struct sx_port *port)
+{
+ if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
+ if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
+ printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
+ }
+ } else {
+ sx_dprintk (SX_DEBUG_TERMIOS,
+ "sx: Not sending reconfigure: port isn't open (%02x).\n",
+ sx_read_channel_byte (port, hi_hstat));
+ }
+}
static void sx_setsignals (struct sx_port *port, int dtr, int rts)
{
@@ -954,7 +876,7 @@ static void sx_set_baud (struct sx_port *port)
/* Simon Allen's version of this routine was 225 lines long. 85 is a lot
better. -- REW */
-static void sx_set_real_termios (void *ptr)
+static int sx_set_real_termios (void *ptr)
{
struct sx_port *port = ptr;
@@ -1008,16 +930,7 @@ static void sx_set_real_termios (void *ptr)
sx_write_channel_byte (port, hi_txoff, STOP_CHAR (port->gs.tty));
sx_write_channel_byte (port, hi_rxoff, STOP_CHAR (port->gs.tty));
- if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) {
- if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
- printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n");
- }
- } else {
- sx_dprintk (SX_DEBUG_TERMIOS,
- "sx: Not sending reconfigure: port isn't open (%02x).\n",
- sx_read_channel_byte (port, hi_hstat));
- }
-
+ sx_reconfigure_port(port);
/* Tell line discipline whether we will do input cooking */
if(I_OTHER(port->gs.tty)) {
@@ -1045,6 +958,7 @@ static void sx_set_real_termios (void *ptr)
O_OTHER(port->gs.tty));
/* port->c_dcd = sx_get_CD (port); */
func_exit ();
+ return 0;
}
@@ -1102,7 +1016,7 @@ void sx_transmit_chars (struct sx_port *port)
/* Don't copy pas the end of the source buffer */
if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail)
- c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
+ c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) \n",
c, SERIAL_XMIT_SIZE- port->gs.xmit_tail);
@@ -1331,6 +1245,9 @@ static void sx_interrupt (int irq, void *ptr, struct pt_regs *regs)
sx_write_board_word (board, cc_int_pending, 0);
if (IS_SX_BOARD (board)) {
write_sx_byte (board, SX_RESET_IRQ, 1);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base+0xc03);
+ write_sx_word(board, 8, 0);
} else {
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR);
write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
@@ -1474,6 +1391,7 @@ static void sx_shutdown_port (void * ptr)
port->gs.flags &= ~ GS_ACTIVE;
if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
sx_setsignals (port, 0, 0);
+ sx_reconfigure_port(port);
}
func_exit();
@@ -1711,7 +1629,7 @@ int do_memtest_w (struct sx_board *board, int min, int max)
static int sx_fw_ioctl (struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
int rc = 0;
int *descr = (int *)arg, i;
@@ -1754,7 +1672,11 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
board = &boards[arg];
break;
case SXIO_GET_TYPE:
- rc = IS_SX_BOARD (board)? SX_TYPE_SX:SX_TYPE_SI;
+ rc = -ENOENT; /* If we manage to miss one, return error. */
+ if (IS_SX_BOARD (board)) rc = SX_TYPE_SX;
+ if (IS_CF_BOARD (board)) rc = SX_TYPE_CF;
+ if (IS_SI_BOARD (board)) rc = SX_TYPE_SI;
+ if (IS_EISA_BOARD (board)) rc = SX_TYPE_SI;
sx_dprintk (SX_DEBUG_FIRMWARE, "returning type= %d\n", rc);
break;
case SXIO_DO_RAMTEST:
@@ -1786,7 +1708,7 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
for (i=0;i<nbytes;i += SX_CHUNK_SIZE) {
copy_from_user (tmp, (char *)data+i,
(i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
- memcpy_toio ((char *) (board->base + offset + i), tmp,
+ memcpy_toio ((char *) (board->base2 + offset + i), tmp,
(i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE);
}
@@ -1830,6 +1752,9 @@ static int sx_fw_ioctl (struct inode *inode, struct file *filp,
case SXIO_GETGSDEBUG:
rc = gs_debug;
break;
+ case SXIO_GETNPORTS:
+ rc = sx_nports;
+ break;
default:
printk (KERN_WARNING "Unknown ioctl on firmware device (%x).\n", cmd);
break;
@@ -1886,6 +1811,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : -1),
((ival & TIOCM_RTS) ? 1 : -1));
+ sx_reconfigure_port(port);
}
break;
case TIOCMBIC:
@@ -1894,6 +1820,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 0 : -1),
((ival & TIOCM_RTS) ? 0 : -1));
+ sx_reconfigure_port(port);
}
break;
case TIOCMSET:
@@ -1902,6 +1829,7 @@ static int sx_ioctl (struct tty_struct * tty, struct file * filp,
Get_user(ival, (unsigned int *) arg);
sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : 0),
((ival & TIOCM_RTS) ? 1 : 0));
+ sx_reconfigure_port(port);
}
break;
@@ -1980,13 +1908,17 @@ static int sx_init_board (struct sx_board *board)
board->flags |= SX_BOARD_INITIALIZED;
+ if (read_sx_byte (board, 0))
+ /* CF boards may need this. */
+ write_sx_byte(board,0, 0);
+
/* This resets the processor again, to make sure it didn't do any
foolish things while we were downloading the image */
if (!sx_reset (board))
return 0;
sx_start_board (board);
-
+ udelay (10);
if (!sx_busy_wait_neq (board, 0, 0xff, 0)) {
printk (KERN_ERR "sx: Ooops. Board won't initialize.\n");
return 0;
@@ -2050,7 +1982,8 @@ static int sx_init_board (struct sx_board *board)
chans=0;
break;
}
- if (IS_SI_BOARD(board) && (mod_compat_type(type) == 4)) {
+ if ((IS_EISA_BOARD(board) ||
+ IS_SI_BOARD(board)) && (mod_compat_type(type) == 4)) {
printk (KERN_ERR "sx: This is an invalid configuration.\n"
"Don't use SXDCs on an SI/XIO adapter.\n");
chans=0;
@@ -2147,52 +2080,56 @@ int probe_sx (struct sx_board *board)
int i;
func_enter();
- sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n",
- board->base + SX_VPD_ROM);
- if (sx_debug & SX_DEBUG_PROBE)
- my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
+ if (!IS_CF_BOARD (board)) {
+ sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n",
+ board->base + SX_VPD_ROM);
- p = (char *) &vpdp;
- for (i=0;i< sizeof (struct vpd_prom);i++)
- *p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd ((char *)(board->base + SX_VPD_ROM), 0x40);
- if (sx_debug & SX_DEBUG_PROBE)
- my_hd ((char *)&vpdp, 0x20);
+ p = (char *) &vpdp;
+ for (i=0;i< sizeof (struct vpd_prom);i++)
+ *p++ = read_sx_byte (board, SX_VPD_ROM + i*2);
- sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd ((char *)&vpdp, 0x20);
- if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
- sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n",
- vpdp.identifier);
- return 0;
+ sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n");
+
+ if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
+ sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n",
+ vpdp.identifier);
+ return 0;
+ }
}
printheader ();
- printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
- printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ",
- vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
- printk ( "Manufactured: %d/%d\n",
- 1970 + vpdp.myear, vpdp.mweek);
+ if (!IS_CF_BOARD (board)) {
+ printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base);
+ printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ",
+ vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
+ printk ( "Manufactured: %d/%d\n",
+ 1970 + vpdp.myear, vpdp.mweek);
- if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
- (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
- /* This might be a bit harsh. This was the primary reason the
- SX/ISA card didn't work at first... */
- printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
- return (0);
- }
+ if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) &&
+ (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
+ /* This might be a bit harsh. This was the primary reason the
+ SX/ISA card didn't work at first... */
+ printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n");
+ return (0);
+ }
- if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
- if (board->base & 0x8000) {
- printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
- printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+ if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) {
+ if (board->base & 0x8000) {
+ printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base);
+ printk (KERN_WARNING "sx: Read sx.txt for more info.\n");
+ }
}
}
-
board->nports = -1;
/* This resets the processor, and keeps it off the bus. */
@@ -2225,9 +2162,11 @@ int probe_si (struct sx_board *board)
if (sx_debug & SX_DEBUG_PROBE)
my_hd ((char *)(board->base + SI2_ISA_ID_BASE), 0x8);
- for (i=0;i<8;i++) {
- if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
- return 0;
+ if (!IS_EISA_BOARD(board)) {
+ for (i=0;i<8;i++) {
+ if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) {
+ return 0;
+ }
}
}
@@ -2449,7 +2388,7 @@ void fix_sx_pci (PDEV, struct sx_board *board)
unsigned int t;
#define CNTRL_REG_OFFSET 0x50
-#define CNTRL_REG_GOODVALUE 0x00260000
+#define CNTRL_REG_GOODVALUE 0x18260000
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase);
hwbase &= PCI_BASE_ADDRESS_MEM_MASK;
@@ -2472,6 +2411,7 @@ int sx_init(void)
{
int i;
int found = 0;
+ int eisa_slot;
struct sx_board *board;
#ifdef CONFIG_PCI
@@ -2518,22 +2458,35 @@ int sx_init(void)
tshort = (tint >> 16) & 0xffff;
sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x.\n", tint);
/* sx_dprintk (SX_DEBUG_PROBE, "pdev = %d/%d (%x)\n", pdev, tint); */
- if (tshort != 0x0200) {
+ if ((tshort != 0x0200) && (tshort != 0x0300)) {
sx_dprintk (SX_DEBUG_PROBE, "But it's not an SX card (%d)...\n",
tshort);
continue;
}
board = &boards[found];
- pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint);
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= (tshort == 0x200)?SX_PCI_BOARD:
+ SX_CFPCI_BOARD;
+
+ /* CF boards use base address 3.... */
+ if (IS_CF_BOARD (board))
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_3,
+ &tint);
+ else
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2,
+ &tint);
board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK;
- board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
+ board->base2 =
+ board->base = (ulong) ioremap(board->hw_base, WINDOW_LEN (board));
+ /* Most of the stuff on the CF board is offset by
+ 0x18000 .... */
+ if (IS_CF_BOARD (board)) board->base += 0x18000;
+
board->irq = get_irq (pdev);
- board->flags &= ~SX_BOARD_TYPE;
- board->flags |= SX_PCI_BOARD;
- sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d).\n",
- tint, boards[found].base, board->irq);
+ sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d) %x.\n",
+ tint, boards[found].base, board->irq, board->flags);
if (probe_sx (board)) {
found++;
@@ -2547,6 +2500,7 @@ int sx_init(void)
for (i=0;i<NR_SX_ADDRS;i++) {
board = &boards[found];
board->hw_base = sx_probe_addrs[i];
+ board->base2 =
board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN);
board->flags &= ~SX_BOARD_TYPE;
board->flags |= SX_ISA_BOARD;
@@ -2562,6 +2516,7 @@ int sx_init(void)
for (i=0;i<NR_SI_ADDRS;i++) {
board = &boards[found];
board->hw_base = si_probe_addrs[i];
+ board->base2 =
board->base = (ulong) ioremap(board->hw_base, SI2_ISA_WINDOW_LEN);
board->flags &= ~SX_BOARD_TYPE;
board->flags |= SI_ISA_BOARD;
@@ -2574,6 +2529,34 @@ int sx_init(void)
}
}
+ sx_dprintk(SX_DEBUG_PROBE, "Probing for EISA cards\n");
+ for(eisa_slot=0x1000; eisa_slot<0x10000; eisa_slot+=0x1000)
+ {
+ if((inb(eisa_slot+0xc80)==0x4d) &&
+ (inb(eisa_slot+0xc81)==0x98))
+ {
+ sx_dprintk(SX_DEBUG_PROBE, "%s : Signature found in EISA slot %d, Product %d Rev %d\n",
+ "XIO", (eisa_slot>>12), inb(eisa_slot+0xc82), inb(eisa_slot+0xc83));
+
+ board = &boards[found];
+ board->eisa_base = eisa_slot;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SI_EISA_BOARD;
+
+ board->hw_base = (((inb(0xc01+eisa_slot) << 8) + inb(0xc00+eisa_slot)) << 16);
+ board->base2 =
+ board->base = (ulong) ioremap(board->hw_base, SI2_EISA_WINDOW_LEN);
+
+ sx_dprintk(SX_DEBUG_PROBE, "IO hw_base address: %x\n", board->hw_base);
+ sx_dprintk(SX_DEBUG_PROBE, "base: %x\n", board->base);
+ board->irq = inb(board->eisa_base+0xc02)>>4;
+ sx_dprintk(SX_DEBUG_PROBE, "IRQ: %d\n", board->irq);
+
+ probe_si(board);
+
+ found++;
+ }
+ }
if (found) {
printk (KERN_INFO "sx: total of %d boards detected.\n", found);
@@ -2588,7 +2571,7 @@ int sx_init(void)
}
-
+#ifdef MODULE
void cleanup_module(void)
{
int i;
@@ -2622,52 +2605,4 @@ void cleanup_module(void)
kfree (sx_termios_locked);
func_exit();
}
-
-
-#ifdef DEBUG
-void my_hd (unsigned char *addr, int len)
-{
- int i, j, ch;
-
- for (i=0;i<len;i+=16) {
- printk ("%08x ", (int) addr+i);
- for (j=0;j<16;j++) {
- printk ("%02x %s", addr[j+i], (j==7)?" ":"");
- }
- for (j=0;j<16;j++) {
- ch = addr[j+i];
- printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
- }
- printk ("\n");
- }
-}
#endif
-
-#ifdef MODULE
-#undef func_enter
-#undef func_exit
-
-#include "generic_serial.c"
-#endif
-
-
-/*
- * Anybody who knows why this doesn't work for me, please tell me -- REW.
- * Snatched from scsi.c (fixed one spelling error):
- * Overrides for Emacs so that we follow Linus' tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/char/sx.h b/drivers/char/sx.h
index e046482e4..662b27ca2 100644
--- a/drivers/char/sx.h
+++ b/drivers/char/sx.h
@@ -37,7 +37,9 @@ struct sx_port {
struct sx_board {
int magic;
unsigned int base;
+ unsigned int base2;
unsigned int hw_base;
+ int eisa_base;
int port_base; /* Number of the first port */
struct sx_port *ports;
int nports;
@@ -65,19 +67,27 @@ struct vpd_prom {
#define MOD_RS232DB25MALE 0x0a
#endif
-
-#define SX_BOARD_PRESENT 0x00000001
+#define SI_ISA_BOARD 0x00000001
#define SX_ISA_BOARD 0x00000002
#define SX_PCI_BOARD 0x00000004
-#define SI_ISA_BOARD 0x00000008
-#define SX_BOARD_INITIALIZED 0x00000010
-#define SX_IRQ_ALLOCATED 0x00000020
+#define SX_CFPCI_BOARD 0x00000008
+#define SX_CFISA_BOARD 0x00000010
+#define SI_EISA_BOARD 0x00000020
+
+#define SX_BOARD_PRESENT 0x00001000
+#define SX_BOARD_INITIALIZED 0x00002000
+#define SX_IRQ_ALLOCATED 0x00004000
+
+#define SX_BOARD_TYPE 0x000000ff
-#define SX_BOARD_TYPE (SX_ISA_BOARD|SX_PCI_BOARD|SI_ISA_BOARD)
+#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_CFPCI_BOARD | \
+ SX_ISA_BOARD | SX_CFISA_BOARD))
-#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_ISA_BOARD))
#define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD)
+#define IS_EISA_BOARD(board) (board->flags & SI_EISA_BOARD)
+
+#define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD))
#define SERIAL_TYPE_NORMAL 1
@@ -168,6 +178,7 @@ struct vpd_prom {
#define SXIO_DO_RAMTEST SPXL(0x07)
#define SXIO_SETGSDEBUG SPXL(0x08)
#define SXIO_GETGSDEBUG SPXL(0x09)
+#define SXIO_GETNPORTS SPXL(0x0a)
#ifndef SXCTL_MISC_MINOR
@@ -175,6 +186,19 @@ struct vpd_prom {
#define SXCTL_MISC_MINOR 167
#endif
+#ifndef SX_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define SX_NORMAL_MAJOR 32
+#define SX_CALLOUT_MAJOR 33
+#endif
+
+
#define SX_TYPE_SX 0x01
#define SX_TYPE_SI 0x02
+#define SX_TYPE_CF 0x03
+
+
+#define WINDOW_LEN(board) (IS_CF_BOARD(board)?0x20000:SX_WINDOW_LEN)
+/* Need a #define for ^^^^^^^ !!! */
diff --git a/drivers/char/sxboards.h b/drivers/char/sxboards.h
index 45636c9e9..2f0f5428e 100644
--- a/drivers/char/sxboards.h
+++ b/drivers/char/sxboards.h
@@ -159,6 +159,7 @@
#define SI2_EISA_OFF 0x42
#define SI2_EISA_VAL 0x01
+#define SI2_EISA_WINDOW_LEN 0x10000
/*****************************************************************************
*********************************** **********************************
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index ed504dcfe..e1e504b79 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1979,6 +1979,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->tq_hangup.routine = do_tty_hangup;
tty->tq_hangup.data = tty;
sema_init(&tty->atomic_read, 1);
+ spin_lock_init(&tty->read_lock);
INIT_LIST_HEAD(&tty->tty_files);
}
@@ -2319,6 +2320,9 @@ void __init tty_init(void)
#ifdef CONFIG_SX
sx_init();
#endif
+#ifdef CONFIG_RIO
+ rio_init();
+#endif
#ifdef CONFIG_8xx
rs_8xx_init();
#endif /* CONFIG_8xx */
diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c
index f0b1aaf4d..0a4e65c57 100644
--- a/drivers/char/videodev.c
+++ b/drivers/char/videodev.c
@@ -24,10 +24,9 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/videodev.h>
+#include <linux/init.h>
-#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
-#endif
#include <asm/system.h>
#include <linux/kmod.h>
@@ -174,20 +173,11 @@ static int video_release(struct inode *inode, struct file *file)
* image ?
*/
-#if LINUX_VERSION_CODE >= 0x020100
static long long video_lseek(struct file * file,
long long offset, int origin)
{
return -ESPIPE;
}
-#else
-static long long video_lseek(struct inode *inode, struct file * file,
- long long offset, int origin)
-{
- return -ESPIPE;
-}
-#endif
-
static int video_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
@@ -210,16 +200,9 @@ static int video_ioctl(struct inode *inode, struct file *file,
*/
-#if LINUX_VERSION_CODE >= 0x020100
int video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-#else
-static int video_mmap(struct inode * ino, struct file * file,
- struct vm_area_struct * vma)
-{
- struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
-#endif
if(vfl->mmap)
return vfl->mmap(vfl, (char *)vma->vm_start,
(unsigned long)(vma->vm_end-vma->vm_start));
@@ -228,8 +211,28 @@ static int video_mmap(struct inode * ino, struct file * file,
extern struct file_operations video_fops;
-/*
- * Video For Linux device drivers request registration here.
+/**
+ * video_register_device - register video4linux devices
+ * @vfd: Video device structure we want to register
+ * @type: type of device to register
+ * FIXME: needs a semaphore on 2.3.x
+ *
+ * The registration code assigns minor numbers based on the type
+ * requested. -ENFILE is returned in all the device slots for this
+ * catetory are full. If not then the minor field is set and the
+ * driver initialize function is called (if non NULL).
+ *
+ * Zero is returned on success.
+ *
+ * Valid types are
+ *
+ * VFL_TYPE_GRABBER - A frame grabber
+ *
+ * VFL_TYPE_VTX - A teletext device
+ *
+ * VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ * VFL_TYPE_RADIO - A radio card
*/
int video_register_device(struct video_device *vfd, int type)
@@ -288,10 +291,14 @@ int video_register_device(struct video_device *vfd, int type)
}
}
sprintf (name, "v4l/%s%d", name_base, i - base);
+ /*
+ * Start the device root only. Anything else
+ * has serious privacy issues.
+ */
vfd->devfs_handle =
devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT,
VIDEO_MAJOR, vfd->minor,
- S_IFCHR | S_IRUGO | S_IWUGO, 0, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
&video_fops, NULL);
return 0;
}
@@ -299,8 +306,12 @@ int video_register_device(struct video_device *vfd, int type)
return -ENFILE;
}
-/*
- * Unregister an unused video for linux device
+/**
+ * video_unregister_device - unregister a video4linux device
+ * @vfd: the device to unregister
+ *
+ * This unregisters the passed device and deassigns the minor
+ * number. Future open calls will be met with errors.
*/
void video_unregister_device(struct video_device *vfd)
@@ -322,16 +333,14 @@ static struct file_operations video_fops=
mmap: video_mmap,
open: video_open,
release: video_release,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
poll: video_poll,
-#endif
};
/*
* Initialise video for linux
*/
-int videodev_init(void)
+int __init videodev_init(void)
{
struct video_init *vfli = video_init_list;
@@ -365,15 +374,10 @@ void cleanup_module(void)
devfs_unregister_chrdev(VIDEO_MAJOR, "video_capture");
}
-
-
-
-
-
-
#endif
-#if LINUX_VERSION_CODE >= 0x020100
EXPORT_SYMBOL(video_register_device);
EXPORT_SYMBOL(video_unregister_device);
-#endif
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers");
diff --git a/drivers/ide/Config.in b/drivers/ide/Config.in
new file mode 100644
index 000000000..bcd17994b
--- /dev/null
+++ b/drivers/ide/Config.in
@@ -0,0 +1,153 @@
+#
+# IDE ATA ATAPI Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'IDE, ATA and ATAPI Block devices'
+
+dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
+if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE $CONFIG_X86
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_IDE
+
+ dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+ dep_mbool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK
+ dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
+ dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+ dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
+
+ comment 'IDE chipset support/bugfixes'
+ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+ dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
+ dep_bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
+ dep_bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
+ if [ "$CONFIG_PCI" = "y" ]; then
+ dep_bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
+ bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+ if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+ bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+ bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+ dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PCI_AUTO
+ define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL $CONFIG_EXPERIMENTAL
+ dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+ dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AEC6210 Tuning support (WIP)' CONFIG_AEC6210_TUNING $CONFIG_BLK_DEV_AEC6210 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' AMD Viper support' CONFIG_BLK_DEV_AMD7409 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD7409_OVERRIDE $CONFIG_BLK_DEV_AMD7409 $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' CMD64X chipset RAID (WIP)' CONFIG_CMD64X_RAID $CONFIG_BLK_DEV_CMD64X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' HPT366 Fast Interrupts (WIP)' CONFIG_HPT366_FIP $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ dep_mbool ' HPT366 mode Three (WIP)' CONFIG_HPT366_MODE3 $CONFIG_BLK_DEV_HPT366 $CONFIG_IDEDMA_PCI_WIP
+ if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
+ dep_mbool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_mbool ' PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO
+ fi
+ dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
+ dep_bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI
+ dep_bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX
+ dep_mbool ' Special Mode Feature (WIP)' CONFIG_PDC202XX_MASTER $CONFIG_BLK_DEV_PDC202XX $CONFIG_IDEDMA_PCI_WIP
+ dep_bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+ dep_bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ dep_bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_IDEDMA_PCI_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
+ bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
+ fi
+ fi
+ if [ "$CONFIG_PMAC" = "y" -o "$CONFIG_ALL_PPC" = "y" ]; then
+ bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
+ dep_bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PMAC_AUTO
+ fi
+ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN
+ dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE
+ dep_bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
+ define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_ICS_AUTO
+ dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
+ fi
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA
+ dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_mbool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI
+ fi
+ if [ "$CONFIG_MAC" = "y" ]; then
+ dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC
+ fi
+
+ bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
+ if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+ comment 'Note: most of these also require special kernel boot parameters'
+ bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+ bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+ bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+ bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+ fi
+ bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+ bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
+ fi
+ fi
+else
+ bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
+ define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_ONLY
+fi
+
+# if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+# "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+# define_bool CONFIG_BLK_DEV_IDEDMA y
+# if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
+# "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
+# define_bool CONFIG_IDEDMA_AUTO y
+# fi
+# else
+# define_bool CONFIG_BLK_DEV_IDEDMA n
+# define_bool CONFIG_IDEDMA_AUTO n
+# fi
+
+if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
+ "$CONFIG_BLK_DEV_AEC6210" = "y" -o \
+ "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \
+ "$CONFIG_BLK_DEV_AMD7409" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD640" = "y" -o \
+ "$CONFIG_BLK_DEV_CMD64X" = "y" -o \
+ "$CONFIG_BLK_DEV_CS5530" = "y" -o \
+ "$CONFIG_BLK_DEV_CY82C693" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT34X" = "y" -o \
+ "$CONFIG_BLK_DEV_HPT366" = "y" -o \
+ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
+ "$CONFIG_BLK_DEV_OPTI621" = "y" -o \
+ "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \
+ "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \
+ "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
+ define_bool CONFIG_BLK_DEV_IDE_MODES y
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+fi
+
+endmenu
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
new file mode 100644
index 000000000..699c48b82
--- /dev/null
+++ b/drivers/ide/Makefile
@@ -0,0 +1,238 @@
+#
+# Makefile for the kernel ata, atapi, and ide block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := ide.a
+L_OBJS := ide-geometry.o
+M_OBJS :=
+MOD_LIST_NAME := IDE_MODULES
+LX_OBJS :=
+MX_OBJS :=
+
+ifeq ($(CONFIG_BLK_DEV_AEC6210),y)
+IDE_OBJS += aec6210.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+IDE_OBJS += ali14xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI15X3),y)
+IDE_OBJS += alim15x3.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_AMD7409),y)
+IDE_OBJS += amd7409.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_BUDDHA),y)
+IDE_OBJS += buddha.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD640),y)
+IDE_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD64X),y)
+IDE_OBJS += cmd64x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CS5530),y)
+IDE_OBJS += cs5530.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CY82C693),y)
+IDE_OBJS += cy82c693.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+IDE_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_FALCON_IDE),y)
+IDE_OBJS += falconide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_GAYLE),y)
+IDE_OBJS += gayle.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_Q40IDE),y)
+IDE_OBJS += q40ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT34X),y)
+IDE_OBJS += hpt34x.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HPT366),y)
+IDE_OBJS += hpt366.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+IDE_OBJS += ht6560b.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
+IDE_OBJS += icside.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
+IDE_OBJS += ide-dma.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
+IDE_OBJS += ide-pci.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
+IDE_OBJS += ide-pnp.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
+IDE_OBJS += ide-pmac.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MAC_IDE),y)
+IDE_OBJS += macide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_NS87415),y)
+IDE_OBJS += ns87415.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
+IDE_OBJS += opti621.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC202XX),y)
+IDE_OBJS += pdc202xx.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PDC4030),y)
+IDE_OBJS += pdc4030.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_PIIX),y)
+IDE_OBJS += piix.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+IDE_OBJS += qd6580.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
+IDE_OBJS += rapide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+IDE_OBJS += rz1000.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SIS5513),y)
+IDE_OBJS += sis5513.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
+IDE_OBJS += sl82c105.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRM290),y)
+IDE_OBJS += trm290.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+IDE_OBJS += umc8672.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y)
+IDE_OBJS += via82cxxx.o
+endif
+
+### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored
+
+ifeq ($(CONFIG_PROC_FS),y)
+IDE_OBJS += ide-proc.o
+endif
+
+###Collect
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+ LX_OBJS += ide.o ide-features.o
+ L_OBJS += ide-probe.o $(IDE_OBJS)
+else
+ ifeq ($(CONFIG_BLK_DEV_IDE),m)
+ MIX_OBJS += ide.o ide-features.o $(IDE_OBJS)
+ M_OBJS += ide-mod.o ide-probe-mod.o
+ endif
+endif
+
+############
+
+ifeq ($(CONFIG_BLK_DEV_IDECS),y)
+L_OBJS += ide-cs.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECS),m)
+ M_OBJS += ide-cs.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
+L_OBJS += ide-disk.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
+ M_OBJS += ide-disk.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+L_OBJS += ide-cd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+ M_OBJS += ide-cd.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
+ M_OBJS += ide-tape.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
+ M_OBJS += ide-floppy.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ide-mod.o: ide.o ide-features.o $(IDE_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-features.o $(IDE_OBJS)
+
+ide-probe-mod.o: ide-probe.o ide-geometry.o
+ $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o
diff --git a/drivers/block/aec6210.c b/drivers/ide/aec6210.c
index cc0aca5fd..cc0aca5fd 100644
--- a/drivers/block/aec6210.c
+++ b/drivers/ide/aec6210.c
diff --git a/drivers/block/ali14xx.c b/drivers/ide/ali14xx.c
index b3ffaa529..b3ffaa529 100644
--- a/drivers/block/ali14xx.c
+++ b/drivers/ide/ali14xx.c
diff --git a/drivers/block/alim15x3.c b/drivers/ide/alim15x3.c
index bc3d2b9e3..bc3d2b9e3 100644
--- a/drivers/block/alim15x3.c
+++ b/drivers/ide/alim15x3.c
diff --git a/drivers/block/amd7409.c b/drivers/ide/amd7409.c
index 7d2018029..7d2018029 100644
--- a/drivers/block/amd7409.c
+++ b/drivers/ide/amd7409.c
diff --git a/drivers/block/buddha.c b/drivers/ide/buddha.c
index 3e1cfcd33..3e1cfcd33 100644
--- a/drivers/block/buddha.c
+++ b/drivers/ide/buddha.c
diff --git a/drivers/block/cmd640.c b/drivers/ide/cmd640.c
index b2077df6d..b2077df6d 100644
--- a/drivers/block/cmd640.c
+++ b/drivers/ide/cmd640.c
diff --git a/drivers/block/cmd64x.c b/drivers/ide/cmd64x.c
index 542ad44a1..542ad44a1 100644
--- a/drivers/block/cmd64x.c
+++ b/drivers/ide/cmd64x.c
diff --git a/drivers/block/cs5530.c b/drivers/ide/cs5530.c
index bb68f7b2e..bb68f7b2e 100644
--- a/drivers/block/cs5530.c
+++ b/drivers/ide/cs5530.c
diff --git a/drivers/block/cy82c693.c b/drivers/ide/cy82c693.c
index cfff0381c..cfff0381c 100644
--- a/drivers/block/cy82c693.c
+++ b/drivers/ide/cy82c693.c
diff --git a/drivers/block/dtc2278.c b/drivers/ide/dtc2278.c
index d8838e111..d8838e111 100644
--- a/drivers/block/dtc2278.c
+++ b/drivers/ide/dtc2278.c
diff --git a/drivers/block/falconide.c b/drivers/ide/falconide.c
index 7bce07517..7bce07517 100644
--- a/drivers/block/falconide.c
+++ b/drivers/ide/falconide.c
diff --git a/drivers/block/gayle.c b/drivers/ide/gayle.c
index 29cceb20e..29cceb20e 100644
--- a/drivers/block/gayle.c
+++ b/drivers/ide/gayle.c
diff --git a/drivers/block/hd.c b/drivers/ide/hd.c
index 5520c17b0..5520c17b0 100644
--- a/drivers/block/hd.c
+++ b/drivers/ide/hd.c
diff --git a/drivers/block/hpt34x.c b/drivers/ide/hpt34x.c
index 425ce35a4..425ce35a4 100644
--- a/drivers/block/hpt34x.c
+++ b/drivers/ide/hpt34x.c
diff --git a/drivers/block/hpt366.c b/drivers/ide/hpt366.c
index d2f2fb433..d2f2fb433 100644
--- a/drivers/block/hpt366.c
+++ b/drivers/ide/hpt366.c
diff --git a/drivers/block/ht6560b.c b/drivers/ide/ht6560b.c
index 6b7d5af72..6b7d5af72 100644
--- a/drivers/block/ht6560b.c
+++ b/drivers/ide/ht6560b.c
diff --git a/drivers/block/icside.c b/drivers/ide/icside.c
index d0e8f8328..d0e8f8328 100644
--- a/drivers/block/icside.c
+++ b/drivers/ide/icside.c
diff --git a/drivers/block/ide-cd.c b/drivers/ide/ide-cd.c
index 641783e9c..641783e9c 100644
--- a/drivers/block/ide-cd.c
+++ b/drivers/ide/ide-cd.c
diff --git a/drivers/block/ide-cd.h b/drivers/ide/ide-cd.h
index 1eb48ef6c..1eb48ef6c 100644
--- a/drivers/block/ide-cd.h
+++ b/drivers/ide/ide-cd.h
diff --git a/drivers/block/ide-cs.c b/drivers/ide/ide-cs.c
index 73d285cb1..73d285cb1 100644
--- a/drivers/block/ide-cs.c
+++ b/drivers/ide/ide-cs.c
diff --git a/drivers/block/ide-disk.c b/drivers/ide/ide-disk.c
index 2ef50f285..2ef50f285 100644
--- a/drivers/block/ide-disk.c
+++ b/drivers/ide/ide-disk.c
diff --git a/drivers/block/ide-dma.c b/drivers/ide/ide-dma.c
index 751424831..751424831 100644
--- a/drivers/block/ide-dma.c
+++ b/drivers/ide/ide-dma.c
diff --git a/drivers/block/ide-features.c b/drivers/ide/ide-features.c
index 3f29ed591..3f29ed591 100644
--- a/drivers/block/ide-features.c
+++ b/drivers/ide/ide-features.c
diff --git a/drivers/block/ide-floppy.c b/drivers/ide/ide-floppy.c
index bee6a4c6c..58eb2411d 100644
--- a/drivers/block/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -1226,10 +1226,10 @@ static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
drive->bios_head = page->heads;
drive->bios_sect = page->sectors;
lba_capacity = floppy->blocks * floppy->block_size;
- if (capacity != lba_capacity) {
- printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
- drive->name, capacity, lba_capacity);
- capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+ if (capacity < lba_capacity) {
+ printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+ "but the drive only handles %d\n",
+ drive->name, lba_capacity, capacity);
floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
}
return 0;
diff --git a/drivers/block/ide-geometry.c b/drivers/ide/ide-geometry.c
index 6ebf20fe1..6ebf20fe1 100644
--- a/drivers/block/ide-geometry.c
+++ b/drivers/ide/ide-geometry.c
diff --git a/drivers/block/ide-pci.c b/drivers/ide/ide-pci.c
index f667ee6dd..f667ee6dd 100644
--- a/drivers/block/ide-pci.c
+++ b/drivers/ide/ide-pci.c
diff --git a/drivers/block/ide-pmac.c b/drivers/ide/ide-pmac.c
index e0803e6fa..e0803e6fa 100644
--- a/drivers/block/ide-pmac.c
+++ b/drivers/ide/ide-pmac.c
diff --git a/drivers/block/ide-pnp.c b/drivers/ide/ide-pnp.c
index ffa3ade56..ffa3ade56 100644
--- a/drivers/block/ide-pnp.c
+++ b/drivers/ide/ide-pnp.c
diff --git a/drivers/block/ide-probe.c b/drivers/ide/ide-probe.c
index 311bcfa25..311bcfa25 100644
--- a/drivers/block/ide-probe.c
+++ b/drivers/ide/ide-probe.c
diff --git a/drivers/block/ide-proc.c b/drivers/ide/ide-proc.c
index 753597e9c..753597e9c 100644
--- a/drivers/block/ide-proc.c
+++ b/drivers/ide/ide-proc.c
diff --git a/drivers/block/ide-tape.c b/drivers/ide/ide-tape.c
index 95e780abf..95e780abf 100644
--- a/drivers/block/ide-tape.c
+++ b/drivers/ide/ide-tape.c
diff --git a/drivers/block/ide.c b/drivers/ide/ide.c
index 326533e7f..9c409dfb6 100644
--- a/drivers/block/ide.c
+++ b/drivers/ide/ide.c
@@ -2587,6 +2587,8 @@ static int ide_ioctl (struct inode *inode, struct file *file,
case BLKFLSBUF:
case BLKSSZGET:
case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
return blk_ioctl(inode->i_rdev, cmd, arg);
default:
diff --git a/drivers/block/ide_modes.h b/drivers/ide/ide_modes.h
index f94d91313..f94d91313 100644
--- a/drivers/block/ide_modes.h
+++ b/drivers/ide/ide_modes.h
diff --git a/drivers/block/macide.c b/drivers/ide/macide.c
index 46a14ba19..46a14ba19 100644
--- a/drivers/block/macide.c
+++ b/drivers/ide/macide.c
diff --git a/drivers/block/ns87415.c b/drivers/ide/ns87415.c
index 0e5675fde..0e5675fde 100644
--- a/drivers/block/ns87415.c
+++ b/drivers/ide/ns87415.c
diff --git a/drivers/block/opti621.c b/drivers/ide/opti621.c
index cc2aa567c..cc2aa567c 100644
--- a/drivers/block/opti621.c
+++ b/drivers/ide/opti621.c
diff --git a/drivers/block/pdc202xx.c b/drivers/ide/pdc202xx.c
index 9ec7c8997..9ec7c8997 100644
--- a/drivers/block/pdc202xx.c
+++ b/drivers/ide/pdc202xx.c
diff --git a/drivers/block/pdc4030.c b/drivers/ide/pdc4030.c
index f42c4946f..f42c4946f 100644
--- a/drivers/block/pdc4030.c
+++ b/drivers/ide/pdc4030.c
diff --git a/drivers/block/pdc4030.h b/drivers/ide/pdc4030.h
index 551785c36..551785c36 100644
--- a/drivers/block/pdc4030.h
+++ b/drivers/ide/pdc4030.h
diff --git a/drivers/block/piix.c b/drivers/ide/piix.c
index 97e57fa55..97e57fa55 100644
--- a/drivers/block/piix.c
+++ b/drivers/ide/piix.c
diff --git a/drivers/block/q40ide.c b/drivers/ide/q40ide.c
index 0f2330370..0f2330370 100644
--- a/drivers/block/q40ide.c
+++ b/drivers/ide/q40ide.c
diff --git a/drivers/block/qd6580.c b/drivers/ide/qd6580.c
index 31781a9f0..31781a9f0 100644
--- a/drivers/block/qd6580.c
+++ b/drivers/ide/qd6580.c
diff --git a/drivers/block/rapide.c b/drivers/ide/rapide.c
index 5905aca41..5905aca41 100644
--- a/drivers/block/rapide.c
+++ b/drivers/ide/rapide.c
diff --git a/drivers/block/rz1000.c b/drivers/ide/rz1000.c
index 455641c1d..455641c1d 100644
--- a/drivers/block/rz1000.c
+++ b/drivers/ide/rz1000.c
diff --git a/drivers/block/sis5513.c b/drivers/ide/sis5513.c
index 942187900..942187900 100644
--- a/drivers/block/sis5513.c
+++ b/drivers/ide/sis5513.c
diff --git a/drivers/block/sl82c105.c b/drivers/ide/sl82c105.c
index 483a98fde..483a98fde 100644
--- a/drivers/block/sl82c105.c
+++ b/drivers/ide/sl82c105.c
diff --git a/drivers/block/trm290.c b/drivers/ide/trm290.c
index fb5e8d1af..fb5e8d1af 100644
--- a/drivers/block/trm290.c
+++ b/drivers/ide/trm290.c
diff --git a/drivers/block/umc8672.c b/drivers/ide/umc8672.c
index 02b581a28..02b581a28 100644
--- a/drivers/block/umc8672.c
+++ b/drivers/ide/umc8672.c
diff --git a/drivers/block/via82cxxx.c b/drivers/ide/via82cxxx.c
index c2ec24d39..c2ec24d39 100644
--- a/drivers/block/via82cxxx.c
+++ b/drivers/ide/via82cxxx.c
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index dab3f99b9..a44153e31 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -2,6 +2,7 @@
*
* (c) Copyright 1998 Red Hat Software Inc
* Written by Alan Cox.
+ * Further debugging by Carl Drougge.
*
* Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
* (for the MCA stuff) written by Wim Dumon.
@@ -15,7 +16,7 @@
*/
static const char *version =
- "3c527.c:v0.07 2000/01/18 Alan Cox (alan@redhat.com)\n";
+ "3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
/**
* DOC: Traps for the unwary
@@ -122,6 +123,7 @@ struct mc32_local
u32 base;
u16 rx_halted;
u16 tx_halted;
+ u16 rx_pending;
u16 exec_pending;
u16 mc_reload_wait; /* a multicast load request is pending */
atomic_t tx_count; /* buffers left */
@@ -451,6 +453,9 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
dev->name, lp->rx_len, lp->tx_len, lp->base);
+
+ if(lp->tx_len > TX_RING_MAX)
+ lp->tx_len = TX_RING_MAX;
dev->open = mc32_open;
dev->stop = mc32_close;
@@ -462,6 +467,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
lp->rx_halted = 1;
lp->tx_halted = 1;
+ lp->rx_pending = 0;
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
@@ -652,6 +658,7 @@ static void mc32_rx_begin(struct net_device *dev)
mc32_ring_poll(dev);
lp->rx_halted=0;
+ lp->rx_pending=0;
}
/**
@@ -944,6 +951,7 @@ static void mc32_timeout(struct net_device *dev)
static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
+ int ioaddr = dev->base_addr;
unsigned long flags;
u16 tx_head;
@@ -967,7 +975,16 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
lp->tx_skb[lp->tx_skb_end] = skb;
lp->tx_skb_end++;
lp->tx_skb_end&=(TX_RING_MAX-1);
+
+ /* TX suspend - shouldnt be needed but apparently is.
+ This is a research item ... */
+
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ lp->tx_box->mbox=0;
+ outb(3, ioaddr+HOST_CMD);
+ /* Transmit now stopped */
+
/* P is the last sending/sent buffer as a pointer */
p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
@@ -990,7 +1007,9 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
p->status = 0;
p->control &= ~(1<<6);
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->tx_box->mbox=0;
+ outb(5, ioaddr+HOST_CMD); /* Restart TX */
restore_flags(flags);
netif_wake_queue(dev);
@@ -1096,11 +1115,16 @@ static void mc32_rx_ring(struct net_device *dev)
base = p->next;
}
while(x++<48);
+
+ /*
+ * Restart ring processing
+ */
while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->rx_box->mbox=0;
lp->rx_box->data[0] = top;
outb(1<<3, ioaddr+HOST_CMD);
+ lp->rx_halted=0;
}
@@ -1123,7 +1147,6 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
struct net_device *dev = dev_id;
struct mc32_local *lp;
int ioaddr, status, boguscount = 0;
- int rx_event = 0;
if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
@@ -1182,7 +1205,15 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
case 0:
break;
case 2: /* RX */
- rx_event=1;
+ lp->rx_pending=1;
+ if(!lp->rx_halted)
+ {
+ /*
+ * Halt ring receive
+ */
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ outb(3<<3, ioaddr+HOST_CMD);
+ }
break;
case 3:
case 4:
@@ -1195,9 +1226,10 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
break;
case 6:
/* Out of RX buffers stat */
- /* Must restart */
lp->net_stats.rx_dropped++;
- rx_event = 1; /* To restart */
+ lp->rx_pending=1;
+ /* Must restart */
+ lp->rx_halted=1;
break;
default:
printk("%s: strange rx ack %d\n",
@@ -1231,11 +1263,17 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
/*
- * Process and restart the receive ring.
+ * Process and restart the receive ring. This has some state
+ * as we must halt the ring to process it and halting the ring
+ * might not occur in the same IRQ handling loop as we issue
+ * the halt.
*/
- if(rx_event)
+ if(lp->rx_pending && lp->rx_halted)
+ {
mc32_rx_ring(dev);
+ lp->rx_pending = 0;
+ }
return;
}
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 839c15ebc..31e8c79b8 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1914,7 +1914,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
EL3WINDOW(4);
mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index b264def30..b62986f83 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -199,11 +199,13 @@ if [ "$CONFIG_FDDI" = "y" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
- if [ "$CONFIG_HIPPI" = "y" ]; then
- tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
- if [ "$CONFIG_ROADRUNNER" != "n" ]; then
- bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ if [ "$CONFIG_INET" = "y" ]; then
+ bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+ if [ "$CONFIG_HIPPI" = "y" ]; then
+ tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
+ if [ "$CONFIG_ROADRUNNER" != "n" ]; then
+ bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ fi
fi
fi
fi
diff --git a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h
index 45e3bc044..844f0ba03 100644
--- a/drivers/net/aironet4500.h
+++ b/drivers/net/aironet4500.h
@@ -454,7 +454,7 @@ struct awc_fid_queue {
};
-extern void
+extern __inline__ void
awc_fid_queue_init(struct awc_fid_queue * queue){
unsigned long flags;
diff --git a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in
index 951cf498d..e42231cbf 100644
--- a/drivers/net/appletalk/Config.in
+++ b/drivers/net/appletalk/Config.in
@@ -3,18 +3,21 @@
#
if [ "$CONFIG_ATALK" != "n" ]; then
- mainmenu_option next_comment
- comment 'Appletalk devices'
- dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
- dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
- if [ "$CONFIG_COPS" != "n" ]; then
- bool ' Dayna firmware support' CONFIG_COPS_DAYNA
- bool ' Tangent firmware support' CONFIG_COPS_TANGENT
- fi
- dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
- if [ "$CONFIG_IPDDP" != "n" ]; then
- bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
- bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
- fi
- endmenu
+ mainmenu_option next_comment
+ comment 'Appletalk devices'
+ bool 'Appletalk interfaces support' CONFIG_APPLETALK
+ if [ "$CONFIG_APPLETALK" != "n" ]; then
+ dep_tristate ' Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_APPLETALK
+ dep_tristate ' COPS LocalTalk PC support' CONFIG_COPS $CONFIG_APPLETALK
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool ' Dayna firmware support' CONFIG_COPS_DAYNA
+ bool ' Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
+ dep_tristate ' Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_APPLETALK
+ if [ "$CONFIG_IPDDP" != "n" ]; then
+ bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+ bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
+ fi
+ fi
+ endmenu
fi
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index 073a7392a..493c2f6c5 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -39,7 +39,7 @@
/*
* This version is for use with contiguous buffers on Linux-derived systems.
*
- * ==FILEVERSION 970607==
+ * ==FILEVERSION 20000226==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the number above to the
@@ -58,32 +58,9 @@
#endif
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
+#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/byteorder.h>
-
-#include <linux/if.h>
-
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
#include <linux/ppp_defs.h>
@@ -1177,17 +1154,18 @@ static struct compressor ppp_bsd_compress = {
* Module support routines
*************************************************************/
-int
-init_module(void)
+int bsdcomp_init(void)
{
- int answer = ppp_register_compressor (&ppp_bsd_compress);
+ int answer = ppp_register_compressor(&ppp_bsd_compress);
if (answer == 0)
- printk (KERN_INFO "PPP BSD Compression module registered\n");
+ printk(KERN_INFO "PPP BSD Compression module registered\n");
return answer;
}
-void
-cleanup_module(void)
+void bsdcomp_cleanup(void)
{
- ppp_unregister_compressor (&ppp_bsd_compress);
+ ppp_unregister_compressor(&ppp_bsd_compress);
}
+
+module_init(bsdcomp_init);
+module_exit(bsdcomp_cleanup);
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index b42b8d390..5a5f3cc62 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -852,6 +852,8 @@ static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd)
return -EFAULT;
return (0);
case DGRS_SETFILTER:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (ioc.port > privN->bcomm->bc_nports)
return -EINVAL;
if (ioc.filter >= NFILTERS)
@@ -1188,8 +1190,11 @@ dgrs_probe1(struct net_device *dev)
priv->intrcnt = 0;
for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
+ {
+ barrier(); /* gcc 2.95 needs this */
if (priv->intrcnt >= 2)
break;
+ }
if (priv->intrcnt < 2)
{
printk("%s: Not interrupting on IRQ %d (%d)\n",
diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c
index 01a32afd5..87e5ccd7f 100644
--- a/drivers/net/dmfe.c
+++ b/drivers/net/dmfe.c
@@ -1,15 +1,26 @@
/*
- dmfe.c: Version 1.26
+ dmfe.c: Version 1.28 01/18/2000
- A Davicom DM9102 fast ethernet driver for Linux.
+ A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+ Copyright (C) 1997 Sten Wang
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 1.
Compiler command:
"gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall
- -Wstrict-prototypes -O6 -c dmfe.c"
+ -Wstrict-prototypes -O6 -c dmfe.c"
+ OR
+ "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall
+ -Wstrict-prototypes -O6 -c dmfe.c"
The following steps teach you how to active DM9102 board:
1. Used the upper compiler command to compile dmfe.c
@@ -25,7 +36,7 @@
"route add -net 172.22.3.0 eth0"
5. Well done. Your DM9102 adapter actived now.
- Author: Sten Wang, E-mail: sten_wang@davicom.com.tw
+ Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
Date: 10/28,1998
@@ -44,6 +55,9 @@
Check and fix on 64bit and big endian boxes.
Sort out the PCI latency.
+ (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+
+ Cleaned up for kernel merge by Alan Cox (alan@redhat.com)
*/
#include <linux/module.h>
@@ -60,38 +74,41 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/version.h>
-
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include <linux/delay.h>
+
#include <asm/processor.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
#define DMFE_SUCC 0
#define DM9102_IO_SIZE 0x80
-#define TX_FREE_DESC_CNT 0x1 /* Tx packet count */
+#define DM9102A_IO_SIZE 0x100
+#define TX_FREE_DESC_CNT 0xc /* Tx packet count */
+#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
#define DM910X_RESET 1
-#define CR6_DEFAULT 0x002c0000 /* SF, MII, HD */
+#define CR6_DEFAULT 0x00280000 /* SF, HD */
#define CR7_DEFAULT 0x1a2cd
#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
#define MAX_PACKET_SIZE 1514
#define DMFE_MAX_MULTICAST 14
-#define RX_MAX_TRAFFIC 0x5000
+#define RX_MAX_TRAFFIC 0x14000
#define MAX_CHECK_PACKET 0x8000
#define DMFE_10MHF 0
@@ -100,8 +117,8 @@
#define DMFE_100MFD 5
#define DMFE_AUTO 8
-#define DMFE_TIMER_WUT jiffies+HZ*1 /* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT HZ*2 /* tx packet time-out time */
+#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*1.5 /* tx packet time-out time 1.5 s" */
#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
@@ -109,7 +126,7 @@
#define DELAY_1US udelay(1) /* udelay scale 1 usec */
-#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
/* CR9 definition: SROM/MII */
@@ -125,6 +142,9 @@
#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
+#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+
+
/* Structure/enum declaration ------------------------------- */
struct tx_desc {
u32 tdes0, tdes1, tdes2, tdes3;
@@ -149,13 +169,14 @@ struct dmfe_board_info {
struct pci_dev *net_dev; /* PCI device */
- u32 ioaddr; /* I/O base address */
+ unsigned long ioaddr; /* I/O base address */
+ u32 cr0_data;
u32 cr5_data;
u32 cr6_data;
u32 cr7_data;
u32 cr15_data;
-
-/* descriptor pointer */
+
+ /* descriptor pointer */
unsigned char *buf_pool_ptr; /* Tx buffer pool memory */
unsigned char *buf_pool_start; /* Tx buffer pool align dword */
unsigned char *desc_pool_ptr; /* descriptor pool memory */
@@ -166,9 +187,12 @@ struct dmfe_board_info {
struct rx_desc *rx_insert_ptr;
struct rx_desc *rx_ready_ptr; /* packet come pointer */
u32 tx_packet_cnt; /* transmitted packet count */
+ u32 tx_queue_cnt; /* wait to send packet count */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
+ u16 phy_id2; /* Phyxcer ID2 */
+
u8 media_mode; /* user specify media mode */
u8 op_mode; /* real work media mode */
u8 phy_addr;
@@ -190,14 +214,14 @@ enum dmfe_offsets {
enum dmfe_CR6_bits {
CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
- CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000
+ CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000,
+ CR6_NO_PURGE = 0x20000000
};
/* Global variable declaration ----------------------------- */
-
static int dmfe_debug = 0;
static unsigned char dmfe_media_mode = 8;
-static struct net_device *dmfe_root_dev = NULL; /* First device */
+static struct net_device *dmfe_root_dev = NULL; /* First device */
static u32 dmfe_cr6_user_set = 0;
/* For module input parameter */
@@ -206,7 +230,7 @@ static u32 cr6set = 0;
static unsigned char mode = 8;
static u8 chkmode = 1;
-unsigned long CrcTable[256] =
+static unsigned long CrcTable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
@@ -275,7 +299,7 @@ unsigned long CrcTable[256] =
};
/* function declaration ------------------------------------- */
-static int dmfe_reg_board(void);
+static int dmfe_probe(void);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static int dmfe_stop(struct net_device *);
@@ -288,11 +312,11 @@ static void dmfe_descriptor_init(struct dmfe_board_info *, u32);
static void allocated_rx_buffer(struct dmfe_board_info *);
static void update_cr6(u32, u32);
static void send_filter_frame(struct net_device *, int);
-static u16 phy_read(u32, u8, u8);
-static void phy_write(u32, u8, u8, u16);
+static void dm9132_id_table(struct net_device *, int);
+static u16 phy_read(u32, u8, u8, u32);
+static void phy_write(u32, u8, u8, u16, u32);
static void phy_write_1bit(u32, u32);
static u16 phy_read_1bit(u32);
-static void parser_ctrl_info(struct dmfe_board_info *);
static void dmfe_sense_speed(struct dmfe_board_info *);
static void dmfe_process_mode(struct dmfe_board_info *);
static void dmfe_timer(unsigned long);
@@ -301,7 +325,7 @@ static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
static void dmfe_dynamic_reset(struct net_device *);
static void dmfe_free_rxbuffer(struct dmfe_board_info *);
static void dmfe_init_dm910x(struct net_device *);
-static unsigned long cal_CRC(unsigned char *, unsigned int);
+static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
/* DM910X network board routine ---------------------------- */
@@ -309,9 +333,9 @@ static unsigned long cal_CRC(unsigned char *, unsigned int);
* Search DM910X board, allocate space and register it
*/
-static int __init dmfe_reg_board(void)
+static int __init dmfe_probe(void)
{
- u32 pci_iobase;
+ unsigned long pci_iobase;
u16 dm9102_count = 0;
u8 pci_irqline;
static int index = 0; /* For multiple call */
@@ -320,7 +344,7 @@ static int __init dmfe_reg_board(void)
struct pci_dev *net_dev = NULL;
struct net_device *dev;
- DMFE_DBUG(0, "dmfe_reg_board()", 0);
+ DMFE_DBUG(0, "dmfe_probe()", 0);
if (!pci_present())
return -ENODEV;
@@ -329,20 +353,18 @@ static int __init dmfe_reg_board(void)
while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
{
u32 pci_id;
+ u32 dev_rev;
index++;
if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
continue;
- if (pci_id != PCI_DM9102_ID)
+ if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
continue;
pci_iobase = net_dev->resource[0].start;
pci_irqline = net_dev->irq;
- if (check_region(pci_iobase, DM9102_IO_SIZE)) /* IO range check */
- continue;
-
/* Enable Master/IO access, Disable memory access */
pci_enable_device (net_dev);
@@ -355,7 +377,19 @@ static int __init dmfe_reg_board(void)
pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
- /* IO range and interrupt check */
+ /* Read Chip revesion */
+ pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev);
+
+ /* IO range check */
+ if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+ printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
+ continue;
+ }
+ /* Interrupt check */
+ if (pci_irqline == 0) {
+ printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
+ continue;
+ }
/* Found DM9102 card and PCI resource allocated OK */
dm9102_count++; /* Found a DM9102 card */
@@ -373,7 +407,7 @@ static int __init dmfe_reg_board(void)
db->chip_id = pci_id; /* keep Chip vandor/Device ID */
db->ioaddr = pci_iobase;
- pci_read_config_dword(net_dev, 8, &db->chip_revesion);
+ db->chip_revesion = dev_rev;
db->net_dev = net_dev;
@@ -386,7 +420,7 @@ static int __init dmfe_reg_board(void)
dev->set_multicast_list = &dmfe_set_filter_mode;
dev->do_ioctl = &dmfe_do_ioctl;
- request_region(pci_iobase, DM9102_IO_SIZE, dev->name);
+ request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
/* read 64 word srom data */
for (i = 0; i < 64; i++)
@@ -422,20 +456,17 @@ static int dmfe_open(struct net_device *dev)
db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
if (db->desc_pool_ptr == NULL)
return -ENOMEM;
-
if ((u32) db->desc_pool_ptr & 0x1f)
db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
else
db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
/* Allocated Tx buffer memory */
-
db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
if (db->buf_pool_ptr == NULL) {
kfree(db->desc_pool_ptr);
return -ENOMEM;
}
-
if ((u32) db->buf_pool_ptr & 0x3)
db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
else
@@ -444,16 +475,21 @@ static int dmfe_open(struct net_device *dev)
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
db->in_reset_state = 0;
db->rx_error_cnt = 0;
- if (chkmode && (db->chip_revesion < 0x02000030)) {
- db->dm910x_chk_mode = 1; /* Enter the check mode */
- } else {
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
+ //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */
+ //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */
+ db->cr0_data = 0xc00000; /* TX/RX desc burst mode */
db->dm910x_chk_mode = 4; /* Enter the normal mode */
+ } else {
+ db->cr0_data = 0;
+ db->dm910x_chk_mode = 1; /* Enter the check mode */
}
/* Initilize DM910X board */
@@ -474,14 +510,12 @@ static int dmfe_open(struct net_device *dev)
return 0;
}
-/*
- * Initialize DM910X board
- * Reset DM910X board
- * Initialize TX/Rx descriptor chain structure
- * Send the set-up frame
- * Enable Tx/Rx machine
+/* Initilize DM910X board
+ Reset DM910X board
+ Initilize TX/Rx descriptor chain structure
+ Send the set-up frame
+ Enable Tx/Rx machine
*/
-
static void dmfe_init_dm910x(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -490,16 +524,18 @@ static void dmfe_init_dm910x(struct net_device *dev)
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x board : need 32 PCI clock to complete */
- outl(DM910X_RESET, ioaddr + DCR0);
+ outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
DELAY_5US;
- outl(0, ioaddr + DCR0);
+ outl(db->cr0_data, ioaddr + DCR0);
outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
- outl(0x80, ioaddr + DCR12); /* Reset DM9102 phyxcer */
+ outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */
outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
- /* Parser control information: Phy addr */
- parser_ctrl_info(db);
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Media Mode Check */
db->media_mode = dmfe_media_mode;
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
@@ -514,7 +550,10 @@ static void dmfe_init_dm910x(struct net_device *dev)
update_cr6(db->cr6_data, ioaddr);
/* Send setup frame */
- send_filter_frame(dev, 0);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
/* Init CR5/CR7, interrupt active bit */
outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */
@@ -533,10 +572,9 @@ static void dmfe_init_dm910x(struct net_device *dev)
/*
- * Hardware start transmission.
- * Send a packet to media from the upper layer.
+ Hardware start transmission.
+ Send a packet to media from the upper layer.
*/
-
static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -561,25 +599,24 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
txptr->tdes1 = 0xe1000000 | skb->len;
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
/* Point to next transmit free descriptor */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
- /* transmit counter increase 1 */
- db->tx_packet_cnt++;
- db->stats.tx_packets++;
-
- /* issue Tx polling command */
- outl(0x1, dev->base_addr + DCR1);
+ /* Transmit Packet Process */
+ if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ } else {
+ db->tx_queue_cnt++; /* queue the tx packet */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ }
/* Tx resource check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
- /* Set transmit time stamp */
- dev->trans_start = jiffies; /* saved the time stamp */
-
/* free this SKB */
dev_kfree_skb(skb);
return 0;
@@ -622,8 +659,8 @@ static int dmfe_stop(struct net_device *dev)
}
/*
- * DM9102 insterrupt handler
- * receive the packet to upper layer, free the transmitted packet
+ DM9102 insterrupt handler
+ receive the packet to upper layer, free the transmitted packet
*/
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -637,7 +674,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
return;
}
-
/* A real interrupt coming */
db = (struct dmfe_board_info *) dev->priv;
ioaddr = dev->base_addr;
@@ -667,15 +703,35 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* printk("tdes0=%x\n", txptr->tdes0); */
if (txptr->tdes0 & 0x80000000)
break;
+ db->stats.tx_packets++;
+
if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
/* printk("tdes0=%x\n", txptr->tdes0); */
db->stats.tx_errors++;
}
+ /* Transmit statistic counter */
+ if (txptr->tdes0 != 0x7fffffff) {
+ /* printk("tdes0=%x\n", txptr->tdes0); */
+ db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+ db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
+ if (txptr->tdes0 & TDES0_ERR_MASK)
+ db->stats.tx_errors++;
+ }
txptr = (struct tx_desc *) txptr->next_tx_desc;
db->tx_packet_cnt--;
}
+ /* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
+ /* Send the Tx packet in queue */
+ if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
+ dev->trans_start = jiffies; /* saved the time stamp */
+ db->tx_queue_cnt--;
+ }
+ /* Resource available check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
@@ -695,7 +751,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/* Restore CR7 to enable interrupt mask */
-
if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
db->cr7_data = 0x1a28d;
else
@@ -704,9 +759,8 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/*
- * Receive the come packet and pass to upper layer
+ Receive the come packet and pass to upper layer
*/
-
static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
{
struct rx_desc *rxptr;
@@ -727,11 +781,11 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
/* reused this SKB */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
- db->rx_error_cnt++;
+ /* db->rx_error_cnt++; */
} else {
+ /* A packet with First/Last flag */
rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */
- /* A packet with First/Last flag */
if (rxptr->rdes0 & 0x8000) { /* error summary bit check */
/* This is a error packet */
/* printk("rdes0 error : %x \n", rxptr->rdes0); */
@@ -748,7 +802,7 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb = (struct sk_buff *) rxptr->rx_skb_ptr;
/* Received Packet CRC check need or not */
- if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) {
+ if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) {
/* Found a error received packet */
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
db->dm910x_chk_mode = 3;
@@ -758,11 +812,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb_put(skb, rxlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* Send to upper layer */
- /* skb->ip_summed = CHECKSUM_UNNECESSARY; */
dev->last_rx = jiffies;
db->stats.rx_packets++;
+ db->stats.rx_bytes += rxlen;
}
} else {
+ /* Reuse SKB buffer when the packet is error */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
}
@@ -772,12 +827,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
}
db->rx_ready_ptr = rxptr;
+
}
/*
- * Get statistics from driver.
+ Get statistics from driver.
*/
-
static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
{
struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
@@ -787,9 +842,8 @@ static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
}
/*
- * Set DM910X multicast address
+ Set DM910X multicast address
*/
-
static void dmfe_set_filter_mode(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -809,13 +863,15 @@ static void dmfe_set_filter_mode(struct net_device *dev)
return;
}
DMFE_DBUG(0, "Set multicast address", dev->mc_count);
- send_filter_frame(dev, dev->mc_count);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
}
/*
- * Process the upper socket ioctl command
+ Process the upper socket ioctl command
*/
-
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -823,10 +879,9 @@ static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
/*
- * A periodic timer routine
- * Dynamic media sense, allocated Rx buffer...
+ A periodic timer routine
+ Dynamic media sense, allocated Rx buffer...
*/
-
static void dmfe_timer(unsigned long data)
{
u32 tmp_cr8;
@@ -861,18 +916,26 @@ static void dmfe_timer(unsigned long data)
if (db->wait_reset | (db->tx_packet_cnt &&
((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
- /* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */
+ /*
+ printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
+ */
DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
dmfe_dynamic_reset(dev);
db->timer.expires = DMFE_TIMER_WUT;
add_timer(&db->timer);
return;
}
- db->rx_error_cnt = 0; /* Clear previous counter */
+ db->rx_error_cnt = 0; /* Clear previos counter */
/* Link status check, Dynamic media type change */
- tmp_cr12 = inb(db->ioaddr + DCR12);
- if (db->chip_revesion == 0x02000030) {
+ if (db->chip_id == PCI_DM9132_ID)
+ tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */
+ else
+ tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */
+
+ if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
+ ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+ /* DM9102A Chip */
if (tmp_cr12 & 2)
tmp_cr12 = 0x0; /* Link failed */
else
@@ -882,10 +945,26 @@ static void dmfe_timer(unsigned long data)
/* Link Failed */
DMFE_DBUG(0, "Link Failed", tmp_cr12);
db->link_failed = 1;
- phy_write(db->ioaddr, db->phy_addr, 0, 0x8000); /* reset Phy controller */
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */
+
+ /* 10/100M link failed, used 1M Home-Net */
+ db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */
+ db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
+ db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+ if (db->phy_id2 == 0xb900)
+ phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
} else if ((tmp_cr12 & 0x3) && db->link_failed) {
DMFE_DBUG(0, "Link link OK", tmp_cr12);
db->link_failed = 0;
+
+ /* CR6 bit18=0, select 10/100M */
+ db->cr6_data &= ~0x00040000;
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* Auto Sense Speed */
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
dmfe_process_mode(db);
@@ -902,13 +981,12 @@ static void dmfe_timer(unsigned long data)
}
/*
- * Dynamic reset the DM910X board
- * Stop DM910X board
- * Free Tx/Rx allocated memory
- * Reset DM910X board
- * Re-initilize DM910X board
+ Dynamic reset the DM910X board
+ Stop DM910X board
+ Free Tx/Rx allocated memory
+ Reset DM910X board
+ Re-initilize DM910X board
*/
-
static void dmfe_dynamic_reset(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -929,6 +1007,7 @@ static void dmfe_dynamic_reset(struct net_device *dev)
/* system variable init */
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
@@ -945,9 +1024,8 @@ static void dmfe_dynamic_reset(struct net_device *dev)
}
/*
- * Free all allocated rx buffer
+ free all allocated rx buffer
*/
-
static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
{
DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
@@ -961,9 +1039,8 @@ static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
}
/*
- * Reused the SK buffer
+ Reused the SK buffer
*/
-
static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
{
struct rx_desc *rxptr = db->rx_insert_ptr;
@@ -979,10 +1056,9 @@ static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
}
/*
- * Initialize transmit/Receive descriptor
- * Using Chain structure, and allocated Tx/Rx buffer
+ Initialize transmit/Receive descriptor
+ Using Chain structure, and allocated Tx/Rx buffer
*/
-
static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
{
struct tx_desc *tmp_tx;
@@ -1033,10 +1109,9 @@ static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
}
/*
- * Update CR6 vaule
- * Firstly stop DM910X , then written value and start
+ Update CR6 vaule
+ Firstly stop DM910X , then written value and start
*/
-
static void update_cr6(u32 cr6_data, u32 ioaddr)
{
u32 cr6_tmp;
@@ -1049,11 +1124,50 @@ static void update_cr6(u32 cr6_data, u32 ioaddr)
/* printk("CR6 update %x ", cr6_tmp); */
}
-/*
- * Send a setup frame
- * This setup frame initilize DM910X addres filter mode
+/* Send a setup frame for DM9132
+ This setup frame initilize DM910X addres filter mode
+ */
+static void dm9132_id_table(struct net_device *dev, int mc_cnt)
+{
+ struct dev_mc_list *mcptr;
+ u16 *addrptr;
+ u32 ioaddr = dev->base_addr + 0xc0; /* ID Table */
+ u32 hash_val;
+ u16 i, hash_table[4];
+
+ DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+ /* Node address */
+ addrptr = (u16 *) dev->dev_addr;
+ outw(addrptr[0], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[1], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[2], ioaddr);
+ ioaddr += 4;
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ /* the multicast address in Hash Table : 64 bits */
+ for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+ }
+
+ /* Write the hash table to MAC MD table */
+ for (i = 0; i < 4; i++, ioaddr += 4) {
+ outw(hash_table[i], ioaddr);
+ }
+}
+
+/* Send a setup frame for DM9102/DM9102A
+ This setup frame initilize DM910X addres filter mode
*/
-
static void send_filter_frame(struct net_device *dev, int mc_cnt)
{
struct dmfe_board_info *db = dev->priv;
@@ -1068,17 +1182,17 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
txptr = db->tx_insert_ptr;
suptr = (u32 *) txptr->tx_buf_ptr;
- /* broadcast address */
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
-
/* Node address */
addrptr = (u16 *) dev->dev_addr;
*suptr++ = addrptr[0];
*suptr++ = addrptr[1];
*suptr++ = addrptr[2];
+ /* broadcast address */
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+
/* fit the multicast address */
for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
addrptr = (u16 *) mcptr->dmi_addr;
@@ -1092,19 +1206,21 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
*suptr++ = 0xffff;
*suptr++ = 0xffff;
}
-
/* prepare the setup frame */
- db->tx_packet_cnt++;
- netif_stop_queue(dev);
- txptr->tdes1 = 0x890000c0;
- txptr->tdes0 = 0x80000000;
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
-
- update_cr6(db->cr6_data | 0x2000, dev->base_addr);
- outl(0x1, dev->base_addr + DCR1);
- update_cr6(db->cr6_data, dev->base_addr);
- dev->trans_start = jiffies;
-
+ txptr->tdes1 = 0x890000c0;
+ /* Resource Check and Send the setup packet */
+ if (!db->tx_packet_cnt) {
+ /* Resource Empty */
+ db->tx_packet_cnt++;
+ txptr->tdes0 = 0x80000000;
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ update_cr6(db->cr6_data, dev->base_addr);
+ } else {
+ /* Put into TX queue */
+ db->tx_queue_cnt++;
+ }
}
/*
@@ -1132,9 +1248,8 @@ static void allocated_rx_buffer(struct dmfe_board_info *db)
}
/*
- * Read one word data from the serial ROM
+ Read one word data from the serial ROM
*/
-
static u16 read_srom_word(long ioaddr, int offset)
{
int i;
@@ -1170,35 +1285,6 @@ static u16 read_srom_word(long ioaddr, int offset)
}
/*
- * Parser Control media block to get Phy address
- */
-
-static void parser_ctrl_info(struct dmfe_board_info *db)
-{
- int i;
- char *sdata = db->srom;
- unsigned char count;
-
- /* point to info leaf0 */
- count = *(sdata + 33);
-
- /* Point to First media block */
- sdata += 34;
- for (i = 0; i < count; i++) {
- if (*(sdata + 1) == 1) {
- db->phy_addr = *(sdata + 2);
- break;
- }
- sdata += ((unsigned char) *(sdata) & 0x7f) + 1;
- }
-
- if (i >= count) {
- printk("Can't found Control Block\n");
- db->phy_addr = 1;
- }
-}
-
-/*
* Auto sense the media mode
*/
@@ -1209,13 +1295,16 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
for (i = 1000; i; i--) {
DELAY_5US;
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 1);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
if ((phy_mode & 0x24) == 0x24)
break;
}
if (i) {
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000;
+ if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
+ else /* DM9102/DM9102A */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000;
/* printk("Phy_mode %x ",phy_mode); */
switch (phy_mode) {
case 0x1000:
@@ -1231,23 +1320,22 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
db->op_mode = DMFE_100MFD;
break;
default:
- db->op_mode = DMFE_100MHF;
- DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode);
+ db->op_mode = DMFE_10MHF;
+ DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
break;
}
} else {
- db->op_mode = DMFE_100MHF;
+ db->op_mode = DMFE_10MHF;
DMFE_DBUG(0, "Link Failed :", phy_mode);
}
}
/*
- * Process op-mode
- * AUTO mode : PHY controller in Auto-negotiation Mode
- * Force mode: PHY controller in force mode with HUB
- * N-way force capability with SWITCH
+ Process op-mode
+ AUTO mode : PHY controller in Auto-negotiation Mode
+ Force mode: PHY controller in force mode with HUB
+ N-way force capability with SWITCH
*/
-
static void dmfe_process_mode(struct dmfe_board_info *db)
{
u16 phy_reg;
@@ -1259,11 +1347,11 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */
/* User force the media type */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 5);
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
/* printk("Nway phy_reg5 %x ",phy_reg); */
if (phy_reg & 0x1) {
/* parter own the N-Way capability */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0;
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
switch (db->op_mode) {
case DMFE_10MHF:
phy_reg |= 0x20;
@@ -1278,7 +1366,7 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg |= 0x100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 4, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
} else {
/* parter without the N-Way capability */
switch (db->op_mode) {
@@ -1295,95 +1383,109 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg = 0x2100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 0, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
}
}
}
/*
- * Write a word to Phy register
+ Write a word to Phy register
*/
-
-static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data)
+static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
{
u16 i;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ ioaddr = iobase + 0x80 + offset * 4;
+ outw(phy_data, ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send write command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* written trasnition */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Write a word data to PHY controller */
- for (i = 0x8000; i > 0; i >>= 1)
- phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Write a word data to PHY controller */
+ for (i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ }
}
/*
- * Read a word data from phy register
+ Read a word data from phy register
*/
-
-static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset)
+static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset, u32 chip_id)
{
int i;
u16 phy_data;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ /* DM9132 Chip */
+ ioaddr = iobase + 0x80 + offset * 4;
+ phy_data = inw(ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+
+ ioaddr = iobase + DCR9;
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send read command(10) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Skip transition state */
- phy_read_1bit(ioaddr);
+ /* Skip transition state */
+ phy_read_1bit(ioaddr);
- /* read 16bit data */
- for (phy_data = 0, i = 0; i < 16; i++) {
- phy_data <<= 1;
- phy_data |= phy_read_1bit(ioaddr);
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr);
+ }
}
return phy_data;
}
/*
- * Write one bit data to Phy Controller
+ Write one bit data to Phy Controller
*/
-
static void phy_write_1bit(u32 ioaddr, u32 phy_data)
{
outl(phy_data, ioaddr); /* MII Clock Low */
@@ -1395,9 +1497,8 @@ static void phy_write_1bit(u32 ioaddr, u32 phy_data)
}
/*
- * Read one bit phy data from PHY controller
+ Read one bit phy data from PHY controller
*/
-
static u16 phy_read_1bit(u32 ioaddr)
{
u16 phy_data;
@@ -1412,10 +1513,11 @@ static u16 phy_read_1bit(u32 ioaddr)
}
/*
- * Calculate the CRC valude of the Rx packet
+ Calculate the CRC valude of the Rx packet
+ flag = 1 : return the reverse CRC (for the received packet CRC)
+ 0 : return the normal CRC (for Hash Table index)
*/
-
-static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
+unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
{
unsigned long Crc = 0xffffffff;
@@ -1423,8 +1525,10 @@ static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
}
- return ~Crc;
-
+ if (flag)
+ return ~Crc;
+ else
+ return Crc;
}
@@ -1461,7 +1565,7 @@ static int __init dmfe_init_module(void)
break;
}
- return dmfe_reg_board(); /* search board and register */
+ return dmfe_probe(); /* search board and register */
}
/*
@@ -1473,14 +1577,16 @@ static int __init dmfe_init_module(void)
static void __exit dmfe_cleanup_module(void)
{
struct net_device *next_dev;
+ struct dmfe_board_info *db;
DMFE_DBUG(0, "clean_module()", 0);
while (dmfe_root_dev) {
- next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+ db = dmfe_root_dev->priv;
+ next_dev = db->next_dev;
unregister_netdev(dmfe_root_dev);
- release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE);
- kfree(dmfe_root_dev->priv); /* free board information */
+ release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+ kfree(db); /* free board information */
kfree(dmfe_root_dev); /* free device structure */
dmfe_root_dev = next_dev;
}
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 03c69417a..d5230ddda 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -712,6 +712,7 @@ static unsigned short eexp_start_irq(struct net_device *dev,
ack_cmd |= SCB_RUstart;
scb_wrrfa(dev, lp->rx_buf_start);
lp->rx_ptr = lp->rx_buf_start;
+ lp->started |= STARTED_RU;
}
ack_cmd |= SCB_CUstart | 0x2000;
}
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 1c546b4d1..f855d1978 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -1071,7 +1071,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (! netif_running(dev)) {
outl(0x0200, ioaddr + GENCTL);
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
index 820bc5d4f..e9f0254a7 100644
--- a/drivers/net/eql.c
+++ b/drivers/net/eql.c
@@ -42,7 +42,7 @@ static const char *version =
* reformatted.
*
* Revision 3.12 1995/03/22 21:07:51 anarchy
- * Added suser() checks on configuration.
+ * Added capable() checks on configuration.
* Moved header file.
*
* Revision 3.11 1995/01/19 23:14:31 guru
@@ -1030,6 +1030,8 @@ int init_module(void)
void cleanup_module(void)
{
+ kfree(((equalizer_t *)dev_eql.priv)->stats );
+ kfree(dev_eql.priv);
unregister_netdev(&dev_eql);
}
#endif /* MODULE */
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 90c82b341..ae92696c9 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -1836,7 +1836,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
status = -EFAULT;
break;
case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 1;
} else {
status = -EPERM;
@@ -1844,7 +1844,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
break;
case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 0;
} else {
status = -EPERM;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 98d228b91..e770baacf 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -1254,7 +1254,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
bc->ch_params.tx_delay = hi.data.cp.tx_delay;
bc->ch_params.tx_tail = hi.data.cp.tx_tail;
@@ -1275,7 +1275,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = hi.data.mp.iobase;
dev->irq = /*hi.data.mp.irq*/0;
@@ -1299,6 +1299,8 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
return 0;
@@ -1314,7 +1316,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODE:
- if (!suser() || netif_running(dev))
+ if (!capable(CAP_NET_ADMIN) || netif_running(dev))
return -EACCES;
hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
return baycom_setmode(bc, hi.data.modename);
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
index b68063b07..63dc93cfb 100644
--- a/drivers/net/hamradio/baycom_par.c
+++ b/drivers/net/hamradio/baycom_par.c
@@ -445,7 +445,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index cdcab2621..a90eb0f8c 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -555,7 +555,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
index 64b27286c..111373ca9 100644
--- a/drivers/net/hamradio/baycom_ser_hdx.c
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -598,7 +598,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 0cc9aa7c1..a48b3f6c6 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -366,7 +366,7 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct bpqdev *bpq = dev->priv;
struct bpq_req req;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (bpq == NULL) /* woops! */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index a3560c207..eac33513d 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -635,7 +635,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
s->ch_params.tx_delay = bi.data.cp.tx_delay;
s->ch_params.tx_tail = bi.data.cp.tx_tail;
@@ -656,7 +656,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = bi.data.mp.iobase;
dev->irq = bi.data.mp.irq;
@@ -684,6 +684,8 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if(!capable(CAP_SYS_RAWIO))
+ return -EPERM;
s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
return 0;
diff --git a/drivers/net/hamradio/pi2.c b/drivers/net/hamradio/pi2.c
index b52a254c8..220c4d920 100644
--- a/drivers/net/hamradio/pi2.c
+++ b/drivers/net/hamradio/pi2.c
@@ -1580,7 +1580,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1597,7 +1597,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & 2) { /* if A channel */
diff --git a/drivers/net/hamradio/pt.c b/drivers/net/hamradio/pt.c
index 59762edd7..8baff63ff 100644
--- a/drivers/net/hamradio/pt.c
+++ b/drivers/net/hamradio/pt.c
@@ -998,7 +998,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1015,7 +1015,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & CHANA) { /* if A channel */
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index 09bb422f1..c7f27414f 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1791,7 +1791,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int found = 1;
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg) return -EFAULT;
if (Nchips >= SCC_MAXCHIPS)
@@ -1887,7 +1887,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (cmd == SIOCSCCINI)
{
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
if (Nchips == 0)
@@ -1904,7 +1904,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
if (cmd == SIOCSCCCHANINI)
{
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg) return -EINVAL;
scc->stat.bufsize = SCC_BUFSIZE;
@@ -1957,7 +1957,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -ENOIOCTLCMD;
case SIOCSCCSMEM:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
return -EINVAL;
scc->stat.bufsize = memcfg.bufsize;
@@ -1977,13 +1977,13 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return 0;
case SIOCSCCSKISS:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
return -EINVAL;
return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
case SIOCSCCCAL:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
return -EINVAL;
diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c
index 4e5e3e9b9..af04a2c87 100644
--- a/drivers/net/hamradio/soundmodem/sm.c
+++ b/drivers/net/hamradio/soundmodem/sm.c
@@ -509,7 +509,7 @@ static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return sethw(dev, sm, hi->data.modename);
diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c
index efc2a3295..772940049 100644
--- a/drivers/net/hamradio/soundmodem/sm_sbc.c
+++ b/drivers/net/hamradio/soundmodem/sm_sbc.c
@@ -1,4 +1,4 @@
-/*****************************************************************************/
+
/*
* sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver
@@ -576,7 +576,7 @@ static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
switch (SCSTATE->revhi) {
case 2:
diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c
index f1dc3744e..23abd970a 100644
--- a/drivers/net/hamradio/soundmodem/sm_wss.c
+++ b/drivers/net/hamradio/soundmodem/sm_wss.c
@@ -637,7 +637,7 @@ static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
!SCSTATE->crystal) &&
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 400facbf8..27929e78b 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -962,7 +962,7 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (yp == NULL || yp->magic != YAM_MAGIC)
return -EINVAL;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd != SIOCDEVPRIVATE)
@@ -977,6 +977,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (netif_running(dev))
return -EINVAL; /* Cannot change this parameter when up */
ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC);
+ if(ym==NULL)
+ return -ENOBUFS;
ym->bitrate = 9600;
if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs)))
return -EFAULT;
@@ -987,6 +989,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCYAMSCFG:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
return -EFAULT;
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index 689ffbd22..ce94c3139 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -14,22 +14,8 @@
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- Fixing alignment problem with 1.3.* kernel and some minor changes
- by Andrey V. Savochkin, 1996.
-
- Problems or questions may be send to Donald Becker (see above) or to
- Andrey Savochkin -- saw@shade.msu.ru or
- Laboratory of Computation Methods,
- Department of Mathematics and Mechanics,
- Moscow State University,
- Leninskye Gory, Moscow 119899
-
- But I should to inform you that I'm not an expert in the LANCE card
- and it may occurs that you will receive no answer on your mail
- to Donald Becker. I didn't receive any answer on all my letters
- to him. Who knows why... But may be you are more lucky? ;->
- SAW
-
+ Andrey V. Savochkin:
+ - alignment problem with 1.3.* kernel and some minor changes.
Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
- added support for Linux/Alpha, but removed most of it, because
it worked only for the PCI chip.
@@ -785,11 +771,19 @@ lance_open(struct net_device *dev)
*/
static void
-lance_purge_tx_ring(struct net_device *dev)
+lance_purge_ring(struct net_device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = 0;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb(skb);
+ }
for (i = 0; i < TX_RING_SIZE; i++) {
if (lp->tx_skbuff[i]) {
dev_kfree_skb(lp->tx_skbuff[i]);
@@ -850,7 +844,7 @@ lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
- lance_purge_tx_ring(dev);
+ lance_purge_ring(dev);
lance_init_ring(dev, GFP_ATOMIC);
}
outw(0x0000, dev->base_addr + LANCE_ADDR);
@@ -869,7 +863,7 @@ static void lance_tx_timeout (struct net_device *dev)
outw (0x0004, ioaddr + LANCE_DATA);
lp->stats.tx_errors++;
#ifndef final_version
- {
+ if (lance_debug > 3) {
int i;
printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
@@ -1192,19 +1186,7 @@ lance_close(struct net_device *dev)
}
free_irq(dev->irq, dev);
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = lp->rx_skbuff[i];
- lp->rx_skbuff[i] = 0;
- lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
- if (skb)
- dev_kfree_skb(skb);
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i])
- dev_kfree_skb(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = 0;
- }
+ lance_purge_ring(dev);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 2b5624b29..53f841af2 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -920,8 +920,6 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
"status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS));
- netif_stop_queue (dev);
-
outw(skb->len, ioaddr + TX_FIFO);
outw(0, ioaddr + TX_FIFO);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
@@ -929,13 +927,13 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
/* TxFree appears only in Window 1, not offset 0x1c. */
- if (inw(ioaddr + TxFree) > 1536) {
- netif_start_queue (dev);
- } else
+ if (inw(ioaddr + TxFree) <= 1536) {
+ netif_stop_queue (dev);
/* Interrupt us when the FIFO has room for max-sized packet.
The threshold is in units of dwords. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-
+ }
+
dev_kfree_skb (skb);
pop_tx_status(dev);
@@ -976,8 +974,6 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue (dev);
- } else {
- netif_stop_queue (dev);
}
if (status & TxComplete)
diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in
index 606d5a606..534a4bdbd 100644
--- a/drivers/net/pcmcia/Config.in
+++ b/drivers/net/pcmcia/Config.in
@@ -18,7 +18,6 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
if [ "$CONFIG_CARDBUS" = "y" ]; then
dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
- dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m
fi
bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile
index 11c583dac..5d0d36f4a 100644
--- a/drivers/net/pcmcia/Makefile
+++ b/drivers/net/pcmcia/Makefile
@@ -20,7 +20,6 @@ obj- :=
export-objs := ray_cs.o
CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE
-CFLAGS_tulip_cb.o = -DCARDBUS -DMODULE
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
@@ -40,7 +39,6 @@ obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o
# Cardbus client drivers
obj-$(CONFIG_PCMCIA_3C575) += 3c575_cb.o
-obj-$(CONFIG_PCMCIA_TULIP) += tulip_cb.o
O_OBJS := $(filter-out $(export-objs), $(obj-y))
OX_OBJS := $(filter $(export-objs), $(obj-y))
diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c
index 7c763cf8e..3c088df36 100644
--- a/drivers/net/pcmcia/ray_cs.c
+++ b/drivers/net/pcmcia/ray_cs.c
@@ -1417,7 +1417,7 @@ static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
#define SIOCGIPFRAMING SIOCDEVPRIVATE + 1 /* Get framing mode */
#define SIOCGIPCOUNTRY SIOCDEVPRIVATE + 3 /* Get country code */
case SIOCSIPFRAMING:
- if(!suser()) /* For private IOCTLs, we need to check permissions */
+ if(!capable(CAP_NET_ADMIN)) /* For private IOCTLs, we need to check permissions */
{
err = -EPERM;
break;
diff --git a/drivers/net/pcmcia/tulip_cb.c b/drivers/net/pcmcia/tulip_cb.c
deleted file mode 100644
index 38dcdc0c1..000000000
--- a/drivers/net/pcmcia/tulip_cb.c
+++ /dev/null
@@ -1,3150 +0,0 @@
-/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
-/*
- Written/copyright 1994-1999 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU Public License, incorporated herein by reference.
-
- This driver is for the Digital "Tulip" Ethernet adapter interface.
- It should work with most DEC 21*4*-based chips/ethercards, as well as
- with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
-
- The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- Center of Excellence in Space Data and Information Sciences
- Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-
- Support and updates available at
- http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-*/
-
-#define SMP_CHECK
-static const char version[] = "tulip.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
-
-/* A few user-configurable values. */
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 25;
-
-#define MAX_UNITS 8
-/* Used to pass the full-duplex flag, etc. */
-static int full_duplex[MAX_UNITS] = {0, };
-static int options[MAX_UNITS] = {0, };
-static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */
-
-/* The possible media types that can be set in options[] are: */
-static const char * const medianame[] = {
- "10baseT", "10base2", "AUI", "100baseTx",
- "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
- "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
- "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-
-/* Keep the ring sizes a power of two for efficiency.
- Making the Tx ring too large decreases the effectiveness of channel
- bonding and packet priority.
- There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 32
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#ifdef __alpha__
-static int rx_copybreak = 1518;
-#else
-static int rx_copybreak = 100;
-#endif
-
-/*
- Set the bus performance register.
- Typical: Set 16 longword cache alignment, no burst limit.
- Cache alignment bits 15:14 Burst length 13:8
- 0000 No alignment 0x00000000 unlimited 0800 8 longwords
- 4000 8 longwords 0100 1 longword 1000 16 longwords
- 8000 16 longwords 0200 2 longwords 2000 32 longwords
- C000 32 longwords 0400 4 longwords
- Warning: many older 486 systems are broken and require setting 0x00A04800
- 8 longword cache alignment, 8 longword burst.
- ToDo: Non-Intel setting could be better.
-*/
-
-#if defined(__alpha__)
-static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__powerpc__)
-static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
-static int csr0 = 0x01B00080 | 0x8000;
-#elif defined(__i386__)
-static int csr0 = 0x01A00000 | 0x8000;
-#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
-#endif
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (4*HZ)
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
- to support a pre-NWay full-duplex signaling mechanism using short frames.
- No one knows what it should be, but if left at its default value some
- 10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC 0x6969
-
-#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
-#warning You must compile this file with the correct options!
-#warning See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <asm/processor.h> /* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-
-/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
- This is only in the support-all-kernels source code. */
-
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(csr0, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define tulip_debug debug
-#ifdef TULIP_DEBUG
-static int tulip_debug = TULIP_DEBUG;
-#else
-static int tulip_debug = 1;
-#endif
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the DECchip "Tulip", Digital's
-single-chip ethernet controllers for PCI. Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike
-chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
-supported.
-
-These chips are used on at least 140 unique PCI board designs. The great
-number of chips and board designs supported is the reason for the
-driver size and complexity. Almost of the increasing complexity is in the
-board configuration and media selection code. There is very little
-increasing in the operational critical path length.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS preferably should assign the
-PCI INTA signal to an otherwise unused system IRQ line.
-
-Some boards have EEPROMs tables with default media entry. The factory default
-is usually "autoselect". This should only be overridden when using
-transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
-for forcing full-duplex when used with old link partners that do not do
-autonegotiation.
-
-III. Driver operation
-
-IIIa. Ring buffers
-
-The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-This driver uses statically allocated rings of Rx and Tx descriptors, set at
-compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs
-for the Rx ring buffers at open() time and passes the skb->data field to the
-Tulip as receive data buffers. When an incoming frame is less than
-RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
-copied to the new skbuff. When the incoming frame is larger, the skbuff is
-passed directly up the protocol stack and replaced by a newly allocated
-skbuff.
-
-The RX_COPYBREAK value is chosen to trade-off the memory wasted by
-using a full-sized skbuff for small frames vs. the copying costs of larger
-frames. For small frames the copying cost is negligible (esp. considering
-that we are pre-loading the cache with immediately useful header
-information). For large frames the copying cost is non-trivial, and the
-larger copy might flush the cache of useful data. A subtle aspect of this
-choice is that the Tulip only receives into longword aligned buffers, thus
-the IP header at offset 14 isn't longword aligned for further processing.
-Copied frames are put into the new skbuff at an offset of "+2", thus copying
-has the beneficial effect of aligning the IP header and preloading the
-cache.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'tp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
-Greg LaPolla at Linksys provided PNIC and other Linksys boards.
-Znyx provided a four-port card for testing.
-
-IVb. References
-
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
-http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM")
-http://www.national.com/pf/DP/DP83840A.html
-http://www.asix.com.tw/pmac.htm
-http://www.admtek.com.tw/
-
-IVc. Errata
-
-The old DEC databooks were light on details.
-The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written. Hmmm, now how is that possible?
-
-The DEC SROM format is very badly designed not precisely defined, leading to
-part of the media selection junkheap below. Some boards do not have EEPROM
-media tables and need to be patched up. Worse, other boards use the DEC
-design kit media table when it isn't correct for their board.
-
-We cannot use MII interrupts because there is no defined GPIO pin to attach
-them. The MII transceiver status is polled using an kernel timer.
-
-*/
-
-/* This table use during operation for capabilities and media timer. */
-
-static void tulip_timer(unsigned long data);
-static void t21142_timer(unsigned long data);
-static void mxic_timer(unsigned long data);
-static void pnic_timer(unsigned long data);
-static void comet_timer(unsigned long data);
-
-enum tbl_flag {
- HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
- HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
- HAS_NWAY143=0x40, /* Uses 21143-like internal NWay. */
-};
-static struct tulip_chip_table {
- char *chip_name;
- int io_size;
- int valid_intrs; /* CSR7 interrupt enable settings */
- int flags;
- void (*media_timer)(unsigned long data);
-} tulip_tbl[] = {
- { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
- { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
- { "Digital DS21140 Tulip", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
- { "Digital DS21143 Tulip", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer },
- { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
- HAS_MII, pnic_timer },
- { "Macronix 98713 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Macronix 98715 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "Macronix 98725 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "ASIX AX88140", 128, 0x0001fbff,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
- { "Lite-On PNIC-II", 256, 0x0001ebef,
- HAS_MII | HAS_NWAY143, pnic_timer },
- { "ADMtek Comet", 256, 0x0001abef,
- MC_HASH_ONLY, comet_timer },
- { "Compex 9881 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
- HAS_MII | HAS_ACPI, tulip_timer },
- {0},
-};
-/* This matches the table above. Note 21142 == 21143. */
-enum chips {
- DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
- LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
- X3201_3,
-};
-
-/* A full-duplex map for media types. */
-enum MediaIs {
- MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
- MediaIs100=16};
-static const char media_cap[] =
-{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 };
-static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0};
-/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
-static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
-static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
-static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
-static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-/* Offsets to the Command and Status Registers, "CSRs". All accesses
- must be longword instructions and quadword aligned. */
-enum tulip_offsets {
- CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
- CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
- CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
-
-/* The bits in the CSR5 status registers, mostly interrupt sources. */
-enum status_bits {
- TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
- NormalIntr=0x10000, AbnormalIntr=0x8000,
- RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
- TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
-};
-
-/* The Tulip Rx and Tx buffer descriptors. */
-struct tulip_rx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2;
-};
-
-struct tulip_tx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2; /* We use only buffer 1. */
-};
-
-enum desc_status_bits {
- DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-
-/* Ring-wrap flag in length field, use for last ring entry.
- 0x01000000 means chain on buffer2 address,
- 0x02000000 means use the ring start address in CSR2/3.
- Note: Some work-alike chips do not function correctly in chained mode.
- The ASIX chip works only in chained mode.
- Thus we indicates ring mode, but always write the 'next' field for
- chained mode as well.
-*/
-#define DESC_RING_WRAP 0x02000000
-
-#ifdef CARDBUS
-#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6)
-#else
-#define EEPROM_ADDRLEN 6
-#endif
-#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
-
-struct medialeaf {
- u8 type;
- u8 media;
- unsigned char *leafdata;
-};
-
-struct mediatable {
- u16 defaultmedia;
- u8 leafcount, csr12dir; /* General purpose pin directions. */
- unsigned has_mii:1, has_nonmii:1, has_reset:6;
- u32 csr15dir, csr15val; /* 21143 NWay setting. */
- struct medialeaf mleaf[0];
-};
-
-struct mediainfo {
- struct mediainfo *next;
- int info_type;
- int index;
- unsigned char *info;
-};
-
-struct tulip_private {
- char devname[8]; /* Used only for kernel debugging. */
- const char *product_name;
- struct tulip_rx_desc rx_ring[RX_RING_SIZE];
- struct tulip_tx_desc tx_ring[TX_RING_SIZE];
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
- /* The X3201-3 requires double word aligned tx bufs */
- struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
- /* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- char *rx_buffs; /* Address of temporary Rx buffers. */
- u8 setup_buf[96*sizeof(u16) + 7];
- u16 *setup_frame; /* Pseudo-Tx frame to init address table. */
- int chip_id;
- int revision;
- struct net_device_stats stats;
- struct timer_list timer; /* Media selection timer. */
- int interrupt; /* In-interrupt flag. */
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int full_duplex_lock:1;
- unsigned int fake_addr:1; /* Multiport board faked address. */
- unsigned int default_port:4; /* Last dev->if_port value. */
- unsigned int media2:4; /* Secondary monitored media port. */
- unsigned int medialock:1; /* Don't sense media type. */
- unsigned int mediasense:1; /* Media sensing in progress. */
- unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */
- unsigned int open:1;
- unsigned int csr0; /* CSR0 setting. */
- unsigned int csr6; /* Current CSR6 control settings. */
- unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
- u16 to_advertise; /* NWay capabilities advertised. */
- u16 lpar; /* 21143 Link partner ability. */
- u16 advertising[4];
- signed char phys[4], mii_cnt; /* MII device addresses. */
- struct mediatable *mtable;
- int cur_index; /* Current media index. */
- int saved_if_port;
- struct pci_dev *pdev;
- spinlock_t lock;
- int pad0, pad1; /* Used for 8-byte alignment */
-};
-
-static void parse_eeprom(struct net_device *dev);
-static int read_eeprom(long ioaddr, int location, int addr_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static void select_media(struct net_device *dev, int startup);
-static void tulip_up(struct net_device *dev);
-static void tulip_down(struct net_device *dev);
-static int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
-static void t21142_start_nway(struct net_device *dev);
-static void tulip_tx_timeout(struct net_device *dev);
-static void tulip_init_ring(struct net_device *dev);
-static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int tulip_close(struct net_device *dev);
-static struct net_device_stats *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
-static void set_rx_mode(struct net_device *dev);
-
-/* The Xircom cards are picky about when certain bits in CSR6 can be
- manipulated. Keith Owens <kaos@ocs.com.au>. */
-
-static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
-{
- const int strict_bits = 0x0060e202;
- int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
- long flags;
- save_flags(flags);
- cli();
- if (chip_idx != X3201_3) {
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
- return;
- }
- newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
- /* read 0 on the Xircom cards */
- newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
- currcsr6 = inl(ioaddr + CSR6);
- if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
- ((currcsr6 & ~0x2002) == 0)) {
- outl(newcsr6, ioaddr + CSR6); /* safe */
- restore_flags(flags);
- return;
- }
- /* make sure the transmitter and receiver are stopped first */
- currcsr6 &= ~0x2002;
- while (1) {
- csr5 = inl(ioaddr + CSR5);
- if (csr5 == 0xffffffff)
- break; /* cannot read csr5, card removed? */
- csr5_22_20 = csr5 & 0x700000;
- csr5_19_17 = csr5 & 0x0e0000;
- if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
- (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
- break; /* both are stopped or suspended */
- if (!--attempts) {
- printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
- "csr5=0x%08x\n", csr5);
- outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */
- restore_flags(flags);
- return;
- }
- outl(currcsr6, ioaddr + CSR6);
- udelay(1);
- }
- /* now it is safe to change csr6 */
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
-}
-
-static struct net_device *tulip_probe1(struct pci_dev *pdev,
- struct net_device *dev, long ioaddr, int irq,
- int chip_idx, int board_idx)
-{
- static int did_version = 0; /* Already printed version info. */
- struct tulip_private *tp;
- /* See note below on the multiport cards. */
- static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
- static int last_irq = 0;
- static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */
- u8 chip_rev;
- int i;
- unsigned short sum;
-
- if (tulip_debug > 0 && did_version++ == 0)
- printk(KERN_INFO "%s", version);
-
- dev = init_etherdev(dev, 0);
-
- pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
- /* Bring the 21143 out of sleep mode.
- Caution: Snooze mode does not work with some boards! */
- if (tulip_tbl[chip_idx].flags & HAS_ACPI)
- pci_write_config_dword(pdev, 0x40, 0x00000000);
-
- printk(KERN_INFO "%s: %s rev %d at %#3lx,",
- dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
-
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
- /* Clear the missed-packet counter. */
- (volatile int)inl(ioaddr + CSR8);
-
- if (chip_idx == DC21041) {
- if (inl(ioaddr + CSR9) & 0x8000) {
- printk(" 21040 compatible mode,");
- chip_idx = DC21040;
- } else {
- printk(" 21041 mode,");
- }
- }
-
- /* The station address ROM is read byte serially. The register must
- be polled, waiting for the value to be read bit serially from the
- EEPROM.
- */
- sum = 0;
- if (chip_idx == DC21040) {
- outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
- for (i = 0; i < 6; i++) {
- int value, boguscnt = 100000;
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- dev->dev_addr[i] = value;
- sum += value & 0xff;
- }
- } else if (chip_idx == LC82C168) {
- for (i = 0; i < 3; i++) {
- int value, boguscnt = 100000;
- outl(0x600 | i, ioaddr + 0x98);
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
- sum += value & 0xffff;
- }
- } else if (chip_idx == COMET) {
- /* No need to read the EEPROM. */
- put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
- put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
- for (i = 0; i < 6; i ++)
- sum += dev->dev_addr[i];
- } else if (chip_idx == X3201_3) {
- /* Xircom has its address stored in the CIS
- * we access it through the boot rom interface for now
- * this might not work, as the CIS is not parsed but I
- * (danilo) use the offset I found on my card's CIS !!!
- *
- * Doug Ledford: I changed this routine around so that it
- * walks the CIS memory space, parsing the config items, and
- * finds the proper lan_node_id tuple and uses the data
- * stored there.
- */
- unsigned char j, tuple, link, data_id, data_count;
- outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
- for (i = 0x100; i < 0x1f7; i += link+2) {
- outl(i, ioaddr + CSR10);
- tuple = inl(ioaddr + CSR9) & 0xff;
- outl(i + 1, ioaddr + CSR10);
- link = inl(ioaddr + CSR9) & 0xff;
- outl(i + 2, ioaddr + CSR10);
- data_id = inl(ioaddr + CSR9) & 0xff;
- outl(i + 3, ioaddr + CSR10);
- data_count = inl(ioaddr + CSR9) & 0xff;
- if ( (tuple == 0x22) &&
- (data_id == 0x04) && (data_count == 0x06) ) {
- /*
- * This is it. We have the data we want.
- */
- for (j = 0; j < 6; j++) {
- outl(i + j + 4, ioaddr + CSR10);
- dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
- }
- break;
- } else if (link == 0) {
- break;
- }
- }
- sum = 1; // to make check below fail!
- } else { /* Must be a new chip, with a serial EEPROM interface. */
- /* We read the whole EEPROM, and sort it out later. DEC has a
- specification _Digital Semiconductor 21X4 Serial ROM Format_
- but early vendor boards just put the address in the first six
- EEPROM locations. */
- unsigned char ee_data[EEPROM_SIZE];
- int sa_offset = 0;
-
- for (i = 0; i < sizeof(ee_data)/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect the simple EEPROM format by the duplicated station addr. */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- sa_offset = 20;
- if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) {
- sa_offset = 2; /* Grrr, damn Matrox boards. */
- multiport_cnt = 4;
- }
- for (i = 0; i < 6; i ++) {
- dev->dev_addr[i] = ee_data[i + sa_offset];
- sum += ee_data[i + sa_offset];
- }
- }
- /* Lite-On boards have the address byte-swapped. */
- if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0)
- && dev->dev_addr[1] == 0x00)
- for (i = 0; i < 6; i+=2) {
- char tmp = dev->dev_addr[i];
- dev->dev_addr[i] = dev->dev_addr[i+1];
- dev->dev_addr[i+1] = tmp;
- }
- /* On the Zynx 315 Etherarray and other multiport boards only the
- first Tulip has an EEPROM.
- The addresses of the subsequent ports are derived from the first.
- Many PCI BIOSes also incorrectly report the IRQ line, so we correct
- that here as well. */
- if (sum == 0 || sum == 6*0xff) {
- printk(" EEPROM not present,");
- for (i = 0; i < 5; i++)
- dev->dev_addr[i] = last_phys_addr[i];
- dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386__) /* Patch up x86 BIOS bug. */
- if (last_irq)
- irq = last_irq;
-#endif
- }
-
- for (i = 0; i < 6; i++)
- printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
- printk(", IRQ %d.\n", irq);
- last_irq = irq;
-
- /* We do a request_region() only to register /proc/ioports info. */
- /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
- request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
-
- /* Make certain the data structures are quadword aligned. */
- tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
- memset(tp, 0, sizeof(*tp));
- dev->priv = tp;
-
- tp->lock = SPIN_LOCK_UNLOCKED;
- tp->pdev = pdev;
- tp->chip_id = chip_idx;
- tp->revision = chip_rev;
- tp->csr0 = csr0;
- tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
-
- /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
- And the ASIX must have a burst limit or horrible things happen. */
- if ( (chip_idx == DC21143 && chip_rev == 65) ||
- (chip_idx == X3201_3) )
- tp->csr0 &= ~0x01000000;
- else if (chip_idx == AX88140)
- tp->csr0 |= 0x2000;
-
-#ifdef TULIP_FULL_DUPLEX
- tp->full_duplex = 1;
- tp->full_duplex_lock = 1;
-#endif
-#ifdef TULIP_DEFAULT_MEDIA
- tp->default_port = TULIP_DEFAULT_MEDIA;
-#endif
-#ifdef TULIP_NO_MEDIA_SWITCH
- tp->medialock = 1;
-#endif
-
- /* The lower four bits are the media type. */
- if (board_idx >= 0 && board_idx < MAX_UNITS) {
- tp->default_port = options[board_idx] & 15;
- if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
- tp->full_duplex = 1;
- if (mtu[board_idx] > 0)
- dev->mtu = mtu[board_idx];
- }
- if (dev->mem_start)
- tp->default_port = dev->mem_start;
- if (tp->default_port) {
- tp->medialock = 1;
- if (media_cap[tp->default_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- }
- if (tp->full_duplex)
- tp->full_duplex_lock = 1;
-
- /* This is logically part of probe1(), but too complex to write inline. */
- if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
- parse_eeprom(dev);
-
- if (media_cap[tp->default_port] & MediaIsMII) {
- u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
- tp->to_advertise = media2advert[tp->default_port - 9];
- } else
- tp->to_advertise = 0x03e1;
-
- if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
- (tp->mtable && tp->mtable->has_mii) ||
- ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) {
- int phy, phy_idx;
- if (tp->mtable && tp->mtable->has_mii) {
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == 11) {
- tp->cur_index = i;
- tp->saved_if_port = dev->if_port;
- select_media(dev, 1);
- dev->if_port = tp->saved_if_port;
- break;
- }
- }
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later,
- but takes much time. */
- for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
- phy++) {
- int mii_status = mdio_read(dev, phy, 1);
- if ((mii_status & 0x8301) == 0x8001 ||
- ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
- int mii_reg0 = mdio_read(dev, phy, 0);
- int mii_advert = mdio_read(dev, phy, 4);
- int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
- tp->phys[phy_idx] = phy;
- tp->advertising[phy_idx++] = reg4;
- printk(KERN_INFO "%s: MII transceiver #%d "
- "config %4.4x status %4.4x advertising %4.4x.\n",
- dev->name, phy, mii_reg0, mii_status, mii_advert);
- /* Fixup for DLink with miswired PHY. */
- if (mii_advert != reg4) {
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d,"
- " previously advertising %4.4x.\n",
- dev->name, reg4, phy, mii_advert);
- mdio_write(dev, phy, 4, reg4);
- }
- /* Enable autonegotiation: some boards default to off. */
- mdio_write(dev, phy, 0, mii_reg0 |
- (tp->full_duplex ? 0x1100 : 0x1000) |
- (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
- }
- }
- tp->mii_cnt = phy_idx;
- if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
- printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
- dev->name);
- tp->phys[0] = 1;
- }
- }
-
- /* The Tulip-specific entries in the device structure. */
- dev->open = &tulip_open;
- dev->hard_start_xmit = &tulip_start_xmit;
- dev->stop = &tulip_close;
- dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
- dev->do_ioctl = &private_ioctl;
-#endif
-#ifdef HAVE_MULTICAST
- dev->set_multicast_list = &set_rx_mode;
-#endif
- dev->tx_timeout = tulip_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- /* Reset the xcvr interface and turn on heartbeat. */
- switch (chip_idx) {
- case DC21041:
- outl(0x00000000, ioaddr + CSR13);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx);
- outl(0x0000EF05, ioaddr + CSR13);
- break;
- case DC21040:
- outl(0x00000000, ioaddr + CSR13);
- outl(0x00000004, ioaddr + CSR13);
- break;
- case DC21140: default:
- if (tp->mtable)
- outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
- break;
- case DC21142:
- case PNIC2:
- if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) {
- outl_CSR6(0x82020000, ioaddr, chip_idx);
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl_CSR6(0x820E0000, ioaddr, chip_idx);
- } else {
- outl_CSR6(0x82420200, ioaddr, chip_idx);
- outl(0x0001, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- outl(0x0001, ioaddr + CSR13);
- outl(0x1301, ioaddr + CSR12); /* Start NWay. */
- }
- break;
- case X3201_3:
- outl(0x0008, ioaddr + CSR15);
- udelay(5); /* The delays are Xircom recommended to give the
- * chipset time to reset the actual hardware
- * on the PCMCIA card
- */
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- outl_CSR6(0x32000200, ioaddr, chip_idx);
- break;
- case LC82C168:
- if ( ! tp->mii_cnt) {
- outl_CSR6(0x00420000, ioaddr, chip_idx);
- outl(0x30, ioaddr + CSR12);
- outl(0x0001F078, ioaddr + 0xB8);
- outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
- }
- break;
- case MX98713: case COMPEX9881:
- outl_CSR6(0x00000000, ioaddr, chip_idx);
- outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
- outl(0x00000001, ioaddr + CSR13);
- break;
- case MX98715: case MX98725:
- outl_CSR6(0x01a80000, ioaddr, chip_idx);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00001000, ioaddr + CSR12);
- break;
- case COMET:
- /* No initialization necessary. */
- break;
- }
-
- return dev;
-}
-
-/* Serial EEPROM section. */
-/* The main routine to parse the very complicated SROM structure.
- Search www.digital.com for "21X4 SROM" to get details.
- This code is very complex, and will require changes to support
- additional cards, so I'll be verbose about what is going on.
- */
-
-/* Known cards that have old-style EEPROMs. */
-static struct fixups {
- char *name;
- unsigned char addr0, addr1, addr2;
- u16 newtable[32]; /* Max length below. */
-} eeprom_fixups[] = {
- {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
- 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
- {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
- 0x0000, 0x009E, /* 10baseT */
- 0x0903, 0x006D, /* 100baseTx */ }},
- {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
- 0x0107, 0x8021, /* 100baseFx */
- 0x0108, 0x8021, /* 100baseFx-FD */
- 0x0103, 0x006D, /* 100baseTx */ }},
- {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
- 0x1001, 0x009E, /* 10base2, CSR12 0x10*/
- 0x0000, 0x009E, /* 10baseT */
- 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
- {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
- 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
- 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
- 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
- }},
- {0, 0, 0, 0, {}}};
-
-static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
-
-#if defined(__i386__) /* AKA get_unaligned() */
-#define get_u16(ptr) (*(u16 *)(ptr))
-#else
-#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
-#endif
-
-static void parse_eeprom(struct net_device *dev)
-{
- /* The last media info list parsed, for multiport boards. */
- static struct mediatable *last_mediatable = NULL;
- static unsigned char *last_ee_data = NULL;
- static int controller_index = 0;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- unsigned char *ee_data = tp->eeprom;
- int i;
-#ifdef CARDBUS
- int chip_rev = tp->revision;
-#endif
-
- tp->mtable = 0;
- for (i = 0; i < EEPROM_SIZE/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect an old-style (SA only) EEPROM layout:
- memcmp(eedata, eedata+16, 8). */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- break;
- if (i >= 8) {
- if (ee_data[0] == 0xff) {
- if (last_mediatable) {
- controller_index++;
- printk(KERN_INFO "%s: Controller %d of multiport board.\n",
- dev->name, controller_index);
- tp->mtable = last_mediatable;
- ee_data = last_ee_data;
- goto subsequent_board;
- } else
- printk(KERN_INFO "%s: Missing EEPROM, this interface may "
- "not work correctly!\n",
- dev->name);
- return;
- }
- /* Do a fix-up based on the vendor half of the station address prefix. */
- for (i = 0; eeprom_fixups[i].name; i++) {
- if (dev->dev_addr[0] == eeprom_fixups[i].addr0
- && dev->dev_addr[1] == eeprom_fixups[i].addr1
- && dev->dev_addr[2] == eeprom_fixups[i].addr2) {
- if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
- i++; /* An Accton EN1207, not an outlaw Maxtech. */
- memcpy(ee_data + 26, eeprom_fixups[i].newtable,
- sizeof(eeprom_fixups[i].newtable));
- printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using"
- " substitute media control info.\n",
- dev->name, eeprom_fixups[i].name);
- break;
- }
- }
- if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
- printk(KERN_INFO "%s: Old style EEPROM with no media selection "
- "information.\n",
- dev->name);
- return;
- }
- }
-
- controller_index = 0;
- if (ee_data[19] > 1) { /* Multiport board. */
- last_ee_data = ee_data;
- }
-subsequent_board:
-
- if (ee_data[27] == 0) { /* No valid media table. */
- } else if (tp->chip_id == DC21041) {
- unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
- short media;
- int count;
-
- media = get_u16(p);
- p += 2;
- count = *p++;
-
- printk(KERN_INFO "%s:21041 Media information at %d, default media "
- "%4.4x (%s).\n", dev->name, ee_data[27], media,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- unsigned char media_code = *p++;
- u16 csrvals[3];
- int idx;
- for (idx = 0; idx < 3; idx++) {
- csrvals[idx] = get_u16(p);
- p += 2;
- }
- if (media_code & 0x40) {
- printk(KERN_INFO "%s: 21041 media %2.2x (%s),"
- " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
- dev->name, media_code & 15, medianame[media_code & 15],
- csrvals[0], csrvals[1], csrvals[2]);
- } else
- printk(KERN_INFO "%s: 21041 media #%d, %s.\n",
- dev->name, media_code & 15, medianame[media_code & 15]);
- }
- } else {
- unsigned char *p = (void *)ee_data + ee_data[27];
- unsigned char csr12dir = 0;
- int count;
- struct mediatable *mtable;
- u16 media = get_u16(p);
-
- p += 2;
- if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
- csr12dir = *p++;
- count = *p++;
- mtable = (struct mediatable *)
- kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
- GFP_KERNEL);
- if (mtable == NULL)
- return; /* Horrible, impossible failure. */
- last_mediatable = tp->mtable = mtable;
- mtable->defaultmedia = media;
- mtable->leafcount = count;
- mtable->csr12dir = csr12dir;
- mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
- mtable->csr15dir = mtable->csr15val = 0;
-
- printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- struct medialeaf *leaf = &mtable->mleaf[i];
-
- if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
- leaf->type = 0;
- leaf->media = p[0] & 0x3f;
- leaf->leafdata = p;
- if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
- mtable->has_mii = 1;
- p += 4;
- } else {
- leaf->type = p[1];
- if (p[1] == 0x05) {
- mtable->has_reset = i;
- leaf->media = p[2] & 0x0f;
- } else if (p[1] & 1) {
- mtable->has_mii = 1;
- leaf->media = 11;
- } else {
- mtable->has_nonmii = 1;
- leaf->media = p[2] & 0x0f;
- if (p[1] == 2) {
- if (leaf->media == 0) {
- mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
- mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
- } else if (leaf->media == 0x40) {
- u32 base15 = get_unaligned((u16*)&p[7]);
- mtable->csr15dir =
- (get_unaligned((u16*)&p[9])<<16) + base15;
- mtable->csr15val =
- (get_unaligned((u16*)&p[11])<<16) + base15;
- }
- }
- }
- leaf->leafdata = p + 2;
- p += (p[0] & 0x3f) + 1;
- }
- if (tulip_debug > 1 && leaf->media == 11) {
- unsigned char *bp = leaf->leafdata;
- printk(KERN_INFO "%s: MII interface PHY %d, setup/reset "
- "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
- dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
- bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
- }
- printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described "
- "by a %s (%d) block.\n",
- dev->name, i, medianame[leaf->media], leaf->media,
- block_name[leaf->type], leaf->type);
- }
- }
-}
-/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
-#define EE_CS 0x01 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x01
-#define EE_WRITE_1 0x05
-#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
-#define EE_ENB (0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
- We add a bus turn-around to insure that this remains true. */
-#define eeprom_delay() inl(ee_addr)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5 << addr_len)
-#define EE_READ_CMD (6 << addr_len)
-#define EE_ERASE_CMD (7 << addr_len)
-
-static int read_eeprom(long ioaddr, int location, int addr_len)
-{
- int i;
- unsigned short retval = 0;
- long ee_addr = ioaddr + CSR9;
- int read_cmd = location | EE_READ_CMD;
-
- outl(EE_ENB & ~EE_CS, ee_addr);
- outl(EE_ENB, ee_addr);
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- outl(EE_ENB | dataval, ee_addr);
- eeprom_delay();
- outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- }
- outl(EE_ENB, ee_addr);
-
- for (i = 16; i > 0; i--) {
- outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
- outl(EE_ENB, ee_addr);
- eeprom_delay();
- }
-
- /* Terminate the EEPROM access. */
- outl(EE_ENB & ~EE_CS, ee_addr);
- return retval;
-}
-
-/* MII transceiver control section.
- Read and write the MII registers using software-generated serial
- MDIO protocol. See the MII specifications or DP83840A data sheet
- for details. */
-
-/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues or future 66Mhz PCI. */
-#define mdio_delay() inl(mdio_addr)
-
-/* Read and write the MII registers using software-generated serial
- MDIO protocol. It is just different enough from the EEPROM protocol
- to not share code. The maxium data clock rate is 2.5 Mhz. */
-#define MDIO_SHIFT_CLK 0x10000
-#define MDIO_DATA_WRITE0 0x00000
-#define MDIO_DATA_WRITE1 0x20000
-#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
-#define MDIO_ENB_IN 0x40000
-#define MDIO_DATA_READ 0x80000
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int retval = 0;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- while (--i > 0)
- if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
- return retval & 0xffff;
- return 0xffff;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id == 1) {
- if (location < 7)
- return inl(ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- return inl(ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- return inl(ioaddr + 0xD4 + ((location-29)<<2));
- }
- return 0xffff;
- }
-
- /* Establish sync by sending at least 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
-
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(cmd, ioaddr + 0xA0);
- do
- if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
- break;
- while (--i > 0);
- return;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id != 1)
- return;
- if (location < 7)
- outl(value, ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- outl(value, ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- outl(value, ioaddr + 0xD4 + ((location-29)<<2));
- return;
- }
-
- /* Establish sync by sending 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return;
-}
-
-static void
-tulip_up(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int i;
-
- /* On some chip revs we must set the MII/SYM port before the reset!? */
- if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- outl_CSR6(0x00040000, ioaddr, tp->chip_id);
-
- /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
- outl(0x00000001, ioaddr + CSR0);
-
- /* Deassert reset. */
- outl(tp->csr0, ioaddr + CSR0);
- udelay(2);
-
- if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
- pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
-
- /* Clear the tx ring */
- for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- }
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
-
- if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
- u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
- if (tp->chip_id == AX88140) {
- outl(0, ioaddr + CSR13);
- outl(addr_low, ioaddr + CSR14);
- outl(1, ioaddr + CSR13);
- outl(addr_high, ioaddr + CSR14);
- } else if (tp->chip_id == COMET) {
- outl(addr_low, ioaddr + 0xA4);
- outl(addr_high, ioaddr + 0xA8);
- outl(0, ioaddr + 0xAC);
- outl(0, ioaddr + 0xB0);
- }
- } else if (tp->chip_id != X3201_3) {
- /* This is set_rx_mode(), but without starting the transmitter. */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[15*6];
-
- /* 21140 bug: you must add the broadcast address. */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* Fill the final entry of the table with our physical address. */
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- /* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[0].status = DescOwned;
-
- tp->cur_tx++;
- } else { /* X3201_3 */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[0*6];
-
- /* fill the table with the broadcast address */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* re-fill the first 14 table entries with our address */
- for(i=0; i<14; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
-
- /* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- /* Lie about the address of our setup frame to make the */
- /* chip happy */
- tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- tp->tx_ring[0].status = DescOwned;
-
- tp->cur_tx++;
- }
- outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
- outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
-
- tp->saved_if_port = dev->if_port;
- if (dev->if_port == 0)
- dev->if_port = tp->default_port;
- if (tp->chip_id == DC21041 && dev->if_port > 4)
- /* Invalid: Select initial TP, autosense, autonegotiate. */
- dev->if_port = 4;
-
- /* Allow selecting a default media. */
- i = 0;
- if (tp->mtable == NULL)
- goto media_picked;
- if (dev->if_port) {
- int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
- (dev->if_port == 12 ? 0 : dev->if_port);
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using user-specified media %s.\n",
- dev->name, medianame[dev->if_port]);
- goto media_picked;
- }
- }
- if ((tp->mtable->defaultmedia & 0x0800) == 0) {
- int looking_for = tp->mtable->defaultmedia & 15;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
- dev->name, medianame[looking_for]);
- goto media_picked;
- }
- }
- /* Start sensing first non-full-duplex media. */
- for (i = tp->mtable->leafcount - 1;
- (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
- ;
-media_picked:
-
- tp->csr6 = 0;
- tp->cur_index = i;
- if (dev->if_port == 0 && tp->chip_id == DC21142) {
- if (tp->mii_cnt) {
- select_media(dev, 1);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Using MII transceiver %d, status "
- "%4.4x.\n",
- dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
- outl_CSR6(0x82020000, ioaddr, tp->chip_id);
- tp->csr6 = 0x820E0000;
- dev->if_port = 11;
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- } else
- t21142_start_nway(dev);
- } else if ((tp->chip_id == LC82C168 || tp->chip_id == PNIC2)
- && tp->mii_cnt && ! tp->medialock) {
- dev->if_port = 11;
- tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0001, ioaddr + CSR15);
- } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
- && ! tp->medialock) {
- dev->if_port = 0;
- tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
- /* Provided by BOLO, Macronix - 12/10/1998. */
- dev->if_port = 0;
- tp->csr6 = 0x01880200;
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
- } else if (tp->chip_id == DC21143 &&
- media_cap[dev->if_port] & MediaIsMII) {
- /* We must reset the media CSRs when we force-select MII mode. */
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- } else if (tp->chip_id == X3201_3) {
- outl(0x0008, ioaddr + CSR15);
- udelay(5);
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- tp->csr6 = 0x32400000;
- } else if (tp->chip_id == COMET) {
- dev->if_port = 0;
- tp->csr6 = 0x00040000;
- } else
- select_media(dev, 1);
-
- /* Start the chip's Tx to process setup frame. */
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
-
- /* Enable interrupts by setting the interrupt mask. */
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- outl(0, ioaddr + CSR2); /* Rx poll demand */
-
- netif_start_queue (dev);
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
- dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
- inl(ioaddr + CSR6));
- }
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = RUN_AT(5*HZ);
- tp->timer.data = (unsigned long)dev;
- tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
- add_timer(&tp->timer);
-}
-
-static int
-tulip_open(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
- return -EAGAIN;
-
- tulip_init_ring(dev);
-
- tulip_up(dev);
- tp->open = 1;
- MOD_INC_USE_COUNT;
-
- return 0;
-}
-
-/* Set up the transceiver control registers for the selected media type. */
-static void select_media(struct net_device *dev, int startup)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- struct mediatable *mtable = tp->mtable;
- u32 new_csr6;
- int i;
-
- if (mtable) {
- struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
- unsigned char *p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: /* 21140 non-MII xcvr. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
- " with control setting %2.2x.\n",
- dev->name, p[1]);
- dev->if_port = p[0];
- if (startup)
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- outl(p[1], ioaddr + CSR12);
- new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
- break;
- case 2: case 4: {
- u16 setup[5];
- u32 csr13val, csr14val, csr15dir, csr15val;
- for (i = 0; i < 5; i++)
- setup[i] = get_u16(&p[i*2 + 1]);
-
- dev->if_port = p[0] & 15;
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
-
- if (startup && mtable->has_reset) {
- struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
- unsigned char *rst = rleaf->leafdata;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
- dev->name);
- for (i = 0; i < rst[0]; i++)
- outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
- "%4.4x/%4.4x.\n",
- dev->name, medianame[dev->if_port], setup[0], setup[1]);
- if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
- csr13val = setup[0];
- csr14val = setup[1];
- csr15dir = (setup[3]<<16) | setup[2];
- csr15val = (setup[4]<<16) | setup[2];
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- outl(csr13val, ioaddr + CSR13);
- } else {
- csr13val = 1;
- csr14val = 0x0003FF7F;
- csr15dir = (setup[0]<<16) | 0x0008;
- csr15val = (setup[1]<<16) | 0x0008;
- if (dev->if_port <= 4)
- csr14val = t21142_csr14[dev->if_port];
- if (startup) {
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- }
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- if (startup) outl(csr13val, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n",
- dev->name, csr15dir, csr15val);
- if (mleaf->type == 4)
- new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
- else
- new_csr6 = 0x82420000;
- break;
- }
- case 1: case 3: {
- int phy_num = p[0];
- int init_length = p[1];
- u16 *misc_info;
- u16 to_advertise;
-
- dev->if_port = 11;
- new_csr6 = 0x020E0000;
- if (mleaf->type == 3) { /* 21142 */
- u16 *init_sequence = (u16*)(p+2);
- u16 *reset_sequence = &((u16*)(p+3))[init_length];
- int reset_length = p[2 + init_length*2];
- misc_info = reset_sequence + reset_length;
- if (startup)
- for (i = 0; i < reset_length; i++)
- outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
- for (i = 0; i < init_length; i++)
- outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
- } else {
- u8 *init_sequence = p + 2;
- u8 *reset_sequence = p + 3 + init_length;
- int reset_length = p[2 + init_length];
- misc_info = (u16*)(reset_sequence + reset_length);
- if (startup) {
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- for (i = 0; i < reset_length; i++)
- outl(reset_sequence[i], ioaddr + CSR12);
- }
- for (i = 0; i < init_length; i++)
- outl(init_sequence[i], ioaddr + CSR12);
- }
- to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
- tp->advertising[phy_num] = to_advertise;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n",
- dev->name, to_advertise, phy_num, tp->phys[phy_num]);
- /* Bogus: put in by a committee? */
- mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
- break;
- }
- default:
- printk(KERN_DEBUG "%s: Invalid media table selection %d.\n",
- dev->name, mleaf->type);
- new_csr6 = 0x020E0000;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12) & 0xff);
- } else if (tp->chip_id == DC21041) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
- dev->name, medianame[dev->if_port & 15],
- inl(ioaddr + CSR12) & 0xffff);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- new_csr6 = 0x80020000;
- } else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) {
- if (startup && ! tp->medialock)
- dev->if_port = tp->mii_cnt ? 11 : 0;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
- " media %s.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
- medianame[dev->if_port]);
- if (tp->mii_cnt) {
- new_csr6 = 0x810C0000;
- outl(0x0001, ioaddr + CSR15);
- outl(0x0201B07A, ioaddr + 0xB8);
- } else if (startup) {
- /* Start with 10mbps to do autonegotiation. */
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x0001B078, ioaddr + 0xB8);
- outl(0x0201B078, ioaddr + 0xB8);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- if (startup)
- outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
- else
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- } else if (tp->chip_id == DC21040) { /* 21040 */
- /* Turn on the xcvr interface. */
- int csr12 = inl(ioaddr + CSR12);
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port], csr12);
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- new_csr6 = 0x20000;
- /* Set the full duplux match frame. */
- outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- if (t21040_csr13[dev->if_port] & 8) {
- outl(0x0705, ioaddr + CSR14);
- outl(0x0006, ioaddr + CSR15);
- } else {
- outl(0xffff, ioaddr + CSR14);
- outl(0x0000, ioaddr + CSR15);
- }
- outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
- } else if (tp->chip_id == X3201_3) { /* Xircom */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
-/* Someone is on crack, the Xircom only does MII, no Fx */
-/* if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;*/
- new_csr6 = 0x324c0000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Xircom CardBus Adapter: "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- } else { /* Unknown chip type with no media table. */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
- if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No media description table, assuming "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- }
-
- tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
- return;
-}
-
-/*
- Check the MII negotiated duplex, and change the CSR6 setting if
- required.
- Return 0 if everything is OK.
- Return < 0 if the transceiver is missing or has no link beat.
- */
-static int check_duplex(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int mii_reg1, mii_reg5, negotiated, duplex;
-
- if (tp->full_duplex_lock)
- return 0;
- mii_reg1 = mdio_read(dev, tp->phys[0], 1);
- mii_reg5 = mdio_read(dev, tp->phys[0], 5);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
- "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
- if (mii_reg1 == 0xffff)
- return -2;
- if ((mii_reg1 & 0x0004) == 0) {
- int new_reg1 = mdio_read(dev, tp->phys[0], 1);
- if ((new_reg1 & 0x0004) == 0) {
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: No link beat on the MII interface,"
- " status %4.4x.\n", dev->name, new_reg1);
- return -1;
- }
- }
- negotiated = mii_reg5 & tp->advertising[0];
- duplex = ((negotiated & 0x0300) == 0x0100
- || (negotiated & 0x00C0) == 0x0040);
- /* 100baseTx-FD or 10T-FD, but not 100-HD */
- if (tp->full_duplex != duplex) {
- tp->full_duplex = duplex;
- if (tp->full_duplex) tp->csr6 |= 0x0200;
- else tp->csr6 &= ~0x0200;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- if (tulip_debug > 0)
- printk(KERN_INFO "%s: Setting %s-duplex based on MII"
- "#%d link partner capability of %4.4x.\n",
- dev->name, tp->full_duplex ? "full" : "half",
- tp->phys[0], mii_reg5);
- }
- return 0;
-}
-
-static void tulip_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u32 csr12 = inl(ioaddr + CSR12);
- int next_tick = 2*HZ;
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
- "SIA %8.8x %8.8x %8.8x %8.8x.\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
- csr12, inl(ioaddr + CSR13),
- inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- }
- switch (tp->chip_id) {
- case DC21040:
- if (!tp->medialock && csr12 & 0x0002) { /* Network error */
- printk(KERN_INFO "%s: No link beat found.\n",
- dev->name);
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- select_media(dev, 0);
- dev->trans_start = jiffies;
- }
- break;
- case DC21041:
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n",
- dev->name, csr12);
- switch (dev->if_port) {
- case 0: case 3: case 4:
- if (csr12 & 0x0004) { /*LnkFail */
- /* 10baseT is dead. Check for activity on alternate port. */
- tp->mediasense = 1;
- if (csr12 & 0x0200)
- dev->if_port = 2;
- else
- dev->if_port = 1;
- printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
- dev->name, medianame[dev->if_port]);
- outl(0, ioaddr + CSR13); /* Reset */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- next_tick = 10*HZ; /* 2.4 sec. */
- } else
- next_tick = 30*HZ;
- break;
- case 1: /* 10base2 */
- case 2: /* AUI */
- if (csr12 & 0x0100) {
- next_tick = (30*HZ); /* 30 sec. */
- tp->mediasense = 0;
- } else if ((csr12 & 0x0004) == 0) {
- printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
- dev->name);
- dev->if_port = 0;
- select_media(dev, 0);
- next_tick = (24*HZ)/10; /* 2.4 sec. */
- } else if (tp->mediasense || (csr12 & 0x0002)) {
- dev->if_port = 3 - dev->if_port; /* Swap ports. */
- select_media(dev, 0);
- next_tick = 20*HZ;
- } else {
- next_tick = 20*HZ;
- }
- break;
- }
- break;
- case DC21140: case DC21142: case MX98713: case COMPEX9881: default: {
- struct medialeaf *mleaf;
- unsigned char *p;
- if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */
- /* Not much that can be done.
- Assume this a generic MII or SYM transceiver. */
- next_tick = 60*HZ;
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
- "CSR12 0x%2.2x.\n",
- dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
- break;
- }
- mleaf = &tp->mtable->mleaf[tp->cur_index];
- p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: case 4: {
- /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */
- int offset = mleaf->type == 4 ? 5 : 2;
- s8 bitnum = p[offset];
- if (p[offset+1] & 0x80) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG"%s: Transceiver monitor tick "
- "CSR12=%#2.2x, no media sense.\n",
- dev->name, csr12);
- if (mleaf->type == 4) {
- if (mleaf->media == 3 && (csr12 & 0x02))
- goto select_next_media;
- }
- break;
- }
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
- " bit %d is %d, expecting %d.\n",
- dev->name, csr12, (bitnum >> 1) & 7,
- (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
- (bitnum >= 0));
- /* Check that the specified bit has the proper value. */
- if ((bitnum < 0) !=
- ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
- medianame[mleaf->media]);
- if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */
- goto actually_mii;
- break;
- }
- if (tp->medialock)
- break;
- select_next_media:
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
- if (media_cap[dev->if_port] & MediaIsFD)
- goto select_next_media; /* Skip FD entries. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No link beat on media %s,"
- " trying transceiver type %s.\n",
- dev->name, medianame[mleaf->media & 15],
- medianame[tp->mtable->mleaf[tp->cur_index].media]);
- select_media(dev, 0);
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- next_tick = (24*HZ)/10;
- break;
- }
- case 1: case 3: /* 21140, 21142 MII */
- actually_mii:
- check_duplex(dev);
- next_tick = 60*HZ;
- break;
- case 2: /* 21142 serial block has no link beat. */
- default:
- break;
- }
- }
- break;
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
- of available transceivers. */
-static void t21142_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = 0;
-
- if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII))
- printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
- dev->name, csr12, medianame[dev->if_port]);
- if (media_cap[dev->if_port] & MediaIsMII) {
- check_duplex(dev);
- next_tick = 60*HZ;
- } else if (tp->nwayset) {
- /* Don't screw up a negotiated session! */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
- dev->name, medianame[dev->if_port], csr12);
- } else if (tp->medialock) {
- ;
- } else if (dev->if_port == 3) {
- if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
- "trying NWay.\n", dev->name, csr12);
- t21142_start_nway(dev);
- next_tick = 3*HZ;
- }
- } else if (((csr12 & 0x7000) != 0x5000)
- && tp->chip_id != X3201_3) {
- /* Negotiation failed. Search media types. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
- dev->name, csr12);
- if (!(csr12 & 4)) { /* 10mbps link beat good. */
- new_csr6 = 0x82420000;
- dev->if_port = 0;
- outl(0, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
- } else {
- /* Select 100mbps port to check for link beat. */
- new_csr6 = 0x83860000;
- dev->if_port = 3;
- outl(0, ioaddr + CSR13);
- outl(0x0003FF7F, ioaddr + CSR14);
- outw(8, ioaddr + CSR15);
- outl(1, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
- dev->name, medianame[dev->if_port]);
- if (new_csr6 != (tp->csr6 & ~0x00D5)) {
- tp->csr6 &= 0x00D5;
- tp->csr6 |= new_csr6;
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- next_tick = 3*HZ;
- }
- if (tp->cur_tx - tp->dirty_tx > 0 &&
- jiffies - dev->trans_start > TX_TIMEOUT) {
- printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- tulip_tx_timeout(dev);
- }
-
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void t21142_start_nway(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr14 = ((tp->to_advertise & 0x0180) << 9) |
- ((tp->to_advertise&0x0020)<<1) | 0xffbf;
-
- dev->if_port = 0;
- tp->nway = tp->mediasense = 1;
- tp->nwayset = tp->lpar = 0;
- if (debug > 1)
- printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
- dev->name, csr14);
- outl(0x0001, ioaddr + CSR13);
- outl(csr14, ioaddr + CSR14);
- tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- if (tp->mtable && tp->mtable->csr15dir) {
- outl(tp->mtable->csr15dir, ioaddr + CSR15);
- outl(tp->mtable->csr15val, ioaddr + CSR15);
- } else
- outw(0x0008, ioaddr + CSR15);
- outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */
-}
-
-static void t21142_lnk_change(struct net_device *dev, int csr5)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
-
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
- "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
-
- /* If NWay finished and we have a negotiated partner capability. */
- if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) {
- int setup_done = 0;
- tp->lpar = csr12 >> 16;
- tp->nwayset = 1;
- if (csr12 & 0x01000000) dev->if_port = 5;
- else if (csr12 & 0x00800000) dev->if_port = 3;
- else if (csr12 & 0x00400000) dev->if_port = 4;
- else if (csr12 & 0x00200000) dev->if_port = 0;
- else {
- tp->nwayset = 0;
- if ( ! (csr12 & 2)) dev->if_port = 3;
- else if ( ! (csr12 & 4)) dev->if_port = 0;
- }
- tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0;
-
- if (tulip_debug > 1) {
- if (tp->nwayset)
- printk(KERN_INFO "%s: Switching to %s based on link partner "
- "advertisement %4.4x.\n",
- dev->name, medianame[dev->if_port], tp->lpar);
- else
- printk(KERN_INFO "%s: Switching to %s based on link beat "
- "status of %4.4x.\n",
- dev->name, medianame[dev->if_port], csr12);
- }
-
- if (tp->mtable) {
- int i;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == dev->if_port) {
- tp->cur_index = i;
- select_media(dev, 0);
- setup_done = 1;
- break;
- }
- }
- if ( ! setup_done) {
- tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
- if (tp->full_duplex)
- tp->csr6 |= 0x0200;
- outw(0x0000, ioaddr + CSR13);
- outw(0x0000, ioaddr + CSR14);
- }
- outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id);
- if (debug > 2)
- printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
- dev->name, inl(ioaddr + CSR5));
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- } else if ((tp->nwayset && (csr5 & 0x08000000)
- && (dev->if_port == 3 || dev->if_port == 5)
- && (csr12 & 2) == 2) ||
- (tp->nway && (csr5 & (TPLnkFail)))) {
- /* Link blew? Maybe restart NWay. */
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
- dev->name, medianame[dev->if_port],
- (csr12 & 2) ? "failed" : "good");
- if ((csr12 & 2) && ! tp->medialock) {
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- }
- } else if (dev->if_port == 0 || dev->if_port == 4) {
- if ((csr12 & 4) == 0)
- printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
- dev->name);
- } else if (!(csr12 & 4)) { /* 10mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
- dev->name);
- dev->if_port = 0;
- } else if (tp->nwayset) {
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
- dev->name, medianame[dev->if_port], tp->csr6);
- } else { /* 100mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
- dev->name);
- dev->if_port = 3;
- tp->csr6 = 0x83860000;
- outl(0x0003FF7F, ioaddr + CSR14);
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
-}
-
-static void mxic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 3) {
- printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
- inl(ioaddr + CSR12));
- }
- if (next_tick) {
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
- }
-}
-
-static void pnic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = tp->csr6 & ~0x40C40200;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, "
- "CSR5 %8.8x.\n",
- dev->name, negotiated, inl(ioaddr + CSR5));
-
- if (negotiated & 0x0380) /* 10 vs 100mbps */
- new_csr6 |= 0x810E0000;
- else
- new_csr6 |= 0x814E0000;
- if (((negotiated & 0x0300) == 0x0100) /* Duplex */
- || (negotiated & 0x00C0) == 0x0040
- || tp->full_duplex_lock) {
- tp->full_duplex = 1;
- new_csr6 |= 0x0200;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link "
- "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
- dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
- tp->csr6, inl(ioaddr + CSR6));
- } else {
- int phy_reg = inl(ioaddr + 0xB8);
- int csr5 = inl(ioaddr + CSR5);
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n",
- dev->name, phy_reg, csr5);
-
- if (phy_reg & 0x04000000) { /* Remote link fault */
- /*outl(0x0201F078, ioaddr + 0xB8);*/
- next_tick = 3*HZ;
- }
- if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
- "CSR5 %8.8x, PHY %3.3x.\n",
- dev->name, medianame[dev->if_port], csr12,
- inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
- if (tp->medialock) {
- } else if (dev->if_port == 0) {
- dev->if_port = 3;
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- dev->if_port = 0;
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- new_csr6 |= (tp->csr6 & 0xfdff);
- next_tick = 3*HZ;
- } else
- new_csr6 = tp->csr6;
- if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) {
- tp->full_duplex = 1;
- new_csr6 |= 0x00000200;
- }
- }
- if (tp->csr6 != new_csr6) {
- tp->csr6 = new_csr6;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); /* Restart Tx */
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- dev->trans_start = jiffies;
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
- "CSR6 %8.8x.\n",
- dev->name, tp->full_duplex ? "full" : "half", new_csr6);
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void comet_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
- "%4.4x.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void tulip_tx_timeout(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- /* Do nothing -- the media monitor should handle this. */
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
- dev->name);
- } else if (tp->chip_id == DC21040) {
- if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) {
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- printk(KERN_INFO "%s: transmit timed out, switching to "
- "%s.\n",
- dev->name, medianame[dev->if_port]);
- select_media(dev, 0);
- }
- dev->trans_start = jiffies;
- return;
- } else if (tp->chip_id == DC21041) {
- int csr12 = inl(ioaddr + CSR12);
-
- printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
- "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), csr12,
- inl(ioaddr + CSR13), inl(ioaddr + CSR14));
- tp->mediasense = 1;
- if ( ! tp->medialock) {
- if (dev->if_port == 1 || dev->if_port == 2)
- if (csr12 & 0x0004) {
- dev->if_port = 2 - dev->if_port;
- } else
- dev->if_port = 0;
- else
- dev->if_port = 1;
- select_media(dev, 0);
- }
- } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
- || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
- printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
- "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
- inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- if ( ! tp->medialock && tp->mtable) {
- do
- --tp->cur_index;
- while (tp->cur_index >= 0
- && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
- & MediaIsFD));
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- select_media(dev, 0);
- printk(KERN_WARNING "%s: transmit timed out, switching to %s "
- "media.\n", dev->name, medianame[dev->if_port]);
- }
- } else {
- printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
- "%8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
- dev->if_port = 0;
- }
-
-#if defined(way_too_many_messages)
- if (tulip_debug > 3) {
- int i;
- for (i = 0; i < RX_RING_SIZE; i++) {
- u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
- int j;
- printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x "
- "%2.2x %2.2x %2.2x.\n",
- i, (unsigned int)tp->rx_ring[i].status,
- (unsigned int)tp->rx_ring[i].length,
- (unsigned int)tp->rx_ring[i].buffer1,
- (unsigned int)tp->rx_ring[i].buffer2,
- buf[0], buf[1], buf[2]);
- for (j = 0; buf[j] != 0xee && j < 1600; j++)
- if (j < 100) printk(" %2.2x", buf[j]);
- printk(" j=%d.\n", j);
- }
- printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
- printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
- printk("\n");
- }
-#endif
-
- /* Stop and restart the chip's Tx processes . */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
-
- dev->trans_start = jiffies;
- netif_wake_queue (dev);
- tp->stats.tx_errors++;
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void tulip_init_ring(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- tp->tx_full = 0;
- tp->cur_rx = tp->cur_tx = 0;
- tp->dirty_rx = tp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- tp->rx_ring[i].status = 0x00000000;
- tp->rx_ring[i].length = PKT_BUF_SZ;
- tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
- tp->rx_skbuff[i] = NULL;
- }
- /* Mark the last entry as wrapping the ring. */
- tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
- tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- /* Note the receive buffer must be longword aligned.
- dev_alloc_skb() provides 16 byte alignment. But do *not*
- use skb_reserve() to align the IP header! */
- struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
- tp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */
- tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
- }
- tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-
- /* The Tx buffer descriptor is filled in as needed, but we
- do need to clear the ownership bit. */
- for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3)
- tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif CARDBUS
- }
- tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
-}
-
-static int
-tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry;
- u32 flag;
-
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- /* Calculate the next Tx descriptor entry. */
- entry = tp->cur_tx % TX_RING_SIZE;
-
- tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3) {
- memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
- } else
-#endif
- tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
-
- if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
- flag = 0x60000000; /* No interrupt */
- } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
- flag = 0xe0000000; /* Tx-done intr. */
- } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
- flag = 0x60000000; /* No Tx-done intr. */
- } else {
- /* Leave room for set_rx_mode() to fill entries. */
- flag = 0xe0000000; /* Tx-done intr. */
- tp->tx_full = 1;
- }
- if (entry == TX_RING_SIZE-1)
- flag |= 0xe0000000 | DESC_RING_WRAP;
-
- tp->tx_ring[entry].length = skb->len | flag;
- tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */
- tp->cur_tx++;
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- /* Trigger an immediate transmit demand. */
- outl(0, dev->base_addr + CSR1);
-
- dev->trans_start = jiffies;
-
- return 0;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_instance;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr5, work_budget = max_interrupt_work;
-
- spin_lock (&tp->lock);
-
- do {
- csr5 = inl(ioaddr + CSR5);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outl(csr5 & 0x0001ffff, ioaddr + CSR5);
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
- dev->name, csr5, inl(dev->base_addr + CSR5));
-
- if (csr5 == 0xffffffff)
- break; /* all bits set, assume PCMCIA card removed */
-
- if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
- break;
-
- if (csr5 & (RxIntr | RxNoBuf))
- work_budget -= tulip_rx(dev);
-
- if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
- unsigned int dirty_tx;
-
- for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
- dirty_tx++) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = tp->tx_ring[entry].status;
-
- if (status < 0)
- break; /* It still hasn't been Txed */
- /* Check for Rx filter setup frames. */
- if (tp->tx_skbuff[entry] == NULL)
- continue;
-
- if (status & 0x8000) {
- /* There was an major error, log it. */
-#ifndef final_version
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
- dev->name, status);
-#endif
- tp->stats.tx_errors++;
- if (status & 0x4104) tp->stats.tx_aborted_errors++;
- if (status & 0x0C00) tp->stats.tx_carrier_errors++;
- if (status & 0x0200) tp->stats.tx_window_errors++;
- if (status & 0x0002) tp->stats.tx_fifo_errors++;
- if ((status & 0x0080) && tp->full_duplex == 0)
- tp->stats.tx_heartbeat_errors++;
-#ifdef ETHER_STATS
- if (status & 0x0100) tp->stats.collisions16++;
-#endif
- } else {
-#ifdef ETHER_STATS
- if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
- tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
- tp->stats.collisions += (status >> 3) & 15;
- tp->stats.tx_packets++;
- }
-
- /* Free the original skb. */
- dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
- }
-
-#ifndef final_version
- if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
- dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- if (tp->tx_full &&
- tp->cur_tx - dirty_tx < TX_RING_SIZE - 2)
- /* The ring is no longer full */
- tp->tx_full = 0;
-
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- tp->dirty_tx = dirty_tx;
- if (csr5 & TxDied) {
- if (tulip_debug > 2)
- printk(KERN_WARNING "%s: The transmitter stopped."
- " CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
- dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- }
-
- /* Log errors. */
- if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */
- if (csr5 == 0xffffffff)
- break;
- if (csr5 & TxJabber) tp->stats.tx_errors++;
- if (csr5 & TxFIFOUnderflow) {
- if ((tp->csr6 & 0xC000) != 0xC000)
- tp->csr6 += 0x4000; /* Bump up the Tx threshold */
- else
- tp->csr6 |= 0x00200000; /* Store-n-forward. */
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & RxDied) { /* Missed a Rx frame. */
- tp->stats.rx_errors++;
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & TimerInt) {
- if (tulip_debug > 2)
- printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
- dev->name, csr5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- }
- if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
- if ( tp->chip_id == DC21142)
- t21142_lnk_change(dev, csr5);
- }
- /* Clear all error sources, included undocumented ones! */
- outl(0x0800f7ba, ioaddr + CSR5);
- }
- if (--work_budget < 0) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Too much work during an interrupt, "
- "csr5=0x%8.8x.\n", dev->name, csr5);
- /* Acknowledge all interrupt sources. */
- outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
- /* Clear all but standard interrupt sources. */
- outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
- break;
- }
- } while (1);
-
- if (tulip_debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- spin_unlock (&tp->lock);
-}
-
-static int
-tulip_rx(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry = tp->cur_rx % RX_RING_SIZE;
- int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
- int work_done = 0;
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- /* If we own the next entry, it's a new packet. Send it up. */
- while (tp->rx_ring[entry].status >= 0) {
- s32 status = tp->rx_ring[entry].status;
-
- if (tulip_debug > 5)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- if (--rx_work_limit < 0)
- break;
- if ((status & 0x38008300) != 0x0300) {
- if ((status & 0x38000300) != 0x0300) {
- /* Ingore earlier buffers. */
- if ((status & 0xffff) != 0x7fff) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Oversized Ethernet frame "
- "spanned multiple buffers, status %8.8x!\n",
- dev->name, status);
- tp->stats.rx_length_errors++;
- }
- } else if (status & RxDescFatalErr) {
- /* There was a fatal error. */
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
- dev->name, status);
- tp->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x0890) tp->stats.rx_length_errors++;
- if (status & 0x0004) tp->stats.rx_frame_errors++;
- if (status & 0x0002) tp->stats.rx_crc_errors++;
- if (status & 0x0001) tp->stats.rx_fifo_errors++;
- }
- } else {
- /* Omit the four octet CRC from the length. */
- short pkt_len = ((status >> 16) & 0x7ff) - 4;
- struct sk_buff *skb;
-
-#ifndef final_version
- if (pkt_len > 1518) {
- printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
- dev->name, pkt_len, pkt_len);
- pkt_len = 1518;
- tp->stats.rx_length_errors++;
- }
-#endif
- /* Check if the packet is long enough to accept without copying
- to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
- skb_reserve(skb, 2); /* 16 byte align the IP header */
-#if ! defined(__alpha__)
- eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
- pkt_len, 0);
- skb_put(skb, pkt_len);
-#else
- memcpy(skb_put(skb, pkt_len),
- bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#endif
- work_done++;
- } else { /* Pass up the skb already on the Rx ring. */
- char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
- tp->rx_skbuff[entry] = NULL;
-#ifndef final_version
- if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
- printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
- "do not match in tulip_rx: %p vs. %p / %p.\n",
- dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
- skb->head, temp);
-#endif
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->last_rx = jiffies;
- tp->stats.rx_packets++;
- tp->stats.rx_bytes += pkt_len;
- }
- entry = (++tp->cur_rx) % RX_RING_SIZE;
- }
-
- /* Refill the Rx ring buffers. */
- for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
- entry = tp->dirty_rx % RX_RING_SIZE;
- if (tp->rx_skbuff[entry] == NULL) {
- struct sk_buff *skb;
- skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
- work_done++;
- }
- tp->rx_ring[entry].status = DescOwned;
- }
-
- return work_done;
-}
-
-static void
-tulip_down(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- /* Disable interrupts by clearing the interrupt mask. */
- outl(0x00000000, ioaddr + CSR7);
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
- /* 21040 -- Leave the card in 10baseT state. */
- if (tp->chip_id == DC21040)
- outl(0x00000004, ioaddr + CSR13);
-
- if (inl(ioaddr + CSR6) != 0xffffffff)
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- dev->if_port = tp->saved_if_port;
-}
-
-static int
-tulip_close(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- netif_stop_queue(dev);
-
- if (netif_device_present(dev))
- tulip_down(dev);
-
- del_timer(&tp->timer);
-
- free_irq(dev->irq, dev);
-
- /* Free all the skbuffs in the Rx queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
- tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
- tp->rx_ring[i].length = 0;
- tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
- if (skb) {
- dev_kfree_skb(skb);
- }
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (tp->tx_skbuff[i])
- dev_kfree_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
- }
-
- MOD_DEC_USE_COUNT;
- tp->open = 0;
- return 0;
-}
-
-static struct net_device_stats *tulip_get_stats(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (netif_device_present(dev))
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- return &tp->stats;
-}
-
-#ifdef HAVE_PRIVATE_IOCTL
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u16 *data = (u16 *)&rq->ifr_data;
- int phy = tp->phys[0] & 0x1f;
- long flags;
-
- switch(cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- if (tp->mii_cnt)
- data[0] = phy;
- else if (tp->chip_id == DC21142) /* 21142 pseudo-MII */
- data[0] = 32;
- else if (tp->chip_id == PNIC2)
- data[0] = 32;
- else if (tp->chip_id == COMET)
- data[0] = 1;
- else
- return -ENODEV;
- return 0;
- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- if (data[0] == 32 &&
- (tp->chip_id == DC21142 || tp->chip_id == PNIC2)) {
- int csr12 = inl(ioaddr + CSR12);
- int csr14 = inl(ioaddr + CSR14);
- switch (data[1]) {
- case 0: {
- data[3] = (csr14<<5) & 0x1000;
- break; }
- case 1:
- data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
- + (csr12&0x06 ? 0x04 : 0);
- break;
- case 4: {
- data[3] = ((csr14>>9)&0x0380) +
- ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
- break;
- }
- case 5: data[3] = csr12 >> 16; break;
- default: data[3] = 0; break;
- }
- } else {
- save_flags(flags);
- cli();
- data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
- restore_flags(flags);
- }
- return 0;
- case SIOCDEVPRIVATE+2: /* Write the specified MII register */
-#if defined(CAP_NET_ADMIN)
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-#else
- if (!suser())
- return -EPERM;
-#endif
- if (data[0] == 32 && tp->chip_id == DC21142) {
- if (data[1] == 5)
- tp->to_advertise = data[2];
- } else {
- save_flags(flags);
- cli();
- mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
- restore_flags(flags);
- }
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-
- return -EOPNOTSUPP;
-}
-#endif /* HAVE_PRIVATE_IOCTL */
-
-/* Set or clear the multicast filter for this adaptor.
- Note that we only use exclusion around actually queueing the
- new frame, not around filling tp->setup_frame. This is non-deterministic
- when re-entered but still correct. */
-
-/* The little-endian AUTODIN32 ethernet CRC calculation.
- N.B. Do not use for bulk data, use a table-based routine instead.
- This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline u32 ether_crc_le(int length, unsigned char *data)
-{
- u32 crc = 0xffffffff; /* Initial value. */
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 8; --bit >= 0; current_octet >>= 1) {
- if ((crc ^ current_octet) & 1) {
- crc >>= 1;
- crc ^= ethernet_polynomial_le;
- } else
- crc >>= 1;
- }
- }
- return crc;
-}
-static unsigned const ethernet_polynomial = 0x04c11db7U;
-static inline u32 ether_crc(int length, unsigned char *data)
-{
- int crc = -1;
-
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 0; bit < 8; bit++, current_octet >>= 1)
- crc = (crc << 1) ^
- ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
- }
- return crc;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- tp->csr6 &= ~0x00D5;
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- tp->csr6 |= 0x00C0;
- csr6 |= 0x00C0;
- /* Unconditionally log net taps. */
- printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
- } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter well -- accept all multicasts. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- /* Some work-alikes have only a 64-entry hash filter table. */
- /* Should verify correctness on big-endian/__powerpc__ */
- struct dev_mc_list *mclist;
- int i;
- u32 mc_filter[2]; /* Multicast hash filter */
- if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else {
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
- if (tp->chip_id == AX88140) {
- outl(2, ioaddr + CSR13);
- outl(mc_filter[0], ioaddr + CSR14);
- outl(3, ioaddr + CSR13);
- outl(mc_filter[1], ioaddr + CSR14);
- } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
- outl(mc_filter[0], ioaddr + 0xAC);
- outl(mc_filter[1], ioaddr + 0xB0);
- }
- }
- } else {
- u16 *eaddrs, *setup_frm = tp->setup_frame;
- struct dev_mc_list *mclist;
- u32 tx_flags = 0x08000000 | 192;
- int i;
-
- /* Note that only the low-address shortword of setup_frame is valid!
- The values are doubled for big-endian architectures. */
- if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */
- u16 hash_table[32];
- tx_flags = 0x08400000 | 192; /* Use hash filter. */
- memset(hash_table, 0, sizeof(hash_table));
- set_bit(255, hash_table); /* Broadcast entry */
- /* This should work on big-endian machines as well. */
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
- hash_table);
- for (i = 0; i < 32; i++) {
- *setup_frm++ = hash_table[i];
- *setup_frm++ = hash_table[i];
- }
- setup_frm = &tp->setup_frame[13*6];
- } else if(tp->chip_id != X3201_3) {
- /* We have <= 14 addresses so we can use the wonderful
- 16 address perfect filtering of the Tulip. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- } else {
- /* fill the first two table entries with our address */
- eaddrs = (u16 *)dev->dev_addr;
- for(i=0; i<2; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Double fill each entry to accomodate chips that */
- /* don't like to parse these correctly */
- for (i=0, mclist=dev->mc_list; i<dev->mc_count;
- i++, mclist=mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- i=((i+1)*2);
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- }
-
- /* Fill the final entry with our physical address. */
- eaddrs = (u16 *)dev->dev_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- /* Now add this frame to the Tx list. */
- if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
- /* Same setup recently queued, we need not add it. */
- } else {
- unsigned long flags;
- unsigned int entry, dummy = -1;
-
- save_flags(flags); cli();
- entry = tp->cur_tx++ % TX_RING_SIZE;
-
- if (entry != 0) {
- /* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
- tp->tx_ring[entry].length =
- (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
- tp->tx_ring[entry].buffer1 = 0;
- /* race with chip, set DescOwned later */
- dummy = entry;
- entry = tp->cur_tx++ % TX_RING_SIZE;
- }
-
- tp->tx_skbuff[entry] = 0;
- /* Put the setup frame on the Tx list. */
- if (entry == TX_RING_SIZE-1)
- tx_flags |= DESC_RING_WRAP; /* Wrap ring. */
- tp->tx_ring[entry].length = tx_flags;
- if(tp->chip_id == X3201_3)
- tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- else
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[entry].status = DescOwned;
- if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
- tp->tx_full = 1;
- netif_stop_queue (dev);
- }
- if (dummy >= 0)
- tp->tx_ring[dummy].status = DescOwned;
- restore_flags(flags);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
- }
- }
- outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
-}
-
-static const struct pci_device_id tulip_pci_table[] __devinitdata = {
- { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 },
- { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 },
- { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
- { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 },
- { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
- { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
- { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
- { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
- { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
- { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
- { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
- {0},
-};
-
-MODULE_DEVICE_TABLE(pci, tulip_pci_table);
-
-static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net_device *dev;
- static int board_idx = 0;
-
- printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
-
- pci_enable_device (pdev);
- pci_set_master (pdev);
- dev = tulip_probe1(pdev, NULL,
- pci_resource_start (pdev, 0), pdev->irq,
- id->driver_data, board_idx++);
- if (dev) {
- pdev->driver_data = dev;
- return 0;
- }
- return -ENODEV;
-}
-
-static void tulip_suspend(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
- if (tp->open) tulip_down(dev);
-}
-
-static void tulip_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
- if (tp->open) tulip_up(dev);
-}
-
-static void __devexit tulip_remove(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
- unregister_netdev(dev);
- kfree(dev);
- kfree(tp);
-}
-
-static struct pci_driver tulip_ops = {
- name: "tulip_cb",
- id_table: tulip_pci_table,
- probe: tulip_pci_probe,
- remove: tulip_remove,
- suspend: tulip_suspend,
- resume: tulip_resume
-};
-
-static int __init tulip_init(void)
-{
- pci_register_driver(&tulip_ops);
- return 0;
-}
-
-static __exit void tulip_exit(void)
-{
- pci_unregister_driver(&tulip_ops);
-}
-
-module_init(tulip_init)
-module_exit(tulip_exit)
-
-
-/*
- * Local variables:
- * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 7374eac66..167d3cb12 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1470,7 +1470,7 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
lp->a.write_bcr (ioaddr, 34, data[2]);
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index d86ce1d85..64fafa17f 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -583,6 +583,61 @@ plip_receive(unsigned short nibble_timeout, struct net_device *dev,
return OK;
}
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ *
+ * PLIP is ethernet ish but the daddr might not be valid if unicast.
+ * PLIP fortunately has no bus architecture (its Point-to-point).
+ *
+ * We can't fix the daddr thing as that quirk (more bug) is embedded
+ * in far too many old systems not all even running Linux.
+ */
+
+static unsigned short plip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ */
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+
/* PLIP_RECEIVE_PACKET --- receive a packet */
static int
plip_receive_packet(struct net_device *dev, struct net_local *nl,
@@ -669,7 +724,7 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
case PLIP_PK_DONE:
/* Inform the upper layer for the arrival of a packet. */
- rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+ rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
netif_rx(rcv->skb);
nl->enet_stats.rx_bytes += rcv->length.h;
nl->enet_stats.rx_packets++;
@@ -1227,6 +1282,8 @@ plip_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
pc->nibble = nl->nibble;
break;
case PLIP_SET_TIMEOUT:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
nl->trigger = pc->trigger;
nl->nibble = pc->nibble;
break;
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 43b5508ae..3cbf0e5e4 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -17,11 +17,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990806==
+ * ==FILEVERSION 20000227==
*/
-/* $Id: ppp_async.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
@@ -31,9 +29,18 @@
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
-#define PPP_VERSION "2.4.0"
+#ifndef spin_trylock_bh
+#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \
+ __r = spin_trylock(lock); \
+ if (!__r) local_bh_enable(); \
+ __r; })
+#endif
+
+#define PPP_VERSION "2.4.1"
#define OBUFSIZE 256
@@ -44,7 +51,9 @@ struct asyncppp {
unsigned int state;
unsigned int rbits;
int mru;
- unsigned long busy;
+ spinlock_t xmit_lock;
+ spinlock_t recv_lock;
+ unsigned long xmit_flags;
u32 xaccm[8];
u32 raccm;
unsigned int bytes_sent;
@@ -55,24 +64,18 @@ struct asyncppp {
u16 tfcs;
unsigned char *optr;
unsigned char *olim;
- struct sk_buff_head xq;
unsigned long last_xmit;
struct sk_buff *rpkt;
- struct sk_buff_head rq;
- wait_queue_head_t rwait;
+ int lcp_fcs;
struct ppp_channel chan; /* interface to generic ppp layer */
- int connected;
- int index;
unsigned char obuf[OBUFSIZE];
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
-#define XMIT_FULL 3
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP 0
+#define XMIT_FULL 1
/* State bits */
#define SC_TOSS 0x20000000
@@ -81,8 +84,6 @@ struct asyncppp {
/* Bits in rbits */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-#define PPPASYNC_MAX_RQLEN 32 /* arbitrary */
-
static int flag_time = HZ;
MODULE_PARM(flag_time, "i");
@@ -95,57 +96,17 @@ static int ppp_async_push(struct asyncppp *ap);
static void ppp_async_flush_output(struct asyncppp *ap);
static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
char *flags, int count);
+static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound);
struct ppp_channel_ops async_ops = {
- ppp_async_send
+ ppp_async_send,
+ ppp_async_ioctl
};
/*
- * Routines for locking and unlocking the transmit and receive paths.
- */
-static inline void
-lock_path(struct asyncppp *ap, int bit)
-{
- do {
- while (test_bit(bit, &ap->busy))
- mb();
- } while (test_and_set_bit(bit, &ap->busy));
- mb();
-}
-
-static inline int
-trylock_path(struct asyncppp *ap, int bit)
-{
- if (test_and_set_bit(bit, &ap->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct asyncppp *ap, int bit)
-{
- mb();
- clear_bit(bit, &ap->busy);
-}
-
-#define lock_xmit_path(ap) lock_path(ap, XMIT_BUSY)
-#define trylock_xmit_path(ap) trylock_path(ap, XMIT_BUSY)
-#define unlock_xmit_path(ap) unlock_path(ap, XMIT_BUSY)
-#define lock_recv_path(ap) lock_path(ap, RECV_BUSY)
-#define trylock_recv_path(ap) trylock_path(ap, RECV_BUSY)
-#define unlock_recv_path(ap) unlock_path(ap, RECV_BUSY)
-
-static inline void
-flush_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(q)) != 0)
- kfree_skb(skb);
-}
-
-/*
* Routines implementing the PPP line discipline.
*/
@@ -153,256 +114,113 @@ flush_skb_queue(struct sk_buff_head *q)
* Called when a tty is put into PPP line discipline.
*/
static int
-ppp_async_open(struct tty_struct *tty)
+ppp_asynctty_open(struct tty_struct *tty)
{
struct asyncppp *ap;
+ int err;
ap = kmalloc(sizeof(*ap), GFP_KERNEL);
if (ap == 0)
return -ENOMEM;
- MOD_INC_USE_COUNT;
-
/* initialize the asyncppp structure */
memset(ap, 0, sizeof(*ap));
ap->tty = tty;
ap->mru = PPP_MRU;
+ spin_lock_init(&ap->xmit_lock);
+ spin_lock_init(&ap->recv_lock);
ap->xaccm[0] = ~0U;
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
ap->optr = ap->obuf;
ap->olim = ap->obuf;
- skb_queue_head_init(&ap->xq);
- skb_queue_head_init(&ap->rq);
- init_waitqueue_head(&ap->rwait);
+ ap->lcp_fcs = -1;
+
+ ap->chan.private = ap;
+ ap->chan.ops = &async_ops;
+ ap->chan.mtu = PPP_MRU;
+ err = ppp_register_channel(&ap->chan);
+ if (err) {
+ kfree(ap);
+ return err;
+ }
tty->disc_data = ap;
+ MOD_INC_USE_COUNT;
return 0;
}
/*
* Called when the tty is put into another line discipline
- * (or it hangs up).
+ * or it hangs up.
+ * We assume that while we are in this routine, the tty layer
+ * won't call any of the other line discipline entries for the
+ * same tty.
*/
static void
-ppp_async_close(struct tty_struct *tty)
+ppp_asynctty_close(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
tty->disc_data = 0;
- lock_xmit_path(ap);
- lock_recv_path(ap);
+ ppp_unregister_channel(&ap->chan);
if (ap->rpkt != 0)
kfree_skb(ap->rpkt);
- flush_skb_queue(&ap->rq);
if (ap->tpkt != 0)
kfree_skb(ap->tpkt);
- flush_skb_queue(&ap->xq);
- if (ap->connected)
- ppp_unregister_channel(&ap->chan);
kfree(ap);
MOD_DEC_USE_COUNT;
}
/*
- * Read a PPP frame. pppd can use this to negotiate over the
- * channel before it joins it to a bundle.
+ * Read does nothing.
*/
static ssize_t
-ppp_async_read(struct tty_struct *tty, struct file *file,
- unsigned char *buf, size_t count)
+ppp_asynctty_read(struct tty_struct *tty, struct file *file,
+ unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- struct sk_buff *skb = 0;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- add_wait_queue(&ap->rwait, &wait);
- current->state = TASK_INTERRUPTIBLE;
- for (;;) {
- ret = -EAGAIN;
- skb = skb_dequeue(&ap->rq);
- if (skb)
- break;
- if (file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&ap->rwait, &wait);
-
- if (skb == 0)
- goto out;
-
- ret = -EOVERFLOW;
- if (skb->len > count)
- goto outf;
- ret = -EFAULT;
- if (copy_to_user(buf, skb->data, skb->len))
- goto outf;
- ret = skb->len;
-
- outf:
- kfree_skb(skb);
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_read(&ap->chan, file, buf, count);
}
/*
- * Write a ppp frame. pppd can use this to send frames over
- * this particular channel.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
*/
static ssize_t
-ppp_async_write(struct tty_struct *tty, struct file *file,
- const unsigned char *buf, size_t count)
+ppp_asynctty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- struct sk_buff *skb;
- ssize_t ret;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- ret = -ENOMEM;
- skb = alloc_skb(count + 2, GFP_KERNEL);
- if (skb == 0)
- goto out;
- skb_reserve(skb, 2);
- ret = -EFAULT;
- if (copy_from_user(skb_put(skb, count), buf, count)) {
- kfree_skb(skb);
- goto out;
- }
-
- skb_queue_tail(&ap->xq, skb);
- ppp_async_push(ap);
-
- ret = count;
-
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_write(&ap->chan, buf, count);
}
static int
-ppp_async_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct asyncppp *ap = tty->disc_data;
int err, val;
- u32 accm[8];
- struct sk_buff *skb;
-
- err = -ENXIO;
- if (ap == 0)
- goto out; /* should never happen */
- err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- goto out;
err = -EFAULT;
switch (cmd) {
- case PPPIOCGFLAGS:
- val = ap->flags | ap->rbits;
- if (put_user(val, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSFLAGS:
- if (get_user(val, (int *) arg))
- break;
- ap->flags = val & ~SC_RCV_BITS;
- ap->rbits = val & SC_RCV_BITS;
- err = 0;
- break;
-
- case PPPIOCGASYNCMAP:
- if (put_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSASYNCMAP:
- if (get_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGRASYNCMAP:
- if (put_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSRASYNCMAP:
- if (get_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGXASYNCMAP:
- if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
- break;
- err = 0;
- break;
- case PPPIOCSXASYNCMAP:
- if (copy_from_user(accm, (void *) arg, sizeof(accm)))
- break;
- accm[2] &= ~0x40000000U; /* can't escape 0x5e */
- accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
- memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
- err = 0;
- break;
-
- case PPPIOCGMRU:
- if (put_user(ap->mru, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSMRU:
- if (get_user(val, (int *) arg))
- break;
- if (val < PPP_MRU)
- val = PPP_MRU;
- ap->mru = val;
- err = 0;
- break;
-
- case PPPIOCATTACH:
- if (get_user(val, (int *) arg))
- break;
- err = -EALREADY;
- if (ap->connected)
- break;
- ap->chan.private = ap;
- ap->chan.ops = &async_ops;
- err = ppp_register_channel(&ap->chan, val);
- if (err != 0)
- break;
- ap->connected = 1;
- ap->index = val;
- break;
- case PPPIOCDETACH:
- err = -ENXIO;
- if (!ap->connected)
- break;
- ppp_unregister_channel(&ap->chan);
- ap->connected = 0;
- err = 0;
- break;
case PPPIOCGUNIT:
err = -ENXIO;
- if (!ap->connected)
+ if (ap == 0)
break;
- if (put_user(ap->index, (int *) arg))
+ err = -EFAULT;
+ if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
break;
err = 0;
break;
@@ -414,8 +232,6 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case TCFLSH:
/* flush our buffers and the serial port's buffer */
- if (arg == TCIFLUSH || arg == TCIOFLUSH)
- flush_skb_queue(&ap->rq);
if (arg == TCIOFLUSH || arg == TCOFLUSH)
ppp_async_flush_output(ap);
err = n_tty_ioctl(tty, file, cmd, arg);
@@ -423,68 +239,86 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case FIONREAD:
val = 0;
- if ((skb = skb_peek(&ap->rq)) != 0)
- val = skb->len;
if (put_user(val, (int *) arg))
break;
err = 0;
break;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ case PPPIOCGFLAGS:
+ case PPPIOCSFLAGS:
+ case PPPIOCGASYNCMAP:
+ case PPPIOCSASYNCMAP:
+ case PPPIOCGRASYNCMAP:
+ case PPPIOCSRASYNCMAP:
+ case PPPIOCGXASYNCMAP:
+ case PPPIOCSXASYNCMAP:
+ case PPPIOCGMRU:
+ case PPPIOCSMRU:
+ err = ppp_async_ioctl(&ap->chan, cmd, arg);
+ break;
+
+ case PPPIOCATTACH:
+ err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+ break;
+
default:
err = -ENOIOCTLCMD;
}
- out:
+
return err;
}
static unsigned int
-ppp_async_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
{
- struct asyncppp *ap = tty->disc_data;
unsigned int mask;
+ struct asyncppp *ap = tty->disc_data;
- if (ap == 0)
- return 0; /* should never happen */
- poll_wait(file, &ap->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ap->rq))
- mask |= POLLIN | POLLRDNORM;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ if (ap != 0)
+ mask |= ppp_channel_poll(&ap->chan, file, wait);
if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
mask |= POLLHUP;
return mask;
}
static int
-ppp_async_room(struct tty_struct *tty)
+ppp_asynctty_room(struct tty_struct *tty)
{
return 65535;
}
static void
-ppp_async_receive(struct tty_struct *tty, const unsigned char *buf,
+ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
- trylock_recv_path(ap);
+ spin_lock_bh(&ap->recv_lock);
ppp_async_input(ap, buf, flags, count);
- unlock_recv_path(ap);
+ spin_unlock_bh(&ap->recv_lock);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver.unthrottle)
tty->driver.unthrottle(tty);
}
static void
-ppp_async_wakeup(struct tty_struct *tty)
+ppp_asynctty_wakeup(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0)
return;
- if (ppp_async_push(ap) && ap->connected)
+ if (ppp_async_push(ap))
ppp_output_wakeup(&ap->chan);
}
@@ -492,15 +326,15 @@ ppp_async_wakeup(struct tty_struct *tty)
static struct tty_ldisc ppp_ldisc = {
magic: TTY_LDISC_MAGIC,
name: "ppp",
- open: ppp_async_open,
- close: ppp_async_close,
- read: ppp_async_read,
- write: ppp_async_write,
- ioctl: ppp_async_ioctl,
- poll: ppp_async_poll,
- receive_room: ppp_async_room,
- receive_buf: ppp_async_receive,
- write_wakeup: ppp_async_wakeup,
+ open: ppp_asynctty_open,
+ close: ppp_asynctty_close,
+ read: ppp_asynctty_read,
+ write: ppp_asynctty_write,
+ ioctl: ppp_asynctty_ioctl,
+ poll: ppp_asynctty_poll,
+ receive_room: ppp_asynctty_room,
+ receive_buf: ppp_asynctty_receive,
+ write_wakeup: ppp_asynctty_wakeup,
};
int
@@ -516,6 +350,91 @@ ppp_async_init(void)
}
/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+ struct asyncppp *ap = chan->private;
+ int err, val;
+ u32 accm[8];
+
+ err = -EFAULT;
+ switch (cmd) {
+ case PPPIOCGFLAGS:
+ val = ap->flags | ap->rbits;
+ if (put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if (get_user(val, (int *) arg))
+ break;
+ ap->flags = val & ~SC_RCV_BITS;
+ spin_lock_bh(&ap->recv_lock);
+ ap->rbits = val & SC_RCV_BITS;
+ spin_unlock_bh(&ap->recv_lock);
+ err = 0;
+ break;
+
+ case PPPIOCGASYNCMAP:
+ if (put_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSASYNCMAP:
+ if (get_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGRASYNCMAP:
+ if (put_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSRASYNCMAP:
+ if (get_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGXASYNCMAP:
+ if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSXASYNCMAP:
+ if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+ break;
+ accm[2] &= ~0x40000000U; /* can't escape 0x5e */
+ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
+ memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+ err = 0;
+ break;
+
+ case PPPIOCGMRU:
+ if (put_user(ap->mru, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSMRU:
+ if (get_user(val, (int *) arg))
+ break;
+ if (val < PPP_MRU)
+ val = PPP_MRU;
+ ap->mru = val;
+ err = 0;
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+
+ return err;
+}
+
+/*
* Procedures for encapsulation and framing.
*/
@@ -597,6 +516,9 @@ ppp_async_encode(struct asyncppp *ap)
islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
if (i == 0) {
+ if (islcp)
+ async_lcp_peek(ap, data, count, 0);
+
/*
* Start of a new packet - insert the leading FLAG
* character if necessary.
@@ -675,7 +597,7 @@ ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
ppp_async_push(ap);
- if (test_and_set_bit(XMIT_FULL, &ap->busy))
+ if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
return 0; /* already full */
ap->tpkt = skb;
ap->tpkt_pos = 0;
@@ -694,12 +616,11 @@ ppp_async_push(struct asyncppp *ap)
struct tty_struct *tty = ap->tty;
int tty_stuffed = 0;
- if (!trylock_xmit_path(ap)) {
- set_bit(XMIT_WAKEUP, &ap->busy);
+ set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+ if (!spin_trylock_bh(&ap->xmit_lock))
return 0;
- }
for (;;) {
- if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy))
+ if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
tty_stuffed = 0;
if (!tty_stuffed && ap->optr < ap->olim) {
avail = ap->olim - ap->optr;
@@ -715,22 +636,17 @@ ppp_async_push(struct asyncppp *ap)
if (ap->optr == ap->olim && ap->tpkt != 0) {
if (ppp_async_encode(ap)) {
/* finished processing ap->tpkt */
- struct sk_buff *skb = skb_dequeue(&ap->xq);
- if (skb != 0) {
- ap->tpkt = skb;
- } else {
- clear_bit(XMIT_FULL, &ap->busy);
- done = 1;
- }
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
+ done = 1;
}
continue;
}
/* haven't made any progress */
- unlock_xmit_path(ap);
- if (!(test_bit(XMIT_WAKEUP, &ap->busy)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
|| (!tty_stuffed && ap->tpkt != 0)))
break;
- if (!trylock_xmit_path(ap))
+ if (!spin_trylock_bh(&ap->xmit_lock))
break;
}
return done;
@@ -739,11 +655,11 @@ flush:
if (ap->tpkt != 0) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
ap->optr = ap->olim;
- unlock_xmit_path(ap);
+ spin_unlock_bh(&ap->xmit_lock);
return done;
}
@@ -756,17 +672,16 @@ ppp_async_flush_output(struct asyncppp *ap)
{
int done = 0;
- flush_skb_queue(&ap->xq);
- lock_xmit_path(ap);
+ spin_lock_bh(&ap->xmit_lock);
ap->optr = ap->olim;
if (ap->tpkt != NULL) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
- unlock_xmit_path(ap);
- if (done && ap->connected)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (done)
ppp_output_wakeup(&ap->chan);
}
@@ -795,7 +710,7 @@ process_input_packet(struct asyncppp *ap)
{
struct sk_buff *skb;
unsigned char *p;
- unsigned int len, fcs;
+ unsigned int len, fcs, proto;
int code = 0;
skb = ap->rpkt;
@@ -827,37 +742,32 @@ process_input_packet(struct asyncppp *ap)
goto err;
p = skb_pull(skb, 2);
}
- if (p[0] & 1) {
+ proto = p[0];
+ if (proto & 1) {
/* protocol is compressed */
skb_push(skb, 1)[0] = 0;
- } else if (skb->len < 2)
- goto err;
-
- /* all OK, give it to the generic layer or queue it */
- if (ap->connected) {
- ppp_input(&ap->chan, skb);
} else {
- skb_queue_tail(&ap->rq, skb);
- /* drop old frames if queue too long */
- while (ap->rq.qlen > PPPASYNC_MAX_RQLEN
- && (skb = skb_dequeue(&ap->rq)) != 0)
- kfree(skb);
- wake_up_interruptible(&ap->rwait);
+ if (skb->len < 2)
+ goto err;
+ proto = (proto << 8) + p[1];
+ if (proto == PPP_LCP)
+ async_lcp_peek(ap, p, skb->len, 1);
}
+
+ /* all OK, give it to the generic layer */
+ ppp_input(&ap->chan, skb);
return;
err:
kfree_skb(skb);
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
static inline void
input_error(struct asyncppp *ap, int code)
{
ap->state |= SC_TOSS;
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
/* called when the tty driver has data for us. */
@@ -950,17 +860,96 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
input_error(ap, 0);
}
-#ifdef MODULE
-int
-init_module(void)
+/*
+ * We look at LCP frames going past so that we can notice
+ * and react to the LCP configure-ack from the peer.
+ * In the situation where the peer has been sent a configure-ack
+ * already, LCP is up once it has sent its configure-ack
+ * so the immediately following packet can be sent with the
+ * configured LCP options. This allows us to process the following
+ * packet correctly without pppd needing to respond quickly.
+ *
+ * We only respond to the received configure-ack if we have just
+ * sent a configure-request, and the configure-ack contains the
+ * same data (this is checked using a 16-bit crc of the data).
+ */
+#define CONFREQ 1 /* LCP code field values */
+#define CONFACK 2
+#define LCP_MRU 1 /* LCP option numbers */
+#define LCP_ASYNCMAP 2
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound)
{
- return ppp_async_init();
+ int dlen, fcs, i, code;
+ u32 val;
+
+ data += 2; /* skip protocol bytes */
+ len -= 2;
+ if (len < 4) /* 4 = code, ID, length */
+ return;
+ code = data[0];
+ if (code != CONFACK && code != CONFREQ)
+ return;
+ dlen = (data[2] << 8) + data[3];
+ if (len < dlen)
+ return; /* packet got truncated or length is bogus */
+
+ if (code == (inbound? CONFACK: CONFREQ)) {
+ /*
+ * sent confreq or received confack:
+ * calculate the crc of the data from the ID field on.
+ */
+ fcs = PPP_INITFCS;
+ for (i = 1; i < dlen; ++i)
+ fcs = PPP_FCS(fcs, data[i]);
+
+ if (!inbound) {
+ /* outbound confreq - remember the crc for later */
+ ap->lcp_fcs = fcs;
+ return;
+ }
+
+ /* received confack, check the crc */
+ fcs ^= ap->lcp_fcs;
+ ap->lcp_fcs = -1;
+ if (fcs != 0)
+ return;
+ } else if (inbound)
+ return; /* not interested in received confreq */
+
+ /* process the options in the confack */
+ data += 4;
+ dlen -= 4;
+ /* data[0] is code, data[1] is length */
+ while (dlen >= 2 && dlen >= data[1]) {
+ switch (data[0]) {
+ case LCP_MRU:
+ val = (data[2] << 8) + data[3];
+ if (inbound)
+ ap->mru = val;
+ else
+ ap->chan.mtu = val;
+ break;
+ case LCP_ASYNCMAP:
+ val = (data[2] << 24) + (data[3] << 16)
+ + (data[4] << 8) + data[5];
+ if (inbound)
+ ap->raccm = val;
+ else
+ ap->xaccm[0] = val;
+ break;
+ }
+ dlen -= data[1];
+ data += data[1];
+ }
}
-void
-cleanup_module(void)
+void __exit ppp_async_cleanup(void)
{
if (tty_register_ldisc(N_PPP, NULL) != 0)
printk(KERN_ERR "failed to unregister PPP line discipline\n");
}
-#endif /* MODULE */
+
+module_init(ppp_async_init);
+module_exit(ppp_async_cleanup);
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 3ef379ab2..761d8705c 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -32,26 +32,9 @@
*/
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-
-#include <asm/system.h>
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
+#include <linux/init.h>
#include <linux/ppp_defs.h>
#include <linux/ppp-comp.h>
@@ -656,31 +639,21 @@ struct compressor ppp_deflate_draft = {
z_comp_stats, /* decomp_stat */
};
-#ifdef MODULE
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-int
-init_module(void)
+int deflate_init(void)
{
- int answer = ppp_register_compressor (&ppp_deflate);
+ int answer = ppp_register_compressor(&ppp_deflate);
if (answer == 0)
- printk (KERN_INFO
- "PPP Deflate Compression module registered\n");
+ printk(KERN_INFO
+ "PPP Deflate Compression module registered\n");
ppp_register_compressor(&ppp_deflate_draft);
return answer;
}
-void
-cleanup_module(void)
+void deflate_cleanup(void)
{
- if (MOD_IN_USE)
- printk (KERN_INFO
- "Deflate Compression module busy, remove delayed\n");
- else {
- ppp_unregister_compressor (&ppp_deflate);
- ppp_unregister_compressor (&ppp_deflate_draft);
- }
+ ppp_unregister_compressor(&ppp_deflate);
+ ppp_unregister_compressor(&ppp_deflate_draft);
}
-#endif
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 0b3aeff4b..01a4e2f81 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1,7 +1,7 @@
/*
* Generic PPP layer for Linux.
*
- * Copyright 1999 Paul Mackerras.
+ * Copyright 1999-2000 Paul Mackerras.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,11 +19,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990915==
+ * ==FILEVERSION 20000313==
*/
-/* $Id: ppp_generic.c,v 1.5 1999/09/15 11:21:48 paulus Exp $ */
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -44,16 +42,9 @@
#include <linux/tcp.h>
#include <linux/spinlock.h>
#include <net/slhc_vj.h>
+#include <asm/atomic.h>
-#define PPP_VERSION "2.4.0"
-
-EXPORT_SYMBOL(ppp_register_channel);
-EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_input);
-EXPORT_SYMBOL(ppp_input_error);
-EXPORT_SYMBOL(ppp_output_wakeup);
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
+#define PPP_VERSION "2.4.1"
/*
* Network protocols we support.
@@ -64,32 +55,56 @@ EXPORT_SYMBOL(ppp_unregister_compressor);
#define NP_AT 3 /* Appletalk protocol */
#define NUM_NP 4 /* Number of NPs. */
+#define MPHDRLEN 4 /* multilink protocol header length */
+#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */
+#define MIN_FRAG_SIZE 64
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel. In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+ enum {
+ INTERFACE=1, CHANNEL
+ } kind;
+ struct sk_buff_head xq; /* pppd transmit queue */
+ struct sk_buff_head rq; /* receive queue for pppd */
+ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
+ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */
+ int hdrlen; /* space to leave for headers */
+ struct list_head list; /* link in all_* list */
+ int index; /* interface unit / channel number */
+};
+
+#define PF_TO_X(pf, X) ((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file)))
+
+#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
+
+#define ROUNDUP(n, x) (((n) + (x) - 1) / (x))
+
/*
* Data structure describing one ppp unit.
* A ppp unit corresponds to a ppp network interface device
* and represents a multilink bundle.
- * It may have 0 or more ppp channels connected to it.
+ * It can have 0 or more ppp channels connected to it.
*/
struct ppp {
- struct list_head list; /* link in list of ppp units */
- int index; /* interface unit number */
+ struct ppp_file file; /* stuff for read/write/poll */
char name[16]; /* unit name */
- int refcnt; /* # open /dev/ppp attached */
- unsigned long busy; /* lock and other bits */
struct list_head channels; /* list of attached channels */
int n_channels; /* how many channels are attached */
+ spinlock_t rlock; /* lock for receive side */
+ spinlock_t wlock; /* lock for transmit side */
int mru; /* max receive unit */
unsigned int flags; /* control bits */
unsigned int xstate; /* transmit state bits */
unsigned int rstate; /* receive state bits */
int debug; /* debug flags */
struct slcompress *vj; /* state for VJ header compression */
- struct sk_buff_head xq; /* pppd transmit queue */
- struct sk_buff_head rq; /* receive queue for pppd */
- wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
enum NPmode npmode[NUM_NP]; /* what to do with each net proto */
struct sk_buff *xmit_pending; /* a packet ready to go out */
- struct sk_buff_head recv_pending;/* pending input packets */
struct compressor *xcomp; /* transmit packet compressor */
void *xc_state; /* its internal state */
struct compressor *rcomp; /* receive decompressor */
@@ -97,57 +112,121 @@ struct ppp {
unsigned long last_xmit; /* jiffies when last pkt sent */
unsigned long last_recv; /* jiffies when last pkt rcvd */
struct net_device *dev; /* network interface device */
+#ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+ int mrru; /* MP: max reconst. receive unit */
+ u32 nextseq; /* MP: seq no of next packet */
+ u32 minseq; /* MP: min of most recent seqnos */
+ struct sk_buff_head mrq; /* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
struct net_device_stats stats; /* statistics */
};
-static LIST_HEAD(all_ppp_units);
-static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ.
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ)
/*
* Private data structure for each channel.
- * Ultimately this will have multilink stuff etc. in it.
+ * This includes the data structure used for multilink.
*/
struct channel {
- struct list_head list; /* link in list of channels per unit */
+ struct ppp_file file; /* stuff for read/write/poll */
struct ppp_channel *chan; /* public channel data structure */
- int blocked; /* if channel refused last packet */
+ spinlock_t downl; /* protects `chan', file.xq dequeue */
struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' and `ulist' */
+#ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+ u32 lastseq; /* MP: last sequence # received */
+#endif /* CONFIG_PPP_MULTILINK */
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
/*
- * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC.
- * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
- * Bits in xstate: SC_COMP_RUN
+ * all_ppp_lock protects the all_ppp_units.
+ * It also ensures that finding a ppp unit in the all_ppp_units list
+ * and updating its file.refcnt field is atomic.
+ */
+static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_ppp_units);
+
+/*
+ * all_channels_lock protects all_channels and last_channel_index,
+ * and the atomicity of find a channel and updating its file.refcnt
+ * field.
*/
-#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC)
+static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_channels);
+static int last_channel_index;
/* Get the PPP protocol number from a skb */
#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
-/* We limit the length of ppp->rq to this (arbitrary) value */
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
#define PPP_MAX_RQLEN 32
+/* Multilink header bits. */
+#define B 0x80 /* this fragment begins a packet */
+#define E 0x40 /* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b) ((s32)((a) - (b)) < 0)
+#define seq_after(a, b) ((s32)((a) - (b)) > 0)
+
/* Prototypes. */
-static void ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh);
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count);
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count);
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static void ppp_xmit_process(struct ppp *ppp, int wakeup);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
-static void ppp_recv_unlock(struct ppp *ppp);
-static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
static void ppp_ccp_closed(struct ppp *ppp);
static struct compressor *find_compressor(int type);
static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_unit(int unit, int *retp);
-static void ppp_release_unit(struct ppp *ppp);
+static struct ppp *ppp_create_interface(int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_destroy_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(int unit);
+static struct channel *ppp_find_channel(int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
/* Translates a PPP protocol number to a NP index (NP == network protocol) */
static inline int proto_to_npindex(int proto)
@@ -199,69 +278,24 @@ static const int npindex_to_ethertype[NUM_NP] = {
};
/*
- * Routines for locking and unlocking the transmit and receive paths
- * of each unit.
- *
- * On the transmit side, we have threads of control coming into the
- * driver from (at least) three places: the core net code, write calls
- * on /dev/ppp from pppd, and wakeup calls from channels. There is
- * possible concurrency even on UP systems (between mainline and
- * BH processing). The XMIT_BUSY bit in ppp->busy serializes the
- * transmit-side processing for each ppp unit.
+ * Locking shorthand.
*/
-static inline void
-lock_path(struct ppp *ppp, int bit)
-{
- int timeout = 1000000;
-
- do {
- while (test_bit(bit, &ppp->busy)) {
- mb();
- if (--timeout == 0) {
- printk(KERN_ERR "lock_path timeout ppp=%p bit=%x\n", ppp, bit);
- return;
- }
- }
- } while (test_and_set_bit(bit, &ppp->busy));
- mb();
-}
+#define ppp_xmit_lock(ppp) spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp) spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp) spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp) spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp) do { ppp_xmit_lock(ppp); \
+ ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \
+ ppp_xmit_unlock(ppp); } while (0)
-static inline int
-trylock_path(struct ppp *ppp, int bit)
-{
- if (test_and_set_bit(bit, &ppp->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct ppp *ppp, int bit)
-{
- mb();
- clear_bit(bit, &ppp->busy);
-}
-
-#define lock_xmit_path(ppp) lock_path(ppp, XMIT_BUSY)
-#define trylock_xmit_path(ppp) trylock_path(ppp, XMIT_BUSY)
-#define unlock_xmit_path(ppp) unlock_path(ppp, XMIT_BUSY)
-#define lock_recv_path(ppp) lock_path(ppp, RECV_BUSY)
-#define trylock_recv_path(ppp) trylock_path(ppp, RECV_BUSY)
-#define unlock_recv_path(ppp) unlock_path(ppp, RECV_BUSY)
-
-static inline void
-free_skbs(struct sk_buff_head *head)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(head)) != 0)
- kfree_skb(skb);
-}
/*
* /dev/ppp device routines.
* The /dev/ppp device is used by pppd to control the ppp unit.
* It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
*/
static int ppp_open(struct inode *inode, struct file *file)
{
@@ -276,11 +310,20 @@ static int ppp_open(struct inode *inode, struct file *file)
static int ppp_release(struct inode *inode, struct file *file)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
- if (ppp != 0) {
+ if (pf != 0) {
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt)) {
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_destroy_interface(PF_TO_PPP(pf));
+ break;
+ case CHANNEL:
+ ppp_destroy_channel(PF_TO_CHANNEL(pf));
+ break;
+ }
+ }
}
MOD_DEC_USE_COUNT;
return 0;
@@ -289,20 +332,27 @@ static int ppp_release(struct inode *inode, struct file *file)
static ssize_t ppp_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_read(pf, file, buf, count);
+}
+
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count)
+{
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
struct sk_buff *skb = 0;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out; /* not currently attached */
- add_wait_queue(&ppp->rwait, &wait);
+ add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
ret = -EAGAIN;
- skb = skb_dequeue(&ppp->rq);
+ skb = skb_dequeue(&pf->rq);
if (skb)
break;
if (file->f_flags & O_NONBLOCK)
@@ -313,7 +363,7 @@ static ssize_t ppp_read(struct file *file, char *buf,
schedule();
}
current->state = TASK_RUNNING;
- remove_wait_queue(&ppp->rwait, &wait);
+ remove_wait_queue(&pf->rwait, &wait);
if (skb == 0)
goto out;
@@ -335,32 +385,42 @@ static ssize_t ppp_read(struct file *file, char *buf,
static ssize_t ppp_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_write(pf, buf, count);
+}
+
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count)
+{
struct sk_buff *skb;
ssize_t ret;
- int extra;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out;
ret = -ENOMEM;
- extra = PPP_HDRLEN - 2;
- if (ppp->dev && ppp->dev->hard_header_len > PPP_HDRLEN)
- extra = ppp->dev->hard_header_len - 2;
- skb = alloc_skb(count + extra, GFP_KERNEL);
+ skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
if (skb == 0)
goto out;
- skb_reserve(skb, extra);
+ skb_reserve(skb, pf->hdrlen);
ret = -EFAULT;
if (copy_from_user(skb_put(skb, count), buf, count)) {
kfree_skb(skb);
goto out;
}
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ skb_queue_tail(&pf->xq, skb);
+
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_xmit_process(PF_TO_PPP(pf), 0);
+ break;
+ case CHANNEL:
+ ppp_channel_push(PF_TO_CHANNEL(pf));
+ break;
+ }
ret = count;
@@ -370,68 +430,82 @@ static ssize_t ppp_write(struct file *file, const char *buf,
static unsigned int ppp_poll(struct file *file, poll_table *wait)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
unsigned int mask;
- if (ppp == 0)
+ if (pf == 0)
return 0;
- poll_wait(file, &ppp->rwait, wait);
+ poll_wait(file, &pf->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ppp->rq) != 0)
+ if (skb_peek(&pf->rq) != 0)
mask |= POLLIN | POLLRDNORM;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ if (pch->chan == 0)
+ mask |= POLLHUP;
+ }
return mask;
}
static int ppp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
- int err, val, val2, i;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+ struct ppp *ppp;
+ int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
struct npioctl npi;
+ int unit;
+ struct slcompress *vj;
- if (cmd == PPPIOCNEWUNIT) {
- /* Create a new ppp unit */
- int unit, ret;
+ if (pf == 0)
+ return ppp_unattached_ioctl(pf, file, cmd, arg);
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- ppp = ppp_create_unit(unit, &ret);
- if (ppp == 0)
- return ret;
- file->private_data = ppp;
- if (put_user(ppp->index, (int *) arg))
- return -EFAULT;
- return 0;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ struct ppp_channel *chan;
+
+ switch (cmd) {
+ case PPPIOCCONNECT:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+
+ case PPPIOCDISCONN:
+ err = ppp_disconnect_channel(pch);
+ break;
+
+ case PPPIOCDETACH:
+ file->private_data = 0;
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_channel(pch);
+ err = 0;
+ break;
+
+ default:
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ err = -ENOTTY;
+ if (chan->ops->ioctl)
+ err = chan->ops->ioctl(chan, cmd, arg);
+ spin_unlock_bh(&pch->downl);
+ }
+ return err;
}
- if (cmd == PPPIOCATTACH) {
- /* Attach to an existing ppp unit */
- int unit;
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp != 0)
- ++ppp->refcnt;
- spin_unlock(&all_ppp_lock);
- if (ppp == 0)
- return -ENXIO;
- file->private_data = ppp;
- return 0;
+ if (pf->kind != INTERFACE) {
+ /* can't happen */
+ printk(KERN_ERR "PPP: not interface or channel??\n");
+ return -EINVAL;
}
- if (ppp == 0)
- return -ENXIO;
- err = -EFAULT;
+ ppp = PF_TO_PPP(pf);
switch (cmd) {
case PPPIOCDETACH:
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_interface(ppp);
err = 0;
break;
@@ -445,9 +519,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
case PPPIOCSFLAGS:
if (get_user(val, (int *) arg))
break;
+ ppp_lock(ppp);
if (ppp->flags & ~val & SC_CCP_OPEN)
ppp_ccp_closed(ppp);
ppp->flags = val & SC_FLAG_BITS;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -463,7 +539,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
break;
case PPPIOCGUNIT:
- if (put_user(ppp->index, (int *) arg))
+ if (put_user(ppp->file.index, (int *) arg))
break;
err = 0;
break;
@@ -497,18 +573,17 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
val2 = val >> 16;
val &= 0xffff;
}
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
- if (ppp->vj != 0)
- slhc_free(ppp->vj);
- ppp->vj = slhc_init(val2+1, val+1);
- ppp_recv_unlock(ppp);
- ppp_xmit_unlock(ppp, 1);
- err = -ENOMEM;
- if (ppp->vj == 0) {
+ vj = slhc_init(val2+1, val+1);
+ if (vj == 0) {
printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+ err = -ENOMEM;
break;
}
+ ppp_lock(ppp);
+ if (ppp->vj != 0)
+ slhc_free(ppp->vj);
+ ppp->vj = vj;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -533,6 +608,76 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
err = 0;
break;
+#ifdef CONFIG_PPP_MULTILINK
+ case PPPIOCSMRRU:
+ if (get_user(val, (int *) arg))
+ break;
+ ppp_recv_lock(ppp);
+ ppp->mrru = val;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ default:
+ err = -ENOTTY;
+ }
+ return err;
+}
+
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit, err = -EFAULT;
+ struct ppp *ppp;
+ struct channel *chan;
+
+ switch (cmd) {
+ case PPPIOCNEWUNIT:
+ /* Create a new ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ ppp = ppp_create_interface(unit, &err);
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = -EFAULT;
+ if (put_user(ppp->file.index, (int *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCATTACH:
+ /* Attach to an existing ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp != 0)
+ atomic_inc(&ppp->file.refcnt);
+ spin_unlock(&all_ppp_lock);
+ err = -ENXIO;
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = 0;
+ break;
+
+ case PPPIOCATTCHAN:
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock_bh(&all_channels_lock);
+ chan = ppp_find_channel(unit);
+ if (chan != 0)
+ atomic_inc(&chan->file.refcnt);
+ spin_unlock_bh(&all_channels_lock);
+ err = -ENXIO;
+ if (chan == 0)
+ break;
+ file->private_data = &chan->file;
+ err = 0;
+ break;
+
default:
err = -ENOTTY;
}
@@ -557,37 +702,17 @@ static devfs_handle_t devfs_handle = NULL;
int __init ppp_init(void)
{
int err;
-#ifndef MODULE
-#ifdef CONFIG_PPP_DEFLATE
- extern struct compressor ppp_deflate, ppp_deflate_draft;
-#endif
- extern int ppp_async_init(void);
- extern int ppp_sync_init(void);
-#endif
printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
if (err)
printk(KERN_ERR "failed to register PPP device (%d)\n", err);
- devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE,
- PPP_MAJOR, 0,
- S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
- &ppp_device_fops, NULL);
-#ifndef MODULE
-#ifdef CONFIG_PPP_ASYNC
- ppp_async_init();
-#endif
-#ifdef CONFIG_PPP_SYNC_TTY
- ppp_sync_init();
-#endif
-#ifdef CONFIG_PPP_DEFLATE
- if (ppp_register_compressor(&ppp_deflate) == 0)
- printk(KERN_INFO "PPP Deflate compression module registered\n");
- ppp_register_compressor(&ppp_deflate_draft);
-#endif
-#endif /* MODULE */
+ devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE,
+ PPP_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &ppp_device_fops, NULL);
- return -ENODEV;
+ return 0;
}
/*
@@ -636,9 +761,8 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
pp[1] = proto;
netif_stop_queue(dev);
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 0);
+ skb_queue_tail(&ppp->file.xq, skb);
+ ppp_xmit_process(ppp, 0);
return 0;
outf:
@@ -719,38 +843,26 @@ ppp_net_init(struct net_device *dev)
*/
/*
- * Called to unlock the transmit side of the ppp unit,
- * making sure that any work queued up gets done.
+ * Called to do any work queued up on the transmit side
+ * that can now be done.
*/
static void
-ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh)
+ppp_xmit_process(struct ppp *ppp, int wakeup)
{
struct sk_buff *skb;
- for (;;) {
- /* Do whatever work is waiting to be done. */
- if (test_and_clear_bit(XMIT_WAKEUP, &ppp->busy))
- ppp_push(ppp);
- /* If there's no work left to do, tell the core net
- code that we can accept some more. */
- while (ppp->xmit_pending == 0
- && (skb = skb_dequeue(&ppp->xq)) != 0)
- ppp_send_frame(ppp, skb);
- if (ppp->xmit_pending == 0 && skb_peek(&ppp->xq) == 0)
- netif_wake_queue(ppp->dev);
-
- /* Now unlock the transmit path, let others in. */
- unlock_xmit_path(ppp);
- /* Check whether any work was queued up
- between our last check and the unlock. */
- if (!(test_bit(XMIT_WAKEUP, &ppp->busy)
- || (ppp->xmit_pending == 0 && skb_peek(&ppp->xq))))
- break;
- /* If so, lock again and do the work. If we can't get
- the lock, someone else has it and they'll do the work. */
- if (!trylock_xmit_path(ppp))
- break;
- }
+ ppp_xmit_lock(ppp);
+ if (wakeup)
+ ppp_push(ppp);
+ while (ppp->xmit_pending == 0
+ && (skb = skb_dequeue(&ppp->file.xq)) != 0)
+ ppp_send_frame(ppp, skb);
+ /* If there's no work left to do, tell the core net
+ code that we can accept some more. */
+ if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0
+ && ppp->dev != 0)
+ netif_wake_queue(ppp->dev);
+ ppp_xmit_unlock(ppp);
}
/*
@@ -847,10 +959,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
* queue it up for pppd to receive.
*/
if (ppp->flags & SC_LOOP_TRAFFIC) {
- if (ppp->rq.qlen > PPP_MAX_RQLEN)
+ if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
goto drop;
- skb_queue_tail(&ppp->rq, skb);
- wake_up_interruptible(&ppp->rwait);
+ skb_queue_tail(&ppp->file.rq, skb);
+ wake_up_interruptible(&ppp->file.rwait);
return;
}
@@ -871,7 +983,7 @@ static void
ppp_push(struct ppp *ppp)
{
struct list_head *list;
- struct channel *chan;
+ struct channel *pch;
struct sk_buff *skb = ppp->xmit_pending;
if (skb == 0)
@@ -885,40 +997,222 @@ ppp_push(struct ppp *ppp)
return;
}
- /* If we are doing multilink, decide which channel gets the
- packet, and/or fragment the packet over several links. */
- /* XXX for now, just take the first channel */
- list = list->next;
- chan = list_entry(list, struct channel, list);
+ if ((ppp->flags & SC_MULTILINK) == 0) {
+ /* not doing multilink: send it down the first channel */
+ list = list->next;
+ pch = list_entry(list, struct channel, clist);
- if (chan->chan->ops->start_xmit(chan->chan, skb)) {
- ppp->xmit_pending = 0;
- chan->blocked = 0;
- } else
- chan->blocked = 1;
+ spin_lock_bh(&pch->downl);
+ if (skb_queue_len(&pch->file.xq) == 0
+ && pch->chan->ops->start_xmit(pch->chan, skb))
+ ppp->xmit_pending = 0;
+ spin_unlock_bh(&pch->downl);
+ return;
+ }
+
+#ifdef CONFIG_PPP_MULTILINK
+ /* Multilink: fragment the packet over as many links
+ as can take the packet at the moment. */
+ if (!ppp_mp_explode(ppp, skb))
+ return;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ ppp->xmit_pending = 0;
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+ int nch, len, fragsize;
+ int i, bits, hdrlen;
+ unsigned char *p, *q;
+ struct list_head *list;
+ struct channel *pch;
+ struct sk_buff *frag;
+ struct ppp_channel *chan;
+
+ nch = 0;
+ list = &ppp->channels;
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0);
+ /*
+ * If a channel hasn't had a fragment yet, it has to get
+ * one before we send any fragments on later channels.
+ * If it can't take a fragment now, don't give any
+ * to subsequent channels.
+ */
+ if (!pch->had_frag && !pch->avail) {
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ pch->avail = 0;
+ }
+ break;
+ }
+ }
+ if (nch == 0)
+ return 0; /* can't take now, leave it in xmit_pending */
+
+ /* Do protocol field compression (XXX this should be optional) */
+ p = skb->data;
+ len = skb->len;
+ if (*p == 0) {
+ ++p;
+ --len;
+ }
+
+ /* decide on fragment size */
+ fragsize = len;
+ if (nch > 1) {
+ int maxch = ROUNDUP(len, MIN_FRAG_SIZE);
+ if (nch > maxch)
+ nch = maxch;
+ fragsize = ROUNDUP(fragsize, nch);
+ }
+
+ /* skip to the channel after the one we last used
+ and start at that one */
+ for (i = 0; i < ppp->nxchan; ++i) {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ break;
+ }
+ }
+
+ /* create a fragment for each channel */
+ bits = B;
+ hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+ /* XXX gotta do A/C and prot compression here */
+ do {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ continue;
+ }
+ pch = list_entry(list, struct channel, clist);
+ ++i;
+ if (!pch->avail)
+ continue;
+ if (fragsize >= len) {
+ fragsize = len;
+ bits |= E;
+ }
+ frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC);
+ if (frag != 0) {
+ q = skb_put(frag, fragsize + hdrlen);
+ /* make the MP header */
+ if (ppp->flags & SC_MP_XSHORTSEQ) {
+ q[0] = bits + ((ppp->nxseq >> 8) & 0xf);
+ q[1] = ppp->nxseq;
+ } else {
+ q[0] = bits;
+ q[1] = ppp->nxseq >> 16;
+ q[2] = ppp->nxseq >> 8;
+ q[3] = ppp->nxseq;
+ }
+
+ /* copy the data in */
+ memcpy(q + hdrlen, p, fragsize);
+
+ /* try to send it down the channel */
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ if (chan != 0) {
+ if (!chan->ops->start_xmit(chan, frag))
+ skb_queue_tail(&pch->file.xq, frag);
+ } else {
+ /* channel got unregistered, too bad */
+ kfree_skb(skb);
+ }
+ spin_unlock_bh(&pch->downl);
+ }
+ p += fragsize;
+ len -= fragsize;
+ ++ppp->nxseq;
+ bits = 0;
+ } while (len > 0);
+ ppp->nxchan = i;
+
+ return 1;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+ struct sk_buff *skb;
+
+ spin_lock_bh(&pch->downl);
+ if (pch->chan != 0) {
+ while (skb_queue_len(&pch->file.xq) > 0) {
+ skb = skb_dequeue(&pch->file.xq);
+ if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+ /* put the packet back and try again later */
+ skb_queue_head(&pch->file.xq, skb);
+ break;
+ }
+ }
+ } else {
+ /* channel got deregistered */
+ skb_queue_purge(&pch->file.xq);
+ }
+ spin_unlock_bh(&pch->downl);
}
/*
* Receive-side routines.
*/
+
+/* misuse a few fields of the skb for MP reconstruction */
+#define sequence priority
+#define BEbits cb[0]
+
static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb)
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- skb_queue_tail(&ppp->recv_pending, skb);
- if (trylock_recv_path(ppp))
- ppp_recv_unlock(ppp);
+ ppp_recv_lock(ppp);
+ /* ppp->dev == 0 means interface is closing down */
+ if (ppp->dev != 0)
+ ppp_receive_frame(ppp, skb, pch);
+ else
+ kfree_skb(skb);
+ ppp_recv_unlock(ppp);
}
void
ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
{
struct channel *pch = chan->ppp;
+ int proto;
if (pch == 0 || skb->len == 0) {
kfree_skb(skb);
return;
}
- ppp_do_recv(pch->ppp, skb);
+
+ proto = PPP_PROTO(skb);
+ read_lock_bh(&pch->upl);
+ if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+ /* put it on the channel queue */
+ skb_queue_tail(&pch->file.rq, skb);
+ /* drop old frames if queue too long */
+ while (pch->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&pch->file.rq)) != 0)
+ kfree_skb(skb);
+ wake_up_interruptible(&pch->file.rwait);
+ } else {
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ read_unlock_bh(&pch->upl);
}
/* Put a 0-length skb in the receive queue as an error indication */
@@ -930,47 +1224,64 @@ ppp_input_error(struct ppp_channel *chan, int code)
if (pch == 0)
return;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (skb == 0)
- return;
- skb->len = 0; /* probably unnecessary */
- skb->cb[0] = code;
- ppp_do_recv(pch->ppp, skb);
+
+ read_lock_bh(&pch->upl);
+ if (pch->ppp != 0) {
+ skb = alloc_skb(0, GFP_ATOMIC);
+ if (skb != 0) {
+ skb->len = 0; /* probably unnecessary */
+ skb->cb[0] = code;
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ }
+ read_unlock_bh(&pch->upl);
}
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
static void
-ppp_recv_unlock(struct ppp *ppp)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- struct sk_buff *skb;
-
- for (;;) {
- while ((skb = skb_dequeue(&ppp->recv_pending)) != 0)
- ppp_receive_frame(ppp, skb);
- unlock_recv_path(ppp);
- if (skb_peek(&ppp->recv_pending) == 0)
- break;
- if (!trylock_recv_path(ppp))
- break;
+ if (skb->len >= 2) {
+#ifdef CONFIG_PPP_MULTILINK
+ /* XXX do channel-level decompression here */
+ if (PPP_PROTO(skb) == PPP_MP)
+ ppp_receive_mp_frame(ppp, skb, pch);
+ else
+#endif /* CONFIG_PPP_MULTILINK */
+ ppp_receive_nonmp_frame(ppp, skb);
+ return;
}
+
+ if (skb->len > 0)
+ /* note: a 0-length skb is used as an error indication */
+ ++ppp->stats.rx_length_errors;
+
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
+ppp_receive_error(struct ppp *ppp)
+{
+ ++ppp->stats.rx_errors;
+ if (ppp->vj != 0)
+ slhc_toss(ppp->vj);
+}
+
+static void
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
{
struct sk_buff *ns;
int proto, len, npi;
- if (skb->len == 0) {
- /* XXX should do something with code in skb->cb[0] */
- goto err; /* error indication */
- }
-
- if (skb->len < 2) {
- ++ppp->stats.rx_length_errors;
- goto err;
- }
-
- /* Decompress the frame, if compressed. */
+ /*
+ * Decompress the frame, if compressed.
+ * Note that some decompressors need to see uncompressed frames
+ * that come in as well as compressed frames.
+ */
if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN)
&& (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
skb = ppp_decompress_frame(ppp, skb);
@@ -995,7 +1306,12 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
}
len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
if (len <= 0) {
- printk(KERN_ERR "PPP: VJ decompression error\n");
+ int i;
+ printk(KERN_DEBUG "PPP: VJ decompression error\n");
+ printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
+ for (i = 0; i < 16 && i < skb->len; ++i)
+ printk(" %.2x", skb->data[i]);
+ printk("\n");
goto err;
}
len += 2;
@@ -1027,15 +1343,13 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
npi = proto_to_npindex(proto);
if (npi < 0) {
/* control or unknown frame - pass it to pppd */
- skb_queue_tail(&ppp->rq, skb);
+ skb_queue_tail(&ppp->file.rq, skb);
/* limit queue length by dropping old frames */
- while (ppp->rq.qlen > PPP_MAX_RQLEN) {
- skb = skb_dequeue(&ppp->rq);
- if (skb)
- kfree_skb(skb);
- }
+ while (ppp->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&ppp->file.rq)) != 0)
+ kfree_skb(skb);
/* wake up any process polling or blocking on read */
- wake_up_interruptible(&ppp->rwait);
+ wake_up_interruptible(&ppp->file.rwait);
} else {
/* network protocol frame - give it to the kernel */
@@ -1054,10 +1368,8 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
return;
err:
- ++ppp->stats.rx_errors;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static struct sk_buff *
@@ -1070,7 +1382,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
if (proto == PPP_COMP) {
ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);
if (ns == 0) {
- printk(KERN_ERR "ppp_receive: no memory\n");
+ printk(KERN_ERR "ppp_decompress_frame: no memory\n");
goto err;
}
/* the decompressor still expects the A/C bytes in the hdr */
@@ -1101,70 +1413,288 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
err:
ppp->rstate |= SC_DC_ERROR;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
- ++ppp->stats.rx_errors;
+ ppp_receive_error(ppp);
return skb;
}
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ u32 mask, seq, minseq;
+ struct list_head *l;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+
+ if (skb->len < mphdrlen + 3)
+ goto err; /* no good, throw it away */
+
+ /* Decode sequence number and begin/end bits */
+ if (ppp->flags & SC_MP_SHORTSEQ) {
+ seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+ mask = 0xfff;
+ } else {
+ seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+ mask = 0xffffff;
+ }
+ skb->BEbits = skb->data[2];
+ skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/
+
+ /* Expand sequence number to 32 bits */
+ seq |= pch->lastseq & ~mask;
+ if (seq_before(seq, pch->lastseq)) {
+ if (seq_after(seq, pch->lastseq - 100)) {
+ printk(KERN_DEBUG "PPP: MP fragments out of order"
+ " (%u, %u)\n", pch->lastseq, seq);
+ goto err;
+ }
+ seq += mask + 1;
+ }
+ skb->sequence = seq;
+ pch->lastseq = seq;
+
+ /*
+ * Reevaluate minseq, the minimum over all channels of the
+ * last sequence number received on each channel. Because of
+ * the increasing sequence number rule, we know that any fragment
+ * before `minseq' which hasn't arrived is never going to arrive.
+ * The list of channels can't change because we have the receive
+ * side of the ppp unit locked.
+ */
+ minseq = seq;
+ for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {
+ struct channel *ch = list_entry(l, struct channel, clist);
+ if (seq_before(ch->lastseq, seq))
+ seq = ch->lastseq;
+ }
+ ppp->minseq = minseq;
+
+ /* Put the fragment on the reconstruction queue */
+ ppp_mp_insert(ppp, skb);
+
+ /* Pull completed packets off the queue and receive them. */
+ while ((skb = ppp_mp_reconstruct(ppp)) != 0)
+ ppp_receive_nonmp_frame(ppp, skb);
+
+ return;
+
+ err:
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *p;
+ struct sk_buff_head *list = &ppp->mrq;
+ u32 seq = skb->sequence;
+
+ /* N.B. we don't need to lock the list lock because we have the
+ ppp unit receive-side lock. */
+ for (p = list->next; p != (struct sk_buff *)list; p = p->next)
+ if (seq_before(seq, p->sequence))
+ break;
+ __skb_insert(skb, p->prev, p, list);
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+ u32 seq = ppp->nextseq;
+ u32 minseq = ppp->minseq;
+ struct sk_buff_head *list = &ppp->mrq;
+ struct sk_buff *p, *next;
+ struct sk_buff *head, *tail;
+ struct sk_buff *skb = NULL;
+ int lost = 0, len = 0;
+
+ head = list->next;
+ tail = NULL;
+ for (p = head; p != (struct sk_buff *) list; p = next) {
+ next = p->next;
+ if (seq_before(p->sequence, seq)) {
+ /* this can't happen, anyway toss the skb */
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+ p->sequence, seq);
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ continue;
+ }
+ if (p->sequence != seq) {
+ /* Fragment `seq' is missing. If it is after
+ minseq, it might arrive later, so stop here. */
+ if (seq_after(seq, minseq))
+ break;
+ /* Fragment `seq' is lost, keep going. */
+ lost = 1;
+ seq = seq_before(p->sequence, minseq)?
+ p->sequence: minseq;
+ next = p;
+ continue;
+ }
+
+ /*
+ * At this point we know that all the fragments from
+ * ppp->nextseq to seq are either present or lost.
+ * Also, there are no complete packets in the queue
+ * that have no missing fragments and end before this
+ * fragment.
+ */
+
+ /* B bit set indicates this fragment starts a packet */
+ if (p->BEbits & B) {
+ head = p;
+ lost = 0;
+ /* reset len, allow for protocol ID compression */
+ len = p->data[0] & 1;
+ }
+
+ len += p->len;
+
+ /* Got a complete packet yet? */
+ if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+ if (len > ppp->mrru) {
+ ++ppp->stats.rx_length_errors;
+ } else if ((skb = dev_alloc_skb(len)) == NULL) {
+ ++ppp->stats.rx_missed_errors;
+ } else {
+ tail = p;
+ break;
+ }
+ }
+
+ /*
+ * If this is the ending fragment of a packet,
+ * and we haven't found a complete valid packet yet,
+ * we can discard up to and including this fragment.
+ */
+ if (p->BEbits & E)
+ head = next;
+
+ ++seq;
+ }
+
+ /* If we have a complete packet, copy it all into one skb. */
+ if (tail != NULL) {
+ /* If we have discarded any fragments,
+ signal a receive error. */
+ if (head->sequence != ppp->nextseq)
+ ppp_receive_error(ppp);
+
+ /* uncompress protocol ID */
+ if (head->data[0] & 1)
+ *skb_put(skb, 1) = 0;
+ p = head;
+ for (;;) {
+ memcpy(skb_put(skb, p->len), p->data, p->len);
+ if (p == tail)
+ break;
+ p = p->next;
+ }
+ ppp->nextseq = tail->sequence + 1;
+ head = tail->next;
+ }
+
+ /* Discard all the skbuffs that we have copied the data out of
+ or that we can't use. */
+ while ((p = list->next) != head) {
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ }
+
+ return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
/*
* Channel interface.
*/
/*
- * Connect a channel to a given PPP unit.
- * The channel MUST NOT be connected to a PPP unit already.
+ * Create a new, unattached ppp channel.
*/
int
-ppp_register_channel(struct ppp_channel *chan, int unit)
+ppp_register_channel(struct ppp_channel *chan)
{
- struct ppp *ppp;
struct channel *pch;
- int ret = -ENXIO;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp == 0)
- goto out;
pch = kmalloc(sizeof(struct channel), GFP_ATOMIC);
- ret = -ENOMEM;
if (pch == 0)
- goto out;
+ return -ENOMEM;
memset(pch, 0, sizeof(struct channel));
- pch->ppp = ppp;
+ pch->ppp = NULL;
pch->chan = chan;
- list_add(&pch->list, &ppp->channels);
chan->ppp = pch;
- ++ppp->n_channels;
- if (ppp->dev && chan->hdrlen + PPP_HDRLEN > ppp->dev->hard_header_len)
- ppp->dev->hard_header_len = chan->hdrlen + PPP_HDRLEN;
- ret = 0;
- out:
- spin_unlock(&all_ppp_lock);
- return ret;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
+ spin_lock_init(&pch->downl);
+ pch->upl = RW_LOCK_UNLOCKED;
+ spin_lock_bh(&all_channels_lock);
+ pch->file.index = ++last_channel_index;
+ list_add(&pch->file.list, &all_channels);
+ spin_unlock_bh(&all_channels_lock);
+ MOD_INC_USE_COUNT;
+ return 0;
}
/*
- * Disconnect a channel from its PPP unit.
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ return pch->file.index;
+}
+
+/*
+ * Disconnect a channel from the generic layer.
+ * This can be called from mainline or BH/softirq level.
*/
void
ppp_unregister_channel(struct ppp_channel *chan)
{
- struct channel *pch;
+ struct channel *pch = chan->ppp;
- spin_lock(&all_ppp_lock);
- if ((pch = chan->ppp) != 0) {
- chan->ppp = 0;
- list_del(&pch->list);
- --pch->ppp->n_channels;
- kfree(pch);
- }
- spin_unlock(&all_ppp_lock);
+ if (pch == 0)
+ return; /* should never happen */
+ chan->ppp = 0;
+
+ /*
+ * This ensures that we have returned from any calls into the
+ * the channel's start_xmit or ioctl routine before we proceed.
+ */
+ spin_lock_bh(&pch->downl);
+ pch->chan = 0;
+ spin_unlock_bh(&pch->downl);
+ ppp_disconnect_channel(pch);
+ wake_up_interruptible(&pch->file.rwait);
+ spin_lock_bh(&all_channels_lock);
+ list_del(&pch->file.list);
+ spin_unlock_bh(&all_channels_lock);
+ if (atomic_dec_and_test(&pch->file.refcnt))
+ ppp_destroy_channel(pch);
+ MOD_DEC_USE_COUNT;
}
/*
* Callback from a channel when it can accept more to transmit.
- * This should ideally be called at BH level, not interrupt level.
+ * This should be called at BH/softirq level, not interrupt level.
*/
void
ppp_output_wakeup(struct ppp_channel *chan)
@@ -1174,11 +1704,75 @@ ppp_output_wakeup(struct ppp_channel *chan)
if (pch == 0)
return;
- ppp = pch->ppp;
- pch->blocked = 0;
- set_bit(XMIT_WAKEUP, &ppp->busy);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ ppp_channel_push(pch);
+ if (skb_queue_len(&pch->file.xq) == 0) {
+ read_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0)
+ ppp_xmit_process(ppp, 1);
+ read_unlock_bh(&pch->upl);
+ }
+}
+
+/*
+ * This is basically temporary compatibility stuff.
+ */
+ssize_t
+ppp_channel_read(struct ppp_channel *chan, struct file *file,
+ char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_read(&pch->file, file, buf, count);
+}
+
+ssize_t
+ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_write(&pch->file, buf, count);
+}
+
+unsigned int
+ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ struct channel *pch = chan->ppp;
+
+ mask = POLLOUT | POLLWRNORM;
+ if (pch != 0) {
+ poll_wait(file, &pch->file.rwait, wait);
+ if (skb_peek(&pch->file.rq) != 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ return mask;
+}
+
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg)
+{
+ struct channel *pch = chan->ppp;
+ int err = -ENOTTY;
+ int unit;
+
+ if (pch == 0)
+ return -EINVAL;
+ switch (cmd) {
+ case PPPIOCATTACH:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+ case PPPIOCDETACH:
+ err = ppp_disconnect_channel(pch);
+ break;
+ }
+ return err;
}
/*
@@ -1192,6 +1786,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
int err;
struct compressor *cp;
struct ppp_option_data data;
+ void *state;
unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
#ifdef CONFIG_KMOD
char modname[32];
@@ -1220,34 +1815,41 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
err = -ENOBUFS;
if (data.transmit) {
- lock_xmit_path(ppp);
+ ppp_xmit_lock(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state != 0) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
-
- ppp->xcomp = cp;
- ppp->xc_state = cp->comp_alloc(ccp_option, data.length);
- ppp_xmit_unlock(ppp, 1);
- if (ppp->xc_state == 0)
- goto out;
+ ppp_xmit_unlock(ppp);
+
+ state = cp->comp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_xmit_lock(ppp);
+ ppp->xcomp = cp;
+ ppp->xc_state = state;
+ ppp_xmit_unlock(ppp);
+ err = 0;
+ }
} else {
- lock_recv_path(ppp);
+ ppp_recv_lock(ppp);
ppp->rstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state != 0) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
-
- ppp->rcomp = cp;
- ppp->rc_state = cp->decomp_alloc(ccp_option, data.length);
ppp_recv_unlock(ppp);
- if (ppp->rc_state == 0)
- goto out;
+
+ state = cp->decomp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_recv_lock(ppp);
+ ppp->rcomp = cp;
+ ppp->rc_state = state;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ }
}
- err = 0;
out:
return err;
@@ -1292,7 +1894,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->rc_state == 0)
break;
if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
- ppp->index, 0, ppp->mru, ppp->debug)) {
+ ppp->file.index, 0, ppp->mru, ppp->debug)) {
ppp->rstate |= SC_DECOMP_RUN;
ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
}
@@ -1301,7 +1903,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->xc_state == 0)
break;
if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
- ppp->index, 0, ppp->debug))
+ ppp->file.index, 0, ppp->debug))
ppp->xstate |= SC_COMP_RUN;
}
break;
@@ -1329,21 +1931,17 @@ ppp_ccp_closed(struct ppp *ppp)
{
ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
- lock_xmit_path(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
- ppp_xmit_unlock(ppp, 1);
- lock_recv_path(ppp);
ppp->xstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
- ppp_recv_unlock(ppp);
}
/* List of compressors. */
@@ -1451,16 +2049,17 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
}
/*
- * Stuff for handling the list of ppp units and for initialization.
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
*/
/*
- * Create a new ppp unit. Fails if it can't allocate memory or
- * if there is already a unit with the requested number.
+ * Create a new ppp interface unit. Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
* unit == -1 means allocate a new number.
*/
static struct ppp *
-ppp_create_unit(int unit, int *retp)
+ppp_create_interface(int unit, int *retp)
{
struct ppp *ppp;
struct net_device *dev;
@@ -1472,13 +2071,13 @@ ppp_create_unit(int unit, int *retp)
spin_lock(&all_ppp_lock);
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if ((unit < 0 && ppp->index > last_unit + 1)
- || (unit >= 0 && unit < ppp->index))
+ ppp = list_entry(list, struct ppp, file.list);
+ if ((unit < 0 && ppp->file.index > last_unit + 1)
+ || (unit >= 0 && unit < ppp->file.index))
break;
- if (unit == ppp->index)
+ if (unit == ppp->file.index)
goto out; /* unit already exists */
- last_unit = ppp->index;
+ last_unit = ppp->file.index;
}
if (unit < 0)
unit = last_unit + 1;
@@ -1496,17 +2095,15 @@ ppp_create_unit(int unit, int *retp)
}
memset(dev, 0, sizeof(struct net_device));
- ppp->index = unit;
+ ppp->file.index = unit;
sprintf(ppp->name, "ppp%d", unit);
ppp->mru = PPP_MRU;
- skb_queue_head_init(&ppp->xq);
- skb_queue_head_init(&ppp->rq);
- init_waitqueue_head(&ppp->rwait);
- ppp->refcnt = 1;
+ init_ppp_file(&ppp->file, INTERFACE);
for (i = 0; i < NUM_NP; ++i)
ppp->npmode[i] = NPMODE_PASS;
INIT_LIST_HEAD(&ppp->channels);
- skb_queue_head_init(&ppp->recv_pending);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
ppp->dev = dev;
dev->init = ppp_net_init;
@@ -1524,7 +2121,7 @@ ppp_create_unit(int unit, int *retp)
goto out;
}
- list_add(&ppp->list, list->prev);
+ list_add(&ppp->file.list, list->prev);
out:
spin_unlock(&all_ppp_lock);
*retp = ret;
@@ -1534,56 +2131,59 @@ ppp_create_unit(int unit, int *retp)
}
/*
- * Remove a reference to a ppp unit, and destroy it if
- * the reference count goes to 0.
+ * Initialize a ppp_file structure.
*/
-static void ppp_release_unit(struct ppp *ppp)
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
{
- struct list_head *list, *next;
- int ref;
+ pf->kind = kind;
+ skb_queue_head_init(&pf->xq);
+ skb_queue_head_init(&pf->rq);
+ atomic_set(&pf->refcnt, 1);
+ init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Free up all the resources used by a ppp interface unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
+{
+ struct net_device *dev;
spin_lock(&all_ppp_lock);
- ref = --ppp->refcnt;
- if (ref == 0)
- list_del(&ppp->list);
- spin_unlock(&all_ppp_lock);
- if (ref != 0)
- return;
+ list_del(&ppp->file.list);
/* Last fd open to this ppp unit is being closed or detached:
mark the interface down, free the ppp unit */
- if (ppp->dev) {
- rtnl_lock();
- dev_close(ppp->dev);
- rtnl_unlock();
- }
- for (list = ppp->channels.next; list != &ppp->channels; list = next) {
- /* forcibly detach this channel */
- struct channel *chan;
- chan = list_entry(list, struct channel, list);
- chan->chan->ppp = 0;
- next = list->next;
- kfree(chan);
- }
-
- /* Free up resources. */
+ ppp_lock(ppp);
ppp_ccp_closed(ppp);
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
if (ppp->vj) {
slhc_free(ppp->vj);
ppp->vj = 0;
}
- free_skbs(&ppp->xq);
- free_skbs(&ppp->rq);
- free_skbs(&ppp->recv_pending);
- if (ppp->dev) {
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+ dev = ppp->dev;
+ ppp->dev = 0;
+ ppp_unlock(ppp);
+
+ if (dev) {
rtnl_lock();
- unregister_netdevice(ppp->dev);
- ppp->dev = 0;
+ dev_close(dev);
+ unregister_netdevice(dev);
rtnl_unlock();
}
- kfree(ppp);
+
+ /*
+ * We can't acquire any new channels (since we have the
+ * all_ppp_lock) so if n_channels is 0, we can free the
+ * ppp structure. Otherwise we leave it around until the
+ * last channel disconnects from it.
+ */
+ if (ppp->n_channels == 0)
+ kfree(ppp);
+
+ spin_unlock(&all_ppp_lock);
}
/*
@@ -1598,32 +2198,136 @@ ppp_find_unit(int unit)
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if (ppp->index == unit)
+ ppp = list_entry(list, struct ppp, file.list);
+ if (ppp->file.index == unit)
return ppp;
}
return 0;
}
/*
- * Module stuff.
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
*/
-#ifdef MODULE
-int
-init_module(void)
+static struct channel *
+ppp_find_channel(int unit)
{
- ppp_init();
+ struct channel *pch;
+ struct list_head *list;
+
+ list = &all_channels;
+ while ((list = list->next) != &all_channels) {
+ pch = list_entry(list, struct channel, file.list);
+ if (pch->file.index == unit)
+ return pch;
+ }
return 0;
}
-void
-cleanup_module(void)
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+ struct ppp *ppp;
+ int ret = -ENXIO;
+ int hdrlen;
+
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp == 0)
+ goto out;
+ write_lock_bh(&pch->upl);
+ ret = -EINVAL;
+ if (pch->ppp != 0)
+ goto outw;
+ ppp_lock(ppp);
+ spin_lock_bh(&pch->downl);
+ if (pch->chan == 0) /* need to check this?? */
+ goto outr;
+
+ hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
+ if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+ ppp->dev->hard_header_len = hdrlen;
+ list_add(&pch->clist, &ppp->channels);
+ ++ppp->n_channels;
+ pch->ppp = ppp;
+ ret = 0;
+
+ outr:
+ spin_unlock_bh(&pch->downl);
+ ppp_unlock(ppp);
+ outw:
+ write_unlock_bh(&pch->upl);
+ out:
+ spin_unlock(&all_ppp_lock);
+ return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+ struct ppp *ppp;
+ int err = -EINVAL;
+
+ write_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0) {
+ /* remove it from the ppp unit's list */
+ pch->ppp = NULL;
+ ppp_lock(ppp);
+ list_del(&pch->clist);
+ --ppp->n_channels;
+ if (ppp->dev == 0 && ppp->n_channels == 0)
+ /* Last disconnect from a ppp unit
+ that is already dead: free it. */
+ kfree(ppp);
+ else
+ ppp_unlock(ppp);
+ err = 0;
+ }
+ write_unlock_bh(&pch->upl);
+ return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ kfree(pch);
+}
+
+void __exit ppp_cleanup(void)
{
/* should never happen */
- if (!list_empty(&all_ppp_units))
+ if (!list_empty(&all_ppp_units) || !list_empty(&all_channels))
printk(KERN_ERR "PPP: removing module but units remain!\n");
if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
printk(KERN_ERR "PPP: failed to unregister PPP device\n");
- devfs_unregister (devfs_handle);
+ devfs_unregister(devfs_handle);
}
-#endif /* MODULE */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+EXPORT_SYMBOL(ppp_channel_read);
+EXPORT_SYMBOL(ppp_channel_write);
+EXPORT_SYMBOL(ppp_channel_poll);
+EXPORT_SYMBOL(ppp_channel_ioctl);
+EXPORT_SYMBOL(all_ppp_units); /* for debugging */
+EXPORT_SYMBOL(all_channels); /* for debugging */
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index 1a69f6ede..8bae76e1c 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -439,7 +439,7 @@ ppp_sync_ioctl(struct tty_struct *tty, struct file *file,
break;
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
- err = ppp_register_channel(&ap->chan, val);
+ err = ppp_register_channel(&ap->chan);
if (err != 0)
break;
ap->connected = 1;
diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c
index 27b0c6474..e19f1e0fd 100644
--- a/drivers/net/rcpci45.c
+++ b/drivers/net/rcpci45.c
@@ -1034,6 +1034,9 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
printk("RCioctl: cmd = 0x%x\n", cmd);
#endif
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case RCU_PROTOCOL_REV:
@@ -1157,14 +1160,14 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
RCUD_DEFAULT -> rc = 0x11223344;
break;
}
- copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+ if(copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser)))
+ return -EFAULT;
break;
- } /* RCU_COMMAND */
+ } /* RCU_COMMAND */
- default:
- printk("RC default\n");
- rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
- break;
+ default:
+ rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+ return -EINVAL;
}
return 0;
}
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index a3a0018cb..8e8236e92 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -76,8 +76,8 @@ static inline void netif_start_queue(struct net_device *dev)
#else
#define NET_BH 0
#define rr_mark_net_bh(foo) {do{} while(0);}
-#define rr_if_busy(dev) test_bit(LINK_STATE_XOFF, &dev->state)
-#define rr_if_running(dev) test_bit(LINK_STATE_START, &dev->state)
+#define rr_if_busy(dev) netif_queue_stopped(dev)
+#define rr_if_running(dev) netif_running(dev)
#define rr_if_down(dev) {do{} while(0);}
#endif
@@ -1550,7 +1550,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
switch(cmd){
case SIOCRRGFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
@@ -1582,7 +1582,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
kfree(image);
break;
case SIOCRRPFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index 9b17cc79d..bcf6e7e5b 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -1052,7 +1052,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMFREQUENCY: /* set frequency */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(get_user(frequency, (int*) ifr->ifr_data))
return -EFAULT;
@@ -1068,7 +1068,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMPIDS: /* set PIDs */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(copy_from_user(PID, ifr->ifr_data, sizeof(PID)))
return -EFAULT;
diff --git a/drivers/net/setup.c b/drivers/net/setup.c
index d9019b046..f8f59b9fb 100644
--- a/drivers/net/setup.c
+++ b/drivers/net/setup.c
@@ -28,6 +28,7 @@ extern int dlci_setup(void);
extern int lapbeth_init(void);
extern int sdla_setup(void);
extern int sdla_c_setup(void);
+extern int comx_init(void);
extern int abyss_probe(void);
extern int madgemc_probe(void);
@@ -75,7 +76,9 @@ struct net_probe pci_probes[] __initdata = {
#if defined(CONFIG_8xx)
{cpm_enet_init, 0},
#endif
- /*
+#if defined(CONFIG_COMX)
+ {comx_init(), 0},
+#endif /*
* SLHC if present needs attaching so other people see it
* even if not opened.
*/
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index 89cbb0e9e..9190b5a87 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -480,6 +480,8 @@ static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev,
}
#endif
+#ifdef CONFIG_INET
+
static int shaper_neigh_setup(struct neighbour *n)
{
if (n->nud_state == NUD_NONE) {
@@ -499,6 +501,15 @@ static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
return 0;
}
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+ return 0;
+}
+
+#endif
+
static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
{
sh->dev = dev;
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index f8af277ea..2653270c3 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1127,7 +1127,7 @@ static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c
index e1e48bc91..93533c5ef 100644
--- a/drivers/net/skfp/drvfbi.c
+++ b/drivers/net/skfp/drvfbi.c
@@ -129,7 +129,7 @@ extern int AIX_vpdReadByte() ;
/*
* FDDI card reset
*/
-void card_start(smc)
+static void card_start(smc)
struct s_smc *smc ;
{
int i ;
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index 6aceec4d5..b6b112318 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1249,7 +1249,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len);
break;
case SKFP_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
memset(&lp->MacStat, 0, sizeof(lp->MacStat));
} else {
status = -EPERM;
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 344e682f8..059d4a4ea 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.92 2000/02/18 13:49:22 davem Exp $
+/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -254,7 +254,7 @@ do { (__txd)->tx_addr = (__addr); \
#define hme_read_desc32(__hp, __p) (*(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
-#define hme_dma_unmap(__hp, __addr, __size) \
+#define hme_dma_unmap(__hp, __addr, __size, __dir) \
sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
#define hme_dma_sync(__hp, __addr, __size, __dir) \
sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))
diff --git a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in
index 31688f34d..316b638c2 100644
--- a/drivers/net/tokenring/Config.in
+++ b/drivers/net/tokenring/Config.in
@@ -9,6 +9,7 @@ bool 'Token Ring driver support' CONFIG_TR
if [ "$CONFIG_TR" != "n" ]; then
dep_tristate ' IBM Tropic chipset based adapter support' CONFIG_IBMTR $CONFIG_TR
dep_tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR
+ dep_tristate ' IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR
dep_tristate ' Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR $CONFIG_TR
if [ "$CONFIG_TMS380TR" != "n" ]; then
dep_tristate ' Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_TMS380TR
diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile
index f90055b45..1fa4547c4 100644
--- a/drivers/net/tokenring/Makefile
+++ b/drivers/net/tokenring/Makefile
@@ -18,6 +18,7 @@ export-objs := tms380tr.o
obj-$(CONFIG_IBMTR) += ibmtr.o
obj-$(CONFIG_IBMOL) += olympic.o
+obj-$(CONFIG_IBMLS) += lanstreamer.o
obj-$(CONFIG_TMS380TR) += tms380tr.o
obj-$(CONFIG_ABYSS) += abyss.o
obj-$(CONFIG_MADGEMC) += madgemc.o
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
new file mode 100644
index 000000000..e0a339bfa
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -0,0 +1,1776 @@
+/*
+ * lanstreamer.c -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ * 03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing
+ * malloc free checks, reviewed code. <alan@redhat.com>
+ *
+ * To Do:
+ *
+ * 1) Test Network Monitor Mode
+ * 2) Add auto reset logic on adapter errors
+ * 3) Test with varying options
+ *
+ * If Problems do Occur
+ * Most problems can be rectified by either closing and opening the interface
+ * (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult
+ * if compiled into the kernel).
+ */
+
+/* Change STREAMER_DEBUG to 1 to get verbose, and I mean really verbose, messages */
+
+#define STREAMER_DEBUG 0
+#define STREAMER_DEBUG_PACKETS 0
+
+/* Change STREAMER_NETWORK_MONITOR to receive mac frames through the arb channel.
+ * Will also create a /proc/net/streamer_tr entry if proc_fs is compiled into the
+ * kernel.
+ * Intended to be used to create a ring-error reporting network module
+ * i.e. it will give you the source address of beaconers on the ring
+ */
+
+#define STREAMER_NETWORK_MONITOR 0
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <net/checksum.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include "lanstreamer.h"
+
+/* I've got to put some intelligence into the version number so that Peter and I know
+ * which version of the code somebody has got.
+ * Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author.
+ * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike
+ *
+ * Official releases will only have an a.b.c version number format.
+ */
+
+static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan";
+
+static char *open_maj_error[] = {
+ "No error", "Lobe Media Test", "Physical Insertion",
+ "Address Verification", "Neighbor Notification (Ring Poll)",
+ "Request Parameters", "FDX Registration Request",
+ "FDX Lobe Media Test", "FDX Duplicate Address Check",
+ "Unknown stage"
+};
+
+static char *open_min_error[] = {
+ "No error", "Function Failure", "Signal Lost", "Wire Fault",
+ "Ring Speed Mismatch", "Timeout", "Ring Failure", "Ring Beaconing",
+ "Duplicate Node Address", "Request Parameters", "Remove Received",
+ "Reserved", "Reserved", "No Monitor Detected for RPL",
+ "Monitor Contention failer for RPL", "FDX Protocol Error"
+};
+
+/* Module paramters */
+
+/* Ring Speed 0,4,16
+ * 0 = Autosense
+ * 4,16 = Selected speed only, no autosense
+ * This allows the card to be the first on the ring
+ * and become the active monitor.
+ *
+ * WARNING: Some hubs will allow you to insert
+ * at the wrong speed
+ */
+
+static int ringspeed[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(ringspeed, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Packet buffer size */
+
+static int pkt_buf_sz[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Message Level */
+
+static int message_level[STREAMER_MAX_ADAPTERS] = { 1, };
+
+MODULE_PARM(message_level,
+ "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+static int streamer_scan(struct net_device *dev);
+static int streamer_init(struct net_device *dev);
+static int streamer_open(struct net_device *dev);
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev);
+static int streamer_close(struct net_device *dev);
+static void streamer_set_rx_mode(struct net_device *dev);
+static void streamer_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs);
+static struct net_device_stats *streamer_get_stats(struct net_device *dev);
+static int streamer_set_mac_address(struct net_device *dev, void *addr);
+static void streamer_arb_cmd(struct net_device *dev);
+static int streamer_change_mtu(struct net_device *dev, int mtu);
+static void streamer_srb_bh(struct net_device *dev);
+static void streamer_asb_bh(struct net_device *dev);
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int sprintf_info(char *buffer, struct net_device *dev);
+#endif
+#endif
+
+int __init streamer_probe(struct net_device *dev)
+{
+ int cards_found;
+
+ cards_found = streamer_scan(dev);
+ return cards_found ? 0 : -ENODEV;
+}
+
+static int __init streamer_scan(struct net_device *dev)
+{
+ struct pci_dev *pci_device = NULL;
+ struct streamer_private *streamer_priv;
+ int card_no = 0;
+ if (pci_present())
+ {
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+ pci_set_master(pci_device);
+
+ /* Check to see if io has been allocated, if so, we've already done this card,
+ so continue on the card discovery loop */
+
+ if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE))
+ {
+ card_no++;
+ continue;
+ }
+
+ streamer_priv = kmalloc(sizeof(struct streamer_private), GFP_KERNEL);
+ if(streamer_priv==NULL)
+ {
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+ memset(streamer_priv, 0, sizeof(struct streamer_private));
+#ifndef MODULE
+ dev = init_trdev(dev, 0);
+ if(dev==NULL)
+ {
+ kfree(streamer_priv);
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+#endif
+ dev->priv = (void *) streamer_priv;
+#if STREAMER_DEBUG
+ printk("pci_device: %p, dev:%p, dev->priv: %p\n",
+ pci_device, dev, dev->priv);
+#endif
+ dev->irq = pci_device->irq;
+ dev->base_addr = pci_device->resource[0].start;
+ dev->init = &streamer_init;
+ streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
+ init_waitqueue_head(&streamer_priv->srb_wait);
+ init_waitqueue_head(&streamer_priv->trb_wait);
+ if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
+ streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
+ else
+ streamer_priv->pkt_buf_sz = pkt_buf_sz[card_no];
+
+ streamer_priv->streamer_ring_speed = ringspeed[card_no];
+ streamer_priv->streamer_message_level = message_level[card_no];
+ streamer_priv->streamer_multicast_set = 0;
+
+ if (streamer_init(dev) == -1) {
+ unregister_netdevice(dev);
+ kfree(dev->priv);
+ return 0;
+ }
+
+ dev->open = &streamer_open;
+ dev->hard_start_xmit = &streamer_xmit;
+ dev->change_mtu = &streamer_change_mtu;
+
+ dev->stop = &streamer_close;
+ dev->do_ioctl = NULL;
+ dev->set_multicast_list = &streamer_set_rx_mode;
+ dev->get_stats = &streamer_get_stats;
+ dev->set_mac_address = &streamer_set_mac_address;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int __init streamer_init(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ __u8 *streamer_mmio;
+ unsigned long t;
+ unsigned int uaa_addr;
+ struct sk_buff *skb = 0;
+ __u16 misr;
+
+ streamer_priv = (struct streamer_private *) dev->priv;
+ streamer_mmio = streamer_priv->streamer_mmio;
+
+ printk("%s \n", version);
+ printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n",
+ dev->name, (unsigned int) dev->base_addr,
+ streamer_priv->streamer_mmio, dev->irq);
+
+ request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
+ writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL);
+ t = jiffies;
+ /* Hold soft reset bit for a while */
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ);
+
+ writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET,
+ streamer_mmio + BCTL);
+
+#if STREAMER_DEBUG
+ printk("BCTL: %x\n", readw(streamer_mmio + BCTL));
+ printk("GPR: %x\n", readw(streamer_mmio + GPR));
+ printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+ if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */
+ writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE,
+ streamer_mmio + GPR);
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Ringspeed autosense mode on\n",
+ dev->name);
+ } else if (streamer_priv->streamer_ring_speed == 16) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n",
+ dev->name);
+ writew(GPR_16MBPS, streamer_mmio + GPR);
+ } else if (streamer_priv->streamer_ring_speed == 4) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n",
+ dev->name);
+ writew(0, streamer_mmio + GPR);
+ }
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (!skb) {
+ printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n",
+ dev->name);
+ } else {
+ streamer_priv->streamer_rx_ring[0].forward = 0;
+ streamer_priv->streamer_rx_ring[0].status = 0;
+ streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[0].framelen_buflen = 512; /* streamer_priv->pkt_buf_sz; */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ }
+
+#if STREAMER_DEBUG
+ printk("GPR = %x\n", readw(streamer_mmio + GPR));
+#endif
+ /* start solo init */
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+
+ while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+ if (jiffies - t > 40 * HZ) {
+ printk(KERN_ERR
+ "IBM PCI tokenring card not responding\n");
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+ }
+ writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (skb)
+ dev_kfree_skb(skb); /* release skb used for diagnostics */
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x LAPE: %x\n",
+ readw(streamer_mmio + LAPWWO), readw(streamer_mmio + LAPA),
+ readw(streamer_mmio + LAPE));
+#endif
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("initialization response srb dump: ");
+ for (i = 0; i < 10; i++)
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ printk("\n");
+ }
+#endif
+
+ writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA);
+ if (readw(streamer_mmio + LAPD)) {
+ printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",
+ readw(streamer_mmio + LAPD));
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+ uaa_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* skip over Level.Addr field */
+ streamer_priv->streamer_addr_table_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->streamer_parms_addr = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ printk("UAA resides at %x\n", uaa_addr);
+#endif
+
+ /* setup uaa area for access with LAPD */
+ writew(uaa_addr, streamer_mmio + LAPA);
+
+ /* setup uaa area for access with LAPD */
+ {
+ int i;
+ __u16 addr;
+ writew(uaa_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 6; i += 2) {
+ addr = readw(streamer_mmio + LAPDINC);
+ dev->dev_addr[i] = addr & 0xff;
+ dev->dev_addr[i + 1] = (addr >> 8) & 0xff;
+ }
+#if STREAMER_DEBUG
+ printk("Adapter address: ");
+ for (i = 0; i < 6; i++) {
+ printk("%02x:", dev->dev_addr[i]);
+ }
+ printk("\n");
+#endif
+ }
+ return 0;
+}
+
+static int streamer_open(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ char open_error[255];
+ int i, open_finished = 1;
+ __u16 srb_word;
+ __u16 srb_open;
+
+
+ if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) {
+ return -EAGAIN;
+ }
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("pending ints: %x\n", readw(streamer_mmio + SISR));
+#endif
+
+ writew(SISR_MI | SISR_SRB_REPLY, streamer_mmio + SISR_MASK); /* more ints later, doesn't stop arb cmd interrupt */
+ writew(LISR_LIE, streamer_mmio + LISR); /* more ints later */
+
+ /* adapter is closed, so SRB is pointed to by LAPWWO */
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x\n", readw(streamer_mmio + LAPWWO),
+ readw(streamer_mmio + LAPA));
+ printk("LAPE: %x\n", readw(streamer_mmio + LAPE));
+ printk("SISR Mask = %04x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+ do {
+ int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < SRB_COMMAND_SIZE; i += 2) {
+ writew(0, streamer_mmio + LAPDINC);
+ }
+
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC); /* open */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+#if STREAMER_NETWORK_MONITOR
+ /* If Network Monitor, instruct card to copy MAC frames through the ARB */
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC); /* offset 8 word contains open options */
+#else
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC); /* Offset 8 word contains Open.Options */
+#endif
+
+ if (streamer_priv->streamer_laa[0]) {
+ writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA);
+ writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC); /* offset 12 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC); /* offset 14 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC); /* offset 16 word */
+ memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len);
+ }
+
+ /* save off srb open offset */
+ srb_open = readw(streamer_mmio + LAPWWO);
+#if STREAMER_DEBUG
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("srb open request: \n");
+ for (i = 0; i < 16; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+
+ streamer_priv->srb_queued = 1;
+
+ /* signal solo that SRB command has been issued */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued) {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ);
+ if (signal_pending(current)) {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x, LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+ restore_flags(flags);
+
+#if STREAMER_DEBUG
+ printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+ printk("srb open response:\n");
+ writew(srb_open, streamer_mmio + LAPA);
+ for (i = 0; i < 10; i++) {
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+#endif
+
+ /* If we get the same return response as we set, the interrupt wasn't raised and the open
+ * timed out.
+ */
+ writew(srb_open + 2, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word == STREAMER_CLEAR_RET_CODE) {
+ printk(KERN_WARNING "%s: Adapter Open time out or error.\n",
+ dev->name);
+ return -EIO;
+ }
+
+ if (srb_word != 0) {
+ if (srb_word == 0x07) {
+ if (!streamer_priv->streamer_ring_speed && open_finished) { /* Autosense , first time around */
+ printk(KERN_WARNING "%s: Retrying at different ring speed \n",
+ dev->name);
+ open_finished = 0;
+ } else {
+ __u16 error_code;
+
+ writew(srb_open + 6, streamer_mmio + LAPA);
+ error_code = ntohs(readw(streamer_mmio + LAPD));
+ strcpy(open_error, open_maj_error[(error_code & 0xf0) >> 4]);
+ strcat(open_error, " - ");
+ strcat(open_error, open_min_error[(error_code & 0x0f)]);
+
+ if (!streamer_priv->streamer_ring_speed
+ && ((error_code & 0x0f) == 0x0d))
+ {
+ printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name);
+ printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ printk(KERN_WARNING "%s: %s\n",
+ dev->name, open_error);
+ free_irq(dev->irq, dev);
+ return -EIO;
+
+ } /* if autosense && open_finished */
+ } else {
+ printk(KERN_WARNING "%s: Bad OPEN response: %x\n",
+ dev->name, srb_word);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+ } else
+ open_finished = 1;
+ } while (!(open_finished)); /* Will only loop if ring speed mismatch re-open attempted && autosense is on */
+
+ writew(srb_open + 18, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word & (1 << 3))
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name);
+
+ if (srb_word & 1)
+ streamer_priv->streamer_ring_speed = 16;
+ else
+ streamer_priv->streamer_ring_speed = 4;
+
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in %d Mbps mode\n",
+ dev->name,
+ streamer_priv->streamer_ring_speed);
+
+ writew(srb_open + 8, streamer_mmio + LAPA);
+ streamer_priv->asb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->srb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->arb = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* offset 14 word is rsvd */
+ streamer_priv->trb = ntohs(readw(streamer_mmio + LAPDINC));
+
+ streamer_priv->streamer_receive_options = 0x00;
+ streamer_priv->streamer_copy_all_options = 0;
+
+ /* setup rx ring */
+ /* enable rx channel */
+ writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM);
+
+ /* setup rx descriptors */
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (skb == NULL)
+ break;
+
+ skb->dev = dev;
+
+ streamer_priv->streamer_rx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_rx_ring[i + 1]);
+ streamer_priv->streamer_rx_ring[i].status = 0;
+ streamer_priv->streamer_rx_ring[i].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[i].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->rx_ring_skb[i] = skb;
+ }
+ streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_rx_ring[0]);
+
+ if (i == 0) {
+ printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ streamer_priv->rx_ring_last_received = STREAMER_RX_RING_SIZE - 1; /* last processed rx status */
+
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1]), streamer_mmio + RXLBDA);
+
+ /* set bus master interrupt event mask */
+ writew(MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+
+
+ /* setup tx ring */
+ writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM); /* Enables TX channel 2 */
+ for (i = 0; i < STREAMER_TX_RING_SIZE; i++) {
+ streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]);
+ streamer_priv->streamer_tx_ring[i].status = 0;
+ streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[i].buffer = 0;
+ streamer_priv->streamer_tx_ring[i].buflen = 0;
+ }
+ streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_tx_ring[0]);;
+
+ streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE;
+ streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */
+ streamer_priv->tx_ring_last_status = STREAMER_TX_RING_SIZE - 1;
+
+ /* set Busmaster interrupt event mask (handle receives on interrupt only */
+ writew(MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+ /* set system event interrupt mask */
+ writew(SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE, streamer_mmio + SISR_MASK_SUM);
+
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("SISR MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+#if STREAMER_NETWORK_MONITOR
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ printk("%s: Node Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ readw(streamer_mmio + LAPDINC);
+ readw(streamer_mmio + LAPDINC);
+ printk("%s: Functional Address: %04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+
+ writew(streamer_priv->streamer_parms_addr + 4,
+ streamer_mmio + LAPA);
+ printk("%s: NAUN Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+#endif
+
+ netif_start_queue(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * When we enter the rx routine we do not know how many frames have been
+ * queued on the rx channel. Therefore we start at the next rx status
+ * position and travel around the receive ring until we have completed
+ * all the frames.
+ *
+ * This means that we may process the frame before we receive the end
+ * of frame interrupt. This is why we always test the status instead
+ * of blindly processing the next frame.
+ *
+ */
+static void streamer_rx(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_rx_desc *rx_desc;
+ int rx_ring_last_received, length, frame_length, buffer_cnt = 0;
+ struct sk_buff *skb, *skb2;
+
+ /* setup the next rx descriptor to be received */
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ while (rx_desc->status & 0x01000000) { /* While processed descriptors are available */
+ if (rx_ring_last_received != streamer_priv->rx_ring_last_received)
+ {
+ printk(KERN_WARNING "RX Error 1 rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received, streamer_priv->rx_ring_last_received);
+ }
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ frame_length = (rx_desc->framelen_buflen >> 16) & 0xffff;
+
+ if (rx_desc->status & 0x7E830000) { /* errors */
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_WARNING "%s: Rx Error %x \n",
+ dev->name, rx_desc->status);
+ }
+ } else { /* received without errors */
+ if (rx_desc->status & 0x80000000) { /* frame complete */
+ buffer_cnt = 1;
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ } else {
+ skb = dev_alloc_skb(frame_length);
+ }
+
+ if (skb == NULL)
+ {
+ printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name);
+ streamer_priv->streamer_stats.rx_dropped++;
+ } else { /* we allocated an skb OK */
+ skb->dev = dev;
+
+ if (buffer_cnt == 1) {
+ skb2 = streamer_priv->rx_ring_skb[rx_ring_last_received];
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_rx packet print: skb->data2 %p skb->head %p\n", skb2->data, skb2->head);
+ for (i = 0; i < frame_length; i++)
+ {
+ printk("%x:", skb2->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+ skb_put(skb2, length);
+ skb2->protocol = tr_type_trans(skb2, dev);
+ /* recycle this descriptor */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ streamer_priv-> rx_ring_skb[rx_ring_last_received] = skb;
+ /* place recycled descriptor back on the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]),streamer_mmio + RXLBDA);
+ /* pass the received skb up to the protocol */
+ netif_rx(skb2);
+ } else {
+ do { /* Walk the buffers */
+ memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length); /* copy this fragment */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ /* give descriptor back to the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA);
+
+ if (rx_desc->status & 0x80000000)
+ break; /* this descriptor completes the frame */
+
+ /* else get the next pending descriptor */
+ if (rx_ring_last_received!= streamer_priv->rx_ring_last_received)
+ {
+ printk("RX Error rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received,
+ streamer_priv->rx_ring_last_received);
+ }
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE-1)];
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+ } while (1);
+
+ skb->protocol = tr_type_trans(skb, dev);
+ /* send up to the protocol */
+ netif_rx(skb);
+ }
+ streamer_priv->streamer_stats.rx_packets++;
+ streamer_priv->streamer_stats.rx_bytes += length;
+ } /* if skb == null */
+ } /* end received without errors */
+
+ /* try the next one */
+ rx_desc = &streamer_priv->streamer_rx_ring[(rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ } /* end for all completed rx descriptors */
+}
+
+static void streamer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 sisr;
+ __u16 misr;
+ __u16 sisrmask;
+
+ sisrmask = SISR_MI;
+ writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
+ sisr = readw(streamer_mmio + SISR);
+ writew(~sisr, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (!sisr) { /* Interrupt isn't for us */
+ return;
+ }
+
+ if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
+ || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
+ if (sisr & SISR_SRB_REPLY) {
+ if (streamer_priv->srb_queued == 1) {
+ wake_up_interruptible(&streamer_priv->srb_wait);
+ } else if (streamer_priv->srb_queued == 2) {
+ streamer_srb_bh(dev);
+ }
+ streamer_priv->srb_queued = 0;
+ }
+ /* SISR_SRB_REPLY */
+ if (misr & MISR_TX2_EOF) {
+ while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status)
+ {
+ streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1);
+ streamer_priv->free_tx_ring_entries++;
+ streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len;
+ streamer_priv->streamer_stats.tx_packets++;
+ dev_kfree_skb_irq(streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]);
+ streamer_priv-> streamer_tx_ring[streamer_priv->tx_ring_last_status].buffer = 0xdeadbeef;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0;
+ }
+ netif_wake_queue(dev);
+ }
+
+ if (misr & MISR_RX_EOF) {
+ streamer_rx(dev);
+ }
+ /* MISR_RX_EOF */
+ if (sisr & SISR_ADAPTER_CHECK) {
+ printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name);
+ writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
+ dev->name, readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC));
+ free_irq(dev->irq, dev);
+ }
+
+ /* SISR_ADAPTER_CHECK */
+ if (sisr & SISR_ASB_FREE) {
+ /* Wake up anything that is waiting for the asb response */
+ if (streamer_priv->asb_queued) {
+ streamer_asb_bh(dev);
+ }
+ }
+ /* SISR_ASB_FREE */
+ if (sisr & SISR_ARB_CMD) {
+ streamer_arb_cmd(dev);
+ }
+ /* SISR_ARB_CMD */
+ if (sisr & SISR_TRB_REPLY) {
+ /* Wake up anything that is waiting for the trb response */
+ if (streamer_priv->trb_queued) {
+ wake_up_interruptible(&streamer_priv->
+ trb_wait);
+ }
+ streamer_priv->trb_queued = 0;
+ }
+ /* SISR_TRB_REPLY */
+ if (misr & MISR_RX_NOBUF) {
+ /* According to the documentation, we don't have to do anything, but trapping it keeps it out of
+ /var/log/messages. */
+ } /* SISR_RX_NOBUF */
+ } else {
+ printk(KERN_WARNING "%s: Unexpected interrupt: %x\n",
+ dev->name, sisr);
+ printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name,
+ readw(streamer_mmio + SISR_MASK));
+ } /* One if the interrupts we want */
+
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+}
+
+
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ netif_stop_queue(dev);
+
+ if (streamer_priv->free_tx_ring_entries) {
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len;
+ streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb;
+ streamer_priv->free_tx_ring_entries--;
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_xmit packet print:\n");
+ for (i = 0; i < skb->len; i++) {
+ printk("%x:", skb->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+
+ writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
+
+ streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
+ netif_start_queue(dev);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+static int streamer_close(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ int i;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ save_flags(flags);
+ cli();
+
+ streamer_priv->srb_queued = 1;
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued)
+ {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait,
+ jiffies + 60 * HZ);
+ if (signal_pending(current))
+ {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ }
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* TBD. Add graceful way to reset the LLC channel without doing a soft reset.
+ writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL);
+ */
+
+#if STREAMER_DEBUG
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ printk("srb): ");
+ for (i = 0; i < 2; i++) {
+ printk("%x ", htons(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void streamer_set_rx_mode(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 options = 0, set_mc_list = 0;
+ __u16 ata1, ata2;
+ struct dev_mc_list *dmi;
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+ options = streamer_priv->streamer_copy_all_options;
+
+ if (dev->flags & IFF_PROMISC)
+ options |= (3 << 5); /* All LLC and MAC frames, all through the main rx channel */
+ else
+ options &= ~(3 << 5);
+
+ if (dev->mc_count) {
+ set_mc_list = 1;
+ }
+
+ /* Only issue the srb if there is a change in options */
+
+ if ((options ^ streamer_priv->streamer_copy_all_options))
+ {
+ /* Now to issue the srb command to alter the copy.all.options */
+
+ writew(SRB_MODIFY_RECEIVE_OPTIONS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC);
+ writew(0x414a, streamer_mmio + LAPDINC);
+ writew(0x454d, streamer_mmio + LAPDINC);
+ writew(0x2053, streamer_mmio + LAPDINC);
+ writew(0x2020, streamer_mmio + LAPDINC);
+
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_copy_all_options = options;
+ return;
+ }
+
+ if (set_mc_list ^ streamer_priv->streamer_multicast_set)
+ { /* Multicast options have changed */
+ dmi = dev->mc_list;
+
+ writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ ata1 = readw(streamer_mmio + LAPDINC);
+ ata2 = readw(streamer_mmio + LAPD);
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+
+ if (set_mc_list)
+ {
+ /* Turn multicast on */
+
+ /* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00
+ * We do this with a set functional address mask.
+ */
+
+ if (!(ata1 & 0x0400)) { /* need to set functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 | 0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 1;
+ }
+
+ } else { /* Turn multicast off */
+
+ if ((ata1 & 0x0400)) { /* Hmmm, need to reset the functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 & ~0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 0;
+ }
+ }
+
+ }
+}
+
+static void streamer_srb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 srb_word;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+
+ switch (srb_word) {
+
+ /* SRB_MODIFY_RECEIVE_OPTIONS i.e. set_multicast_list options (promiscuous)
+ * At some point we should do something if we get an error, such as
+ * resetting the IFF_PROMISC flag in dev
+ */
+
+ case SRB_MODIFY_RECEIVE_OPTIONS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_WARNING "%s: Receive Options Modified to %x,%x\n",
+ dev->name,
+ streamer_priv->streamer_copy_all_options,
+ streamer_priv->streamer_receive_options);
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_GROUP_ADDRESS - Multicast group setting
+ */
+ case SRB_SET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 1;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x3c:
+ printk(KERN_WARNING "%s: Group/Functional address indicator bits not set correctly\n", dev->name);
+ break;
+ case 0x3e: /* If we ever implement individual multicast addresses, will need to deal with this */
+ printk(KERN_WARNING "%s: Group address registers full\n", dev->name);
+ break;
+ case 0x55:
+ printk(KERN_INFO "%s: Group Address already set.\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list
+ */
+ case SRB_RESET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 0;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x39: /* Must deal with this if individual multicast addresses used */
+ printk(KERN_INFO "%s: Group address not found \n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_FUNC_ADDRESS - Called by the set_rx_mode
+ */
+
+ case SRB_SET_FUNC_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Functional Address Mask Set \n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_LOG - Read and reset the adapter error counters
+ */
+
+ case SRB_READ_LOG:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ {
+ int i;
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Log command complete\n", dev->name);
+ printk("Read Log statistics: ");
+ writew(streamer_priv->srb + 6,
+ streamer_mmio + LAPA);
+ for (i = 0; i < 5; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+ }
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */
+
+ case SRB_READ_SR_COUNTERS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Source Routing Counters issued\n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ default:
+ printk(KERN_WARNING "%s: Unrecognized srb bh return value.\n", dev->name);
+ break;
+ } /* switch srb[0] */
+}
+
+static struct net_device_stats *streamer_get_stats(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ streamer_priv = (struct streamer_private *) dev->priv;
+ return (struct net_device_stats *) &streamer_priv->streamer_stats;
+}
+
+static int streamer_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *saddr = addr;
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+
+ if (netif_running(dev)) {
+ printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name);
+ return -EBUSY;
+ }
+
+ memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len);
+
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_INFO "%s: MAC/LAA Set to = %x.%x.%x.%x.%x.%x\n",
+ dev->name, streamer_priv->streamer_laa[0],
+ streamer_priv->streamer_laa[1],
+ streamer_priv->streamer_laa[2],
+ streamer_priv->streamer_laa[3],
+ streamer_priv->streamer_laa[4],
+ streamer_priv->streamer_laa[5]);
+ }
+ return 0;
+}
+
+static void streamer_arb_cmd(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 header_len;
+ __u16 frame_len, buffer_len;
+ struct sk_buff *mac_frame;
+ __u8 frame_data[256];
+ __u16 buff_off;
+ __u16 lan_status = 0, lan_status_diff; /* Initialize to stop compiler warning */
+ __u8 fdx_prot_error;
+ __u16 next_ptr;
+ __u16 arb_word;
+
+#if STREAMER_NETWORK_MONITOR
+ struct trh_hdr *mac_hdr;
+#endif
+
+ writew(streamer_priv->arb, streamer_mmio + LAPA);
+ arb_word = readw(streamer_mmio + LAPD) & 0xFF;
+
+ if (arb_word == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC));
+ header_len = readw(streamer_mmio + LAPDINC) & 0xff; /* 802.5 Token-Ring Header Length */
+ frame_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ __u16 next;
+ __u8 status;
+ __u16 len;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /*setup window to frame data */
+ next = ntohs(readw(streamer_mmio + LAPDINC));
+ status =
+ ntohs(readw(streamer_mmio + LAPDINC)) & 0xff;
+ len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ /* print out 1st 14 bytes of frame data */
+ for (i = 0; i < 7; i++) {
+ printk("Loc %d = %04x\n", i,
+ ntohs(readw
+ (streamer_mmio + LAPDINC)));
+ }
+
+ printk("next %04x, fs %02x, len %04x \n", next,
+ status, len);
+ }
+#endif
+ mac_frame = dev_alloc_skb(frame_len);
+
+ /* Walk the buffer chain, creating the frame */
+
+ do {
+ int i;
+ __u16 rx_word;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /* setup window to frame data */
+ next_ptr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* read thru status word */
+ buffer_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ if (buffer_len > 256)
+ break;
+
+ i = 0;
+ while (i < buffer_len) {
+ rx_word = readw(streamer_mmio + LAPDINC);
+ frame_data[i] = rx_word & 0xff;
+ frame_data[i + 1] = (rx_word >> 8) & 0xff;
+ i += 2;
+ }
+
+ memcpy_fromio(skb_put(mac_frame, buffer_len),
+ frame_data, buffer_len);
+ } while (next_ptr && (buff_off = next_ptr));
+
+#if STREAMER_NETWORK_MONITOR
+ printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
+ dev->name);
+ mac_hdr = (struct trh_hdr *) mac_frame->data;
+ printk(KERN_WARNING
+ "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
+ mac_hdr->daddr[2], mac_hdr->daddr[3],
+ mac_hdr->daddr[4], mac_hdr->daddr[5]);
+ printk(KERN_WARNING
+ "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->saddr[0], mac_hdr->saddr[1],
+ mac_hdr->saddr[2], mac_hdr->saddr[3],
+ mac_hdr->saddr[4], mac_hdr->saddr[5]);
+#endif
+ mac_frame->dev = dev;
+ mac_frame->protocol = tr_type_trans(mac_frame, dev);
+ netif_rx(mac_frame);
+
+ /* Now tell the card we have dealt with the received frame */
+
+ /* Set LISR Bit 1 */
+ writel(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ /* Is the ASB free ? */
+
+ if (!(readl(streamer_priv->streamer_mmio + SISR) & SISR_ASB_FREE))
+ {
+ streamer_priv->asb_queued = 1;
+ writel(LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ return;
+ /* Drop out and wait for the bottom half to be run */
+ }
+
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+
+ streamer_priv->asb_queued = 2;
+ return;
+
+ } else if (arb_word == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ lan_status = ntohs(readw(streamer_mmio + LAPDINC));
+ fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF;
+
+ /* Issue ARB Free */
+ writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status;
+
+ if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR))
+ {
+ if (lan_status_diff & LSC_LWF)
+ printk(KERN_WARNING "%s: Short circuit detected on the lobe\n", dev->name);
+ if (lan_status_diff & LSC_ARW)
+ printk(KERN_WARNING "%s: Auto removal error\n", dev->name);
+ if (lan_status_diff & LSC_FPE)
+ printk(KERN_WARNING "%s: FDX Protocol Error\n", dev->name);
+ if (lan_status_diff & LSC_RR)
+ printk(KERN_WARNING "%s: Force remove MAC frame received\n", dev->name);
+
+ /* Adapter has been closed by the hardware */
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* @TBD. no llc reset on autostreamer writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); */
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name);
+
+ }
+ /* If serious error */
+ if (streamer_priv->streamer_message_level) {
+ if (lan_status_diff & LSC_SIG_LOSS)
+ printk(KERN_WARNING "%s: No receive signal detected \n", dev->name);
+ if (lan_status_diff & LSC_HARD_ERR)
+ printk(KERN_INFO "%s: Beaconing \n", dev->name);
+ if (lan_status_diff & LSC_SOFT_ERR)
+ printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n", dev->name);
+ if (lan_status_diff & LSC_TRAN_BCN)
+ printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name);
+ if (lan_status_diff & LSC_SS)
+ printk(KERN_INFO "%s: Single Station on the ring \n", dev->name);
+ if (lan_status_diff & LSC_RING_REC)
+ printk(KERN_INFO "%s: Ring recovery ongoing\n", dev->name);
+ if (lan_status_diff & LSC_FDX_MODE)
+ printk(KERN_INFO "%s: Operating in FDX mode\n", dev->name);
+ }
+
+ if (lan_status_diff & LSC_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Counter Overflow \n", dev->name);
+
+ /* Issue READ.LOG command */
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_LOG, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+ }
+
+ if (lan_status_diff & LSC_SR_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name);
+
+ /* Issue a READ.SR.COUNTERS */
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_SR_COUNTERS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE,
+ streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ }
+ streamer_priv->streamer_lan_status = lan_status;
+ } /* Lan.change.status */
+ else
+ printk(KERN_WARNING "%s: Unknown arb command \n", dev->name);
+}
+
+static void streamer_asb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ if (streamer_priv->asb_queued == 1)
+ {
+ /* Dropped through the first time */
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ streamer_priv->asb_queued = 2;
+
+ return;
+ }
+
+ if (streamer_priv->asb_queued == 2) {
+ __u8 rc;
+ writew(streamer_priv->asb + 2, streamer_mmio + LAPA);
+ rc = readw(streamer_mmio + LAPD) & 0xff;
+ switch (rc) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name);
+ break;
+ case 0x26:
+ printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name);
+ break;
+ case 0xFF:
+ /* Valid response, everything should be ok again */
+ break;
+ default:
+ printk(KERN_WARNING "%s: Invalid return code in asb\n", dev->name);
+ break;
+ }
+ }
+ streamer_priv->asb_queued = 0;
+}
+
+static int streamer_change_mtu(struct net_device *dev, int mtu)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u16 max_mtu;
+
+ if (streamer_priv->streamer_ring_speed == 4)
+ max_mtu = 4500;
+ else
+ max_mtu = 18000;
+
+ if (mtu > max_mtu)
+ return -EINVAL;
+ if (mtu < 100)
+ return -EINVAL;
+
+ dev->mtu = mtu;
+ streamer_priv->pkt_buf_sz = mtu + TR_HLEN;
+
+ return 0;
+}
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int streamer_proc_info(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ struct pci_dev *pci_device = NULL;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct net_device *dev;
+
+
+ size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n");
+
+ pos += size;
+ len += size;
+
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->base_addr == (pci_device->base_address[0] & (~3)))
+ { /* Yep, a Streamer device */
+ size = sprintf_info(buffer + len, dev);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ } /* if */
+ } /* for */
+ } /* While */
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+ return len;
+}
+
+static int sprintf_info(char *buffer, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_adapter_addr_table sat;
+ struct streamer_parameters_table spt;
+ int size = 0;
+ int i;
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 14; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & sat;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+ writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 68; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & spt;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+
+
+ size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4],
+ dev->dev_addr[5], sat.node_addr[0], sat.node_addr[1],
+ sat.node_addr[2], sat.node_addr[3], sat.node_addr[4],
+ sat.node_addr[5], sat.func_addr[0], sat.func_addr[1],
+ sat.func_addr[2], sat.func_addr[3]);
+
+ size += sprintf(buffer + size, "\n%6s: Token Ring Parameters Table:\n", dev->name);
+
+ size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address : Poll Address : AccPri : Auth Src : Att Code :\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x :\n",
+ dev->name, spt.phys_addr[0], spt.phys_addr[1],
+ spt.phys_addr[2], spt.phys_addr[3],
+ spt.up_node_addr[0], spt.up_node_addr[1],
+ spt.up_node_addr[2], spt.up_node_addr[3],
+ spt.up_node_addr[4], spt.up_node_addr[4],
+ spt.poll_addr[0], spt.poll_addr[1], spt.poll_addr[2],
+ spt.poll_addr[3], spt.poll_addr[4], spt.poll_addr[5],
+ ntohs(spt.acc_priority), ntohs(spt.auth_source_class),
+ ntohs(spt.att_code));
+
+ size += sprintf(buffer + size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x : %04x : %04x : %04x : \n",
+ dev->name, spt.source_addr[0], spt.source_addr[1],
+ spt.source_addr[2], spt.source_addr[3],
+ spt.source_addr[4], spt.source_addr[5],
+ ntohs(spt.beacon_type), ntohs(spt.major_vector),
+ ntohs(spt.lan_status), ntohs(spt.local_ring),
+ ntohs(spt.mon_error), ntohs(spt.frame_correl));
+
+ size += sprintf(buffer + size, "%6s: Beacon Details : Tx : Rx : NAUN Node Address : NAUN Node Phys : \n",
+ dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: : %02x : %02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x : \n",
+ dev->name, ntohs(spt.beacon_transmit),
+ ntohs(spt.beacon_receive), spt.beacon_naun[0],
+ spt.beacon_naun[1], spt.beacon_naun[2],
+ spt.beacon_naun[3], spt.beacon_naun[4],
+ spt.beacon_naun[5], spt.beacon_phys[0],
+ spt.beacon_phys[1], spt.beacon_phys[2],
+ spt.beacon_phys[3]);
+ return size;
+}
+#endif
+#endif
+
+#ifdef MODULE
+
+static struct net_device *dev_streamer[STREAMER_MAX_ADAPTERS];
+
+int init_module(void)
+{
+ int i;
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+
+ ent = create_proc_entry("net/streamer_tr", 0, 0);
+ ent->read_proc = &streamer_proc_info;
+#endif
+#endif
+ for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++)
+ {
+ dev_streamer[i] = NULL;
+ dev_streamer[i] = init_trdev(dev_streamer[i], 0);
+ if (dev_streamer[i] == NULL)
+ return -ENOMEM;
+
+ dev_streamer[i]->init = &streamer_probe;
+
+ if (register_trdev(dev_streamer[i]) != 0) {
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ if (i == 0)
+ {
+ printk(KERN_INFO "Streamer: No IBM LanStreamer PCI Token Ring cards found in system.\n");
+ return -EIO;
+ } else {
+ printk(KERN_INFO "Streamer: %d IBM LanStreamer PCI Token Ring card(s) found in system.\n", i);
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+ for (i = 0; i < STREAMER_MAX_ADAPTERS; i++)
+ if (dev_streamer[i]) {
+ unregister_trdev(dev_streamer[i]);
+ release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE);
+ kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private));
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ }
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("net/streamer_tr", NULL);
+#endif
+#endif
+}
+#endif /* MODULE */
diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h
new file mode 100644
index 000000000..7ba86dfe5
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.h
@@ -0,0 +1,319 @@
+/*
+ * lanstreamer.h -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ *
+ */
+
+#define BCTL 0x60
+#define BCTL_SOFTRESET (1<<15)
+
+#define GPR 0x4a
+#define GPR_AUTOSENSE (1<<2)
+#define GPR_16MBPS (1<<3)
+
+#define LISR 0x10
+#define LISR_SUM 0x12
+#define LISR_RUM 0x14
+
+#define LISR_LIE (1<<15)
+#define LISR_SLIM (1<<13)
+#define LISR_SLI (1<<12)
+#define LISR_BPEI (1<<9)
+#define LISR_BPE (1<<8)
+#define LISR_SRB_CMD (1<<5)
+#define LISR_ASB_REPLY (1<<4)
+#define LISR_ASB_FREE_REQ (1<<2)
+#define LISR_ARB_FREE (1<<1)
+#define LISR_TRB_FRAME (1<<0)
+
+#define SISR 0x16
+#define SISR_SUM 0x18
+#define SISR_RUM 0x1A
+#define SISR_MASK 0x54
+#define SISR_MASK_SUM 0x56
+#define SISR_MASK_RUM 0x58
+
+#define SISR_MI (1<<15)
+#define SISR_TIMER (1<<11)
+#define SISR_LAP_PAR_ERR (1<<10)
+#define SISR_LAP_ACC_ERR (1<<9)
+#define SISR_PAR_ERR (1<<8)
+#define SISR_ADAPTER_CHECK (1<<6)
+#define SISR_SRB_REPLY (1<<5)
+#define SISR_ASB_FREE (1<<4)
+#define SISR_ARB_CMD (1<<3)
+#define SISR_TRB_REPLY (1<<2)
+
+#define MISR_RUM 0x5A
+#define MISR_MASK 0x5C
+#define MISR_MASK_RUM 0x5E
+
+#define MISR_TX2_IDLE (1<<15)
+#define MISR_TX2_NO_STATUS (1<<14)
+#define MISR_TX2_HALT (1<<13)
+#define MISR_TX2_EOF (1<<12)
+#define MISR_TX1_IDLE (1<<11)
+#define MISR_TX1_NO_STATUS (1<<10)
+#define MISR_TX1_HALT (1<<9)
+#define MISR_TX1_EOF (1<<8)
+#define MISR_RX_NOBUF (1<<5)
+#define MISR_RX_EOB (1<<4)
+#define MISR_RX_NO_STATUS (1<<2)
+#define MISR_RX_HALT (1<<1)
+#define MISR_RX_EOF (1<<0)
+
+#define LAPA 0x62
+#define LAPE 0x64
+#define LAPD 0x66
+#define LAPDINC 0x68
+#define LAPWWO 0x6A
+#define LAPWWC 0x6C
+#define LAPCTL 0x6E
+
+#define TIMER 0x4E4
+
+#define BMCTL_SUM 0x50
+#define BMCTL_RUM 0x52
+#define BMCTL_TX1_DIS (1<<14)
+#define BMCTL_TX2_DIS (1<<10)
+#define BMCTL_RX_DIS (1<<6)
+
+#define RXLBDA 0x90
+#define RXBDA 0x94
+#define RXSTAT 0x98
+#define RXDBA 0x9C
+
+#define TX1LFDA 0xA0
+#define TX1FDA 0xA4
+#define TX1STAT 0xA8
+#define TX1DBA 0xAC
+#define TX2LFDA 0xB0
+#define TX2FDA 0xB4
+#define TX2STAT 0xB8
+#define TX2DBA 0xBC
+
+#define STREAMER_IO_SPACE 256
+
+#define SRB_COMMAND_SIZE 50
+
+#define STREAMER_MAX_ADAPTERS 8 /* 0x08 __MODULE_STRING can't hand 0xnn */
+
+/* Defines for LAN STATUS CHANGE reports */
+#define LSC_SIG_LOSS 0x8000
+#define LSC_HARD_ERR 0x4000
+#define LSC_SOFT_ERR 0x2000
+#define LSC_TRAN_BCN 0x1000
+#define LSC_LWF 0x0800
+#define LSC_ARW 0x0400
+#define LSC_FPE 0x0200
+#define LSC_RR 0x0100
+#define LSC_CO 0x0080
+#define LSC_SS 0x0040
+#define LSC_RING_REC 0x0020
+#define LSC_SR_CO 0x0010
+#define LSC_FDX_MODE 0x0004
+
+/* Defines for OPEN ADAPTER command */
+
+#define OPEN_ADAPTER_EXT_WRAP (1<<15)
+#define OPEN_ADAPTER_DIS_HARDEE (1<<14)
+#define OPEN_ADAPTER_DIS_SOFTERR (1<<13)
+#define OPEN_ADAPTER_PASS_ADC_MAC (1<<12)
+#define OPEN_ADAPTER_PASS_ATT_MAC (1<<11)
+#define OPEN_ADAPTER_ENABLE_EC (1<<10)
+#define OPEN_ADAPTER_CONTENDER (1<<8)
+#define OPEN_ADAPTER_PASS_BEACON (1<<7)
+#define OPEN_ADAPTER_ENABLE_FDX (1<<6)
+#define OPEN_ADAPTER_ENABLE_RPL (1<<5)
+#define OPEN_ADAPTER_INHIBIT_ETR (1<<4)
+#define OPEN_ADAPTER_INTERNAL_WRAP (1<<3)
+
+
+/* Defines for SRB Commands */
+#define SRB_CLOSE_ADAPTER 0x04
+#define SRB_CONFIGURE_BRIDGE 0x0c
+#define SRB_CONFIGURE_HP_CHANNEL 0x13
+#define SRB_MODIFY_BRIDGE_PARMS 0x15
+#define SRB_MODIFY_OPEN_OPTIONS 0x01
+#define SRB_MODIFY_RECEIVE_OPTIONS 0x17
+#define SRB_NO_OPERATION 0x00
+#define SRB_OPEN_ADAPTER 0x03
+#define SRB_READ_LOG 0x08
+#define SRB_READ_SR_COUNTERS 0x16
+#define SRB_RESET_GROUP_ADDRESS 0x02
+#define SRB_RESET_TARGET_SEGMETN 0x14
+#define SRB_SAVE_CONFIGURATION 0x1b
+#define SRB_SET_BRIDGE_PARMS 0x09
+#define SRB_SET_FUNC_ADDRESS 0x07
+#define SRB_SET_GROUP_ADDRESS 0x06
+#define SRB_SET_TARGET_SEGMENT 0x05
+
+/* Clear return code */
+#define STREAMER_CLEAR_RET_CODE 0xfe
+
+/* ARB Commands */
+#define ARB_RECEIVE_DATA 0x81
+#define ARB_LAN_CHANGE_STATUS 0x84
+
+/* ASB Response commands */
+#define ASB_RECEIVE_DATA 0x81
+
+
+/* Streamer defaults for buffers */
+
+#define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */
+#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */
+
+#define PKT_BUF_SZ 4096 /* Default packet size */
+
+/* Streamer data structures */
+
+struct streamer_tx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 bufcnt_framelen;
+ __u32 buffer;
+ __u32 buflen;
+ __u32 rsvd1;
+ __u32 rsvd2;
+ __u32 rsvd3;
+};
+
+struct streamer_rx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 buffer;
+ __u32 framelen_buflen;
+};
+
+struct mac_receive_buffer {
+ __u16 next;
+ __u8 padding;
+ __u8 frame_status;
+ __u16 buffer_length;
+ __u8 frame_data;
+};
+
+struct streamer_private {
+
+ __u16 srb;
+ __u16 trb;
+ __u16 arb;
+ __u16 asb;
+
+ __u8 *streamer_mmio;
+
+ volatile int srb_queued; /* True if an SRB is still posted */
+ wait_queue_head_t srb_wait;
+
+ volatile int asb_queued; /* True if an ASB is posted */
+
+ volatile int trb_queued; /* True if a TRB is posted */
+ wait_queue_head_t trb_wait;
+
+ struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE];
+ struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE];
+ struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE],
+ *rx_ring_skb[STREAMER_RX_RING_SIZE];
+ int tx_ring_free, tx_ring_last_status, rx_ring_last_received,
+ free_tx_ring_entries;
+
+ struct net_device_stats streamer_stats;
+ __u16 streamer_lan_status;
+ __u8 streamer_ring_speed;
+ __u16 pkt_buf_sz;
+ __u8 streamer_receive_options, streamer_copy_all_options,
+ streamer_message_level;
+ __u8 streamer_multicast_set;
+ __u16 streamer_addr_table_addr, streamer_parms_addr;
+ __u16 mac_rx_buffer;
+ __u8 streamer_laa[6];
+};
+
+struct streamer_adapter_addr_table {
+
+ __u8 node_addr[6];
+ __u8 reserved[4];
+ __u8 func_addr[4];
+};
+
+struct streamer_parameters_table {
+
+ __u8 phys_addr[4];
+ __u8 up_node_addr[6];
+ __u8 up_phys_addr[4];
+ __u8 poll_addr[6];
+ __u16 reserved;
+ __u16 acc_priority;
+ __u16 auth_source_class;
+ __u16 att_code;
+ __u8 source_addr[6];
+ __u16 beacon_type;
+ __u16 major_vector;
+ __u16 lan_status;
+ __u16 soft_error_time;
+ __u16 reserved1;
+ __u16 local_ring;
+ __u16 mon_error;
+ __u16 beacon_transmit;
+ __u16 beacon_receive;
+ __u16 frame_correl;
+ __u8 beacon_naun[6];
+ __u32 reserved2;
+ __u8 beacon_phys[4];
+};
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index bbca052e7..19615012f 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1105,7 +1105,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in
index bc4bd5331..e0a6f5e8f 100644
--- a/drivers/net/wan/Config.in
+++ b/drivers/net/wan/Config.in
@@ -16,6 +16,25 @@ if [ "$CONFIG_WAN" = "y" ]; then
dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
+ #
+ # COMX drivers
+ #
+
+ tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+ if [ "$CONFIG_COMX" != "n" ]; then
+ dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+ dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+ dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+ dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+ if [ "$CONFIG_LAPB" = "y" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+ fi
+ if [ "$CONFIG_LAPB" = "m" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+ fi
+ dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+ fi
+
# There is no way to detect a Sealevel board. Force it modular
dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 67f6bb59b..b2cd8aafb 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -49,6 +49,58 @@ else
endif
endif
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else
+ ifeq ($(CONFIG_COMX_HW_COMX),m)
+ M_OBJS += comx-hw-comx.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else
+ ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+ M_OBJS += comx-hw-locomx.o
+ CONFIG_85230_MODULE=y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else
+ ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+ M_OBJS += comx-hw-mixcom.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else
+ ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+ M_OBJS += comx-proto-ppp.o
+ CONFIG_SYNCPPP_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+ M_OBJS += comx-proto-lapb.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_FR),m)
+ M_OBJS += comx-proto-fr.o
+ endif
+endif
+
ifeq ($(CONFIG_COSA),y)
L_OBJS += cosa.o
CONFIG_SYNCPPP_BUILTIN = y
@@ -180,8 +232,8 @@ clean:
rm -f core *.o *.a *.s
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $(WANPIPE_OBJS)
+ $(LD) -r -o $@ $(WANPIPE_OBJS)
cyclomx.o: $(CYCLOMX_OBJS)
- ld -r -o $@ $(CYCLOMX_OBJS)
+ $(LD) -r -o $@ $(CYCLOMX_OBJS)
diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c
new file mode 100644
index 000000000..7381dc8a9
--- /dev/null
+++ b/drivers/net/wan/comx-hw-comx.c
@@ -0,0 +1,1426 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - port back to kernel, add support builtin driver
+ * - cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ * - cleaned up the board load functions, no more long reset
+ * timeouts
+ * - lower modem lines on close
+ * - some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ * - fix multiple board support
+ *
+ * Version 0.83 (99/11/30):
+ * - interrupt handling and locking fixes during initalization
+ * - really fix multiple board support
+ *
+ * Version 0.84 (99/12/02):
+ * - some workarounds for problematic hardware/firmware
+ *
+ * Version 0.85 (00/01/14):
+ * - some additional workarounds :/
+ * - printk cleanups
+ */
+
+#define VERSION "0.85"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
+ (unsigned int)(((struct comx_privdata *)\
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
+ + (unsigned int)(((struct comx_privdata *) \
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+ int len;
+ unsigned char *data;
+};
+
+struct comx_privdata {
+ struct comx_firmware *firmware;
+ u16 clock;
+ char channel; // channel no.
+ int memory_size;
+ short io_extent;
+ u_long histogram[5];
+};
+
+static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct net_device *COMX_access_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct net_device *ret;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ ret = memory_used[mempos];
+
+ if(ret == dev) {
+ goto out;
+ }
+
+ memory_used[mempos] = dev;
+
+ if (!ch->twin || ret != ch->twin) {
+ if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
+ ch->HW_board_on(dev);
+ }
+out:
+ restore_flags(flags);
+ return ret;
+}
+
+static void COMX_release_board(struct net_device *dev, struct net_device *savep)
+{
+ unsigned long flags;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct comx_channel *ch = dev->priv;
+
+ save_flags(flags); cli();
+
+ if (memory_used[mempos] == savep) {
+ goto out;
+ }
+
+ memory_used[mempos] = savep;
+ if (!ch->twin || ch->twin != savep) {
+ ch->HW_board_off(dev);
+ if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
+ }
+out:
+ restore_flags(flags);
+}
+
+static int COMX_txe(struct net_device *dev)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ int rc = 0;
+
+ savep = ch->HW_access_board(dev);
+ if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+ rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+ }
+ ch->HW_release_board(dev,savep);
+ if(rc==0xffff) {
+ printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
+ }
+ return rc;
+}
+
+static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int ret = FRAME_DROPPED;
+ word tmp;
+
+ savep = ch->HW_access_board(dev);
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+ }
+
+ if (skb->len > COMX_MAX_TX_SIZE) {
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if ((ch->line_status & LINE_UP) && tmp==1) {
+ int lensave = skb->len;
+ int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+ word *data = (word *)skb->data;
+
+ if(dest==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ writew((unsigned short)skb->len, dev->mem_start + dest);
+ dest += 2;
+ while (skb->len > 1) {
+ writew(*data++, dev->mem_start + dest);
+ dest += 2; skb->len -= 2;
+ }
+ if (skb->len == 1) {
+ writew(*((byte *)data), dev->mem_start + dest);
+ }
+ writew(0, dev->mem_start + (int)hw->channel *
+ COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += lensave;
+ ret = FRAME_ACCEPTED;
+ } else {
+ ch->stats.tx_dropped++;
+ printk(KERN_INFO "%s: frame dropped\n",dev->name);
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
+ }
+ }
+
+out:
+ ch->HW_release_board(dev, savep);
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static inline int comx_read_buffer(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ word rbuf_offs;
+ struct sk_buff *skb;
+ word len;
+ int i=0;
+ word *writeptr;
+
+ i = 0;
+ rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+ if(rbuf_offs == 0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
+ return 0;
+ }
+ len = readw(dev->mem_start + rbuf_offs);
+ if(len > COMX_MAX_RX_SIZE) {
+ printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
+ return 0;
+ }
+ if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+ ch->stats.rx_dropped++;
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ return 0;
+ }
+ rbuf_offs += 2;
+ skb_reserve(skb, 16);
+ skb_put(skb, len);
+ skb->dev = dev;
+ writeptr = (word *)skb->data;
+ while (i < len) {
+ *writeptr++ = readw(dev->mem_start + rbuf_offs);
+ rbuf_offs += 2;
+ i += 2;
+ }
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += len;
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+ }
+ ch->LINE_rx(dev, skb);
+ return 1;
+}
+
+static inline char comx_line_change(struct net_device *dev, char linestat)
+{
+ struct comx_channel *ch=dev->priv;
+ char idle=1;
+
+
+ if (linestat & LINE_UP) { /* Vonal fol */
+ if (ch->lineup_delay) {
+ if (!test_and_set_bit(0, &ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies +
+ HZ*ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ idle=0;
+ }
+ } else {
+ idle=0;
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+ } else { /* Vonal le */
+ idle=0;
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+ }
+ }
+ return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *interrupted;
+ unsigned long jiffs;
+ char idle = 0;
+ int count = 0;
+ word tmp;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+ return;
+ }
+
+ jiffs = jiffies;
+
+ interrupted = ch->HW_access_board(dev);
+
+ while (!idle && count < 5000) {
+ char channel = 0;
+ idle = 1;
+
+ while (channel < 2) {
+ char linestat = 0;
+ char buffers_emptied = 0;
+
+ if (channel == 1) {
+ if (ch->twin) {
+ dev = ch->twin;
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+ } else {
+ break;
+ }
+ } else {
+ COMX_WRITE(dev, OFF_A_L1_REPENA,
+ COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+ }
+ channel++;
+
+ if ((ch->init_status & (HW_OPEN | LINE_OPEN)) !=
+ (HW_OPEN | LINE_OPEN)) {
+ continue;
+ }
+
+ /* Collect stats */
+ tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+ COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_over_errors += tmp & 0xff;
+ }
+ tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+ COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_missed_errors += tmp & 0xff;
+ }
+
+ if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ while (tmp==1) {
+ idle=0;
+ buffers_emptied+=comx_read_buffer(dev);
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ }
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
+ break;
+ }
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if (tmp==1 && ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
+ break;
+ }
+
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ linestat &= ~LINE_UP;
+ } else {
+ linestat |= LINE_UP;
+ }
+
+ if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+ ch->stats.tx_carrier_errors++;
+ idle &= comx_line_change(dev,linestat);
+ }
+
+ hw->histogram[(int)buffers_emptied]++;
+ }
+ count++;
+ }
+
+ if(count==5000) {
+ printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+ }
+
+ ch->HW_release_board(dev, interrupted);
+}
+
+static int COMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long jiffs;
+ int twin_open=0;
+ int retval;
+ struct net_device *savep;
+
+ if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+ return -ENODEV;
+ }
+
+ if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
+ twin_open=1;
+ }
+
+ if (!twin_open) {
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+ if (request_irq(dev->irq, COMX_interrupt, 0, dev->name,
+ (void *)dev)) {
+ printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status |= IRQ_ALLOCATED;
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+ if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+ ch->init_status &= ~IRQ_ALLOCATED;
+ retval=-ENODEV;
+ goto error;
+ }
+ }
+
+ savep = ch->HW_access_board(dev);
+ COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+ if (ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+
+ COMX_CMD(dev, COMX_CMD_INIT);
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+ udelay(1000);
+
+ COMX_CMD(dev, COMX_CMD_OPEN);
+
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+
+ ch->init_status |= HW_OPEN;
+
+ /* Ez eleg ciki, de ilyen a rendszer */
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ ch->line_status &= ~LINE_UP;
+ } else {
+ ch->line_status |= LINE_UP;
+ }
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+
+ ch->HW_release_board(dev, savep);
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0444;
+
+ }
+ }
+
+ return 0;
+
+error:
+ if(!twin_open) {
+ release_region(dev->base_addr, hw->io_extent);
+ free_irq(dev->irq, (void *)dev);
+ }
+ return retval;
+
+}
+
+static int COMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_channel *twin_ch;
+ struct net_device *savep;
+
+ savep = ch->HW_access_board(dev);
+
+ COMX_CMD(dev, COMX_CMD_CLOSE);
+ udelay(1000);
+ COMX_CMD(dev, COMX_CMD_EXIT);
+
+ ch->HW_release_board(dev, savep);
+
+ if (ch->init_status & IRQ_ALLOCATED) {
+ free_irq(dev->irq, (void *)dev);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+ release_region(dev->base_addr, hw->io_extent);
+
+ if (ch->twin && (twin_ch = ch->twin->priv) &&
+ (twin_ch->init_status & HW_OPEN)) {
+ /* Pass the irq to the twin */
+ if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name,
+ (void *)ch->twin) == 0) {
+ twin_ch->init_status |= IRQ_ALLOCATED;
+ }
+ }
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int COMX_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *savep;
+ int len = 0;
+
+ savep = ch->HW_access_board(dev);
+
+ len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+ "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+ "TxEMPTY: %02x, TxBUFP: %02x\n",
+ (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+ (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+ (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+ (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+ COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+ (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+ COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+ COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+ len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+ "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+ hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+ ch->HW_release_board(dev, savep);
+
+ return len;
+}
+
+static int COMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+ unsigned char id1, id2;
+ struct net_device *saved;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n",
+ dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+ /* 10 usec should be enough here */
+ udelay(100);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount=0;
+ while(loopcount++ < 10000 &&
+ readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ udelay(100);
+ }
+
+ if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+ dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+ retval=-ENODEV;
+ goto out;
+ }
+
+ writeb(0x55, dev->mem_start + 0x18ff);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
+ udelay(100);
+ }
+
+ if(readb(dev->mem_start + 0x18ff) != 0) {
+ printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+ dev->name);
+ retval=-ENODEV;
+ goto out;
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount = 0;
+ while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int CMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ #if 0
+ unsigned char id1, id2;
+ #endif
+ struct net_device *saved;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+ if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+ printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+ inb_p(dev->base_addr));
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET,
+ dev->base_addr);
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ loopcount=0;
+ while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int HICOMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 12;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct net_device *saved;
+ unsigned char id1, id2;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ word *HICOMX_address;
+ char id = 1;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ while (id != 4) {
+ if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+ break;
+ }
+ }
+
+ if (id != 4) {
+ printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+ dev->name, (unsigned int)dev->base_addr, id - 1,
+ inb_p(dev->base_addr + id - 1));
+ return -1;
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ udelay(10);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+ outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], HICOMX_address++);
+ }
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ udelay(100);
+ }
+
+ if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static struct net_device *comx_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct comx_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+
+ if(!S_ISDIR(procfile->mode)) {
+ continue;
+ }
+
+ twin=procfile->data;
+ ch_twin=twin->priv;
+ hw_twin=ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+ dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+ dev->mem_start == twin->mem_start &&
+ hw->channel == (1 - hw_twin->channel) &&
+ ch->hardware == ch_twin->hardware) {
+ return twin;
+ }
+ }
+ return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ char *page;
+
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if(ch->init_status & HW_OPEN) {
+ return -EAGAIN;
+ }
+
+ if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+ } else {
+ byte *tmp;
+
+ if (!hw->firmware) {
+ if ((hw->firmware = kmalloc(sizeof(struct comx_firmware),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ hw->firmware->len = 0;
+ hw->firmware->data = NULL;
+ }
+
+ if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+ if (hw->firmware && hw->firmware->len && file->f_pos
+ && hw->firmware->len < count + file->f_pos) {
+ memcpy(tmp, hw->firmware->data, hw->firmware->len);
+ }
+ if (hw->firmware->data) {
+ kfree(hw->firmware->data);
+ }
+ copy_from_user(tmp + file->f_pos, buffer, count);
+ hw->firmware->len = entry->size = file->f_pos + count;
+ hw->firmware->data = tmp;
+ file->f_pos += count;
+ return count;
+ }
+
+ if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ hw->channel = simple_strtoul(page, NULL, 0);
+ if (hw->channel >= MAX_CHANNELNO) {
+ printk(KERN_ERR "Invalid channel number\n");
+ hw->channel = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ dev->irq = simple_strtoul(page, NULL, 0);
+ if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+ if (dev->irq < 3 || dev->irq > 15) {
+ printk(KERN_ERR "comxhw: Invalid irq number\n");
+ dev->irq = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IO) == 0) {
+ dev->base_addr = simple_strtoul(page, NULL, 0);
+ if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300
+ || dev->base_addr > 0x3fc) {
+ printk(KERN_ERR "Invalid io value\n");
+ dev->base_addr = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+ dev->mem_start = simple_strtoul(page, NULL, 0);
+ if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+ dev->mem_start *= 16;
+ }
+ if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+ || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+ printk(KERN_ERR "Invalid memory page\n");
+ dev->mem_start = 0;
+ }
+ dev->mem_end = dev->mem_start + hw->memory_size;
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+ len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) {
+ len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+ } else {
+ len = sprintf(page, "external\n");
+ }
+ } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+ len = min(FILE_PAGESIZE, min(count,
+ hw->firmware ? (hw->firmware->len - off) : 0));
+ if (len < 0) {
+ len = 0;
+ }
+ *start = hw->firmware ? (hw->firmware->data + off) : NULL;
+ if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+ *eof = 1;
+ }
+ return len;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+ if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+ hw->memory_size = COMX_MEMORY_SIZE;
+ hw->io_extent = COMX_IO_EXTENT;
+ dev->base_addr = COMX_DEFAULT_IO;
+ dev->irq = COMX_DEFAULT_IRQ;
+ dev->mem_start = COMX_DEFAULT_MEMADDR;
+ dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+ } else if (ch->hardware == &hicomx_hw) {
+ hw->memory_size = HICOMX_MEMORY_SIZE;
+ hw->io_extent = HICOMX_IO_EXTENT;
+ dev->base_addr = HICOMX_DEFAULT_IO;
+ dev->irq = HICOMX_DEFAULT_IRQ;
+ dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+ dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 6;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 2; // Ezt tudjuk
+ new_file->nlink = 1;
+
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 9;
+ new_file->nlink = 1;
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 8;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = NULL;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if (ch->hardware == &comx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = COMX_load_board;
+ } else if (ch->hardware == &cmx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = CMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else if (ch->hardware == &hicomx_hw) {
+ ch->HW_board_on = HICOMX_board_on;
+ ch->HW_board_off = HICOMX_board_off;
+ ch->HW_load_board = HICOMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ ch->HW_access_board = COMX_access_board;
+ ch->HW_release_board = COMX_release_board;
+ ch->HW_txe = COMX_txe;
+ ch->HW_open = COMX_open;
+ ch->HW_close = COMX_close;
+ ch->HW_send_packet = COMX_send_packet;
+ ch->HW_statistics = COMX_statistics;
+
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ if (hw->firmware) {
+ if (hw->firmware->data) kfree(hw->firmware->data);
+ kfree(hw->firmware);
+ } if (ch->twin) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+ remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+ }
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int COMX_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+ return 0;
+}
+
+static struct comx_hardware comx_hw = {
+ "comx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware cmx_hw = {
+ "cmx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+ "hicomx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+int __init comx_hw_comx_init(void)
+{
+ comx_register_hardware(&comx_hw);
+ comx_register_hardware(&cmx_hw);
+ comx_register_hardware(&hicomx_hw);
+ memset(memory_used, 0, sizeof(memory_used));
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("comx");
+ comx_unregister_hardware("cmx");
+ comx_unregister_hardware("hicomx");
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c
new file mode 100644
index 000000000..010f02373
--- /dev/null
+++ b/drivers/net/wan/comx-hw-locomx.c
@@ -0,0 +1,496 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu>
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.10 (99/06/17):
+ * - rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ * - some printk's fixed
+ * - get rid of a memory leak (it was impossible though :))
+ *
+ * Version 0.12 (99/07/07):
+ * - check CTS for modem lines, not DCD (which is always high
+ * in case of this board)
+ * Version 0.13 (99/07/08):
+ * - Fix the transmitter status check
+ * - Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+ 11, TCRTxCP,
+ 14, DTRREQ,
+ 255
+};
+
+struct locomx_data {
+ int io_extent;
+ struct z8530_dev board;
+ struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+ struct net_device *dev=c->netdevice;
+ struct comx_channel *ch=dev->priv;
+
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "locomx_rx receiving");
+ }
+ ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+ printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+ return FRAME_DROPPED;
+ }
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+ }
+
+ if(!hw->board.chanA.tx_next_skb) {
+ return FRAME_QUEUED;
+ } else {
+ return FRAME_ACCEPTED;
+ }
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct locomx_data *hw=ch->HW_privdata;
+
+ if(!(ch->line_status & LINE_UP) &&
+ (hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status | LINE_UP);
+ }
+ if((ch->line_status & LINE_UP) &&
+ !(hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+ }
+ mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+ int ret;
+
+ if (!dev->base_addr || !dev->irq) {
+ return -ENODEV;
+ }
+
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+
+ hw->board.chanA.ctrlio=dev->base_addr + 5;
+ hw->board.chanA.dataio=dev->base_addr + 7;
+
+ hw->board.irq=dev->irq;
+ hw->board.chanA.netdevice=dev;
+ hw->board.chanA.dev=&hw->board;
+ hw->board.name=dev->name;
+ hw->board.chanA.txdma=TX_DMA;
+ hw->board.chanA.rxdma=RX_DMA;
+ hw->board.chanA.irqs=&z8530_nop;
+ hw->board.chanB.irqs=&z8530_nop;
+
+ if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT,
+ dev->name, &hw->board)) {
+ printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name,
+ dev->irq);
+ ret=-EAGAIN;
+ goto irq_fail;
+ }
+ if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+ printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n",
+ dev->name, TX_DMA);
+ ret=-EAGAIN;
+ goto dma1_fail;
+ }
+
+ if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+ printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n",
+ dev->name, RX_DMA);
+ ret=-EAGAIN;
+ goto dma2_fail;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if(z8530_init(&hw->board)!=0)
+ {
+ printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+ ret=-ENODEV;
+ goto z8530_fail;
+ }
+
+ hw->board.chanA.dcdcheck=CTS;
+
+ z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+ z8530_channel_load(&hw->board.chanA, z8530_locomx);
+ z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+ z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+ if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+ goto z8530_fail;
+ }
+
+ restore_flags(flags);
+
+
+ hw->board.active=1;
+ hw->board.chanA.rx_function=locomx_rx;
+
+ ch->init_status |= HW_OPEN;
+ if (hw->board.chanA.status & DCD) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ comx_status(dev, ch->line_status);
+
+ init_timer(&hw->status_timer);
+ hw->status_timer.function=locomx_status_timerfun;
+ hw->status_timer.data=(unsigned long)dev;
+ hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+ add_timer(&hw->status_timer);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+ return 0;
+
+z8530_fail:
+ restore_flags(flags);
+ free_dma(RX_DMA);
+dma2_fail:
+ free_dma(TX_DMA);
+dma1_fail:
+ free_irq(dev->irq, &hw->board);
+irq_fail:
+ release_region(dev->base_addr, hw->io_extent);
+ return ret;
+}
+
+static int LOCOMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+ hw->board.chanA.rx_function=z8530_null_rx;
+ netif_stop_queue(dev);
+ z8530_sync_dma_close(dev, &hw->board.chanA);
+
+ z8530_shutdown(&hw->board);
+
+ del_timer(&hw->status_timer);
+ free_dma(RX_DMA);
+ free_dma(TX_DMA);
+ free_irq(dev->irq,&hw->board);
+ release_region(dev->base_addr,8);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int LOCOMX_statistics(struct net_device *dev,char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, "Hello\n");
+
+ return len;
+}
+
+static int LOCOMX_dump(struct net_device *dev) {
+ printk(KERN_INFO "LOCOMX_dump called\n");
+ return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ int val;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 0x360 && val != 0x368 && val != 0x370 &&
+ val != 0x378) {
+ printk(KERN_ERR "LoCOMX: incorrect io address!\n");
+ } else {
+ dev->base_addr = val;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+ printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+ } else {
+ dev->irq = val;
+ }
+ } else {
+ printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n",
+ entry->name);
+ free_page((unsigned long)page);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+
+
+static int LOCOMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw;
+ struct proc_dir_entry *new_file;
+
+ /* Alloc data for private structure */
+ if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+ hw->io_extent = LOCOMX_IO_EXTENT;
+
+ /* Register /proc files */
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+/* No clock yet */
+/*
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+*/
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = LOCOMX_txe;
+ ch->HW_open = LOCOMX_open;
+ ch->HW_close = LOCOMX_close;
+ ch->HW_send_packet = LOCOMX_send_packet;
+ ch->HW_statistics = LOCOMX_statistics;
+ ch->HW_set_clock = NULL;
+
+ ch->current_stats = &hw->board.chanA.stats;
+ memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+ dev->base_addr = LOCOMX_DEFAULT_IO;
+ dev->irq = LOCOMX_DEFAULT_IRQ;
+
+
+ /* O.K. Count one more user on this module */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int LOCOMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_send_packet = NULL;
+ ch->HW_statistics = NULL;
+ ch->HW_set_clock = NULL;
+ memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+ ch->current_stats = &ch->stats;
+
+ kfree(ch->HW_privdata);
+
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+// remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+ "locomx",
+ VERSION,
+ LOCOMX_init,
+ LOCOMX_exit,
+ LOCOMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+int __init comx_hw_locomx_init(void)
+{
+ comx_register_hardware(&locomx_hw);
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("locomx");
+ return;
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c
new file mode 100644
index 000000000..552443f88
--- /dev/null
+++ b/drivers/net/wan/comx-hw-mixcom.c
@@ -0,0 +1,948 @@
+/*
+ * Hardware driver for the MixCom synchronous serial board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.60 (99/06/11):
+ * - ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ * - recognize the one-channel MixCOM card (id byte = 0x13)
+ * - printk fixes
+ *
+ * Version 0.62 (99/07/15):
+ * - fixes according to the new hw docs
+ * - report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ * - line status report fixes
+ *
+ * Version 0.64 (99/12/01):
+ * - some more cosmetical fixes
+ */
+
+#define VERSION "0.64"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+ HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+ (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+ (1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+ u16 clock;
+ char channel;
+ char txbusy;
+ struct sk_buff *sending;
+ unsigned tx_ptr;
+ struct sk_buff *recving;
+ unsigned rx_ptr;
+ unsigned char status;
+ char card_has_status;
+};
+
+static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct net_device *dev, int cmd)
+{
+ unsigned long jiffs = jiffies;
+ unsigned char cec;
+ unsigned delay = 0;
+
+ while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) &&
+ (jiffs + HZ > jiffies)) {
+ udelay(1);
+ if (++delay > (100000 / HZ)) break;
+ }
+ if (cec) {
+ printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+ } else {
+ wr_hscx(dev, HSCX_CMDR, cmd);
+ }
+}
+
+static inline void hscx_fill_fifo(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+ outsb(dev->base_addr + HSCX_FIFO,
+ &(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+ if (to_send <= 32) {
+ hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ } else {
+ hscx_cmd(dev, HSCX_XTF);
+ hw->tx_ptr += 32;
+ }
+}
+
+static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->recving == NULL) {
+ if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+ ch->stats.rx_dropped++;
+ hscx_cmd(dev, HSCX_RHR);
+ } else {
+ skb_reserve(hw->recving, 16);
+ skb_put(hw->recving, HSCX_MTU);
+ }
+ hw->rx_ptr = 0;
+ }
+ if (cnt > 32 || !cnt || hw->recving == NULL) {
+ printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+ cnt, (void *)hw->recving);
+ return;
+ }
+
+ insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+ hw->rx_ptr += cnt;
+ hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct net_device *dev)
+{
+ unsigned long flags;
+ int id, vstr, ret=0;
+
+ save_flags(flags); cli();
+
+ id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
+
+ if (id != MIXCOM_ID ) {
+ ret=-ENODEV;
+ printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+ goto out;
+ }
+
+ vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+ if(vstr>=sizeof(hscx_versions)/sizeof(char*) ||
+ hscx_versions[vstr]==NULL) {
+ printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+ ret = -ENODEV;
+ } else {
+ printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+ ret = 0;
+ }
+
+out:
+
+ restore_flags(flags);
+ return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->clock) {
+ ;
+ } else {
+ ;
+ }
+}
+#endif
+
+static void mixcom_board_on(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+ outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON,
+ MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_board_off(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_off(struct net_device *dev)
+{
+ wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+ wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+ wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+ wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+ wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+ hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+ if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ unsigned long flags;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if (skb->len > HSCX_MTU) {
+ ch->stats.tx_errors++;
+ return FRAME_ERROR;
+ }
+
+ save_flags(flags); cli();
+
+ if (test_and_set_bit(0, &hw->txbusy)) {
+ printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+ restore_flags(flags);
+ return FRAME_DROPPED;
+ }
+
+
+ hw->sending = skb;
+ hw->tx_ptr = 0;
+ hw->txbusy = 1;
+// atomic_inc(&skb->users); // save it
+ hscx_fill_fifo(dev);
+ restore_flags(flags);
+
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte rsta;
+ register word length;
+
+ rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO |
+ HSCX_CRC | HSCX_RAB);
+ length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) |
+ rd_hscx(dev, HSCX_RBCL);
+
+ if ( length > hw->rx_ptr ) {
+ hscx_empty_fifo(dev, length - hw->rx_ptr);
+ }
+
+ if (!(rsta & HSCX_VFR)) {
+ ch->stats.rx_length_errors++;
+ }
+ if (rsta & HSCX_RDO) {
+ ch->stats.rx_over_errors++;
+ }
+ if (!(rsta & HSCX_CRC)) {
+ ch->stats.rx_crc_errors++;
+ }
+ if (rsta & HSCX_RAB) {
+ ch->stats.rx_frame_errors++;
+ }
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += length;
+
+ if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+ skb_trim(hw->recving, hw->rx_ptr - 1);
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, hw->recving,
+ "MIXCOM_interrupt receiving");
+ }
+ hw->recving->dev = dev;
+ if (ch->LINE_rx) {
+ ch->LINE_rx(dev, hw->recving);
+ }
+ }
+ else if(hw->recving) {
+ kfree_skb(hw->recving);
+ }
+ hw->recving = NULL;
+ hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte exir;
+
+ exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+ if (exir & HSCX_RFO) {
+ ch->stats.rx_over_errors++;
+ if (hw->rx_ptr) {
+ kfree_skb(hw->recving);
+ hw->recving = NULL; hw->rx_ptr = 0;
+ }
+ printk(KERN_ERR "MIXCOM: rx overrun\n");
+ hscx_cmd(dev, HSCX_RHR);
+ }
+
+ if (exir & HSCX_XDU) { // xmit underrun
+ ch->stats.tx_errors++;
+ ch->stats.tx_aborted_errors++;
+ if (hw->tx_ptr) {
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ }
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ printk(KERN_ERR "MIXCOM: tx underrun\n");
+ }
+
+ if (exir & HSCX_CSC) {
+ ch->stats.tx_carrier_errors++;
+ if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else if (ch->line_status & LINE_UP) {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev,ch->line_status);
+ }
+ }
+ }
+ if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) &
+ HSCX_CTS)) { // Vonal fol
+ if (!test_and_set_bit(0,&ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies + HZ *
+ ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (hw->sending) {
+ kfree_skb(hw->sending);
+ }
+ hw->sending=NULL;
+ hw->tx_ptr = 0;
+ }
+ }
+ }
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct comx_channel *ch, *twin_ch;
+ struct mixcom_privdata *hw, *twin_hw;
+ register unsigned char ista;
+
+ if (dev==NULL) {
+ printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+ return;
+ }
+
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+
+ save_flags(flags); cli();
+
+ while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF |
+ HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+ register byte ista2 = 0;
+
+ if (ista & HSCX_RME) {
+ mixcom_receive_frame(dev);
+ }
+ if (ista & HSCX_RPF) {
+ hscx_empty_fifo(dev, 32);
+ }
+ if (ista & HSCX_XPR) {
+ if (hw->tx_ptr) {
+ hscx_fill_fifo(dev);
+ } else {
+ clear_bit(0, &hw->txbusy);
+ ch->LINE_tx(dev);
+ }
+ }
+
+ if (ista & HSCX_EXB) {
+ mixcom_extended_interrupt(dev);
+ }
+
+ if ((ista & HSCX_EXA) && ch->twin) {
+ mixcom_extended_interrupt(ch->twin);
+ }
+
+ if ((ista & HSCX_ICA) && ch->twin &&
+ (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+ (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+ if (ista2 & HSCX_RME) {
+ mixcom_receive_frame(ch->twin);
+ }
+ if (ista2 & HSCX_RPF) {
+ hscx_empty_fifo(ch->twin, 32);
+ }
+ if (ista2 & HSCX_XPR) {
+ twin_ch=ch->twin->priv;
+ twin_hw=twin_ch->HW_privdata;
+ if (twin_hw->tx_ptr) {
+ hscx_fill_fifo(ch->twin);
+ } else {
+ clear_bit(0, &twin_hw->txbusy);
+ ch->LINE_tx(ch->twin);
+ }
+ }
+ }
+ }
+
+ restore_flags(flags);
+ return;
+}
+
+static int MIXCOM_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+ if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+ if(hw->channel==1) {
+ if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status &
+ IRQ_ALLOCATED)) {
+ printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+ return -EAGAIN;
+ }
+ }
+
+
+ /* Is our hw present at all ? Not checking for channel 0 if it is already
+ open */
+ if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+ if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+ return -EAGAIN;
+ }
+ if (mixcom_probe(dev)) {
+ return -ENODEV;
+ }
+ }
+
+ save_flags(flags); cli();
+
+ if(hw->channel==1) {
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ }
+
+ if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+ if (request_irq(dev->irq, MIXCOM_interrupt, 0,
+ dev->name, (void *)dev)) {
+ printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status|=IRQ_ALLOCATED;
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ mixcom_board_on(dev);
+ }
+
+ mixcom_on(dev);
+
+
+ hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+ if(hw->status != 0xff) {
+ printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+ hw->card_has_status=1;
+ }
+
+ hw->txbusy = 0;
+ ch->init_status |= HW_OPEN;
+
+ if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ restore_flags(flags);
+
+ ch->LINE_status(dev, ch->line_status);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int MIXCOM_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ mixcom_off(dev);
+
+ /* This is channel 0, twin is not open, we can safely turn off everything */
+ if(hw->channel==0 && (!(TWIN(dev)) ||
+ !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+ mixcom_board_off(dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+
+ /* This is channel 1, channel 0 has already been shutdown, we can release
+ this one too */
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+ mixcom_board_off(TWIN(dev));
+ free_irq(TWIN(dev)->irq, TWIN(dev));
+ release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+ COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+ }
+ }
+
+ /* the ioports for channel 1 can be safely released */
+ if(hw->channel==1) {
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ }
+
+ restore_flags(flags);
+
+ /* If we don't hold any hardware open */
+ if(!(ch->init_status & IRQ_ALLOCATED)) {
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ /* channel 0 was only waiting for us to close channel 1
+ close it completely */
+
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir;
+ procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int MIXCOM_statistics(struct net_device *dev,char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ // struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if(ch->init_status && IRQ_ALLOCATED) {
+ len += sprintf(page + len, "Mixcom board: hardware open\n");
+ }
+
+ return len;
+}
+
+static int MIXCOM_dump(struct net_device *dev) {
+ return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n",
+ (unsigned int)MIXCOM_BOARD_BASE(dev));
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+ else len = sprintf(page, "external\n");
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ if (ch->twin) {
+ len = sprintf(page, "%s\n",ch->twin->name);
+ } else {
+ len = sprintf(page, "none\n");
+ }
+ } else {
+ printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+
+static struct net_device *mixcom_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct mixcom_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if(!S_ISDIR(procfile->mode)) continue;
+
+ twin = procfile->data;
+ ch_twin = twin->priv;
+ hw_twin = ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr &&
+ dev->irq == twin->irq &&
+ ch->hardware == ch_twin->hardware &&
+ dev->base_addr == twin->base_addr +
+ (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+ hw->channel == (1 - hw_twin->channel)) {
+ if (!TWIN(twin) || TWIN(twin)==dev) {
+ return twin;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static void setup_twin(struct net_device* dev)
+{
+
+ if(TWIN(dev) && TWIN(TWIN(dev))) {
+ TWIN(TWIN(dev))=NULL;
+ }
+ if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+ if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+ TWIN(dev)=NULL;
+ } else {
+ TWIN(TWIN(dev))=dev;
+ }
+ }
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ char *page;
+ int value;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value != 0x180 && value != 0x280 && value != 0x380) {
+ printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+ } else {
+ dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+ printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+ } else {
+ dev->irq = value;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ if (!kbps) {
+ hw->clock = 0;
+ } else {
+ hw->clock = kbps;
+ }
+ if (hw->clock < 32 || hw->clock > 2000) {
+ hw->clock = 0;
+ printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+ }
+ }
+ if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+ } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value > 2) {
+ printk(KERN_ERR "Invalid channel number\n");
+ } else {
+ dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+ hw->channel = value;
+ }
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ setup_twin(dev);
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int MIXCOM_init(struct net_device *dev) {
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+#if 0
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+#endif
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ setup_twin(dev);
+
+ /* Fill in ch_struct hw specific pointers */
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = MIXCOM_txe;
+ ch->HW_open = MIXCOM_open;
+ ch->HW_close = MIXCOM_close;
+ ch->HW_send_packet = MIXCOM_send_packet;
+ ch->HW_statistics = MIXCOM_statistics;
+ ch->HW_set_clock = NULL;
+
+ dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+ dev->irq = MIXCOM_DEFAULT_IRQ;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int MIXCOM_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if(hw->channel==0 && TWIN(dev)) {
+ return -EBUSY;
+ }
+
+ if(hw->channel==1 && TWIN(dev)) {
+ TWIN(TWIN(dev))=NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+ "mixcom",
+ VERSION,
+ MIXCOM_init,
+ MIXCOM_exit,
+ MIXCOM_dump,
+ NULL
+};
+
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+int __init comx_hw_mixcom_init(void)
+{
+ return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ comx_unregister_hardware("mixcom");
+}
+#endif
diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c
new file mode 100644
index 000000000..ad62e310c
--- /dev/null
+++ b/drivers/net/wan/comx-proto-fr.c
@@ -0,0 +1,1006 @@
+/*
+ * Frame-relay protocol module for the COMX driver
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.70 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as builtin code
+ *
+ * Version 0.71 (99/06/25):
+ * - use skb priorities and queues for sending keepalive
+ * - use device queues for slave->master data transmit
+ * - set IFF_RUNNING only line protocol up
+ * - fixes on slave device flags
+ *
+ * Version 0.72 (99/07/09):
+ * - handle slave tbusy with master tbusy (should be fixed)
+ * - fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+ "for Linux kernel 2.2.X");
+
+#define FRAD_UI 0x03
+#define NLPID_IP 0xcc
+#define NLPID_Q933_LMI 0x08
+#define NLPID_CISCO_LMI 0x09
+#define Q933_ENQ 0x75
+#define Q933_LINESTAT 0x51
+#define Q933_COUNTERS 0x53
+
+#define MAXALIVECNT 3 /* No. of failures */
+
+struct fr_data {
+ u16 dlci;
+ struct net_device *master;
+ char keepa_pend;
+ char keepa_freq;
+ char keepalivecnt, keeploopcnt;
+ struct timer_list keepa_timer;
+ u8 local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct sk_buff *skb;
+ u8 *fr_packet;
+
+ skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+
+ if(skb==NULL)
+ return;
+
+ skb_reserve(skb, dev->hard_header_len);
+
+ fr_packet=(u8*)skb_put(skb, 13);
+
+ fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+ fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ fr_packet[2] = FRAD_UI;
+ fr_packet[3] = NLPID_Q933_LMI;
+ fr_packet[4] = 0;
+ fr_packet[5] = Q933_ENQ;
+ fr_packet[6] = Q933_LINESTAT;
+ fr_packet[7] = 0x01;
+ fr_packet[8] = 0x01;
+ fr_packet[9] = Q933_COUNTERS;
+ fr_packet[10] = 0x02;
+ fr_packet[11] = ++fr->local_cnt;
+ fr_packet[12] = fr->remote_cnt;
+
+ skb->dev = dev;
+ skb->priority = TC_PRIO_CONTROL;
+ dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (ch->init_status & LINE_OPEN) {
+ if (fr->keepalivecnt == MAXALIVECNT) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ dev->flags &= ~IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags &= ~IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status & ~PROTO_UP);
+ }
+ }
+ }
+ if (fr->keepalivecnt <= MAXALIVECNT) {
+ ++fr->keepalivecnt;
+ }
+ fr_keepalive_send(dev);
+ }
+ mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb,
+ u16 dlci, u8 nlpid)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+ return;
+ }
+
+ fr->remote_cnt = skb->data[7];
+ if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+ fr->keepalivecnt = 0;
+ if ((ch->line_status & LINE_UP) &&
+ !(ch->line_status & PROTO_UP)) {
+ comx_status(dev, ch->line_status |= PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags |= IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status | PROTO_UP);
+ }
+ }
+ }
+ }
+}
+
+static void fr_set_keepalive(struct net_device *dev, int keepa)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!keepa && fr->keepa_freq) { // switch off
+ fr->keepa_freq = 0;
+ if (ch->line_status & LINE_UP) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ del_timer(&fr->keepa_timer);
+ }
+ return;
+ }
+
+ if (keepa) { // bekapcs
+ if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+ del_timer(&fr->keepa_timer);
+ }
+ fr->keepa_freq = keepa;
+ fr->local_cnt = fr->remote_cnt = 0;
+ fr->keepa_timer.expires = jiffies + HZ;
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+ comx_status(dev, ch->line_status);
+ if(ch->line_status & LINE_UP) {
+ add_timer(&fr->keepa_timer);
+ }
+ }
+}
+
+static void fr_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ u16 dlci;
+ u8 nlpid;
+
+ if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+ dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+ if ((nlpid = skb->data[3]) == 0) { // Optional padding
+ nlpid = skb->data[4];
+ skb_pull(skb, 1);
+ }
+ skb_pull(skb, 4); /* DLCI and header throw away */
+
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n",
+ dlci, nlpid);
+ comx_debug_skb(dev, skb, "Contents");
+ }
+
+ /* Megkeressuk, kihez tartozik */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sfr->dlci == dlci)) {
+ skb->dev = sdev;
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Passing it to %s\n",sdev->name);
+ }
+ if (dev != sdev) {
+ sch->stats.rx_packets++;
+ sch->stats.rx_bytes += skb->len;
+ }
+ break;
+ }
+ }
+ switch(nlpid) {
+ case NLPID_IP:
+ skb->protocol = htons(ETH_P_IP);
+ skb->mac.raw = skb->data;
+ comx_rx(sdev, skb);
+ break;
+ case NLPID_Q933_LMI:
+ fr_rx_lmi(dev, skb, dlci, nlpid);
+ default:
+ kfree_skb(skb);
+ break;
+ }
+}
+
+static int fr_tx(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int cnt = 1;
+
+ /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel,
+ es annal a slave-nel aki eppen kuldott.
+ Egy helyen akkor all, ha a master kuldott.
+ Ez megint jo lesz majd, ha utemezni akarunk */
+
+ /* This should be fixed, the slave tbusy should be set when
+ the masters queue is full and reset when not */
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_wake_queue(sdev);
+ cnt++;
+ }
+ }
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static void fr_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (status & LINE_UP) {
+ if (!fr->keepa_freq) {
+ status |= PROTO_UP;
+ }
+ } else {
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ }
+
+ if (dev == fr->master && fr->keepa_freq) {
+ if (status & LINE_UP) {
+ fr->keepa_timer.expires = jiffies + HZ;
+ add_timer(&fr->keepa_timer);
+ fr->keepalivecnt = MAXALIVECNT + 1;
+ fr->keeploopcnt = 0;
+ } else {
+ del_timer(&fr->keepa_timer);
+ }
+ }
+
+ /* Itt a status valtozast vegig kell vinni az osszes slave-n */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ if(status & LINE_UP) {
+ netif_wake_queue(sdev);
+ }
+ comx_status(sdev, status);
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ dev->flags |= IFF_RUNNING;
+ } else {
+ dev->flags &= ~IFF_RUNNING;
+ }
+ }
+ }
+}
+
+static int fr_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+ struct comx_channel *mch;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+ (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+ printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+ return -EINVAL;
+ }
+
+ if (!fr->master) {
+ return -ENODEV;
+ }
+ mch = fr->master->priv;
+ if (fr->master != dev && (!(mch->init_status & LINE_OPEN)
+ || (mch->protocol != &fr_master_protocol))) {
+ printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+ "unable to open %s\n", fr->master->name, dev->name);
+ return -ENODEV;
+ }
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ if (fr->master == dev) {
+ if (fr->keepa_freq) {
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ add_timer(&fr->keepa_timer);
+ } else {
+ if (ch->line_status & LINE_UP) {
+ ch->line_status |= PROTO_UP;
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+ } else {
+ ch->line_status = mch->line_status;
+ if(fr->master->flags & IFF_RUNNING) {
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+// comx_status(dev, ch->line_status);
+ return 0;
+}
+
+static int fr_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+
+ if (fr->master == dev) { // Ha master
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (fr->keepa_freq) {
+ del_timer(&fr->keepa_timer);
+ }
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) &&
+ (sch->init_status & LINE_OPEN)) {
+ dev_close(sdev);
+ }
+ }
+ }
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_channel *sch, *mch;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ if (!fr->master) {
+ printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+ return 0;
+ }
+
+ mch = fr->master->priv;
+
+ /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug_skb(dev, skb, "Sending frame");
+ }
+
+ if (dev != fr->master) {
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ newskb->dev=fr->master;
+ dev_queue_xmit(newskb);
+ dev_kfree_skb(skb);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+ } else {
+ netif_stop_queue(dev);
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_stop_queue(sdev);
+ }
+ }
+
+ switch(mch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ skb_push(skb, dev->hard_header_len);
+ /* Put in DLCI */
+ skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+ skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ skb->data[2] = FRAD_UI;
+ skb->data[3] = NLPID_IP;
+
+ return dev->hard_header_len;
+}
+
+static int fr_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ int len = 0;
+
+ if (fr->master == dev) {
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int slaves = 0;
+
+ len += sprintf(page + len,
+ "This is a Frame Relay master device\nSlaves: ");
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sdev != dev)) {
+ slaves++;
+ len += sprintf(page + len, "%s ", sdev->name);
+ }
+ }
+ len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+ if (fr->keepa_freq) {
+ len += sprintf(page + len, "Line keepalive (value %d) "
+ "status %s [%d]\n", fr->keepa_freq,
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN",
+ fr->keepalivecnt);
+ } else {
+ len += sprintf(page + len, "Line keepalive protocol "
+ "is not set\n");
+ }
+ } else { // if slave
+ len += sprintf(page + len,
+ "This is a Frame Relay slave device, master: %s\n",
+ fr->master ? fr->master->name : "(not set)");
+ }
+ return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ int len = 0;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (strcmp(file->name, FILENAME_DLCI) == 0) {
+ len = sprintf(page, "%04d\n", fr->dlci);
+ } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+ len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+ "(none)");
+ } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+ len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq)
+ : sprintf(page, "off\n");
+ } else {
+ printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ char *page;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+ u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+ if (dlci_new > 1023) {
+ printk(KERN_ERR "Invalid DLCI value\n");
+ }
+ else fr->dlci = dlci_new;
+ } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+ struct net_device *new_master = dev_get_by_name(page);
+
+ if (new_master && new_master->type == ARPHRD_FRAD) {
+ struct comx_channel *sch = new_master->priv;
+ struct fr_data *sfr = sch->LINE_privdata;
+
+ if (sfr && sfr->master == new_master) {
+ if(fr->master)
+ dev_put(fr->master);
+ fr->master = new_master;
+ /* Megorokli a master statuszat */
+ ch->line_status = sch->line_status;
+ }
+ }
+ } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+ int keepa_new = -1;
+
+ if (strcmp(page, KEEPALIVE_OFF) == 0) {
+ keepa_new = 0;
+ } else {
+ keepa_new = simple_strtoul(page, NULL, 10);
+ }
+
+ if (keepa_new < 0 || keepa_new > 100) {
+ printk(KERN_ERR "invalid keepalive\n");
+ } else {
+ if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ if (keepa_new) {
+ fr_set_keepalive(dev, keepa_new);
+ }
+ }
+ } else {
+ printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int fr_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+ if (fr->master && fr->master == dev) {
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ dev_close(sdev);
+ sfr->master = NULL;
+ }
+ }
+ }
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ ch->LINE_status = 0;
+
+ if (fr->master != dev) { // if not master, remove dlci
+ if(fr->master)
+ dev_put(fr->master);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ remove_proc_entry(FILENAME_MASTER, ch->procdir);
+ } else {
+ if (fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ }
+
+ kfree(fr);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int fr_master_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+ fr->master = dev; // this means master
+ fr->dlci = 0; // let's say default
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_FRAD;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 4;
+ new_file->nlink = 1;
+
+ fr_set_keepalive(dev, 0);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int fr_slave_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_DLCI;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 10;
+ new_file->nlink = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status |= HW_OPEN;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status &= ~HW_OPEN;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!fr->master) {
+ return 0;
+ }
+
+ ch = fr->master->priv;
+ fr = ch->LINE_privdata;
+ return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct net_device *dev, char *page)
+{
+ return 0;
+}
+
+static int dlci_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = dlci_open;
+ ch->HW_close = dlci_close;
+ ch->HW_txe = dlci_txe;
+ ch->HW_statistics = dlci_statistics;
+
+ /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_statistics = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+ return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+ "frad",
+ VERSION,
+ ARPHRD_FRAD,
+ fr_master_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_protocol fr_slave_protocol = {
+ "ietf-ip",
+ VERSION,
+ ARPHRD_DLCI,
+ fr_slave_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_hardware fr_dlci = {
+ "dlci",
+ VERSION,
+ dlci_init,
+ dlci_exit,
+ dlci_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+int __init comx_proto_fr_init(void)
+{
+ int ret;
+
+ if ((ret = comx_register_hardware(&fr_dlci))) {
+ return ret;
+ }
+ if ((ret = comx_register_protocol(&fr_master_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware(fr_dlci.name);
+ comx_unregister_protocol(fr_master_protocol.name);
+ comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c
new file mode 100644
index 000000000..c0fc0c6bd
--- /dev/null
+++ b/drivers/net/wan/comx-proto-lapb.c
@@ -0,0 +1,548 @@
+/*
+ * LAPB protocol module for the COMX driver
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as non-module
+ *
+ */
+
+#define VERSION "0.80"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ if (!dev || !dev->priv) {
+ dev_kfree_skb(skb);
+ } else {
+ lapb_data_received(dev->priv, skb);
+ }
+}
+
+static int comxlapb_tx(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ return dev->hard_header_len;
+}
+
+static void comxlapb_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch;
+
+ if (!dev || !(ch = dev->priv)) {
+ return;
+ }
+ if (status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int comxlapb_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int err = 0;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ err = lapb_connect_request(ch);
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb opened, error code: %d\n",
+ dev->name, err);
+ }
+
+ if (!err) {
+ ch->init_status |= LINE_OPEN;
+ MOD_INC_USE_COUNT;
+ }
+ return err;
+}
+
+static int comxlapb_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb closed\n", dev->name);
+ }
+
+ lapb_disconnect_request(ch);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~PROTO_UP;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct sk_buff *skb2;
+
+ if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+ return -ENODEV;
+ }
+
+ if (dev->type == ARPHRD_X25) { // first byte tells what to do
+ switch(skb->data[0]) {
+ case 0x00:
+ break; // transmit
+ case 0x01:
+ lapb_connect_request(ch);
+ kfree_skb(skb);
+ return 0;
+ case 0x02:
+ lapb_disconnect_request(ch);
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ skb_pull(skb,1);
+ }
+
+ netif_stop_queue(dev);
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ lapb_data_request(ch, skb2);
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct net_device *dev, char *page)
+{
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ len += sprintf(page + len, "Line status: ");
+ if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+ len += sprintf(page + len, "not initialized\n");
+ return len;
+ }
+ len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+ "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+ parms.t1timer, parms.t1, parms.t2timer, parms.t2,
+ parms.n2count, parms.n2, parms.window);
+
+ return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (strcmp(file->name, FILENAME_T1) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t1timer, parms.t1);
+ } else if (strcmp(file->name, FILENAME_T2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t2timer, parms.t2);
+ } else if (strcmp(file->name, FILENAME_N2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.n2count, parms.n2);
+ } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+ len += sprintf(page + len, "%u\n", parms.window);
+ } else if (strcmp(file->name, FILENAME_MODE) == 0) {
+ len += sprintf(page + len, "%s, %s\n",
+ parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+ } else {
+ printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct lapb_parms_struct parms;
+ unsigned long parm;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_T1) == 0) {
+ parm=simple_strtoul(page,NULL,10);
+ if (parm > 0 && parm < 100) {
+ parms.t1=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_T2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.t2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_N2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.n2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+ parms.window = simple_strtoul(page, NULL, 10);
+ lapb_setparms(dev->priv, &parms);
+ } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+ if (comx_strcasecmp(page, "dte") == 0) {
+ parms.mode &= ~(LAPB_DCE | LAPB_DTE);
+ parms.mode |= LAPB_DTE;
+ } else if (comx_strcasecmp(page, "dce") == 0) {
+ parms.mode &= ~(LAPB_DTE | LAPB_DCE);
+ parms.mode |= LAPB_DCE;
+ } else if (comx_strcasecmp(page, "std") == 0 ||
+ comx_strcasecmp(page, "standard") == 0) {
+ parms.mode &= ~LAPB_EXTENDED;
+ parms.mode |= LAPB_STANDARD;
+ } else if (comx_strcasecmp(page, "ext") == 0 ||
+ comx_strcasecmp(page, "extended") == 0) {
+ parms.mode &= ~LAPB_STANDARD;
+ parms.mode |= LAPB_EXTENDED;
+ }
+ lapb_setparms(dev->priv, &parms);
+ } else {
+ printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb connected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x01; // link established
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+
+ ch->line_status |= PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x02; // link disconnected
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->line_status &= ~PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->dev->type == ARPHRD_X25) {
+ skb_push(skb, 1);
+ skb->data[0] = 0; // indicate data for X25
+ skb->protocol = htons(ETH_P_X25);
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ }
+
+ skb->dev = ch->dev;
+ skb->mac.raw = skb->data;
+ comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->HW_send_packet) {
+ ch->HW_send_packet(ch->dev, skb);
+ }
+}
+
+static int comxlapb_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+ }
+ lapb_unregister(dev->priv);
+
+ remove_proc_entry(FILENAME_T1, ch->procdir);
+ remove_proc_entry(FILENAME_T2, ch->procdir);
+ remove_proc_entry(FILENAME_N2, ch->procdir);
+ remove_proc_entry(FILENAME_MODE, ch->procdir);
+ remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct lapb_register_struct lapbreg;
+
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = comxlapb_rx;
+ ch->LINE_tx = comxlapb_tx;
+ ch->LINE_status = comxlapb_status;
+ ch->LINE_open = comxlapb_open;
+ ch->LINE_close = comxlapb_close;
+ ch->LINE_xmit = comxlapb_xmit;
+ ch->LINE_header = comxlapb_header;
+ ch->LINE_statistics = comxlapb_statistics;
+
+ lapbreg.connect_confirmation = comxlapb_connected;
+ lapbreg.connect_indication = comxlapb_connected;
+ lapbreg.disconnect_confirmation = comxlapb_disconnected;
+ lapbreg.disconnect_indication = comxlapb_disconnected;
+ lapbreg.data_indication = comxlapb_data_indication;
+ lapbreg.data_transmit = comxlapb_data_transmit;
+ if (lapb_register(dev->priv, &lapbreg)) {
+ return -ENOMEM;
+ }
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb registered\n", dev->name);
+ }
+
+ if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+ return -ENOMEM;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init_lapb(struct net_device *dev)
+{
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_LAPB;
+
+ return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct net_device *dev)
+{
+ dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_X25;
+
+ return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxlapb_read_proc;
+ new_file->write_proc = &comxlapb_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+ "lapb",
+ VERSION,
+ ARPHRD_LAPB,
+ comxlapb_init_lapb,
+ comxlapb_exit,
+ NULL
+};
+
+static struct comx_protocol comx25_protocol = {
+ "x25",
+ VERSION,
+ ARPHRD_X25,
+ comxlapb_init_x25,
+ comxlapb_exit,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+ int ret;
+
+ if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+ return ret;
+ }
+ return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(comxlapb_protocol.name);
+ comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c
new file mode 100644
index 000000000..0b791685d
--- /dev/null
+++ b/drivers/net/wan/comx-proto-ppp.c
@@ -0,0 +1,269 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ * - written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ * - added hdlc protocol
+ * - protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ * - some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ * - don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ * Version 0.23 (99/12/02):
+ * - tbusy fixes
+ *
+ */
+
+#define VERSION "0.23"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include "syncppp.h"
+#include "comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+ struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct syncppp_data *spch=ch->LINE_privdata;
+ struct sppp *sp = (struct sppp *)sppp_of(dev);
+
+ if(!(ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_UP)) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ }
+ if((ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_DOWN)) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ }
+ mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+
+ if(ch->line_status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ return 0;
+}
+
+static void syncppp_status(struct net_device *dev, unsigned short status)
+{
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ if(status & LINE_UP) {
+ netif_wake_queue(dev);
+ sppp_open(dev);
+ } else {
+ /* Line went down */
+ netif_stop_queue(dev);
+ sppp_close(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int syncppp_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ if(ch->line_status & LINE_UP) {
+ sppp_open(dev);
+ }
+
+ init_timer(&spch->status_timer);
+ spch->status_timer.function=syncppp_status_timerfun;
+ spch->status_timer.data=(unsigned long)dev;
+ spch->status_timer.expires=jiffies + HZ*3;
+ add_timer(&spch->status_timer);
+
+ return 0;
+}
+
+static int syncppp_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+ del_timer(&spch->status_timer);
+
+ sppp_close(dev);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ netif_stop_queue(dev);
+ switch(ch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ return 0;
+}
+
+
+static int syncppp_statistics(struct net_device *dev, char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, " ");
+ return len;
+}
+
+
+static int syncppp_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ sppp_detach(dev);
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ kfree(ch->LINE_privdata);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int syncppp_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
+
+ ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+ pppdev->dev = dev;
+ sppp_attach(pppdev);
+
+ if(ch->protocol == &hdlc_protocol) {
+ pppdev->sppp.pp_flags |= PP_CISCO;
+ dev->type = ARPHRD_HDLC;
+ } else {
+ pppdev->sppp.pp_flags &= ~PP_CISCO;
+ dev->type = ARPHRD_PPP;
+ }
+
+ ch->LINE_rx = sppp_input;
+ ch->LINE_tx = syncppp_tx;
+ ch->LINE_status = syncppp_status;
+ ch->LINE_open = syncppp_open;
+ ch->LINE_close = syncppp_close;
+ ch->LINE_xmit = syncppp_xmit;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = syncppp_statistics;
+
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+ "ppp",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+static struct comx_protocol hdlc_protocol = {
+ "hdlc",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+int __init comx_proto_ppp_init(void)
+{
+ int ret;
+
+ if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(syncppp_protocol.name);
+ comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c
new file mode 100644
index 000000000..d3ca69e86
--- /dev/null
+++ b/drivers/net/wan/comx.c
@@ -0,0 +1,1238 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ *
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - clean up source code (playing a bit of indent)
+ * - port back to kernel, add support for non-module versions
+ * - add support for board resets when channel protocol is down
+ * - reset the device structure after protocol exit
+ * the syncppp driver needs it
+ * - add support for /proc/comx/protocols and
+ * /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ * - comment out the board reset support code, the locomx
+ * driver seems not buggy now
+ * - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ * - Handle stats correctly if the lowlevel driver is
+ * is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ * - reset line_status when interface is down
+ *
+ * Version 0.84 (99/12/01):
+ * - comx_status should not check for IFF_UP (to report
+ * line status from dev->open())
+ */
+
+#define VERSION "0.84"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops;
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops; // with open/relase
+static struct file_operations comx_debug_file_ops; // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ &comx_delete_dentry /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+ 0, 4, "comx",
+ S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &comx_root_inode_ops,
+ NULL, comx_fill_inode,
+ NULL, &proc_root, NULL
+};
+
+struct comx_debugflags_struct comx_debugflags[] = {
+ { "comx_rx", DEBUG_COMX_RX },
+ { "comx_tx", DEBUG_COMX_TX },
+ { "hw_tx", DEBUG_HW_TX },
+ { "hw_rx", DEBUG_HW_RX },
+ { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE },
+ { "comxppp", DEBUG_COMX_PPP },
+ { "comxlapb", DEBUG_COMX_LAPB },
+ { "dlci", DEBUG_COMX_DLCI },
+ { NULL, 0 }
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct net_device *dev, char *fmt, ...)
+{
+ struct comx_channel *ch = dev->priv;
+ char *page,*str;
+ va_list args;
+ int len;
+
+ if (!ch->debug_area) return 0;
+
+ if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+ va_start(args, fmt);
+ len = vsprintf(str = page, fmt, args);
+ va_end(args);
+
+ if (len >= PAGE_SIZE) {
+ printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+
+ while (len) {
+ int to_copy;
+ int free = (ch->debug_start - ch->debug_end + ch->debug_size)
+ % ch->debug_size;
+
+ to_copy = min( free ? free : ch->debug_size,
+ min (ch->debug_size - ch->debug_end, len) );
+ memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+ str += to_copy;
+ len -= to_copy;
+ ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+ if (ch->debug_start == ch->debug_end) // Full ? push start away
+ ch->debug_start = (ch->debug_start + len + 1) %
+ ch->debug_size;
+ ch->debug_file->size = (ch->debug_end - ch->debug_start +
+ ch->debug_size) % ch->debug_size;
+ }
+
+ free_page((unsigned long)page);
+ return 0;
+}
+
+int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+ if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+ if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+ return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg)
+{
+ int pos = 0;
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+
+ comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+ while (pos != len) {
+ char line[80];
+ int i = 0;
+
+ memset(line, 0, 80);
+ sprintf(line,"%04d ", pos);
+ do {
+ sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+ sprintf(line + 60 + (pos % 16), "%c",
+ isprint(bytes[pos]) ? bytes[pos] : '.');
+ pos++;
+ } while (pos != len && pos % 16);
+
+ while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+ line[77] = '\n';
+ line[78] = 0;
+
+ comx_debug(dev, "%s", line);
+ }
+ comx_debug(dev, "\n");
+ return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+ ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] =
+ ch->current_stats->tx_bytes;
+
+ ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+ mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+ if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+ ch->HW_reset(dev);
+ }
+ }
+
+ mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif
+
+static int comx_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret=0;
+
+ if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+ if ((ret = ch->HW_open(dev))) return ret;
+ if ((ret = ch->LINE_open(dev))) {
+ ch->HW_close(dev);
+ return ret;
+ };
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0444;
+ }
+
+#if 0
+ ch->reset_pending = 1;
+ ch->reset_timeout = 30;
+ ch->reset_timer.function = comx_reset_timerfun;
+ ch->reset_timer.data = (unsigned long)dev;
+ ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+ add_timer(&ch->reset_timer);
+#endif
+
+ return 0;
+}
+
+static int comx_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret = -ENODEV;
+
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ }
+
+#if 0
+ del_timer(&ch->reset_timer);
+#endif
+
+ if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+ ret = ch->LINE_close(dev);
+ }
+
+ if (ret) return ret;
+
+ if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+ ret = ch->HW_close(dev);
+ }
+
+ ch->line_status=0;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0644;
+ }
+
+ return ret;
+}
+
+void comx_status(struct net_device *dev, int status)
+{
+ struct comx_channel *ch = dev->priv;
+
+#if 0
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ clear_bit(0,&ch->reset_pending);
+ }
+#endif
+
+ printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+ dev->name, status & LINE_UP ? "UP" : "DOWN",
+ status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ?
+ "UP" : "DOWN");
+
+ ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int rc;
+
+ if (skb->len > dev->mtu + dev->hard_header_len) {
+ printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+ (int)skb->len, dev->mtu);
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_TX) {
+ comx_debug_skb(dev, skb, "comx_xmit skb");
+ }
+
+ rc=ch->LINE_xmit(skb, dev);
+// if (!rc) dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_header) {
+ return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+ } else {
+ return 0;
+ }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_rebuild_header) {
+ return(ch->LINE_rebuild_header(skb));
+ } else {
+ return 0;
+ }
+}
+
+int comx_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->debug_flags & DEBUG_COMX_RX) {
+ comx_debug_skb(dev, skb, "comx_rx skb");
+ }
+ if (skb) {
+ netif_rx(skb);
+ }
+ return 0;
+}
+
+static struct net_device_stats *comx_stats(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ del_timer(&ch->lineup_timer);
+ clear_bit(0, &ch->lineup_pending);
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+}
+
+#define LOADAVG(avg, off) (int) \
+ ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \
+ - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ int len = 0;
+ int tmp;
+ int i = 0;
+ char tmpstr[20];
+ int tmpstrlen = 0;
+
+ len += sprintf(page + len, "Interface administrative status is %s, "
+ "modem status is %s, protocol is %s\n",
+ dev->flags & IFF_UP ? "UP" : "DOWN",
+ ch->line_status & LINE_UP ? "UP" : "DOWN",
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN");
+ len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+ "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+ LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+ tmpstr[0] = 0;
+ for (i=0; i != 3; i++) {
+ char tf;
+
+ tf = ch->loadavg[i] % 60 == 0 &&
+ ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+ tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s",
+ ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf,
+ i == 2 ? ")\n" : "/");
+ }
+ len += sprintf(page + len,
+ "%s (output): %d / %d / %d bits/s (%s", tmpstr,
+ LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size),
+ LOADAVG(2, ch->loadavg_size), tmpstr);
+
+ len += sprintf(page + len, "Debug flags: ");
+ tmp = len; i = 0;
+ while (comx_debugflags[i].name) {
+ if (ch->debug_flags & comx_debugflags[i].value)
+ len += sprintf(page + len, "%s ",
+ comx_debugflags[i].name);
+ i++;
+ }
+ len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+ len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+ "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n"
+ "TX errors: underrun: %lu\n",
+ ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors,
+ ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors,
+ ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+ ch->current_stats->tx_fifo_errors);
+
+ if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+ len += ch->LINE_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Line status: driver not initialized\n");
+ }
+ if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+ len += ch->HW_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Board status: driver not initialized\n");
+ }
+
+ return len;
+}
+
+static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_ioctl) {
+ return(ch->LINE_ioctl(dev, ifr, cmd));
+ }
+ return -EINVAL;
+}
+
+static void comx_reset_dev(struct net_device *dev)
+{
+ dev->open = comx_open;
+ dev->stop = comx_close;
+ dev->hard_start_xmit = comx_xmit;
+ dev->hard_header = comx_header;
+ dev->rebuild_header = comx_rebuild_header;
+ dev->get_stats = comx_stats;
+ dev->do_ioctl = comx_ioctl;
+ dev->change_mtu = NULL;
+ dev->tx_queue_len = 20;
+ dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct net_device *dev)
+{
+ struct comx_channel *ch;
+
+ if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch, 0, sizeof(struct comx_channel));
+
+ ch->loadavg[0] = 5;
+ ch->loadavg[1] = 300;
+ ch->loadavg[2] = 900;
+ ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1;
+ if ((ch->avg_bytes = kmalloc(ch->loadavg_size *
+ sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+ ch->loadavg_counter = 0;
+ ch->loadavg_timer.function = comx_loadavg_timerfun;
+ ch->loadavg_timer.data = (unsigned long)dev;
+ ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+ add_timer(&ch->loadavg_timer);
+
+ dev->priv = (void *)ch;
+ ch->dev = dev;
+ ch->line_status &= ~LINE_UP;
+
+ ch->current_stats = &ch->stats;
+
+ comx_reset_dev(dev);
+ return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_STATUS) == 0) {
+ len = comx_statistics(dev, page);
+ } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+ len = sprintf(page, "%s\n", ch->hardware ?
+ ch->hardware->name : HWNAME_NONE);
+ } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+ len = sprintf(page, "%s\n", ch->protocol ?
+ ch->protocol->name : PROTONAME_NONE);
+ } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+ len = sprintf(page, "%01d\n", ch->lineup_delay);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct comx_hardware *hw;
+ struct comx_protocol *line;
+
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+ for(hw=comx_channels;hw;hw=hw->next)
+ len+=sprintf(page+len, "%s\n", hw->name);
+ } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+ for(line=comx_lines;line;line=line->next)
+ len+=sprintf(page+len, "%s\n", line->name);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+ void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ char *page;
+ struct comx_hardware *hw = comx_channels;
+ struct comx_protocol *line = comx_lines;
+ char str[30];
+ int ret=0;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+ copy_from_user(page, buffer, count);
+
+ if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+ if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+ int i;
+ int ret = 0;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (ch->debug_area) kfree(ch->debug_area);
+ if ((ch->debug_area = kmalloc(ch->debug_size = i,
+ GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ }
+ ch->debug_start = ch->debug_end = 0;
+ restore_flags(flags);
+ free_page((unsigned long)page);
+ return count;
+ }
+
+ if (*page != '+' && *page != '-') {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (comx_debugflags[i].value &&
+ strncmp(comx_debugflags[i].name, page + 1,
+ strlen(comx_debugflags[i].name))) {
+ i++;
+ }
+
+ if (comx_debugflags[i].value == 0) {
+ printk(KERN_ERR "Invalid debug option\n");
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ if (*page == '+') {
+ ch->debug_flags |= comx_debugflags[i].value;
+ } else {
+ ch->debug_flags &= ~comx_debugflags[i].value;
+ }
+ } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (hw) {
+ if (strcmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+ sprintf(str,"comx-hw-%s",page);
+ request_module(str);
+ }
+ hw=comx_channels;
+ while (hw) {
+ if (comx_strcasecmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+ if (ch->init_status & HW_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->hardware = hw;
+ entry->size = strlen(page) + 1;
+ if (hw && hw->hw_init) hw->hw_init(dev);
+ } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+ sprintf(str,"comx-proto-%s",page);
+ request_module(str);
+ }
+ line=comx_lines;
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+
+ if (ch->init_status & LINE_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->protocol = line;
+ entry->size = strlen(page) + 1;
+ comx_reset_dev(dev);
+ if (line && line->line_init) line->line_init(dev);
+ } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+ int i;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ if (i >=0 && i < 10) {
+ ch->lineup_delay = i;
+ } else {
+ printk(KERN_ERR "comx: invalid lineup_delay value\n");
+ }
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+ switch(orig) {
+ case 0:
+ file->f_pos = max(0, min(offset,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 1:
+ file->f_pos = max(0, min(offset + file->f_pos,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 2:
+ file->f_pos = max(0,
+ min(offset + file->f_dentry->d_inode->i_size,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ }
+ return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+ if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+ return -EACCES;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+ struct net_device *dev = de->parent->data;
+ struct comx_channel *ch = dev->priv;
+ loff_t copied = 0;
+ unsigned long flags;
+
+ save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+ // from irq inside read. no problem if the buffer is
+ // large enough
+
+ while (count > 0 && ch->debug_start != ch->debug_end) {
+ int len;
+
+ len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+ %ch->debug_size, min (ch->debug_size -
+ ch->debug_start, count));
+
+ if (len) copy_to_user(buffer + copied,
+ ch->debug_area + ch->debug_start, len);
+ ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+ de->size -= len;
+ count -= len;
+ copied += len;
+ }
+
+ restore_flags(flags);
+ return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct proc_dir_entry *new_dir, *debug_file;
+ struct net_device *dev;
+ struct comx_channel *ch;
+
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
+ &comx_root_dir)) == NULL) {
+ return -EIO;
+ }
+
+ new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->nlink = 2;
+ new_dir->data = NULL; // ide jon majd a struct dev
+
+ /* Ezek kellenek */
+ if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644,
+ strlen(HWNAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
+ strlen(PROTONAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+ return -ENOMEM;
+ }
+
+ if ((debug_file = create_proc_entry(FILENAME_DEBUG,
+ S_IFREG | 0644, new_dir)) == NULL) {
+ return -ENOMEM;
+ }
+ debug_file->ops = &comx_debug_inode_ops;
+ debug_file->data = (void *)debug_file;
+ debug_file->read_proc = NULL; // see below
+ debug_file->write_proc = &comx_write_proc;
+ debug_file->nlink = 1;
+
+ if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct net_device));
+ dev->name = (char *)new_dir->name;
+ dev->init = comx_init_dev;
+
+ if (register_netdevice(dev)) {
+ return -EIO;
+ }
+ ch=dev->priv;
+ if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch->if_ptr, 0, sizeof(struct ppp_device));
+ ch->debug_file = debug_file;
+ ch->procdir = new_dir;
+ new_dir->data = dev;
+
+ ch->debug_start = ch->debug_end = 0;
+ if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE,
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+ struct net_device *dev = entry->data;
+ struct comx_channel *ch = dev->priv;
+ int ret;
+
+ /* Egyelore miert ne ? */
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ return ret;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ if(ch->protocol && ch->protocol->line_init) {
+ ch->protocol->line_init(dev);
+ }
+ return ret;
+ }
+ ch->protocol = NULL;
+ ch->hardware = NULL;
+
+ del_timer(&ch->loadavg_timer);
+ kfree(ch->avg_bytes);
+
+ unregister_netdev(dev);
+ if (ch->debug_area) {
+ kfree(ch->debug_area);
+ }
+ if (dev->priv) {
+ kfree(dev->priv);
+ }
+ kfree(dev);
+
+ remove_proc_entry(FILENAME_DEBUG, entry);
+ remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+ remove_proc_entry(FILENAME_STATUS, entry);
+ remove_proc_entry(FILENAME_HARDWARE, entry);
+ remove_proc_entry(FILENAME_PROTOCOL, entry);
+ remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *de;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ return ERR_PTR(-ENOTDIR);
+ }
+
+ if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+ for (de = de->subdir ; de ; de = de->next) {
+ if ((de && de->low_ino) &&
+ (de->namelen == dentry->d_name.len) &&
+ (memcmp(dentry->d_name.name, de->name,
+ de->namelen) == 0)) {
+ if ((inode = proc_get_inode(dir->i_sb,
+ de->low_ino, de)) == NULL) {
+ printk(KERN_ERR "COMX: lookup error\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ }
+ }
+ }
+ dentry->d_op = &comx_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+ d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comx_read_proc;
+ new_file->write_proc = &comx_write_proc;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ comx_channels = comx_hw;
+ } else {
+ while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+ hw = hw->next;
+ }
+ if (strcmp(comx_hw->name, hw->name) == 0) {
+ return -1;
+ }
+ hw->next = comx_hw;
+ }
+
+ printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+ return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ return -1;
+ }
+
+ if (strcmp(hw->name, name) == 0) {
+ comx_channels = comx_channels->next;
+ return 0;
+ }
+
+ while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+ hw = hw->next;
+ }
+
+ if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+ hw->next = hw->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ comx_lines = comx_line;
+ } else {
+ while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+ pr = pr->next;
+ }
+ if (strcmp(comx_line->name, pr->name) == 0) {
+ return -1;
+ }
+ pr->next = comx_line;
+ }
+
+ printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+ return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ return -1;
+ }
+
+ if (strcmp(pr->name, name) == 0) {
+ comx_lines = comx_lines->next;
+ return 0;
+ }
+
+ while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+ pr = pr->next;
+ }
+
+ if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+ pr->next = pr->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+ struct proc_dir_entry *new_file;
+
+ memcpy(&comx_root_inode_ops, &proc_dir_inode_operations,
+ sizeof(struct inode_operations));
+ comx_root_inode_ops.lookup = &comx_lookup;
+ comx_root_inode_ops.mkdir = &comx_mkdir;
+ comx_root_inode_ops.rmdir = &comx_rmdir;
+
+ memcpy(&comx_normal_inode_ops, &proc_net_inode_operations,
+ sizeof(struct inode_operations));
+ comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+ comx_normal_inode_ops.lookup = &comx_lookup;
+
+ memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops,
+ sizeof(struct inode_operations));
+ comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+ memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+ sizeof(struct file_operations));
+ comx_normal_file_ops.open = &comx_file_open;
+ comx_normal_file_ops.release = &comx_file_release;
+
+ memcpy(&comx_debug_file_ops, &comx_normal_file_ops,
+ sizeof(struct file_operations));
+ comx_debug_file_ops.llseek = &comx_debug_lseek;
+ comx_debug_file_ops.read = &comx_debug_read;
+
+ if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+ if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+
+ printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n",
+ VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+ comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+ comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+ comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+ comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+ comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+ comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+ comx_proto_fr_init();
+#endif
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+ remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+ proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h
new file mode 100644
index 000000000..e02849b90
--- /dev/null
+++ b/drivers/net/wan/comx.h
@@ -0,0 +1,240 @@
+/*
+ * General definitions for the COMX driver
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * net_device_stats:
+ * rx_length_errors rec_len < 4 || rec_len > 2000
+ * rx_over_errors receive overrun (OVR)
+ * rx_crc_errors rx crc error
+ * rx_frame_errors aborts rec'd (ABO)
+ * rx_fifo_errors status fifo overrun (PBUFOVR)
+ * rx_missed_errors receive buffer overrun (BUFOVR)
+ * tx_aborted_errors ?
+ * tx_carrier_errors modem line status changes
+ * tx_fifo_errors tx underrun (locomx)
+ */
+#include <linux/config.h>
+
+struct comx_protocol {
+ char *name;
+ char *version;
+ unsigned short encap_type;
+ int (*line_init)(struct net_device *dev);
+ int (*line_exit)(struct net_device *dev);
+ struct comx_protocol *next;
+ };
+
+struct comx_hardware {
+ char *name;
+ char *version;
+ int (*hw_init)(struct net_device *dev);
+ int (*hw_exit)(struct net_device *dev);
+ int (*hw_dump)(struct net_device *dev);
+ struct comx_hardware *next;
+ };
+
+struct comx_channel {
+ void *if_ptr; // General purpose pointer
+ struct net_device *dev; // Where we belong to
+ struct net_device *twin; // On dual-port cards
+ struct proc_dir_entry *procdir; // the directory
+
+ unsigned char init_status;
+ unsigned char line_status;
+
+ struct timer_list lineup_timer; // against line jitter
+ int lineup_pending;
+ unsigned char lineup_delay;
+
+#if 0
+ struct timer_list reset_timer; // for board resetting
+ int reset_pending;
+ int reset_timeout;
+#endif
+
+ struct net_device_stats stats;
+ struct net_device_stats *current_stats;
+#if 0
+ unsigned long board_resets;
+#endif
+ unsigned long *avg_bytes;
+ int loadavg_counter, loadavg_size;
+ int loadavg[3];
+ struct timer_list loadavg_timer;
+ int debug_flags;
+ char *debug_area;
+ int debug_start, debug_end, debug_size;
+ struct proc_dir_entry *debug_file;
+#ifdef CONFIG_COMX_DEBUG_RAW
+ char *raw;
+ int raw_len;
+#endif
+ // LINE specific
+ struct comx_protocol *protocol;
+ void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb);
+ int (*LINE_tx)(struct net_device *dev);
+ void (*LINE_status)(struct net_device *dev, u_short status);
+ int (*LINE_open)(struct net_device *dev);
+ int (*LINE_close)(struct net_device *dev);
+ int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev);
+ int (*LINE_header)(struct sk_buff *skb, struct net_device *dev,
+ u_short type,void *daddr, void *saddr,
+ unsigned len);
+ int (*LINE_rebuild_header)(struct sk_buff *skb);
+ int (*LINE_statistics)(struct net_device *dev, char *page);
+ int (*LINE_parameter_check)(struct net_device *dev);
+ int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr,
+ int cmd);
+ void (*LINE_mod_use)(int);
+ void * LINE_privdata;
+
+ // HW specific
+
+ struct comx_hardware *hardware;
+ void (*HW_board_on)(struct net_device *dev);
+ void (*HW_board_off)(struct net_device *dev);
+ struct net_device *(*HW_access_board)(struct net_device *dev);
+ void (*HW_release_board)(struct net_device *dev, struct net_device *savep);
+ int (*HW_txe)(struct net_device *dev);
+ int (*HW_open)(struct net_device *dev);
+ int (*HW_close)(struct net_device *dev);
+ int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb);
+ int (*HW_statistics)(struct net_device *dev, char *page);
+#if 0
+ int (*HW_reset)(struct net_device *dev, char *page);
+#endif
+ int (*HW_load_board)(struct net_device *dev);
+ void (*HW_set_clock)(struct net_device *dev);
+ void *HW_privdata;
+ };
+
+struct comx_debugflags_struct {
+ char *name;
+ int value;
+ };
+
+#define COMX_ROOT_DIR_NAME "comx"
+
+#define FILENAME_HARDWARE "boardtype"
+#define FILENAME_HARDWARELIST "boardtypes"
+#define FILENAME_PROTOCOL "protocol"
+#define FILENAME_PROTOCOLLIST "protocols"
+#define FILENAME_DEBUG "debug"
+#define FILENAME_CLOCK "clock"
+#define FILENAME_STATUS "status"
+#define FILENAME_IO "io"
+#define FILENAME_IRQ "irq"
+#define FILENAME_KEEPALIVE "keepalive"
+#define FILENAME_LINEUPDELAY "lineup_delay"
+#define FILENAME_CHANNEL "channel"
+#define FILENAME_FIRMWARE "firmware"
+#define FILENAME_MEMADDR "memaddr"
+#define FILENAME_TWIN "twin"
+#define FILENAME_T1 "t1"
+#define FILENAME_T2 "t2"
+#define FILENAME_N2 "n2"
+#define FILENAME_WINDOW "window"
+#define FILENAME_MODE "mode"
+#define FILENAME_DLCI "dlci"
+#define FILENAME_MASTER "master"
+#ifdef CONFIG_COMX_DEBUG_RAW
+#define FILENAME_RAW "raw"
+#endif
+
+#define PROTONAME_NONE "none"
+#define HWNAME_NONE "none"
+#define KEEPALIVE_OFF "off"
+
+#define FRAME_ACCEPTED 0 /* sending and xmitter busy */
+#define FRAME_DROPPED 1
+#define FRAME_ERROR 2 /* xmitter error */
+#define FRAME_QUEUED 3 /* sending but more can come */
+
+#define LINE_UP 1 /* Modem UP */
+#define PROTO_UP 2
+#define PROTO_LOOP 4
+
+#define HW_OPEN 1
+#define LINE_OPEN 2
+#define FW_LOADED 4
+#define IRQ_ALLOCATED 8
+
+#define DEBUG_COMX_RX 2
+#define DEBUG_COMX_TX 4
+#define DEBUG_HW_TX 16
+#define DEBUG_HW_RX 32
+#define DEBUG_HDLC_KEEPALIVE 64
+#define DEBUG_COMX_PPP 128
+#define DEBUG_COMX_LAPB 256
+#define DEBUG_COMX_DLCI 512
+
+#define DEBUG_PAGESIZE 3072
+#define DEFAULT_DEBUG_SIZE 4096
+#define DEFAULT_LINEUP_DELAY 1
+#define FILE_PAGESIZE 3072
+
+#ifndef COMX_PPP_MAJOR
+#define COMX_PPP_MAJOR 88
+#endif
+
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8 byte;
+#endif
+#ifndef word
+typedef u16 word;
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int comx_register_hardware(struct comx_hardware *comx_hw);
+extern int comx_unregister_hardware(char *name);
+extern int comx_register_protocol(struct comx_protocol *comx_line);
+extern int comx_unregister_protocol(char *name);
+
+extern int comx_rx(struct net_device *dev, struct sk_buff *skb);
+extern void comx_status(struct net_device *dev, int status);
+extern void comx_lineup_func(unsigned long d);
+
+extern int comx_debug(struct net_device *dev, char *fmt, ...);
+extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg);
+extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg);
+extern int comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h
new file mode 100644
index 000000000..15230dc1f
--- /dev/null
+++ b/drivers/net/wan/comxhw.h
@@ -0,0 +1,113 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define LOCOMX_IO_EXTENT 8
+#define COMX_IO_EXTENT 4
+#define HICOMX_IO_EXTENT 16
+
+#define COMX_MAX_TX_SIZE 1600
+#define COMX_MAX_RX_SIZE 2048
+
+#define COMX_JAIL_OFFSET 0xffff
+#define COMX_JAIL_VALUE 0xfe
+#define COMX_MEMORY_SIZE 65536
+#define HICOMX_MEMORY_SIZE 16384
+#define COMX_MEM_MIN 0xa0000
+#define COMX_MEM_MAX 0xf0000
+
+#define COMX_DEFAULT_IO 0x360
+#define COMX_DEFAULT_IRQ 10
+#define COMX_DEFAULT_MEMADDR 0xd0000
+#define HICOMX_DEFAULT_IO 0x320
+#define HICOMX_DEFAULT_IRQ 10
+#define HICOMX_DEFAULT_MEMADDR 0xd0000
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+#define MAX_CHANNELNO 2
+
+#define COMX_CHANNEL_OFFSET 0x2000
+
+#define COMX_ENABLE_BOARD_IT 0x40
+#define COMX_BOARD_RESET 0x20
+#define COMX_ENABLE_BOARD_MEM 0x10
+#define COMX_DISABLE_BOARD_MEM 0
+#define COMX_DISABLE_ALL 0x00
+
+#define HICOMX_DISABLE_ALL 0x00
+#define HICOMX_ENABLE_BOARD_MEM 0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET 0x01
+#define HICOMX_PRG_MEM 4
+#define HICOMX_DATA_MEM 0
+#define HICOMX_ID_BYTE 0x55
+
+#define CMX_ID_BYTE 0x31
+#define COMX_CLOCK_CONST 8000
+
+#define LINKUP_READY 3
+
+#define OFF_FW_L1_ID 0x01e /* ID bytes */
+#define OFF_FW_L2_ID 0x1006
+#define FW_L1_ID_1 0xab
+#define FW_L1_ID_2_COMX 0xc0
+#define FW_L1_ID_2_HICOMX 0xc1
+#define FW_L2_ID_1 0xab
+
+#define OFF_A_L2_CMD 0x130 /* command register for L2 */
+#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */
+#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */
+#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */
+#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA 0x12c /* IT rep disable */
+#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */
+#define OFF_A_L1_CLKINI 0x12e /* Timer Const */
+#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */
+#define OFF_A_L2_DAV 0x134 /* Rx DAV */
+#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */
+#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */
+#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */
+
+#define OFF_A_L2_T1 0x174 /* T1 timer */
+#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */
+
+#define COMX_CMD_INIT 1
+#define COMX_CMD_EXIT 2
+#define COMX_CMD_OPEN 16
+#define COMX_CMD_CLOSE 17
+
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 633514166..544fcb8dd 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -1150,19 +1150,19 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
{
switch(cmd) {
case COSAIORSET: /* Reset the device */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
return cosa_reset(cosa);
case COSAIOSTRT: /* Start the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_start(cosa, arg);
case COSAIODOWNLD: /* Download the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_download(cosa, (struct cosa_download *)arg);
case COSAIORMEM:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_readmem(cosa, (struct cosa_download *)arg);
case COSAIORTYPE:
@@ -1189,7 +1189,7 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
case COSAIONRCHANS:
return cosa->nchannels;
case COSAIOBMSET:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if (is_8bit(cosa))
return -EINVAL;
diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h
new file mode 100644
index 000000000..675b7b1f1
--- /dev/null
+++ b/drivers/net/wan/hscx.h
@@ -0,0 +1,103 @@
+#define HSCX_MTU 1600
+
+#define HSCX_ISTA 0x00
+#define HSCX_MASK 0x00
+#define HSCX_STAR 0x01
+#define HSCX_CMDR 0x01
+#define HSCX_MODE 0x02
+#define HSCX_TIMR 0x03
+#define HSCX_EXIR 0x04
+#define HSCX_XAD1 0x04
+#define HSCX_RBCL 0x05
+#define HSCX_SAD2 0x05
+#define HSCX_RAH1 0x06
+#define HSCX_RSTA 0x07
+#define HSCX_RAH2 0x07
+#define HSCX_RAL1 0x08
+#define HSCX_RCHR 0x09
+#define HSCX_RAL2 0x09
+#define HSCX_XBCL 0x0a
+#define HSCX_BGR 0x0b
+#define HSCX_CCR2 0x0c
+#define HSCX_RBCH 0x0d
+#define HSCX_XBCH 0x0d
+#define HSCX_VSTR 0x0e
+#define HSCX_RLCR 0x0e
+#define HSCX_CCR1 0x0f
+#define HSCX_FIFO 0x1e
+
+#define HSCX_HSCX_CHOFFS 0x400
+#define HSCX_SEROFFS 0x1000
+
+#define HSCX_RME 0x80
+#define HSCX_RPF 0x40
+#define HSCX_RSC 0x20
+#define HSCX_XPR 0x10
+#define HSCX_TIN 0x08
+#define HSCX_ICA 0x04
+#define HSCX_EXA 0x02
+#define HSCX_EXB 0x01
+
+#define HSCX_XMR 0x80
+#define HSCX_XDU 0x40
+#define HSCX_EXE 0x40
+#define HSCX_PCE 0x20
+#define HSCX_RFO 0x10
+#define HSCX_CSC 0x08
+#define HSCX_RFS 0x04
+
+#define HSCX_XDOV 0x80
+#define HSCX_XFW 0x40
+#define HSCX_XRNR 0x20
+#define HSCX_RRNR 0x10
+#define HSCX_RLI 0x08
+#define HSCX_CEC 0x04
+#define HSCX_CTS 0x02
+#define HSCX_WFA 0x01
+
+#define HSCX_RMC 0x80
+#define HSCX_RHR 0x40
+#define HSCX_RNR 0x20
+#define HSCX_XREP 0x20
+#define HSCX_STI 0x10
+#define HSCX_XTF 0x08
+#define HSCX_XIF 0x04
+#define HSCX_XME 0x02
+#define HSCX_XRES 0x01
+
+#define HSCX_AUTO 0x00
+#define HSCX_NONAUTO 0x40
+#define HSCX_TRANS 0x80
+#define HSCX_XTRANS 0xc0
+#define HSCX_ADM16 0x20
+#define HSCX_ADM8 0x00
+#define HSCX_TMD_EXT 0x00
+#define HSCX_TMD_INT 0x10
+#define HSCX_RAC 0x08
+#define HSCX_RTS 0x04
+#define HSCX_TLP 0x01
+
+#define HSCX_VFR 0x80
+#define HSCX_RDO 0x40
+#define HSCX_CRC 0x20
+#define HSCX_RAB 0x10
+
+#define HSCX_CIE 0x04
+#define HSCX_RIE 0x02
+
+#define HSCX_DMA 0x80
+#define HSCX_NRM 0x40
+#define HSCX_CAS 0x20
+#define HSCX_XC 0x10
+
+#define HSCX_OV 0x10
+
+#define HSCX_CD 0x80
+
+#define HSCX_RC 0x80
+
+#define HSCX_PU 0x80
+#define HSCX_NRZ 0x00
+#define HSCX_NRZI 0x40
+#define HSCX_ODS 0x10
+#define HSCX_ITF 0x08
diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h
new file mode 100644
index 000000000..1815eef75
--- /dev/null
+++ b/drivers/net/wan/mixcom.h
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define MIXCOM_IO_EXTENT 0x20
+
+#define MIXCOM_DEFAULT_IO 0x180
+#define MIXCOM_DEFAULT_IRQ 5
+
+#define MIXCOM_ID 0x11
+#define MIXCOM_SERIAL_OFFSET 0x1000
+#define MIXCOM_CHANNEL_OFFSET 0x400
+#define MIXCOM_IT_OFFSET 0xc14
+#define MIXCOM_STATUS_OFFSET 0xc14
+#define MIXCOM_ID_OFFSET 0xc10
+#define MIXCOM_ON 0x1
+#define MIXCOM_OFF 0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB 0x1
+#define MIXCOM_CTSA 0x2
+#define MIXCOM_CHANNELNO 0x20
+#define MIXCOM_POWERFAIL 0x40
+#define MIXCOM_BOOT 0x80
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index b161fbabc..be665f0ae 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -101,7 +101,7 @@ static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int sbni_close(struct net_device *dev);
static void sbni_drop_tx_queue(struct net_device *dev);
static struct enet_statistics *sbni_get_stats(struct net_device *dev);
-void card_start(struct net_device *dev);
+static void card_start(struct net_device *dev);
static inline unsigned short sbni_recv(struct net_device *dev);
void change_level(struct net_device *dev);
static inline void sbni_xmit(struct net_device *dev);
@@ -647,7 +647,7 @@ static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-void card_start(struct net_device *dev)
+static void card_start(struct net_device *dev)
{
struct net_local *lp = (struct net_local*)dev->priv;
@@ -1200,6 +1200,8 @@ static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
case SIOCDEVRESINSTATS:
{
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); )
lp->in_stats.all_rx_number = 0;
lp->in_stats.bad_rx_number = 0;
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index e7875649e..0454118a8 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -1247,7 +1247,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct frad_local *flp;
- if(!suser())
+ if(!capable(CAP_NET_ADMIN))
return -EPERM;
flp = dev->priv;
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index e039bbc28..5b8504616 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -147,13 +147,17 @@ static void sppp_print_bytes (u8 *p, u16 len);
static int debug = 0;
+MODULE_PARM(debug,"1i");
+
/*
* Interface down stub
*/
static void if_down(struct net_device *dev)
{
- ;
+ struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+ sp->pp_link_state=SPPP_LINK_DOWN;
}
/*
@@ -182,8 +186,19 @@ static void sppp_clear_timeout(struct sppp *p)
}
}
-/*
- * Process the received packet.
+/**
+ * sppp_input - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ *
+ * This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
+ *
+ * We process the options in the card. If the frame is destined for
+ * the protocol stacks then it requeues the frame for the upper level
+ * protocol. If it is a control from it is processed and discarded
+ * here.
*/
void sppp_input (struct net_device *dev, struct sk_buff *skb)
@@ -194,7 +209,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb)
skb->dev=dev;
skb->mac.raw=skb->data;
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_RUNNING)
{
/* Count received bytes, add FCS and one flag */
sp->ibytes+= skb->len + 3;
@@ -324,7 +339,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 t
h=(struct ppp_header *)skb->data;
if(sp->pp_flags&PP_CISCO)
{
- h->address = CISCO_MULTICAST;
+ h->address = CISCO_UNICAST;
h->control = 0;
}
else
@@ -370,7 +385,7 @@ static void sppp_keepalive (unsigned long dummy)
/* Keepalive mode disabled or channel down? */
if (! (sp->pp_flags & PP_KEEPALIVE) ||
- ! (dev->flags & IFF_RUNNING))
+ ! (dev->flags & IFF_UP))
continue;
/* No keepalive in PPP mode if LCP not opened yet. */
@@ -530,10 +545,10 @@ badreq:
if (h->ident != sp->lcp.confid)
break;
sppp_clear_timeout (sp);
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
+ if ((sp->pp_link_state != SPPP_LINK_UP) &&
+ (dev->flags & IFF_UP)) {
/* Coming out of loopback mode. */
- dev->flags |= IFF_UP;
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
switch (sp->lcp.state) {
@@ -698,9 +713,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
break;
}
sp->pp_loopcnt = 0;
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
- dev->flags |= IFF_UP;
+ if (sp->pp_link_state==SPPP_LINK_DOWN &&
+ (dev->flags & IFF_UP)) {
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
break;
@@ -825,11 +840,19 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
dev_queue_xmit(skb);
}
+/**
+ * sppp_close - close down a synchronous PPP or Cisco HDLC link
+ * @dev: The network device to drop the link of
+ *
+ * This drops the logical interface to the channel. It is not
+ * done politely as we assume we will also be dropping DTR. Any
+ * timeouts are killed.
+ */
int sppp_close (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
- dev->flags &= ~IFF_RUNNING;
+ sp->pp_link_state = SPPP_LINK_DOWN;
sp->lcp.state = LCP_STATE_CLOSED;
sp->ipcp.state = IPCP_STATE_CLOSED;
sppp_clear_timeout (sp);
@@ -838,24 +861,49 @@ int sppp_close (struct net_device *dev)
EXPORT_SYMBOL(sppp_close);
+/**
+ * sppp_open - open a synchronous PPP or Cisco HDLC link
+ * @dev: Network device to activate
+ *
+ * Close down any existing synchronous session and commence
+ * from scratch. In the PPP case this means negotiating LCP/IPCP
+ * and friends, while for Cisco HDLC we simply need to staet sending
+ * keepalives
+ */
int sppp_open (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
- if (!(sp->pp_flags & PP_CISCO))
+ if (!(sp->pp_flags & PP_CISCO)) {
sppp_lcp_open (sp);
+ }
+ sp->pp_link_state = SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_open);
+/**
+ * sppp_reopen - notify of physical link loss
+ * @dev: Device that lost the link
+ *
+ * This function informs the synchronous protocol code that
+ * the underlying link died (for example a carrier drop on X.21)
+ *
+ * We increment the magic numbers to ensure that if the other end
+ * failed to notice we will correctly start a new session. It happens
+ * do to the nature of telco circuits is that you can lose carrier on
+ * one endonly.
+ *
+ * Having done this we go back to negotiating. This function may
+ * be called from an interrupt context.
+ */
+
int sppp_reopen (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
if (!(sp->pp_flags & PP_CISCO))
{
sp->lcp.magic = jiffies;
@@ -864,12 +912,23 @@ int sppp_reopen (struct net_device *dev)
sp->ipcp.state = IPCP_STATE_CLOSED;
/* Give it a moment for the line to settle then go */
sppp_set_timeout (sp, 1);
- }
+ }
+ sp->pp_link_state=SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_reopen);
+/**
+ * sppp_change_mtu - Change the link MTU
+ * @dev: Device to change MTU on
+ * @new_mtu: New MTU
+ *
+ * Change the MTU on the link. This can only be called with
+ * the link down. It returns an error if the link is up or
+ * the mtu is out of range.
+ */
+
int sppp_change_mtu(struct net_device *dev, int new_mtu)
{
if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
@@ -880,6 +939,18 @@ int sppp_change_mtu(struct net_device *dev, int new_mtu)
EXPORT_SYMBOL(sppp_change_mtu);
+/**
+ * sppp_do_ioctl - Ioctl handler for ppp/hdlc
+ * @dev: Device subject to ioctl
+ * @ifr: Interface request block from the user
+ * @cmd: Command that is being issued
+ *
+ * This function handles the ioctls that may be issued by the user
+ * to control the settings of a PPP/HDLC link. It does both busy
+ * and security checks. This function is intended to be wrapped by
+ * callers who wish to add additional ioctl calls of their own.
+ */
+
int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
@@ -913,6 +984,16 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
EXPORT_SYMBOL(sppp_do_ioctl);
+/**
+ * sppp_attach - attach synchronous PPP/HDLC to a device
+ * @pd: PPP device to initialise
+ *
+ * This initialises the PPP/HDLC support on an interface. At the
+ * time of calling the dev element must point to the network device
+ * that this interface is attached to. The interface should not yet
+ * be registered.
+ */
+
void sppp_attach(struct ppp_device *pd)
{
struct net_device *dev = pd->dev;
@@ -973,6 +1054,15 @@ void sppp_attach(struct ppp_device *pd)
EXPORT_SYMBOL(sppp_attach);
+/**
+ * sppp_detach - release PPP resources from a device
+ * @dev: Network device to release
+ *
+ * Stop and free up any PPP/HDLC resources used by this
+ * interface. This must be called before the device is
+ * freed.
+ */
+
void sppp_detach (struct net_device *dev)
{
struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
@@ -1187,7 +1277,7 @@ static void sppp_cp_timeout (unsigned long arg)
cli();
sp->pp_flags &= ~PP_TIMO;
- if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+ if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
restore_flags(flags);
return;
}
@@ -1273,18 +1363,24 @@ static void sppp_print_bytes (u_char *p, u16 len)
printk ("-%x", *p++);
}
-/*
+/**
+ * sppp_rcv - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ * @p: Unused
+ *
* Protocol glue. This drives the deferred processing mode the poorer
- * cards use.
+ * cards use. This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
*/
-int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
{
sppp_input(dev,skb);
return 0;
}
-EXPORT_SYMBOL(sppp_rcv);
struct packet_type sppp_packet_type=
{
@@ -1305,8 +1401,6 @@ void sync_ppp_init(void)
dev_add_pack(&sppp_packet_type);
}
-EXPORT_SYMBOL(sync_ppp_init);
-
#ifdef MODULE
int init_module(void)
diff --git a/drivers/net/wan/syncppp.h b/drivers/net/wan/syncppp.h
index 1e2056869..8f6ed5c1f 100644
--- a/drivers/net/wan/syncppp.h
+++ b/drivers/net/wan/syncppp.h
@@ -47,6 +47,7 @@ struct sppp
u32 ipkts,opkts; /* Packets in/out */
struct timer_list pp_timer;
struct net_device *pp_if;
+ char pp_link_state; /* Link status */
};
struct ppp_device
@@ -75,6 +76,9 @@ struct ppp_device
#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */
#define IPCP_STATE_OPENED 3 /* IPCP state: opened */
+#define SPPP_LINK_DOWN 0 /* link down - no keepalive */
+#define SPPP_LINK_UP 1 /* link is up - keepalive ok */
+
void sppp_attach (struct ppp_device *pd);
void sppp_detach (struct net_device *dev);
void sppp_input (struct net_device *dev, struct sk_buff *m);
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 2ff818b04..f1c618a90 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -55,7 +55,7 @@
static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
/**
- * z8530_read_port:
+ * z8530_read_port - Architecture specific interface function
* @p: port to read
*
* Provided port access methods. The Comtrol SV11 requires no delays
@@ -79,7 +79,7 @@ extern __inline__ int z8530_read_port(unsigned long p)
}
/**
- * z8530_write_port:
+ * z8530_write_port - Architecture specific interface function
* @p: port to write
* @d: value to write
*
@@ -108,7 +108,7 @@ static void z8530_tx_done(struct z8530_channel *c);
/**
- * read_zsreg:
+ * read_zsreg - Read a register from a Z85230
* @c: Z8530 channel to read from (2 per chip)
* @reg: Register to read
* FIXME: Use a spinlock.
@@ -133,7 +133,7 @@ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg)
}
/**
- * read_zsdata:
+ * read_zsdata - Read the data port of a Z8530 channel
* @c: The Z8530 channel to read the data port from
*
* The data port provides fast access to some things. We still
@@ -148,7 +148,7 @@ extern inline u8 read_zsdata(struct z8530_channel *c)
}
/**
- * write_zsreg:
+ * write_zsreg - Write to a Z8530 channel register
* @c: The Z8530 channel
* @reg: Register number
* @val: Value to write
@@ -169,11 +169,28 @@ extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val)
restore_flags(flags);
}
+/**
+ * write_zsctrl - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the control register on the Z8530
+ */
+
extern inline void write_zsctrl(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->ctrlio, val);
}
+/**
+ * write_zsdata - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the data register on the Z8530
+ */
+
+
extern inline void write_zsdata(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->dataio, val);
@@ -249,7 +266,7 @@ u8 z8530_hdlc_kilostream_85230[]=
EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);
/**
- * z8530_flush_fifo:
+ * z8530_flush_fifo - Flush on chip RX FIFO
* @c: Channel to flush
*
* Flush the receive FIFO. There is no specific option for this, we
@@ -276,8 +293,8 @@ static void z8530_flush_fifo(struct z8530_channel *c)
}
/**
- * z8530_rtsdtr:
- * @c: The Z8530 channel to contro;
+ * z8530_rtsdtr - Control the outgoing DTS/RTS line
+ * @c: The Z8530 channel to control;
* @set: 1 to set, 0 to clear
*
* Sets or clears DTR/RTS on the requested line. All locking is handled
@@ -296,7 +313,7 @@ static void z8530_rtsdtr(struct z8530_channel *c, int set)
}
/**
- * z8530_rx:
+ * z8530_rx - Handle a PIO receive event
* @c: Z8530 channel to process
*
* Receive handler for receiving in PIO mode. This is much like the
@@ -378,7 +395,7 @@ static void z8530_rx(struct z8530_channel *c)
/**
- * z8530_tx:
+ * z8530_tx - Handle a PIO transmit event
* @c: Z8530 channel to process
*
* Z8530 transmit interrupt handler for the PIO mode. The basic
@@ -421,7 +438,7 @@ static void z8530_tx(struct z8530_channel *c)
}
/**
- * z8530_status:
+ * z8530_status - Handle a PIO status exception
* @chan: Z8530 channel to process
*
* A status event occured in PIO synchronous mode. There are several
@@ -479,7 +496,7 @@ struct z8530_irqhandler z8530_sync=
EXPORT_SYMBOL(z8530_sync);
/**
- * z8530_dma_rx:
+ * z8530_dma_rx - Handle a DMA RX event
* @chan: Channel to handle
*
* Non bus mastering DMA interfaces for the Z8x30 devices. This
@@ -514,7 +531,7 @@ static void z8530_dma_rx(struct z8530_channel *chan)
}
/**
- * z8530_dma_tx:
+ * z8530_dma_tx - Handle a DMA TX event
* @chan: The Z8530 channel to handle
*
* We have received an interrupt while doing DMA transmissions. It
@@ -525,17 +542,17 @@ static void z8530_dma_tx(struct z8530_channel *chan)
{
if(!chan->dma_tx)
{
- printk("Hey who turned the DMA off?\n");
+ printk(KERN_WARNING "Hey who turned the DMA off?\n");
z8530_tx(chan);
return;
}
/* This shouldnt occur in DMA mode */
- printk(KERN_ERR "DMA tx ??\n");
+ printk(KERN_ERR "DMA tx - bogus event!\n");
z8530_tx(chan);
}
/**
- * z8530_dma_status:
+ * z8530_dma_status - Handle a DMA status exception
* @chan: Z8530 channel to process
*
* A status event occured on the Z8530. We receive these for two reasons
@@ -606,7 +623,7 @@ struct z8530_irqhandler z8530_txdma_sync=
EXPORT_SYMBOL(z8530_txdma_sync);
/**
- * z8530_rx_clear:
+ * z8530_rx_clear - Handle RX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Receive interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -635,7 +652,7 @@ static void z8530_rx_clear(struct z8530_channel *c)
}
/**
- * z8530_tx_clear:
+ * z8530_tx_clear - Handle TX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Transmit interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -650,7 +667,7 @@ static void z8530_tx_clear(struct z8530_channel *c)
}
/**
- * z8530_status_clear:
+ * z8530_status_clear - Handle status events from a stopped chip
* @chan: Z8530 channel to shut up
*
* Status interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -678,7 +695,7 @@ struct z8530_irqhandler z8530_nop=
EXPORT_SYMBOL(z8530_nop);
/**
- * z8530_interrupt:
+ * z8530_interrupt - Handle an interrupt from a Z8530
* @irq: Interrupt number
* @dev_id: The Z8530 device that is interrupting.
* @regs: unused
@@ -758,7 +775,7 @@ static char reg_init[16]=
/**
- * z8530_sync_open:
+ * z8530_sync_open - Open a Z8530 channel for PIO
* @dev: The network interface we are using
* @c: The Z8530 channel to open in synchronous PIO mode
*
@@ -789,7 +806,7 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_open);
/**
- * z8530_sync_close:
+ * z8530_sync_close - Close a PIO Z8530 channel
* @dev: Network device to close
* @c: Z8530 channel to disassociate and move to idle
*
@@ -814,7 +831,7 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_close);
/**
- * z8530_sync_dma_open:
+ * z8530_sync_dma_open - Open a Z8530 for DMA I/O
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -934,7 +951,7 @@ int z8530_sync_dma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_open);
/**
- * z8530_sync_dma_close:
+ * z8530_sync_dma_close - Close down DMA I/O
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -999,7 +1016,7 @@ int z8530_sync_dma_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_close);
/**
- * z8530_sync_txdma_open:
+ * z8530_sync_txdma_open - Open a Z8530 for TX driven DMA
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -1099,7 +1116,7 @@ int z8530_sync_txdma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_txdma_open);
/**
- * z8530_sync_txdma_close:
+ * z8530_sync_txdma_close - Close down a TX driven DMA channel
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -1168,7 +1185,7 @@ static char *z8530_type_name[]={
};
/**
- * z8530_describe:
+ * z8530_describe - Uniformly describe a Z8530 port
* @dev: Z8530 device to describe
* @mapping: string holding mapping type (eg "I/O" or "Mem")
* @io: the port value in question
@@ -1191,7 +1208,7 @@ void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io)
EXPORT_SYMBOL(z8530_describe);
/**
- * z8530_init:
+ * z8530_init - Initialise a Z8530 device
* @dev: Z8530 device to initialise.
*
* Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
@@ -1273,7 +1290,7 @@ int z8530_init(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_init);
/**
- * z8530_shutdown:
+ * z8530_shutdown - Shutdown a Z8530 device
* @dev: The Z8530 chip to shutdown
*
* We set the interrupt handlers to silence any interrupts. We then
@@ -1294,7 +1311,7 @@ int z8530_shutdown(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_shutdown);
/**
- * z8530_channel_load:
+ * z8530_channel_load - Load channel data
* @c: Z8530 channel to configure
* @rtable: Table of register, value pairs
* FIXME: ioctl to allow user uploaded tables
@@ -1333,7 +1350,7 @@ EXPORT_SYMBOL(z8530_channel_load);
/**
- * z8530_tx_begin:
+ * z8530_tx_begin - Begin packet transmission
* @c: The Z8530 channel to kick
*
* This is the speed sensitive side of transmission. If we are called
@@ -1430,7 +1447,7 @@ static void z8530_tx_begin(struct z8530_channel *c)
}
/**
- * z8530_tx_done:
+ * z8530_tx_done - TX complete callback
* @c: The channel that completed a transmit.
*
* This is called when we complete a packet send. We wake the queue,
@@ -1461,7 +1478,7 @@ static void z8530_tx_done(struct z8530_channel *c)
}
/**
- * z8530_null_rx:
+ * z8530_null_rx - Discard a packet
* @c: The channel the packet arrived on
* @skb: The buffer
*
@@ -1477,7 +1494,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_null_rx);
/**
- * z8530_rx_done:
+ * z8530_rx_done - Receive completion callback
* @c: The channel that completed a receive
*
* A new packet is complete. Our goal here is to get back into receive
@@ -1630,7 +1647,7 @@ static void z8530_rx_done(struct z8530_channel *c)
}
/**
- * spans_boundary:
+ * spans_boundary - Check a packet can be ISA DMA'd
* @skb: The buffer to check
*
* Returns true if the buffer cross a DMA boundary on a PC. The poor
@@ -1642,15 +1659,12 @@ extern inline int spans_boundary(struct sk_buff *skb)
unsigned long a=(unsigned long)skb->data;
a^=(a+skb->len);
if(a&0x00010000) /* If the 64K bit is different.. */
- {
- printk("spanner\n");
return 1;
- }
return 0;
}
/**
- * z8530_queue_xmit:
+ * z8530_queue_xmit - Queue a packet
* @c: The channel to use
* @skb: The packet to kick down the channel
*
@@ -1707,7 +1721,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_queue_xmit);
/**
- * z8530_get_stats:
+ * z8530_get_stats - Get network statistics
* @c: The channel to use
*
* Get the statistics block. We keep the statistics in software as
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
index 9fc75e5e6..2162d5ff0 100644
--- a/drivers/net/wavelan.c
+++ b/drivers/net/wavelan.c
@@ -1990,7 +1990,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
}
/* only super-user can see encryption key */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2224,7 +2224,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
/* ------------------ PRIVATE IOCTL ------------------ */
case SIOCSIPQTHR:
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2248,7 +2248,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
#ifdef HISTOGRAM
case SIOCSIPHISTO:
/* Verify that the user is root. */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
diff --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h
index f55bf48e2..38534c934 100644
--- a/drivers/net/wavelan.h
+++ b/drivers/net/wavelan.h
@@ -26,7 +26,7 @@
* product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
* you might need to modify this part to accommodate your hardware.
*/
-const char MAC_ADDRESSES[][3] =
+static const char MAC_ADDRESSES[][3] =
{
{ 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */
{ 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */
@@ -49,14 +49,14 @@ const char MAC_ADDRESSES[][3] =
* (as read in the offset register of the dac area).
* Used to map channel numbers used by `wfreqsel' to frequencies
*/
-const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
0xD0, 0xF0, 0xF8, 0x150 };
/* Frequencies of the 1.0 modem (fixed frequencies).
* Use to map the PSA `subband' to a frequency
* Note : all frequencies apart from the first one need to be multiplied by 10
*/
-const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 85febe47d..f28feea43 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -77,7 +77,6 @@ static int gx_fix = 0;
#include <linux/version.h>
#include <linux/module.h>
-#include <linux/modversions.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog
index 7429ec38b..8d01df21e 100644
--- a/drivers/parport/ChangeLog
+++ b/drivers/parport/ChangeLog
@@ -1,3 +1,24 @@
+2000-03-13 <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_init): Moved from asm/parport.h.
+
+ * Config.in: CONFIG_PARPORT_PC_SUPERIO: new option.
+
+ * parport_pc.c (show_parconfig_smsc37c669): Make __devinit.
+ (show_parconfig_winbond): Likewise.
+ (decode_winbond): Likewise.
+ (decode_smsc): Likewise.
+ (winbond_check): Likewise.
+ (winbond_check2): Likewise.
+ (smsc_check): Likewise.
+ (detect_and_report_winbond): Likewise.
+ (detect_and_report_smsc): Likewise.
+ (get_superio_dma): Likewise.
+ (get_superio_irq): Likewise.
+ (parport_pc_find_isa_ports): New function.
+ (parport_pc_find_ports): New function.
+ (init_module): Make superio a config option, not a parameter.
+
2000-03-10 <twaugh@redhat.com>
* parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
diff --git a/drivers/parport/Config.in b/drivers/parport/Config.in
index d4222f353..1e486c6b3 100644
--- a/drivers/parport/Config.in
+++ b/drivers/parport/Config.in
@@ -13,6 +13,9 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
if [ "$CONFIG_PARPORT_PC" != "n" ]; then
bool ' Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO
+ fi
fi
if [ "$CONFIG_PARPORT_PC" = "y" ]; then
# Don't bother with this if parport_pc is a module; it only affects
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 86fc6f874..7117c847e 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -60,6 +60,8 @@
#include <linux/parport_pc.h>
#include <asm/parport.h>
+#define PARPORT_PC_MAX_PORTS PARPORT_MAX
+
/* ECR modes */
#define ECR_SPP 00
#define ECR_PS2 01
@@ -84,8 +86,10 @@ static struct superio_struct { /* For Super-IO chips autodetection */
int io;
int irq;
int dma;
-} superios[NR_SUPERIOS]= { {0,},};
-
+} superios[NR_SUPERIOS] __devinitdata = { {0,},};
+
+static int user_specified __devinitdata = 0;
+
/* frob_control, but for ECR */
static void frob_econtrol (struct parport *pb, unsigned char m,
unsigned char v)
@@ -1015,9 +1019,9 @@ struct parport_operations parport_pc_ops =
parport_ieee1284_read_byte,
};
+#ifdef CONFIG_PARPORT_PC_SUPERIO
/* Super-IO chipset detection, Winbond, SMSC */
-
-static void show_parconfig_smsc37c669(int io, int key)
+static void __devinit show_parconfig_smsc37c669(int io, int key)
{
int cr1,cr4,cra,cr23,cr26,cr27,i=0;
char *modes[]={ "SPP and Bidirectional (PS/2)",
@@ -1092,7 +1096,7 @@ static void show_parconfig_smsc37c669(int io, int key)
}
-static void show_parconfig_winbond(int io, int key)
+static void __devinit show_parconfig_winbond(int io, int key)
{
int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
@@ -1152,7 +1156,7 @@ static void show_parconfig_winbond(int io, int key)
}
}
-static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
{
char *type=NULL;
int id,progif=2;
@@ -1188,7 +1192,7 @@ static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
return;
}
-static void decode_smsc(int efer, int key, int devid, int devrev)
+static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
{
char *type=NULL;
void (*func)(int io, int key);
@@ -1219,7 +1223,7 @@ static void decode_smsc(int efer, int key, int devid, int devrev)
}
-static void winbond_check(int io, int key)
+static void __devinit winbond_check(int io, int key)
{
int devid,devrev,oldid;
@@ -1237,7 +1241,7 @@ static void winbond_check(int io, int key)
decode_winbond(io,key,devid,devrev,oldid);
}
-static void winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io,int key)
{
int devid,devrev,oldid;
@@ -1254,7 +1258,7 @@ static void winbond_check2(int io,int key)
decode_winbond(io,key,devid,devrev,oldid);
}
-static void smsc_check(int io, int key)
+static void __devinit smsc_check(int io, int key)
{
int devid,devrev;
@@ -1271,7 +1275,7 @@ static void smsc_check(int io, int key)
}
-static void detect_and_report_winbond (void)
+static void __devinit detect_and_report_winbond (void)
{
printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
@@ -1284,7 +1288,7 @@ static void detect_and_report_winbond (void)
winbond_check2(0x250,0x89);
}
-static void detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc (void)
{
printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
smsc_check(0x3f0,0x55);
@@ -1292,8 +1296,9 @@ static void detect_and_report_smsc (void)
smsc_check(0x3f0,0x44);
smsc_check(0x370,0x44);
}
+#endif /* CONFIG_PARPORT_PC_SUPERIO */
-static int get_superio_dma (struct parport *p)
+static int __devinit get_superio_dma (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -1303,7 +1308,7 @@ static int get_superio_dma (struct parport *p)
return PARPORT_DMA_NONE;
}
-static int get_superio_irq (struct parport *p)
+static int __devinit get_superio_irq (struct parport *p)
{
int i=0;
while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -2330,7 +2335,7 @@ static struct pci_driver parport_pc_pci_driver = {
probe: parport_pc_pci_probe,
};
-static int __devinit parport_pc_init_superio (void)
+static int __init parport_pc_init_superio (void)
{
#ifdef CONFIG_PCI
const struct pci_device_id *id;
@@ -2348,6 +2353,74 @@ static int __devinit parport_pc_init_superio (void)
return 0; /* zero devices found */
}
+/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
+static int __init __attribute__((unused))
+parport_pc_find_isa_ports (int autoirq, int autodma)
+{
+ int count = 0;
+
+ if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
+ count++;
+ if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL))
+ count++;
+
+ return count;
+}
+
+/* This function is called by parport_pc_init if the user didn't
+ * specify any ports to probe. Its job is to find some ports. Order
+ * is important here -- we want ISA ports to be registered first,
+ * followed by PCI cards (for least surprise), but before that we want
+ * to do chipset-specific tests for some onboard ports that we know
+ * about.
+ *
+ * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
+ * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
+ */
+static int __init parport_pc_find_ports (int autoirq, int autodma)
+{
+ int count = 0, r;
+
+#ifdef CONFIG_PARPORT_PC_SUPERIO
+ detect_and_report_winbond ();
+ detect_and_report_smsc ();
+#endif
+
+ /* Onboard SuperIO chipsets that show themselves on the PCI bus. */
+ count += parport_pc_init_superio ();
+
+ /* ISA ports and whatever (see asm/parport.h). */
+ count += parport_pc_find_nonpci_ports (autoirq, autodma);
+
+ r = pci_register_driver (&parport_pc_pci_driver);
+ if (r > 0)
+ count += r;
+
+ return count;
+}
+
+int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
+{
+ int count = 0, i = 0;
+
+ if (io && *io) {
+ /* Only probe the ports we were given. */
+ user_specified = 1;
+ do {
+ if (!*io_hi) *io_hi = 0x400 + *io;
+ if (parport_pc_probe_port(*(io++), *(io_hi++),
+ *(irq++), *(dma++), NULL))
+ count++;
+ } while (*io && (++i < PARPORT_PC_MAX_PORTS));
+ } else {
+ count += parport_pc_find_ports (irq[0], dma[0]);
+ }
+
+ return count;
+}
+
/* Exported symbols. */
#ifdef CONFIG_PARPORT_PC_PCMCIA
@@ -2367,7 +2440,6 @@ static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PAR
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-static int superio = 0;
MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
MODULE_DESCRIPTION("PC-style parallel port driver");
@@ -2379,18 +2451,12 @@ MODULE_PARM_DESC(irq, "IRQ line");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
MODULE_PARM_DESC(dma, "DMA channel");
MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
-MODULE_PARM(superio, "i");
int init_module(void)
{
/* Work out how many ports we have, then get parport_share to parse
the irq values. */
- unsigned int i, n;
- if (superio) {
- detect_and_report_winbond ();
- detect_and_report_smsc ();
- }
+ unsigned int i;
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
if (i) {
if (parport_parse_irqs(i, irq, irqval)) return 1;
@@ -2415,12 +2481,7 @@ int init_module(void)
}
}
- n = parport_pc_init_superio ();
- n += parport_pc_init (io, io_hi, irqval, dmaval);
- i = pci_register_driver (&parport_pc_pci_driver);
-
- if (i > 0) n += i;
- return !n;
+ return !parport_pc_init (io, io_hi, irqval, dmaval);
}
void cleanup_module(void)
diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c
index e517fbf27..c57fdfcc6 100644
--- a/drivers/pcmcia/yenta.c
+++ b/drivers/pcmcia/yenta.c
@@ -498,7 +498,7 @@ static int yenta_socket_thread(void * data)
return 0;
}
-static unsigned int yenta_probe_irq(pci_socket_t *socket)
+static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask)
{
int i;
unsigned long val;
@@ -518,7 +518,7 @@ static unsigned int yenta_probe_irq(pci_socket_t *socket)
*/
cb_writel(socket, CB_SOCKET_EVENT, -1);
cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
- val = probe_irq_on();
+ val = probe_irq_on() & isa_irq_mask;
for (i = 1; i < 16; i++) {
if (!((val >> i) & 1))
continue;
@@ -647,12 +647,12 @@ static int yenta_suspend(pci_socket_t *socket)
/*
* Set static data that doesn't need re-initializing..
*/
-static void yenta_get_socket_capabilities(pci_socket_t *socket)
+static void yenta_get_socket_capabilities(pci_socket_t *socket, u32 isa_irq_mask)
{
socket->cap.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
socket->cap.map_size = 0x1000;
socket->cap.pci_irq = socket->cb_irq;
- socket->cap.irq_mask = yenta_probe_irq(socket);
+ socket->cap.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
socket->cap.cb_dev = socket->dev;
socket->cap.bus = NULL;
@@ -752,6 +752,17 @@ static struct cardbus_override_struct {
#define NR_OVERRIDES (sizeof(cardbus_override)/sizeof(struct cardbus_override_struct))
/*
+ * Only probe "regular" interrupts, don't
+ * touch dangerous spots like the mouse irq,
+ * because there are mice that apparently
+ * get really confused if they get fondled
+ * too intimately.
+ *
+ * Default to 11, 10, 9, 7, 6, 5, 4, 3.
+ */
+static u32 isa_interrupts = 0x0ef8;
+
+/*
* Initialize a cardbus controller. Make sure we have a usable
* interrupt, and that we can map the cardbus area. Fill in the
* socket information structure..
@@ -790,9 +801,6 @@ static int yenta_open(pci_socket_t *socket)
if (dev->irq && !request_irq(dev->irq, yenta_interrupt, SA_SHIRQ, dev->name, socket))
socket->cb_irq = dev->irq;
- /* And figure out what the dang thing can do for the PCMCIA layer... */
- yenta_get_socket_capabilities(socket);
-
/* Do we have special options for the device? */
for (i = 0; i < NR_OVERRIDES; i++) {
struct cardbus_override_struct *d = cardbus_override+i;
@@ -806,6 +814,9 @@ static int yenta_open(pci_socket_t *socket)
}
}
+ /* Figure out what the dang thing can do for the PCMCIA layer... */
+ yenta_get_socket_capabilities(socket, isa_interrupts);
+
kernel_thread(yenta_socket_thread, socket, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
printk("Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
return 0;
diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c
index 01c76073f..4c7e8003c 100644
--- a/drivers/sbus/audio/audio.c
+++ b/drivers/sbus/audio/audio.c
@@ -1,4 +1,4 @@
-/* $Id: audio.c,v 1.49 2000/02/17 05:52:41 davem Exp $
+/* $Id: audio.c,v 1.50 2000/03/13 03:54:07 davem Exp $
* drivers/sbus/audio/audio.c
*
* Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
@@ -70,7 +70,8 @@ static void lis_free_elist( strevent_t **list);
static void kill_procs( struct strevent *elist, int sig, short e);
static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
-
+static devfs_handle_t devfs_handle = NULL;
+
/* This crap to be pulled off into a local include file */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
@@ -92,185 +93,6 @@ static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
#endif
-int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
- int i, dev;
-
- /* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
- for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
- if (drivers[dev] == NULL)
- break;
- }
-
- if (drivers[dev])
- return -EIO;
-
- /* Ensure that the driver has a proper operations structure. */
- if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
- !drv->ops->start_input || !drv->ops->stop_input)
- return -EINVAL;
-
- /* Setup the circular queues of output and input buffers
- *
- * Each buffer is a single page, but output buffers might
- * be partially filled (by a write with count < output_buffer_size),
- * so each output buffer also has a paired output size.
- *
- * Input buffers, on the other hand, always fill completely,
- * so we don't need input counts - each contains input_buffer_size
- * bytes of audio data.
- *
- * TODO: Make number of input/output buffers tunable parameters
- */
-
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
- init_waitqueue_head(&drv->open_wait);
- init_waitqueue_head(&drv->output_write_wait);
- init_waitqueue_head(&drv->output_drain_wait);
- init_waitqueue_head(&drv->input_read_wait);
-#endif
-
- drv->num_output_buffers = 8;
- drv->output_buffer_size = (4096 * 2);
- drv->playing_count = 0;
- drv->output_offset = 0;
- drv->output_eof = 0;
- drv->output_front = 0;
- drv->output_rear = 0;
- drv->output_count = 0;
- drv->output_active = 0;
- drv->output_buffers = kmalloc(drv->num_output_buffers *
- sizeof(__u8 *), GFP_KERNEL);
- drv->output_sizes = kmalloc(drv->num_output_buffers *
- sizeof(size_t), GFP_KERNEL);
- drv->output_notify = kmalloc(drv->num_output_buffers *
- sizeof(char), GFP_KERNEL);
- if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
- goto kmalloc_failed1;
-
- drv->output_buffer = kmalloc((drv->output_buffer_size *
- drv->num_output_buffers),
- GFP_KERNEL);
- if (!drv->output_buffer)
- goto kmalloc_failed2;
-
- /* Allocate the pages for each output buffer. */
- for (i = 0; i < drv->num_output_buffers; i++) {
- drv->output_buffers[i] = (void *)(drv->output_buffer +
- (i * drv->output_buffer_size));
- drv->output_sizes[i] = 0;
- drv->output_notify[i] = 0;
- }
-
- /* Setup the circular queue of input buffers. */
- drv->num_input_buffers = 8;
- drv->input_buffer_size = (4096 * 2);
- drv->recording_count = 0;
- drv->input_front = 0;
- drv->input_rear = 0;
- drv->input_count = 0;
- drv->input_offset = 0;
- drv->input_size = 0;
- drv->input_active = 0;
- drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
- GFP_KERNEL);
- drv->input_sizes = kmalloc(drv->num_input_buffers *
- sizeof(size_t), GFP_KERNEL);
- if (!drv->input_buffers || !drv->input_sizes)
- goto kmalloc_failed3;
-
- /* Allocate the pages for each input buffer. */
- if (duplex == 1) {
- drv->input_buffer = kmalloc((drv->input_buffer_size *
- drv->num_input_buffers),
- GFP_DMA);
- if (!drv->input_buffer)
- goto kmalloc_failed4;
-
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = (void *)(drv->input_buffer +
- (i * drv->input_buffer_size));
- } else {
- if (duplex == 2) {
- drv->input_buffer = drv->output_buffer;
- drv->input_buffer_size = drv->output_buffer_size;
- drv->num_input_buffers = drv->num_output_buffers;
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = drv->output_buffers[i];
- } else {
- for (i = 0; i < drv->num_input_buffers; i++)
- drv->input_buffers[i] = NULL;
- }
- }
-
- /* Take note of our duplexity */
- drv->duplex = duplex;
-
- /* Ensure that the driver is marked as not being open. */
- drv->flags = 0;
-
- MOD_INC_USE_COUNT;
-
- /* Take driver slot, note which we took */
- drv->index = dev;
- drivers[dev] = drv;
-
- return 0;
-
-kmalloc_failed4:
- kfree(drv->input_buffer);
-
-kmalloc_failed3:
- if (drv->input_sizes)
- kfree(drv->input_sizes);
- if (drv->input_buffers)
- kfree(drv->input_buffers);
- i = drv->num_output_buffers;
-
-kmalloc_failed2:
- kfree(drv->output_buffer);
-
-kmalloc_failed1:
- if (drv->output_buffers)
- kfree(drv->output_buffers);
- if (drv->output_sizes)
- kfree(drv->output_sizes);
- if (drv->output_notify)
- kfree(drv->output_notify);
-
- return -ENOMEM;
-}
-
-int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
-{
- /* Figure out which driver is unregistering */
- if (drivers[drv->index] != drv)
- return -EIO;
-
- /* Deallocate the queue of output buffers. */
- kfree(drv->output_buffer);
- kfree(drv->output_buffers);
- kfree(drv->output_sizes);
- kfree(drv->output_notify);
-
- /* Deallocate the queue of input buffers. */
- if (duplex == 1) {
- kfree(drv->input_buffer);
- kfree(drv->input_sizes);
- }
- kfree(drv->input_buffers);
-
- if (&(drv->sd_siglist) != NULL)
- lis_free_elist( &(drv->sd_siglist) );
-
- MOD_DEC_USE_COUNT;
-
- /* Null the appropriate driver */
- drivers[drv->index] = NULL;
-
- return 0;
-}
-
void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
{
/* If !status, just restart current output.
@@ -2171,6 +1993,229 @@ static struct file_operations sparcaudio_fops = {
release: sparcaudio_release,
};
+static struct {
+ unsigned short minor;
+ char *name;
+ umode_t mode;
+} dev_list[] = {
+ { SPARCAUDIO_MIXER_MINOR, "mixer", S_IWUSR | S_IRUGO },
+ { SPARCAUDIO_DSP_MINOR, "dsp", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_AUDIO_MINOR, "audio", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_DSP16_MINOR, "dspW", S_IWUGO | S_IRUSR | S_IRGRP },
+ { SPARCAUDIO_STATUS_MINOR, "status", S_IRUGO },
+ { SPARCAUDIO_AUDIOCTL_MINOR, "audioctl", S_IRUGO }
+};
+
+static void sparcaudio_mkname (char *buf, char *name, int dev)
+{
+ if (dev)
+ sprintf (buf, "%s%d", name, dev);
+ else
+ sprintf (buf, "%s", name);
+}
+
+int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+ int i, dev;
+ unsigned short minor;
+ char name_buf[32];
+
+ /* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
+ for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
+ if (drivers[dev] == NULL)
+ break;
+ }
+
+ if (drivers[dev])
+ return -EIO;
+
+ /* Ensure that the driver has a proper operations structure. */
+ if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
+ !drv->ops->start_input || !drv->ops->stop_input)
+ return -EINVAL;
+
+ /* Register ourselves with devfs */
+ for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+ sparcaudio_mkname (name_buf, dev_list[i].name, dev);
+ minor = (dev << SPARCAUDIO_DEVICE_SHIFT) | dev_list[i].minor;
+ devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
+ SOUND_MAJOR, minor, S_IFCHR | dev_list[i].mode,
+ 0, 0, &sparcaudio_fops, NULL);
+ }
+
+ /* Setup the circular queues of output and input buffers
+ *
+ * Each buffer is a single page, but output buffers might
+ * be partially filled (by a write with count < output_buffer_size),
+ * so each output buffer also has a paired output size.
+ *
+ * Input buffers, on the other hand, always fill completely,
+ * so we don't need input counts - each contains input_buffer_size
+ * bytes of audio data.
+ *
+ * TODO: Make number of input/output buffers tunable parameters
+ */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
+ init_waitqueue_head(&drv->open_wait);
+ init_waitqueue_head(&drv->output_write_wait);
+ init_waitqueue_head(&drv->output_drain_wait);
+ init_waitqueue_head(&drv->input_read_wait);
+#endif
+
+ drv->num_output_buffers = 8;
+ drv->output_buffer_size = (4096 * 2);
+ drv->playing_count = 0;
+ drv->output_offset = 0;
+ drv->output_eof = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_active = 0;
+ drv->output_buffers = kmalloc(drv->num_output_buffers *
+ sizeof(__u8 *), GFP_KERNEL);
+ drv->output_sizes = kmalloc(drv->num_output_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ drv->output_notify = kmalloc(drv->num_output_buffers *
+ sizeof(char), GFP_KERNEL);
+ if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
+ goto kmalloc_failed1;
+
+ drv->output_buffer = kmalloc((drv->output_buffer_size *
+ drv->num_output_buffers),
+ GFP_KERNEL);
+ if (!drv->output_buffer)
+ goto kmalloc_failed2;
+
+ /* Allocate the pages for each output buffer. */
+ for (i = 0; i < drv->num_output_buffers; i++) {
+ drv->output_buffers[i] = (void *)(drv->output_buffer +
+ (i * drv->output_buffer_size));
+ drv->output_sizes[i] = 0;
+ drv->output_notify[i] = 0;
+ }
+
+ /* Setup the circular queue of input buffers. */
+ drv->num_input_buffers = 8;
+ drv->input_buffer_size = (4096 * 2);
+ drv->recording_count = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_offset = 0;
+ drv->input_size = 0;
+ drv->input_active = 0;
+ drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
+ GFP_KERNEL);
+ drv->input_sizes = kmalloc(drv->num_input_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ if (!drv->input_buffers || !drv->input_sizes)
+ goto kmalloc_failed3;
+
+ /* Allocate the pages for each input buffer. */
+ if (duplex == 1) {
+ drv->input_buffer = kmalloc((drv->input_buffer_size *
+ drv->num_input_buffers),
+ GFP_DMA);
+ if (!drv->input_buffer)
+ goto kmalloc_failed4;
+
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = (void *)(drv->input_buffer +
+ (i * drv->input_buffer_size));
+ } else {
+ if (duplex == 2) {
+ drv->input_buffer = drv->output_buffer;
+ drv->input_buffer_size = drv->output_buffer_size;
+ drv->num_input_buffers = drv->num_output_buffers;
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = drv->output_buffers[i];
+ } else {
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = NULL;
+ }
+ }
+
+ /* Take note of our duplexity */
+ drv->duplex = duplex;
+
+ /* Ensure that the driver is marked as not being open. */
+ drv->flags = 0;
+
+ MOD_INC_USE_COUNT;
+
+ /* Take driver slot, note which we took */
+ drv->index = dev;
+ drivers[dev] = drv;
+
+ return 0;
+
+kmalloc_failed4:
+ kfree(drv->input_buffer);
+
+kmalloc_failed3:
+ if (drv->input_sizes)
+ kfree(drv->input_sizes);
+ if (drv->input_buffers)
+ kfree(drv->input_buffers);
+ i = drv->num_output_buffers;
+
+kmalloc_failed2:
+ kfree(drv->output_buffer);
+
+kmalloc_failed1:
+ if (drv->output_buffers)
+ kfree(drv->output_buffers);
+ if (drv->output_sizes)
+ kfree(drv->output_sizes);
+ if (drv->output_notify)
+ kfree(drv->output_notify);
+
+ return -ENOMEM;
+}
+
+int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
+{
+ devfs_handle_t de;
+ int i;
+ char name_buf[32];
+
+ /* Figure out which driver is unregistering */
+ if (drivers[drv->index] != drv)
+ return -EIO;
+
+ /* Deallocate the queue of output buffers. */
+ kfree(drv->output_buffer);
+ kfree(drv->output_buffers);
+ kfree(drv->output_sizes);
+ kfree(drv->output_notify);
+
+ /* Deallocate the queue of input buffers. */
+ if (duplex == 1) {
+ kfree(drv->input_buffer);
+ kfree(drv->input_sizes);
+ }
+ kfree(drv->input_buffers);
+
+ if (&(drv->sd_siglist) != NULL)
+ lis_free_elist( &(drv->sd_siglist) );
+
+ /* Unregister ourselves with devfs */
+ for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) {
+ sparcaudio_mkname (name_buf, dev_list[i].name, drv->index);
+ de = devfs_find_handle (devfs_handle, name_buf, 0, 0, 0,
+ DEVFS_SPECIAL_CHR, 0);
+ devfs_unregister (de);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ /* Null the appropriate driver */
+ drivers[drv->index] = NULL;
+
+ return 0;
+}
+
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static struct symbol_table sparcaudio_syms = {
#include <linux/symtab_begin.h>
@@ -2201,6 +2246,8 @@ int __init sparcaudio_init(void)
/* Register our character device driver with the VFS. */
if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
return -EIO;
+
+ devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL);
#ifdef CONFIG_SPARCAUDIO_AMD7930
amd7930_init();
@@ -2222,6 +2269,7 @@ int __init sparcaudio_init(void)
void cleanup_module(void)
{
devfs_unregister_chrdev(SOUND_MAJOR, "sparcaudio");
+ devfs_unregister (devfs_handle);
}
#endif
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
index 220dc62f7..b2fc1ffc3 100644
--- a/drivers/sbus/char/sab82532.c
+++ b/drivers/sbus/char/sab82532.c
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.40 1999/12/19 23:28:08 davem Exp $
+/* $Id: sab82532.c,v 1.41 2000/03/13 03:54:17 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -2163,7 +2163,7 @@ static void __init sab82532_kgdb_hook(int line)
static inline void __init show_serial_version(void)
{
- char *revision = "$Revision: 1.40 $";
+ char *revision = "$Revision: 1.41 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2196,7 +2196,7 @@ int __init sab82532_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
+ serial_driver.name = "tts/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + su_num_ports;
serial_driver.num = NR_PORTS;
@@ -2236,7 +2236,7 @@ int __init sab82532_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c
index 6e30f9b58..a691db430 100644
--- a/drivers/sbus/char/su.c
+++ b/drivers/sbus/char/su.c
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.36 2000/02/09 21:11:22 davem Exp $
+/* $Id: su.c,v 1.37 2000/03/13 03:54:15 davem Exp $
* su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -2223,7 +2223,7 @@ done:
*/
static __inline__ void __init show_su_version(void)
{
- char *revision = "$Revision: 1.36 $";
+ char *revision = "$Revision: 1.37 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2442,7 +2442,7 @@ int __init su_serial_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "su";
- serial_driver.name = "ttyS";
+ serial_driver.name = "ttys/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NR_PORTS;
@@ -2482,7 +2482,7 @@ int __init su_serial_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
index b08b73e18..868193747 100644
--- a/drivers/sbus/char/zs.c
+++ b/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.55 2000/02/09 21:11:24 davem Exp $
+/* $Id: zs.c,v 1.56 2000/03/12 04:02:11 davem Exp $
* zs.c: Zilog serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1928,7 +1928,7 @@ int zs_open(struct tty_struct *tty, struct file * filp)
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.55 $";
+ char *revision = "$Revision: 1.56 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2415,7 +2415,7 @@ int __init zs_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
- serial_driver.name = "ttyS";
+ serial_driver.name = "tts/%d";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NUM_CHANNELS;
@@ -2454,7 +2454,7 @@ int __init zs_init(void)
* major number and the subtype code.
*/
callout_driver = serial_driver;
- callout_driver.name = "cua";
+ callout_driver.name = "cua/%d";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx
index f884dfb1e..8f4d4f19c 100644
--- a/drivers/scsi/ChangeLog.ncr53c8xx
+++ b/drivers/scsi/ChangeLog.ncr53c8xx
@@ -1,3 +1,20 @@
+Mon March 6 23:15 2000 Gerard Roudier (groudier@club-internet.fr)
+ * revision 3.2g
+ - Add the file sym53c8xx_comm.h that collects code that should
+ be shared by sym53c8xx and ncr53c8xx drivers. For now, it is
+ a header file that is only included by the ncr53c8xx driver,
+ but things will be cleaned up later. This code addresses
+ notably:
+ * Chip detection and PCI related initialisations
+ * NVRAM detection and reading
+ * DMA mapping
+ * Boot setup command
+ * And some other ...
+ - Add support for the new dynamic dma mapping kernel interface.
+ Requires Linux-2.3.47 (tested with pre-2.3.47-6).
+ - Get data transfer direction from the scsi command structure
+ (Scsi_Cmnd) when this information is available.
+
Sat Jan 8 22:00 2000 Gerard Roudier (groudier@club-internet.fr)
* revision 3.2e
- Add year 2000 copyright.
diff --git a/drivers/scsi/ChangeLog.sym53c8xx b/drivers/scsi/ChangeLog.sym53c8xx
index 4abf98759..47448ec71 100644
--- a/drivers/scsi/ChangeLog.sym53c8xx
+++ b/drivers/scsi/ChangeLog.sym53c8xx
@@ -1,3 +1,12 @@
+Mon Mar 6 23:30 2000 Gerard Roudier (groudier@club-internet.fr)
+ * version sym53c8xx-1.5k
+ - Test against expected data transfer direction from SCRIPTS.
+ - Revert the change in 'ncr_flush_done_cmds()' but unmap the
+ scsi dma buffer prior to queueing the command to our done
+ list.
+ - Miscellaneous (minor) fixes in the code added in driver
+ version 1.5j.
+
Sun Feb 20 11:00 2000 Gerard Roudier (groudier@club-internet.fr)
* version sym53c8xx-1.5j
- Add support for the new dynamic dma mapping kernel interface.
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index 4b68450e4..6eee76033 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -8,10 +8,6 @@ fi
dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
-if [ "$CONFIG_CHR_DEV_ST" != "n" ]; then
- int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
-fi
-
dep_tristate ' SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
diff --git a/drivers/scsi/README.st b/drivers/scsi/README.st
index b1b6362f3..77121aa0b 100644
--- a/drivers/scsi/README.st
+++ b/drivers/scsi/README.st
@@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver.
The driver is currently maintained by Kai M{kisara (email
Kai.Makisara@metla.fi)
-Last modified: Sat Aug 7 13:52:16 1999 by makisara@kai.makisara.local
+Last modified: Sat Mar 11 10:34:44 2000 by makisara@kai.makisara.local
BASICS
@@ -134,11 +134,7 @@ A small number of buffers are allocated at driver initialisation. The
maximum number of these buffers is defined by ST_MAX_BUFFERS. The
maximum can be changed with kernel or module startup options. One
buffer is allocated for each drive detected when the driver is
-initialized up to the maximum. The minimum number of allocated buffers
-is ST_EXTRA_DEVS (in hosts.h) (unless this number exceeds the defined
-maximum). This ensures some functionality also for the drives found
-after tape driver initialization (a SCSI adapter driver is loaded as a
-module). The default for ST_EXTRA_DEVS is two.
+initialized up to the maximum.
The driver tries to allocate new buffers at run-time if
necessary. These buffers are freed after use. If the maximum number of
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index c0b3f3a62..6c92531fc 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -105,6 +105,11 @@ irq_numok:
{
tmport += 0x1f;
j = inb(tmport);
+ if((j&0x80)==0)
+ {
+ dev->in_int=0;
+ return;
+ }
tmpcip = dev->pciport;
if ((inb(tmpcip) & 0x08) != 0)
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 849ce76c9..5a74bc2df 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -158,16 +158,20 @@ void print_status (int status) {
}
#if (CONSTANTS & CONST_XSENSE)
-#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
-#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
-#define L 0x004 /* PRINTER DEVICE */
-#define P 0x008 /* PROCESSOR DEVICE */
-#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
-#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
-#define S 0x040 /* SCANNER DEVICE */
-#define O 0x080 /* OPTICAL MEMORY DEVICE */
-#define M 0x100 /* MEDIA CHANGER DEVICE */
-#define C 0x200 /* COMMUNICATION DEVICE */
+#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x0004 /* PRINTER DEVICE */
+#define P 0x0008 /* PROCESSOR DEVICE */
+#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x0020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x0040 /* SCANNER DEVICE */
+#define O 0x0080 /* OPTICAL MEMORY DEVICE */
+#define M 0x0100 /* MEDIA CHANGER DEVICE */
+#define C 0x0200 /* COMMUNICATION DEVICE */
+#define A 0x0400 /* ARRAY STORAGE */
+#define E 0x0800 /* ENCLOSURE SERVICES DEVICE */
+#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE */
+#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE */
struct error_info{
unsigned char code1, code2;
@@ -192,131 +196,213 @@ static struct error_info2 additional2[] =
static struct error_info additional[] =
{
+ {0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
{0x00,0x01,T,"Filemark detected"},
{0x00,0x02,T|S,"End-of-partition/medium detected"},
{0x00,0x03,T,"Setmark detected"},
{0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
- {0x00,0x05,T|S,"End-of-data detected"},
- {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x05,T|L|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
{0x00,0x11,R,"Audio play operation in progress"},
{0x00,0x12,R,"Audio play operation paused"},
{0x00,0x13,R,"Audio play operation successfully completed"},
{0x00,0x14,R,"Audio play operation stopped due to error"},
{0x00,0x15,R,"No current audio status to return"},
- {0x01,0x00,D|W|O,"No index/sector signal"},
- {0x02,0x00,D|W|R|O|M,"No seek complete"},
- {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
+ {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
+ {0x01,0x00,D|W|O|B|K,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
{0x03,0x01,T,"No write current"},
{0x03,0x02,T,"Excessive write errors"},
- {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, cause not reportable"},
- {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
- "Logical unit is in process of becoming ready"},
- {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, initializing command required"},
- {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
- "Logical unit not ready, manual intervention required"},
- {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
- {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
- {0x06,0x00,D|W|R|O|M,"No reference position found"},
- {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
- {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
- {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
- {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
- {0x09,0x00,D|T|W|R|O,"Track following error"},
- {0x09,0x01,W|R|O,"Tracking servo failure"},
- {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
+ {0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
+ {0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
+ {0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
+ {0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
+ {0x04,0x08,R,"Logical unit not ready,long write in progress"},
+ {0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
+ {0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA/32)"},
+ {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
+ {0x09,0x00,D|T|W|R|O|B,"Track following error"},
+ {0x09,0x01,W|R|O|K,"Tracking servo failure"},
+ {0x09,0x02,W|R|O|K,"Focus servo failure"},
{0x09,0x03,W|R|O,"Spindle servo failure"},
- {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
- {0x0C,0x00,T|S,"Write error"},
- {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
- {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
- {0x10,0x00,D|W|O,"Id crc or ecc error"},
- {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
- {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
- {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
- {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
- {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
- {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
- {0x11,0x06,W|R|O,"Circ unrecovered error"},
- {0x11,0x07,W|O,"Data resynchronization error"},
+ {0x09,0x04,D|T|W|R|O|B,"Head select fault"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
+ {0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
+ {0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
+ {0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
+ {0x0C,0x00,T|R|S,"Write error"},
+ {0x0C,0x01,K,"Write error - recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
+ {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
+ {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
+ {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
+ {0x0C,0x06,D|T|W|O|B,"Block not compressible"},
+ {0x0C,0x07,R,"Write error - recovery needed"},
+ {0x0C,0x08,R,"Write error - recovery failed"},
+ {0x0C,0x09,R,"Write error - loss of streaming"},
+ {0x0C,0x0A,R,"Write error - padding blocks added"},
+ {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
+ {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
+ {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
+ {0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
+ {0x11,0x07,W|O|B,"Data re-synchronization error"},
{0x11,0x08,T,"Incomplete block read"},
{0x11,0x09,T,"No gap found"},
- {0x11,0x0A,D|T|O,"Miscorrected error"},
- {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
- {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
- {0x12,0x00,D|W|O,"Address mark not found for id field"},
- {0x13,0x00,D|W|O,"Address mark not found for data field"},
- {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
- {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
+ {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
+ {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
+ {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
+ {0x11,0x0F,R,"Error reading UPC/EAN number"},
+ {0x11,0x10,R,"Error reading ISRC number"},
+ {0x11,0x11,R,"Read error - loss of streaming"},
+ {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
{0x14,0x02,T,"Filemark or setmark not found"},
{0x14,0x03,T,"End-of-data not found"},
{0x14,0x04,T,"Block sequence error"},
- {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
- {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
- {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
- {0x16,0x00,D|W|O,"Data synchronization mark error"},
- {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
- {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
- {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
- {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
- {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
- {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
- {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
- {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
- {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
- {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
- {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
- {0x18,0x03,R,"Recovered data with circ"},
- {0x18,0x04,R,"Recovered data with lec"},
- {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
- {0x19,0x00,D|O,"Defect list error"},
- {0x19,0x01,D|O,"Defect list not available"},
- {0x19,0x02,D|O,"Defect list error in primary list"},
- {0x19,0x03,D|O,"Defect list error in grown list"},
- {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
- {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
- {0x1C,0x00,D|O,"Defect list not found"},
- {0x1C,0x01,D|O,"Primary defect list not found"},
- {0x1C,0x02,D|O,"Grown defect list not found"},
- {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
- {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
- {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
- {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
- {0x21,0x01,M,"Invalid element address"},
- {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
- {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
- {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
- {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
- {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
- {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
- {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
- {0x27,0x00,D|T|W|O,"Write protected"},
- {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
- {0x28,0x01,M,"Import or export element accessed"},
- {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
- {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
- {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
- {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
- {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
- {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
+ {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
+ {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
+ {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
+ {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
+ {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
+ {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
+ {0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
+ {0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
+ {0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
+ {0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
+ {0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with CIRC"},
+ {0x18,0x04,R,"Recovered data with L-EC"},
+ {0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
+ {0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
+ {0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
+ {0x19,0x00,D|O|K,"Defect list error"},
+ {0x19,0x01,D|O|K,"Defect list not available"},
+ {0x19,0x02,D|O|K,"Defect list error in primary list"},
+ {0x19,0x03,D|O|K,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O|B|K,"Defect list not found"},
+ {0x1C,0x01,D|O|B|K,"Primary defect list not found"},
+ {0x1C,0x02,D|O|B|K,"Grown defect list not found"},
+ {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
+ {0x1F,0x00,D|O|K,"Partial defect list transfer"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
+ {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
+ {0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
+ {0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
+ {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
+ {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
+ {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
+ {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
+ {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
+ {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
+ {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
+ {0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
+ {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
+ {0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
+ {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
+ {0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
+ {0x27,0x03,T|R,"Associated write protect"},
+ {0x27,0x04,T|R,"Persistent write protect"},
+ {0x27,0x05,T|R,"Permanent write protect"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
+ {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
+ {0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
+ {0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
+ {0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
+ {0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
+ {0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
+ {0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
+ {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
+ {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
+ {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
{0x2C,0x01,S,"Too many windows specified"},
{0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2C,0x03,R,"Current program area is not empty"},
+ {0x2C,0x04,R,"Current program area is empty"},
+ {0x2C,0x05,B,"Illegal power condition request"},
{0x2D,0x00,T,"Overwrite error on update in place"},
- {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
- {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
- {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
- {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
- {0x30,0x03,D|T,"Cleaning cartridge installed"},
- {0x31,0x00,D|T|W|O,"Medium format corrupted"},
- {0x31,0x01,D|L|O,"Format command failed"},
- {0x32,0x00,D|W|O,"No defect spare location available"},
- {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
+ {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
+ {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
+ {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
+ {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
+ {0x30,0x08,R,"Cannot write - application code mismatch"},
+ {0x30,0x09,R,"Current session not fixated for append"},
+ {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
+ {0x31,0x01,D|L|R|O|B,"Format command failed"},
+ {0x32,0x00,D|W|O|B|K,"No defect spare location available"},
+ {0x32,0x01,D|W|O|B|K,"Defect list update failure"},
{0x33,0x00,T,"Tape length error"},
- {0x36,0x00,L,"Ribbon, ink, or toner failure"},
- {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
- {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
- {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
+ {0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
+ {0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
+ {0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
+ {0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
+ {0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
+ {0x36,0x00,L,"Ribbon,ink,or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
+ {0x38,0x00,B,"Event status notification"},
+ {0x38,0x02,B,"Esn - power management class event"},
+ {0x38,0x04,B,"Esn - media class event"},
+ {0x38,0x06,B,"Esn - device busy class event"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
+ {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
+ {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
+ {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
+ {0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
{0x3B,0x00,T|L,"Sequential positioning error"},
{0x3B,0x01,T,"Tape position error at beginning-of-medium"},
{0x3B,0x02,T,"Tape position error at end-of-medium"},
@@ -329,57 +415,244 @@ static struct error_info additional[] =
{0x3B,0x09,S,"Read past end of medium"},
{0x3B,0x0A,S,"Read past beginning of medium"},
{0x3B,0x0B,S,"Position past end of medium"},
- {0x3B,0x0C,S,"Position past beginning of medium"},
- {0x3B,0x0D,M,"Medium destination element full"},
- {0x3B,0x0E,M,"Medium source element empty"},
- {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
- {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
- {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
- {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
- {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
- {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
- {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
- {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
- {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
- {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
- {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
- {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
- {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
- {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
- {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
- {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
- {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x3B,0x0C,T|S,"Position past beginning of medium"},
+ {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
+ {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
+ {0x3B,0x0F,R,"End of medium reached"},
+ {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
+ {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
+ {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
+ {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
+ {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
+ {0x3B,0x16,R,"Mechanical positioning or changer error"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
+ {0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
+ {0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
+ {0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
+ {0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
+ {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
+ {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
+ {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
+ {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
+ {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
+ {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
+ {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
+ {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
+ {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
+ {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
+ {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
+ {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
+ {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
+ {0x40,0x00,D,"Ram failure (should use 40 nn)"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
+ {0x41,0x00,D,"Data path failure (should use 40 nn)"},
+ {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
+ {0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
+ {0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
+ {0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
+ {0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
{0x50,0x00,T,"Write append error"},
{0x50,0x01,T,"Write append position error"},
{0x50,0x02,T,"Position error related to timing"},
- {0x51,0x00,T|O,"Erase failure"},
+ {0x51,0x00,T|R|O,"Erase failure"},
{0x52,0x00,T,"Cartridge fault"},
- {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
{0x53,0x01,T,"Unload tape failure"},
- {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
{0x54,0x00,P,"Scsi to host system interface failure"},
{0x55,0x00,P,"System resource failure"},
+ {0x55,0x01,D|O|B|K,"System buffer full"},
+ {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
+ {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
+ {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
{0x57,0x00,R,"Unable to recover table-of-contents"},
{0x58,0x00,O,"Generation does not exist"},
{0x59,0x00,O,"Updated block read"},
- {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
- {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
- {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
- {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
- {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
- {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
- {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
- {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
+ {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
{0x5C,0x00,D|O,"Rpl status change"},
{0x5C,0x01,D|O,"Spindles synchronized"},
{0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
+ {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
+ {0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
+ {0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
+ {0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
+ {0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
+ {0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
+ {0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
+ {0x5D,0x15,D|B,"Hardware impending failure access times too high"},
+ {0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
+ {0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
+ {0x5D,0x18,D|B,"Hardware impending failure controller detected"},
+ {0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
+ {0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
+ {0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
+ {0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
+ {0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
+ {0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
+ {0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
+ {0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
+ {0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
+ {0x5D,0x25,D|B,"Controller impending failure access times too high"},
+ {0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
+ {0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
+ {0x5D,0x28,D|B,"Controller impending failure controller detected"},
+ {0x5D,0x29,D|B,"Controller impending failure throughput performance"},
+ {0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
+ {0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
+ {0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
+ {0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
+ {0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
+ {0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
+ {0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
+ {0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
+ {0x5D,0x35,D|B,"Data channel impending failure access times too high"},
+ {0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
+ {0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
+ {0x5D,0x38,D|B,"Data channel impending failure controller detected"},
+ {0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
+ {0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
+ {0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
+ {0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
+ {0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
+ {0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
+ {0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
+ {0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
+ {0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
+ {0x5D,0x45,D|B,"Servo impending failure access times too high"},
+ {0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
+ {0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
+ {0x5D,0x48,D|B,"Servo impending failure controller detected"},
+ {0x5D,0x49,D|B,"Servo impending failure throughput performance"},
+ {0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
+ {0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
+ {0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
+ {0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
+ {0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
+ {0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
+ {0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
+ {0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
+ {0x5D,0x55,D|B,"Spindle impending failure access times too high"},
+ {0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
+ {0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
+ {0x5D,0x58,D|B,"Spindle impending failure controller detected"},
+ {0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
+ {0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
+ {0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
+ {0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
+ {0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
+ {0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
+ {0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
+ {0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
+ {0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
+ {0x5D,0x65,D|B,"Firmware impending failure access times too high"},
+ {0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
+ {0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
+ {0x5D,0x68,D|B,"Firmware impending failure controller detected"},
+ {0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
+ {0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
+ {0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
+ {0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
+ {0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
+ {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
+ {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
+ {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
+ {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
+ {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
+ {0x5E,0x41,B,"Power state change to active"},
+ {0x5E,0x42,B,"Power state change to idle"},
+ {0x5E,0x43,B,"Power state change to standby"},
+ {0x5E,0x45,B,"Power state change to sleep"},
+ {0x5E,0x47,B|K,"Power state change to device control"},
{0x60,0x00,S,"Lamp failure"},
{0x61,0x00,S,"Video acquisition error"},
{0x61,0x01,S,"Unable to acquire video"},
{0x61,0x02,S,"Out of focus"},
{0x62,0x00,S,"Scan head positioning error"},
{0x63,0x00,R,"End of user area encountered on this track"},
+ {0x63,0x01,R,"Packet does not fit in available space"},
{0x64,0x00,R,"Illegal mode for this track"},
+ {0x64,0x01,R,"Invalid packet size"},
+ {0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
+ {0x66,0x00,S,"Automatic document feeder cover up"},
+ {0x66,0x01,S,"Automatic document feeder lift up"},
+ {0x66,0x02,S,"Document jam in automatic document feeder"},
+ {0x66,0x03,S,"Document miss feed automatic in document feeder"},
+ {0x67,0x00,A,"Configuration failure"},
+ {0x67,0x01,A,"Configuration of incapable logical units failed"},
+ {0x67,0x02,A,"Add logical unit failed"},
+ {0x67,0x03,A,"Modification of logical unit failed"},
+ {0x67,0x04,A,"Exchange of logical unit failed"},
+ {0x67,0x05,A,"Remove of logical unit failed"},
+ {0x67,0x06,A,"Attachment of logical unit failed"},
+ {0x67,0x07,A,"Creation of logical unit failed"},
+ {0x67,0x08,A,"Assign failure occurred"},
+ {0x67,0x09,A,"Multiply assigned logical unit"},
+ {0x68,0x00,A,"Logical unit not configured"},
+ {0x69,0x00,A,"Data loss on logical unit"},
+ {0x69,0x01,A,"Multiple logical unit failures"},
+ {0x69,0x02,A,"Parity/data mismatch"},
+ {0x6A,0x00,A,"Informational,refer to log"},
+ {0x6B,0x00,A,"State change has occurred"},
+ {0x6B,0x01,A,"Redundancy level got better"},
+ {0x6B,0x02,A,"Redundancy level got worse"},
+ {0x6C,0x00,A,"Rebuild failure occurred"},
+ {0x6D,0x00,A,"Recalculate failure occurred"},
+ {0x6E,0x00,A,"Command to logical unit failed"},
+ {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
+ {0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
+ {0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
+ {0x6F,0x03,R,"Read of scrambled sector without authentication"},
+ {0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
+ {0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
+ /*
+ * FIXME(eric) - need a way to represent wildcards here.
+ */
+ {0x70,0x00,T,"Decompression exception short algorithm id of nn"},
+ {0x71,0x00,T,"Decompression exception long algorithm id"},
+ {0x72,0x00,R,"Session fixation error"},
+ {0x72,0x01,R,"Session fixation error writing lead-in"},
+ {0x72,0x02,R,"Session fixation error writing lead-out"},
+ {0x72,0x03,R,"Session fixation error - incomplete track in session"},
+ {0x72,0x04,R,"Empty or partially written reserved track"},
+ {0x72,0x05,R,"No more track reservations allowed"},
+ {0x73,0x00,R,"Cd control error"},
+ {0x73,0x01,R,"Power calibration area almost full"},
+ {0x73,0x02,R,"Power calibration area is full"},
+ {0x73,0x03,R,"Power calibration area error"},
+ {0x73,0x04,R,"Program memory area update failure"},
+ {0x73,0x05,R,"Program memory area is full"},
+ {0x73,0x06,R,"RMA/PMA is full"},
{0, 0, 0, NULL}
};
#endif
diff --git a/drivers/scsi/eata_dma_proc.c b/drivers/scsi/eata_dma_proc.c
index 8768db48c..7961483e1 100644
--- a/drivers/scsi/eata_dma_proc.c
+++ b/drivers/scsi/eata_dma_proc.c
@@ -69,7 +69,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
Scsi_Device *scd, *SDev;
struct Scsi_Host *HBA_ptr;
- Scsi_Cmnd * scmd;
+ Scsi_Request * scmd;
char cmnd[MAX_COMMAND_SIZE];
static u8 buff[512];
static u8 buff2[512];
@@ -153,7 +153,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
} else {
SDev = scsi_get_host_dev(HBA_ptr);
- scmd = scsi_allocate_device(SDev, 1, FALSE);
+ scmd = scsi_allocate_request(SDev);
cmnd[0] = LOG_SENSE;
cmnd[1] = 0;
@@ -166,13 +166,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
cmnd[8] = 0x66;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,
+ scsi_wait_req (scmd, cmnd, buff + 0x144, 0x66,
1 * HZ, 1);
size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
@@ -291,13 +291,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
cmnd[8] = 0x44;
cmnd[9] = 0;
- scmd->cmd_len = 10;
- scmd->sc_data_direction = SCSI_DATA_READ;
+ scmd->sr_cmd_len = 10;
+ scmd->sr_data_direction = SCSI_DATA_READ;
/*
* Do the command and wait for it to finish.
*/
- scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+ scsi_wait_req (scmd, cmnd, buff2, 0x144,
1 * HZ, 1);
swap_statistics(buff2);
@@ -333,7 +333,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
pos = begin + len;
}
- scsi_release_command(scmd);
+ scsi_release_request(scmd);
scsi_free_host_dev(SDev);
}
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index f1b82a5a1..685925e7d 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -469,10 +469,10 @@ extern void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt);
* Prototypes for functions/data in scsi_scan.c
*/
extern void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun);
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun);
extern void scsi_mark_host_reset(struct Scsi_Host *Host);
@@ -485,12 +485,12 @@ struct Scsi_Device_Template
const char * tag;
struct module * module; /* Used for loadable modules */
unsigned char scsi_type;
- unsigned char major;
- unsigned char min_major; /* Minimum major in range. */
- unsigned char max_major; /* Maximum major in range. */
- unsigned char nr_dev; /* Number currently attached */
- unsigned char dev_noticed; /* Number of devices detected. */
- unsigned char dev_max; /* Current size of arrays */
+ unsigned int major;
+ unsigned int min_major; /* Minimum major in range. */
+ unsigned int max_major; /* Maximum major in range. */
+ unsigned int nr_dev; /* Number currently attached */
+ unsigned int dev_noticed; /* Number of devices detected. */
+ unsigned int dev_max; /* Current size of arrays */
unsigned blk:1; /* 0 if character device */
int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
int (*init)(void); /* Sizes arrays based upon number of devices
@@ -534,20 +534,18 @@ extern void scsi_unregister_module(int, void *);
* Note: These things are all evil and all need to go away. My plan is to
* tackle the character devices first, as there aren't any locking implications
* in the block device layer. The block devices will require more work.
+ *
+ * The generics driver has been updated to resize as required. So as the tape
+ * driver. Two down, two more to go.
*/
#ifndef CONFIG_SD_EXTRA_DEVS
#define CONFIG_SD_EXTRA_DEVS 2
#endif
-#ifndef CONFIG_ST_EXTRA_DEVS
-#define CONFIG_ST_EXTRA_DEVS 2
-#endif
#ifndef CONFIG_SR_EXTRA_DEVS
#define CONFIG_SR_EXTRA_DEVS 2
#endif
#define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
-#define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS
#define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
-#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
#endif
/*
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index d2943f103..5fc1bfaab 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -73,7 +73,7 @@
*/
/*
-** January 8 2000, version 3.2e
+** March 6 2000, version 3.2g
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -104,7 +104,7 @@
/*
** Name and version of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2e"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2g"
#define SCSI_NCR_DEBUG_FLAGS (0)
@@ -141,11 +141,6 @@
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/stat.h>
-#ifdef __mips__
-#include <asm/bootinfo.h>
-#include <asm/pgtable.h>
-#include <asm/sni.h>
-#endif /* __mips__ */
#include <linux/version.h>
#include <linux/blk.h>
@@ -189,98 +184,14 @@
*/
typedef u32 u_int32;
typedef u64 u_int64;
-
+typedef u_long vm_offset_t;
#include "ncr53c8xx.h"
-/*==========================================================
-**
-** A la VMS/CAM-3 queue management.
-** Implemented from linux list management.
-**
-**==========================================================
-*/
-
-typedef struct xpt_quehead {
- struct xpt_quehead *flink; /* Forward pointer */
- struct xpt_quehead *blink; /* Backward pointer */
-} XPT_QUEHEAD;
-
-#define xpt_que_init(ptr) do { \
- (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
-} while (0)
-
-static inline void __xpt_que_add(struct xpt_quehead * new,
- struct xpt_quehead * blink,
- struct xpt_quehead * flink)
-{
- flink->blink = new;
- new->flink = flink;
- new->blink = blink;
- blink->flink = new;
-}
-
-static inline void __xpt_que_del(struct xpt_quehead * blink,
- struct xpt_quehead * flink)
-{
- flink->blink = blink;
- blink->flink = flink;
-}
-
-static inline int xpt_que_empty(struct xpt_quehead *head)
-{
- return head->flink == head;
-}
-
-static inline void xpt_que_splice(struct xpt_quehead *list,
- struct xpt_quehead *head)
-{
- struct xpt_quehead *first = list->flink;
-
- if (first != list) {
- struct xpt_quehead *last = list->blink;
- struct xpt_quehead *at = head->flink;
+#define NAME53C "ncr53c"
+#define NAME53C8XX "ncr53c8xx"
+#define DRIVER_SMP_LOCK ncr53c8xx_lock
- first->blink = head;
- head->flink = first;
-
- last->flink = at;
- at->blink = last;
- }
-}
-
-#define xpt_que_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-
-#define xpt_insque(new, pos) __xpt_que_add(new, pos, (pos)->flink)
-
-#define xpt_remque(el) __xpt_que_del((el)->blink, (el)->flink)
-
-#define xpt_insque_head(new, head) __xpt_que_add(new, head, (head)->flink)
-
-static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
-{
- struct xpt_quehead *elem = head->flink;
-
- if (elem != head)
- __xpt_que_del(head, elem->flink);
- else
- elem = 0;
- return elem;
-}
-
-#define xpt_insque_tail(new, head) __xpt_que_add(new, (head)->blink, head)
-
-static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
-{
- struct xpt_quehead *elem = head->blink;
-
- if (elem != head)
- __xpt_que_del(elem->blink, head);
- else
- elem = 0;
- return elem;
-}
+#include "sym53c8xx_comm.h"
/*==========================================================
**
@@ -320,33 +231,6 @@ static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
/*==========================================================
**
-** On x86 architecture, write buffers management does
-** not reorder writes to memory. So, using compiler
-** optimization barriers is enough to guarantee some
-** ordering when the CPU is writing data accessed by
-** the NCR.
-** On Alpha architecture, explicit memory barriers have
-** to be used.
-** Other architectures are defaulted to mb() macro if
-** defined, otherwise use compiler barrier.
-**
-**==========================================================
-*/
-
-#if defined(__i386__)
-#define MEMORY_BARRIER() barrier()
-#elif defined(__alpha__)
-#define MEMORY_BARRIER() mb()
-#else
-# ifdef mb
-# define MEMORY_BARRIER() mb()
-# else
-# define MEMORY_BARRIER() barrier()
-# endif
-#endif
-
-/*==========================================================
-**
** Configuration and Debugging
**
**==========================================================
@@ -469,381 +353,11 @@ typedef u_int32 tagmap_t;
#endif
/*
-** Io mapped or memory mapped.
-*/
-
-#if defined(SCSI_NCR_IOMAPPED)
-#define NCR_IOMAPPED
-#endif
-
-/*
** other
*/
#define NCR_SNOOP_TIMEOUT (1000000)
-/*==========================================================
-**
-** Defines for Linux.
-**
-** Linux and Bsd kernel functions are quite different.
-** These defines allow a minimum change of the original
-** code.
-**
-**==========================================================
-*/
-
- /*
- ** Obvious definitions
- */
-
-#define u_char unsigned char
-#define u_short unsigned short
-#define u_int unsigned int
-#define u_long unsigned long
-
-typedef u_long vm_offset_t;
-typedef int vm_size_t;
-
-#ifndef bcopy
-#define bcopy(s, d, n) memcpy((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(d, n) memset((d), 0, (n))
-#endif
-
-#ifndef offsetof
-#define offsetof(t, m) ((size_t) (&((t *)0)->m))
-#endif
-
-/*
-** Simple Wrapper to kernel PCI bus interface.
-**
-** This wrapper allows to get rid of old kernel PCI interface
-** and still allows to preserve linux-2.0 compatibilty.
-** In fact, it is mostly an incomplete emulation of the new
-** PCI code for pre-2.2 kernels. When kernel-2.0 support
-** will be dropped, we will just have to remove most of this
-** code.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
-
-typedef struct pci_dev *pcidev_t;
-#define PCIDEV_NULL (0)
-#define PciBusNumber(d) (d)->bus->number
-#define PciDeviceFn(d) (d)->devfn
-#define PciVendorId(d) (d)->vendor
-#define PciDeviceId(d) (d)->device
-#define PciIrqLine(d) (d)->irq
-
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
-
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
- *base = pdev->resource[index].start;
- if ((pdev->resource[index].flags & 0x7) == 0x4)
- ++index;
- return ++index;
-}
-#else
-static int __init
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
- *base = pdev->base_address[index++];
- if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
- *base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
- ++index;
- }
- return index;
-}
-#endif
-
-#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */
-
-typedef unsigned int pcidev_t;
-#define PCIDEV_NULL (~0u)
-#define PciBusNumber(d) ((d)>>8)
-#define PciDeviceFn(n) ((d)&0xff)
-#define __PciDev(busn, devfn) (((busn)<<8)+(devfn))
-
-#define pci_present pcibios_present
-
-#define pci_read_config_byte(d, w, v) \
- pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_word(d, w, v) \
- pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_read_config_dword(d, w, v) \
- pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-
-#define pci_write_config_byte(d, w, v) \
- pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_word(d, w, v) \
- pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
-#define pci_write_config_dword(d, w, v) \
- pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
-
-static pcidev_t __init
-pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
-{
- static unsigned short pci_index;
- int retv;
- unsigned char bus_number, device_fn;
-
- if (prev == PCIDEV_NULL)
- pci_index = 0;
- else
- ++pci_index;
- retv = pcibios_find_device (vendor, device, pci_index,
- &bus_number, &device_fn);
- return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
-}
-
-static u_short __init PciVendorId(pcidev_t dev)
-{
- u_short vendor_id;
- pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
- return vendor_id;
-}
-
-static u_short __init PciDeviceId(pcidev_t dev)
-{
- u_short device_id;
- pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
- return device_id;
-}
-
-static u_int __init PciIrqLine(pcidev_t dev)
-{
- u_short irq;
- pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
- return irq;
-}
-
-static int __init
-pci_get_base_address(pcidev_t dev, int offset, u_long *base)
-{
- u_int32 tmp;
-
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
- *base = tmp;
- offset += sizeof(u_int32);
- if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
- *base |= (((u_long)tmp) << 32);
-#endif
- offset += sizeof(u_int32);
- }
- return offset;
-}
-
-#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
-
-/*
-** SMP threading.
-**
-** Assuming that SMP systems are generally high end systems and may
-** use several SCSI adapters, we are using one lock per controller
-** instead of some global one. For the moment (linux-2.1.95), driver's
-** entry points are called with the 'io_request_lock' lock held, so:
-** - We are uselessly loosing a couple of micro-seconds to lock the
-** controller data structure.
-** - But the driver is not broken by design for SMP and so can be
-** more resistant to bugs or bad changes in the IO sub-system code.
-** - A small advantage could be that the interrupt code is grained as
-** wished (e.g.: threaded by controller).
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
-
-#if 0 /* not yet needed */
-static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
-#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&driver_lock, flags)
-#define NCR_UNLOCK_DRIVER(flags) spin_unlock_irqrestore(&driver_lock, flags)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np) spin_lock_init(&np->smp_lock);
-#define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags)
-#define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags)
-
-#define NCR_LOCK_SCSI_DONE(np, flags) \
- spin_lock_irqsave(&io_request_lock, flags)
-#define NCR_UNLOCK_SCSI_DONE(np, flags) \
- spin_unlock_irqrestore(&io_request_lock, flags)
-
-#else
-
-#if 0 /* not yet needed */
-#define NCR_LOCK_DRIVER(flags) do {;} while (0)
-#define NCR_UNLOCK_DRIVER(flags) do {;} while (0)
-#endif
-
-#define NCR_INIT_LOCK_NCB(np) do { } while (0)
-#define NCR_LOCK_NCB(np, flags) do { save_flags(flags); cli(); } while (0)
-#define NCR_UNLOCK_NCB(np, flags) do { restore_flags(flags); } while (0)
-
-#define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0)
-#define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0)
-
-#endif
-
-/*
-** Address translation
-**
-** The driver has to provide physical memory addresses to
-** the script processor. Because some architectures use
-** different physical addresses from the PCI BUS, we must
-** use virt_to_bus instead of virt_to_phys.
-**
-** FIXME: Bus addresses are _not_ physical addresses.
-*/
-
-#define vtophys(p) virt_to_bus(p)
-
-/*
-** Memory mapped IO
-**
-** Since linux-2.1, we must use ioremap() to map the io memory space.
-** iounmap() to unmap it. That allows portability.
-** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater
-** than the highest physical memory address to kernel virtual pages with
-** vremap() / vfree(). That was not portable but worked with i386
-** architecture.
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
-#define ioremap vremap
-#define iounmap vfree
-#endif
-
-#if defined (__sparc__)
-#include <asm/irq.h>
-#elif defined (__alpha__)
-#define bus_dvma_to_mem(p) ((p) & 0xfffffffful)
-#else
-#define bus_dvma_to_mem(p) (p)
-#endif
-
-#if defined(__i386__) || !defined(NCR_IOMAPPED)
-static vm_offset_t __init remap_pci_mem(u_long base, u_long size)
-{
- u_long page_base = ((u_long) base) & PAGE_MASK;
- u_long page_offs = ((u_long) base) - page_base;
- u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
-
- return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL);
-}
-
-static void __init unmap_pci_mem(vm_offset_t vaddr, u_long size)
-{
- if (vaddr)
- iounmap((void *) (vaddr & PAGE_MASK));
-}
-#endif /* __i386__ || !NCR_IOMAPPED */
-
-/*
-** Insert a delay in micro-seconds and milli-seconds.
-** -------------------------------------------------
-** Under Linux, udelay() is restricted to delay < 1 milli-second.
-** In fact, it generally works for up to 1 second delay.
-** Since 2.1.105, the mdelay() function is provided for delays
-** in milli-seconds.
-** Under 2.0 kernels, udelay() is an inline function that is very
-** inaccurate on Pentium processors.
-*/
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
-#define UDELAY udelay
-#define MDELAY mdelay
-#else
-static void UDELAY(long us) { udelay(us); }
-static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
-#endif
-
-/*
-** Internal data structure allocation.
-**
-** Linux scsi memory poor pool is adjusted for the need of
-** middle-level scsi driver.
-** We allocate our control blocks in the kernel memory pool
-** to avoid scsi pool shortage.
-**
-** kmalloc() only ensures 8 bytes boundary alignment.
-** The NCR need better alignment for cache line bursting.
-** The global header is moved between the NCB and CCBs and needs
-** origin and destination addresses to have same lower four bits.
-**
-** We use 32 boundary alignment for NCB and CCBs and offset multiple
-** of 32 for global header fields. That's too much but at least enough.
-*/
-
-#define ALIGN_SIZE(shift) (1UL << shift)
-#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1))
-
-#define CACHE_LINE_SHIFT 5
-#define CACHE_LINE_SIZE ALIGN_SIZE(CACHE_LINE_SHIFT)
-#define CACHE_LINE_MASK ALIGN_MASK(CACHE_LINE_SHIFT)
-
-static void *m_alloc(int size, int a_shift)
-{
- u_long addr;
- void *ptr;
- u_long a_size, a_mask;
-
- if (a_shift < 3)
- a_shift = 3;
-
- a_size = ALIGN_SIZE(a_shift);
- a_mask = ALIGN_MASK(a_shift);
-
- ptr = (void *) kmalloc(size + a_size, GFP_UNCACHED | GFP_ATOMIC);
- if (ptr) {
- addr = (((u_long) ptr) + a_size) & a_mask;
- *((void **) (addr - sizeof(void *))) = ptr;
- ptr = (void *) addr;
- }
-
- return ptr;
-}
-
-#ifdef MODULE
-static void m_free(void *ptr, int size)
-{
- u_long addr;
-
- if (ptr) {
- addr = (u_long) ptr;
- ptr = *((void **) (addr - sizeof(void *)));
-
- kfree(ptr);
- }
-}
-#endif
-
-/*
-** Transfer direction
-**
-** Low-level scsi drivers under Linux do not receive the expected
-** data transfer direction from upper scsi drivers.
-** The driver will only check actual data direction for common
-** scsi opcodes. Other ones may cause problem, since they may
-** depend on device type or be vendor specific.
-** I would prefer to never trust the device for data direction,
-** but that is not possible.
-**
-** The original driver requires the expected direction to be known.
-** The Linux version of the driver has been enhanced in order to
-** be able to transfer data in the direction choosen by the target.
-*/
-
-#define XFER_IN (1)
-#define XFER_OUT (2)
-
/*
** Head of list of NCR boards
**
@@ -855,44 +369,8 @@ static void m_free(void *ptr, int size)
static struct Scsi_Host *first_host = NULL;
static Scsi_Host_Template *the_template = NULL;
-
-/*
-** /proc directory entry and proc_info function
-*/
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
-static struct proc_dir_entry proc_scsi_ncr53c8xx = {
- PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-#endif
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int func);
-#endif
-
/*
-** Driver setup.
-**
-** This structure is initialized from linux config options.
-** It can be overridden at boot-up by the boot command line.
-*/
-static struct ncr_driver_setup
- driver_setup = SCSI_NCR_DRIVER_SETUP;
-
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
-static struct ncr_driver_setup
- driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
-# ifdef MODULE
-char *ncr53c8xx = 0; /* command line passed by insmod */
-# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
-MODULE_PARM(ncr53c8xx, "s");
-# endif
-# endif
-#endif
-
-/*
-** Other Linux definitions
+** Other definitions
*/
#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
@@ -903,273 +381,13 @@ static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
static void ncr53c8xx_timeout(unsigned long np);
#define initverbose (driver_setup.verbose)
-#define bootverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
#ifdef SCSI_NCR_NVRAM_SUPPORT
static u_char Tekram_sync[16] __initdata =
{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
#endif /* SCSI_NCR_NVRAM_SUPPORT */
-/*
-** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
-** transmit device configuration to the ncr_attach() function.
-*/
-typedef struct {
- int bus;
- u_char device_fn;
- u_long base;
- u_long base_2;
- u_long io_port;
- int irq;
-/* port and reg fields to use INB, OUTB macros */
- u_long port;
- volatile struct ncr_reg *reg;
-} ncr_slot;
-
-typedef struct {
- int type;
-#define SCSI_NCR_SYMBIOS_NVRAM (1)
-#define SCSI_NCR_TEKRAM_NVRAM (2)
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- union {
- Symbios_nvram Symbios;
- Tekram_nvram Tekram;
- } data;
-#endif
-} ncr_nvram;
-
-/*
-** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
-** to save data on each detected board for ncr_attach().
-*/
-typedef struct {
- ncr_slot slot;
- ncr_chip chip;
- ncr_nvram *nvram;
- u_char host_id;
- int attach_done;
-} ncr_device;
-
-/*==========================================================
-**
-** Debugging tags
-**
-**==========================================================
-*/
-
-#define DEBUG_ALLOC (0x0001)
-#define DEBUG_PHASE (0x0002)
-#define DEBUG_QUEUE (0x0008)
-#define DEBUG_RESULT (0x0010)
-#define DEBUG_SCATTER (0x0020)
-#define DEBUG_SCRIPT (0x0040)
-#define DEBUG_TINY (0x0080)
-#define DEBUG_TIMING (0x0100)
-#define DEBUG_NEGO (0x0200)
-#define DEBUG_TAGS (0x0400)
-
-/*
-** Enable/Disable debug messages.
-** Can be changed at runtime too.
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
- #define DEBUG_FLAGS ncr_debug
-#else
- #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
-#endif
-
-
-
-/*==========================================================
-**
-** assert ()
-**
-**==========================================================
-**
-** modified copy from 386bsd:/usr/include/sys/assert.h
-**
-**----------------------------------------------------------
-*/
-
-#define assert(expression) { \
- if (!(expression)) { \
- (void)printk(KERN_ERR \
- "assertion \"%s\" failed: file \"%s\", line %d\n", \
- #expression, \
- __FILE__, __LINE__); \
- } \
-}
-
-/*==========================================================
-**
-** Big/Little endian support.
-**
-**==========================================================
-*/
-
-/*
-** If the NCR uses big endian addressing mode over the
-** PCI, actual io register addresses for byte and word
-** accesses must be changed according to lane routing.
-** Btw, ncr_offb() and ncr_offw() macros only apply to
-** constants and so donnot generate bloated code.
-*/
-
-#if defined(SCSI_NCR_BIG_ENDIAN)
-
-#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
-#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
-
-#else
-
-#define ncr_offb(o) (o)
-#define ncr_offw(o) (o)
-
-#endif
-
-/*
-** If the CPU and the NCR use same endian-ness addressing,
-** no byte reordering is needed for script patching.
-** Macro cpu_to_scr() is to be used for script patching.
-** Macro scr_to_cpu() is to be used for getting a DWORD
-** from the script.
-*/
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw) cpu_to_le32(dw)
-#define scr_to_cpu(dw) le32_to_cpu(dw)
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define cpu_to_scr(dw) cpu_to_be32(dw)
-#define scr_to_cpu(dw) be32_to_cpu(dw)
-
-#else
-
-#define cpu_to_scr(dw) (dw)
-#define scr_to_cpu(dw) (dw)
-
-#endif
-
-/*==========================================================
-**
-** Access to the controller chip.
-**
-** If NCR_IOMAPPED is defined, the driver will use
-** normal IOs instead of the MEMORY MAPPED IO method
-** recommended by PCI specifications.
-** If all PCI bridges, host brigdes and architectures
-** would have been correctly designed for PCI, this
-** option would be useless.
-**
-**==========================================================
-*/
-
-/*
-** If the CPU and the NCR use same endian-ness addressing,
-** no byte reordering is needed for accessing chip io
-** registers. Functions suffixed by '_raw' are assumed
-** to access the chip over the PCI without doing byte
-** reordering. Functions suffixed by '_l2b' are
-** assumed to perform little-endian to big-endian byte
-** reordering, those suffixed by '_b2l' blah, blah,
-** blah, ...
-*/
-
-#if defined(NCR_IOMAPPED)
-
-/*
-** IO mapped only input / ouput
-*/
-
-#define INB_OFF(o) inb (np->port + ncr_offb(o))
-#define OUTB_OFF(o, val) outb ((val), np->port + ncr_offb(o))
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) inw_l2b (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_l2b (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_b2l ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_b2l ((val), np->port + (o))
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) inw_b2l (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_b2l (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_l2b ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_l2b ((val), np->port + (o))
-
-#else
-
-#define INW_OFF(o) inw_raw (np->port + ncr_offw(o))
-#define INL_OFF(o) inl_raw (np->port + (o))
-
-#define OUTW_OFF(o, val) outw_raw ((val), np->port + ncr_offw(o))
-#define OUTL_OFF(o, val) outl_raw ((val), np->port + (o))
-
-#endif /* ENDIANs */
-
-#else /* defined NCR_IOMAPPED */
-
-/*
-** MEMORY mapped IO input / output
-*/
-
-#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
-#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
-
-#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
-
-#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
-
-#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
-
-#else
-
-#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
-#define INL_OFF(o) readl_raw((char *)np->reg + (o))
-
-#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
-#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
-
-#endif
-
-#endif /* defined NCR_IOMAPPED */
-
-#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
-#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
-#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
-
-#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
-#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
-
-/*
-** Set bit field ON, OFF
-*/
-
-#define OUTONB(r, m) OUTB(r, INB(r) | (m))
-#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
-#define OUTONW(r, m) OUTW(r, INW(r) | (m))
-#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
-#define OUTONL(r, m) OUTL(r, INL(r) | (m))
-#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
-
-
/*==========================================================
**
** Command control block states.
@@ -1765,6 +983,8 @@ struct ccb {
**----------------------------------------------------------------
*/
Scsi_Cmnd *cmd; /* SCSI command */
+ u_char cdb_buf[16]; /* Copy of CDB */
+ u_char sense_buf[64];
int data_len; /* Total data length */
/*----------------------------------------------------------------
@@ -1895,6 +1115,7 @@ struct ncb {
** General controller parameters and configuration.
**----------------------------------------------------------------
*/
+ pcidev_t pdev;
u_short device_id; /* PCI device id */
u_char revision_id; /* PCI device revision id */
u_char bus; /* PCI BUS number */
@@ -1963,6 +1184,7 @@ struct ncb {
u_char order; /* Tag order to use */
u_char verbose; /* Verbosity for this controller*/
int ncr_cache; /* Used for cache test at init. */
+ u_long p_ncb; /* BUS address of this NCB */
/*----------------------------------------------------------------
** Command completion handling.
@@ -1976,6 +1198,9 @@ struct ncb {
** Fields that should be removed or changed.
**----------------------------------------------------------------
*/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ u_long ktime; /* Copy of kernel time */
+#endif
struct ccb *ccb; /* Global CCB */
struct usrcmd user; /* Command from user */
u_char release_stage; /* Synchronisation stage on release */
@@ -2165,7 +1390,7 @@ static void ncb_profile (ncb_p np, ccb_p cp);
static void ncr_script_copy_and_bind
(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
static void ncr_script_fill (struct script * scr, struct scripth * scripth);
-static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
+static int ncr_scatter (ncb_p np, ccb_p cp, Scsi_Cmnd *cmd);
static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln);
@@ -2195,25 +1420,6 @@ static void process_waiting_list(ncb_p np, int sts);
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static void ncr_get_nvram (ncr_device *devp, ncr_nvram *nvp);
-static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
-static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
-#endif
-
-/*==========================================================
-**
-**
-** Global static data.
-**
-**
-**==========================================================
-*/
-
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
-static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
-#endif
-
static inline char *ncr_name (ncb_p np)
{
return np->inst_name;
@@ -2242,7 +1448,9 @@ static inline char *ncr_name (ncb_p np)
#define RELOC_SOFTC 0x40000000
#define RELOC_LABEL 0x50000000
#define RELOC_REGISTER 0x60000000
+#if 0
#define RELOC_KVAR 0x70000000
+#endif
#define RELOC_LABELH 0x80000000
#define RELOC_MASK 0xf0000000
@@ -2251,19 +1459,21 @@ static inline char *ncr_name (ncb_p np)
#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
#define RADDR(label) (RELOC_REGISTER | REG(label))
#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#if 0
#define KVAR(which) (RELOC_KVAR | (which))
+#endif
+#if 0
#define SCRIPT_KVAR_JIFFIES (0)
-
#define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES
#define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES
-
/*
* Kernel variables referenced in the scripts.
* THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
*/
static void *script_kvars[] __initdata =
{ (void *)&jiffies };
+#endif
static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
@@ -2434,7 +1644,7 @@ static struct script script0 __initdata = {
** ... set a timestamp ...
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.command),
#endif
/*
@@ -2547,7 +1757,7 @@ static struct script script0 __initdata = {
** set the timestamp.
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.status),
#endif
/*
@@ -2783,7 +1993,7 @@ static struct script script0 __initdata = {
** and count the disconnects.
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.disconnect),
SCR_COPY (4),
NADDR (disc_phys),
@@ -2939,7 +2149,7 @@ static struct script script0 __initdata = {
** Set a time stamp for this reselection
*/
SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (ktime),
NADDR (header.stamp.reselect),
#endif
/*
@@ -3777,7 +2987,6 @@ void __init ncr_script_fill (struct script * scr, struct scripth * scrh)
};
assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
-flush_cache_all();
}
/*==========================================================
@@ -3833,11 +3042,15 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
*/
relocs = 2;
tmp1 = src[0];
+#ifdef RELOC_KVAR
if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
tmp1 = 0;
+#endif
tmp2 = src[1];
+#ifdef RELOC_KVAR
if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
tmp2 = 0;
+#endif
if ((tmp1 ^ tmp2) & 3) {
printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
ncr_name(np), (int) (src-start-1));
@@ -3890,7 +3103,7 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
switch (old & RELOC_MASK) {
case RELOC_REGISTER:
new = (old & ~RELOC_MASK)
- + bus_dvma_to_mem(np->paddr);
+ + pcivtobus(np->paddr);
break;
case RELOC_LABEL:
new = (old & ~RELOC_MASK) + np->p_script;
@@ -3899,8 +3112,9 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
new = (old & ~RELOC_MASK) + np->p_scripth;
break;
case RELOC_SOFTC:
- new = (old & ~RELOC_MASK) + vtophys(np);
+ new = (old & ~RELOC_MASK) + np->p_ncb;
break;
+#ifdef RELOC_KVAR
case RELOC_KVAR:
if (((old & ~RELOC_MASK) <
SCRIPT_KVAR_FIRST) ||
@@ -3910,6 +3124,7 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
new = vtophys(script_kvars[old &
~RELOC_MASK]);
break;
+#endif
case 0:
/* Don't relocate a 0 address. */
if (old == 0) {
@@ -3928,7 +3143,6 @@ ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
*dst++ = cpu_to_scr(*src++);
};
-flush_cache_all();
}
/*==========================================================
@@ -3950,17 +3164,6 @@ flush_cache_all();
struct host_data {
struct ncb *ncb;
-
- char ncb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct ncb _ncb_data;
-
- char ccb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct ccb _ccb_data;
-
- char scr_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
- struct script script_data;
-
- struct scripth scripth_data;
};
/*
@@ -4399,88 +3602,6 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
return 0;
}
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-
-void __init ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
-{
- int i;
-
- /* display Symbios nvram host data */
- printk(KERN_DEBUG "%s: HOST ID=%d%s%s%s%s%s\n",
- ncr_name(np), nvram->host_id & 0x0f,
- (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
- (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
- (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"",
- (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"",
- (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
-
- /* display Symbios nvram drive data */
- for (i = 0 ; i < 15 ; i++) {
- struct Symbios_target *tn = &nvram->target[i];
- printk(KERN_DEBUG "%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
- ncr_name(np), i,
- (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
- (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
- (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
- (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
- tn->bus_width,
- tn->sync_period / 4,
- tn->timeout);
- }
-}
-
-static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
-
-void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
-{
- int i, tags, boot_delay;
- char *rem;
-
- /* display Tekram nvram host data */
- tags = 2 << nvram->max_tags_index;
- boot_delay = 0;
- if (nvram->boot_delay_index < 6)
- boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
- switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
- default:
- case 0: rem = ""; break;
- case 1: rem = " REMOVABLE=boot device"; break;
- case 2: rem = " REMOVABLE=all"; break;
- }
-
- printk(KERN_DEBUG
- "%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
- ncr_name(np), nvram->host_id & 0x0f,
- (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
- (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
- (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
- (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
- (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
- (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
- (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
- (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
- rem, boot_delay, tags);
-
- /* display Tekram nvram drive data */
- for (i = 0; i <= 15; i++) {
- int sync, j;
- struct Tekram_target *tn = &nvram->target[i];
- j = tn->sync_index & 0xf;
- sync = Tekram_sync[j];
- printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
- ncr_name(np), i,
- (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
- (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
- (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
- (tn->flags & TEKRAM_START_CMD) ? " START" : "",
- (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
- (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
- sync);
- }
-}
-#endif /* SCSI_NCR_DEBUG_NVRAM */
-
/*
** Host attach and initialisations.
**
@@ -4495,7 +3616,7 @@ static int __init
ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
{
struct host_data *host_data;
- ncb_p np;
+ ncb_p np = 0;
struct Scsi_Host *instance = 0;
u_long flags = 0;
ncr_nvram *nvram = device->nvram;
@@ -4521,21 +3642,25 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
*/
if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
goto attach_error;
-
- /*
- ** Initialize structure.
- */
host_data = (struct host_data *) instance->hostdata;
- bzero (host_data, sizeof(*host_data));
/*
- ** Align np and first ccb to 32 boundary for cache line
- ** bursting when copying the global header.
+ ** Allocate the host control block.
*/
- np = (ncb_p) (((u_long) &host_data->_ncb_data) & CACHE_LINE_MASK);
+ np = __m_calloc_dma(device->pdev, sizeof(struct ncb), "NCB");
+ if (!np)
+ goto attach_error;
NCR_INIT_LOCK_NCB(np);
+ np->pdev = device->pdev;
+ np->p_ncb = vtobus(np);
host_data->ncb = np;
- np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CACHE_LINE_MASK);
+
+ /*
+ ** Allocate the default CCB.
+ */
+ np->ccb = (ccb_p) m_calloc_dma(sizeof(struct ccb), "CCB");
+ if (!np->ccb)
+ goto attach_error;
/*
** Store input informations in the host data structure.
@@ -4554,9 +3679,17 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
np->maxburst = device->chip.burst_max;
np->myaddr = device->host_id;
+ /*
+ ** Allocate SCRIPTS areas.
+ */
np->script0 = (struct script *)
- (((u_long) &host_data->script_data) & CACHE_LINE_MASK);
- np->scripth0 = &host_data->scripth_data;
+ m_calloc_dma(sizeof(struct script), "SCRIPT");
+ if (!np->script0)
+ goto attach_error;
+ np->scripth0 = (struct scripth *)
+ m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
+ if (!np->scripth0)
+ goto attach_error;
/*
** Initialize timer structure
@@ -4592,7 +3725,7 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
** can be used safely.
*/
- np->reg = virt_to_bus((struct ncr_reg*) np->vaddr);
+ np->reg = (struct ncr_reg*) np->vaddr;
#endif /* !defined NCR_IOMAPPED */
@@ -4608,12 +3741,12 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
switch(nvram->type) {
case SCSI_NCR_SYMBIOS_NVRAM:
#ifdef SCSI_NCR_DEBUG_NVRAM
- ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+ ncr_display_Symbios_nvram(&nvram->data.Symbios);
#endif
break;
case SCSI_NCR_TEKRAM_NVRAM:
#ifdef SCSI_NCR_DEBUG_NVRAM
- ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+ ncr_display_Tekram_nvram(&nvram->data.Tekram);
#endif
break;
default:
@@ -4665,13 +3798,14 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
ncr_script_fill (&script0, &scripth0);
np->scripth = np->scripth0;
- np->p_scripth = vtophys(np->scripth);
+ np->p_scripth = vtobus(np->scripth);
- np->p_script = (np->paddr2) ? bus_dvma_to_mem(np->paddr2) : vtophys(np->script0);
+ np->p_script = (np->paddr2) ?
+ pcivtobus(np->paddr2) : vtobus(np->script0);
ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
- np->ccb->p_ccb = vtophys (np->ccb);
+ np->ccb->p_ccb = vtobus (np->ccb);
/*
** Patch the script for LED support.
@@ -4807,6 +3941,8 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
attach_error:
if (!instance) return -1;
printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
+ if (!np)
+ goto unregister;
#ifndef NCR_IOMAPPED
if (np->vaddr) {
#ifdef DEBUG_NCR53C8XX
@@ -4832,6 +3968,15 @@ attach_error:
#endif
free_irq(np->irq, np);
}
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+
+unregister:
scsi_unregister(instance);
return -1;
@@ -4859,6 +4004,7 @@ attach_error:
*/
static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
{
+ unmap_scsi_data(np, cmd);
cmd->host_scribble = (char *) np->done_list;
np->done_list = cmd;
}
@@ -5114,7 +4260,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
**----------------------------------------------------
*/
- segments = ncr_scatter (cp, cp->cmd);
+ segments = ncr_scatter (np, cp, cp->cmd);
if (segments < 0) {
ncr_free_ccb(np, cp);
@@ -5123,47 +4269,24 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
/*----------------------------------------------------
**
- ** Guess xfer direction.
- ** Spare some CPU by testing here frequently opcode.
+ ** Determine xfer direction.
**
**----------------------------------------------------
*/
if (!cp->data_len)
- direction = 0;
- else {
- switch((int) cmd->cmnd[0]) {
- case 0x08: /* READ(6) 08 */
- case 0x28: /* READ(10) 28 */
- case 0xA8: /* READ(12) A8 */
- direction = XFER_IN;
- break;
- case 0x0A: /* WRITE(6) 0A */
- case 0x2A: /* WRITE(10) 2A */
- case 0xAA: /* WRITE(12) AA */
- direction = XFER_OUT;
- break;
- default:
- direction = (XFER_IN|XFER_OUT);
- break;
- }
- }
-
- /*----------------------------------------------------
- **
- ** Set the SAVED_POINTER.
- **
- **----------------------------------------------------
- */
-
- /*
- ** Default to no data transfer.
- */
- lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+ direction = SCSI_DATA_NONE;
+ else
+ direction = scsi_data_direction(cmd);
/*
- ** Compute data out pointers, if needed.
+ ** If data direction is UNKNOWN, speculate DATA_READ
+ ** but prepare alternate pointers for WRITE in case
+ ** of our speculation will be just wrong.
+ ** SCRIPTS will swap values if needed.
*/
- if (direction & XFER_OUT) {
+ switch(direction) {
+ case SCSI_DATA_UNKNOWN:
+ case SCSI_DATA_WRITE:
goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
if (segments <= MAX_SCATTERL)
lastp = goalp - 8 - (segments * 16);
@@ -5171,21 +4294,12 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
lastp -= (segments - MAX_SCATTERL) * 16;
}
- /*
- ** If actual data direction is unknown, save pointers
- ** in header. The SCRIPTS will swap them to current
- ** if target decision will be data out.
- */
- if (direction & XFER_IN) {
- cp->phys.header.wgoalp = cpu_to_scr(goalp);
- cp->phys.header.wlastp = cpu_to_scr(lastp);
- }
- }
-
- /*
- ** Compute data in pointers, if needed.
- */
- if (direction & XFER_IN) {
+ if (direction != SCSI_DATA_UNKNOWN)
+ break;
+ cp->phys.header.wgoalp = cpu_to_scr(goalp);
+ cp->phys.header.wlastp = cpu_to_scr(lastp);
+ /* fall through */
+ case SCSI_DATA_READ:
goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
if (segments <= MAX_SCATTERL)
lastp = goalp - 8 - (segments * 16);
@@ -5193,6 +4307,11 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
lastp -= (segments - MAX_SCATTERL) * 16;
}
+ break;
+ default:
+ case SCSI_DATA_NONE:
+ lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+ break;
}
/*
@@ -5202,7 +4321,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
cp->phys.header.lastp = cpu_to_scr(lastp);
cp->phys.header.goalp = cpu_to_scr(goalp);
- if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+ if (direction == SCSI_DATA_UNKNOWN)
cp->phys.header.savep =
cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
else
@@ -5241,14 +4360,13 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
*/
cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
cp->phys.smsg.size = cpu_to_scr(msglen);
-flush_cache_all();
/*
** command
*/
- cp->phys.cmd.addr = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+ memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
+ cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
- dma_cache_wback_inv((unsigned long)cmd->cmnd, cmd->cmd_len);
/*
** status
@@ -5276,9 +4394,6 @@ flush_cache_all();
** activate this job.
*/
cp->magic = CCB_MAGIC;
-//printk("cp == %08lx\n", cp);
- dma_cache_wback_inv((unsigned long)cp, sizeof(*cp));
-flush_cache_all();
/*
** insert next CCBs into start queue.
@@ -5350,7 +4465,6 @@ static void ncr_put_start_queue(ncb_p np, ccb_p cp)
if (DEBUG_FLAGS & DEBUG_QUEUE)
printk ("%s: queuepos=%d.\n", ncr_name (np), np->squeueput);
- dma_cache_wback_inv((unsigned long)np, sizeof(*np));
/*
** Script processor may be waiting for reselect.
@@ -5699,7 +4813,7 @@ static int ncr_detach(ncb_p np)
#ifdef DEBUG_NCR53C8XX
printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
#endif
- m_free(cp, sizeof(*cp));
+ m_free_dma(cp, sizeof(*cp), "CCB");
}
/*
@@ -5715,12 +4829,20 @@ static int ncr_detach(ncb_p np)
printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
#endif
if (lp->jump_ccb != &lp->jump_ccb_0)
- m_free(lp->jump_ccb, 256);
- m_free(lp, sizeof(*lp));
+ m_free_dma(lp->jump_ccb,256,"JUMP_CCB");
+ m_free_dma(lp, sizeof(*lp), "LCB");
}
}
}
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+
printk("%s: host resources successfully released\n", ncr_name(np));
return 1;
@@ -5874,6 +4996,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
*/
if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
cmd->cmnd[4] >= 7 && !cmd->use_sg) {
+ sync_scsi_data(np, cmd); /* SYNC the data */
ncr_setup_lcb (np, cmd->target, cmd->lun,
(char *) cmd->request_buffer);
}
@@ -5900,6 +5023,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
*/
cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
+ /*
+ ** Copy back sense data to caller's buffer.
+ */
+ memcpy(cmd->sense_buffer, cp->sense_buf,
+ MIN(sizeof(cmd->sense_buffer), sizeof(cp->sense_buf)));
+
if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
u_char * p = (u_char*) & cmd->sense_buffer;
int i;
@@ -6263,11 +5392,9 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code)
if (tp->usrwide > np->maxwide)
tp->usrwide = np->maxwide;
- dma_cache_wback_inv((unsigned long) tp, sizeof(*tp));
ncr_negotiate (np, tp);
}
- dma_cache_wback_inv((unsigned long) np, sizeof(*np));
/*
** Start script processor.
@@ -6277,7 +5404,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code)
if (bootverbose)
printk ("%s: Downloading SCSI SCRIPTS.\n",
ncr_name(np));
- OUTL (nc_scratcha, vtophys(np->script0));
+ OUTL (nc_scratcha, vtobus(np->script0));
OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, start_ram));
}
else
@@ -6661,7 +5788,6 @@ static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln)
lp->jump_tag.l_paddr = lp->usetags?
cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_tag)) :
cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_notag));
-flush_cache_all();
/*
** Announce change to user.
@@ -6792,7 +5918,12 @@ static void ncr_timeout (ncb_p np)
return;
}
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ np->ktime = thistime;
+ np->timer.expires = ktime_get(1);
+#else
np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
+#endif
add_timer(&np->timer);
/*
@@ -6959,7 +6090,6 @@ void ncr_exception (ncb_p np)
u_char istat, dstat;
u_short sist;
int i;
-flush_cache_all();
/*
** interrupt on the fly ?
@@ -7117,7 +6247,6 @@ flush_cache_all();
if (sist & UDC) {
printk ("%s: unexpected disconnect\n", ncr_name(np));
OUTB (HS_PRT, HS_UNEXPECTED);
-//flush_cache_all(); // ???
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
return;
};
@@ -7170,7 +6299,6 @@ void ncr_int_sto (ncb_p np)
** repair start queue and jump to start point.
*/
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sto_restart));
return;
}
@@ -7310,6 +6438,7 @@ static void ncr_int_ma (ncb_p np)
u_int32 dsp;
u_int32 dsa;
u_int32 nxtdsp;
+ u_int32 newtmp;
u_int32 *vdsp;
u_int32 oadr, olen;
u_int32 *tblp;
@@ -7404,11 +6533,11 @@ static void ncr_int_ma (ncb_p np)
nxtdsp = dsp;
}
else if (cp) {
- if (dsp == vtophys (&cp->patch[2])) {
+ if (dsp == CCB_PHYS (cp, patch[2])) {
vdsp = &cp->patch[0];
nxtdsp = scr_to_cpu(vdsp[3]);
}
- else if (dsp == vtophys (&cp->patch[6])) {
+ else if (dsp == CCB_PHYS (cp, patch[6])) {
vdsp = &cp->patch[4];
nxtdsp = scr_to_cpu(vdsp[3]);
}
@@ -7503,7 +6632,11 @@ static void ncr_int_ma (ncb_p np)
*/
newcmd = cp->patch;
- if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+ newtmp = CCB_PHYS (cp, patch);
+ if (newtmp == scr_to_cpu(cp->phys.header.savep)) {
+ newcmd = &cp->patch[4];
+ newtmp = CCB_PHYS (cp, patch[4]);
+ }
/*
** fillin the commands
@@ -7530,7 +6663,7 @@ static void ncr_int_ma (ncb_p np)
#ifdef SCSI_NCR_PROFILE_SUPPORT
np->profile.num_break++;
#endif
- OUTL (nc_temp, vtophys (newcmd));
+ OUTL (nc_temp, newtmp);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
return;
@@ -7699,19 +6832,14 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp)
*/
cp->sensecmd[0] = 0x03;
cp->sensecmd[1] = cmd->lun << 5;
- cp->sensecmd[4] = sizeof(cmd->sense_buffer);
- dma_cache_wback_inv((unsigned long)cmd->sense_buffer,
- sizeof(cmd->sense_buffer));
+ cp->sensecmd[4] = sizeof(cp->sense_buf);
/*
** sense data
*/
- cp->phys.sense.addr =
- cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
- cp->phys.sense.size =
- cpu_to_scr(sizeof(cmd->sense_buffer));
- dma_cache_wback_inv((unsigned long)cmd->sense_buffer,
- sizeof(cmd->sense_buffer));
+ bzero(cp->sense_buf, sizeof(cp->sense_buf));
+ cp->phys.sense.addr = cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cp->sense_buf));
/*
** requeue the command.
@@ -7745,8 +6873,6 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp)
}
out:
-flush_cache_all(); // ???
-//dma_cache_wback_inv((unsigned long)cmd->cmnd, cmd->cmd_len);
OUTONB (nc_dcntl, (STD|NOCOM));
return;
}
@@ -7837,7 +6963,6 @@ void ncr_int_sir (ncb_p np)
}
switch (num) {
-flush_cache_all(); // ???
/*-----------------------------------------------------------------------------
**
** Was Sie schon immer ueber transfermode negotiation wissen wollten ...
@@ -7943,7 +7068,6 @@ flush_cache_all(); // ???
np->msgin [0] = M_NOOP;
np->msgout[0] = M_NOOP;
cp->nego_status = 0;
-flush_cache_all();
break;
case SIR_NEGO_SYNC:
@@ -8024,14 +7148,12 @@ flush_cache_all();
** Answer wasn't acceptable.
*/
ncr_setsync (np, cp, 0, 0xe0);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
@@ -8065,7 +7187,6 @@ flush_cache_all();
}
if (!ofs) {
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
return;
}
@@ -8124,14 +7245,12 @@ flush_cache_all();
** Answer wasn't acceptable.
*/
ncr_setwide (np, cp, 0, 1);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
ncr_setwide (np, cp, wide, 1);
-flush_cache_all();
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
@@ -8429,7 +7548,7 @@ static void ncr_free_ccb (ncb_p np, ccb_p cp)
#define ncr_reg_bus_addr(r) \
- (bus_dvma_to_mem(np->paddr) + offsetof (struct ncr_reg, r))
+ (pcivtobus(np->paddr) + offsetof (struct ncr_reg, r))
/*------------------------------------------------------------------------
** Initialize the fixed part of a CCB structure.
@@ -8443,7 +7562,7 @@ static void ncr_init_ccb(ncb_p np, ccb_p cp)
/*
** Remember virtual and bus address of this ccb.
*/
- cp->p_ccb = vtophys(cp);
+ cp->p_ccb = vtobus(cp);
cp->phys.header.cp = cp;
/*
@@ -8458,10 +7577,10 @@ static void ncr_init_ccb(ncb_p np, ccb_p cp)
** JUMP @(sched_point)
*/
cp->start.setup_dsa[0] = cpu_to_scr(copy_4);
- cp->start.setup_dsa[1] = cpu_to_scr(vtophys(&cp->start.p_phys));
+ cp->start.setup_dsa[1] = cpu_to_scr(CCB_PHYS(cp, start.p_phys));
cp->start.setup_dsa[2] = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
- cp->start.p_phys = cpu_to_scr(vtophys(&cp->phys));
+ cp->start.p_phys = cpu_to_scr(CCB_PHYS(cp, phys));
bcopy(&cp->start, &cp->restart, sizeof(cp->restart));
@@ -8484,15 +7603,10 @@ static void ncr_alloc_ccb(ncb_p np, u_char tn, u_char ln)
/*
** Allocate memory for this CCB.
*/
- cp = m_alloc(sizeof(struct ccb), 5);
+ cp = m_calloc_dma(sizeof(struct ccb), "CCB");
if (!cp)
return;
- if (DEBUG_FLAGS & DEBUG_ALLOC) {
- PRINT_LUN(np, tn, ln);
- printk ("new ccb @%p.\n", cp);
- }
-
/*
** Count it and initialyze it.
*/
@@ -8510,7 +7624,6 @@ static void ncr_alloc_ccb(ncb_p np, u_char tn, u_char ln)
xpt_insque_head(&cp->link_ccbq, &lp->free_ccbq);
ncr_setup_tags (np, tn, ln);
-flush_cache_all();
}
/*==========================================================
@@ -8551,7 +7664,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
** COPY @(tp->sval), @(sxfer)
*/
tp->getscr[0] = cpu_to_scr(copy_1);
- tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+ tp->getscr[1] = cpu_to_scr(vtobus (&tp->sval));
tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
/*
@@ -8559,7 +7672,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
** COPY @(tp->wval), @(scntl3)
*/
tp->getscr[3] = cpu_to_scr(copy_1);
- tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+ tp->getscr[4] = cpu_to_scr(vtobus (&tp->wval));
tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
/*
@@ -8584,7 +7697,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn)
/*
** Link this target control block to the JUMP chain.
*/
- np->jump_tcb[th].l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+ np->jump_tcb[th].l_paddr = cpu_to_scr(vtobus (&tp->jump_tcb));
/*
** These assert's should be moved at driver initialisations.
@@ -8619,17 +7732,12 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
/*
** Allocate the lcb.
*/
- lp = m_alloc(sizeof(struct lcb), 3);
+ lp = m_calloc_dma(sizeof(struct lcb), "LCB");
if (!lp)
goto fail;
bzero(lp, sizeof(*lp));
tp->lp[ln] = lp;
- if (DEBUG_FLAGS & DEBUG_ALLOC) {
- PRINT_LUN(np, tn, ln);
- printk ("new lcb @%p.\n", lp);
- }
-
/*
** Initialize the target control block if not yet.
*/
@@ -8650,7 +7758,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
*/
lp->maxnxs = 1;
lp->jump_ccb = &lp->jump_ccb_0;
- lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
/*
** Initilialyze the reselect script:
@@ -8668,7 +7776,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
- lp->load_jump_ccb[1] = cpu_to_scr(vtophys (&lp->p_jump_ccb));
+ lp->load_jump_ccb[1] = cpu_to_scr(vtobus (&lp->p_jump_ccb));
lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
lp->jump_tag.l_cmd = cpu_to_scr(SCR_JUMP);
@@ -8677,7 +7785,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
/*
** Link this lun control block to the JUMP chain.
*/
- tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
+ tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtobus (&lp->jump_lcb));
/*
** Initialize command queuing control.
@@ -8761,12 +7869,12 @@ static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data)
*/
if ((inq_byte7 & INQ7_QUEUE) && lp->jump_ccb == &lp->jump_ccb_0) {
int i;
- lp->jump_ccb = m_alloc(256, 8);
+ lp->jump_ccb = m_calloc_dma(256, "JUMP_CCB");
if (!lp->jump_ccb) {
lp->jump_ccb = &lp->jump_ccb_0;
goto fail;
}
- lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
for (i = 0 ; i < 64 ; i++)
lp->jump_ccb[i] =
cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
@@ -8818,7 +7926,7 @@ fail:
** sizes to the data segment array.
*/
-static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+static int ncr_scatter(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd)
{
struct scr_tblmove *data;
int segment = 0;
@@ -8829,32 +7937,28 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
if (!use_sg) {
if (cmd->request_bufflen) {
- unsigned long addr, len;
+ u_long baddr = map_scsi_single_data(np, cmd);
- addr = cmd->request_buffer;
- len = cmd->request_bufflen;
data = &data[MAX_SCATTER - 1];
- data[0].addr = cpu_to_scr(vtophys(addr));
- data[0].size = cpu_to_scr(len);
- if (addr)
- dma_cache_wback_inv(addr, len);
- cp->data_len = len;
+ data[0].addr = cpu_to_scr(baddr);
+ data[0].size = cpu_to_scr(cmd->request_bufflen);
+ cp->data_len = cmd->request_bufflen;
segment = 1;
}
}
else if (use_sg <= MAX_SCATTER) {
struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+ use_sg = map_scsi_sg_data(np, cmd);
data = &data[MAX_SCATTER - use_sg];
+
while (segment < use_sg) {
- unsigned long addr, len;
+ u_long baddr = scsi_sg_dma_address(&scatter[segment]);
+ unsigned int len = scsi_sg_dma_len(&scatter[segment]);
- addr = scatter[segment].address;
- len = scatter[segment].length;
- data[segment].addr = cpu_to_scr(vtophys(addr));
+ data[segment].addr = cpu_to_scr(baddr);
data[segment].size = cpu_to_scr(len);
- dma_cache_wback_inv(addr, len);
- cp->data_len += len;
+ cp->data_len += len;
++segment;
}
}
@@ -8862,7 +7966,6 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
return -1;
}
- dma_cache_wback_inv(data, sizeof(*data) * segment);
return segment;
}
@@ -8922,7 +8025,6 @@ static int __init ncr_snooptest (struct ncb* np)
** Set memory and register.
*/
np->ncr_cache = cpu_to_scr(host_wr);
- dma_cache_wback_inv((unsigned long)np, sizeof(*np));
OUTL (nc_temp, ncr_wr);
/*
** Start script (exchange values)
@@ -9277,833 +8379,10 @@ static void __init ncr_getclock (ncb_p np, int mult)
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
-#ifndef uchar
-#define uchar unsigned char
-#endif
-
-#ifndef ushort
-#define ushort unsigned short
-#endif
-
-#ifndef ulong
-#define ulong unsigned long
-#endif
-
-/* ---------------------------------------------------------------------
-**
-** Driver setup from the boot command line
-**
-** ---------------------------------------------------------------------
-*/
-
-#ifdef MODULE
-#define ARG_SEP ' '
-#else
-#define ARG_SEP ','
-#endif
-
-int __init ncr53c8xx_setup(char *str)
-{
-#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
- char *cur = str;
- char *pc, *pv;
- int val;
- int base;
- int c;
- int xi = 0;
-
- while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
- char *pe;
-
- val = 0;
- pv = pc;
- c = *++pv;
-
- if (c == 'n')
- val = 0;
- else if (c == 'y')
- val = 1;
- else {
- base = 0;
- val = (int) simple_strtoul(pv, &pe, base);
- }
- if (!strncmp(cur, "tags:", 5)) {
- int i;
- driver_setup.default_tags = val;
- if (pe && *pe == '/') {
- i = 0;
- while (*pe && *pe != ARG_SEP &&
- i < sizeof(driver_setup.tag_ctrl)-1) {
- driver_setup.tag_ctrl[i++] = *pe++;
- }
- driver_setup.tag_ctrl[i] = '\0';
- }
- }
- else if (!strncmp(cur, "mpar:", 5))
- driver_setup.master_parity = val;
- else if (!strncmp(cur, "spar:", 5))
- driver_setup.scsi_parity = val;
- else if (!strncmp(cur, "disc:", 5))
- driver_setup.disconnection = val;
- else if (!strncmp(cur, "specf:", 6))
- driver_setup.special_features = val;
- else if (!strncmp(cur, "ultra:", 6))
- driver_setup.ultra_scsi = val;
- else if (!strncmp(cur, "fsn:", 4))
- driver_setup.force_sync_nego = val;
- else if (!strncmp(cur, "revprob:", 8))
- driver_setup.reverse_probe = val;
- else if (!strncmp(cur, "sync:", 5))
- driver_setup.default_sync = val;
- else if (!strncmp(cur, "verb:", 5))
- driver_setup.verbose = val;
- else if (!strncmp(cur, "debug:", 6))
- driver_setup.debug = val;
- else if (!strncmp(cur, "burst:", 6))
- driver_setup.burst_max = val;
- else if (!strncmp(cur, "led:", 4))
- driver_setup.led_pin = val;
- else if (!strncmp(cur, "wide:", 5))
- driver_setup.max_wide = val? 1:0;
- else if (!strncmp(cur, "settle:", 7))
- driver_setup.settle_delay= val;
- else if (!strncmp(cur, "diff:", 5))
- driver_setup.diff_support= val;
- else if (!strncmp(cur, "irqm:", 5))
- driver_setup.irqm = val;
- else if (!strncmp(cur, "pcifix:", 7))
- driver_setup.pci_fix_up = val;
- else if (!strncmp(cur, "buschk:", 7))
- driver_setup.bus_check = val;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- else if (!strncmp(cur, "nvram:", 6))
- driver_setup.use_nvram = val;
-#endif
-
- else if (!strncmp(cur, "safe:", 5) && val)
- memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
- else if (!strncmp(cur, "excl:", 5)) {
- if (xi < SCSI_NCR_MAX_EXCLUDES)
- driver_setup.excludes[xi++] = val;
- }
- else if (!strncmp(cur, "hostid:", 7))
- driver_setup.host_id = val;
- else
- printk("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
-
- if ((cur = strchr(cur, ARG_SEP)) != NULL)
- ++cur;
- }
-#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
- return 0;
-}
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
-#ifndef MODULE
-__setup("ncr53c8xx=", ncr53c8xx_setup);
-#endif
-#endif
-
-static int
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device);
-
-/*
-** Linux entry point for NCR53C8XX devices detection routine.
-**
-** Called by the middle-level scsi drivers at initialization time,
-** or at module installation.
-**
-** Read the PCI configuration and try to attach each
-** detected NCR board.
-**
-** If NVRAM is present, try to attach boards according to
-** the used defined boot order.
-**
-** Returns the number of boards successfully attached.
-*/
-
-static void __init ncr_print_driver_setup(void)
-{
-#define YesNo(y) y ? 'y' : 'n'
- printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
- "burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
- YesNo(driver_setup.disconnection),
- driver_setup.special_features,
- driver_setup.ultra_scsi,
- driver_setup.default_tags,
- driver_setup.default_sync,
- driver_setup.burst_max,
- YesNo(driver_setup.max_wide),
- driver_setup.diff_support,
- YesNo(driver_setup.reverse_probe),
- driver_setup.bus_check);
-
- printk ("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
- "led:%c,settle:%d,irqm:%d,nvram:0x%x,pcifix:0x%x\n",
- YesNo(driver_setup.master_parity),
- YesNo(driver_setup.scsi_parity),
- YesNo(driver_setup.force_sync_nego),
- driver_setup.verbose,
- driver_setup.debug,
- YesNo(driver_setup.led_pin),
- driver_setup.settle_delay,
- driver_setup.irqm,
- driver_setup.use_nvram,
- driver_setup.pci_fix_up);
-#undef YesNo
-}
-
-/*
-** NCR53C8XX devices description table and chip ids list.
-*/
-
-static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
-static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
-
-
-/*===================================================================
-** Detect all 53c8xx hosts and then attach them.
-**
-** If we are using NVRAM, once all hosts are detected, we need to
-** check any NVRAM for boot order in case detect and boot order
-** differ and attach them using the order in the NVRAM.
-**
-** If no NVRAM is found or data appears invalid attach boards in
-** the the order they are detected.
-**===================================================================
-*/
-int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
-{
- pcidev_t pcidev;
- int i, j, chips, hosts, count;
- int attach_count = 0;
- ncr_device *devtbl, *devp;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- ncr_nvram nvram0, nvram, *nvp;
-#endif
-
- /*
- ** PCI is required.
- */
- if (!pci_present())
- return 0;
-
- /*
- ** Initialize driver general stuff.
- */
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
- tpnt->proc_dir = &proc_scsi_ncr53c8xx;
-#else
- tpnt->proc_name = "ncr53c8xx";
-#endif
- tpnt->proc_info = ncr53c8xx_proc_info;
-#endif
-
-#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
-if (ncr53c8xx)
- ncr53c8xx_setup(ncr53c8xx);
-#endif
-#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
- ncr_debug = driver_setup.debug;
-#endif
-
- if (initverbose >= 2)
- ncr_print_driver_setup();
-
- /*
- ** Allocate the device table since we donnot want to
- ** overflow the kernel stack.
- ** 1 x 4K PAGE is enough for more than 40 devices for i386.
- */
- devtbl = kmalloc(4000, GFP_ATOMIC);
- if (!devtbl)
- return 0;
-
- /*
- ** Detect all 53c8xx hosts.
- ** Save the first Symbios NVRAM content if any
- ** for the boot order.
- */
- chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
- hosts = 4000 / sizeof(*devtbl);
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
-#endif
- j = 0;
- count = 0;
- pcidev = PCIDEV_NULL;
- while (1) {
- char *msg = "";
- if (count >= hosts)
- break;
- if (j >= chips)
- break;
- i = driver_setup.reverse_probe ? chips - 1 - j : j;
- pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
- pcidev);
- if (pcidev == PCIDEV_NULL) {
- ++j;
- continue;
- }
- /* Some HW as the HP LH4 may report twice PCI devices */
- for (i = 0; i < count ; i++) {
- if (devtbl[i].slot.bus == PciBusNumber(pcidev) &&
- devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
- break;
- }
- if (i != count) /* Ignore this device if we already have it */
- continue;
- devp = &devtbl[count];
- devp->host_id = driver_setup.host_id;
- devp->attach_done = 0;
- if (ncr53c8xx_pci_init(tpnt, pcidev, devp)) {
- continue;
- }
- ++count;
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- if (nvp) {
- ncr_get_nvram(devp, nvp);
- switch(nvp->type) {
- case SCSI_NCR_SYMBIOS_NVRAM:
- /*
- * Switch to the other nvram buffer, so that
- * nvram0 will contain the first Symbios
- * format NVRAM content with boot order.
- */
- nvp = &nvram;
- msg = "with Symbios NVRAM";
- break;
- case SCSI_NCR_TEKRAM_NVRAM:
- msg = "with Tekram NVRAM";
- break;
- }
- }
-#endif
- printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
- devp->chip.name, msg);
- }
-
- /*
- ** If we have found a SYMBIOS NVRAM, use first the NVRAM boot
- ** sequence as device boot order.
- ** check devices in the boot record against devices detected.
- ** attach devices if we find a match. boot table records that
- ** do not match any detected devices will be ignored.
- ** devices that do not match any boot table will not be attached
- ** here but will attempt to be attached during the device table
- ** rescan.
- */
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
- goto next;
- for (i = 0; i < 4; i++) {
- Symbios_host *h = &nvram0.data.Symbios.host[i];
- for (j = 0 ; j < count ; j++) {
- devp = &devtbl[j];
- if (h->device_fn != devp->slot.device_fn ||
- h->bus_nr != devp->slot.bus ||
- h->device_id != devp->chip.device_id)
- continue;
- if (devp->attach_done)
- continue;
- ncr_get_nvram(devp, nvp);
- if (!ncr_attach (tpnt, attach_count, devp))
- attach_count++;
- devp->attach_done = 1;
- break;
- }
- }
-next:
-#endif
-
- /*
- ** Rescan device list to make sure all boards attached.
- ** Devices without boot records will not be attached yet
- ** so try to attach them here.
- */
- for (i= 0; i < count; i++) {
- devp = &devtbl[i];
- if (!devp->attach_done) {
-#ifdef SCSI_NCR_NVRAM_SUPPORT
- ncr_get_nvram(devp, nvp);
-#endif
- if (!ncr_attach (tpnt, attach_count, devp))
- attach_count++;
- }
- }
-
- kfree(devtbl);
-
- return attach_count;
-}
-
-/*===================================================================
-** Detect and try to read SYMBIOS and TEKRAM NVRAM.
-**
-** Data can be used to order booting of boards.
-**
-** Data is saved in ncr_device structure if NVRAM found. This
-** is then used to find drive boot order for ncr_attach().
-**
-** NVRAM data is passed to Scsi_Host_Template later during
-** ncr_attach() for any device set up.
-*===================================================================
-*/
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
-{
- devp->nvram = nvp;
- if (!nvp)
- return;
- /*
- ** Get access to chip IO registers
- */
-#ifdef NCR_IOMAPPED
- request_region(devp->slot.io_port, 128, "ncr53c8xx");
- devp->slot.port = devp->slot.io_port;
-#else
- devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
- if (!devp->slot.reg)
- return;
-#endif
-
- /*
- ** Try to read SYMBIOS nvram.
- ** Try to read TEKRAM nvram if Symbios nvram not found.
- */
- if (!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
- nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
- else if (!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram))
- nvp->type = SCSI_NCR_TEKRAM_NVRAM;
- else {
- nvp->type = 0;
- devp->nvram = 0;
- }
-
- /*
- ** Release access to chip IO registers
- */
-#ifdef NCR_IOMAPPED
- release_region(devp->slot.port, 128);
-#else
- unmap_pci_mem((u_long) devp->slot.reg, 128ul);
-#endif
-
-}
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
-
-/*
-** Read and check the PCI configuration for any detected NCR
-** boards and save data for attaching after all boards have
-** been detected.
-*/
-
-static int __init
-ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
-{
- ushort vendor_id, device_id, command;
- uchar cache_line_size, latency_timer;
- uchar revision;
- uint irq;
- ulong base, base_2, io_port;
- int i;
- ncr_chip *chip;
-
- /*
- ** Read info from the PCI config space.
- ** pci_read_config_xxx() functions are assumed to be used for
- ** successfully detected PCI devices.
- */
- vendor_id = PciVendorId(pdev);
- device_id = PciDeviceId(pdev);
- irq = PciIrqLine(pdev);
- i = 0;
- i = pci_get_base_address(pdev, i, &io_port);
- i = pci_get_base_address(pdev, i, &base);
- (void) pci_get_base_address(pdev, i, &base_2);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
-
- /*
- ** If user excludes this chip, donnot initialize it.
- */
- for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
- if (driver_setup.excludes[i] ==
- (io_port & PCI_BASE_ADDRESS_IO_MASK))
- return -1;
- }
- /*
- * Check if the chip is supported
- */
- chip = 0;
- for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
- if (device_id != ncr_chip_table[i].device_id)
- continue;
- if (revision > ncr_chip_table[i].revision_id)
- continue;
- chip = &device->chip;
- memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
- chip->revision_id = revision;
- break;
- }
-
-#if defined(__i386__)
- /*
- * Ignore Symbios chips controlled by SISL RAID controller.
- */
- if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
- unsigned int ScriptsSize, MagicValue;
- vm_offset_t ScriptsRAM;
-
- if (chip->features & FE_RAM8K)
- ScriptsSize = 8192;
- else
- ScriptsSize = 4096;
-
- ScriptsRAM = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
- ScriptsSize);
- if (ScriptsRAM) {
- MagicValue = readl(ScriptsRAM + ScriptsSize - 16);
- unmap_pci_mem(ScriptsRAM, ScriptsSize);
- if (MagicValue == 0x52414944)
- return -1;
- }
- }
-#endif
-
- printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
- PciBusNumber(pdev),
- (int) (PciDeviceFn(pdev) & 0xf8) >> 3,
- (int) (PciDeviceFn(pdev) & 0x7));
-
- if (!chip) {
- printk("ncr53c8xx: not initializing, device not supported\n");
- return -1;
- }
-
-#ifdef __powerpc__
- /*
- * Several fix-up for power/pc.
- * Should not be performed by the driver.
- */
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER...");
- command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
- if (!(command & PCI_COMMAND_IO)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_IO...");
- command |= PCI_COMMAND_IO;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_IO)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
- if (!(command & PCI_COMMAND_MEMORY)) {
- printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY...");
- command |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & PCI_COMMAND_MEMORY)) {
- printk("failed!\n");
- } else {
- printk("succeeded.\n");
- }
- }
-
-
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,140)
- if ( is_prep ) {
- if (io_port >= 0x10000000) {
- printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
- io_port = (io_port & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, io_port);
- }
- if (base >= 0x10000000) {
- printk("ncr53c8xx: reallocating base (Wacky IBM)");
- base = (base & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base);
- }
- if (base_2 >= 0x10000000) {
- printk("ncr53c8xx: reallocating base2 (Wacky IBM)");
- base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
- pci_write_config_dword(pdev, PCI_BASE_ADDRESS_2, base_2);
- }
- }
-#endif
-#endif /* __powerpc__ */
-
-#ifdef __sparc__
- /*
- * Severall fix-ups for sparc.
- *
- * Should not be performed by the driver, but how can OBP know
- * each and every PCI card, if they don't use Fcode?
- */
-
- base = __pa(base);
- base_2 = __pa(base_2);
-
- if (!(command & PCI_COMMAND_MASTER)) {
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
- command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- }
-
- if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
- command |= PCI_COMMAND_INVALIDATE;
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- }
-
- if ((chip->features & FE_CLSE) && !cache_line_size) {
- /* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
- cache_line_size = 64 / sizeof(u_int32);
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
- }
-
- if (!latency_timer) {
- latency_timer = 128;
- if (initverbose >= 2)
- printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
- }
-#endif /* __sparc__ */
-
- /*
- * Check availability of IO space, memory space and master capability.
- */
- if (command & PCI_COMMAND_IO)
- io_port &= PCI_BASE_ADDRESS_IO_MASK;
- else
- io_port = 0;
-
- if (command & PCI_COMMAND_MEMORY)
- base &= PCI_BASE_ADDRESS_MEM_MASK;
- else
- base = 0;
-
- if (!io_port && !base) {
- printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
- return -1;
- }
-
- base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
-
- if (io_port && check_region (io_port, 128)) {
-#ifdef __sparc__
- printk("ncr53c8xx: IO region 0x%lx to 0x%lx is in use\n",
- io_port, (io_port + 127));
-#else
- printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
- (int) io_port, (int) (io_port + 127));
-#endif
- return -1;
- }
-
- if (!(command & PCI_COMMAND_MASTER)) {
- printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
- return -1;
- }
-
- /*
- * Fix some features according to driver setup.
- */
- if (!(driver_setup.special_features & 1))
- chip->features &= ~FE_SPECIAL_SET;
- else {
- if (driver_setup.special_features & 2)
- chip->features &= ~FE_WRIE;
- }
- if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
- chip->features |= FE_ULTRA;
- chip->features &= ~FE_ULTRA2;
- }
- if (driver_setup.ultra_scsi < 1)
- chip->features &= ~FE_ULTRA;
- if (!driver_setup.max_wide)
- chip->features &= ~FE_WIDE;
-
-
-#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
-
- /*
- * Try to fix up PCI config according to wished features.
- */
-#if defined(__i386__) && !defined(MODULE)
- if ((driver_setup.pci_fix_up & 1) &&
- (chip->features & FE_CLSE) && cache_line_size == 0) {
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
- extern char x86;
- switch(x86) {
-#else
- switch(boot_cpu_data.x86) {
-#endif
- case 4: cache_line_size = 4; break;
- case 6:
- case 5: cache_line_size = 8; break;
- }
- if (cache_line_size)
- (void) pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cache_line_size);
- if (initverbose)
- printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
- }
-
- if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
- (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- command |= PCI_COMMAND_INVALIDATE;
- (void) pci_write_config_word(pdev, PCI_COMMAND, command);
- if (initverbose)
- printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
- }
-#endif
- /*
- * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
- * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
- * and donnot enable READ LINE.
- * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
- */
-
- if (!(chip->features & FE_CLSE)) {
- int burst_max = chip->burst_max;
- if (cache_line_size == 0) {
- chip->features &= ~FE_ERL;
- if (burst_max > 3)
- burst_max = 3;
- }
- else {
- while (cache_line_size < (1 << burst_max))
- --burst_max;
- }
- chip->burst_max = burst_max;
- }
-
- /*
- * Tune PCI LATENCY TIMER according to burst max length transfer.
- * (latency timer >= burst length + 6, we add 10 to be quite sure)
- * If current value is zero, the device has probably been configured
- * for no bursting due to some broken hardware.
- */
-
- if (latency_timer == 0 && chip->burst_max)
- printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
-
- if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
- uchar lt = (1 << chip->burst_max) + 6 + 10;
- if (latency_timer < lt) {
- latency_timer = lt;
- if (initverbose)
- printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
- (void) pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer);
- }
- }
-
- /*
- * Fix up for recent chips that support CACHE LINE SIZE.
- * If PCI config space is not OK, remove features that shall not be
- * used by the chip. No need to trigger possible chip bugs.
- */
-
- if ((chip->features & FE_CLSE) && cache_line_size == 0) {
- chip->features &= ~FE_CACHE_SET;
- printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
- }
-
- if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
- chip->features &= ~FE_WRIE;
- printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
- }
-
-#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
-
- /* initialise ncr_device structure with items required by ncr_attach */
- device->slot.bus = PciBusNumber(pdev);
- device->slot.device_fn = PciDeviceFn(pdev);
- device->slot.base = base;
- device->slot.base_2 = base_2;
- device->slot.io_port = io_port;
- device->slot.irq = irq;
- device->attach_done = 0;
-
- return 0;
-}
-
/*
** Linux select queue depths function
*/
-#define DEF_DEPTH (driver_setup.default_tags)
-#define ALL_TARGETS -2
-#define NO_TARGET -1
-#define ALL_LUNS -2
-#define NO_LUN -1
-
-static int device_queue_depth(ncb_p np, int target, int lun)
-{
- int c, h, t, u, v;
- char *p = driver_setup.tag_ctrl;
- char *ep;
-
- h = -1;
- t = NO_TARGET;
- u = NO_LUN;
- while ((c = *p++) != 0) {
- v = simple_strtoul(p, &ep, 0);
- switch(c) {
- case '/':
- ++h;
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- case 't':
- if (t != target)
- t = (target == v) ? v : NO_TARGET;
- u = ALL_LUNS;
- break;
- case 'u':
- if (u != lun)
- u = (lun == v) ? v : NO_LUN;
- break;
- case 'q':
- if (h == np->unit &&
- (t == ALL_TARGETS || t == target) &&
- (u == ALL_LUNS || u == lun))
- return v;
- break;
- case '-':
- t = ALL_TARGETS;
- u = ALL_LUNS;
- break;
- default:
- break;
- }
- p = ep;
- }
- return DEF_DEPTH;
-}
-
static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
{
struct scsi_device *device;
@@ -10127,7 +8406,7 @@ static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de
** Use at least 2.
** Donnot use more than our maximum.
*/
- numtags = device_queue_depth(np, device->id, device->lun);
+ numtags = device_queue_depth(np->unit, device->id, device->lun);
if (numtags > tp->usrtags)
numtags = tp->usrtags;
if (!device->tagged_supported)
@@ -10157,14 +8436,6 @@ printk("ncr53c8xx_select_queue_depth: host=%d, id=%d, lun=%d, depth=%d\n",
}
/*
-** Linux entry point for info() function
-*/
-const char *ncr53c8xx_info (struct Scsi_Host *host)
-{
- return SCSI_NCR_DRIVER_NAME;
-}
-
-/*
** Linux entry point of queuecommand() function
*/
@@ -10180,6 +8451,10 @@ printk("ncr53c8xx_queue_command\n");
cmd->scsi_done = done;
cmd->host_scribble = NULL;
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ cmd->__data_mapped = 0;
+ cmd->__data_mapping = 0;
+#endif
NCR_LOCK_NCB(np, flags);
@@ -10196,8 +8471,10 @@ printk("ncr53c8xx : command successfully queued\n");
NCR_UNLOCK_NCB(np, flags);
- if (sts != DID_OK)
+ if (sts != DID_OK) {
+ unmap_scsi_data(np, cmd);
done(cmd);
+ }
return sts;
}
@@ -10208,11 +8485,9 @@ printk("ncr53c8xx : command successfully queued\n");
** passing the internal host descriptor as 'dev_id'.
** Otherwise, we scan the host list and call the interrupt
** routine for each host that uses this IRQ.
-**
-** Exported for certain MIPS machines with a dedicated NCR interrupt.
*/
-void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
{
unsigned long flags;
ncb_p np = (ncb_p) dev_id;
@@ -10458,7 +8733,6 @@ static void process_waiting_list(ncb_p np, int sts)
printk("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
#endif
wcmd->result = ScsiResult(sts, 0);
-//flush_cache_all();
ncr_queue_done_cmd(np, wcmd);
}
}
@@ -10676,50 +8950,8 @@ printk("ncr_user_command: data=%ld\n", uc->data);
#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */
-#ifdef SCSI_NCR_USER_INFO_SUPPORT
-
-struct info_str
-{
- char *buffer;
- int length;
- int offset;
- int pos;
-};
-
-static void copy_mem_info(struct info_str *info, char *data, int len)
-{
- if (info->pos + len > info->length)
- len = info->length - info->pos;
-
- if (info->pos + len < info->offset) {
- info->pos += len;
- return;
- }
- if (info->pos < info->offset) {
- data += (info->offset - info->pos);
- len -= (info->offset - info->pos);
- }
-
- if (len > 0) {
- memcpy(info->buffer + info->pos, data, len);
- info->pos += len;
- }
-}
-
-static int copy_info(struct info_str *info, char *fmt, ...)
-{
- va_list args;
- char buf[81];
- int len;
-
- va_start(args, fmt);
- len = vsprintf(buf, fmt, args);
- va_end(args);
-
- copy_mem_info(info, buf, len);
- return len;
-}
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
/*
** Copy formatted profile information into the input buffer.
*/
@@ -10827,7 +9059,6 @@ printk("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
return retv;
}
-
/*=========================================================================
** End of proc file system stuff
**=========================================================================
@@ -10835,432 +9066,105 @@ printk("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
#endif
-#ifdef SCSI_NCR_NVRAM_SUPPORT
-
-/* ---------------------------------------------------------------------
+/*==========================================================
**
-** Try reading Symbios format nvram
+** /proc directory entry.
**
-** ---------------------------------------------------------------------
+**==========================================================
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_ncr53c8xx = {
+ PROC_SCSI_NCR53C8XX, 9, NAME53C8XX,
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+/*==========================================================
**
-** GPOI0 - data in/data out
-** GPIO1 - clock
+** Boot command line.
**
-** return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**==========================================================
*/
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(ncr53c8xx, "s");
+# endif
+#endif
-#define SET_BIT 0
-#define CLR_BIT 1
-#define SET_CLK 2
-#define CLR_CLK 3
-
-static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
-static void nvram_start(ncr_slot *np, u_char *gpreg);
-static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
-static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
-static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
-static void nvram_stop(ncr_slot *np, u_char *gpreg);
-static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
-
-static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
-{
- static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
- u_char gpcntl, gpreg;
- u_char old_gpcntl, old_gpreg;
- u_short csum;
- u_char ack_data;
- int retv = 1;
-
- /* save current state of GPCNTL and GPREG */
- old_gpreg = INB (nc_gpreg);
- old_gpcntl = INB (nc_gpcntl);
- gpcntl = old_gpcntl & 0xfc;
-
- /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
- OUTB (nc_gpreg, old_gpreg);
- OUTB (nc_gpcntl, gpcntl);
-
- /* this is to set NVRAM into a known state with GPIO0/1 both low */
- gpreg = old_gpreg;
- nvram_setBit(np, 0, &gpreg, CLR_CLK);
- nvram_setBit(np, 0, &gpreg, CLR_BIT);
-
- /* now set NVRAM inactive with GPIO0/1 both high */
- nvram_stop(np, &gpreg);
-
- /* activate NVRAM */
- nvram_start(np, &gpreg);
-
- /* write device code and random address MSB */
- nvram_write_byte(np, &ack_data,
- 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* write random address LSB */
- nvram_write_byte(np, &ack_data,
- (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* regenerate START state to set up for reading */
- nvram_start(np, &gpreg);
-
- /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
- nvram_write_byte(np, &ack_data,
- 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
- if (ack_data & 0x01)
- goto out;
-
- /* now set up GPIO0 for inputting data */
- gpcntl |= 0x01;
- OUTB (nc_gpcntl, gpcntl);
-
- /* input all active data - only part of total NVRAM */
- csum = nvram_read_data(np,
- (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
-
- /* finally put NVRAM back in inactive mode */
- gpcntl &= 0xfe;
- OUTB (nc_gpcntl, gpcntl);
- nvram_stop(np, &gpreg);
-
-#ifdef SCSI_NCR_DEBUG_NVRAM
-printk("ncr53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
- nvram->type,
- nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
- nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
- nvram->byte_count, sizeof(*nvram) - 12,
- nvram->checksum, csum);
-#endif
-
- /* check valid NVRAM signature, verify byte count and checksum */
- if (nvram->type == 0 &&
- !memcmp(nvram->trailer, Symbios_trailer, 6) &&
- nvram->byte_count == sizeof(*nvram) - 12 &&
- csum == nvram->checksum)
- retv = 0;
-out:
- /* return GPIO0/1 to original states after having accessed NVRAM */
- OUTB (nc_gpcntl, old_gpcntl);
- OUTB (nc_gpreg, old_gpreg);
-
- return retv;
-}
-
-/*
- * Read Symbios NvRAM data and compute checksum.
- */
-static u_short __init
-nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
-{
- int x;
- u_short csum;
-
- for (x = 0; x < len; x++)
- nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
-
- for (x = 6, csum = 0; x < len - 6; x++)
- csum += data[x];
-
- return csum;
-}
-
-/*
- * Send START condition to NVRAM to wake it up.
- */
-static void __init nvram_start(ncr_slot *np, u_char *gpreg)
-{
- nvram_setBit(np, 1, gpreg, SET_BIT);
- nvram_setBit(np, 0, gpreg, SET_CLK);
- nvram_setBit(np, 0, gpreg, CLR_BIT);
- nvram_setBit(np, 0, gpreg, CLR_CLK);
-}
-
-/*
- * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
- * GPIO0 must already be set as an output
- */
-static void __init
-nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
-{
- int x;
-
- for (x = 0; x < 8; x++)
- nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
-
- nvram_readAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * READ a byte from the NVRAM and then send an ACK to say we have got it,
- * GPIO0 must already be set as an input
- */
-static void __init
-nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
-{
- int x;
- u_char read_bit;
-
- *read_data = 0;
- for (x = 0; x < 8; x++) {
- nvram_doBit(np, &read_bit, 1, gpreg);
- *read_data |= ((read_bit & 0x01) << (7 - x));
- }
-
- nvram_writeAck(np, ack_data, gpreg, gpcntl);
-}
-
-/*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
- */
-static void __init
-nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
-{
- OUTB (nc_gpcntl, *gpcntl & 0xfe);
- nvram_doBit(np, 0, write_bit, gpreg);
- OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
- */
-static void __init
-nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
-{
- OUTB (nc_gpcntl, *gpcntl | 0x01);
- nvram_doBit(np, read_bit, 1, gpreg);
- OUTB (nc_gpcntl, *gpcntl);
-}
-
-/*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
- */
-static void __init nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
-{
- nvram_setBit(np, write_bit, gpreg, SET_BIT);
- nvram_setBit(np, 0, gpreg, SET_CLK);
- if (read_bit)
- *read_bit = INB (nc_gpreg);
- nvram_setBit(np, 0, gpreg, CLR_CLK);
- nvram_setBit(np, 0, gpreg, CLR_BIT);
-}
-
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
- */
-static void __init nvram_stop(ncr_slot *np, u_char *gpreg)
-{
- nvram_setBit(np, 0, gpreg, SET_CLK);
- nvram_setBit(np, 1, gpreg, SET_BIT);
-}
-
-/*
- * Set/clear data/clock bit in GPIO0
- */
-static void __init
-nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+int __init ncr53c8xx_setup(char *str)
{
- UDELAY (5);
- switch (bit_mode){
- case SET_BIT:
- *gpreg |= write_bit;
- break;
- case CLR_BIT:
- *gpreg &= 0xfe;
- break;
- case SET_CLK:
- *gpreg |= 0x02;
- break;
- case CLR_CLK:
- *gpreg &= 0xfd;
- break;
-
- }
- OUTB (nc_gpreg, *gpreg);
- UDELAY (5);
+ return sym53c8xx__setup(str);
}
-#undef SET_BIT 0
-#undef CLR_BIT 1
-#undef SET_CLK 2
-#undef CLR_CLK 3
-
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("ncr53c8xx=", ncr53c8xx_setup);
+#endif
+#endif
-/* ---------------------------------------------------------------------
-**
-** Try reading Tekram format nvram
-**
-** ---------------------------------------------------------------------
+/*===================================================================
**
-** GPOI0 - data in
-** GPIO1 - data out
-** GPIO2 - clock
-** GPIO4 - chip select
+** SYM53C8XX supported device list
**
-** return 0 if NVRAM data OK, 1 if NVRAM data not OK
-** ---------------------------------------------------------------------
+**===================================================================
*/
-static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
-static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
-static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
-static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
-static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
-static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
-
-static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
-{
- u_char gpcntl, gpreg;
- u_char old_gpcntl, old_gpreg;
- u_short csum;
-
- /* save current state of GPCNTL and GPREG */
- old_gpreg = INB (nc_gpreg);
- old_gpcntl = INB (nc_gpcntl);
-
- /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
- 1/2/4 out */
- gpreg = old_gpreg & 0xe9;
- OUTB (nc_gpreg, gpreg);
- gpcntl = (old_gpcntl & 0xe9) | 0x09;
- OUTB (nc_gpcntl, gpcntl);
-
- /* input all of NVRAM, 64 words */
- csum = Tnvram_read_data(np, (u_short *) nvram,
- sizeof(*nvram) / sizeof(short), &gpreg);
-
- /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
- OUTB (nc_gpcntl, old_gpcntl);
- OUTB (nc_gpreg, old_gpreg);
-
- /* check data valid */
- if (csum != 0x1234)
- return 1;
-
- return 0;
-}
-
-/*
- * Read Tekram NvRAM data and compute checksum.
- */
-static u_short __init
-Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
-{
- u_char read_bit;
- u_short csum;
- int x;
-
- for (x = 0, csum = 0; x < len; x++) {
-
- /* output read command and address */
- Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
- if (read_bit & 0x01)
- return 0; /* Force bad checksum */
-
- Tnvram_Read_Word(np, &data[x], gpreg);
- csum += data[x];
-
- Tnvram_Stop(np, gpreg);
- }
-
- return csum;
-}
-
-/*
- * Send read command and address to NVRAM
- */
-static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
-{
- int x;
-
- /* send 9 bits, start bit (1), command (2), address (6) */
- for (x = 0; x < 9; x++)
- Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
-
- *read_bit = INB (nc_gpreg);
-}
-
-/*
- * READ a byte from the NVRAM
- */
-static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
-{
- int x;
- u_char read_bit;
-
- *nvram_data = 0;
- for (x = 0; x < 16; x++) {
- Tnvram_Read_Bit(np, &read_bit, gpreg);
-
- if (read_bit & 0x01)
- *nvram_data |= (0x01 << (15 - x));
- else
- *nvram_data &= ~(0x01 << (15 - x));
- }
-}
-
-/*
- * Read bit from NVRAM
- */
-static void __init
-Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-{
- UDELAY (2);
- Tnvram_Clk(np, gpreg);
- *read_bit = INB (nc_gpreg);
-}
+static u_short ncr_chip_ids[] __initdata = {
+ PCI_DEVICE_ID_NCR_53C810,
+ PCI_DEVICE_ID_NCR_53C815,
+ PCI_DEVICE_ID_NCR_53C820,
+ PCI_DEVICE_ID_NCR_53C825,
+ PCI_DEVICE_ID_NCR_53C860,
+ PCI_DEVICE_ID_NCR_53C875,
+ PCI_DEVICE_ID_NCR_53C875J,
+ PCI_DEVICE_ID_NCR_53C885,
+ PCI_DEVICE_ID_NCR_53C895,
+ PCI_DEVICE_ID_NCR_53C896,
+ PCI_DEVICE_ID_NCR_53C895A,
+ PCI_DEVICE_ID_NCR_53C1510D
+};
-/*
- * Write bit to GPIO0
- */
-static void __init
-Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+/*==========================================================
+**
+** Chip detection entry point.
+**
+**==========================================================
+*/
+int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt)
{
- if (write_bit & 0x01)
- *gpreg |= 0x02;
- else
- *gpreg &= 0xfd;
-
- *gpreg |= 0x10;
-
- OUTB (nc_gpreg, *gpreg);
- UDELAY (2);
-
- Tnvram_Clk(np, gpreg);
-}
+ /*
+ ** Initialize driver general stuff.
+ */
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+ tpnt->proc_dir = &proc_scsi_ncr53c8xx;
+#else
+ tpnt->proc_name = NAME53C8XX;
+#endif
+ tpnt->proc_info = ncr53c8xx_proc_info;
+#endif
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
- */
-static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg)
-{
- *gpreg &= 0xef;
- OUTB (nc_gpreg, *gpreg);
- UDELAY (2);
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx);
+#endif
- Tnvram_Clk(np, gpreg);
+ return sym53c8xx__detect(tpnt, ncr_chip_ids,
+ sizeof(ncr_chip_ids)/sizeof(ncr_chip_ids[0]));
}
-/*
- * Pulse clock bit in GPIO0
- */
-static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+/*==========================================================
+**
+** Entry point for info() function
+**
+**==========================================================
+*/
+const char *ncr53c8xx_info (struct Scsi_Host *host)
{
- OUTB (nc_gpreg, *gpreg | 0x04);
- UDELAY (2);
- OUTB (nc_gpreg, *gpreg);
+ return SCSI_NCR_DRIVER_NAME;
}
-#endif /* SCSI_NCR_NVRAM_SUPPORT */
-
/*
** Module stuff
*/
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a6e2c14d9..21cb989ca 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -218,23 +218,6 @@ static void scsi_wait_done(Scsi_Cmnd * SCpnt)
}
}
-void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
- void *buffer, unsigned bufflen,
- int timeout, int retries)
-{
- DECLARE_MUTEX_LOCKED(sem);
-
- if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE)
- BUG();
- SCpnt->request.sem = &sem;
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- scsi_do_cmd (SCpnt, (void *) cmnd,
- buffer, bufflen, scsi_wait_done, timeout, retries);
- down (&sem);
- SCpnt->request.sem = NULL;
-}
-
-
/*
* This lock protects the freelist for all devices on the system.
* We could make this finer grained by having a single lock per
@@ -2499,7 +2482,6 @@ static void scsi_dump_status(int level)
atomic_read(&shpnt->host_active),
shpnt->host_blocked,
shpnt->host_self_blocked);
-
}
printk("\n\n");
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 7a073f86e..677d21410 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -497,9 +497,6 @@ extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
void *buffer, unsigned bufflen,
void (*done) (struct scsi_cmnd *),
int timeout, int retries);
-extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
- void *buffer, unsigned bufflen,
- int timeout, int retries);
extern int scsi_dev_init(void);
/*
@@ -571,7 +568,7 @@ struct scsi_device {
Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */
/* public: */
- unsigned char id, lun, channel;
+ unsigned int id, lun, channel;
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
@@ -731,9 +728,9 @@ struct scsi_cmnd {
/* public: */
- unsigned char target;
- unsigned char lun;
- unsigned char channel;
+ unsigned int target;
+ unsigned int lun;
+ unsigned int channel;
unsigned char cmd_len;
unsigned char old_cmd_len;
unsigned char sc_data_direction;
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9f36d08c5..4bed377ed 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -549,24 +549,24 @@ static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
static unsigned char cmd[6] =
{TEST_UNIT_READY, 0, 0, 0, 0, 0};
- Scsi_Cmnd * scp;
+ Scsi_Request * scp;
Scsi_Device * sdev;
printk("Allocating host dev\n");
sdev = scsi_get_host_dev(shpnt);
printk("Got %p. Allocating command block\n", sdev);
- scp = scsi_allocate_device(sdev, 1, FALSE);
+ scp = scsi_allocate_request(sdev);
printk("Got %p\n", scp);
- scp->cmd_len = 6;
- scp->use_sg = 0;
+ scp->sr_cmd_len = 6;
+ scp->sr_use_sg = 0;
printk("Sending command\n");
- scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
+ scsi_wait_req (scp, (void *) cmd, (void *) NULL,
0, 100, 3);
printk("Releasing command\n");
- scsi_release_command(scp);
+ scsi_release_request(scp);
printk("Freeing device\n");
scsi_free_host_dev(sdev);
}
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index abdef85ef..a211980a5 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -94,24 +94,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
int timeout, int retries)
{
int result;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0]));
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
- {
- return -EINTR;
- }
+ SRpnt = scsi_allocate_request(dev);
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
+ scsi_wait_req(SRpnt, cmd, NULL, 0, timeout, retries);
- SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result));
+ SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SRpnt->sr_result));
- if (driver_byte(SCpnt->result) != 0)
- switch (SCpnt->sense_buffer[2] & 0xf) {
+ if (driver_byte(SRpnt->sr_result) != 0)
+ switch (SRpnt->sr_sense_buffer[2] & 0xf) {
case ILLEGAL_REQUEST:
if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
dev->lockable = 0;
@@ -126,7 +122,7 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
case UNIT_ATTENTION:
if (dev->removable) {
dev->changed = 1;
- SCpnt->result = 0; /* This is no longer considered an error */
+ SRpnt->sr_result = 0; /* This is no longer considered an error */
/* gag this error, VFS will log it anyway /axboe */
/* printk(KERN_INFO "Disc change detected.\n"); */
break;
@@ -136,20 +132,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
dev->host->host_no,
dev->id,
dev->lun,
- SCpnt->result);
+ SRpnt->sr_result);
printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(SCpnt->sense_buffer[0]),
- sense_error(SCpnt->sense_buffer[0]),
- SCpnt->sense_buffer[2] & 0xf);
+ sense_class(SRpnt->sr_sense_buffer[0]),
+ sense_error(SRpnt->sr_sense_buffer[0]),
+ SRpnt->sr_sense_buffer[2] & 0xf);
};
- result = SCpnt->result;
+ result = SRpnt->sr_result;
SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return result;
}
@@ -192,7 +188,7 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
char *buf;
unsigned char cmd[MAX_COMMAND_SIZE];
char *cmd_in;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Device *SDpnt;
unsigned char opcode;
int inlen, outlen, cmdlen;
@@ -235,9 +231,9 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
return -ENOMEM;
memset(buf, 0, buf_needed);
if( inlen == 0 ) {
- data_direction = SCSI_DATA_WRITE;
- } else if (outlen == 0 ) {
data_direction = SCSI_DATA_READ;
+ } else if (outlen == 0 ) {
+ data_direction = SCSI_DATA_WRITE;
} else {
/*
* Can this ever happen?
@@ -297,38 +293,38 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
#ifndef DEBUG_NO_CMD
- SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
- if( SCpnt == NULL )
+ SRpnt = scsi_allocate_request(dev);
+ if( SRpnt == NULL )
{
return -EINTR;
}
- SCpnt->sc_data_direction = data_direction;
- scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
+ SRpnt->sr_data_direction = data_direction;
+ scsi_wait_req(SRpnt, cmd, buf, needed, timeout, retries);
/*
* If there was an error condition, pass the info back to the user.
*/
- if (SCpnt->result) {
- int sb_len = sizeof(SCpnt->sense_buffer);
+ if (SRpnt->sr_result) {
+ int sb_len = sizeof(SRpnt->sr_sense_buffer);
sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
result = verify_area(VERIFY_WRITE, cmd_in, sb_len);
if (result)
return result;
- copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len);
+ copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len);
} else {
result = verify_area(VERIFY_WRITE, cmd_in, outlen);
if (result)
return result;
copy_to_user(cmd_in, buf, outlen);
}
- result = SCpnt->result;
+ result = SRpnt->sr_result;
- SDpnt = SCpnt->device;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ SDpnt = SRpnt->sr_device;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
if (buf)
scsi_free(buf, buf_needed);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 2438aecc6..74ac6d245 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -87,6 +87,7 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head)
SCpnt->request.cmd = SPECIAL;
SCpnt->request.special = (void *) SCpnt;
SCpnt->request.q = NULL;
+ SCpnt->request.nr_segments = 0;
/*
* We have the option of inserting the head or the tail of the queue.
@@ -155,6 +156,8 @@ int scsi_insert_special_req(Scsi_Request * SRpnt, int at_head)
q = &SRpnt->sr_device->request_queue;
SRpnt->sr_request.cmd = SPECIAL;
SRpnt->sr_request.special = (void *) SRpnt;
+ SRpnt->sr_request.q = NULL;
+ SRpnt->sr_request.nr_segments = 0;
/*
* We have the option of inserting the head or the tail of the queue.
@@ -909,6 +912,9 @@ void scsi_request_fn(request_queue_t * q)
* be in an interrupt handler. Only do this
* from user space, since we do not want to
* sleep from an interrupt.
+ *
+ * FIXME(eric) - have the error handler thread do
+ * this work.
*/
SDpnt->was_reset = 0;
if (SDpnt->removable && !in_interrupt()) {
@@ -950,6 +956,9 @@ void scsi_request_fn(request_queue_t * q)
if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
SCpnt = scsi_allocate_device(SRpnt->sr_device,
FALSE, FALSE);
+ if( !SCpnt ) {
+ break;
+ }
scsi_init_cmd_from_req(SCpnt, SRpnt);
}
diff --git a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c
index d917d9306..84f29ce74 100644
--- a/drivers/scsi/scsi_merge.c
+++ b/drivers/scsi/scsi_merge.c
@@ -324,7 +324,7 @@ static inline int scsi_new_mergeable(request_queue_t * q,
req->nr_segments >= SHpnt->sg_tablesize)
return 0;
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
@@ -346,7 +346,7 @@ static inline int scsi_new_segment(request_queue_t * q,
return 0;
req->nr_hw_segments++;
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
}
#else
@@ -362,7 +362,7 @@ static inline int scsi_new_segment(request_queue_t * q,
* counter.
*/
req->nr_segments++;
- q->nr_segments++;
+ q->elevator.nr_segments++;
return 1;
} else {
return 0;
@@ -665,7 +665,7 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q,
* This one is OK. Let it go.
*/
req->nr_segments += next->nr_segments - 1;
- q->nr_segments--;
+ q->elevator.nr_segments--;
#ifdef DMA_CHUNK_SIZE
req->nr_hw_segments += next->nr_hw_segments - 1;
#endif
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 4060872bf..a43f2988c 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -252,12 +252,12 @@ static int get_device_flags(unsigned char *response_data)
* devices to the disk driver.
*/
void scan_scsis(struct Scsi_Host *shpnt,
- unchar hardcoded,
- unchar hchannel,
- unchar hid,
- unchar hlun)
+ uint hardcoded,
+ uint hchannel,
+ uint hid,
+ uint hlun)
{
- int channel;
+ uint channel;
int dev;
int lun;
int max_dev_lun;
@@ -299,8 +299,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
initialize_merge_fn(SDpnt);
/*
@@ -405,7 +403,7 @@ void scan_scsis(struct Scsi_Host *shpnt,
leave:
- { /* Unchain SCpnt from host_queue */
+ { /* Unchain SRpnt from host_queue */
Scsi_Device *prev, *next;
Scsi_Device *dqptr;
@@ -423,8 +421,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
}
}
- scsi_release_commandblocks(SDpnt);
-
/* Last device block does not exist. Free memory. */
if (SDpnt != NULL)
kfree((char *) SDpnt);
@@ -460,7 +456,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
struct Scsi_Device_Template *sdtpnt;
Scsi_Device *SDtail, *SDpnt = *SDpnt2;
- Scsi_Cmnd * SCpnt;
+ Scsi_Request * SRpnt;
int bflags, type = -1;
static int ghost_channel=-1, ghost_dev=-1;
int org_lun = lun;
@@ -472,6 +468,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
SDpnt->channel = channel;
SDpnt->online = TRUE;
+ scsi_build_commandblocks(SDpnt);
if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
SDpnt->lun = 0;
@@ -496,37 +493,32 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[1] = lun << 5;
scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
- SCpnt = scsi_allocate_device(SDpnt, 0, 0);
+ SRpnt = scsi_allocate_request(SDpnt);
- SCpnt->host = SDpnt->host;
- SCpnt->device = SDpnt;
- SCpnt->target = SDpnt->id;
- SCpnt->lun = SDpnt->lun;
- SCpnt->channel = SDpnt->channel;
- SCpnt->sc_data_direction = SCSI_DATA_NONE;
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) NULL,
0, SCSI_TIMEOUT + 4 * HZ, 5);
SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
- dev, lun, SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
- SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+ dev, lun, SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_driverbyte(SRpnt->sr_result));
+ SCSI_LOG_SCAN_BUS(3, print_hostbyte(SRpnt->sr_result));
SCSI_LOG_SCAN_BUS(3, printk("\n"));
- if (SCpnt->result) {
- if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
- (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
- ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
- if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
- ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
- ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ if (((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) ||
+ (status_byte(SRpnt->sr_result) & CHECK_CONDITION)) &&
+ ((SRpnt->sr_sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SRpnt->sr_sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SRpnt->sr_sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
+ scsi_release_request(SRpnt);
return 1;
}
} else {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0;
}
}
@@ -540,18 +532,18 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[3] = 0;
scsi_cmd[4] = 255;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result,
256, SCSI_TIMEOUT, 3);
SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
- SCpnt->result ? "failed" : "successful", SCpnt->result));
+ SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
- if (SCpnt->result) {
- scsi_release_command(SCpnt);
+ if (SRpnt->sr_result) {
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
@@ -560,7 +552,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
* are supported here or not.
*/
if ((scsi_result[0] >> 5) == 3) {
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
return 0; /* assume no peripheral if any sort of error */
}
@@ -705,15 +697,15 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
scsi_cmd[3] = 0;
scsi_cmd[4] = 0x2a;
scsi_cmd[5] = 0;
- SCpnt->cmd_len = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req (SRpnt, (void *) scsi_cmd,
(void *) scsi_result, 0x2a,
SCSI_TIMEOUT, 3);
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_release_commandblocks(SDpnt);
@@ -734,8 +726,6 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
SDpnt->host = shpnt;
SDpnt->online = TRUE;
- scsi_build_commandblocks(SDpnt);
-
/*
* Register the queue for the device. All I/O requests will come
* in through here. We also need to register a pointer to
diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
index f6e8939a6..94820f5db 100644
--- a/drivers/scsi/scsi_syms.c
+++ b/drivers/scsi/scsi_syms.c
@@ -43,7 +43,6 @@ EXPORT_SYMBOL(scsicam_bios_param);
EXPORT_SYMBOL(scsi_partsize);
EXPORT_SYMBOL(scsi_allocate_device);
EXPORT_SYMBOL(scsi_do_cmd);
-EXPORT_SYMBOL(scsi_wait_cmd);
EXPORT_SYMBOL(scsi_command_size);
EXPORT_SYMBOL(scsi_ioctl);
EXPORT_SYMBOL(print_command);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 43187600b..584a84905 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -176,6 +176,8 @@ static int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
case BLKFLSBUF:
case BLKSSZGET:
case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
return blk_ioctl(inode->i_rdev, cmd, arg);
case BLKRRPART: /* Re-read partition tables */
@@ -660,7 +662,7 @@ static int sd_init_onedisk(int i)
unsigned long spintime_value = 0;
int the_result, retries, spintime;
int sector_size;
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
/*
* Get the name of the disk, in case we need to log it somewhere.
@@ -679,7 +681,7 @@ static int sd_init_onedisk(int i)
* just after a scsi bus reset.
*/
- SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE);
+ SRpnt = scsi_allocate_request(rscsi_disks[i].device);
buffer = (unsigned char *) scsi_malloc(512);
@@ -694,18 +696,18 @@ static int sd_init_onedisk(int i)
cmd[0] = TEST_UNIT_READY;
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
+ scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries++;
if (the_result == 0
- || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
break;
}
@@ -716,8 +718,8 @@ static int sd_init_onedisk(int i)
*/
if( the_result != 0
&& ((driver_byte(the_result) & DRIVER_SENSE) != 0)
- && SCpnt->sense_buffer[2] == UNIT_ATTENTION
- && SCpnt->sense_buffer[12] == 0x3A ) {
+ && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
+ && SRpnt->sr_sense_buffer[12] == 0x3A ) {
rscsi_disks[i].capacity = 0x1fffff;
sector_size = 512;
rscsi_disks[i].device->changed = 1;
@@ -728,7 +730,7 @@ static int sd_init_onedisk(int i)
/* Look for non-removable devices that return NOT_READY.
* Issue command to spin up drive for these cases. */
if (the_result && !rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY) {
+ SRpnt->sr_sense_buffer[2] == NOT_READY) {
unsigned long time1;
if (!spintime) {
printk("%s: Spinning up disk...", nbuff);
@@ -737,12 +739,12 @@ static int sd_init_onedisk(int i)
cmd[1] |= 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
0/*512*/, SD_TIMEOUT, MAX_RETRIES);
}
spintime = 1;
@@ -768,15 +770,15 @@ static int sd_init_onedisk(int i)
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset((void *) &cmd[2], 0, 8);
memset((void *) buffer, 0, 8);
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
8, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
retries--;
} while (the_result && retries);
@@ -806,7 +808,7 @@ static int sd_init_onedisk(int i)
);
if (driver_byte(the_result) & DRIVER_SENSE)
printk("%s : extended sense code = %1x \n",
- nbuff, SCpnt->sense_buffer[2] & 0xf);
+ nbuff, SRpnt->sr_sense_buffer[2] & 0xf);
else
printk("%s : sense not available. \n", nbuff);
@@ -818,7 +820,7 @@ static int sd_init_onedisk(int i)
/* Set dirty bit for removable devices if not ready - sometimes drives
* will not report this properly. */
if (rscsi_disks[i].device->removable &&
- SCpnt->sense_buffer[2] == NOT_READY)
+ SRpnt->sr_sense_buffer[2] == NOT_READY)
rscsi_disks[i].device->changed = 1;
} else {
@@ -919,16 +921,16 @@ static int sd_init_onedisk(int i)
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
cmd[2] = 1; /* page code 1 ?? */
cmd[4] = 12;
- SCpnt->cmd_len = 0;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->sense_buffer[2] = 0;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
/* same code as READCAPA !! */
- SCpnt->sc_data_direction = SCSI_DATA_READ;
- scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
512, SD_TIMEOUT, MAX_RETRIES);
- the_result = SCpnt->result;
+ the_result = SRpnt->sr_result;
if (the_result) {
printk("%s: test WP failed, assume Write Protected\n", nbuff);
@@ -940,12 +942,12 @@ static int sd_init_onedisk(int i)
}
} /* check for write protect */
- SCpnt->device->ten = 1;
- SCpnt->device->remap = 1;
- SCpnt->device->sector_size = sector_size;
+ SRpnt->sr_device->ten = 1;
+ SRpnt->sr_device->remap = 1;
+ SRpnt->sr_device->sector_size = sector_size;
/* Wake up a process waiting for device */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
scsi_free(buffer, 512);
return i;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 781cde85a..dc6712247 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -486,7 +486,7 @@ void get_sectorsize(int i)
SRpnt->sr_data_direction = SCSI_DATA_READ;
scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
- 512, SR_TIMEOUT, MAX_RETRIES);
+ 8, SR_TIMEOUT, MAX_RETRIES);
the_result = SRpnt->sr_result;
retries--;
@@ -663,7 +663,7 @@ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command
/* do the locking and issue the command */
SRpnt->sr_request.rq_dev = cdi->dev;
- /* scsi_wait_cmd sets the command length */
+ /* scsi_wait_req sets the command length */
SRpnt->sr_cmd_len = 0;
SRpnt->sr_data_direction = cgc->data_direction;
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 1de7686dc..8df062781 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -6,12 +6,13 @@
Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
Contribution and ideas from several people including (in alphabetical
order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
- Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+ Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and
+ Eric Youngdale.
Copyright 1992 - 2000 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue Feb 29 20:47:03 2000 by makisara@kai.makisara.local
+ Last modified: Mon Mar 13 21:15:29 2000 by makisara@kai.makisara.local
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -125,14 +126,17 @@ DEB( static int debugging = DEBUG; )
24 bits) */
#define SET_DENS_AND_BLK 0x10001
+#define ST_DEV_ARR_LUMP 6
+static rwlock_t st_dev_arr_lock = RW_LOCK_UNLOCKED;
+
static int st_nbr_buffers;
-static ST_buffer **st_buffers;
+static ST_buffer **st_buffers = NULL;
static int st_buffer_size = ST_BUFFER_SIZE;
static int st_write_threshold = ST_WRITE_THRESHOLD;
static int st_max_buffers = ST_MAX_BUFFERS;
static int st_max_sg_segs = ST_MAX_SG;
-static Scsi_Tape *scsi_tapes = NULL;
+static Scsi_Tape **scsi_tapes = NULL;
static int modes_defined = FALSE;
@@ -161,16 +165,14 @@ struct Scsi_Device_Template st_template =
static int st_compression(Scsi_Tape *, int);
-static int find_partition(struct inode *);
-static int update_partition(struct inode *);
+static int find_partition(Scsi_Tape *);
+static int update_partition(Scsi_Tape *);
-static int st_int_ioctl(struct inode *inode, unsigned int cmd_in,
- unsigned long arg);
+static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
-
/* Convert the result to success code */
-static int st_chk_result(Scsi_Request * SRpnt)
+static int st_chk_result(Scsi_Tape *STp, Scsi_Request * SRpnt)
{
int dev;
int result = SRpnt->sr_result;
@@ -184,8 +186,10 @@ static int st_chk_result(Scsi_Request * SRpnt)
if (driver_byte(result) & DRIVER_SENSE)
scode = sense[2] & 0x0f;
- else
+ else {
+ sense[0] = 0;
scode = 0;
+ }
dev = TAPE_NR(SRpnt->sr_request.rq_dev);
DEB(
@@ -224,8 +228,8 @@ static int st_chk_result(Scsi_Request * SRpnt)
&& SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
#endif
) {
- scsi_tapes[dev].recover_count++;
- scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
+ STp->recover_count++;
+ STp->recover_reg++;
DEB(
if (debugging) {
@@ -236,7 +240,7 @@ static int st_chk_result(Scsi_Request * SRpnt)
else
stp = "ioctl";
printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp,
- scsi_tapes[dev].recover_count);
+ STp->recover_count);
} ) /* end DEB */
if ((sense[2] & 0xe0) == 0)
@@ -254,7 +258,9 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt)
Scsi_Tape *STp;
if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < st_template.nr_dev) {
- STp = &(scsi_tapes[st_nbr]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[st_nbr];
+ read_unlock(&st_dev_arr_lock);
if ((STp->buffer)->writing &&
(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40)) {
@@ -330,7 +336,7 @@ static Scsi_Request *
if (do_wait) {
down(SRpnt->sr_request.sem);
SRpnt->sr_request.sem = NULL;
- (STp->buffer)->syscall_result = st_chk_result(SRpnt);
+ (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
}
return SRpnt;
}
@@ -354,7 +360,7 @@ static void write_behind_check(Scsi_Tape * STp)
down(&(STp->sem));
(STp->buffer)->last_SRpnt->sr_request.sem = NULL;
- (STp->buffer)->syscall_result = st_chk_result((STp->buffer)->last_SRpnt);
+ (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt);
scsi_release_request((STp->buffer)->last_SRpnt);
if (STbuffer->writing < STbuffer->buffer_bytes)
@@ -491,15 +497,12 @@ static int flush_write_buffer(Scsi_Tape * STp)
/* Flush the tape buffer. The tape will be positioned correctly unless
seek_next is true. */
-static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
+static int flush_buffer(Scsi_Tape *STp, int seek_next)
{
int backspace, result;
- Scsi_Tape *STp;
ST_buffer *STbuffer;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
/*
@@ -538,7 +541,7 @@ static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
}
}
if (!result && backspace > 0)
- result = st_int_ioctl(inode, MTBSR, backspace);
+ result = st_int_ioctl(STp, MTBSR, backspace);
} else if (STps->eof == ST_FM_HIT) {
if (STps->drv_file >= 0)
STps->drv_file++;
@@ -550,11 +553,11 @@ static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
}
/* Set the mode parameters */
-static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
+static int set_mode_densblk(Scsi_Tape * STp, ST_mode * STm)
{
int set_it = FALSE;
unsigned long arg;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
if (!STp->density_changed &&
STm->default_density >= 0 &&
@@ -572,7 +575,7 @@ static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
} else
arg |= STp->block_size;
if (set_it &&
- st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
+ st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
printk(KERN_WARNING
"st%d: Can't set default block size to %d bytes and density %x.\n",
dev, STm->default_blksize, STm->default_density);
@@ -597,22 +600,26 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
int dev = TAPE_NR(inode->i_rdev);
int mode = TAPE_MODE(inode->i_rdev);
- if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ if (dev >= st_template.dev_max || STp == NULL) {
+ read_unlock(&st_dev_arr_lock);
return (-ENXIO);
+ }
+ read_unlock(&st_dev_arr_lock);
- if (!scsi_block_when_processing_errors(scsi_tapes[dev].device)) {
+ if (!scsi_block_when_processing_errors(STp->device)) {
return -ENXIO;
}
- STp = &(scsi_tapes[dev]);
if (STp->in_use) {
DEB( printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); )
return (-EBUSY);
}
STp->in_use = 1;
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+ STp->rew_at_close = STp->autorew_dev = (MINOR(inode->i_rdev) & 0x80) == 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_INC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_INC_USE_COUNT(st_template.module);
@@ -626,10 +633,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
/* Allocate a buffer for this user */
need_dma_buffer = STp->restr_dma;
+ read_lock(&st_dev_arr_lock);
for (i = 0; i < st_nbr_buffers; i++)
if (!st_buffers[i]->in_use &&
- (!need_dma_buffer || st_buffers[i]->dma))
+ (!need_dma_buffer || st_buffers[i]->dma)) {
+ STp->buffer = st_buffers[i];
break;
+ }
+ read_unlock(&st_dev_arr_lock);
if (i >= st_nbr_buffers) {
STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
if (STp->buffer == NULL) {
@@ -637,8 +648,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
retval = (-EBUSY);
goto err_out;
}
- } else
- STp->buffer = st_buffers[i];
+ }
+
(STp->buffer)->in_use = 1;
(STp->buffer)->writing = 0;
(STp->buffer)->syscall_result = 0;
@@ -824,7 +835,7 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
partition support has been enabled. */
DEBC(printk(ST_DEB_MSG
"st%d: Updating partition number in status.\n", dev));
- if ((STp->partition = find_partition(inode)) < 0) {
+ if ((STp->partition = find_partition(STp)) < 0) {
retval = STp->partition;
goto err_out;
}
@@ -836,11 +847,11 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
STp->density_changed = STp->blksize_changed = FALSE;
STp->compression_changed = FALSE;
if (!(STm->defaults_for_writes) &&
- (retval = set_mode_densblk(inode, STp, STm)) < 0)
+ (retval = set_mode_densblk(STp, STm)) < 0)
goto err_out;
if (STp->default_drvbuffer != 0xff) {
- if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
+ if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
printk(KERN_WARNING
"st%d: Can't set default drive buffering to %d.\n",
dev, STp->default_drvbuffer);
@@ -855,8 +866,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp)
STp->buffer = NULL;
}
STp->in_use = 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_DEC_USE_COUNT(st_template.module);
return retval;
@@ -882,7 +893,9 @@ static int scsi_tape_flush(struct file *filp)
return 0;
dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
@@ -893,7 +906,7 @@ static int scsi_tape_flush(struct file *filp)
}
if (STp->can_partitions &&
- (result2 = update_partition(inode)) < 0) {
+ (result2 = update_partition(STp)) < 0) {
DEBC(printk(ST_DEB_MSG
"st%d: update_partition at close failed.\n", dev));
if (result == 0)
@@ -950,7 +963,7 @@ static int scsi_tape_flush(struct file *filp)
STps = &(STp->ps[STp->partition]);
if (!STm->sysv || STps->rw != ST_READING) {
if (STp->can_bsr)
- result = flush_buffer(inode, filp, 0);
+ result = flush_buffer(STp, 0);
else if (STps->eof == ST_FM_HIT) {
result = cross_eof(STp, FALSE);
if (result) {
@@ -973,7 +986,7 @@ static int scsi_tape_flush(struct file *filp)
out:
if (STp->rew_at_close) {
- result2 = st_int_ioctl(inode, MTREW, 1);
+ result2 = st_int_ioctl(STp, MTREW, 1);
if (result == 0)
result = result2;
}
@@ -991,10 +1004,12 @@ static int scsi_tape_close(struct inode *inode, struct file *filp)
int dev;
dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
if (STp->door_locked == ST_LOCKED_AUTO)
- st_int_ioctl(inode, MTUNLOCK, 0);
+ st_int_ioctl(STp, MTUNLOCK, 0);
if (STp->buffer != NULL) {
normalize_buffer(STp->buffer);
@@ -1002,8 +1017,8 @@ static int scsi_tape_close(struct inode *inode, struct file *filp)
}
STp->in_use = 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (STp->device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
if (st_template.module)
__MOD_DEC_USE_COUNT(st_template.module);
@@ -1028,7 +1043,9 @@ static ssize_t
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
/*
* If we are in the middle of error recovery, don't let anyone
@@ -1079,7 +1096,7 @@ static ssize_t
}
if (STp->can_partitions &&
- (retval = update_partition(inode)) < 0)
+ (retval = update_partition(STp)) < 0)
return retval;
STps = &(STp->ps[STp->partition]);
@@ -1092,17 +1109,17 @@ static ssize_t
return (-EOVERFLOW);
if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
+ !st_int_ioctl(STp, MTLOCK, 0))
STp->door_locked = ST_LOCKED_AUTO;
if (STps->rw == ST_READING) {
- retval = flush_buffer(inode, filp, 0);
+ retval = flush_buffer(STp, 0);
if (retval)
return retval;
STps->rw = ST_WRITING;
} else if (STps->rw != ST_WRITING &&
STps->drv_file == 0 && STps->drv_block == 0) {
- if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
+ if ((retval = set_mode_densblk(STp, STm)) < 0)
return retval;
if (STm->default_compression != ST_DONT_TOUCH &&
!(STp->compression_changed)) {
@@ -1328,21 +1345,19 @@ static ssize_t
/* Read data from the tape. Returns zero in the normal case, one if the
eof status has changed, and the negative error code in case of a
fatal error. Otherwise updates the buffer and the eof state. */
-static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
+static long read_tape(Scsi_Tape *STp, long count, Scsi_Request ** aSRpnt)
{
int transfer, blks, bytes;
static unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
- Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
int retval = 0;
if (count == 0)
return 0;
- STp = &(scsi_tapes[dev]);
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
if (STps->eof == ST_FM_HIT)
@@ -1418,7 +1433,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
STps->drv_block += blks - transfer + 1;
- st_int_ioctl(inode, MTBSR, 1);
+ st_int_ioctl(STp, MTBSR, 1);
return (-EIO);
}
/* We have some data, deliver it */
@@ -1429,7 +1444,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
dev, count, (STp->buffer)->buffer_bytes));
if (STps->drv_block >= 0)
STps->drv_block += 1;
- if (st_int_ioctl(inode, MTBSR, 1))
+ if (st_int_ioctl(STp, MTBSR, 1))
return (-EIO);
}
} else if (SRpnt->sr_sense_buffer[2] & 0x80) { /* FM overrides EOM */
@@ -1509,7 +1524,9 @@ static ssize_t
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
/*
* If we are in the middle of error recovery, don't let anyone
@@ -1542,7 +1559,7 @@ static ssize_t
} ) /* end DEB */
if (STp->can_partitions &&
- (total = update_partition(inode)) < 0)
+ (total = update_partition(STp)) < 0)
return total;
if (STp->block_size == 0 &&
@@ -1555,12 +1572,12 @@ static ssize_t
return (-EIO); /* Read must be integral number of blocks */
if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
+ !st_int_ioctl(STp, MTLOCK, 0))
STp->door_locked = ST_LOCKED_AUTO;
STps = &(STp->ps[STp->partition]);
if (STps->rw == ST_WRITING) {
- transfer = flush_buffer(inode, filp, 0);
+ transfer = flush_buffer(STp, 0);
if (transfer)
return transfer;
STps->rw = ST_READING;
@@ -1596,7 +1613,7 @@ static ssize_t
/* Get new data if the buffer is empty */
if ((STp->buffer)->buffer_bytes == 0) {
- special = read_tape(inode, count - total, &SRpnt);
+ special = read_tape(STp, count - total, &SRpnt);
if (special < 0) { /* No need to continue read */
if (SRpnt != NULL) {
scsi_release_request(SRpnt);
@@ -1684,15 +1701,13 @@ static void st_log_options(Scsi_Tape * STp, ST_mode * STm, int dev)
}
-static int st_set_options(struct inode *inode, long options)
+static int st_set_options(Scsi_Tape *STp, long options)
{
int value;
long code;
- Scsi_Tape *STp;
ST_mode *STm;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
- STp = &(scsi_tapes[dev]);
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined) {
memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
@@ -1823,95 +1838,146 @@ static int st_set_options(struct inode *inode, long options)
return 0;
}
+#define MODE_HEADER_LENGTH 4
-#define COMPRESSION_PAGE 0x0f
-#define COMPRESSION_PAGE_LENGTH 16
+/* Mode header and page byte offsets */
+#define MH_OFF_DATA_LENGTH 0
+#define MH_OFF_MEDIUM_TYPE 1
+#define MH_OFF_DEV_SPECIFIC 2
+#define MH_OFF_BDESCS_LENGTH 3
+#define MP_OFF_PAGE_NBR 0
+#define MP_OFF_PAGE_LENGTH 1
-#define MODE_HEADER_LENGTH 4
+/* Mode header and page bit masks */
+#define MH_BIT_WP 0x80
+#define MP_MSK_PAGE_NBR 0x3f
-#define DCE_MASK 0x80
-#define DCC_MASK 0x40
-#define RED_MASK 0x60
+/* Don't return block descriptors */
+#define MODE_SENSE_OMIT_BDESCS 0x08
+#define MODE_SELECT_PAGE_FORMAT 0x10
-/* Control the compression with mode page 15. Algorithm not changed if zero. */
-static int st_compression(Scsi_Tape * STp, int state)
+/* Read a mode page into the tape buffer. The block descriptors are included
+ if incl_block_descs is true. */
+static int read_mode_page(Scsi_Tape *STp, int page, int omit_block_descs)
{
- int dev;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt = NULL;
- if (STp->ready != ST_READY)
- return (-EIO);
-
- /* Read the current page contents */
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = COMPRESSION_PAGE;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+ if (omit_block_descs)
+ cmd[1] = MODE_SENSE_OMIT_BDESCS;
+ cmd[2] = page;
+ cmd[4] = 255;
SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
STp->timeout, 0, TRUE);
if (SRpnt == NULL)
return (STp->buffer)->syscall_result;
- dev = TAPE_NR(SRpnt->sr_request.rq_dev);
+ scsi_release_request(SRpnt);
- if ((STp->buffer)->syscall_result != 0) {
+ return (STp->buffer)->syscall_result;
+}
+
+
+/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
+ in the buffer is correctly formatted. */
+static int write_mode_page(Scsi_Tape *STp, int page)
+{
+ int pgo;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ Scsi_Request *SRpnt = NULL;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = MODE_SELECT_PAGE_FORMAT;
+ pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
+ cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
+
+ /* Clear reserved fields */
+ (STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
+ (STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
+ (STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
+ (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
+ STp->timeout, 0, TRUE);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
+
+ scsi_release_request(SRpnt);
+
+ return (STp->buffer)->syscall_result;
+}
+
+
+#define COMPRESSION_PAGE 0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define CP_OFF_DCE_DCC 2
+
+#define DCE_MASK 0x80
+#define DCC_MASK 0x40
+#define RED_MASK 0x60
+
+
+/* Control the compression with mode page 15. Algorithm not changed if zero.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+ Including block descriptors should not cause any harm to other drives. */
+
+static int st_compression(Scsi_Tape * STp, int state)
+{
+ int retval;
+ int mpoffs; /* Offset to mode page start */
+ unsigned char *b_data = (STp->buffer)->b_data;
+ DEB( int dev = TAPE_NR(STp->devt); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ /* Read the current page contents */
+ retval = read_mode_page(STp, COMPRESSION_PAGE, FALSE);
+ if (retval) {
DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n",
dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
+
+ mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
- ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0)));
+ (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
/* Check if compression can be changed */
- if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+ if ((b_data[mpoffs + 2] & DCC_MASK) == 0) {
DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
/* Do the change */
if (state)
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
+ b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
else
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
+ b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- (STp->buffer)->b_data[0] = 0; /* Reserved data length */
- (STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
- (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
- STp->timeout, 0, TRUE);
-
- if ((STp->buffer)->syscall_result != 0) {
+ retval = write_mode_page(STp, COMPRESSION_PAGE);
+ if (retval) {
DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
return (-EIO);
}
DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
dev, state));
- scsi_release_request(SRpnt);
- SRpnt = NULL;
STp->compression_changed = TRUE;
return 0;
}
/* Internal ioctl function */
-static int st_int_ioctl(struct inode *inode,
- unsigned int cmd_in, unsigned long arg)
+static int st_int_ioctl(Scsi_Tape *STp, unsigned int cmd_in, unsigned long arg)
{
int timeout;
long ltmp;
@@ -1919,13 +1985,11 @@ static int st_int_ioctl(struct inode *inode,
int chg_eof = TRUE;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
- Scsi_Tape *STp;
ST_partstat *STps;
int fileno, blkno, at_sm, undone;
int datalen = 0, direction = SCSI_DATA_NONE;
- int dev = TAPE_NR(inode->i_rdev);
+ int dev = TAPE_NR(STp->devt);
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY && cmd_in != MTLOAD) {
if (STp->ready == ST_NO_TAPE)
return (-ENOMEDIUM);
@@ -2120,7 +2184,7 @@ static int st_int_ioctl(struct inode *inode,
case MTEOM:
if (!STp->fast_mteom) {
/* space to the end of tape */
- ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
+ ioctl_result = st_int_ioctl(STp, MTFSF, 0x3fff);
fileno = STps->drv_file;
if (STps->eof >= ST_EOD_1)
return 0;
@@ -2247,9 +2311,9 @@ static int st_int_ioctl(struct inode *inode,
STp->door_locked = ST_UNLOCKED;
if (cmd_in == MTBSFM)
- ioctl_result = st_int_ioctl(inode, MTFSF, 1);
+ ioctl_result = st_int_ioctl(STp, MTFSF, 1);
else if (cmd_in == MTFSFM)
- ioctl_result = st_int_ioctl(inode, MTBSF, 1);
+ ioctl_result = st_int_ioctl(STp, MTBSF, 1);
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
STp->block_size = arg & MT_ST_BLKSIZE_MASK;
@@ -2275,7 +2339,7 @@ static int st_int_ioctl(struct inode *inode,
if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
STp->rew_at_close = 0;
else if (cmd_in == MTLOAD) {
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+ STp->rew_at_close = STp->autorew_dev;
for (i = 0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
STp->ps[i].last_block_valid = FALSE;
@@ -2368,16 +2432,14 @@ static int st_int_ioctl(struct inode *inode,
/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
structure. */
-static int get_location(struct inode *inode, unsigned int *block, int *partition,
+static int get_location(Scsi_Tape *STp, unsigned int *block, int *partition,
int logical)
{
- Scsi_Tape *STp;
- int dev = TAPE_NR(inode->i_rdev);
int result;
unsigned char scmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
+ DEB( int dev = TAPE_NR(STp->devt); )
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
@@ -2430,19 +2492,17 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition
/* Set the tape block and partition. Negative partition means that only the
block should be set in vendor specific way. */
-static int set_location(struct inode *inode, unsigned int block, int partition,
+static int set_location(Scsi_Tape *STp, unsigned int block, int partition,
int logical)
{
- Scsi_Tape *STp;
ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
int result, p;
unsigned int blk;
int timeout;
unsigned char scmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
+ DEB( int dev = TAPE_NR(STp->devt); )
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
timeout = STp->long_timeout;
@@ -2458,7 +2518,7 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
partition >= ST_NBR_PARTITIONS)
return (-EINVAL);
if (partition != STp->partition) {
- if (get_location(inode, &blk, &p, 1))
+ if (get_location(STp, &blk, &p, 1))
STps->last_block_valid = FALSE;
else {
STps->last_block_valid = TRUE;
@@ -2508,7 +2568,7 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
result = (-EIO);
if (STp->can_partitions &&
(STp->device)->scsi_level >= SCSI_2 &&
- (p = find_partition(inode)) >= 0)
+ (p = find_partition(STp)) >= 0)
STp->partition = p;
} else {
if (STp->can_partitions) {
@@ -2535,12 +2595,12 @@ static int set_location(struct inode *inode, unsigned int block, int partition,
/* Find the current partition number for the drive status. Called from open and
returns either partition number of negative error code. */
-static int find_partition(struct inode *inode)
+static int find_partition(Scsi_Tape *STp)
{
int i, partition;
unsigned int block;
- if ((i = get_location(inode, &block, &partition, 1)) < 0)
+ if ((i = get_location(STp, &block, &partition, 1)) < 0)
return i;
if (partition >= ST_NBR_PARTITIONS)
return (-EIO);
@@ -2549,60 +2609,52 @@ static int find_partition(struct inode *inode)
/* Change the partition if necessary */
-static int update_partition(struct inode *inode)
+static int update_partition(Scsi_Tape *STp)
{
- int dev = TAPE_NR(inode->i_rdev);
- Scsi_Tape *STp;
ST_partstat *STps;
- STp = &(scsi_tapes[dev]);
if (STp->partition == STp->new_partition)
return 0;
STps = &(STp->ps[STp->new_partition]);
if (!STps->last_block_valid)
STps->last_block_visited = 0;
- return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
+ return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
}
/* Functions for reading and writing the medium partition mode page. These
seem to work with Wangtek 6200HS and HP C1533A. */
#define PART_PAGE 0x11
-#define PART_PAGE_LENGTH 10
+#define PART_PAGE_FIXED_LENGTH 8
+
+#define PP_OFF_MAX_ADD_PARTS 2
+#define PP_OFF_NBR_ADD_PARTS 3
+#define PP_OFF_FLAGS 4
+#define PP_OFF_PART_UNITS 6
+#define PP_OFF_RESERVED 7
+
+#define PP_BIT_IDP 0x20
+#define PP_MSK_PSUM_MB 0x10
/* Get the number of partitions on the tape. As a side effect reads the
mode page into the tape buffer. */
-static int nbr_partitions(struct inode *inode)
+static int nbr_partitions(Scsi_Tape *STp)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- Scsi_Tape *STp;
- Scsi_Request *SRpnt = NULL;
- unsigned char cmd[MAX_COMMAND_SIZE];
+ int result;
+ DEB( int dev = TAPE_NR(STp->devt) );
- STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
return (-EIO);
- memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8; /* Page format */
- cmd[2] = PART_PAGE;
- cmd[4] = 200;
-
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 200, SCSI_DATA_READ, STp->timeout,
- MAX_READY_RETRIES, TRUE);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ result = read_mode_page(STp, PART_PAGE, TRUE);
- scsi_release_request(SRpnt);
- SRpnt = NULL;
-
- if ((STp->buffer)->syscall_result != 0) {
+ if (result) {
DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n",
dev));
result = (-EIO);
} else {
- result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
+ result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
+ PP_OFF_NBR_ADD_PARTS] + 1;
DEBC(printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result));
}
@@ -2611,62 +2663,69 @@ static int nbr_partitions(struct inode *inode)
/* Partition the tape into two partitions if size > 0 or one partition if
- size == 0 */
-static int partition_tape(struct inode *inode, int size)
+ size == 0.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+
+ My HP C1533A drive returns only one partition size field. This is used to
+ set the size of partition 1. There is no size field for the default partition.
+ Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
+ used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
+ The following algorithm is used to accomodate both drives: if the number of
+ partition size fields is greater than the maximum number of additional partitions
+ in the mode page, the second field is used. Otherwise the first field is used.
+ */
+static int partition_tape(Scsi_Tape *STp, int size)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- int length;
- Scsi_Tape *STp;
- Scsi_Request *SRpnt = NULL;
- unsigned char cmd[MAX_COMMAND_SIZE], *bp;
+ int dev = TAPE_NR(STp->devt), result;
+ int pgo, psd_cnt, psdo;
+ unsigned char *bp;
- if ((result = nbr_partitions(inode)) < 0)
+ result = read_mode_page(STp, PART_PAGE, FALSE);
+ if (result) {
+ DEBC(printk(ST_DEB_MSG "st%d: Can't read partition mode page.\n", dev));
return result;
- STp = &(scsi_tapes[dev]);
-
+ }
/* The mode page is in the buffer. Let's modify it and write it. */
- bp = &((STp->buffer)->b_data[0]);
+ bp = (STp->buffer)->b_data;
+ pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
+ DEBC(printk(ST_DEB_MSG "st%d: Partition page length is %d bytes.\n",
+ dev, bp[pgo + MP_OFF_PAGE_LENGTH] + 2));
+
+ psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
+ psdo = pgo + PART_PAGE_FIXED_LENGTH;
+ if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
+ bp[psdo] = bp[psdo + 1] = 0xff; /* Rest of the tape */
+ psdo += 2;
+ }
+ memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
+
+ DEBC(printk("st%d: psd_cnt %d, max.parts %d, nbr_parts %d\n", dev,
+ psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
+ bp[pgo + PP_OFF_NBR_ADD_PARTS]));
+
if (size <= 0) {
- length = 8;
- bp[MODE_HEADER_LENGTH + 3] = 0;
+ bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
DEBC(printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n",
dev));
} else {
- length = 10;
- bp[MODE_HEADER_LENGTH + 3] = 1;
- bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
- bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
+ bp[psdo] = (size >> 8) & 0xff;
+ bp[psdo + 1] = size & 0xff;
+ bp[pgo + 3] = 1;
DEBC(printk(ST_DEB_MSG
- "st%d: Formatting tape with two partition (1 = %d MB).\n",
+ "st%d: Formatting tape with two partitions (1 = %d MB).\n",
dev, size));
}
- bp[MODE_HEADER_LENGTH + 6] = 0;
- bp[MODE_HEADER_LENGTH + 7] = 0;
- bp[MODE_HEADER_LENGTH + 4] = 0x30; /* IDP | PSUM = MB */
-
- bp[0] = 0;
- bp[1] = 0;
- bp[MODE_HEADER_LENGTH] &= 0x3f;
- bp[MODE_HEADER_LENGTH + 1] = length - 2;
+ bp[pgo + PP_OFF_PART_UNITS] = 0;
+ bp[pgo + PP_OFF_RESERVED] = 0;
+ bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB;
- memset(cmd, 0, MAX_COMMAND_SIZE);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = length + MODE_HEADER_LENGTH;
-
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
- STp->long_timeout, MAX_READY_RETRIES, TRUE);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
-
- scsi_release_request(SRpnt);
- SRpnt = NULL;
-
- if ((STp->buffer)->syscall_result != 0) {
+ result = write_mode_page(STp, PART_PAGE);
+ if (result) {
printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
result = (-EIO);
- } else
- result = 0;
+ }
return result;
}
@@ -2679,14 +2738,15 @@ static int st_ioctl(struct inode *inode, struct file *file,
{
int i, cmd_nr, cmd_type, bt;
unsigned int blk;
- struct mtop mtc;
- struct mtpos mt_pos;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
- STp = &(scsi_tapes[dev]);
+ read_lock(&st_dev_arr_lock);
+ STp = scsi_tapes[dev];
+ read_unlock(&st_dev_arr_lock);
+
DEB(
if (debugging && !STp->in_use) {
printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
@@ -2709,6 +2769,8 @@ static int st_ioctl(struct inode *inode, struct file *file,
cmd_nr = _IOC_NR(cmd_in);
if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+ struct mtop mtc;
+
if (_IOC_SIZE(cmd_in) != sizeof(mtc))
return (-EINVAL);
@@ -2751,7 +2813,7 @@ static int st_ioctl(struct inode *inode, struct file *file,
mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
mtc.mt_op == MTCOMPRESSION;
}
- i = flush_buffer(inode, file, i);
+ i = flush_buffer(STp, i);
if (i < 0)
return i;
} else {
@@ -2770,7 +2832,7 @@ static int st_ioctl(struct inode *inode, struct file *file,
STp->device->was_reset = 0;
if (STp->door_locked != ST_UNLOCKED &&
STp->door_locked != ST_LOCK_FAILS) {
- if (st_int_ioctl(inode, MTLOCK, 0)) {
+ if (st_int_ioctl(STp, MTLOCK, 0)) {
printk(KERN_NOTICE
"st%d: Could not relock door after bus reset.\n",
dev);
@@ -2785,18 +2847,18 @@ static int st_ioctl(struct inode *inode, struct file *file,
STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
- st_int_ioctl(inode, MTUNLOCK, 0); /* Ignore result! */
+ st_int_ioctl(STp, MTUNLOCK, 0); /* Ignore result! */
if (mtc.mt_op == MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) != 0)
- return st_set_options(inode, mtc.mt_count);
+ return st_set_options(STp, mtc.mt_count);
if (mtc.mt_op == MTSETPART) {
if (!STp->can_partitions ||
mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
return (-EINVAL);
if (mtc.mt_count >= STp->nbr_partitions &&
- (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+ (STp->nbr_partitions = nbr_partitions(STp)) < 0)
return (-EIO);
if (mtc.mt_count >= STp->nbr_partitions)
return (-EINVAL);
@@ -2807,8 +2869,8 @@ static int st_ioctl(struct inode *inode, struct file *file,
if (mtc.mt_op == MTMKPART) {
if (!STp->can_partitions)
return (-EINVAL);
- if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
- (i = partition_tape(inode, mtc.mt_count)) < 0)
+ if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 ||
+ (i = partition_tape(STp, mtc.mt_count)) < 0)
return i;
for (i = 0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
@@ -2822,93 +2884,97 @@ static int st_ioctl(struct inode *inode, struct file *file,
}
if (mtc.mt_op == MTSEEK) {
- i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
+ i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
if (!STp->can_partitions)
STp->ps[0].rw = ST_IDLE;
return i;
}
if (STp->can_partitions && STp->ready == ST_READY &&
- (i = update_partition(inode)) < 0)
+ (i = update_partition(STp)) < 0)
return i;
if (mtc.mt_op == MTCOMPRESSION)
return st_compression(STp, (mtc.mt_count & 1));
else
- return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
+ return st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
}
if (!STm->defined)
return (-ENXIO);
- if ((i = flush_buffer(inode, file, FALSE)) < 0)
+ if ((i = flush_buffer(STp, FALSE)) < 0)
return i;
if (STp->can_partitions &&
- (i = update_partition(inode)) < 0)
+ (i = update_partition(STp)) < 0)
return i;
if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+ struct mtget mt_status;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
return (-EINVAL);
- (STp->mt_status)->mt_dsreg =
+ mt_status.mt_type = STp->tape_type;
+ mt_status.mt_dsreg =
((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
- (STp->mt_status)->mt_blkno = STps->drv_block;
- (STp->mt_status)->mt_fileno = STps->drv_file;
+ mt_status.mt_blkno = STps->drv_block;
+ mt_status.mt_fileno = STps->drv_file;
if (STp->block_size != 0) {
if (STps->rw == ST_WRITING)
- (STp->mt_status)->mt_blkno +=
+ mt_status.mt_blkno +=
(STp->buffer)->buffer_bytes / STp->block_size;
else if (STps->rw == ST_READING)
- (STp->mt_status)->mt_blkno -=
+ mt_status.mt_blkno -=
((STp->buffer)->buffer_bytes +
STp->block_size - 1) / STp->block_size;
}
- (STp->mt_status)->mt_gstat = 0;
+ mt_status.mt_gstat = 0;
if (STp->drv_write_prot)
- (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
- if ((STp->mt_status)->mt_blkno == 0) {
- if ((STp->mt_status)->mt_fileno == 0)
- (STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
+ mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+ if (mt_status.mt_blkno == 0) {
+ if (mt_status.mt_fileno == 0)
+ mt_status.mt_gstat |= GMT_BOT(0xffffffff);
else
- (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOF(0xffffffff);
}
- (STp->mt_status)->mt_resid = STp->partition;
+ mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
+ mt_status.mt_resid = STp->partition;
if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
- (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOT(0xffffffff);
else if (STps->eof >= ST_EOM_OK)
- (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
+ mt_status.mt_gstat |= GMT_EOD(0xffffffff);
if (STp->density == 1)
- (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_800(0xffffffff);
else if (STp->density == 2)
- (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
else if (STp->density == 3)
- (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+ mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
if (STp->ready == ST_READY)
- (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+ mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
if (STp->ready == ST_NO_TAPE)
- (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
if (STps->at_sm)
- (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
+ mt_status.mt_gstat |= GMT_SM(0xffffffff);
if (STm->do_async_writes ||
(STm->do_buffer_writes && STp->block_size != 0) ||
STp->drv_buffer != 0)
- (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+ mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
- i = copy_to_user((char *) arg, (char *) (STp->mt_status),
+ i = copy_to_user((char *) arg, (char *) &(mt_status),
sizeof(struct mtget));
if (i)
return (-EFAULT);
- (STp->mt_status)->mt_erreg = 0; /* Clear after read */
+ STp->recover_reg = 0; /* Clear after read */
return 0;
} /* End of MTIOCGET */
if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+ struct mtpos mt_pos;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
return (-EINVAL);
- if ((i = get_location(inode, &blk, &bt, 0)) < 0)
+ if ((i = get_location(STp, &blk, &bt, 0)) < 0)
return i;
mt_pos.mt_blkno = blk;
i = copy_to_user((char *) arg, (char *) (&mt_pos), sizeof(struct mtpos));
@@ -2920,15 +2986,21 @@ static int st_ioctl(struct inode *inode, struct file *file,
}
-/* Try to allocate a new tape buffer */
+/* Try to allocate a new tape buffer. Calling function must not hold
+ dev_arr_lock. */
static ST_buffer *
new_tape_buffer(int from_initialization, int need_dma)
{
int i, priority, b_size, order, got = 0, segs = 0;
+ unsigned long flags;
ST_buffer *tb;
- if (st_nbr_buffers >= st_template.dev_max)
+ read_lock(&st_dev_arr_lock);
+ if (st_nbr_buffers >= st_template.dev_max) {
+ read_unlock(&st_dev_arr_lock);
return NULL; /* Should never happen */
+ }
+ read_unlock(&st_dev_arr_lock);
if (from_initialization)
priority = GFP_ATOMIC;
@@ -3014,7 +3086,10 @@ static ST_buffer *
tb->dma = need_dma;
tb->buffer_size = got;
tb->writing = 0;
+
+ write_lock_irqsave(&st_dev_arr_lock, flags);
st_buffers[st_nbr_buffers++] = tb;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return tb;
}
@@ -3039,7 +3114,8 @@ static int enlarge_buffer(ST_buffer * STbuffer, int new_size, int need_dma)
priority |= GFP_DMA;
for (b_size = PAGE_SIZE, order=0;
b_size * nbr < new_size - STbuffer->buffer_size;
- order++, b_size *= 2);
+ order++, b_size *= 2)
+ ; /* empty */
for (segs = STbuffer->sg_segs, got = STbuffer->buffer_size;
segs < max_segs && got < new_size;) {
@@ -3080,7 +3156,7 @@ static void normalize_buffer(ST_buffer * STbuffer)
for (i = STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
for (b_size=PAGE_SIZE, order=0; b_size < STbuffer->sg[i].length;
order++, b_size *= 2)
- ;
+ ; /* empty */
free_pages((unsigned long)(STbuffer->sg[i].address), order);
STbuffer->buffer_size -= STbuffer->sg[i].length;
}
@@ -3239,23 +3315,77 @@ static int st_attach(Scsi_Device * SDp)
Scsi_Tape *tpnt;
ST_mode *STm;
ST_partstat *STps;
- int i, mode;
+ int i, mode, target_nbr;
+ unsigned long flags = 0;
if (SDp->type != TYPE_TAPE)
return 1;
+ write_lock_irqsave(&st_dev_arr_lock, flags);
if (st_template.nr_dev >= st_template.dev_max) {
- SDp->attached--;
- return 1;
+ Scsi_Tape **tmp_da;
+ ST_buffer **tmp_ba;
+ int tmp_dev_max;
+
+ tmp_dev_max = st_template.nr_dev + ST_DEV_ARR_LUMP;
+ if (tmp_dev_max > ST_MAX_TAPES)
+ tmp_dev_max = ST_MAX_TAPES;
+ if (tmp_dev_max <= st_template.nr_dev) {
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Too many tape devices (max. %d).\n",
+ ST_MAX_TAPES);
+ return 1;
+ }
+
+ tmp_da = (Scsi_Tape **) kmalloc(tmp_dev_max * sizeof(Scsi_Tape *),
+ GFP_ATOMIC);
+ tmp_ba = (ST_buffer **) kmalloc(tmp_dev_max * sizeof(ST_buffer *),
+ GFP_ATOMIC);
+ if (tmp_da == NULL || tmp_ba == NULL) {
+ if (tmp_da != NULL)
+ kfree(tmp_da);
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Can't extend device array.\n");
+ return 1;
+ }
+
+ memset(tmp_da, 0, tmp_dev_max * sizeof(Scsi_Tape *));
+ if (scsi_tapes != NULL) {
+ memcpy(tmp_da, scsi_tapes,
+ st_template.dev_max * sizeof(Scsi_Tape *));
+ kfree(scsi_tapes);
+ }
+ scsi_tapes = tmp_da;
+
+ memset(tmp_ba, 0, tmp_dev_max * sizeof(ST_buffer *));
+ if (st_buffers != NULL) {
+ memcpy(tmp_ba, st_buffers,
+ st_template.dev_max * sizeof(ST_buffer *));
+ kfree(st_buffers);
+ }
+ st_buffers = tmp_ba;
+
+ st_template.dev_max = tmp_dev_max;
}
- for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
- if (!tpnt->device)
+ for (i = 0; i < st_template.dev_max; i++)
+ if (scsi_tapes[i] == NULL)
break;
-
if (i >= st_template.dev_max)
panic("scsi_devices corrupt (st)");
+ tpnt = (Scsi_Tape *)kmalloc(sizeof(Scsi_Tape), GFP_ATOMIC);
+ if (tpnt == NULL) {
+ SDp->attached--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+ printk(KERN_ERR "st: Can't allocate device descriptor.\n");
+ return 1;
+ }
+ memset(tpnt, 0, sizeof(Scsi_Tape));
+ scsi_tapes[i] = tpnt;
+
for (mode = 0; mode < ST_NBR_MODES; ++mode) {
char name[8];
static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
@@ -3276,11 +3406,11 @@ static int st_attach(Scsi_Device * SDp)
0, 0, &st_fops, NULL);
}
devfs_register_tape (tpnt->de_r[0]);
- scsi_tapes[i].device = SDp;
+ tpnt->device = SDp;
if (SDp->scsi_level <= 2)
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+ tpnt->tape_type = MT_ISSCSI1;
else
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+ tpnt->tape_type = MT_ISSCSI2;
tpnt->inited = 0;
tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
@@ -3333,6 +3463,20 @@ static int st_attach(Scsi_Device * SDp)
tpnt->blksize_changed = FALSE;
st_template.nr_dev++;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
+
+ /* See if we need to allocate more static buffers */
+ target_nbr = st_template.nr_dev;
+ if (target_nbr > st_max_buffers)
+ target_nbr = st_max_buffers;
+ for (i=st_nbr_buffers; i < target_nbr; i++)
+ if (!new_tape_buffer(TRUE, TRUE)) {
+ printk(KERN_INFO "st: Unable to allocate new static buffer.\n");
+ break;
+ }
+ /* If the previous allocation fails, we will try again when the buffer is
+ really needed. */
+
return 0;
};
@@ -3354,90 +3498,28 @@ static int st_registered = 0;
/* Driver initialization (not __init because may be called later) */
static int st_init()
{
- int i, j;
- Scsi_Tape *STp;
- int target_nbr;
+ unsigned long flags;
- if (st_template.dev_noticed == 0)
+ if (st_template.dev_noticed == 0 || st_registered)
return 0;
printk(KERN_INFO "st: bufsize %d, wrt %d, max init. buffers %d, s/g segs %d.\n",
st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
+ write_lock_irqsave(&st_dev_arr_lock, flags);
if (!st_registered) {
if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
MAJOR_NR);
return 1;
}
st_registered++;
}
- if (scsi_tapes)
- return 0;
- st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
- if (st_template.dev_max < ST_MAX_TAPES)
- st_template.dev_max = ST_MAX_TAPES;
- if (st_template.dev_max > 128 / ST_NBR_MODES)
- printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
- scsi_tapes =
- (Scsi_Tape *) kmalloc(st_template.dev_max * sizeof(Scsi_Tape),
- GFP_ATOMIC);
- if (scsi_tapes == NULL) {
- printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
-
- DEB(printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n",
- st_buffer_size, st_write_threshold));
- memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
- for (i = 0; i < st_template.dev_max; ++i) {
- STp = &(scsi_tapes[i]);
- STp->capacity = 0xfffff;
- STp->mt_status = (struct mtget *) kmalloc(sizeof(struct mtget),
- GFP_ATOMIC);
- if (STp->mt_status == NULL) {
- for (j=0; j < i; j++)
- kfree(scsi_tapes[j].mt_status);
- kfree(scsi_tapes);
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
- /* Initialize status */
- memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
- }
-
- /* Allocate the buffers */
- st_buffers =
- (ST_buffer **) kmalloc(st_template.dev_max * sizeof(ST_buffer *),
- GFP_ATOMIC);
- if (st_buffers == NULL) {
- printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
- devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- for (i=0; i < st_template.dev_max; i++)
- kfree(scsi_tapes[i].mt_status);
- kfree(scsi_tapes);
- unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
- target_nbr = st_template.dev_noticed;
- if (target_nbr < ST_EXTRA_DEVS)
- target_nbr = ST_EXTRA_DEVS;
- if (target_nbr > st_max_buffers)
- target_nbr = st_max_buffers;
-
- for (i = st_nbr_buffers = 0; i < target_nbr; i++) {
- if (!new_tape_buffer(TRUE, TRUE)) {
- if (i == 0) {
- printk(KERN_INFO
- "No tape buffers allocated at initialization.\n");
- break;
- }
- printk(KERN_INFO "Number of tape buffers adjusted.\n");
- break;
- }
- }
+ st_template.dev_max = 0;
+ st_nbr_buffers = 0;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return 0;
}
@@ -3446,9 +3528,12 @@ static void st_detach(Scsi_Device * SDp)
{
Scsi_Tape *tpnt;
int i, mode;
+ unsigned long flags;
- for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
- if (tpnt->device == SDp) {
+ write_lock_irqsave(&st_dev_arr_lock, flags);
+ for (i = 0; i < st_template.dev_max; i++) {
+ tpnt = scsi_tapes[i];
+ if (tpnt != NULL && tpnt->device == SDp) {
tpnt->device = NULL;
for (mode = 0; mode < ST_NBR_MODES; ++mode) {
devfs_unregister (tpnt->de_r[mode]);
@@ -3456,11 +3541,17 @@ static void st_detach(Scsi_Device * SDp)
devfs_unregister (tpnt->de_n[mode]);
tpnt->de_n[mode] = NULL;
}
+ kfree(tpnt);
+ scsi_tapes[i] = 0;
SDp->attached--;
st_template.nr_dev--;
st_template.dev_noticed--;
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return;
}
+ }
+
+ write_unlock_irqrestore(&st_dev_arr_lock, flags);
return;
}
@@ -3484,7 +3575,8 @@ void cleanup_module(void)
st_registered--;
if (scsi_tapes != NULL) {
for (i=0; i < st_template.dev_max; ++i)
- kfree(scsi_tapes[i].mt_status);
+ if (scsi_tapes[i])
+ kfree(scsi_tapes[i]);
kfree(scsi_tapes);
if (st_buffers != NULL) {
for (i = 0; i < st_nbr_buffers; i++) {
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index e751efc28..47b3fbff5 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -47,6 +47,7 @@ typedef struct {
#define ST_NBR_MODES (1 << ST_NBR_MODE_BITS)
#define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
#define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
+#define ST_MAX_TAPES (1 << ST_MODE_SHIFT)
/* The status related to each partition */
typedef struct {
@@ -64,7 +65,6 @@ typedef struct {
/* The tape drive descriptor */
typedef struct {
kdev_t devt;
- unsigned capacity;
Scsi_Device *device;
struct semaphore sem;
ST_buffer *buffer;
@@ -79,6 +79,7 @@ typedef struct {
unsigned char restr_dma;
unsigned char scsi2_logical;
unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */
+ int tape_type;
int write_threshold;
int timeout; /* timeout for normal commands */
int long_timeout; /* timeout for commands known to take long time */
@@ -105,13 +106,14 @@ typedef struct {
unsigned char drv_buffer;
unsigned char density;
unsigned char door_locked;
- unsigned char rew_at_close;
+ unsigned char autorew_dev; /* auto-rewind device */
+ unsigned char rew_at_close; /* rewind necessary at close */
unsigned char inited;
int block_size;
int min_block;
int max_block;
- int recover_count;
- struct mtget *mt_status;
+ int recover_count; /* From tape opening */
+ int recover_reg; /* From last status call */
#if DEBUG
unsigned char write_pending;
@@ -122,7 +124,6 @@ typedef struct {
#endif
} Scsi_Tape;
-extern Scsi_Tape *scsi_tapes;
/* Values of eof */
#define ST_NOEOF 0
diff --git a/drivers/scsi/st_options.h b/drivers/scsi/st_options.h
index 8cbc1c69e..fa3926c5d 100644
--- a/drivers/scsi/st_options.h
+++ b/drivers/scsi/st_options.h
@@ -3,17 +3,12 @@
Copyright 1995-2000 Kai Makisara.
- Last modified: Sat Jan 1 18:34:38 2000 by makisara@kai.makisara.local
+ Last modified: Sat Mar 11 10:32:00 2000 by makisara@kai.makisara.local
*/
#ifndef _ST_OPTIONS_H
#define _ST_OPTIONS_H
-/* The minimum limit for the number of SCSI tape devices is determined by
- ST_MAX_TAPES. If the number of tape devices and the "slack" defined by
- ST_EXTRA_DEVS exceeds ST_MAX_TAPES, the large number is used. */
-#define ST_MAX_TAPES 4
-
/* The driver does not wait for some operations to finish before returning
to the user program if ST_NOWAIT is non-zero. This helps if the SCSI
adapter does not support multiple outstanding commands. However, the user
@@ -47,7 +42,7 @@
driver initialisation. The number is also constrained by the number
of drives detected. If more buffers are needed, they are allocated
at run time and freed after use. */
-#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
+#define ST_MAX_BUFFERS 4
/* Maximum number of scatter/gather segments */
#define ST_MAX_SG 16
diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c
index 36eb7b0c1..f9bbce41e 100644
--- a/drivers/scsi/sym53c8xx.c
+++ b/drivers/scsi/sym53c8xx.c
@@ -55,7 +55,7 @@
*/
/*
-** February 20 2000, sym53c8xx 1.5j
+** March 6 2000, sym53c8xx 1.5k
**
** Supported SCSI features:
** Synchronous data transfers
@@ -84,7 +84,7 @@
/*
** Name and version of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5j"
+#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5k"
/* #define DEBUG_896R1 */
#define SCSI_NCR_OPTIMIZE_896
@@ -174,6 +174,9 @@ typedef u64 u_int64;
#include "sym53c8xx.h"
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
/*
** Hmmm... What complex some PCI-HOST bridges actually are,
** despite the fact that the PCI specifications are looking
@@ -1000,8 +1003,9 @@ static m_addr_t ___dma_getp(m_pool_s *mp)
++mp->nump;
return vp;
}
+ else
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
}
- __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
return 0;
}
@@ -1253,7 +1257,7 @@ static void ncr_printl_hex(char *label, u_char *p, int n)
#define SCSI_DATA_READ 2
#define SCSI_DATA_NONE 3
-static __inline__ scsi_data_direction(Scsi_Cmnd *cmd)
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
{
int direction;
@@ -2043,7 +2047,7 @@ struct head {
#define HF_ACT_PM (1u<<2)
#define HF_DP_SAVED (1u<<3)
#define HF_AUTO_SENSE (1u<<4)
-#define HF_DATA_ST (1u<<5)
+#define HF_DATA_IN (1u<<5)
#define HF_PM_TO_C (1u<<6)
#ifdef SCSI_NCR_IARB_SUPPORT
@@ -2051,6 +2055,11 @@ struct head {
#endif
/*
+** This one is stolen from QU_REG.:)
+*/
+#define HF_DATA_ST (1u<<7)
+
+/*
** First four bytes (script)
*/
#define xerr_st header.scr_st[0]
@@ -2568,8 +2577,12 @@ struct script {
ncrcmd data_in2 [ 4];
ncrcmd data_out [MAX_SCATTER * SCR_SG_SIZE];
ncrcmd data_out2 [ 4];
- ncrcmd pm0_data [ 16];
- ncrcmd pm1_data [ 16];
+ ncrcmd pm0_data [ 12];
+ ncrcmd pm0_data_out [ 6];
+ ncrcmd pm0_data_end [ 6];
+ ncrcmd pm1_data [ 12];
+ ncrcmd pm1_data_out [ 6];
+ ncrcmd pm1_data_end [ 6];
};
/*
@@ -2607,7 +2620,7 @@ struct scripth {
ncrcmd sdata_in [ 6];
ncrcmd data_io [ 2];
ncrcmd data_io_com [ 8];
- ncrcmd data_io_out [ 10];
+ ncrcmd data_io_out [ 12];
ncrcmd bad_identify [ 12];
ncrcmd bad_i_t_l [ 4];
ncrcmd bad_i_t_l_q [ 4];
@@ -3146,12 +3159,11 @@ static struct script script0 __initdata = {
}/*-------------------------< DATAPHASE >------------------*/,{
#ifdef SCSI_NCR_PROFILE_SUPPORT
- SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST),
+ SCR_REG_REG (QU_REG, SCR_OR, HF_DATA_ST),
0,
#endif
SCR_RETURN,
- 0,
-
+ 0,
}/*-------------------------< MSG_IN >--------------------*/,{
/*
** Get the first byte of the message.
@@ -3390,7 +3402,7 @@ static struct script script0 __initdata = {
*/
SCR_LOAD_REL (scratcha, 4),
offsetof (struct ccb, phys.num_disc),
- SCR_FROM_REG (HF_REG),
+ SCR_FROM_REG (QU_REG),
0,
SCR_JUMPR ^ IFTRUE (MASK (HF_DATA_ST, HF_DATA_ST)),
8,
@@ -3692,26 +3704,57 @@ static struct script script0 __initdata = {
}/*-------------------------< PM0_DATA >--------------------*/,{
/*
- ** Keep track we are executing the PM0 DATA
- ** mini-script.
+ ** Read our host flags to SFBR, so we will be able
+ ** to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ ** Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR (pm0_data_out),
+ /*
+ ** Actual phase is DATA IN.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM0 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
0,
/*
- ** MOVE the data according to the actual
- ** DATA direction.
+ ** Move the data to memory.
*/
- SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
- 16,
SCR_CHMOV_TBL ^ SCR_DATA_IN,
offsetof (struct ccb, phys.pm0.sg),
- SCR_JUMPR,
- 8,
+ SCR_JUMP,
+ PADDR (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >----------------*/,{
+ /*
+ ** Actual phase is DATA OUT.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ ** Move the data from memory.
+ */
SCR_CHMOV_TBL ^ SCR_DATA_OUT,
offsetof (struct ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >----------------*/,{
/*
- ** Clear the flag that told we were in
- ** the PM0 DATA mini-script.
+ ** Clear the flag that told we were moving
+ ** data from the PM0 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
0,
@@ -3726,26 +3769,57 @@ static struct script script0 __initdata = {
0,
}/*-------------------------< PM1_DATA >--------------------*/,{
/*
- ** Keep track we are executing the PM1 DATA
- ** mini-script.
+ ** Read our host flags to SFBR, so we will be able
+ ** to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ ** Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR (pm1_data_out),
+ /*
+ ** Actual phase is DATA IN.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM1 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
0,
/*
- ** MOVE the data according to the actual
- ** DATA direction.
+ ** Move the data to memory.
*/
- SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
- 16,
SCR_CHMOV_TBL ^ SCR_DATA_IN,
offsetof (struct ccb, phys.pm1.sg),
- SCR_JUMPR,
- 8,
+ SCR_JUMP,
+ PADDR (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >----------------*/,{
+ /*
+ ** Actual phase is DATA OUT.
+ ** Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDRH (no_data),
+ /*
+ ** Keep track we are moving data from the
+ ** PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ ** Move the data from memory.
+ */
SCR_CHMOV_TBL ^ SCR_DATA_OUT,
offsetof (struct ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >----------------*/,{
/*
- ** Clear the flag that told we were in
- ** the PM1 DATA mini-script.
+ ** Clear the flag that told we were moving
+ ** data from the PM1 DATA mini-script.
*/
SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
0,
@@ -4193,6 +4267,8 @@ static struct scripth scripth0 __initdata = {
/*
** Direction is DATA OUT.
*/
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+ 0,
SCR_LOAD_REL (scratcha, 4),
offsetof (struct ccb, phys.header.wlastp),
SCR_STORE_REL (scratcha, 4),
@@ -5550,7 +5626,7 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
goto attach_error;
NCR_INIT_LOCK_NCB(np);
np->pdev = device->pdev;
- np->p_ncb = __vtobus(device->pdev, np);
+ np->p_ncb = vtobus(np);
host_data->ncb = np;
/*
@@ -6119,18 +6195,18 @@ static void ncr_free_resources(ncb_p np)
*/
static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
{
+ unmap_scsi_data(np, cmd);
cmd->host_scribble = (char *) np->done_list;
np->done_list = cmd;
}
-static inline void ncr_flush_done_cmds(pcidev_t pdev, Scsi_Cmnd *lcmd)
+static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd)
{
Scsi_Cmnd *cmd;
while (lcmd) {
cmd = lcmd;
lcmd = (Scsi_Cmnd *) cmd->host_scribble;
- __unmap_scsi_data(pdev, cmd);
cmd->scsi_done(cmd);
}
}
@@ -6411,6 +6487,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
cp->phys.header.wlastp = cpu_to_scr(lastp);
/* fall through */
case SCSI_DATA_READ:
+ cp->host_flags |= HF_DATA_IN;
goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4));
break;
@@ -6488,7 +6565,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
/*
** command
*/
- memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len);
+ memcpy(cp->cdb_buf, cmd->cmnd, MIN(cmd->cmd_len, sizeof(cp->cdb_buf)));
cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
@@ -9154,7 +9231,7 @@ next:
cp->scsi_status = S_ILLEGAL;
cp->xerr_status = 0;
cp->phys.extra_bytes = 0;
- cp->host_flags &= HF_PM_TO_C;
+ cp->host_flags &= (HF_PM_TO_C|HF_DATA_IN);
break;
@@ -9221,7 +9298,7 @@ next:
*/
cp->sensecmd[0] = 0x03;
cp->sensecmd[1] = cp->lun << 5;
- cp->sensecmd[4] = sizeof(cmd->sense_buffer);
+ cp->sensecmd[4] = sizeof(cp->sense_buf);
/*
** sense data
@@ -9243,7 +9320,7 @@ next:
cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
cp->scsi_status = S_ILLEGAL;
- cp->host_flags = HF_AUTO_SENSE;
+ cp->host_flags = (HF_AUTO_SENSE|HF_DATA_IN);
cp->phys.header.go.start =
cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
@@ -12616,8 +12693,10 @@ printk("sym53c8xx : command successfully queued\n");
NCR_UNLOCK_NCB(np, flags);
- if (sts != DID_OK)
+ if (sts != DID_OK) {
+ unmap_scsi_data(np, cmd);
done(cmd);
+ }
return sts;
}
@@ -12635,7 +12714,6 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
unsigned long flags;
ncb_p np = (ncb_p) dev_id;
Scsi_Cmnd *done_list;
- pcidev_t pdev;
#ifdef DEBUG_SYM53C8XX
printk("sym53c8xx : interrupt received\n");
@@ -12645,7 +12723,6 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
NCR_LOCK_NCB(np, flags);
ncr_exception(np);
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
@@ -12654,7 +12731,7 @@ static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
if (done_list) {
NCR_LOCK_SCSI_DONE(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
NCR_UNLOCK_SCSI_DONE(np, flags);
}
}
@@ -12667,19 +12744,17 @@ static void sym53c8xx_timeout(unsigned long npref)
{
ncb_p np = (ncb_p) npref;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
NCR_LOCK_NCB(np, flags);
ncr_timeout((ncb_p) np);
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
if (done_list) {
NCR_LOCK_SCSI_DONE(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
NCR_UNLOCK_SCSI_DONE(np, flags);
}
}
@@ -12697,7 +12772,6 @@ int sym53c8xx_reset(Scsi_Cmnd *cmd)
ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
int sts;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12742,12 +12816,11 @@ int sym53c8xx_reset(Scsi_Cmnd *cmd)
#endif
out:
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
return sts;
}
@@ -12761,7 +12834,6 @@ int sym53c8xx_abort(Scsi_Cmnd *cmd)
ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
int sts;
unsigned long flags;
- pcidev_t pdev;
Scsi_Cmnd *done_list;
#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
@@ -12785,12 +12857,11 @@ int sym53c8xx_abort(Scsi_Cmnd *cmd)
sts = ncr_abort_command(np, cmd);
out:
- pdev = np->pdev;
done_list = np->done_list;
np->done_list = 0;
NCR_UNLOCK_NCB(np, flags);
- ncr_flush_done_cmds(pdev, done_list);
+ ncr_flush_done_cmds(done_list);
return sts;
}
diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h
new file mode 100644
index 000000000..fa02ff585
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_comm.h
@@ -0,0 +1,2863 @@
+/******************************************************************************
+** High Performance device driver for the Symbios 53C896 controller.
+**
+** Copyright (C) 1998-2000 Gerard Roudier <groudier@club-internet.fr>
+**
+** This driver also supports all the Symbios 53C8XX controller family,
+** except 53C810 revisions < 16, 53C825 revisions < 16 and all
+** revisions of 53C815 controllers.
+**
+** This driver is based on the Linux port of the FreeBSD ncr driver.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+**-----------------------------------------------------------------------------
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+** The Linux port of the FreeBSD ncr driver has been achieved in
+** november 1995 by:
+**
+** Gerard Roudier <groudier@club-internet.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+** Major contributions:
+** --------------------
+**
+** NVRAM detection and reading.
+** Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+*******************************************************************************
+*/
+
+/*
+** This file contains definitions and code that the
+** sym53c8xx and ncr53c8xx drivers should share.
+** The sharing will be achieved in a further version
+** of the driver bundle. For now, only the ncr53c8xx
+** driver includes this file.
+*/
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+/*==========================================================
+**
+** Hmmm... What complex some PCI-HOST bridges actually
+** are, despite the fact that the PCI specifications
+** are looking so smart and simple! ;-)
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define SCSI_NCR_DYNAMIC_DMA_MAPPING
+#endif
+
+/*==========================================================
+**
+** Io mapped versus memory mapped.
+**
+**==========================================================
+*/
+
+#if defined(SCSI_NCR_IOMAPPED) || defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+#define NCR_IOMAPPED
+#endif
+
+/*==========================================================
+**
+** Miscallaneous defines.
+**
+**==========================================================
+*/
+
+#define u_char unsigned char
+#define u_short unsigned short
+#define u_int unsigned int
+#define u_long unsigned long
+
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+
+#ifndef bcmp
+#define bcmp(s, d, n) memcmp((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(d, n) memset((d), 0, (n))
+#endif
+
+#ifndef offsetof
+#define offsetof(t, m) ((size_t) (&((t *)0)->m))
+#endif
+
+/*==========================================================
+**
+** assert ()
+**
+**==========================================================
+**
+** modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)panic( \
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+
+/*==========================================================
+**
+** Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_POINTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_NEGO (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_SCATTER (0x0800)
+
+/*
+** Enable/Disable debug messages.
+** Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+ #define DEBUG_FLAGS ncr_debug
+#else
+ #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
+#endif
+
+/*==========================================================
+**
+** A la VMS/CAM-3 queue management.
+** Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+ struct xpt_quehead *flink; /* Forward pointer */
+ struct xpt_quehead *blink; /* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+ (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+ struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
+{
+ flink->blink = new;
+ new->flink = flink;
+ new->blink = blink;
+ blink->flink = new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
+{
+ flink->blink = blink;
+ blink->flink = flink;
+}
+
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+ return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+ struct xpt_quehead *head)
+{
+ struct xpt_quehead *first = list->flink;
+
+ if (first != list) {
+ struct xpt_quehead *last = list->blink;
+ struct xpt_quehead *at = head->flink;
+
+ first->blink = head;
+ head->flink = first;
+
+ last->flink = at;
+ at->blink = last;
+ }
+}
+
+#define xpt_que_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos) __xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el) __xpt_que_del((el)->blink, (el)->flink)
+
+#define xpt_insque_head(new, head) __xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->flink;
+
+ if (elem != head)
+ __xpt_que_del(head, elem->flink);
+ else
+ elem = 0;
+ return elem;
+}
+
+#define xpt_insque_tail(new, head) __xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->blink;
+
+ if (elem != head)
+ __xpt_que_del(elem->blink, head);
+ else
+ elem = 0;
+ return elem;
+}
+
+/*==========================================================
+**
+** On x86 architecture, write buffers management does
+** not reorder writes to memory. So, using compiler
+** optimization barriers is enough to guarantee some
+** ordering when the CPU is writing data accessed by
+** the NCR.
+** On Alpha architecture, explicit memory barriers have
+** to be used.
+** Other architectures are defaulted to mb() macro if
+** defined, otherwise use compiler barrier.
+**
+**==========================================================
+*/
+
+#if defined(__i386__)
+#define MEMORY_BARRIER() barrier()
+#elif defined(__alpha__)
+#define MEMORY_BARRIER() mb()
+#else
+# ifdef mb
+# define MEMORY_BARRIER() mb()
+# else
+# define MEMORY_BARRIER() barrier()
+# endif
+#endif
+
+/*==========================================================
+**
+** Simple Wrapper to kernel PCI bus interface.
+**
+** This wrapper allows to get rid of old kernel PCI
+** interface and still allows to preserve linux-2.0
+** compatibilty. In fact, it is mostly an incomplete
+** emulation of the new PCI code for pre-2.2 kernels.
+** When kernel-2.0 support will be dropped, we will
+** just have to remove most of this code.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL (0)
+#define PciBusNumber(d) (d)->bus->number
+#define PciDeviceFn(d) (d)->devfn
+#define PciVendorId(d) (d)->vendor
+#define PciDeviceId(d) (d)->device
+#define PciIrqLine(d) (d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+ *base = pdev->resource[index].start;
+ if ((pdev->resource[index].flags & 0x7) == 0x4)
+ ++index;
+ return ++index;
+}
+#else
+static int __init
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+ *base = pdev->base_address[index++];
+ if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+ *base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+ ++index;
+ }
+ return index;
+}
+#endif
+
+#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL (~0u)
+#define PciBusNumber(d) ((d)>>8)
+#define PciDeviceFn(d) ((d)&0xff)
+#define __PciDev(busn, devfn) (((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+ pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+ pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+ pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+ pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+ pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+ pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+ static unsigned short pci_index;
+ int retv;
+ unsigned char bus_number, device_fn;
+
+ if (prev == PCIDEV_NULL)
+ pci_index = 0;
+ else
+ ++pci_index;
+ retv = pcibios_find_device (vendor, device, pci_index,
+ &bus_number, &device_fn);
+ return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+ u_short vendor_id;
+ pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+ return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+ u_short device_id;
+ pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+ return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+ u_char irq;
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+ return irq;
+}
+
+static int __init
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+ u_int32 tmp;
+
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+ *base = tmp;
+ offset += sizeof(u_int32);
+ if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+ *base |= (((u_long)tmp) << 32);
+#endif
+ offset += sizeof(u_int32);
+ }
+ return offset;
+}
+
+#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
+/*==========================================================
+**
+** SMP threading.
+**
+** Assuming that SMP systems are generally high end
+** systems and may use several SCSI adapters, we are
+** using one lock per controller instead of some global
+** one. For the moment (linux-2.1.95), driver's entry
+** points are called with the 'io_request_lock' lock
+** held, so:
+** - We are uselessly loosing a couple of micro-seconds
+** to lock the controller data structure.
+** - But the driver is not broken by design for SMP and
+** so can be more resistant to bugs or bad changes in
+** the IO sub-system code.
+** - A small advantage could be that the interrupt code
+** is grained as wished (e.g.: by controller).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
+#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&DRIVER_SMP_LOCK, flags)
+#define NCR_UNLOCK_DRIVER(flags) \
+ spin_unlock_irqrestore(&DRIVER_SMP_LOCK, flags)
+
+#define NCR_INIT_LOCK_NCB(np) spin_lock_init(&np->smp_lock)
+#define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags)
+#define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags)
+
+#define NCR_LOCK_SCSI_DONE(np, flags) \
+ spin_lock_irqsave(&io_request_lock, flags)
+#define NCR_UNLOCK_SCSI_DONE(np, flags) \
+ spin_unlock_irqrestore(&io_request_lock, flags)
+
+#else
+
+#define NCR_LOCK_DRIVER(flags) do { save_flags(flags); cli(); } while (0)
+#define NCR_UNLOCK_DRIVER(flags) do { restore_flags(flags); } while (0)
+
+#define NCR_INIT_LOCK_NCB(np) do { } while (0)
+#define NCR_LOCK_NCB(np, flags) do { save_flags(flags); cli(); } while (0)
+#define NCR_UNLOCK_NCB(np, flags) do { restore_flags(flags); } while (0)
+
+#define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0)
+#define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0)
+
+#endif
+
+/*==========================================================
+**
+** Memory mapped IO
+**
+** Since linux-2.1, we must use ioremap() to map the io
+** memory space and iounmap() to unmap it. This allows
+** portability. Linux 1.3.X and 2.0.X allow to remap
+** physical pages addresses greater than the highest
+** physical memory address to kernel virtual pages with
+** vremap() / vfree(). That was not portable but worked
+** with i386 architecture.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+#ifdef __sparc__
+# include <asm/irq.h>
+# define pcivtobus(p) bus_dvma_to_mem(p)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#elif defined(__alpha__)
+# define pcivtobus(p) ((p) & 0xfffffffful)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#else /* others */
+# define pcivtobus(p) (p)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#endif
+
+#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
+static u_long __init remap_pci_mem(u_long base, u_long size)
+{
+ u_long page_base = ((u_long) base) & PAGE_MASK;
+ u_long page_offs = ((u_long) base) - page_base;
+ u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
+
+ return page_remapped? (page_remapped + page_offs) : 0UL;
+}
+
+static void __init unmap_pci_mem(u_long vaddr, u_long size)
+{
+ if (vaddr)
+ iounmap((void *) (vaddr & PAGE_MASK));
+}
+
+#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */
+
+/*==========================================================
+**
+** Insert a delay in micro-seconds and milli-seconds.
+**
+** Under Linux, udelay() is restricted to delay <
+** 1 milli-second. In fact, it generally works for up
+** to 1 second delay. Since 2.1.105, the mdelay() function
+** is provided for delays in milli-seconds.
+** Under 2.0 kernels, udelay() is an inline function
+** that is very inaccurate on Pentium processors.
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
+#define UDELAY udelay
+#define MDELAY mdelay
+#else
+static void UDELAY(long us) { udelay(us); }
+static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
+#endif
+
+/*==========================================================
+**
+** Simple power of two buddy-like allocator.
+**
+** This simple code is not intended to be fast, but to
+** provide power of 2 aligned memory allocations.
+** Since the SCRIPTS processor only supplies 8 bit
+** arithmetic, this allocator allows simple and fast
+** address calculations from the SCRIPTS code.
+** In addition, cache line alignment is guaranteed for
+** power of 2 cache line size.
+** Enhanced in linux-2.3.44 to provide a memory pool
+** per pcidev to support dynamic dma mapping. (I would
+** have preferred a real bus astraction, btw).
+**
+**==========================================================
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
+#else
+#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)
+#endif
+
+#define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */
+#else
+#define MEMO_PAGE_ORDER 1 /* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED /* Free unused pages immediately */
+#define MEMO_WARN 1
+#define MEMO_GFP_FLAGS GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */
+typedef pcidev_t m_bush_t; /* Something that addresses DMAable */
+
+typedef struct m_link { /* Link between free memory chunks */
+ struct m_link *next;
+} m_link_s;
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+typedef struct m_vtob { /* Virtual to Bus address translation */
+ struct m_vtob *next;
+ m_addr_t vaddr;
+ m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT 5
+#define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK (VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m) \
+ ((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif
+
+typedef struct m_pool { /* Memory pool of a given kind */
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ m_bush_t bush;
+ m_addr_t (*getp)(struct m_pool *);
+ void (*freep)(struct m_pool *, m_addr_t);
+#define M_GETP() mp->getp(mp)
+#define M_FREEP(p) mp->freep(mp, p)
+#define GetPages() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define FreePages(p) free_pages(p, MEMO_PAGE_ORDER)
+ int nump;
+ m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+ struct m_pool *next;
+#else
+#define M_GETP() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)
+#define M_FREEP(p) free_pages(p, MEMO_PAGE_ORDER)
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+ struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
+
+static void *___m_alloc(m_pool_s *mp, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ int j;
+ m_addr_t a;
+ m_link_s *h = mp->h;
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return 0;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ j = i;
+ while (!h[j].next) {
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ h[j].next = (m_link_s *) M_GETP();
+ if (h[j].next)
+ h[j].next->next = 0;
+ break;
+ }
+ ++j;
+ s <<= 1;
+ }
+ a = (m_addr_t) h[j].next;
+ if (a) {
+ h[j].next = h[j].next->next;
+ while (j > i) {
+ j -= 1;
+ s >>= 1;
+ h[j].next = (m_link_s *) (a+s);
+ h[j].next->next = 0;
+ }
+ }
+#ifdef DEBUG
+ printk("___m_alloc(%d) = %p\n", size, (void *) a);
+#endif
+ return (void *) a;
+}
+
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ m_link_s *q;
+ m_addr_t a, b;
+ m_link_s *h = mp->h;
+
+#ifdef DEBUG
+ printk("___m_free(%p, %d)\n", ptr, size);
+#endif
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ a = (m_addr_t) ptr;
+
+ while (1) {
+#ifdef MEMO_FREE_UNUSED
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ M_FREEP(a);
+ break;
+ }
+#endif
+ b = a ^ s;
+ q = &h[i];
+ while (q->next && q->next != (m_link_s *) b) {
+ q = q->next;
+ }
+ if (!q->next) {
+ ((m_link_s *) a)->next = h[i].next;
+ h[i].next = (m_link_s *) a;
+ break;
+ }
+ q->next = q->next->next;
+ a = a & b;
+ s <<= 1;
+ ++i;
+ }
+}
+
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
+{
+ void *p;
+
+ p = ___m_alloc(mp, size);
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("new %-10s[%4d] @%p.\n", name, size, p);
+
+ if (p)
+ bzero(p, size);
+ else if (uflags & MEMO_WARN)
+ printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
+
+ return p;
+}
+
+#define __m_calloc(mp, s, n) __m_calloc2(mp, s, n, MEMO_WARN)
+
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+ ___m_free(mp, ptr, size);
+
+}
+
+/*
+ * With pci bus iommu support, we use a default pool of unmapped memory
+ * for memory we donnot need to DMA from/to and one pool per pcidev for
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+static m_pool_s mp0;
+
+#else
+
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+ m_addr_t m = GetPages();
+ if (m)
+ ++mp->nump;
+ return m;
+}
+
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+ FreePages(m);
+ --mp->nump;
+}
+
+static m_pool_s mp0 = {0, ___mp0_getp, ___mp0_freep};
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+static void *m_calloc(int size, char *name)
+{
+ u_long flags;
+ void *m;
+ NCR_LOCK_DRIVER(flags);
+ m = __m_calloc(&mp0, size, name);
+ NCR_UNLOCK_DRIVER(flags);
+ return m;
+}
+
+static void m_free(void *ptr, int size, char *name)
+{
+ u_long flags;
+ NCR_LOCK_DRIVER(flags);
+ __m_free(&mp0, ptr, size, name);
+ NCR_UNLOCK_DRIVER(flags);
+}
+
+/*
+ * DMAable pools.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Without pci bus iommu support, all the memory is assumed DMAable */
+
+#define __m_calloc_dma(b, s, n) m_calloc(s, n)
+#define __m_free_dma(b, p, s, n) m_free(p, s, n)
+#define __vtobus(b, p) virt_to_bus(p)
+
+#else
+
+/*
+ * With pci bus iommu support, we maintain one pool per pcidev and a
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+ m_addr_t vp;
+ m_vtob_s *vbp;
+
+ vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+ if (vbp) {
+ dma_addr_t daddr;
+ vp = (m_addr_t) pci_alloc_consistent(mp->bush,
+ PAGE_SIZE<<MEMO_PAGE_ORDER,
+ &daddr);
+ if (vp) {
+ int hc = VTOB_HASH_CODE(vp);
+ vbp->vaddr = vp;
+ vbp->baddr = daddr;
+ vbp->next = mp->vtob[hc];
+ mp->vtob[hc] = vbp;
+ ++mp->nump;
+ return vp;
+ }
+ }
+ if (vbp)
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ return 0;
+}
+
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+ m_vtob_s **vbpp, *vbp;
+ int hc = VTOB_HASH_CODE(m);
+
+ vbpp = &mp->vtob[hc];
+ while (*vbpp && (*vbpp)->vaddr != m)
+ vbpp = &(*vbpp)->next;
+ if (*vbpp) {
+ vbp = *vbpp;
+ *vbpp = (*vbpp)->next;
+ pci_free_consistent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+ (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ --mp->nump;
+ }
+}
+
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+ return mp;
+}
+
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+ if (mp) {
+ bzero(mp, sizeof(*mp));
+ mp->bush = bush;
+ mp->getp = ___dma_getp;
+ mp->freep = ___dma_freep;
+ mp->next = mp0.next;
+ mp0.next = mp;
+ }
+ return mp;
+}
+
+static void ___del_dma_pool(m_pool_s *p)
+{
+ struct m_pool **pp = &mp0.next;
+
+ while (*pp && *pp != p)
+ pp = &(*pp)->next;
+ if (*pp) {
+ *pp = (*pp)->next;
+ __m_free(&mp0, p, sizeof(*p), "MPOOL");
+ }
+}
+
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+ void *m = 0;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (!mp)
+ mp = ___cre_dma_pool(bush);
+ if (mp)
+ m = __m_calloc(mp, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ NCR_UNLOCK_DRIVER(flags);
+
+ return m;
+}
+
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (mp)
+ __m_free(mp, m, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ NCR_UNLOCK_DRIVER(flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+ u_long flags;
+ m_pool_s *mp;
+ int hc = VTOB_HASH_CODE(m);
+ m_vtob_s *vp = 0;
+ m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+ NCR_LOCK_DRIVER(flags);
+ mp = ___get_dma_pool(bush);
+ if (mp) {
+ vp = mp->vtob[hc];
+ while (vp && (m_addr_t) vp->vaddr != a)
+ vp = vp->next;
+ }
+ NCR_UNLOCK_DRIVER(flags);
+ return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define _m_calloc_dma(np, s, n) __m_calloc_dma(np->pdev, s, n)
+#define _m_free_dma(np, p, s, n) __m_free_dma(np->pdev, p, s, n)
+#define m_calloc_dma(s, n) _m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n) _m_free_dma(np, p, s, n)
+#define _vtobus(np, p) __vtobus(np->pdev, p)
+#define vtobus(p) _vtobus(np, p)
+
+/*
+ * Deal with DMA mapping/unmapping.
+ */
+
+#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPING
+
+/* Linux versions prior to pci bus iommu kernel interface */
+
+#define __unmap_scsi_data(pdev, cmd) do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd) ((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd) do {; } while (0)
+
+#define scsi_sg_dma_address(sc) vtobus((sc)->address)
+#define scsi_sg_dma_len(sc) ((sc)->length)
+
+#else
+
+/* Linux version with pci bus iommu kernel interface */
+
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped SCp.phase
+#define __data_mapping SCp.have_data_in
+
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ pci_unmap_single(pdev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+ cmd->__data_mapped = 0;
+}
+
+static u_long __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ dma_addr_t mapping;
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ if (cmd->request_bufflen == 0)
+ return 0;
+
+ mapping = pci_map_single(pdev, cmd->request_buffer,
+ cmd->request_bufflen, dma_dir);
+ cmd->__data_mapped = 1;
+ cmd->__data_mapping = mapping;
+
+ return mapping;
+}
+
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int use_sg;
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ if (cmd->use_sg == 0)
+ return 0;
+
+ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ cmd->__data_mapped = 2;
+ cmd->__data_mapping = use_sg;
+
+ return use_sg;
+}
+
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+ int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ pci_dma_sync_single(pdev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+}
+
+#define scsi_sg_dma_address(sc) sg_dma_address(sc)
+#define scsi_sg_dma_len(sc) sg_dma_len(sc)
+
+#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */
+
+#define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->pdev, cmd)
+#define map_scsi_single_data(np, cmd) __map_scsi_single_data(np->pdev, cmd)
+#define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->pdev, cmd)
+#define sync_scsi_data(np, cmd) __sync_scsi_data(np->pdev, cmd)
+
+/*==========================================================
+**
+** SCSI data transfer direction
+**
+** Until some linux kernel version near 2.3.40,
+** low-level scsi drivers were not told about data
+** transfer direction. We check the existence of this
+** feature that has been expected for a _long_ time by
+** all SCSI driver developers by just testing against
+** the definition of SCSI_DATA_UNKNOWN. Indeed this is
+** a hack, but testing against a kernel version would
+** have been a shame. ;-)
+**
+**==========================================================
+*/
+#ifdef SCSI_DATA_UNKNOWN
+
+#define scsi_data_direction(cmd) (cmd->sc_data_direction)
+
+#else
+
+#define SCSI_DATA_UNKNOWN 0
+#define SCSI_DATA_WRITE 1
+#define SCSI_DATA_READ 2
+#define SCSI_DATA_NONE 3
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+ int direction;
+
+ switch((int) cmd->cmnd[0]) {
+ case 0x08: /* READ(6) 08 */
+ case 0x28: /* READ(10) 28 */
+ case 0xA8: /* READ(12) A8 */
+ direction = SCSI_DATA_READ;
+ break;
+ case 0x0A: /* WRITE(6) 0A */
+ case 0x2A: /* WRITE(10) 2A */
+ case 0xAA: /* WRITE(12) AA */
+ direction = SCSI_DATA_WRITE;
+ break;
+ default:
+ direction = SCSI_DATA_UNKNOWN;
+ break;
+ }
+
+ return direction;
+}
+
+#endif /* SCSI_DATA_UNKNOWN */
+
+/*==========================================================
+**
+** Driver setup.
+**
+** This structure is initialized from linux config
+** options. It can be overridden at boot-up by the boot
+** command line.
+**
+**==========================================================
+*/
+static struct ncr_driver_setup
+ driver_setup = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+ driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+/*==========================================================
+**
+** Big/Little endian support.
+**
+** If the NCR uses big endian addressing mode over the
+** PCI, actual io register addresses for byte and word
+** accesses must be changed according to lane routing.
+** Btw, ncr_offb() and ncr_offw() macros only apply to
+** constants and so donnot generate bloated code.
+**
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for script patching.
+** Macro cpu_to_scr() is to be used for script patching.
+** Macro scr_to_cpu() is to be used for getting a DWORD
+** from the script.
+**
+**==========================================================
+*/
+
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
+
+#endif
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*==========================================================
+**
+** Access to the controller chip.
+**
+** If NCR_IOMAPPED is defined, the driver will use
+** normal IOs instead of the MEMORY MAPPED IO method
+** recommended by PCI specifications.
+** If all PCI bridges, host brigdes and architectures
+** would have been correctly designed for PCI, this
+** option would be useless.
+**
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for accessing chip io
+** registers. Functions suffixed by '_raw' are assumed
+** to access the chip over the PCI without doing byte
+** reordering. Functions suffixed by '_l2b' are
+** assumed to perform little-endian to big-endian byte
+** reordering, those suffixed by '_b2l' blah, blah,
+** blah, ...
+**
+**==========================================================
+*/
+
+#if defined(NCR_IOMAPPED)
+
+/*
+** IO mapped only input / ouput
+*/
+
+#define INB_OFF(o) inb (np->base_io + ncr_offb(o))
+#define OUTB_OFF(o, val) outb ((val), np->base_io + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_l2b (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_l2b (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_b2l ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_b2l ((val), np->base_io + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_b2l (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_b2l (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_l2b ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_l2b ((val), np->base_io + (o))
+
+#else
+
+#define INW_OFF(o) inw_raw (np->base_io + ncr_offw(o))
+#define INL_OFF(o) inl_raw (np->base_io + (o))
+
+#define OUTW_OFF(o, val) outw_raw ((val), np->base_io + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_raw ((val), np->base_io + (o))
+
+#endif /* ENDIANs */
+
+#else /* defined NCR_IOMAPPED */
+
+/*
+** MEMORY mapped IO input / output
+*/
+
+#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
+
+#else
+
+#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_raw((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
+
+#endif
+
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+** Set bit field ON, OFF
+*/
+
+#define OUTONB(r, m) OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m) OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m) OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
+
+/*==========================================================
+**
+** Structures used by the detection routine to transmit
+** device configuration to the attach function.
+**
+**==========================================================
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_long base;
+ u_long base_2;
+ u_long io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_long base_io;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+/*==========================================================
+**
+** Structure used to store the NVRAM content.
+**
+**==========================================================
+*/
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*==========================================================
+**
+** Structure used by detection routine to save data on
+** each detected board for attach.
+**
+**==========================================================
+*/
+typedef struct {
+ pcidev_t pdev;
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ u_char host_id;
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ u_char pqs_pds;
+#endif
+ int attach_done;
+} ncr_device;
+
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
+
+/*==========================================================
+**
+** NVRAM detection and reading.
+**
+** Currently supported:
+** - 24C16 EEPROM with both Symbios and Tekram layout.
+** - 93C46 EEPROM with Tekram layout.
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+ * 24C16 EEPROM reading.
+ *
+ * GPOI0 - data in/data out
+ * GPIO1 - clock
+ * Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+{
+ UDELAY (5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (5);
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+static void __init
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+{
+ S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ S24C16_do_bit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+static void __init
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ S24C16_do_bit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+static void __init
+S24C16_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+static void __init
+S24C16_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ S24C16_do_bit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Read 'len' bytes starting at 'offset'.
+ */
+static int __init
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_char ack_data;
+ int retv = 1;
+ int x;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ S24C16_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ S24C16_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ S24C16_write_byte(np, &ack_data,
+ 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ S24C16_write_byte(np, &ack_data,
+ offset & 0xff, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ S24C16_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ S24C16_write_byte(np, &ack_data,
+ 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all requested data - only part of total NVRAM */
+ for (x = 0; x < len; x++)
+ S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ S24C16_stop(np, &gpreg);
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+/*
+ * Try reading Symbios NVRAM.
+ * Return 0 if OK.
+ */
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ /* probe the 24c16 and read the SYMBIOS 24c16 area */
+ if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+ return 1;
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->type != 0 ||
+ memcmp(nvram->trailer, Symbios_trailer, 6) ||
+ nvram->byte_count != len - 12)
+ return 1;
+
+ /* verify checksum */
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+ if (csum != nvram->checksum)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 93C46 EEPROM reading.
+ *
+ * GPOI0 - data in
+ * GPIO1 - data out
+ * GPIO2 - clock
+ * GPIO4 - chip select
+ *
+ * Used by Tekram.
+ */
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ UDELAY (2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+/*
+ * Read bit from NVRAM
+ */
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+{
+ UDELAY (2);
+ T93C46_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+static void __init T93C46_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ UDELAY (2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+static void __init
+T93C46_Send_Command(ncr_slot *np, u_short write_data,
+ u_char *read_bit, u_char *gpreg)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ 2 bytes from the NVRAM
+ */
+static void __init
+T93C46_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ T93C46_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read Tekram NvRAM data.
+ */
+static int __init
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
+{
+ u_char read_bit;
+ int x;
+
+ for (x = 0; x < len; x++) {
+
+ /* output read command and address */
+ T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 1; /* Bad */
+ T93C46_Read_Word(np, &data[x], gpreg);
+ T93C46_Stop(np, gpreg);
+ }
+
+ return 0;
+}
+
+/*
+ * Try reading 93C46 Tekram NVRAM.
+ */
+static int __init
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ retv = T93C46_Read_Data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Try reading Tekram NVRAM.
+ * Return 0 if OK.
+ */
+static int __init
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
+{
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ switch (device_id) {
+ case PCI_DEVICE_ID_NCR_53C885:
+ case PCI_DEVICE_ID_NCR_53C895:
+ case PCI_DEVICE_ID_NCR_53C896:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ break;
+ case PCI_DEVICE_ID_NCR_53C875:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ if (!x)
+ break;
+ default:
+ x = sym_read_T93C46_nvram(np, nvram);
+ break;
+ }
+ if (x)
+ return 1;
+
+ /* verify checksum */
+ for (x = 0, csum = 0; x < len - 1; x += 2)
+ csum += data[x] + (data[x+1] << 8);
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*===================================================================
+**
+** Detect and try to read SYMBIOS and TEKRAM NVRAM.
+**
+** Data can be used to order booting of boards.
+**
+** Data is saved in ncr_device structure if NVRAM found. This
+** is then used to find drive boot order for ncr_attach().
+**
+** NVRAM data is passed to Scsi_Host_Template later during
+** ncr_attach() for any device set up.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static void __init ncr_get_nvram(ncr_device *devp, ncr_nvram *nvp)
+{
+ devp->nvram = nvp;
+ if (!nvp)
+ return;
+ /*
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(devp->slot.io_port, 128, NAME53C8XX);
+ devp->slot.base_io = devp->slot.io_port;
+#else
+ devp->slot.reg = (struct ncr_reg *) remap_pci_mem(devp->slot.base, 128);
+ if (!devp->slot.reg)
+ return;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+ if (!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+ nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+ &nvp->data.Tekram))
+ nvp->type = SCSI_NCR_TEKRAM_NVRAM;
+ else {
+ nvp->type = 0;
+ devp->nvram = 0;
+ }
+
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(devp->slot.base_io, 128);
+#else
+ unmap_pci_mem((u_long) devp->slot.reg, 128ul);
+#endif
+
+}
+
+/*===================================================================
+**
+** Display the content of NVRAM for debugging purpose.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_DEBUG_NVRAM
+static void __init ncr_display_Symbios_nvram(Symbios_nvram *nvram)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printk(KERN_DEBUG NAME53C8XX ": HOST ID=%d%s%s%s%s%s\n",
+ nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"",
+ (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printk(KERN_DEBUG NAME53C8XX
+ "-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+static void __init ncr_display_Tekram_nvram(Tekram_nvram *nvram)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printk(KERN_DEBUG NAME53C8XX
+ ": HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = Tekram_sync[j];
+ printk(KERN_DEBUG NAME53C8XX "-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+
+/*===================================================================
+**
+** Utility routines that protperly return data through /proc FS.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+#endif
+
+/*===================================================================
+**
+** Driver setup from the boot command line
+**
+**===================================================================
+*/
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+#define OPT_TAGS 1
+#define OPT_MASTER_PARITY 2
+#define OPT_SCSI_PARITY 3
+#define OPT_DISCONNECTION 4
+#define OPT_SPECIAL_FEATURES 5
+#define OPT_ULTRA_SCSI 6
+#define OPT_FORCE_SYNC_NEGO 7
+#define OPT_REVERSE_PROBE 8
+#define OPT_DEFAULT_SYNC 9
+#define OPT_VERBOSE 10
+#define OPT_DEBUG 11
+#define OPT_BURST_MAX 12
+#define OPT_LED_PIN 13
+#define OPT_MAX_WIDE 14
+#define OPT_SETTLE_DELAY 15
+#define OPT_DIFF_SUPPORT 16
+#define OPT_IRQM 17
+#define OPT_PCI_FIX_UP 18
+#define OPT_BUS_CHECK 19
+#define OPT_OPTIMIZE 20
+#define OPT_RECOVERY 21
+#define OPT_SAFE_SETUP 22
+#define OPT_USE_NVRAM 23
+#define OPT_EXCLUDE 24
+#define OPT_HOST_ID 25
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB 26
+#endif
+
+static char setup_token[] __initdata =
+ "tags:" "mpar:"
+ "spar:" "disc:"
+ "specf:" "ultra:"
+ "fsn:" "revprob:"
+ "sync:" "verb:"
+ "debug:" "burst:"
+ "led:" "wide:"
+ "settle:" "diff:"
+ "irqm:" "pcifix:"
+ "buschk:" "optim:"
+ "recovery:"
+ "safe:" "nvram:"
+ "excl:" "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+ "iarb:"
+#endif
+ ; /* DONNOT REMOVE THIS ';' */
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+ char *cur = setup_token;
+ char *pc;
+ int i = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ ++pc;
+ ++i;
+ if (!strncmp(p, cur, pc - cur))
+ return i;
+ cur = pc;
+ }
+ return 0;
+}
+
+
+static int __init sym53c8xx__setup(char *str)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+ char *cur = str;
+ char *pc, *pv;
+ int i, val, c;
+ int xi = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ char *pe;
+
+ val = 0;
+ pv = pc;
+ c = *++pv;
+
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else
+ val = (int) simple_strtoul(pv, &pe, 0);
+
+ switch (get_setup_token(cur)) {
+ case OPT_TAGS:
+ driver_setup.default_tags = val;
+ if (pe && *pe == '/') {
+ i = 0;
+ while (*pe && *pe != ARG_SEP &&
+ i < sizeof(driver_setup.tag_ctrl)-1) {
+ driver_setup.tag_ctrl[i++] = *pe++;
+ }
+ driver_setup.tag_ctrl[i] = '\0';
+ }
+ break;
+ case OPT_MASTER_PARITY:
+ driver_setup.master_parity = val;
+ break;
+ case OPT_SCSI_PARITY:
+ driver_setup.scsi_parity = val;
+ break;
+ case OPT_DISCONNECTION:
+ driver_setup.disconnection = val;
+ break;
+ case OPT_SPECIAL_FEATURES:
+ driver_setup.special_features = val;
+ break;
+ case OPT_ULTRA_SCSI:
+ driver_setup.ultra_scsi = val;
+ break;
+ case OPT_FORCE_SYNC_NEGO:
+ driver_setup.force_sync_nego = val;
+ break;
+ case OPT_REVERSE_PROBE:
+ driver_setup.reverse_probe = val;
+ break;
+ case OPT_DEFAULT_SYNC:
+ driver_setup.default_sync = val;
+ break;
+ case OPT_VERBOSE:
+ driver_setup.verbose = val;
+ break;
+ case OPT_DEBUG:
+ driver_setup.debug = val;
+ break;
+ case OPT_BURST_MAX:
+ driver_setup.burst_max = val;
+ break;
+ case OPT_LED_PIN:
+ driver_setup.led_pin = val;
+ break;
+ case OPT_MAX_WIDE:
+ driver_setup.max_wide = val? 1:0;
+ break;
+ case OPT_SETTLE_DELAY:
+ driver_setup.settle_delay = val;
+ break;
+ case OPT_DIFF_SUPPORT:
+ driver_setup.diff_support = val;
+ break;
+ case OPT_IRQM:
+ driver_setup.irqm = val;
+ break;
+ case OPT_PCI_FIX_UP:
+ driver_setup.pci_fix_up = val;
+ break;
+ case OPT_BUS_CHECK:
+ driver_setup.bus_check = val;
+ break;
+ case OPT_OPTIMIZE:
+ driver_setup.optimize = val;
+ break;
+ case OPT_RECOVERY:
+ driver_setup.recovery = val;
+ break;
+ case OPT_USE_NVRAM:
+ driver_setup.use_nvram = val;
+ break;
+ case OPT_SAFE_SETUP:
+ memcpy(&driver_setup, &driver_safe_setup,
+ sizeof(driver_setup));
+ break;
+ case OPT_EXCLUDE:
+ if (xi < SCSI_NCR_MAX_EXCLUDES)
+ driver_setup.excludes[xi++] = val;
+ break;
+ case OPT_HOST_ID:
+ driver_setup.host_id = val;
+ break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+ case OPT_IARB:
+ driver_setup.iarb = val;
+ break;
+#endif
+ default:
+ printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+ break;
+ }
+
+ if ((cur = strchr(cur, ARG_SEP)) != NULL)
+ ++cur;
+ }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+ return 0;
+}
+
+/*===================================================================
+**
+** Get device queue depth from boot command line.
+**
+**===================================================================
+*/
+#define DEF_DEPTH (driver_setup.default_tags)
+#define ALL_TARGETS -2
+#define NO_TARGET -1
+#define ALL_LUNS -2
+#define NO_LUN -1
+
+static int device_queue_depth(int unit, int target, int lun)
+{
+ int c, h, t, u, v;
+ char *p = driver_setup.tag_ctrl;
+ char *ep;
+
+ h = -1;
+ t = NO_TARGET;
+ u = NO_LUN;
+ while ((c = *p++) != 0) {
+ v = simple_strtoul(p, &ep, 0);
+ switch(c) {
+ case '/':
+ ++h;
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ case 't':
+ if (t != target)
+ t = (target == v) ? v : NO_TARGET;
+ u = ALL_LUNS;
+ break;
+ case 'u':
+ if (u != lun)
+ u = (lun == v) ? v : NO_LUN;
+ break;
+ case 'q':
+ if (h == unit &&
+ (t == ALL_TARGETS || t == target) &&
+ (u == ALL_LUNS || u == lun))
+ return v;
+ break;
+ case '-':
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ default:
+ break;
+ }
+ p = ep;
+ }
+ return DEF_DEPTH;
+}
+
+/*===================================================================
+**
+** Print out information about driver configuration.
+**
+**===================================================================
+*/
+static void __init ncr_print_driver_setup(void)
+{
+#define YesNo(y) y ? 'y' : 'n'
+ printk (NAME53C8XX ": setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
+ "burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
+ YesNo(driver_setup.disconnection),
+ driver_setup.special_features,
+ driver_setup.ultra_scsi,
+ driver_setup.default_tags,
+ driver_setup.default_sync,
+ driver_setup.burst_max,
+ YesNo(driver_setup.max_wide),
+ driver_setup.diff_support,
+ YesNo(driver_setup.reverse_probe),
+ driver_setup.bus_check);
+
+ printk (NAME53C8XX ": setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
+ "led:%c,settle:%d,irqm:0x%x,nvram:0x%x,pcifix:0x%x\n",
+ YesNo(driver_setup.master_parity),
+ YesNo(driver_setup.scsi_parity),
+ YesNo(driver_setup.force_sync_nego),
+ driver_setup.verbose,
+ driver_setup.debug,
+ YesNo(driver_setup.led_pin),
+ driver_setup.settle_delay,
+ driver_setup.irqm,
+ driver_setup.use_nvram,
+ driver_setup.pci_fix_up);
+#undef YesNo
+}
+
+/*===================================================================
+**
+** SYM53C8XX devices description table.
+**
+**===================================================================
+*/
+
+static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+/*===================================================================
+**
+** Detect all NCR PQS/PDS boards and keep track of their bus nr.
+**
+** The NCR PQS or PDS card is constructed as a DEC bridge
+** behind which sit a proprietary NCR memory controller and
+** four or two 53c875s as separate devices. In its usual mode
+** of operation, the 875s are slaved to the memory controller
+** for all transfers. We can tell if an 875 is part of a
+** PQS/PDS or not since if it is, it will be on the same bus
+** as the memory controller. To operate with the Linux
+** driver, the memory controller is disabled and the 875s
+** freed to function independently. The only wrinkle is that
+** the preset SCSI ID (which may be zero) must be read in from
+** a special configuration space register of the 875.
+**
+**===================================================================
+*/
+#define SCSI_NCR_MAX_PQS_BUS 16
+static int pqs_bus[SCSI_NCR_MAX_PQS_BUS] __initdata = { 0 };
+
+static void __init ncr_detect_pqs_pds(void)
+{
+ short index;
+ pcidev_t dev = PCIDEV_NULL;
+
+ for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+ u_char tmp;
+
+ dev = pci_find_device(0x101a, 0x0009, dev);
+ if (dev == PCIDEV_NULL) {
+ pqs_bus[index] = -1;
+ break;
+ }
+ printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+ pci_read_config_byte(dev, 0x44, &tmp);
+ /* bit 1: allow individual 875 configuration */
+ tmp |= 0x2;
+ pci_write_config_byte(dev, 0x44, tmp);
+ pci_read_config_byte(dev, 0x45, &tmp);
+ /* bit 2: drive individual 875 interrupts to the bus */
+ tmp |= 0x4;
+ pci_write_config_byte(dev, 0x45, tmp);
+
+ pqs_bus[index] = PciBusNumber(dev);
+ }
+}
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+/*===================================================================
+**
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
+{
+ u_short vendor_id, device_id, command;
+ u_char cache_line_size, latency_timer;
+ u_char suggested_cache_line_size = 0;
+ u_char pci_fix_up = driver_setup.pci_fix_up;
+ u_char revision;
+ u_int irq;
+ u_long base, base_2, io_port;
+ int i;
+ ncr_chip *chip;
+
+ printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
+ PciBusNumber(pdev),
+ (int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+ (int) (PciDeviceFn(pdev) & 7));
+
+#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING
+ if (!pci_dma_supported(pdev, (dma_addr_t) (0xffffffffUL))) {
+ printk(KERN_WARNING NAME53C8XX
+ "32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+ return -1;
+ }
+#endif
+
+ /*
+ ** Read info from the PCI config space.
+ ** pci_read_config_xxx() functions are assumed to be used for
+ ** successfully detected PCI devices.
+ */
+ vendor_id = PciVendorId(pdev);
+ device_id = PciDeviceId(pdev);
+ irq = PciIrqLine(pdev);
+ i = 0;
+ i = pci_get_base_address(pdev, i, &io_port);
+ i = pci_get_base_address(pdev, i, &base);
+ (void) pci_get_base_address(pdev, i, &base_2);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer);
+
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ /*
+ ** Match the BUS number for PQS/PDS devices.
+ ** Read the SCSI ID from a special register mapped
+ ** into the configuration space of the individual
+ ** 875s. This register is set up by the PQS bios
+ */
+ for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
+ u_char tmp;
+ if (pqs_bus[i] == PciBusNumber(pdev)) {
+ pci_read_config_byte(pdev, 0x84, &tmp);
+ device->pqs_pds = 1;
+ device->host_id = tmp;
+ break;
+ }
+ }
+#endif /* SCSI_NCR_PQS_PDS_SUPPORT */
+
+ /*
+ ** If user excludes this chip, donnot initialize it.
+ */
+ for (i = 0 ; i < SCSI_NCR_MAX_EXCLUDES ; i++) {
+ if (driver_setup.excludes[i] ==
+ (io_port & PCI_BASE_ADDRESS_IO_MASK))
+ return -1;
+ }
+ /*
+ ** Check if the chip is supported
+ */
+ chip = 0;
+ for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+ if (device_id != ncr_chip_table[i].device_id)
+ continue;
+ if (revision > ncr_chip_table[i].revision_id)
+ continue;
+ chip = &device->chip;
+ memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+ chip->revision_id = revision;
+ break;
+ }
+
+ /*
+ ** Ignore Symbios chips controlled by SISL RAID controller.
+ ** This controller sets value 0x52414944 at RAM end - 16.
+ */
+#if defined(__i386__) && !defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED)
+ if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
+ unsigned int ram_size, ram_val;
+ u_long ram_ptr;
+
+ if (chip->features & FE_RAM8K)
+ ram_size = 8192;
+ else
+ ram_size = 4096;
+
+ ram_ptr = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
+ ram_size);
+ if (ram_ptr) {
+ ram_val = readl_raw(ram_ptr + ram_size - 16);
+ unmap_pci_mem(ram_ptr, ram_size);
+ if (ram_val == 0x52414944) {
+ printk(NAME53C8XX": not initializing, "
+ "driven by SISL RAID controller.\n");
+ return -1;
+ }
+ }
+ }
+#endif /* i386 and PCI MEMORY accessible */
+
+ if (!chip) {
+ printk(NAME53C8XX ": not initializing, device not supported\n");
+ return -1;
+ }
+
+#ifdef __powerpc__
+ /*
+ ** Fix-up for power/pc.
+ ** Should not be performed by the driver.
+ */
+ if ((command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+ != (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+ printk(NAME53C8XX ": setting%s%s...\n",
+ (command & PCI_COMMAND_IO) ? "" : " PCI_COMMAND_IO",
+ (command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
+ command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
+ if ( is_prep ) {
+ if (io_port >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating base (Wacky IBM)");
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_1, base);
+ }
+ if (base_2 >= 0x10000000) {
+ printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
+ base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
+ pci_write_config_dword(pdev,
+ PCI_BASE_ADDRESS_2, base_2);
+ }
+ }
+#endif
+#endif /* __powerpc__ */
+
+#ifdef __sparc__
+ /*
+ ** Fix-ups for sparc.
+ */
+ if (!cache_line_size)
+ suggested_cache_line_size = 16;
+
+ driver_setup.pci_fix_up |= 0x7;
+#endif /* __sparc__ */
+
+#if defined(__i386__) && !defined(MODULE)
+ if (!cache_line_size) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+ extern char x86;
+ switch(x86) {
+#else
+ switch(boot_cpu_data.x86) {
+#endif
+ case 4: suggested_cache_line_size = 4; break;
+ case 6:
+ case 5: suggested_cache_line_size = 8; break;
+ }
+ }
+#endif /* __i386__ */
+
+ /*
+ ** Check availability of IO space, memory space.
+ ** Enable master capability if not yet.
+ **
+ ** We shouldn't have to care about the IO region when
+ ** we are using MMIO. But calling check_region() from
+ ** both the ncr53c8xx and the sym53c8xx drivers prevents
+ ** from attaching devices from the both drivers.
+ ** If you have a better idea, let me know.
+ */
+/* #ifdef NCR_IOMAPPED */
+#if 1
+ if (!(command & PCI_COMMAND_IO)) {
+ printk(NAME53C8XX ": I/O base address (0x%lx) disabled.\n",
+ (long) io_port);
+ io_port = 0;
+ }
+#endif
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ printk(NAME53C8XX ": PCI_COMMAND_MEMORY not set.\n");
+ base = 0;
+ base_2 = 0;
+ }
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+/* #ifdef NCR_IOMAPPED */
+#if 1
+ if (io_port && check_region (io_port, 128)) {
+ printk(NAME53C8XX ": IO region 0x%lx[0..127] is in use\n",
+ (long) io_port);
+ io_port = 0;
+ }
+ if (!io_port)
+ return -1;
+#endif
+#ifndef NCR_IOMAPPED
+ if (!base) {
+ printk(NAME53C8XX ": MMIO base address disabled.\n");
+ return -1;
+ }
+#endif
+
+/* The ncr53c8xx driver never did set the PCI parity bit. */
+/* Since setting this bit is known to trigger spurious MDPE */
+/* errors on some 895 controllers when noise on power lines is */
+/* too high, I donnot want to change previous ncr53c8xx driver */
+/* behaviour on that point (the sym53c8xx driver set this bit). */
+#if 0
+ /*
+ ** Set MASTER capable and PARITY bit, if not yet.
+ */
+ if ((command & (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY))
+ != (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)) {
+ printk(NAME53C8XX ": setting%s%s...(fix-up)\n",
+ (command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
+ (command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
+ command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+#else
+ /*
+ ** Set MASTER capable if not yet.
+ */
+ if ((command & PCI_COMMAND_MASTER) != PCI_COMMAND_MASTER) {
+ printk(NAME53C8XX ": setting PCI_COMMAND_MASTER...(fix-up)\n");
+ command |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+#endif
+
+ /*
+ ** Fix some features according to driver setup.
+ */
+ if (!(driver_setup.special_features & 1))
+ chip->features &= ~FE_SPECIAL_SET;
+ else {
+ if (driver_setup.special_features & 2)
+ chip->features &= ~FE_WRIE;
+ if (driver_setup.special_features & 4)
+ chip->features &= ~FE_NOPM;
+ }
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
+ if (driver_setup.ultra_scsi < 1)
+ chip->features &= ~FE_ULTRA;
+ if (!driver_setup.max_wide)
+ chip->features &= ~FE_WIDE;
+
+ /*
+ ** Some features are required to be enabled in order to
+ ** work around some chip problems. :) ;)
+ ** (ITEM 12 of a DEL about the 896 I haven't yet).
+ ** We must ensure the chip will use WRITE AND INVALIDATE.
+ ** The revision number limit is for now arbitrary.
+ */
+ if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
+ chip->features |= (FE_WRIE | FE_CLSE);
+ pci_fix_up |= 3; /* Force appropriate PCI fix-up */
+ }
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+ /*
+ ** Try to fix up PCI config according to wished features.
+ */
+ if ((pci_fix_up & 1) && (chip->features & FE_CLSE) &&
+ !cache_line_size && suggested_cache_line_size) {
+ cache_line_size = suggested_cache_line_size;
+ pci_write_config_byte(pdev,
+ PCI_CACHE_LINE_SIZE, cache_line_size);
+ printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
+ cache_line_size);
+ }
+
+ if ((pci_fix_up & 2) && cache_line_size &&
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
+ command |= PCI_COMMAND_INVALIDATE;
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+ }
+
+ /*
+ ** Tune PCI LATENCY TIMER according to burst max length transfer.
+ ** (latency timer >= burst length + 6, we add 10 to be quite sure)
+ */
+
+ if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
+ u_char lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ printk(NAME53C8XX
+ ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+ (int) latency_timer, (int) lt);
+ latency_timer = lt;
+ pci_write_config_byte(pdev,
+ PCI_LATENCY_TIMER, latency_timer);
+ }
+ }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /*
+ ** Initialise ncr_device structure with items required by ncr_attach.
+ */
+ device->pdev = pdev;
+ device->slot.bus = PciBusNumber(pdev);
+ device->slot.device_fn = PciDeviceFn(pdev);
+ device->slot.base = base;
+ device->slot.base_2 = base_2;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attach_done = 0;
+
+ return 0;
+}
+
+/*===================================================================
+**
+** Detect all 53c8xx hosts and then attach them.
+**
+** If we are using NVRAM, once all hosts are detected, we need to
+** check any NVRAM for boot order in case detect and boot order
+** differ and attach them using the order in the NVRAM.
+**
+** If no NVRAM is found or data appears invalid attach boards in
+** the the order they are detected.
+**
+**===================================================================
+*/
+static int __init
+sym53c8xx__detect(Scsi_Host_Template *tpnt, u_short ncr_chip_ids[], int chips)
+{
+ pcidev_t pcidev;
+ int i, j, hosts, count;
+ int attach_count = 0;
+ ncr_device *devtbl, *devp;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram0, nvram, *nvp;
+#endif
+
+ /*
+ ** PCI is required.
+ */
+ if (!pci_present())
+ return 0;
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ ncr_debug = driver_setup.debug;
+#endif
+ if (initverbose >= 2)
+ ncr_print_driver_setup();
+
+ /*
+ ** Allocate the device table since we donnot want to
+ ** overflow the kernel stack.
+ ** 1 x 4K PAGE is enough for more than 40 devices for i386.
+ */
+ devtbl = m_calloc(PAGE_SIZE, "devtbl");
+ if (!devtbl)
+ return 0;
+
+ /*
+ ** Detect all NCR PQS/PDS memory controllers.
+ */
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ ncr_detect_pqs_pds();
+#endif
+
+ /*
+ ** Detect all 53c8xx hosts.
+ ** Save the first Symbios NVRAM content if any
+ ** for the boot order.
+ */
+ hosts = PAGE_SIZE / sizeof(*devtbl);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
+#endif
+ j = 0;
+ count = 0;
+ pcidev = PCIDEV_NULL;
+ while (1) {
+ char *msg = "";
+ if (count >= hosts)
+ break;
+ if (j >= chips)
+ break;
+ i = driver_setup.reverse_probe ? chips - 1 - j : j;
+ pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ pcidev);
+ if (pcidev == PCIDEV_NULL) {
+ ++j;
+ continue;
+ }
+ /* Some HW as the HP LH4 may report twice PCI devices */
+ for (i = 0; i < count ; i++) {
+ if (devtbl[i].slot.bus == PciBusNumber(pcidev) &&
+ devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
+ break;
+ }
+ if (i != count) /* Ignore this device if we already have it */
+ continue;
+ devp = &devtbl[count];
+ devp->host_id = driver_setup.host_id;
+ devp->attach_done = 0;
+ if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
+ continue;
+ }
+ ++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvp) {
+ ncr_get_nvram(devp, nvp);
+ switch(nvp->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ /*
+ * Switch to the other nvram buffer, so that
+ * nvram0 will contain the first Symbios
+ * format NVRAM content with boot order.
+ */
+ nvp = &nvram;
+ msg = "with Symbios NVRAM";
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ }
+ }
+#endif
+#ifdef SCSI_NCR_PQS_PDS_SUPPORT
+ if (devp->pqs_pds)
+ msg = "(NCR PQS/PDS)";
+#endif
+ printk(KERN_INFO NAME53C8XX ": 53c%s detected %s\n",
+ devp->chip.name, msg);
+ }
+
+ /*
+ ** If we have found a SYMBIOS NVRAM, use first the NVRAM boot
+ ** sequence as device boot order.
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvp || nvram0.type != SCSI_NCR_SYMBIOS_NVRAM)
+ goto next;
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram0.data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &devtbl[j];
+ if (h->device_fn != devp->slot.device_fn ||
+ h->bus_nr != devp->slot.bus ||
+ h->device_id != devp->chip.device_id)
+ continue;
+ if (devp->attach_done)
+ continue;
+ if (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) {
+ ncr_get_nvram(devp, nvp);
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ }
+ else if (!(driver_setup.use_nvram & 0x80))
+ printk(KERN_INFO NAME53C8XX
+ ": 53c%s state OFF thus not attached\n",
+ devp->chip.name);
+ else
+ continue;
+
+ devp->attach_done = 1;
+ break;
+ }
+ }
+next:
+#endif
+
+ /*
+ ** Rescan device list to make sure all boards attached.
+ ** Devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ devp = &devtbl[i];
+ if (!devp->attach_done) {
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_get_nvram(devp, nvp);
+#endif
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ }
+ }
+
+ m_free(devtbl, PAGE_SIZE, "devtbl");
+
+ return attach_count;
+}
diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in
index 1d74abb50..947e0294e 100644
--- a/drivers/sound/Config.in
+++ b/drivers/sound/Config.in
@@ -81,11 +81,14 @@ fi
dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
+ bool ' Verbose initialisation' CONFIG_SOUND_TRACEINIT
+ bool ' Persistent DMA buffers' CONFIG_SOUND_DMAP
+
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND
fi
-
dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS
+ dep_tristate ' Adlib Cards' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS
dep_tristate ' ACI mixer (miroPCM12)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS
dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS
dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 6a3576df0..5b4d35c8c 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -44,26 +44,27 @@ obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
-obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o
-obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
-obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
+obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o
+obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o
-obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o uart401.o mpu401.o
-obj-$(CONFIG_SOUND_MSS) += ad1848.o
-obj-$(CONFIG_SOUND_PAS) += pas2.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o
-obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
-obj-$(CONFIG_SOUND_MPU401) += mpu401.o
-obj-$(CONFIG_SOUND_UART6850) += uart6850.o
+obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o uart401.o mpu401.o
+obj-$(CONFIG_SOUND_MSS) += ad1848.o
+obj-$(CONFIG_SOUND_PAS) += pas2.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o
+obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
+obj-$(CONFIG_SOUND_MPU401) += mpu401.o
+obj-$(CONFIG_SOUND_UART6850) += uart6850.o
obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o
-obj-$(CONFIG_SOUND_YM3812) += adlib_card.o opl3.o
-obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
-obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
-obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
-obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
-obj-$(CONFIG_SOUND_AD1816) += ad1816.o
+obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o
+obj-$(CONFIG_SOUND_YM3812) += opl3.o
+obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
+obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
+obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
+obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
+obj-$(CONFIG_SOUND_AD1816) += ad1816.o
obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
index 52d4d79f8..99a4d8f44 100644
--- a/drivers/sound/dev_table.c
+++ b/drivers/sound/dev_table.c
@@ -17,8 +17,6 @@
#include "sound_config.h"
int softoss_dev = 0;
-int sound_started = 0;
-int sndtable_get_cardcount(void);
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
index db2b141d6..a5525a4bf 100644
--- a/drivers/sound/dev_table.h
+++ b/drivers/sound/dev_table.h
@@ -43,8 +43,6 @@
* NOTE! NOTE! NOTE! NOTE!
*/
-extern int sound_started;
-
struct driver_info
{
char *driver_id;
@@ -350,11 +348,14 @@ struct sound_timer_operations
#ifdef _DEV_TABLE_C_
-struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0;
-struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
-struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
-struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
-
+struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL};
+int num_audiodevs = 0;
+struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL};
+int num_mixers = 0;
+struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL};
+int num_synths = 0;
+struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL};
+int num_midis = 0;
#ifndef EXCLUDE_TIMERS
extern struct sound_timer_operations default_sound_timer;
struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
@@ -370,18 +371,17 @@ int num_sound_timers = 0;
#else
-extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
-extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
-extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths;
-extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
-extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; extern int num_sound_timers;
+extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+extern int num_audiodevs;
+extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+extern int num_mixers;
+extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+extern int num_synths;
+extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
+extern int num_midis;
+extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
+extern int num_sound_timers;
#endif /* _DEV_TABLE_C_ */
-void setup_cards(void);
-int sndtable_get_cardcount (void);
-void sound_chconf(int card_type, int ioaddr, int irq, int dma);
-int snd_find_driver(int type);
-void sound_unload_driver(int type);
-int sndtable_identify_card(char *name);
extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
int sndtable_probe (int unit, struct address_info *hw_config);
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
index c0edb593f..ce5e4f732 100644
--- a/drivers/sound/dmabuf.c
+++ b/drivers/sound/dmabuf.c
@@ -66,6 +66,14 @@ static int sound_alloc_dmap(struct dma_buffparms *dmap)
if (dma_buffsize < 4096)
dma_buffsize = 4096;
dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+
+ /*
+ * Now check for the Cyrix problem.
+ */
+
+ if(isa_dma_bridge_buggy==2)
+ dma_pagesize=32768;
+
dmap->raw_buf = NULL;
dmap->buffsize = dma_buffsize;
if (dmap->buffsize > dma_pagesize)
diff --git a/drivers/sound/miroaci.h b/drivers/sound/miroaci.h
index 9fea58a53..ee4e01d1f 100644
--- a/drivers/sound/miroaci.h
+++ b/drivers/sound/miroaci.h
@@ -1,4 +1,3 @@
-#include <linux/config.h>
extern int aci_implied_cmd(unsigned char opcode);
extern int aci_write_cmd(unsigned char opcode, unsigned char parameter);
extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2);
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
index 2757659ac..a329f0e72 100644
--- a/drivers/sound/mpu401.c
+++ b/drivers/sound/mpu401.c
@@ -1726,25 +1726,24 @@ int init_mpu401(void)
{
/* Can be loaded either for module use or to provide functions
to others */
- cfg.irq = irq;
- cfg.io_base = io;
-
- if (cfg.io_base != -1 && cfg.irq != -1) {
- printk(KERN_WARNING "mpu401: need io and irq !");
- return -ENODEV;
+ if (io != -1 && irq != -1) {
+ cfg.irq = irq;
+ cfg.io_base = io;
+ if (probe_mpu401(&cfg) == 0)
+ return -ENODEV;
+ attach_mpu401(&cfg);
}
- if (probe_mpu401(&cfg) == 0)
- return -ENODEV;
- attach_mpu401(&cfg);
-
SOUND_LOCK;
return 0;
}
void cleanup_mpu401(void)
{
- unload_mpu401(&cfg);
+ if (io != -1 && irq != -1) {
+ /* Check for use by, for example, sscape driver */
+ unload_mpu401(&cfg);
+ }
SOUND_LOCK_END;
}
diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c
index 40804417c..c471163ef 100644
--- a/drivers/sound/sb_card.c
+++ b/drivers/sound/sb_card.c
@@ -399,7 +399,6 @@ static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card
/* @X@0001:mpu
*/
-#ifdef CONFIG_MIDI
if((mpu_dev = isapnp_find_dev(bus,
ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL)))
{
@@ -413,7 +412,6 @@ static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card
}
else
printk(KERN_ERR "sb: DT0197H panic: mpu not found\n");
-#endif
/* @P@:Gameport
diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c
index 923b46e90..217bdb605 100644
--- a/drivers/sound/sound_core.c
+++ b/drivers/sound/sound_core.c
@@ -217,6 +217,16 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
static struct sound_unit *chains[16];
+/**
+ * register_sound_special
+ * @fops: File operations for the driver
+ * @unit: Unit number to allocate
+ *
+ * Allocate a special sound device by minor number from the sound
+ * subsystem. The allocated number is returned on succes. On failure
+ * a negative error code is returned.
+ */
+
int register_sound_special(struct file_operations *fops, int unit)
{
char *name;
@@ -240,8 +250,8 @@ int register_sound_special(struct file_operations *fops, int unit)
case 5:
name = "unknown5";
break;
- case 6:
- name = "sndstat";
+ case 6: /* Was once sndstat */
+ name = "unknown6";
break;
case 7:
name = "unknown7";
@@ -272,23 +282,43 @@ int register_sound_special(struct file_operations *fops, int unit)
break;
}
return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1,
- name, S_IRUGO | S_IWUGO);
+ name, S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_special);
+/**
+ * register_sound_mixer
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a mixer device. Unit is the number of the mixer requested.
+ * Pass -1 to request the next free mixer unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_mixer(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[0], fops, dev, 0, 128,
- "mixer", S_IRUGO | S_IWUGO);
+ "mixer", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_mixer);
+/**
+ * register_sound_midi
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a midi device. Unit is the number of the midi device requested.
+ * Pass -1 to request the next free midi unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
int register_sound_midi(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[2], fops, dev, 2, 130,
- "midi", S_IRUGO | S_IWUGO);
+ "midi", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_midi);
@@ -298,22 +328,55 @@ EXPORT_SYMBOL(register_sound_midi);
* in open - see below.
*/
+/**
+ * register_sound_dsp
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a DSP device. Unit is the number of the DSP requested.
+ * Pass -1 to request the next free DSP unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ *
+ * This function allocates both the audio and dsp device entries together
+ * and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
int register_sound_dsp(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[3], fops, dev, 3, 131,
- "dsp", S_IWUGO | S_IRUSR | S_IRGRP);
+ "dsp", S_IWUSR | S_IRUSR);
}
EXPORT_SYMBOL(register_sound_dsp);
+/**
+ * register_sound_synth
+ * @fops: File operations for the driver
+ * @dev: Unit number to allocate
+ *
+ * Allocate a synth device. Unit is the number of the synth device requested.
+ * Pass -1 to request the next free synth unit. On success the allocated
+ * number is returned, on failure a negative error code is returned.
+ */
+
+
int register_sound_synth(struct file_operations *fops, int dev)
{
return sound_insert_unit(&chains[9], fops, dev, 9, 137,
- "synth", S_IRUGO | S_IWUGO);
+ "synth", S_IRUSR | S_IWUSR);
}
EXPORT_SYMBOL(register_sound_synth);
+/**
+ * unregister_sound_special
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_special.
+ * The unit passed is the return value from the register function.
+ */
+
+
void unregister_sound_special(int unit)
{
sound_remove_unit(&chains[unit&15], unit);
@@ -321,6 +384,14 @@ void unregister_sound_special(int unit)
EXPORT_SYMBOL(unregister_sound_special);
+/**
+ * unregister_sound_mixer
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_mixer.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_mixer(int unit)
{
sound_remove_unit(&chains[0], unit);
@@ -328,6 +399,14 @@ void unregister_sound_mixer(int unit)
EXPORT_SYMBOL(unregister_sound_mixer);
+/**
+ * unregister_sound_midi
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_midi.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_midi(int unit)
{
return sound_remove_unit(&chains[2], unit);
@@ -335,13 +414,32 @@ void unregister_sound_midi(int unit)
EXPORT_SYMBOL(unregister_sound_midi);
+/**
+ * unregister_sound_dsp
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_dsp.
+ * The unit passed is the return value from the register function.
+ *
+ * Both of the allocated units are released together automatically.
+ */
+
void unregister_sound_dsp(int unit)
{
return sound_remove_unit(&chains[3], unit);
}
+
EXPORT_SYMBOL(unregister_sound_dsp);
+/**
+ * unregister_sound_synth
+ * @unit: Unit number to allocate
+ *
+ * Release a sound device that was allocated with register_sound_synth.
+ * The unit passed is the return value from the register function.
+ */
+
void unregister_sound_synth(int unit)
{
return sound_remove_unit(&chains[9], unit);
diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c
index c446e98e0..393e6a780 100644
--- a/drivers/sound/sound_firmware.c
+++ b/drivers/sound/sound_firmware.c
@@ -47,6 +47,24 @@ static int do_mod_firmware_load(const char *fn, char **fp)
return (int) l;
}
+/**
+ * mod_firmware_load - load sound driver firmware
+ * @fn: filename
+ * @fp: return for the buffer.
+ *
+ * Load the firmware for a sound module (up to 128K) into a buffer.
+ * The buffer is returned in *fp. It is allocated with vmalloc so is
+ * virtually linear and not DMAable. The caller should free it with
+ * vfree when finished.
+ *
+ * The length of the buffer is returned on a successful load, the
+ * value zero on a failure.
+ *
+ * Caution: This API is not recommended. Firmware should be loaded via
+ * an ioctl call and a setup application. This function may disappear
+ * in future.
+ */
+
int mod_firmware_load(const char *fn, char **fp)
{
int r;
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 00f8b6e7b..f3b008f0c 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -74,7 +74,12 @@ caddr_t sound_mem_blocks[1024];
int sound_nblocks = 0;
/* Persistent DMA buffers */
-int sound_dmap_flag = 0;
+#ifdef CONFIG_SOUND_DMAP
+int sound_dmap_flag = 1;
+#else
+int sound_dmap_flag = 0;
+#endif
+
static int soundcard_configured = 0;
static char dma_alloc_map[MAX_DMA_CHANNELS] = {0};
@@ -92,8 +97,6 @@ unsigned long seq_time = 0; /* Time for /dev/sequencer */
static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
static int num_mixer_volumes = 0;
-int traceinit = 0;
-
int *load_mixer_volumes(char *name, int *levels, int present)
{
int i, n;
@@ -637,11 +640,6 @@ soundcard_init(void)
soundcard_configured = 1;
-#if defined(CONFIG_LOWLEVEL_SOUND) && !defined(MODULE)
- sound_preinit_lowlevel_drivers();
- sound_init_lowlevel_drivers();
-#endif
-
audio_init_devices();
soundcard_register_devfs(1); /* register after we know # of devices */
@@ -663,38 +661,15 @@ static int sound[20] = {
static int dmabuf = 0;
static int dmabug = 0;
-MODULE_PARM(traceinit, "i");
MODULE_PARM(dmabuf, "i");
MODULE_PARM(dmabug, "i");
int init_module(void)
{
int err;
-#if FIXED_FOR_2_4_0
- int ints[21];
- int i;
-#endif
-#ifdef HAS_BRIDGE_BUGGY_FUNC
if(dmabug)
isa_dma_bridge_buggy = dmabug;
-#else
- if(dmabug)
- printk(KERN_ERR "sound: rebuild with PCI_QUIRKS enabled to configure this.\n");
-#endif
-
-#if FIXED_FOR_2_4_0
- /*
- * "sound=" command line handling by Harald Milz.
- */
- i = 0;
- while (i < 20 && sound[i])
- ints[i + 1] = sound[i++];
- ints[0] = i;
-
- if (i)
- sound_setup("sound=", ints);
-#endif
err = create_special_devices();
if (err)
@@ -730,13 +705,6 @@ void cleanup_module(void)
sound_stop_timer();
-#ifdef CONFIG_LOWLEVEL_SOUND
- {
- extern void sound_unload_lowlevel_drivers(void);
-
- sound_unload_lowlevel_drivers();
- }
-#endif
sequencer_unload();
for (i = 0; i < MAX_DMA_CHANNELS; i++)
@@ -855,8 +823,9 @@ void sound_stop_timer(void)
void conf_printf(char *name, struct address_info *hw_config)
{
- if (!traceinit)
- return;
+#ifndef CONFIG_SOUND_TRACEINIT
+ return;
+#else
printk("<%s> at 0x%03x", name, hw_config->io_base);
if (hw_config->irq)
@@ -869,13 +838,14 @@ void conf_printf(char *name, struct address_info *hw_config)
printk(",%d", hw_config->dma2);
}
printk("\n");
+#endif
}
void conf_printf2(char *name, int base, int irq, int dma, int dma2)
{
- if (!traceinit)
- return;
-
+#ifndef CONFIG_SOUND_TRACEINIT
+ return;
+#else
printk("<%s> at 0x%03x", name, base);
if (irq)
@@ -888,6 +858,7 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2)
printk(",%d", dma2);
}
printk("\n");
+#endif
}
/*
diff --git a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c
index d1631defd..75d1a8977 100644
--- a/drivers/sound/waveartist.c
+++ b/drivers/sound/waveartist.c
@@ -1771,6 +1771,18 @@ MODULE_PARM(dma2, "i"); /* DMA2 */
static int __init init_waveartist(void)
{
+ if (!io && machine_is_netwinder()) {
+ /*
+ * The NetWinder WaveArtist is at a fixed address.
+ * If the user does not supply an address, use the
+ * well-known parameters.
+ */
+ io = 0x250;
+ irq = 12;
+ dma = 3;
+ dma2 = 7;
+ }
+
cfg.io_base = io;
cfg.irq = irq;
cfg.dma = dma;
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 9665598b9..6a5af7943 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -508,7 +508,7 @@ static void ixj_timeout(unsigned long ptr)
j->flags.cringing = 0;
ixj_ring_off(board);
} else {
- if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) {
+ if (jiffies - j->ring_cadence_jif >= (hertz/2)) {
j->ring_cadence_t--;
if (j->ring_cadence_t == -1)
j->ring_cadence_t = 15;
@@ -3799,6 +3799,32 @@ int ixj_ioctl(struct inode *inode, struct file *file_p,
case PHONE_CPT_STOP:
ixj_cpt_stop(board);
break;
+ case PHONE_QUERY_CODEC:
+ {
+ struct phone_codec_data pd;
+ int val;
+ int proto_size[] = {
+ -1,
+ 12, 10, 16, 9, 8, 48, 5,
+ 40, 40, 80, 40, 40
+ };
+ if(copy_from_user(&pd, (void *)arg, sizeof(pd)))
+ return -EFAULT;
+ if(pd.type<1 || pd.type>12)
+ return -EPROTONOSUPPORT;
+ if(pd.type<G729)
+ val=proto_size[pd.type];
+ else switch(j->baseframe.low)
+ {
+ case 0xA0:val=2*proto_size[pd.type];break;
+ case 0x50:val=proto_size[pd.type];break;
+ default:val=proto_size[pd.type]*3;break;
+ }
+ pd.buf_min=pd.buf_max=pd.buf_opt=val;
+ if(copy_to_user((void *)arg, &pd, sizeof(pd)))
+ return -EFAULT;
+ return 0;
+ }
case IXJCTL_DSP_IDLE:
idle(board);
break;
@@ -3839,6 +3865,7 @@ int ixj_ioctl(struct inode *inode, struct file *file_p,
ixj_daa_cr4(board, arg | 0x02);
break;
case IXJCTL_PSTN_LINETEST:
+ case PHONE_PSTN_LINETEST:
retval = ixj_linetest(board);
break;
case IXJCTL_CID:
diff --git a/drivers/telephony/phonedev.c b/drivers/telephony/phonedev.c
index 21f850d9b..0f3195163 100644
--- a/drivers/telephony/phonedev.c
+++ b/drivers/telephony/phonedev.c
@@ -10,7 +10,8 @@
*
* Author: Alan Cox, <alan@redhat.com>
*
- * Fixes:
+ * Fixes: Mar 01 2000 Thomas Sparr, <thomas.l.sparr@telia.com>
+ * phone_register_device now works with unit!=PHONE_UNIT_ANY
*/
#include <linux/config.h>
@@ -84,7 +85,7 @@ int phone_register_device(struct phone_device *p, int unit)
if (unit != PHONE_UNIT_ANY) {
base = unit;
- end = unit;
+ end = unit + 1; /* enter the loop at least one time */
}
for (i = base; i < end; i++) {
if (phone_device[i] == NULL) {
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 5f1b61d07..fbb7562dc 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -9,9 +9,6 @@ if [ ! "$CONFIG_USB" = "n" ]; then
comment 'USB Controllers'
dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
- if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
- fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -51,6 +48,7 @@ comment 'USB Devices'
dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB
dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB
dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB
+ dep_tristate ' D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB
comment 'USB HID'
dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7c5b02f21..35bef0e45 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -4,9 +4,10 @@
# Subdirs.
-SUB_DIRS := serial
+SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
+MOD_IN_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) serial
# The target object and module list name.
@@ -37,6 +38,18 @@ obj-m :=
obj-n :=
obj- :=
+# Object files in subdirectories
+
+ifeq ($(CONFIG_USB_SERIAL),y)
+ SUB_DIRS += serial
+ obj-y += serial/serial.o
+else
+ ifeq ($(CONFIG_USB_SERIAL),m)
+ MOD_SUB_DIRS += serial
+ endif
+endif
+
+
# Each configuration option enables a list of files.
obj-$(CONFIG_USB) += usbcore.o
@@ -68,6 +81,7 @@ obj-$(CONFIG_USB_PLUSB) += plusb.o
obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RIO500) += rio500.o
+obj-$(CONFIG_USB_DSBR) += dsbr100.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c
new file mode 100644
index 000000000..8b81e06c0
--- /dev/null
+++ b/drivers/usb/dsbr100.c
@@ -0,0 +1,353 @@
+/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
+ into both the USB and an analog audio input, so this thing
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well. So, scanning will only find
+ stereo stations. Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning. My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ History:
+
+ Version 0.21:
+ Markus Demleitner <msdemlei@tucana.harvard.edu>:
+ Minor cleanup, warnings if something goes wrong, lame attempt
+ to adhere to Documentation/CodingStyle
+
+ Version 0.2:
+ Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+ Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+
+#if CONFIG_MODVERSIONS==1
+#define MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+#define TB_LEN 16
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
+ void *arg);
+static int usb_dsbr100_open(struct video_device *dev, int flags);
+static void usb_dsbr100_close(struct video_device *dev);
+
+
+typedef struct
+{ struct urb readurb,writeurb;
+ struct usb_device *dev;
+ char transfer_buffer[TB_LEN];
+ int curfreq;
+ int stereo;
+ int ifnum;
+} usb_dsbr100;
+
+
+static struct video_device usb_dsbr100_radio=
+{
+ "D-Link DSB R-100 USB radio",
+ VID_TYPE_TUNER,
+ VID_HARDWARE_AZTECH,
+ usb_dsbr100_open,
+ usb_dsbr100_close,
+ NULL, /* Can't read (no capture ability) */
+ NULL, /* Can't write */
+ NULL, /* No poll */
+ usb_dsbr100_ioctl,
+ NULL,
+ NULL
+};
+
+static int users = 0;
+
+static struct usb_driver usb_dsbr100_driver = {
+ name: "dsbr100",
+ probe: usb_dsbr100_probe,
+ disconnect: usb_dsbr100_disconnect,
+ driver_list: {NULL,NULL},
+ fops: NULL,
+ minor: 0
+};
+
+
+static int dsbr100_start(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_stop(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+
+static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
+{
+ freq = (freq*80)/16+856;
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff,
+ radio->transfer_buffer, 8, 300)<0
+ || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+ radio->stereo = -1;
+ return -1;
+ }
+ radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+ return (radio->transfer_buffer)[0];
+}
+
+static void dsbr100_getstat(usb_dsbr100 *radio)
+{
+ if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+ 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+ radio->stereo = -1;
+ else
+ radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ usb_dsbr100 *radio;
+
+ if (dev->descriptor.idVendor!=DSB100_VENDOR ||
+ dev->descriptor.idProduct!=DSB100_PRODUCT)
+ return NULL;
+ if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL)))
+ return NULL;
+ usb_dsbr100_radio.priv = radio;
+ radio->dev = dev;
+ radio->ifnum = ifnum;
+ radio->curfreq = 1454;
+ return (void*)radio;
+}
+
+static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr)
+{
+ usb_dsbr100 *radio=ptr;
+
+ if (users)
+ return;
+ kfree(radio);
+ usb_dsbr100_radio.priv = NULL;
+}
+
+static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
+ void *arg)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (!radio)
+ return -EINVAL;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP: {
+ struct video_capability v;
+ v.type=VID_TYPE_TUNER;
+ v.channels=1;
+ v.audios=1;
+ /* No we don't do pictures */
+ v.maxwidth=0;
+ v.maxheight=0;
+ v.minwidth=0;
+ v.minheight=0;
+ strcpy(v.name, "D-Link R-100 USB Radio");
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGTUNER: {
+ struct video_tuner v;
+ dsbr100_getstat(radio);
+ if(copy_from_user(&v, arg,sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v.rangelow=(87*16000);
+ v.rangehigh=(108*16000);
+ /*v.flags=VIDEO_TUNER_LOW;*/
+ v.mode=VIDEO_MODE_AUTO;
+ v.signal=radio->stereo;
+ v.flags|=VIDEO_TUNER_STEREO_ON;
+ strcpy(v.name, "FM");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER: {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ if (radio->curfreq==-1)
+ return -EINVAL;
+ if(copy_to_user(arg, &(radio->curfreq),
+ sizeof(radio->curfreq)))
+ return -EFAULT;
+ return 0;
+
+ case VIDIOCSFREQ:
+ if(copy_from_user(&(radio->curfreq), arg,
+ sizeof(radio->curfreq)))
+ return -EFAULT;
+ if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+ warn("set frequency failed");
+ return 0;
+
+ case VIDIOCGAUDIO: {
+ struct video_audio v;
+ memset(&v,0, sizeof(v));
+ v.flags|=VIDEO_AUDIO_MUTABLE;
+ v.mode=VIDEO_SOUND_STEREO;
+ v.volume=1;
+ v.step=1;
+ strcpy(v.name, "Radio");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSAUDIO: {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+
+ if(v.flags&VIDEO_AUDIO_MUTE) {
+ if (dsbr100_stop(radio)==-1)
+ warn("radio did not respond properly");
+ }
+ else
+ if (dsbr100_start(radio)==-1)
+ warn("radio did not respond properly");
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+static int usb_dsbr100_open(struct video_device *dev, int flags)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (! radio) {
+ warn("radio not initialised");
+ return -EAGAIN;
+ }
+ if(users)
+ {
+ warn("radio in use");
+ return -EBUSY;
+ }
+ users++;
+ MOD_INC_USE_COUNT;
+ if (dsbr100_start(radio)<0)
+ warn("radio did not start up properly");
+ dsbr100_setfreq(radio,radio->curfreq);
+ return 0;
+}
+
+static void usb_dsbr100_close(struct video_device *dev)
+{
+ usb_dsbr100 *radio=dev->priv;
+
+ if (!radio)
+ return;
+ users--;
+ dsbr100_stop(radio);
+ MOD_DEC_USE_COUNT;
+}
+
+int __init dsbr100_init(void)
+{
+ usb_dsbr100_radio.priv = NULL;
+ usb_register(&usb_dsbr100_driver);
+ if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {
+ warn("couldn't register video device");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int __init init_module(void)
+{
+ return dsbr100_init();
+}
+
+void cleanup_module(void)
+{
+ usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+ if (radio)
+ dsbr100_stop(radio);
+ video_unregister_device(&usb_dsbr100_radio);
+ usb_deregister(&usb_dsbr100_driver);
+}
+
+/*
+vi: ts=8
+Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
diff --git a/drivers/usb/inode.c b/drivers/usb/inode.c
index 92a008d2f..c30f2eaff 100644
--- a/drivers/usb/inode.c
+++ b/drivers/usb/inode.c
@@ -29,6 +29,7 @@
/*****************************************************************************/
#define __NO_VERSION__
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sched.h>
diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c
index 4e9b4f65e..d1f5048e3 100644
--- a/drivers/usb/ov511.c
+++ b/drivers/usb/ov511.c
@@ -2,20 +2,17 @@
* OmniVision OV511 Camera-to-USB Bridge Driver
* Copyright (c) 1999/2000 Mark W. McClelland
* Many improvements by Bret Wallach
- *
+ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
+ * Snapshot code by Kevin Moore
+ *
* Based on the Linux CPiA driver.
*
* Released under GPL v.2 license.
*
- * Important keywords in comments:
- * CAMERA SPECIFIC - Camera specific code; may not work with other cameras.
- * DEBUG - Debugging code.
- * FIXME - Something that is broken or needs improvement.
- *
- * Version: 1.07
+ * Version: 1.09
*
* Please see the file: linux/Documentation/usb/ov511.txt
- * and the website at: http://people.delphi.com/mmcclelland/linux/
+ * and the website at: http://alpha.dyndns.org/ov511
* for more info.
*/
@@ -39,15 +36,6 @@
/* Handle mangled (versioned) external symbols */
-#include <linux/config.h> /* retrieve the CONFIG_* macros */
-#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
-# define MODVERSIONS /* force it on */
-#endif
-
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -58,6 +46,7 @@
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/usb.h>
@@ -67,8 +56,6 @@
#define OV511_I2C_RETRIES 3
-#define OV7610_AUTO_ADJUST 1
-
/* Video Size 640 x 480 x 3 bytes for RGB */
#define MAX_FRAME_SIZE (640 * 480 * 3)
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
@@ -77,6 +64,33 @@
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
+// PARAMETER VARIABLES:
+static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
+
+/* 0=no debug messages
+ * 1=init/detection/unload and other significant messages,
+ * 2=some warning messages
+ * 3=config/control function calls
+ * 4=most function calls and data parsing messages
+ * 5=highly repetitive mesgs
+ * NOTE: This should be changed to 0, 1, or 2 for production kernels
+ */
+static int debug = 3;
+
+/* Fix vertical misalignment of red and blue at 640x480 */
+static int fix_rgb_offset = 0;
+
+/* Snapshot mode enabled flag */
+static int snapshot = 0;
+
+MODULE_PARM(autoadjust, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM(snapshot, "i");
+
+MODULE_AUTHOR("Mark McClelland (and others)");
+MODULE_DESCRIPTION("OV511 USB Camera Driver");
+
char kernel_version[] = UTS_RELEASE;
/*******************************/
@@ -206,10 +220,8 @@ int ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char val
USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, &value, 1, HZ);
-#if 0
- PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-#endif
-
+ PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+
return rc;
}
@@ -225,9 +237,7 @@ int ov511_reg_read(struct usb_device *dev, unsigned char reg)
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, buffer, 1, HZ);
-#if 0
- PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]);
-#endif
+ PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
if(rc < 0)
return rc;
@@ -239,9 +249,8 @@ int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char val
{
int rc, retries;
-#if 0
- PDEBUG("i2c write: 0x%02X:0x%02X", reg, value);
-#endif
+ PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+
/* Three byte write cycle */
for(retries = OV511_I2C_RETRIES;;) {
/* Select camera register */
@@ -321,9 +330,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
}
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
-#if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
-#endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
/* This is needed to make ov511_i2c_write() work */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
@@ -355,9 +363,8 @@ int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
if (rc < 0) return rc;
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
- #if 0
- PDEBUG("i2c read: 0x%02X:0x%02X", reg, value);
- #endif
+
+ PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
return (value);
}
@@ -391,15 +398,14 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_i2c_read(dev, i);
-#if 0
- PDEBUG("OV7610[0x%X] = 0x%X", i, rc);
-#endif
+
+ PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_i2c_regs( struct usb_device *dev)
{
- PDEBUG("I2C REGS");
+ PDEBUG(3, "I2C REGS");
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
@@ -409,27 +415,27 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn)
int rc;
for(i=reg1; i<=regn; i++) {
rc = ov511_reg_read(dev, i);
- PDEBUG("OV511[0x%X] = 0x%X", i, rc);
+ PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
}
}
static void ov511_dump_regs( struct usb_device *dev)
{
- PDEBUG("CAMERA INTERFACE REGS");
+ PDEBUG(1, "CAMERA INTERFACE REGS");
ov511_dump_reg_range(dev, 0x10, 0x1f);
- PDEBUG("DRAM INTERFACE REGS");
+ PDEBUG(1, "DRAM INTERFACE REGS");
ov511_dump_reg_range(dev, 0x20, 0x23);
- PDEBUG("ISO FIFO REGS");
+ PDEBUG(1, "ISO FIFO REGS");
ov511_dump_reg_range(dev, 0x30, 0x31);
- PDEBUG("PIO REGS");
+ PDEBUG(1, "PIO REGS");
ov511_dump_reg_range(dev, 0x38, 0x39);
ov511_dump_reg_range(dev, 0x3e, 0x3e);
- PDEBUG("I2C REGS");
+ PDEBUG(1, "I2C REGS");
ov511_dump_reg_range(dev, 0x40, 0x49);
- PDEBUG("SYSTEM CONTROL REGS");
+ PDEBUG(1, "SYSTEM CONTROL REGS");
ov511_dump_reg_range(dev, 0x50, 0x53);
ov511_dump_reg_range(dev, 0x5e, 0x5f);
- PDEBUG("OmniCE REGS");
+ PDEBUG(1, "OmniCE REGS");
ov511_dump_reg_range(dev, 0x70, 0x79);
ov511_dump_reg_range(dev, 0x80, 0x9f);
ov511_dump_reg_range(dev, 0xa0, 0xbf);
@@ -441,7 +447,7 @@ int ov511_reset(struct usb_device *dev, unsigned char reset_type)
{
int rc;
- PDEBUG("Reset: type=0x%X", reset_type);
+ PDEBUG(3, "Reset: type=0x%X", reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
if (rc < 0)
err("reset: command failed");
@@ -457,9 +463,7 @@ int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
{
int alt, multiplier, rc;
-#if 0
- PDEBUG("set packet size: %d", size);
-#endif
+ PDEBUG(3, "set packet size: %d", size);
switch (size) {
case 992:
@@ -576,7 +580,7 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511,
p->hue = 0x8000;
p->whiteness = 105 << 8;
- p->depth = 24;
+ p->depth = 3; /* Don't know if this is right */
p->palette = VIDEO_PALETTE_RGB24;
/* Restart the camera */
@@ -594,10 +598,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
int rc = 0;
struct usb_device *dev = ov511->dev;
-#if 0
- PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)",
+ PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
width, height, mode, sub_flag);
-#endif
// ov511_set_packet_size(ov511, 0);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
@@ -606,13 +608,19 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
}
if (mode == VIDEO_PALETTE_GREY) {
- ov511_reg_write(dev, 0x16, 0);
- ov511_i2c_write(dev, 0xe, 0x44);
+ ov511_reg_write(dev, 0x16, 0x00);
+ ov511_i2c_write(dev, 0x0e, 0x44);
ov511_i2c_write(dev, 0x13, 0x21);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x00);
+ ov511_reg_write(dev, 0x1f, 0x01);
} else {
- ov511_reg_write(dev, 0x16, 1);
- ov511_i2c_write(dev, 0xe, 0x4);
- ov511_i2c_write(dev, 0x13, 0x1);
+ ov511_reg_write(dev, 0x16, 0x01);
+ ov511_i2c_write(dev, 0x0e, 0x04);
+ ov511_i2c_write(dev, 0x13, 0x01);
+ /* For snapshot */
+ ov511_reg_write(dev, 0x1e, 0x01);
+ ov511_reg_write(dev, 0x1f, 0x03);
}
if (width == 640 && height == 480) {
@@ -626,13 +634,26 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1);
ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1);
ov511_i2c_write(dev, 0x11, 0x01);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
} else {
ov511_i2c_write(ov511->dev, 0x17, 0x38);
ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2));
ov511_i2c_write(ov511->dev, 0x19, 0x5);
- ov511_i2c_write(ov511->dev, 0x1c, + (480>>1));
+ ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1));
ov511_reg_write(dev, 0x12, 0x4f);
ov511_reg_write(dev, 0x13, 0x3d);
+
+ /* Snapshot additions */
+ ov511_reg_write(ov511->dev, 0x1a, 0x4f);
+ ov511_reg_write(ov511->dev, 0x1b, 0x3d);
+ ov511_reg_write(ov511->dev, 0x1c, 0x00);
+ ov511_reg_write(ov511->dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 4); /* check */
} else {
@@ -642,6 +663,8 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(dev, 0x14, 0x00);
ov511_reg_write(dev, 0x15, 0x00);
+
+ /* FIXME?? Shouldn't below be true only for YUV420? */
ov511_reg_write(dev, 0x18, 0x03);
ov511_i2c_write(dev, 0x12, 0x24);
@@ -654,6 +677,12 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_reg_write(dev, 0x15, 0x00);
ov511_reg_write(dev, 0x18, 0x03);
+ /* Snapshot additions */
+ ov511_reg_write(dev, 0x1a, 0x27);
+ ov511_reg_write(dev, 0x1b, 0x1f);
+ ov511_reg_write(dev, 0x1c, 0x00);
+ ov511_reg_write(dev, 0x1d, 0x00);
+
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 1); /* check */
} else {
@@ -671,7 +700,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
// ov511_set_packet_size(ov511, 993);
if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
- PDEBUG("reset: command failed");
+ err("reset: command failed");
return -EIO;
}
@@ -683,52 +712,74 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
Turn a YUV4:2:0 block into an RGB block
+Video4Linux seems to use the blue, green, red channel
+order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+
+Color space conversion coefficients taken from the excellent
+http://www.inforamp.net/~poynton/ColorFAQ.html
+In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+Y values are given for all 4 pixels, but the U (Pb)
+and V (Pr) are assumed constant over the 2x2 block.
+
+To avoid floating point arithmetic, the color conversion
+coefficients are scaled into 16.16 fixed-point integers.
+
*************************************************************/
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-static inline void ov511_move_420_block(int y00, int y01, int y10, int y11,
- int u, int v, int w,
- unsigned char * pOut)
+// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
+#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
+static inline void ov511_move_420_block(
+ int yTL, int yTR, int yBL, int yBR,
+ int u, int v,
+ int rowPixels, unsigned char * rgb)
{
- int r = 68911 * v;
- int g = -16915 * u + -35101 * v;
- int b = 87097 * u;
- y00 *= 49152;
- y01 *= 49152;
- y10 *= 49152;
- y11 *= 49152;
- *(pOut+w*3) = LIMIT(r + y10);
- *pOut++ = LIMIT(r + y00);
- *(pOut+w*3) = LIMIT(g + y10);
- *pOut++ = LIMIT(g + y00);
- *(pOut+w*3) = LIMIT(b + y10);
- *pOut++ = LIMIT(b + y00);
- *(pOut+w*3) = LIMIT(r + y11);
- *pOut++ = LIMIT(r + y01);
- *(pOut+w*3) = LIMIT(g + y11);
- *pOut++ = LIMIT(g + y01);
- *(pOut+w*3) = LIMIT(b + y11);
- *pOut++ = LIMIT(b + y01);
+ const double brightness=1.0;//0->black; 1->full scale
+ const double saturation=1.0;//0->greyscale; 1->full color
+ const double fixScale=brightness*256*256;
+ const int rvScale=(int)(1.402*saturation*fixScale);
+ const int guScale=(int)(-0.344136*saturation*fixScale);
+ const int gvScale=(int)(-0.714136*saturation*fixScale);
+ const int buScale=(int)(1.772*saturation*fixScale);
+ const int yScale=(int)(fixScale);
+
+ int r = rvScale * v;
+ int g = guScale * u + gvScale * v;
+ int b = buScale * u;
+ yTL *= yScale; yTR *= yScale;
+ yBL *= yScale; yBR *= yScale;
+
+ //Write out top two pixels
+ rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL);
+ rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR);
+ rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels
+ rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL);
+ rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR);
}
+
/***************************************************************
For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The
-first 64 bytes of each segment are V, the next 64 are U. The V and
-U are arranged as follows:
+first 64 bytes of each segment are U, the next 64 are V. The U and
+V are arranged as follows:
0 1 ... 7
8 9 ... 15
...
56 57 ... 63
-The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as
-follows:
+U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+
+The next 256 bytes are full resolution Y data and represent 4
+squares of 8x8 pixels as follows:
0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
8 9 ... 15 72 73 ... 79 200 201 ... 207
... ... ...
56 57 ... 63 120 121 127 248 249 ... 255
+Note that the U and V data in one segment represents a 16 x 16 pixel
+area, but the Y data represents a 32 x 8 pixel area.
+
If OV511_DUMPPIX is defined, _parse_data just dumps the
incoming segments, verbatim, in order, into the frame.
When used with vidcat -f ppm -s 640x480 this puts the data
@@ -780,8 +831,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
int y01 = *(pOut+3);
int y10 = *(pOut+iWidth*3);
int y11 = *(pOut+iWidth*3+3);
- int u = *(pIn+64) - 128;
- int v = *pIn++ - 128;
+ int v = *(pIn+64) - 128;
+ int u = *pIn++ - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut);
pOut += 6;
}
@@ -810,8 +861,8 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
int y00 = *pIn++;
int y11 = *(pIn+8);
int y01 = *pIn++;
- int u = *pOut1 - 128;
- int v = *(pOut1+1) - 128;
+ int v = *pOut1 - 128;
+ int u = *(pOut1+1) - 128;
ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1);
pOut1 += 6;
}
@@ -868,6 +919,42 @@ static void ov511_parse_data_grey(unsigned char * pIn0,
}
}
+
+/**************************************************************
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ *************************************************************/
+static void fixFrameRGBoffset(struct ov511_frame *frame)
+{
+ int x,y;
+ int rowBytes=frame->width*3,w=frame->width;
+ unsigned char *rgb=frame->data;
+ const int shift=1;//Distance to shift pixels by, vertically
+
+ if (frame->width<400)
+ return;//Don't bother with little images
+
+ //Shift red channel up
+ for (y=shift;y<frame->height;y++)
+ {
+ int lp=(y-shift)*rowBytes;//Previous line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up
+ }
+ //Shift blue channel down
+ for (y=frame->height-shift-1;y>=0;y--)
+ {
+ int ln=(y+shift)*rowBytes;//Next line offset
+ int lc=y*rowBytes;//Current line offset
+ for (x=0;x<w;x++)
+ rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down
+ }
+}
+
+
static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
{
unsigned char *cdata;
@@ -887,7 +974,7 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
if (!n || ov511->curframe == -1) continue;
if (st)
- PDEBUG("data error: [%d] len=%d, status=%d", i, n, st);
+ PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
frame = &ov511->frame[ov511->curframe];
@@ -899,14 +986,15 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
struct timeval *ts;
ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
do_gettimeofday(ts);
-#if 0
- PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
+
+ PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d",
ov511->curframe, (int)(cdata[992]),
(int)(cdata[9]), (int)(cdata[10]));
-#endif
if (frame->scanstate == STATE_LINES) {
int iFrameNext;
+ if (fix_rgb_offset)
+ fixFrameRGBoffset(frame);
frame->grabstate = FRAME_DONE;
if (waitqueue_active(&frame->wq)) {
frame->grabstate = FRAME_DONE;
@@ -919,10 +1007,10 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
ov511->curframe = iFrameNext;
ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
} else {
-#if 0
- PDEBUG("Frame not ready? state = %d",
+
+ PDEBUG(4, "Frame not ready? state = %d",
ov511->frame[iFrameNext].grabstate);
-#endif
+
ov511->curframe = -1;
}
}
@@ -932,11 +1020,18 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 &&
(cdata[8] & 8)) {
-#if 0
- PDEBUG("ov511: Found Frame Start!, framenum = %d",
+
+ PDEBUG(4, "ov511: Found Frame Start!, framenum = %d",
ov511->curframe);
-#endif
- frame->scanstate = STATE_LINES;
+
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (cdata[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "ov511_move_data: snapshot detected");
+ }
+
+ frame->scanstate = STATE_LINES;
frame->segment = 0;
}
@@ -1014,11 +1109,11 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
}
}
-#if 0
- PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n",
+
+ PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n",
aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-#endif
+
return totlen;
}
@@ -1032,7 +1127,7 @@ static void ov511_isoc_irq(struct urb *urb)
return;
if (!ov511->streaming) {
- PDEBUG("hmmm... not streaming, but got interrupt\n");
+ PDEBUG(2, "hmmm... not streaming, but got interrupt");
return;
}
@@ -1166,6 +1261,7 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
frame->grabstate = FRAME_GRABBING;
frame->scanstate = STATE_SCANNING;
frame->scanlength = 0; /* accumulated in ov511_parse_data() */
+ frame->snapshot = 0;
ov511->curframe = framenum;
@@ -1192,7 +1288,7 @@ static int ov511_open(struct video_device *dev, int flags)
int err = -EBUSY;
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_open");
+ PDEBUG(4, "ov511_open");
down(&ov511->lock);
if (ov511->user)
@@ -1212,8 +1308,8 @@ static int ov511_open(struct video_device *dev, int flags)
ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE;
ov511->sub_flag = 0;
- PDEBUG("frame [0] @ %p", ov511->frame[0].data);
- PDEBUG("frame [1] @ %p", ov511->frame[1].data);
+ PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data);
+ PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data);
ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!ov511->sbuf[0].data)
@@ -1222,8 +1318,8 @@ static int ov511_open(struct video_device *dev, int flags)
if (!ov511->sbuf[1].data)
goto open_err_on1;
- PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data);
- PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data);
+ PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data);
+ PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data);
err = ov511_init_isoc(ov511);
if (err)
@@ -1254,7 +1350,7 @@ static void ov511_close(struct video_device *dev)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
- PDEBUG("ov511_close");
+ PDEBUG(4, "ov511_close");
down(&ov511->lock);
ov511->user--;
@@ -1289,9 +1385,8 @@ static long ov511_write(struct video_device *dev, const char *buf, unsigned long
static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
-#if 0
- PDEBUG("IOCtl: 0x%X", cmd);
-#endif
+
+ PDEBUG(4, "IOCtl: 0x%X", cmd);
if (!ov511->dev)
return -EIO;
@@ -1464,11 +1559,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
-#if 0
- PDEBUG("MCAPTURE");
- PDEBUG("frame: %d, size: %dx%d, format: %d",
+ PDEBUG(4, "MCAPTURE");
+ PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
-#endif
if (vm.format != VIDEO_PALETTE_RGB24 &&
vm.format != VIDEO_PALETTE_GREY)
@@ -1516,10 +1609,9 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
if (copy_from_user((void *)&frame, arg, sizeof(int)))
return -EFAULT;
-#if 0
- PDEBUG("syncing to frame %d, grabstate = %d", frame,
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
ov511->frame[frame].grabstate);
-#endif
+
switch (ov511->frame[frame].grabstate) {
case FRAME_UNUSED:
return -EINVAL;
@@ -1552,7 +1644,17 @@ redo:
}
ov511->frame[frame].grabstate = FRAME_UNUSED;
-
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov511->snap_enabled) &&
+ (ov511->frame[frame].snapshot)) {
+ ov511->frame[frame].snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
return 0;
}
case VIDIOCGFBUF:
@@ -1594,7 +1696,7 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
int frmx = -1;
volatile struct ov511_frame *frame;
- PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock);
+ PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock);
if (!dev || !buf)
return -EFAULT;
@@ -1644,18 +1746,37 @@ restart:
goto restart;
}
- PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+
+ /* Repeat until we get a snapshot frame */
+ if (ov511->snap_enabled && !frame->snapshot) {
+ frame->bytes_read = 0;
+ if (ov511_new_frame(ov511, frmx))
+ err("ov511_read: ov511_new_frame error");
+ goto restart;
+ }
+
+ /* Clear the snapshot */
+ if (ov511->snap_enabled && frame->snapshot) {
+ frame->snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
+ PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
frame->bytes_read, frame->scanlength);
/* copy bytes to user space; we allow for partials reads */
- if ((count + frame->bytes_read) > frame->scanlength)
- count = frame->scanlength - frame->bytes_read;
+// if ((count + frame->bytes_read) > frame->scanlength)
+// count = frame->scanlength - frame->bytes_read;
+ /* FIXME - count hardwired to be one frame... */
+ count = frame->width * frame->height * frame->depth;
if (copy_to_user(buf, frame->data + frame->bytes_read, count))
return -EFAULT;
frame->bytes_read += count;
- PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld",
+ PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld",
count, frame->bytes_read);
if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
@@ -1679,7 +1800,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s
if (!ov511->dev)
return -EIO;
- PDEBUG("mmap: %ld (%lX) bytes", size, size);
+ PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
return -EINVAL;
@@ -1702,24 +1823,22 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s
}
static struct video_device ov511_template = {
- "OV511 USB Camera",
- VID_TYPE_CAPTURE,
- VID_HARDWARE_OV511,
- ov511_open,
- ov511_close,
- ov511_read,
- ov511_write,
- NULL,
- ov511_ioctl,
- ov511_mmap,
- ov511_init_done,
- NULL,
- 0,
- 0
+ name: "OV511 USB Camera",
+ type: VID_TYPE_CAPTURE,
+ hardware: VID_HARDWARE_OV511,
+ open: ov511_open,
+ close: ov511_close,
+ read: ov511_read,
+ write: ov511_write,
+ ioctl: ov511_ioctl,
+ mmap: ov511_mmap,
+ initialize: ov511_init_done,
};
static int ov7610_configure(struct usb_device *dev)
{
+ int tries;
+
if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
OV7610_I2C_WRITE_ID) < 0)
return -1;
@@ -1731,6 +1850,7 @@ static int ov7610_configure(struct usb_device *dev)
if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
return -1;
+ /* Reset the 7610 and wait a bit for it to initialize */
if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
schedule_timeout (1 + 150 * HZ / 1000);
@@ -1738,8 +1858,14 @@ static int ov7610_configure(struct usb_device *dev)
if(ov511_i2c_read(dev, 0x00) < 0)
return -1;
- if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
- (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) {
+ tries = 5;
+ while((tries > 0) &&
+ ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
+ (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
+ --tries;
+ }
+
+ if (tries == 0) {
err("Failed to read OV7610 ID. You might not have an OV7610,");
err("or it may be not responding. Report this to");
err("mmcclelland@delphi.com");
@@ -1786,12 +1912,11 @@ static int ov511_configure(struct usb_ov511 *ov511)
{OV511_I2C_BUS, 0x16, 0x06},
{OV511_I2C_BUS, 0x28, 0x24}, /* 24 */
{OV511_I2C_BUS, 0x2b, 0xac},
- {OV511_I2C_BUS, 0x5, 0x00},
- {OV511_I2C_BUS, 0x6, 0x00},
-#if 0
-#endif
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x06, 0x00},
+
{OV511_I2C_BUS, 0x12, 0x00},
- {OV511_I2C_BUS, 0x13, 0x00},
+// {OV511_I2C_BUS, 0x13, 0x00},
{OV511_I2C_BUS, 0x38, 0x81},
{OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
{OV511_I2C_BUS, 0x05, 0x00},
@@ -1813,7 +1938,7 @@ static int ov511_configure(struct usb_ov511 *ov511)
{OV511_I2C_BUS, 0x33, 0x20},
{OV511_I2C_BUS, 0x34, 0x48},
{OV511_I2C_BUS, 0x12, 0x24},
- {OV511_I2C_BUS, 0x13, 0x01},
+// {OV511_I2C_BUS, 0x13, 0x01},
{OV511_I2C_BUS, 0x11, 0x01},
{OV511_I2C_BUS, 0x0c, 0x24},
{OV511_I2C_BUS, 0x0d, 0x24},
@@ -1853,7 +1978,8 @@ static int ov511_configure(struct usb_ov511 *ov511)
}
ov511->compress = 0;
-
+ ov511->snap_enabled = snapshot;
+
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */
ov511->frame[0].width = DEFAULT_WIDTH;
@@ -1864,10 +1990,16 @@ static int ov511_configure(struct usb_ov511 *ov511)
ov511->frame[1].bytes_read = 0;
/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
- if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc;
+ if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error;
if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
- VIDEO_PALETTE_RGB24, 0)) < 0) return rc;
+ VIDEO_PALETTE_RGB24, 0)) < 0) goto error;
+ if (autoadjust) {
+ if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
+ }
+ else {
+ if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error;
+ }
return 0;
@@ -1887,7 +2019,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
struct usb_ov511 *ov511;
int rc;
- PDEBUG("probing for device...");
+ PDEBUG(1, "probing for device...");
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
@@ -1933,19 +2065,25 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
case 3:
printk("ov511: Camera is a D-Link DSB-C300\n");
break;
+ case 4:
+ printk("ov511: Camera is a generic OV511/OV7610\n");
+ break;
case 5:
printk("ov511: Camera is a Puretek PT-6007\n");
break;
case 21:
printk("ov511: Camera is a Creative Labs WebCam 3\n");
break;
+ case 36:
+ printk("ov511: Camera is a Koala-Cam\n");
+ break;
case 100:
printk("ov511: Camera is a Lifeview RoboCam\n");
break;
case 102:
printk("ov511: Camera is a AverMedia InterCam Elite\n");
break;
- case 112:
+ case 112: /* The OmniVision OV7110 evaluation kit uses this too */
printk("ov511: Camera is a MediaForte MV300\n");
break;
default:
@@ -2025,31 +2163,21 @@ static struct usb_driver ov511_driver = {
{ NULL, NULL }
};
-int usb_ov511_init(void)
+static int __init usb_ov511_init(void)
{
- PDEBUG("usb_ov511_init()");
-
- EXPORT_NO_SYMBOLS;
-
- return usb_register(&ov511_driver);
-}
+ if (usb_register(&ov511_driver) < 0)
+ return -1;
-void usb_ov511_cleanup(void)
-{
- usb_deregister(&ov511_driver);
-}
+ info("ov511 driver registered");
-#ifdef MODULE
-int init_module(void)
-{
- return usb_ov511_init();
+ return 0;
}
-void cleanup_module(void)
+static void __exit usb_ov511_exit(void)
{
- usb_ov511_cleanup();
-
- PDEBUG("Module unloaded");
+ usb_deregister(&ov511_driver);
+ info("ov511 driver deregistered");
}
-#endif
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h
index ba54fb47a..6a4a332bf 100644
--- a/drivers/usb/ov511.h
+++ b/drivers/usb/ov511.h
@@ -6,9 +6,10 @@
#define OV511_DEBUG /* Turn on debug messages */
#ifdef OV511_DEBUG
-# define PDEBUG(fmt, args...) printk("ov511: " fmt "\n" , ## args)
+# define PDEBUG(level, fmt, args...) \
+if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#else
-# define PDEBUG(fmt, args...) do {} while(0)
+# define PDEBUG(level, fmt, args...) do {} while(0)
#endif
/* Camera interface register numbers */
@@ -227,6 +228,8 @@ struct ov511_frame {
long bytes_read; /* amount of scanlength that has been read from *data */
wait_queue_head_t wq; /* Processes waiting */
+
+ int snapshot; /* True if frame was a snapshot */
};
#define OV511_NUMFRAMES 2
@@ -269,6 +272,8 @@ struct usb_ov511 {
int scratchlen;
wait_queue_head_t wq; /* Processes waiting */
+
+ int snap_enabled; /* Snapshot mode enabled */
};
#endif
diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c
index d3761bf52..2d5de6f73 100644
--- a/drivers/usb/pegasus.c
+++ b/drivers/usb/pegasus.c
@@ -1,578 +1,474 @@
/*
-**
** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
**
-** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
-**
+** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**
** Distribute under GPL version 2 or later.
*/
-
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/usb.h>
-
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/usb.h>
-#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
-#error You can not compile this driver on this kernel with this C options!
-#endif
-
-
-#define ADMTEK_VENDOR_ID 0x07a6
-#define ADMTEK_HPNA_PEGASUS 0x0986
-
-#define HPNA_MTU 1500
-#define MAX_MTU 1536
-#define TX_TIMEOUT (HZ*5)
-#define SOMETHING (jiffies + TX_TIMEOUT)
+static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
-static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+#define ADMTEK_VENDOR_ID 0x07a6
+#define ADMTEK_DEVICE_ID_PEGASUS 0x0986
+#define PEGASUS_MTU 1500
+#define PEGASUS_MAX_MTU 1536
+#define PEGASUS_TX_TIMEOUT (HZ*5)
+#define ALIGN(x) x __attribute__((aligned(16)))
-typedef struct usb_hpna
-{
- struct usb_device *usb_dev;
- struct net_device *net_dev;
- int present;
- int active;
- void *irq_handler;
- struct list_head list;
+struct pegasus {
+ struct usb_device *usb;
+ struct net_device *net;
struct net_device_stats stats;
- spinlock_t hpna_lock;
- struct timer_list timer;
-
- unsigned int rx_pipe;
- unsigned char * rx_buff;
- urb_t rx_urb;
-
- unsigned int tx_pipe;
- unsigned char * tx_buff;
- urb_t tx_urb;
- struct sk_buff * tx_skbuff;
-
- __u8 intr_ival;
- unsigned int intr_pipe;
- unsigned char intr_buff[8];
- urb_t intr_urb;
-} usb_hpna_t;
-
-
-usb_hpna_t usb_dev_hpna;
-static int loopback = 0;
-int multicast_filter_limit = 32;
-static LIST_HEAD(hpna_list);
+ spinlock_t pegasus_lock;
+ struct urb rx_urb, tx_urb, intr_urb;
+ unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
+ unsigned char ALIGN(intr_buff[8]);
+};
+static int loopback = 0;
+static int multicast_filter_limit = 32;
MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
-MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
-
-/*** vendor specific commands ***/
-static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
- indx, data, size, HZ);
-}
-
-
-static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
-{
- __u8 data = value;
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
- data, indx, &data, 1, HZ);
-}
+#define pegasus_get_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
+#define pegasus_set_registers(dev, indx, size, data)\
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
+#define pegasus_set_register(dev, indx, value) \
+ { __u8 data = value; \
+ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
-static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
{
- return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
- indx, data, size, HZ);
-}
-
-
-static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
-{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = 0;
- data[2] = 0;
- data[3] = 0x40 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x25, 4, data );
- if ( data[3] & 0x80 ) {
- *regdata = *(__u16 *)(data+1);
- return 0;
+ int i;
+ __u8 data[4] = { 1, 0, 0, 0x40 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x26, 3, data);
+ if (data[2] & 0x80) {
+ *regdata = *(__u16 *)(data);
+ return 0;
}
udelay(100);
}
+
warn("read_phy_word() failed");
- return 1;
+ return 1;
}
-
-static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
{
- int i;
- __u8 data[4];
-
- data[0] = 1;
- data[1] = regdata;
- data[2] = regdata >> 8;
- data[3] = 0x20 + index;
- hpna_set_registers( dev, 0x25, 4, data );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 0x28, 1, data );
- if ( data[0] & 0x80 ) {
- return 0;
- }
+ int i;
+ __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
+
+ pegasus_set_registers(dev, 0x25, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x28, 1, data);
+ if (data[0] & 0x80)
+ return 0;
udelay(100);
}
+
warn("write_phy_word() failed");
- return 1;
+ return 1;
}
-
-int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
{
- int i;
- __u8 data[4];
-
- data[0] = index;
- data[1] = data[2] = 0;
- data[3] = 0x02;
- hpna_set_registers(dev, 0x20, 4, data);
- for ( i=0; i<100; i++ ) {
- hpna_get_registers(dev, 0x23, 1, data);
- if ( data[0] & 4 ) {
- hpna_get_registers(dev, 0x21, 2, data);
+ int i;
+ __u8 data[4] = { index, 0, 0, 0x02 };
+
+ pegasus_set_registers(dev, 0x20, 4, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 0x23, 1, data);
+ if (data[0] & 4) {
+ pegasus_get_registers(dev, 0x21, 2, data);
*retdata = *(__u16 *)data;
- return 0;
+ return 0;
}
}
+
warn("read_srom_word() failed");
- return 1;
+ return 1;
}
-/*** end ***/
-
-
-
-int get_node_id( struct usb_device *dev, __u8 *id )
+static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
{
- int i;
-
- for ( i=0; i<3; i++ ) {
- if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
- return 1;
- }
- return 0;
+ int i;
+ for (i = 0; i < 3; i++)
+ if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+ return 1;
+ return 0;
}
-
-static int reset_mac( struct usb_device *dev )
+static int pegasus_reset_mac(struct usb_device *dev)
{
- __u8 data = 0x8;
- int i;
-
- hpna_set_register( dev, 1, 0x08 );
- for ( i=0; i<100; i++ ) {
- hpna_get_registers( dev, 1, 1, &data);
- if ( !(data & 0x08) ) {
- if ( loopback & 1 )
- return 0;
- else if ( loopback & 2 ) {
- write_phy_word( dev, 0, 0x4000 );
- /*return 0;*/
- }
- hpna_set_register( dev, 0x7e, 0x24 );
- hpna_set_register( dev, 0x7e, 0x27 );
- return 0;
+ __u8 data = 0x8;
+ int i;
+
+ pegasus_set_register(dev, 1, data);
+ for (i = 0; i < 100; i++) {
+ pegasus_get_registers(dev, 1, 1, &data);
+ if (~data & 0x08) {
+ if (loopback & 1)
+ return 0;
+ if (loopback & 2)
+ pegasus_write_phy_word(dev, 0, 0x4000);
+ pegasus_set_register(dev, 0x7e, 0x24);
+ pegasus_set_register(dev, 0x7e, 0x27);
+ return 0;
}
}
+
return 1;
}
-
-int start_net( struct net_device *dev, struct usb_device *usb_dev )
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
{
- __u16 partmedia, temp;
- __u8 node_id[6];
- __u8 data[4];
-
- if ( get_node_id(usb_dev, node_id) )
- return 1;
- hpna_set_registers(usb_dev, 0x10, 6, node_id);
+ __u16 partmedia, temp;
+ __u8 node_id[6];
+ __u8 data[4];
+
+ if (pegasus_get_node_id(usb, node_id))
+ return 1;
+
+ pegasus_set_registers(usb, 0x10, 6, node_id);
memcpy(dev->dev_addr, node_id, 6);
- if ( read_phy_word(usb_dev, 1, &temp) )
- return 2;
- if ( !(temp & 4) ) {
- if ( loopback )
- goto ok;
+ if (pegasus_read_phy_word(usb, 1, &temp))
+ return 2;
+
+ if ((~temp & 4) && !loopback) {
err("link NOT established - %x", temp);
- return 3;
- }
-ok:
- if ( read_phy_word(usb_dev, 5, &partmedia) )
- return 4;
- temp = partmedia;
- partmedia &= 0x1f;
- if ( partmedia != 1 ) {
- err("party FAIL %x", temp);
- return 5;
+ return 3;
}
- partmedia = temp;
- if ( partmedia & 0x100 )
- data[1] = 0x30;
- else {
- if ( partmedia & 0x80 )
- data[1] = 0x10;
- else
- data[1] = 0;
+
+ if (pegasus_read_phy_word(usb, 5, &partmedia))
+ return 4;
+
+ if ((partmedia & 0x1f) != 1) {
+ err("party FAIL %x", partmedia);
+ return 5;
}
-
- data[0] = 0xc9;
- data[2] = (loopback & 1) ? 0x08 : 0x00;
-
- hpna_set_registers(usb_dev, 0, 3, data);
-
- return 0;
-}
+ data[0] = 0xc9;
+ data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+ data[2] = (loopback & 1) ? 0x08 : 0x00;
-static void hpna_read_irq( purb_t urb )
-{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
- int count = urb->actual_length, res;
- int rx_status = *(int *)(hpna->rx_buff + count - 4);
+ pegasus_set_registers(usb, 0, 3, data);
+ return 0;
+}
- if ( urb->status ) {
- info( "%s: RX status %d\n", net_dev->name, urb->status );
+static void pegasus_read_bulk(struct urb *urb)
+{
+ struct pegasus *pegasus = urb->context;
+ struct net_device *net = pegasus->net;
+ int count = urb->actual_length, res;
+ int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+ struct sk_buff *skb;
+ __u16 pkt_len;
+
+ if (urb->status) {
+ info("%s: RX status %d", net->name, urb->status);
goto goon;
}
- if ( !count )
+ if (!count)
goto goon;
-/* if ( rx_status & 0x00010000 )
+#if 0
+ if (rx_status & 0x00010000)
+ goto goon;
+#endif
+ if (rx_status & 0x000e0000) {
+
+ dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+ pegasus->stats.rx_errors++;
+ if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
+ if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
+ if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+
goto goon;
-*/
- if ( rx_status & 0x000e0000 ) {
- dbg("%s: error receiving packet %x",
- net_dev->name, rx_status & 0xe0000);
- hpna->stats.rx_errors++;
- if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
- if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
- if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
- } else {
- struct sk_buff *skb;
- __u16 pkt_len = (rx_status & 0xfff) - 8;
-
-
- if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
- skb->dev = net_dev;
- skb_reserve(skb, 2);
- eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
- skb_put(skb, pkt_len);
- } else
- goto goon;
- skb->protocol = eth_type_trans(skb, net_dev);
- netif_rx(skb);
- hpna->stats.rx_packets++;
- hpna->stats.rx_bytes += pkt_len;
}
+
+ pkt_len = (rx_status & 0xfff) - 8;
+
+ if(!(skb = dev_alloc_skb(pkt_len+2)))
+ goto goon;
+
+ skb->dev = net;
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+ skb_put(skb, pkt_len);
+
+ skb->protocol = eth_type_trans(skb, net);
+ netif_rx(skb);
+ pegasus->stats.rx_packets++;
+ pegasus->stats.rx_bytes += pkt_len;
+
goon:
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(prb)failed rx_urb %d", res);
}
-
-static void hpna_irq( urb_t *urb)
+static void pegasus_irq(urb_t *urb)
{
- if( urb->status ) {
+ if(urb->status) {
__u8 *d = urb->transfer_buffer;
printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
- d[0], d[1], d[2], d[3], d[4], d[5] );
+ d[0], d[1], d[2], d[3], d[4], d[5]);
}
}
-
-static void hpna_write_irq( purb_t urb )
+static void pegasus_write_bulk(struct urb *urb)
{
- struct net_device *net_dev = urb->context;
- usb_hpna_t *hpna = net_dev->priv;
+ struct pegasus *pegasus = urb->context;
+ spin_lock(&pegasus->pegasus_lock);
- spin_lock( &hpna->hpna_lock );
-
- if ( urb->status )
- info("%s: TX status %d\n", net_dev->name, urb->status);
- netif_wake_queue( net_dev );
+ if (urb->status)
+ info("%s: TX status %d", pegasus->net->name, urb->status);
+ netif_wake_queue(pegasus->net);
- spin_unlock( &hpna->hpna_lock );
+ spin_unlock(&pegasus->pegasus_lock);
}
-
-static void tx_timeout( struct net_device *dev )
+static void pegasus_tx_timeout(struct net_device *net)
{
- usb_hpna_t *hpna = dev->priv;
+ struct pegasus *pegasus = net->priv;
- warn( "%s: Tx timed out. Reseting...", dev->name );
- hpna->stats.tx_errors++;
- dev->trans_start = jiffies;
- netif_wake_queue( dev );
-}
+ warn("%s: Tx timed out. Reseting...", net->name);
+ pegasus->stats.tx_errors++;
+ net->trans_start = jiffies;
+ netif_wake_queue(net);
+}
-static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
- int res;
+ struct pegasus *pegasus = net->priv;
+ int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+ int res;
- spin_lock( &hpna->hpna_lock );
+ spin_lock(&pegasus->pegasus_lock);
- netif_stop_queue( net_dev );
- ((__u16 *)hpna->tx_buff)[0] = skb->len;
- memcpy(hpna->tx_buff+2, skb->data, skb->len);
- (&hpna->tx_urb)->transfer_buffer_length = count;
- if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+ netif_stop_queue(net);
+
+ ((__u16 *)pegasus->tx_buff)[0] = skb->len;
+ memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+ (&pegasus->tx_urb)->transfer_buffer_length = count;
+
+ if ((res = usb_submit_urb(&pegasus->tx_urb))) {
warn("failed tx_urb %d", res);
- hpna->stats.tx_errors++;
- netif_start_queue( net_dev );
+ pegasus->stats.tx_errors++;
+ netif_start_queue(net);
} else {
- hpna->stats.tx_packets++;
- hpna->stats.tx_bytes += skb->len;
- net_dev->trans_start = jiffies;
+ pegasus->stats.tx_packets++;
+ pegasus->stats.tx_bytes += skb->len;
+ net->trans_start = jiffies;
}
- dev_kfree_skb( skb );
- spin_unlock( &hpna->hpna_lock );
- return 0;
-}
+ dev_kfree_skb(skb);
-static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
-{
- return &((usb_hpna_t *)dev->priv)->stats;
+ spin_unlock(&pegasus->pegasus_lock);
+
+ return 0;
}
-static int hpna_open( struct net_device *net_dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
{
- usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
- int res;
+ return &((struct pegasus *)dev->priv)->stats;
+}
- if ( hpna->active )
- return -EBUSY;
- else
- hpna->active = 1;
+static int pegasus_open(struct net_device *net)
+{
+ struct pegasus *pegasus = (struct pegasus *)net->priv;
+ int res;
- if ( start_net(net_dev, hpna->usb_dev) ) {
- err("can't start_net()");
- return -EIO;
+ if ((res = pegasus_start_net(net, pegasus->usb))) {
+ err("can't start_net() - %d", res);
+ return -EIO;
}
- if ( (res = usb_submit_urb( &hpna->rx_urb )) )
- warn("failed rx_urb %d", res);
+ if ((res = usb_submit_urb(&pegasus->rx_urb)))
+ warn("(open)failed rx_urb %d", res);
-/* usb_submit_urb( &hpna->intr_urb );*/
- netif_start_queue( net_dev );
+/* usb_submit_urb(&pegasus->intr_urb);*/
+ netif_start_queue(net);
MOD_INC_USE_COUNT;
return 0;
}
-
-static int hpna_close( struct net_device *net_dev )
+static int pegasus_close(struct net_device *net)
{
- usb_hpna_t *hpna = net_dev->priv;
-
-
- netif_stop_queue( net_dev );
+ struct pegasus *pegasus = net->priv;
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( hpna->intr_urb );*/
+ netif_stop_queue(net);
- hpna->active = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb); */
MOD_DEC_USE_COUNT;
return 0;
}
-
-static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
- __u16 *data = (__u16 *)&rq->ifr_data;
- usb_hpna_t *hpna = dev->priv;
+ __u16 *data = (__u16 *)&rq->ifr_data;
+ struct pegasus *pegasus = net->priv;
- switch( cmd ) {
- case SIOCDEVPRIVATE:
+ switch(cmd) {
+ case SIOCDEVPRIVATE:
data[0] = 1;
case SIOCDEVPRIVATE+1:
- read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
- return 0;
+ pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+ return 0;
case SIOCDEVPRIVATE+2:
- if ( !capable(CAP_NET_ADMIN) )
- return -EPERM;
- write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
- return 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+ return 0;
default:
- return -EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
}
-
-static void set_rx_mode( struct net_device *net_dev )
+static void pegasus_set_rx_mode(struct net_device *net)
{
- usb_hpna_t *hpna=net_dev->priv;
+ struct pegasus *pegasus = net->priv;
- netif_stop_queue( net_dev );
-
- if ( net_dev->flags & IFF_PROMISC ) {
- info("%s: Promiscuous mode enabled", net_dev->name);
- hpna_set_register( hpna->usb_dev, 2, 0x04 );
- } else if ((net_dev->mc_count > multicast_filter_limit) ||
- (net_dev->flags & IFF_ALLMULTI)) {
- hpna_set_register(hpna->usb_dev, 0, 0xfa);
- hpna_set_register(hpna->usb_dev, 2, 0);
+ netif_stop_queue(net);
+
+ if (net->flags & IFF_PROMISC) {
+ info("%s: Promiscuous mode enabled", net->name);
+ pegasus_set_register(pegasus->usb, 2, 0x04);
+ } else if ((net->mc_count > multicast_filter_limit) ||
+ (net->flags & IFF_ALLMULTI)) {
+ pegasus_set_register(pegasus->usb, 0, 0xfa);
+ pegasus_set_register(pegasus->usb, 2, 0);
} else {
- dbg("%s: set Rx mode", net_dev->name);
+ dbg("%s: set Rx mode", net->name);
}
- netif_wake_queue( net_dev );
+ netif_wake_queue(net);
}
-
-static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct net_device *net_dev;
- usb_hpna_t *hpna = &usb_dev_hpna;
-
+ struct net_device *net;
+ struct pegasus *pegasus;
-
- if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
- dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
- return NULL;
+ if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+ dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+ return NULL;
}
- printk("USB HPNA Pegasus found\n");
-
- if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
return NULL;
}
- hpna->usb_dev = dev;
-
- hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
- hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
- hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
-
- if ( reset_mac(dev) ) {
- err("can't reset MAC");
+ if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+ err("out of memory allocating device structure");
+ return NULL;
}
+ memset(pegasus, 0, sizeof(struct pegasus));
- hpna->present = 1;
-
- if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- err("not enough mem for out buff");
- return NULL;
- }
- if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
- kfree_s(hpna->rx_buff, MAX_MTU);
- err("not enough mem for out buff");
- return NULL;
+ if (pegasus_reset_mac(dev)) {
+ err("can't reset MAC");
+ kfree(pegasus);
+ return NULL;
}
-
- net_dev = init_etherdev( 0, 0 );
- hpna->net_dev = net_dev;
- net_dev->priv = hpna;
- net_dev->open = hpna_open;
- net_dev->stop = hpna_close;
- net_dev->watchdog_timeo = TX_TIMEOUT;
- net_dev->tx_timeout = tx_timeout;
- net_dev->do_ioctl = hpna_ioctl;
- net_dev->hard_start_xmit = hpna_start_xmit;
- net_dev->set_multicast_list = set_rx_mode;
- net_dev->get_stats = hpna_netdev_stats;
- net_dev->mtu = HPNA_MTU;
- hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
-
- FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe,
- hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
- FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe,
- hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
- FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
- hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
-/* list_add( &hpna->list, &hpna_list );*/
-
- return net_dev;
+ net = init_etherdev(0, 0);
+ net->priv = pegasus;
+ net->open = pegasus_open;
+ net->stop = pegasus_close;
+ net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+ net->tx_timeout = pegasus_tx_timeout;
+ net->do_ioctl = pegasus_ioctl;
+ net->hard_start_xmit = pegasus_start_xmit;
+ net->set_multicast_list = pegasus_set_rx_mode;
+ net->get_stats = pegasus_netdev_stats;
+ net->mtu = PEGASUS_MTU;
+
+ pegasus->usb = dev;
+ pegasus->net = net;
+ pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+
+ FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+ pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk,
+ pegasus);
+ FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+ pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
+ pegasus);
+ FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 0),
+ pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
+
+
+ printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+
+ return pegasus;
}
-
-static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
{
- struct net_device *net_dev = ptr;
- struct usb_hpna *hpna = net_dev->priv;
+ struct pegasus *pegasus = ptr;
+ if (!pegasus) {
+ warn("unregistering non-existant device");
+ return;
+ }
- if ( net_dev->flags & IFF_UP )
- dev_close(net_dev);
-
- unregister_netdev( net_dev );
+ if (pegasus->net->flags & IFF_UP)
+ dev_close(pegasus->net);
- if ( !hpna ) /* should never happen */
- return;
-
- usb_unlink_urb( &hpna->rx_urb );
- usb_unlink_urb( &hpna->tx_urb );
-/* usb_unlink_urb( &hpna->intr_urb );*/
- kfree_s(hpna->rx_buff, MAX_MTU);
- kfree_s(hpna->tx_buff, MAX_MTU);
+ unregister_netdev(pegasus->net);
- hpna->usb_dev = NULL;
- hpna->present = 0;
+ usb_unlink_urb(&pegasus->rx_urb);
+ usb_unlink_urb(&pegasus->tx_urb);
+/* usb_unlink_urb(&pegasus->intr_urb);*/
- printk("USB HPNA disconnected\n");
+ kfree(pegasus);
}
-
-static struct usb_driver usb_hpna_driver = {
- "ADMtek \"Pegasus\" USB Ethernet",
- usb_hpna_probe,
- usb_hpna_disconnect,
- {NULL, NULL}
+static struct usb_driver pegasus_driver = {
+ name: "pegasus",
+ probe: pegasus_probe,
+ disconnect: pegasus_disconnect,
};
-
-
-static int __init start_hpna( void )
+int __init pegasus_init(void)
{
printk( version );
- return usb_register( &usb_hpna_driver );
+ return usb_register(&pegasus_driver);
}
-
-static void __exit stop_hpna( void )
+void __exit pegasus_exit(void)
{
- usb_deregister( &usb_hpna_driver );
+ usb_deregister(&pegasus_driver);
}
-
-module_init( start_hpna );
-module_exit( stop_hpna );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index d5c245bf1..20dc5dde5 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -6,14 +6,15 @@
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
+MOD_IN_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS)
# The target object and module list name.
-O_TARGET := usbdrv.o
-M_OBJS :=
-O_OBJS :=
-MOD_LIST_NAME := USB_MODULES
+O_TARGET := serial.o
+M_OBJS := usb-serial.o
+O_OBJS := usb-serial.o
+#MOD_LIST_NAME := USB_MODULES
# Objects that export symbols.
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index c3b4ebc2e..480c6252d 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -236,7 +236,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *prevtd = NULL;
+ struct uhci_td *td, *prevtd;
if (!urbp)
return;
@@ -617,6 +617,8 @@ static int uhci_submit_control(urb_t *urb)
return -EINPROGRESS;
}
+static int usb_control_retrigger_status(urb_t *urb);
+
static int uhci_result_control(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@ static int uhci_result_control(urb_t *urb)
if (!td)
return -EINVAL;
+ if (urbp->short_control_packet)
+ goto status_phase;
+
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@ static int uhci_result_control(urb_t *urb)
/* If SPD is set then we received a short packet */
/* There will be no status phase at the end */
- /* FIXME: Re-setup the queue to run the STATUS phase? */
if ((td->status & TD_CTRL_SPD) &&
(uhci_actual_length(td->status) < uhci_expected_length(td->info)))
- return 0;
+ return usb_control_retrigger_status(urb);
if (status)
goto td_error;
@@ -664,12 +668,13 @@ static int uhci_result_control(urb_t *urb)
td = td->list.next;
}
+status_phase:
/* Control status phase */
status = uhci_status_bits(td->status);
/* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of */
- /* the amount we requested */
+ /* It tries to send all of the descriptor instead of the amount */
+ /* we requested */
if (td->status & TD_CTRL_IOC &&
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
@@ -700,6 +705,47 @@ td_error:
return uhci_map_status(status, uhci_packetout(td->info));
}
+static int usb_control_retrigger_status(urb_t *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci *uhci = urb->dev->bus->hcpriv;
+ struct uhci_td *td, *nexttd;
+
+ urbp->short_control_packet = 1;
+
+ /* Delete all of the TD's except for the status TD at the end */
+ td = urbp->list.begin;
+ while (td && td->list.next) {
+ nexttd = td->list.next;
+
+ uhci_remove_td_from_urb(urb, td);
+
+ uhci_remove_td(uhci, td);
+
+ uhci_free_td(td);
+
+ td = nexttd;
+ }
+
+ /* Create a new QH to avoid pointer overwriting problems */
+ uhci_remove_qh(uhci, urbp->qh);
+
+ urbp->qh = uhci_alloc_qh(urb->dev);
+ if (!urbp->qh)
+ return -ENOMEM;
+
+ /* One TD, who cares about Breadth first? */
+ uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+ /* Low speed or small transfers gets a different queue and treatment */
+ if (urb->pipe & TD_CTRL_LS)
+ uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+ else
+ uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+ return -EINPROGRESS;
+}
+
/*
* Interrupt transfers
*/
diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h
index 62d4e772e..9f4c45e96 100644
--- a/drivers/usb/uhci.h
+++ b/drivers/usb/uhci.h
@@ -338,7 +338,11 @@ struct uhci {
struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
- int fsbr;
+ int fsbr; /* Did this URB turn on FSBR? */
+
+ char short_control_packet; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
unsigned long inserttime; /* In jiffies */
diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c
index 2bba728d9..cb6a70621 100644
--- a/drivers/usb/usb-core.c
+++ b/drivers/usb/usb-core.c
@@ -31,7 +31,6 @@ void usb_major_cleanup(void);
int usb_audio_init(void);
int usb_cpia_init(void);
int usb_ibmcam_init(void);
-int usb_ov511_init(void);
int dabusb_init(void);
int plusb_init(void);
@@ -78,12 +77,12 @@ int usb_init(void)
#ifdef CONFIG_USB_IBMCAM
usb_ibmcam_init();
#endif
-#ifdef CONFIG_USB_OV511
- usb_ov511_init();
-#endif
#ifdef CONFIG_USB_DABUSB
dabusb_init();
#endif
+#ifdef CONFIG_USB_DSBR
+ dsbr100_init();
+#endif
#ifdef CONFIG_USB_PLUSB
plusb_init();
#endif
diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c
index 0e2ab20a4..956e80a0a 100644
--- a/drivers/usb/usb-storage.c
+++ b/drivers/usb/usb-storage.c
@@ -6,9 +6,9 @@
* Further reference:
* This driver is based on the 'USB Mass Storage Class' document. This
* describes in detail the protocol used to communicate with such
- * devices. Clearly, the designers had SCSI commands in mind when they
- * created this document. The commands are all similar to commands
- * in the SCSI-II specification.
+ * devices. Clearly, the designers had SCSI and ATAPI commands in mind
+ * when they created this document. The commands are all very similar
+ * to commands in the SCSI-II and ATAPI specifications.
*
* It is important to note that in a number of cases this class exhibits
* class-specific exemptions from the USB specification. Notably the
@@ -65,8 +65,6 @@ unsigned char us_direction[256/8] = {
static int my_host_number;
-int usb_stor_debug = 1;
-
struct us_data;
typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -74,7 +72,7 @@ typedef int (*trans_reset)(struct us_data*);
typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
struct us_data {
- struct us_data *next; /* next device */
+ struct us_data *next; /* next device */
struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */
__u8 ifnum; /* interface number */
@@ -93,15 +91,17 @@ struct us_data {
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
+ Scsi_Cmnd *queue_srb; /* the single queue slot */
int action; /* what to do */
- wait_queue_head_t waitq; /* thread waits */
- wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ struct semaphore ip_waitq; /* for CBI interrupts */
__u16 ip_data; /* interrupt data */
int ip_wanted; /* needed */
int pid; /* control thread */
struct semaphore *notify; /* wait for thread to begin */
void *irq_handle; /* for USB int requests */
unsigned int irqpipe; /* pipe for release_irq */
+ struct semaphore sleeper; /* to sleep on */
+ struct semaphore queue_exclusion; /* to protect data structs */
};
/*
@@ -129,117 +129,100 @@ static struct usb_driver storage_driver = {
* Data transfer routines
***********************************************************************/
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory. Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
*
- * FIXME: is the above true? Or will the URB status show ETIMEDOUT after
- * retrying several times allready? Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit. Thus we don't have to worry about it for individual
+ * packets.
*/
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe,
+ char *buf, int length)
{
- int max_size;
- int this_xfer;
int result;
int partial;
- int maxtry;
-
- /* determine the maximum packet size for these transfers */
- max_size = usb_maxpacket(us->pusb_dev,
- pipe, usb_pipeout(pipe)) * 16;
-
- /* while we have data left to transfer */
- while (length) {
-
- /* calculate how long this will be -- maximum or a remainder */
- this_xfer = length > max_size ? max_size : length;
- length -= this_xfer;
-
- /* FIXME: this number is totally outrageous. We need to pick
- * a better (smaller) number).
- */
-
- /* setup the retry counter */
- maxtry = 100;
-
- /* set up the transfer loop */
- do {
- /* transfer the data */
- US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n",
- (unsigned int)buf, this_xfer, 101 - maxtry);
- result = usb_bulk_msg(us->pusb_dev, pipe, buf,
- this_xfer, &partial, HZ*5);
- US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
- result, partial, this_xfer);
-
- /* if we stall, we need to clear it before we go on */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
- }
-
- /* update to show what data was transferred */
- this_xfer -= partial;
- buf += partial;
-
- /* NAK - we retry a few times */
- if (result == -ETIMEDOUT) {
- US_DEBUGP("us_one_transfer: device NAKed\n");
-
- /* if our try counter reaches 0, bail out */
- if (!maxtry--)
- return -ETIMEDOUT;
+ /* transfer the data */
+ US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
+ result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5);
+ US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+ result, partial, length);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
- /* just continue the while loop */
- continue;
- }
-
- /* other errors (besides NAK) -- we just bail out*/
- if (result != 0) {
- US_DEBUGP("us_one_transfer: device returned error %d\n", result);
- return result;
- }
+ /* did we send all the data? */
+ if (partial == length) {
+ return US_BULK_TRANSFER_GOOD;
+ }
- /* continue until this transfer is done */
- } while ( this_xfer );
+ /* uh oh... we have an error code, so something went wrong. */
+ if (result) {
+ /* NAK - that means we've retried a few times allready */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("us_bulk_transfer: device NAKed\n");
+ }
+ return US_BULK_TRANSFER_FAILED;
}
- /* if we get here, we're done and successful */
- return 0;
+ /* no error code, so we must have transferred some data,
+ * just not all of it */
+ return US_BULK_TRANSFER_SHORT;
}
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately. For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, int dir_in)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
+ struct us_data *us;
int i;
int result = -1;
- unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
- usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+ unsigned int pipe;
+ struct scatterlist *sg;
- /* FIXME: stop transferring data at us_transfer_length(), not
- * bufflen */
+ /* calculate the appropriate pipe information */
+ us = (struct us_data*) srb->host_scribble;
+ if (dir_in)
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* are we scatter-gathering? */
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ /* loop over all the scatter gather structures and
+ * make the appropriate requests for each, until done
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
- result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+ result = us_bulk_transfer(us, pipe, sg[i].address,
+ sg[i].length);
if (result)
break;
}
}
else
- result = us_one_transfer(us, pipe, srb->request_buffer,
- us_transfer_length(srb));
+ /* no scatter-gather, just make the request */
+ result = us_bulk_transfer(us, pipe, srb->request_buffer,
+ srb->request_bufflen);
- if (result < 0)
- US_DEBUGP("us_transfer returning error %d\n", result);
- return result;
+ /* return the result in the data structure itself */
+ srb->result = result;
}
/* calculate the length of the data transfer (not the command) for any
@@ -265,6 +248,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
case MODE_SENSE:
return srb->cmnd[4];
+ case READ_CAPACITY:
+ return 8;
+
case LOG_SENSE:
case MODE_SENSE_10:
return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -274,8 +260,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
}
if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+ struct scatterlist *sg;
+ sg = (struct scatterlist *) srb->request_buffer;
for (i = 0; i < srb->use_sg; i++) {
total += sg[i].length;
}
@@ -289,12 +276,148 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
* Protocol routines
***********************************************************************/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+ int result;
+
+ /* Fix some commands -- this is a form of mode translation
+ * ATAPI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (us->srb->cmnd[0]) {
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are ATAPI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* send the command to the transport layer */
+ result = us->transport(srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
+
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ int temp_result;
+ void* old_request_buffer;
+ int old_sg;
+
+ US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+ us->srb->cmnd[0] = REQUEST_SENSE;
+ us->srb->cmnd[1] = 0;
+ us->srb->cmnd[2] = 0;
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[4] = 18;
+ us->srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
+ us->srb->request_bufflen = 18;
+ us->srb->request_buffer = us->srb->sense_buffer;
+
+ /* FIXME: what if this command fails? */
+ temp_result = us->transport(us->srb, us);
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+ US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ us->srb->sense_buffer[2] & 0xf,
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
+
+ /* we're done here */
+ us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
+ return;
+ }
+
+ /* Fix the MODE_SENSE data if we translated the command
+ */
+ if (old_cmnd == MODE_SENSE) {
+ unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+ /* FIXME: we need to compress the entire data structure here
+ */
+ dta[0] = dta[1]; /* data len */
+ dta[1] = dta[2]; /* med type */
+ dta[2] = dta[3]; /* dev-spec prm */
+ dta[3] = dta[7]; /* block desc len */
+ printk (KERN_DEBUG USB_STORAGE
+ "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+ dta[0], dta[1], dta[2], dta[3]);
+ }
+
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+ */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+ }
+}
+
static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
{
int old_cmnd = 0;
+ int result;
/* fix some commands -- this is a form of mode translation
* UFI devices only accept 12 byte long commands
@@ -372,23 +495,31 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
} /* end switch on cmnd[0] */
/* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
+ result = us->transport(srb, us);
- /* if we have an error, we're going to do a
- * REQUEST_SENSE automatically */
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
- /* FIXME: we should only do this for device
- * errors, not system errors */
- if (us->srb->result) {
+ /*
+ * If we have an error, we're going to do a
+ * REQUEST_SENSE automatically
+ */
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
void* old_request_buffer;
+ int old_sg;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
@@ -398,49 +529,34 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
us->srb->request_buffer = old_request_buffer;
+ us->srb->use_sg = old_sg;
return;
}
- /* FIXME: if we need to send more data, or recieve data, we should
- * do it here. Then, we can do status handling here also.
- *
- * This includes MODE_SENSE from above
+ /* Fix the MODE_SENSE data here if we had to translate the command
*/
if (old_cmnd == MODE_SENSE) {
unsigned char *dta = (unsigned char *)us->srb->request_buffer;
- /* calculate the new length */
- int length = (dta[0] << 8) + dta[1] + 2;
-
- /* copy the available data length into the structure */
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length & 0xFF;
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
-
- /* FIXME: this assumes that the 2nd attempt is always
- * successful convert MODE_SENSE_10 return data format
- * to MODE_SENSE_6 format */
+ /* FIXME: we need to compress the entire data structure here
+ */
dta[0] = dta[1]; /* data len */
dta[1] = dta[2]; /* med type */
dta[2] = dta[3]; /* dev-spec prm */
@@ -450,126 +566,18 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
dta[0], dta[1], dta[2], dta[3]);
}
- /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
- * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry
- */
-
- /* FIXME: here is where we need to fix-up the return data from
- * an INQUIRY command to show ANSI SCSI rev 2
- */
-
- /* FIXME: The rest of this is bogus. usb_control_msg() will only
- * return an error if we've really honked things up. If it just
- * needs a START_STOP, then we'll get some data back via
- * REQUEST_SENSE -- either way, this belongs at a higher level
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
*/
-
-#if 0
- /* For UFI, if this is the first time we've sent this TEST_UNIT_READY
- * command, we can try again
- */
- if (!done_start && (us->subclass == US_SC_UFI)
- && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-
- /* as per spec try a start command, wait and retry */
- wait_ms(100);
-
- done_start++;
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = START_STOP;
- cmd[4] = 1; /* start */
-
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- cmd, 12, HZ*5);
- US_DEBUGP("Next usb_control_msg returns %d\n", result);
-
- /* allow another retry */
- retry++;
- continue;
+ if (us->srb->cmnd[0] == INQUIRY) {
+ ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
}
-#endif
}
static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
{
- unsigned int savelen = us->srb->request_bufflen;
- unsigned int saveallocation = 0;
-
-#if 0
- /* force attention on first command */
- if (!us->attention_done) {
- if (us->srb->cmnd[0] == REQUEST_SENSE) {
- US_DEBUGP("forcing unit attention\n");
- us->attention_done = 1;
-
- if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
-
- if ((p[2] & 0x0f) != UNIT_ATTENTION) {
- p[2] = UNIT_ATTENTION;
- p[12] = 0x29; /* power on, reset or bus-reset */
- p[13] = 0;
- } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
- } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
- }
- } /* if (!us->attention_done) */
-#endif
-
- /* If the command has a variable-length payload, then we do them
- * in two steps -- first we do the minimum, then we recalculate
- * then length, and re-issue the command
- *
- * we use savelen to remember how much buffer we really have
- * we use savealloction to remember how much was really requested
- */
+ unsigned int result = 0;
- /* FIXME: remove savelen based on mods to us_transfer_length() */
- switch (us->srb->cmnd[0]) {
- case REQUEST_SENSE:
- if (us->srb->request_bufflen > 18)
- us->srb->request_bufflen = 18;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 18;
- break;
-
- case INQUIRY:
- if (us->srb->request_bufflen > 36)
- us->srb->request_bufflen = 36;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 36;
- break;
-
- case MODE_SENSE:
- if (us->srb->request_bufflen > 4)
- us->srb->request_bufflen = 4;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 4;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- if (us->srb->request_bufflen > 8)
- us->srb->request_bufflen = 8;
- else
- break;
- saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
- us->srb->cmnd[7] = 0;
- us->srb->cmnd[8] = 8;
- break;
-
- default:
- break;
- } /* end switch on cmnd[0] */
-
/* This code supports devices which do not support {READ|WRITE}_6
* Apparently, neither Windows or MacOS will use these commands,
* so some devices do not support them
@@ -631,25 +639,33 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
US_DEBUG(us_show_command(us->srb));
}
- } /* end if (us->flags & US_FL_MODE_XLATE) */
+ } /* if (us->flags & US_FL_MODE_XLATE) */
/* send the command to the transport layer */
- us->srb->result = us->transport(us->srb, us);
+ result = us->transport(us->srb, us);
+
+ /* If we got a short transfer, but it was for a command that
+ * can have short transfers, we're actually okay
+ */
+ if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+ ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+ (us->srb->cmnd[0] == INQUIRY) ||
+ (us->srb->cmnd[0] == MODE_SENSE) ||
+ (us->srb->cmnd[0] == LOG_SENSE) ||
+ (us->srb->cmnd[0] == MODE_SENSE_10))) {
+ us->srb->result = DID_OK;
+ }
/* if we have an error, we're going to do a REQUEST_SENSE
* automatically */
- /* FIXME: we should only do this for device errors, not
- * system errors */
- if (us->srb->result) {
+ if (result != USB_STOR_TRANSPORT_GOOD) {
int temp_result;
- int count;
+ int old_sg;
void* old_request_buffer;
US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
+ /* set up the REQUEST_SENSE command and parameters */
us->srb->cmnd[0] = REQUEST_SENSE;
us->srb->cmnd[1] = 0;
us->srb->cmnd[2] = 0;
@@ -659,115 +675,32 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
/* set the buffer length for transfer */
old_request_buffer = us->srb->request_buffer;
+ old_sg = us->srb->use_sg;
us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+ us->srb->request_buffer = us->srb->sense_buffer;
/* FIXME: what if this command fails? */
temp_result = us->transport(us->srb, us);
US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+ us->srb->sense_buffer[12],
+ us->srb->sense_buffer[13]);
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
/* we're done here */
- kfree(us->srb->request_buffer);
+ us->srb->use_sg = old_sg;
us->srb->request_buffer = old_request_buffer;
return;
}
- if (savelen != us->srb->request_bufflen) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
- unsigned int length = 0;
-
- /* set correct length and retry */
- switch (us->srb->cmnd[0]) {
-
- /* FIXME: we should try to get all the sense data */
- case REQUEST_SENSE:
- /* simply return 18 bytes */
- p[7] = 10;
- length = us->srb->request_bufflen;
- break;
-
- case INQUIRY:
- length = p[4] + 5 > savelen ? savelen : p[4] + 5;
- us->srb->cmnd[4] = length;
- break;
-
- case MODE_SENSE:
- US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
- length = p[0] + 1 > savelen ? savelen : p[0] + 1;
- us->srb->cmnd[4] = length;
- break;
-
- case LOG_SENSE:
- length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
-
- case MODE_SENSE_10:
- US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
- (p[0] << 8) + p[1]);
- length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
- } /* end switch on cmnd[0] */
-
- US_DEBUGP("Old/New length = %d/%d\n",
- savelen, length);
-
- /* issue the new command */
- /* FIXME: this assumes that the second attempt is
- * always successful */
- if (us->srb->request_bufflen != length) {
- US_DEBUGP("redoing cmd with len=%d\n", length);
- us->srb->request_bufflen = length;
- us->srb->result = us->transport(us->srb, us);
- }
-
- /* reset back to original values */
- us->srb->request_bufflen = savelen;
-
- /* fix data as necessary */
- switch (us->srb->cmnd[0]) {
- case INQUIRY:
- if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) {
- US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
- ((unsigned char*)us->srb->request_buffer)[2] |= 2;
- }
- /* FALL THROUGH */
- case REQUEST_SENSE:
- case MODE_SENSE:
- if (us->srb->use_sg == 0 && length > 0) {
- int i;
- printk(KERN_DEBUG "Data is");
- for (i = 0; i < 32 && i < length; ++i)
- printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
- if (i < length)
- printk(" ...");
- printk("\n");
- }
-
- /* FIXME: is this really necessary? */
- us->srb->cmnd[4] = saveallocation;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- /* FIXME: is this really necessary? */
- us->srb->cmnd[7] = saveallocation >> 8;
- us->srb->cmnd[8] = saveallocation;
- break;
- } /* end switch on cmnd[0] */
- } /* if good command */
+ /* fix the results of an INQUIRY */
+ if (us->srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+ ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+ }
}
/***********************************************************************
@@ -789,7 +722,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
/* was this a wanted interrupt? */
if (us->ip_wanted) {
us->ip_wanted = 0;
- wake_up(&us->ip_waitq);
+ up(&(us->ip_waitq));
} else {
US_DEBUGP("ERROR: Unwanted interrupt received!\n");
}
@@ -801,9 +734,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
return 0;
}
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
*/
static int CB_reset(struct us_data *us)
{
@@ -816,41 +747,39 @@ static int CB_reset(struct us_data *us)
cmd[0] = SEND_DIAGNOSTIC;
cmd[1] = 4;
result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, us->ifnum, cmd, sizeof(cmd), HZ*5);
/* long wait for reset */
schedule_timeout(HZ*6);
US_DEBUGP("CB_reset: clearing endpoint halt\n");
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
US_DEBUGP("CB_reset done\n");
return 0;
}
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
*/
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
US_DEBUGP("CBI gets a command:\n");
US_DEBUG(us_show_command(srb));
- /* FIXME: we aren't setting the ip_wanted indicator early enough, which
- * causes some commands to never complete. This hangs the driver.
- */
-
+ /* COMMAND STAGE */
/* let's send the command via the control pipe */
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- srb->cmnd, srb->cmd_len, HZ*5);
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
/* check the return code for the command */
if (result < 0) {
@@ -858,131 +787,160 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
- US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
usb_clear_halt(us->pusb_dev,
- usb_sndctrlpipe(us->pusb_dev, 0)));
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
+ /* Set up for status notification */
+ us->ip_wanted = 1;
+
+ /* DATA STAGE */
/* transfer the data payload for this command, if one exists*/
if (us_transfer_length(srb)) {
- result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
- US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
-
- /* FIXME: what do the return codes from us_transfer mean? */
- if ((result < 0) &&
- (result != USB_ST_DATAUNDERRUN) &&
- (result != USB_ST_STALL)) {
- return DID_ERROR << 16;
- }
- } /* if (us_transfer_length(srb)) */
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBI data stage result is 0x%x\n", result);
+ }
- /* get status and return it */
- return pop_CB_status(srb);
+ /* STATUS STAGE */
+
+ /* go to sleep until we get this interrup */
+ /* FIXME: this should be changed to use a timeout */
+ down(&(us->ip_waitq));
+
+ /* FIXME: currently this code is unreachable, but the idea is
+ * necessary. See above comment.
+ */
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+
+ /* UFI gives us ASC and ASCQ, like a request sense */
+ /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special
+ * case handling?
+ */
+ if (us->subclass == US_SC_UFI) {
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return USB_STOR_TRANSPORT_GOOD;
+ else
+ if (us->ip_data)
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ /* otherwise, we interpret the data normally */
+ switch (us->ip_data) {
+ case 0x0001:
+ return USB_STOR_TRANSPORT_GOOD;
+ case 0x0002:
+ return USB_STOR_TRANSPORT_FAILED;
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("CBI_transport() reached end of function\n");
+ return USB_STOR_TRANSPORT_ERROR;
}
/*
- * Control/Bulk status handler
+ * Control/Bulk transport
*/
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
{
- struct us_data *us = (struct us_data *)srb->host_scribble;
- int result = 0;
+ int result;
__u8 status[2];
- int retry = 5;
- US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
- switch (us->protocol) {
- case US_PR_CB:
- /* get from control */
-
- while (retry--) {
- result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
- USB_REQ_GET_STATUS, USB_DIR_IN |
- USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- 0, us->ifnum, status, sizeof(status), HZ*5);
- if (result != USB_ST_TIMEOUT)
- break;
- }
- if (result) {
- US_DEBUGP("Bad AP status request %d\n", result);
- return DID_ABORT << 16;
- }
- US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
- if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
- ( (status[0] & ~3) || status[1]))
- return (DID_OK << 16) | 2;
- else
- return USB_STOR_TRANSPORT_GOOD;
- break;
+ US_DEBUGP("CBC gets a command:\n");
+ US_DEBUG(us_show_command(srb));
- /* FIXME: this should be in a separate function */
- case US_PR_CBI:
- /* get from interrupt pipe */
+ /* COMMAND STAGE */
+ /* let's send the command via the control pipe */
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
- /* add interrupt transfer, marked for removal */
- us->ip_wanted = 1;
+ /* check the return code for the command */
+ if (result < 0) {
+ US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
- /* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
- sleep_on(&us->ip_waitq);
-
- if (us->ip_wanted) {
- US_DEBUGP("Did not get interrupt on CBI\n");
- us->ip_wanted = 0;
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+ usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,
+ 0)));
return USB_STOR_TRANSPORT_ERROR;
}
-
- US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
- /* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special
- * case handling?
- */
- if (us->subclass == US_SC_UFI) {
- if (srb->cmnd[0] == REQUEST_SENSE ||
- srb->cmnd[0] == INQUIRY)
- return USB_STOR_TRANSPORT_GOOD;
- else
- if (us->ip_data)
- return USB_STOR_TRANSPORT_FAILED;
- else
- return USB_STOR_TRANSPORT_GOOD;
- }
+ /* FIXME: we need to handle NAKs here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
- /* otherwise, we interpret the data normally */
- switch (us->ip_data) {
- case 0x0001:
- return USB_STOR_TRANSPORT_GOOD;
- case 0x0002:
- return USB_STOR_TRANSPORT_FAILED;
- default:
- return USB_STOR_TRANSPORT_ERROR;
- }
+ /* DATA STAGE */
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb)) {
+ us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBC data stage result is 0x%x\n", result);
}
- US_DEBUGP("pop_CB_status, reached end of function\n");
+
+
+ /* STATUS STAGE */
+ /* FIXME: this is wrong */
+ result = usb_control_msg(us->pusb_dev,
+ usb_rcvctrlpipe(us->pusb_dev,0),
+ USB_REQ_GET_STATUS, USB_DIR_IN |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 0, us->ifnum, status, sizeof(status), HZ*5);
+
+ if (result < 0) {
+ US_DEBUGP("CBC Status stage returns %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+ if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+ ( (status[0] & ~3) || status[1]))
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+
+ US_DEBUGP("CB_transport() reached end of function\n");
return USB_STOR_TRANSPORT_ERROR;
}
+/* FIXME: Does this work? */
static int Bulk_reset(struct us_data *us)
{
int result;
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- US_BULK_RESET_HARD, us->ifnum,
- NULL, 0, HZ*5);
- if (result)
+ result = usb_control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ US_BULK_RESET,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+ if (result < 0)
US_DEBUGP("Bulk hard reset failed %d\n", result);
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+ usb_clear_halt(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out));
/* long wait for reset */
schedule_timeout(HZ*6);
@@ -991,8 +949,7 @@ static int Bulk_reset(struct us_data *us)
}
/*
- * The bulk only protocol handler.
- * Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
*/
static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
{
@@ -1001,7 +958,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
int result;
int pipe;
int partial;
-
+
/* set up the command wrapper */
bcb.Signature = US_BULK_CB_SIGN;
bcb.DataTransferLength = us_transfer_length(srb);
@@ -1009,14 +966,14 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
bcb.Tag = srb->serial_number;
bcb.Lun = 0;
bcb.Length = srb->cmd_len;
-
+
/* construct the pipe handle */
pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+
/* copy the command payload */
memset(bcb.CDB, 0, sizeof(bcb.CDB));
memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+
/* send it to out endpoint */
US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
bcb.Signature, bcb.Tag, bcb.DataTransferLength,
@@ -1024,94 +981,83 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
US_BULK_CB_WRAP_LEN, &partial, HZ*5);
US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+
/* if we stall, we need to clear it before we go on */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
}
-
+
/* if the command transfered well, then we go to the data stage */
- /* FIXME: Regardless of the status of the data stage, we go on to the
- * status stage. Note that this implies that if a command is
- * partially successful, we rely on the device reporting an error
- * the CSW. The spec says that the device may just decide to short us.
- */
if (result == 0) {
/* send/receive data payload, if there is any */
if (bcb.DataTransferLength) {
- result = us_transfer(srb, bcb.Flags);
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
- if ((result < 0) && (result != USB_ST_DATAUNDERRUN)
- && (result != USB_ST_STALL)) {
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
- return DID_ABORT << 16;
- }
-#endif
+ us_transfer(srb, bcb.Flags);
+ US_DEBUGP("Bulk data transfer result 0x%x\n",
+ srb->result);
}
}
-
+
/* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
-
+
/* construct the pipe handle */
pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+
/* get CSW for device status */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* did the attempt to read the CSW fail? */
if (result == -EPIPE) {
US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
usb_clear_halt(us->pusb_dev, pipe);
-
+
/* get the status again */
result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+
/* if it fails again, we need a reset and return an error*/
if (result == -EPIPE) {
Bulk_reset(us);
- return (DID_ABORT << 16);
+ return USB_STOR_TRANSPORT_ERROR;
}
}
-
+
/* if we still have a failure at this point, we're in trouble */
if (result) {
- US_DEBUGP("Bulk status result = 0x%x\n", result);
- return DID_ABORT << 16;
+ US_DEBUGP("Bulk status result = %d\n", result);
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* check bulk status */
US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
US_DEBUGP("Bulk logical error\n");
- return DID_ABORT << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
+
/* based on the status code, we report good or bad */
switch (bcs.Status) {
case US_BULK_STAT_OK:
- /* if there is residue, we really didn't finish the command */
- if (bcs.Residue)
- return DID_ERROR << 16;
- else
- return DID_OK << 16;
+ /* command good -- note that we could be short on data */
+ return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
- return DID_ERROR << 16;
-
+ /* command failed */
+ return USB_STOR_TRANSPORT_FAILED;
+
case US_BULK_STAT_PHASE:
+ /* phase error */
Bulk_reset(us);
- return DID_ERROR << 16;
+ return USB_STOR_TRANSPORT_ERROR;
}
-
- return DID_OK << 16; /* check sense required */
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
}
/***********************************************************************
@@ -1163,14 +1109,20 @@ static int us_release(struct Scsi_Host *psh)
usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
us->irq_handle = NULL;
}
- if (us->pusb_dev)
- usb_deregister(&storage_driver);
+
+ /* FIXME: release the interface claim here? */
+ // if (us->pusb_dev)
+ // usb_deregister(&storage_driver);
/* FIXME - leaves hanging host template copy */
/* (because scsi layer uses it after removal !!!) */
- while (prev->next != us)
- prev = prev->next;
- prev->next = us->next;
+ if (us_list == us)
+ us_list = us->next;
+ else {
+ while (prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ }
return 0;
}
@@ -1188,17 +1140,19 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
struct us_data *us = (struct us_data *)srb->host->hostdata[0];
US_DEBUGP("Command wakeup\n");
- if (us->srb) {
- /* busy */
- }
srb->host_scribble = (unsigned char *)us;
- us->srb = srb;
+
+ /* get exclusive access to the structures we want */
+ down(&(us->queue_exclusion));
+
+ /* enqueue the command */
+ us->queue_srb = srb;
srb->scsi_done = done;
us->action = US_ACT_COMMAND;
/* wake up the process task */
-
- wake_up_interruptible(&us->waitq);
+ up(&(us->queue_exclusion));
+ up(&(us->sleeper));
return 0;
}
@@ -1209,6 +1163,7 @@ static int us_abort( Scsi_Cmnd *srb )
return 0;
}
+/* FIXME: this doesn't do anything right now */
static int us_bus_reset( Scsi_Cmnd *srb )
{
// struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -1340,11 +1295,11 @@ static Scsi_Host_Template my_host_template = {
NULL, /* select_queue_depths */
1, /* can_queue */
-1, /* this_id */
- SG_ALL, /* sg_tablesize */
+ SG_ALL, /* sg_tablesize */
1, /* cmd_per_lun */
0, /* present */
- FALSE, /* unchecked_isa_dma */
- FALSE, /* use_clustering */
+ FALSE, /* unchecked_isa_dma */
+ TRUE, /* use_clustering */
TRUE, /* use_new_eh_code */
TRUE /* emulated */
};
@@ -1391,10 +1346,18 @@ static int usb_stor_control_thread(void * __us)
siginfo_t info;
int unsigned long signr;
- interruptible_sleep_on(&us->waitq);
+ US_DEBUGP("*** thread sleeping.\n");
+ down(&(us->sleeper));
+ down(&(us->queue_exclusion));
+ US_DEBUGP("*** thread awakened.\n");
+ /* take the command off the queue */
action = us->action;
us->action = 0;
+ us->srb = us-> queue_srb;
+
+ /* release the queue lock as fast as possible */
+ up(&(us->queue_exclusion));
/* FIXME: we need to examine placment of break; and
* scsi_done() calls */
@@ -1460,29 +1423,20 @@ static int usb_stor_control_thread(void * __us)
break;
} /* end switch on action */
-
+
+ /* FIXME: we ignore TERM and KILL... is this right? */
if (signal_pending(current)) {
/* sending SIGUSR1 makes us print out some info */
spin_lock_irq(&current->sigmask_lock);
signr = dequeue_signal(&current->blocked, &info);
spin_unlock_irq(&current->sigmask_lock);
-
- if (signr == SIGUSR2) {
- usb_stor_debug = !usb_stor_debug;
- printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
- } else {
- break; /* exit the loop on any other signal */
- }
- }
- }
+ } /* if (singal_pending(current)) */
+ } /* for (;;) */
// MOD_DEC_USE_COUNT;
printk("usb_stor_control_thread exiting\n");
- /* FIXME: this is a hack to allow for debugging */
- // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
return 0;
}
@@ -1498,7 +1452,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
unsigned int flags = 0;
GUID(guid); /* Global Unique Identifier */
struct us_data *prev;
- Scsi_Host_Template *htmplt;
int protocol = 0;
int subclass = 0;
struct usb_interface_descriptor *altsetting =
@@ -1565,14 +1518,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
return NULL;
}
memset(ss, 0, sizeof(struct us_data));
+
+ /* Initialize the mutexes only when the struct is new */
+ init_MUTEX_LOCKED(&(ss->sleeper));
+ init_MUTEX(&(ss->queue_exclusion));
}
- /* Initialize the us_data structure with some useful info */
+ /* establish the connection to the new device */
interface = altsetting;
ss->flags = flags;
ss->ifnum = ifnum;
- ss->pusb_dev = dev;
ss->attention_done = 0;
+ ss->pusb_dev = dev;
/* If the device has subclass and protocol, then use that. Otherwise,
* take data from the specific interface.
@@ -1596,7 +1553,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
case US_PR_CBI:
US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CB_transport;
+ ss->transport = CBI_transport;
ss->transport_reset = CB_reset;
break;
@@ -1620,7 +1577,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
*/
for (i = 0; i < interface->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK) {
if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in = interface->endpoint[i].bEndpointAddress &
@@ -1646,7 +1603,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
(ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
US_DEBUGP("Problems with device\n");
if (ss->host) {
- scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
kfree(ss->htmplt->name);
kfree(ss->htmplt);
}
@@ -1667,11 +1623,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
US_DEBUGP("Protocol: ");
switch (ss->subclass) {
case US_SC_RBC:
- US_DEBUGPX("Reduced Block Commands\n");
+ US_DEBUGPX("Reduced Block Commands (RBC)\n");
+ ss->proto_handler = transparent_scsi_command;
break;
case US_SC_8020:
- US_DEBUGPX("8020\n");
+ US_DEBUGPX("8020i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_QIC:
@@ -1679,7 +1637,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
case US_SC_8070:
- US_DEBUGPX("8070\n");
+ US_DEBUGPX("8070i\n");
+ ss->proto_handler = ATAPI_command;
break;
case US_SC_SCSI:
@@ -1697,22 +1656,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
}
- /* We only handle certain protocols. Currently, these are
- *the only ones that devices use.
- */
- if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
- US_DEBUGP("Sorry, we do not support that protocol yet.\n");
- US_DEBUGP("If you have a device which uses one of the unsupported\n");
- US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-
- kfree(ss);
- return NULL;
- }
-
/* Allocate memory for the SCSI Host Template */
- if ((htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+ if ((ss->htmplt = (Scsi_Host_Template *)
+ kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n");
kfree(ss);
@@ -1720,7 +1666,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
/* Initialize the host template based on the default one */
- memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+ memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
/* Grab the next host number */
ss->host_number = my_host_number++;
@@ -1729,32 +1675,34 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
* can pass the ss pointer to the host controler thread
* in us_detect
*/
- (struct us_data *)htmplt->proc_dir = ss;
+ (struct us_data *)ss->htmplt->proc_dir = ss;
/* shuttle E-USB */
if (dev->descriptor.idVendor == 0x04e6 &&
dev->descriptor.idProduct == 0x0001) {
__u8 qstat[2];
int result;
-
- result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
1, 0xC0,
0, ss->ifnum,
qstat, 2, HZ*5);
US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_waitqueue_head(&ss->ip_waitq);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result)
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
return NULL;
-
- interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
- } else if (ss->protocol == US_PR_CBI)
- {
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ } else if (ss->protocol == US_PR_CBI) {
int result;
-
- init_waitqueue_head(&ss->ip_waitq);
+
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
/* set up the IRQ pipe and handler */
/* FIXME: This needs to get the period from the device */
@@ -1768,18 +1716,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
- /* start up our thread */
+ /* start up our thread */
{
DECLARE_MUTEX_LOCKED(sem);
- init_waitqueue_head(&ss->waitq);
-
ss->notify = &sem;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (ss->pid < 0) {
printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(htmplt);
+ kfree(ss->htmplt);
kfree(ss);
return NULL;
@@ -1790,17 +1736,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
/* now register - our detect function will be called */
- scsi_register_module(MODULE_SCSI_HA, htmplt);
+ ss->htmplt->module = &__this_module;
+ scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
/* put us in the list */
- prev = (struct us_data *)&us_list;
- while (prev->next)
- prev = prev->next;
- prev->next = ss;
+ ss->next = us_list;
+ us_list = ss;
}
- printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+ printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
return ss;
}
@@ -1814,7 +1759,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
return;
ss->pusb_dev = NULL;
- // MOD_DEC_USE_COUNT;
}
@@ -1824,8 +1768,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
int __init usb_stor_init(void)
{
- // MOD_INC_USE_COUNT;
-
if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n",
@@ -1844,6 +1786,14 @@ int __init usb_stor_init(void)
void __exit usb_stor_exit(void)
{
+ static struct us_data *ptr;
+
+ // FIXME: this needs to be put back to free _all_ the hosts
+ // for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+ // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
+ printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
+ scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+
usb_deregister(&storage_driver) ;
}
diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h
index 80a03f3c9..06e6d958b 100644
--- a/drivers/usb/usb-storage.h
+++ b/drivers/usb/usb-storage.h
@@ -9,13 +9,11 @@
#define USB_STORAGE "usb-storage: "
-extern int usb_stor_debug;
-
#ifdef CONFIG_USB_STORAGE_DEBUG
void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x) { if(usb_stor_debug) x; }
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) x
#else
#define US_DEBUGP(x...)
#define US_DEBUGPX(x...)
@@ -83,15 +81,22 @@ struct bulk_cs_wrap {
#define US_BULK_RESET_HARD 0
/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD 0
+#define US_BULK_TRANSFER_SHORT 1
+#define US_BULK_TRANSFER_FAILED 2
+
+/*
* Transport return codes
*/
-#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
-#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
-#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */
+#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
+#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */
/*
- * CBI style
+ * CBI accept device specific command
*/
#define US_CBI_ADSC 0
@@ -128,3 +133,4 @@ static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *seri
#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
#define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for
Win/MacOS compatibility */
+
diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h
index 73d16937a..4eedc4183 100644
--- a/drivers/usb/usb-uhci-debug.h
+++ b/drivers/usb/usb-uhci-debug.h
@@ -1,41 +1,32 @@
#ifdef DEBUG
-
static void uhci_show_qh (puhci_desc_t qh)
{
if (qh->type != QH_TYPE) {
dbg("qh has not QH_TYPE");
return;
}
- dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
+ dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
if (qh->hw.qh.head & UHCI_PTR_TERM)
- dbg("Head Terminate");
- else {
- if (qh->hw.qh.head & UHCI_PTR_QH)
- dbg("Head points to QH");
- else
- dbg("Head points to TD");
-
- dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
- }
+ dbg(" Head Terminate");
+ else
+ dbg(" Head: %s @ %08X",
+ (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.head & ~UHCI_PTR_BITS);
+
if (qh->hw.qh.element & UHCI_PTR_TERM)
- dbg("Element Terminate");
- else {
-
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Element points to QH");
- else
- dbg("Element points to TD");
- dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
- }
+ dbg(" Element Terminate");
+ else
+ dbg(" Element: %s @ %08X",
+ (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+ qh->hw.qh.element & ~UHCI_PTR_BITS);
}
#endif
static void uhci_show_td (puhci_desc_t td)
{
char *spid;
- warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
-
+
switch (td->hw.td.info & 0xff) {
case USB_PID_SETUP:
spid = "SETUP";
@@ -51,16 +42,16 @@ static void uhci_show_td (puhci_desc_t td)
break;
}
- warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
+ warn(" TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+ td, virt_to_bus (td),
td->hw.td.info >> 21,
((td->hw.td.info >> 19) & 1),
(td->hw.td.info >> 15) & 15,
(td->hw.td.info >> 8) & 127,
- (td->hw.td.info & 0xff),
spid,
td->hw.td.buffer);
- warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+ warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
td->hw.td.status & 0x7ff,
((td->hw.td.status >> 27) & 3),
(td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
@@ -74,50 +65,41 @@ static void uhci_show_td (puhci_desc_t td)
(td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
);
-#if 1
+
if (td->hw.td.link & UHCI_PTR_TERM)
- warn("Link Terminate");
- else {
- if (td->hw.td.link & UHCI_PTR_QH)
- warn("%s, link points to QH @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- else
- warn("%s, link points to TD @ %08x",
- (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
- td->hw.td.link & ~UHCI_PTR_BITS);
- }
-#endif
+ warn(" TD Link Terminate");
+ else
+ warn(" Link points to %s @ %08x, %s",
+ (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+ td->hw.td.link & ~UHCI_PTR_BITS,
+ (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
}
#ifdef DEBUG
static void uhci_show_td_queue (puhci_desc_t td)
{
- dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+ //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
while (1) {
uhci_show_td (td);
if (td->hw.td.link & UHCI_PTR_TERM)
break;
- //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
- // break;
if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
else {
dbg("td points to itself!");
break;
}
-// schedule();
}
}
static void uhci_show_queue (puhci_desc_t qh)
{
+ uhci_desc_t *start_qh=qh;
+
dbg("uhci_show_queue %p:", qh);
while (1) {
uhci_show_qh (qh);
- if (qh->hw.qh.element & UHCI_PTR_QH)
- dbg("Warning: qh->element points to qh!");
- else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+ if (!(qh->hw.qh.element & UHCI_PTR_TERM))
uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
if (qh->hw.qh.head & UHCI_PTR_TERM)
@@ -129,7 +111,12 @@ static void uhci_show_queue (puhci_desc_t qh)
dbg("qh points to itself!");
break;
}
- }
+
+ if (qh==start_qh) { // avoid loop
+ dbg("Loop detect");
+ break;
+ }
+ }
}
static void uhci_show_sc (int port, unsigned short status)
diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c
index aed79f849..85a5cd476 100644
--- a/drivers/usb/usb-uhci.c
+++ b/drivers/usb/usb-uhci.c
@@ -12,7 +12,7 @@
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
+ * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $
*/
#include <linux/config.h>
@@ -28,9 +28,9 @@
#include <linux/unistd.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/init.h>
-/* This enables debug printks */
-#define DEBUG
-#include <linux/usb.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
+#include <linux/pm.h>
+#endif
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -40,51 +40,56 @@
/* This enables more detailed sanity checks in submit_iso */
//#define ISO_SANITY_CHECK
+/* This enables debug printks */
+#define DEBUG
+
/* This enables all symbols to be exported, to ease debugging oopses */
//#define DEBUG_SYMBOLS
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
+#include <linux/usb.h>
#include "usb-uhci.h"
#include "usb-uhci-debug.h"
#undef DEBUG
#undef dbg
#define dbg(format, arg...) do {} while (0)
-
-#include <linux/pm.h>
-
+#define DEBUG_SYMBOLS
#ifdef DEBUG_SYMBOLS
#define _static
#ifndef EXPORT_SYMTAB
- #define EXPORT_SYMTAB
+ #define EXPORT_SYMTAB
#endif
#else
#define _static static
#endif
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
#ifdef DEBUG_SLAB
static kmem_cache_t *uhci_desc_kmem;
static kmem_cache_t *urb_priv_kmem;
#endif
+#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-#define USE_RECLAMATION_LOOP
-#else
-//#define USE_RECLAMATION_LOOP
-#endif
-
-// stop bandwidth reclamation after (roughly) 50ms (depends also on
-// hub polling interval)
+// stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT (HZ/20)
_static int rh_submit_urb (urb_t *urb);
_static int rh_unlink_urb (urb_t *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
static uhci_t *devs = NULL;
@@ -105,58 +110,47 @@ void clean_descs(uhci_t *s, int force)
qh = list_entry (q, uhci_desc_t, horizontal);
if ((qh->last_used!=now) || force)
delete_qh(s,qh);
+
q=qh->horizontal.prev;
}
}
/*-------------------------------------------------------------------*/
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
_static void enable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("enable_desc_loop: enter");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
- s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+ s->chain_end->hw.qh.head&=~UHCI_PTR_TERM;
+ mb();
s->loop_usage++;
((urb_priv_t*)urb->hcpriv)->use_loop=1;
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("enable_desc_loop: finished");
}
/*-------------------------------------------------------------------*/
_static void disable_desc_loop(uhci_t *s, urb_t *urb)
{
int flags;
-
- dbg("disable_desc_loop: enter\n");
-
+
spin_lock_irqsave (&s->qh_lock, flags);
if (((urb_priv_t*)urb->hcpriv)->use_loop) {
s->loop_usage--;
- if (!s->loop_usage)
- s->chain_end->hw.qh.head=UHCI_PTR_TERM;
-
+ if (!s->loop_usage) {
+ s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+ mb();
+ }
((urb_priv_t*)urb->hcpriv)->use_loop=0;
}
spin_unlock_irqrestore (&s->qh_lock, flags);
-
- dbg("disable_desc_loop: finished");
-
}
#endif
/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
{
- unsigned long flags=0;
struct list_head *p=&urb->urb_list;
-
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
-
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
{
int type;
type=usb_pipetype (urb->pipe);
@@ -166,15 +160,21 @@ _static void queue_urb (uhci_t *s, urb_t *urb)
}
#endif
((urb_priv_t*)urb->hcpriv)->started=jiffies;
- list_add_tail (p, &s->urb_list);
-
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ list_add (p, &s->urb_list);
}
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+ unsigned long flags=0;
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ queue_urb_unlocked(s,urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
/*-------------------------------------------------------------------*/
_static void dequeue_urb (uhci_t *s, urb_t *urb)
{
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type;
type=usb_pipetype (urb->pipe);
@@ -189,9 +189,9 @@ _static void dequeue_urb (uhci_t *s, urb_t *urb)
_static int alloc_td (uhci_desc_t ** new, int flags)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
@@ -205,6 +205,19 @@ _static int alloc_td (uhci_desc_t ** new, int flags)
return 0;
}
/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags)
+{
+ unsigned long xxx;
+
+ spin_lock_irqsave (&s->td_lock, xxx);
+
+ td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+
+ mb();
+ spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
/* insert td at last position in td-list of qh (vertical) */
_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
{
@@ -271,10 +284,9 @@ _static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
if (prev->type == TD_TYPE)
prev->hw.td.link = element->hw.td.link;
else
- prev->hw.qh.element = element->hw.td.link;
+ prev->hw.qh.element = element->hw.td.link;
}
- element->hw.td.link=UHCI_PTR_TERM;
mb ();
if (dir == 0)
@@ -302,9 +314,9 @@ _static int delete_desc (uhci_desc_t *element)
_static int alloc_qh (uhci_desc_t ** new)
{
#ifdef DEBUG_SLAB
- *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
#else
- *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
#endif
if (!*new)
return -ENOMEM;
@@ -350,9 +362,10 @@ _static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
mb ();
spin_unlock_irqrestore (&s->qh_lock, flags);
-
+
return 0;
}
+
/*-------------------------------------------------------------------*/
_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
{
@@ -406,6 +419,14 @@ _static void clean_td_chain (uhci_desc_t *td)
delete_desc (td);
}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+ td->hw.td.status = status;
+ td->hw.td.info = info;
+ td->hw.td.buffer = buffer;
+}
/*-------------------------------------------------------------------*/
// Removes ALL qhs in chain (paranoia!)
_static void cleanup_skel (uhci_t *s)
@@ -441,19 +462,20 @@ _static void cleanup_skel (uhci_t *s)
qh = s->control_chain;
while ((p = qh->horizontal.next) != &qh->horizontal) {
qh1 = list_entry (p, uhci_desc_t, horizontal);
- dbg("delete_qh @ %p",qh1);
delete_qh (s, qh1);
}
- dbg("delete_qh last @ %p",qh);
+
delete_qh (s, qh);
}
else {
+ if (s->ls_control_chain)
+ delete_desc (s->ls_control_chain);
if (s->control_chain)
- kfree (s->control_chain);
+ delete_desc(s->control_chain);
if (s->bulk_chain)
- kfree (s->bulk_chain);
+ delete_desc (s->bulk_chain);
if (s->chain_end)
- kfree (s->chain_end);
+ delete_desc (s->chain_end);
}
dbg("cleanup_skel finished");
}
@@ -480,6 +502,7 @@ _static int init_skel (uhci_t *s)
if (!s->iso_td)
goto init_skel_cleanup;
+ s->ls_control_chain = NULL;
s->control_chain = NULL;
s->bulk_chain = NULL;
s->chain_end = NULL;
@@ -499,26 +522,46 @@ _static int init_skel (uhci_t *s)
if (ret)
goto init_skel_cleanup;
-
+
s->chain_end = qh;
+ ret = alloc_td (&td, 0);
+
+ if (ret)
+ goto init_skel_cleanup;
+
+ fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt
+ insert_td (s, qh, td, 0);
+
dbg("allocating qh: bulk_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->chain_end, qh, 0);
s->bulk_chain = qh;
+
dbg("allocating qh: control_chain");
ret = alloc_qh (&qh);
-
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->bulk_chain, qh, 0);
s->control_chain = qh;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ // disabled reclamation loop
+ s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+ dbg("allocating qh: ls_control_chain");
+ ret = alloc_qh (&qh);
+ if (ret)
+ goto init_skel_cleanup;
+
+ insert_qh (s, s->control_chain, qh, 0);
+ s->ls_control_chain = qh;
+
for (n = 0; n < 8; n++)
s->int_chain[n] = 0;
@@ -532,7 +575,7 @@ _static int init_skel (uhci_t *s)
goto init_skel_cleanup;
s->int_chain[n] = td;
if (n == 0) {
- s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+ s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
}
else {
s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
@@ -547,20 +590,16 @@ _static int init_skel (uhci_t *s)
dbg("framelist[%i]=%x",n,s->framelist[n]);
if ((n&127)==127)
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
- else {
- for (o = 1, m = 2; m <= 128; o++, m += m) {
- // n&(m-1) = n%m
- if ((n & (m - 1)) == ((m - 1) / 2)) {
+ else
+ for (o = 1, m = 2; m <= 128; o++, m += m)
+ if ((n & (m - 1)) == ((m - 1) / 2))
((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
- }
- }
- }
}
mb();
//uhci_show_queue(s->control_chain);
dbg("init_skel exit");
- return 0; // OK
+ return 0;
init_skel_cleanup:
cleanup_skel (s);
@@ -568,14 +607,6 @@ _static int init_skel (uhci_t *s)
}
/*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
- td->hw.td.status = status;
- td->hw.td.info = info;
- td->hw.td.buffer = buffer;
-}
-
-/*-------------------------------------------------------------------*/
// LOW LEVEL STUFF
// assembles QHs und TDs for control, bulk and iso
/*-------------------------------------------------------------------*/
@@ -586,10 +617,15 @@ _static int uhci_submit_control_urb (urb_t *urb)
urb_priv_t *urb_priv = urb->hcpriv;
unsigned long destination, status;
int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
- unsigned long len, bytesrequested;
+ unsigned long len;
char *data;
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method
+ if (!maxsze) {
+ err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+ return -EINVAL;
+ }
+
dbg("uhci_submit_control start");
alloc_qh (&qh); // alloc qh for this request
@@ -615,26 +651,21 @@ _static int uhci_submit_control_urb (urb_t *urb)
insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
#if 0
- dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
- urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
- urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+ {
+ char *sp=urb->setup_packet;
+ dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+ sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+ }
//uhci_show_td(td);
#endif
- /* Build the DATA TD's */
len = urb->transfer_buffer_length;
- bytesrequested = len;
data = urb->transfer_buffer;
/* If direction is "send", change the frame from SETUP (0x2D)
to OUT (0xE1). Else change it from SETUP to IN (0x69). */
- destination &= ~UHCI_PID;
-
- if (usb_pipeout (urb->pipe))
- destination |= USB_PID_OUT;
- else
- destination |= USB_PID_IN;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
while (len > 0) {
int pktsze = len;
@@ -664,7 +695,7 @@ _static int uhci_submit_control_urb (urb_t *urb)
destination &= ~UHCI_PID;
- if (usb_pipeout (urb->pipe) || (bytesrequested == 0))
+ if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
@@ -690,32 +721,34 @@ _static int uhci_submit_control_urb (urb_t *urb)
urb->status = -EINPROGRESS;
queue_urb (s, urb); // queue before inserting in desc chain
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
//uhci_show_queue(qh);
/* Start it up... put low speed first */
if (urb->pipe & TD_CTRL_LS)
- insert_qh (s, s->control_chain, qh, 1); // insert after control chain
+ insert_qh (s, s->control_chain, qh, 0);
else
- insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
- //uhci_show_queue(qh);
+ insert_qh (s, s->bulk_chain, qh, 0);
dbg("uhci_submit_control end");
return 0;
}
/*-------------------------------------------------------------------*/
-_static int uhci_submit_bulk_urb (urb_t *urb)
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
- uhci_desc_t *qh, *td;
+ uhci_desc_t *qh, *td, *nqh, *bqh;
unsigned long destination, status;
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
-
+ urb_priv_t *upriv, *bpriv;
if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
return -EPIPE;
@@ -727,12 +760,55 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
if (!maxsze)
return -EMSGSIZE;
- /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+
+ queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+ urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
- alloc_qh (&qh); // get qh for this request
+ upriv=(urb_priv_t*)urb->hcpriv;
- if (!qh)
- return -ENOMEM;
+ if (!bulk_urb) {
+ alloc_qh (&qh); // get qh for this request
+
+ if (!qh)
+ return -ENOMEM;
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh(&nqh); // placeholder for clean unlink
+ if (!nqh) {
+ delete_desc (qh);
+ return -ENOMEM;
+ }
+ upriv->next_qh = nqh;
+ queue_dbg("new next qh %p",nqh);
+ }
+ }
+ else {
+ bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+ qh = bpriv->bottom_qh; // re-use bottom qh and next qh
+ nqh = bpriv->next_qh;
+ upriv->next_qh=nqh;
+ bpriv->next_queued_urb=urb;
+ upriv->prev_queued_urb=bulk_urb;
+ }
+
+ queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ alloc_qh (&bqh); // "bottom" QH,
+
+ if (!bqh) {
+ if (!bulk_urb) {
+ delete_desc(qh);
+ delete_desc(nqh);
+ }
+ return -ENOMEM;
+ }
+ bqh->hw.qh.element = UHCI_PTR_TERM;
+ bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+ upriv->bottom_qh = bqh;
+ queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+ }
+
/* The "pipe" thing contains the destination in bits 8--18. */
destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
@@ -744,7 +820,6 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
/* Build the TDs for the bulk request */
len = urb->transfer_buffer_length;
data = urb->transfer_buffer;
- dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
do { // TBD: Really allow zero-length packets?
int pktsze = len;
@@ -770,54 +845,149 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
if (!len)
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
- //dbg("insert td %p, len %i",td,pktsze);
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-
- /* Alternate Data0/1 (start with Data0) */
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
} while (len > 0);
list_add (&qh->desc_list, &urb_priv->desc_list);
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ qh->hw.qh.element&=~UHCI_PTR_TERM;
+ append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+ }
+
urb->status = -EINPROGRESS;
- queue_urb (s, urb);
+ queue_urb_unlocked (s, urb);
- qh->hw.qh.element&=~UHCI_PTR_TERM;
+ qh->hw.qh.element &= ~UHCI_PTR_TERM;
- insert_qh (s, s->chain_end, qh, 0); // insert before end marker
+ if (!bulk_urb) {
+ if (urb->transfer_flags & USB_QUEUE_BULK) {
+ spin_lock (&s->td_lock); // both QHs in one go
+ insert_qh (s, s->chain_end, qh, 0); // Main QH
+ insert_qh (s, s->chain_end, nqh, 0); // Helper QH
+ spin_unlock (&s->td_lock);
+ }
+ else
+ insert_qh (s, s->chain_end, qh, 0);
+ }
+
//uhci_show_queue(s->bulk_chain);
-
- dbg("uhci_submit_bulk_urb: exit");
+ //dbg("uhci_submit_bulk_urb: exit\n");
return 0;
}
-
/*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-_static int uhci_unlink_urb (urb_t *urb)
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
{
- uhci_t *s;
- uhci_desc_t *qh;
+ struct list_head *p;
uhci_desc_t *td;
- urb_priv_t *urb_priv;
- unsigned long flags=0;
+ for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ unlink_td (s, td, 1);
+ }
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
struct list_head *p;
+ uhci_desc_t *td;
- if (!urb || !urb->dev) // you never know...
- return -EINVAL;
+ while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+ td = list_entry (p, uhci_desc_t, desc_list);
+ list_del (p);
+ delete_desc (td);
+ }
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
- s = (uhci_t*) urb->dev->bus->hcpriv; // get pointer to uhci struct
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+ uhci_desc_t *bqh, *nqh, *prevqh;
+ int now;
+ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
- if (usb_pipedevice (urb->pipe) == s->rh.devnum)
- return rh_unlink_urb (urb);
+ now=UHCI_GET_CURRENT_FRAME(s);
- if (!urb->hcpriv) // you never know...
- return -EINVAL;
+ dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+ bqh=priv->bottom_qh;
- //dbg("unlink_urb called %p",urb);
+ if (!priv->next_queued_urb) { // no more appended bulk queues
+
+ if (mode != 2)
+ unlink_qh (s, qh);
+
+ if (priv->prev_queued_urb) {
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+ ppriv->bottom_qh = priv->bottom_qh;
+ ppriv->next_queued_urb = NULL;
+ }
+ else if (bqh) { // queue dead
+ nqh=priv->next_qh;
+
+ if (mode != 2)
+ unlink_qh(s, nqh);
+
+ if (mode) {
+ nqh->last_used = bqh->last_used = now;
+ list_add_tail (&nqh->horizontal, &s->free_desc);
+ list_add_tail (&bqh->horizontal, &s->free_desc);
+ }
+ }
+ }
+ else { // there are queued urbs following
+ urb_t *nurb;
+ unsigned long flags;
+
+ nurb=priv->next_queued_urb;
+ spin_lock_irqsave (&s->qh_lock, flags);
+
+ if (!priv->prev_queued_urb) { // top
+ if (mode !=2) {
+ prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);
+
+ list_del (&qh->horizontal);
+ list_add (&bqh->horizontal, &prevqh->horizontal);
+ }
+ }
+ else { //intermediate
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+ uhci_desc_t * bnqh;
+
+ bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+ ppriv->bottom_qh=bnqh;
+ ppriv->next_queued_urb=nurb;
+
+ if (mode!=2) {
+ prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+ prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+ queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);
+ }
+ }
+ mb();
+ spin_unlock_irqrestore (&s->qh_lock, flags);
+ ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+ }
+
+ if (mode) {
+ qh->last_used = now;
+ list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ }
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+ unsigned long flags=0;
spin_lock_irqsave (&s->urb_list_lock, flags);
@@ -833,32 +1003,22 @@ _static int uhci_unlink_urb (urb_t *urb)
switch (usb_pipetype (urb->pipe)) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
- for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
- td = list_entry (p, uhci_desc_t, desc_list);
- unlink_td (s, td, 1);
- }
- // wait at least 1 Frame
- uhci_wait_ms(1);
- while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
- td = list_entry (p, uhci_desc_t, desc_list);
- list_del (p);
- delete_desc (td);
- }
+ uhci_clean_iso_step1(s, urb_priv);
+ uhci_wait_ms(1);
+ uhci_clean_iso_step2(s, urb_priv);
break;
case PIPE_BULK:
case PIPE_CONTROL:
qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-
- unlink_qh (s, qh); // remove this qh from qh-list
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
- // wait at least 1 Frame
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ uhci_clean_transfer(s, urb, qh, 1);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
uhci_wait_ms(1);
}
#ifdef DEBUG_SLAB
- kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+ kmem_cache_free (urb_priv_kmem, urb->hcpriv);
#else
kfree (urb->hcpriv);
#endif
@@ -874,6 +1034,147 @@ _static int uhci_unlink_urb (urb_t *urb)
return 0;
}
/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+ struct list_head *q;
+ urb_t *urb;
+ struct usb_device *dev;
+ int pipe,now;
+ urb_priv_t *urb_priv;
+
+ q=s->urb_unlinked.next;
+ now=UHCI_GET_CURRENT_FRAME(s);
+
+ while (q != &s->urb_unlinked) {
+
+ urb = list_entry (q, urb_t, urb_list);
+
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+ q = urb->urb_list.next;
+
+ if (force ||
+ ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+ async_dbg("async cleanup %p",urb);
+ switch (usb_pipetype (urb->pipe)) { // process descriptors
+ case PIPE_CONTROL:
+ process_transfer (s, urb, 2);
+ break;
+ case PIPE_BULK:
+ if (!s->avoid_bulk.counter)
+ process_transfer (s, urb, 2); // don't unlink (already done)
+ else
+ continue;
+ break;
+ case PIPE_ISOCHRONOUS:
+ process_iso (s, urb, 1); // force, don't unlink
+ break;
+ case PIPE_INTERRUPT:
+ process_interrupt (s, urb);
+ break;
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ECONNRESET; // mark as asynchronously killed
+
+ pipe = urb->pipe; // completion may destroy all...
+ dev = urb->dev;
+ urb_priv = urb->hcpriv;
+
+ if (urb->complete) {
+ spin_unlock(&s->urb_list_lock);
+ urb->complete ((struct urb *) urb);
+ spin_lock(&s->urb_list_lock);
+ }
+
+ if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ENOENT; // now the urb is really dead
+
+ usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+ kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+ kfree (urb_priv);
+#endif
+ switch (usb_pipetype (pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step2(s, urb_priv);
+ break;
+ }
+ list_del (&urb->urb_list);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+
+ async_dbg("unlink_urb_async called %p",urb);
+
+ if (urb->status == -EINPROGRESS) {
+ ((urb_priv_t*)urb->hcpriv)->started = ~0;
+ dequeue_urb (s, urb);
+ list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+ s->unlink_urb_done = 1;
+
+ urb->status = -ECONNABORTED; // mark urb as "waiting to be killed"
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ uhci_clean_iso_step1 (s, urb_priv);
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+ uhci_clean_transfer (s, urb, qh, 0);
+ break;
+ }
+ ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ }
+
+ return -EINPROGRESS;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+ uhci_t *s;
+ unsigned long flags=0;
+ dbg("uhci_unlink_urb called for %p",urb);
+ if (!urb || !urb->dev) // you never know...
+ return -EINVAL;
+
+ s = (uhci_t*) urb->dev->bus->hcpriv;
+
+ if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+ return rh_unlink_urb (urb);
+
+ if (!urb->hcpriv)
+ return -EINVAL;
+
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ int ret;
+
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ ret = uhci_unlink_urb_async(s, urb);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ return ret;
+ }
+ else
+ return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
// In case of ASAP iso transfer, search the URB-list for already queued URBs
// for this EP and calculate the earliest start frame for the new
// URB (easy seamless URB continuation!)
@@ -886,9 +1187,9 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
unsigned long flags;
spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
+ p=s->urb_list.prev;
- for (; p != &s->urb_list; p = p->next) {
+ for (; p != &s->urb_list; p = p->prev) {
u = list_entry (p, urb_t, urb_list);
// look for pending URBs with identical pipe handle
// works only because iso doesn't toggle the data bit!
@@ -906,8 +1207,7 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
spin_unlock_irqrestore(&s->urb_list_lock, flags);
- return ret; // no previous urb found
-
+ return ret;
}
/*-------------------------------------------------------------------*/
// adjust start_frame according to scheduling constraints (ASAP etc)
@@ -940,35 +1240,7 @@ _static int iso_find_start (urb_t *urb)
info("iso_find_start: gap in seamless isochronous scheduling");
dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
now, urb->start_frame, urb->number_of_packets, urb->pipe);
-// The following code is only for debugging purposes...
-#if 0
- {
- uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
- struct list_head *p;
- urb_t *u;
- int a = -1, b = -1;
- unsigned long flags;
-
- spin_lock_irqsave (&s->urb_list_lock, flags);
- p=s->urb_list.next;
-
- for (; p != &s->urb_list; p = p->next) {
- u = list_entry (p, urb_t, urb_list);
- if (urb->dev != u->dev)
- continue;
- dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
- u->pipe, u->status, u->start_frame, u->number_of_packets);
- if (!usb_pipeisoc (u->pipe))
- continue;
- if (a == -1)
- a = u->start_frame;
- b = (u->start_frame + u->number_of_packets - 1) & 1023;
- }
- spin_unlock_irqrestore(&s->urb_list_lock, flags);
- }
-#endif
urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME!
- //return -EAGAIN; //FIXME
}
}
}
@@ -996,7 +1268,7 @@ _static int iso_find_start (urb_t *urb)
/*-------------------------------------------------------------------*/
// submits USB interrupt (ie. polling ;-)
// ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
+// if period==0, the the transfer is only done once
_static int uhci_submit_int_urb (urb_t *urb)
{
@@ -1005,12 +1277,9 @@ _static int uhci_submit_int_urb (urb_t *urb)
int nint, n, ret;
uhci_desc_t *td;
int status, destination;
- int now;
int info;
unsigned int pipe = urb->pipe;
- //dbg("SUBMIT INT");
-
if (urb->interval < 0 || urb->interval >= 256)
return -EINVAL;
@@ -1029,8 +1298,7 @@ _static int uhci_submit_int_urb (urb_t *urb)
dbg("Rounded interval to %i, chain %i", urb->interval, nint);
- now = UHCI_GET_CURRENT_FRAME (s) & 1023;
- urb->start_frame = now; // remember start frame, just in case...
+ urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case...
urb->number_of_packets = 1;
@@ -1062,13 +1330,6 @@ _static int uhci_submit_int_urb (urb_t *urb)
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-#if 0
- td = tdm[urb->number_of_packets];
- fill_td (td, TD_CTRL_IOC, 0, 0);
- insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td);
- list_add_tail (&td->desc_list, &urb_priv->desc_list);
-#endif
-
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1086,11 +1347,11 @@ _static int uhci_submit_iso_urb (urb_t *urb)
__save_flags(flags);
__cli(); // Disable IRQs to schedule all ISO-TDs in time
ret = iso_find_start (urb); // adjusts urb->start_frame for later use
-
+
if (ret)
goto err;
- tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
if (!tdm) {
ret = -ENOMEM;
@@ -1105,14 +1366,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
tdm[n] = 0;
continue;
}
- #ifdef ISO_SANITY_CHECK
+
if(urb->iso_frame_desc[n].length > maxsze) {
+#ifdef ISO_SANITY_CHECK
err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
tdm[n] = 0;
ret=-EINVAL;
goto inval;
+#endif
}
- #endif
+
ret = alloc_td (&td, UHCI_PTR_DEPTH);
inval:
if (ret) {
@@ -1120,7 +1383,7 @@ _static int uhci_submit_iso_urb (urb_t *urb)
for (i = 0; i < n; n++)
if (tdm[i])
- kfree (tdm[i]);
+ delete_desc(tdm[i]);
kfree (tdm);
goto err;
}
@@ -1165,15 +1428,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
}
/*-------------------------------------------------------------------*/
-_static int search_dev_ep (uhci_t *s, urb_t *urb)
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
{
- unsigned long flags;
struct list_head *p;
urb_t *tmp;
unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
dbg("search_dev_ep:");
- spin_lock_irqsave (&s->urb_list_lock, flags);
+
p=s->urb_list.next;
for (; p != &s->urb_list; p = p->next) {
@@ -1181,13 +1445,12 @@ _static int search_dev_ep (uhci_t *s, urb_t *urb)
dbg("urb: %p", tmp);
// we can accept this urb if it is not queued at this time
// or if non-iso transfer requests should be scheduled for the same device and pipe
- if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) ||
+ if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
(urb == tmp)) {
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
- return 1; // found another urb already queued for processing
+ return tmp; // found another urb already queued for processing
}
}
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1196,60 +1459,93 @@ _static int uhci_submit_urb (urb_t *urb)
uhci_t *s;
urb_priv_t *urb_priv;
int ret = 0;
-
+ unsigned long flags;
+ urb_t *bulk_urb=NULL;
+
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
s = (uhci_t*) urb->dev->bus->hcpriv;
//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-
+
+ if (!s->running)
+ return -ENODEV;
+
if (usb_pipedevice (urb->pipe) == s->rh.devnum)
return rh_submit_urb (urb); /* virtual root hub */
usb_inc_dev_use (urb->dev);
- if (search_dev_ep (s, urb)) {
- usb_dec_dev_use (urb->dev);
- return -ENXIO; // urb already queued
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+
+ bulk_urb = search_dev_ep (s, urb);
+ if (bulk_urb) {
+
+ queue_dbg("found bulk urb %p\n",bulk_urb);
+
+ if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+ ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+ (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ usb_dec_dev_use (urb->dev);
+ err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+ return -ENXIO; // urb already queued
+ }
}
#ifdef DEBUG_SLAB
- urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+ urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
#else
- urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+ urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
#endif
if (!urb_priv) {
usb_dec_dev_use (urb->dev);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
return -ENOMEM;
}
urb->hcpriv = urb_priv;
INIT_LIST_HEAD (&urb_priv->desc_list);
- urb_priv->short_control_packet=0;
+ urb_priv->short_control_packet = 0;
dbg("submit_urb: scheduling %p", urb);
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- ret = uhci_submit_iso_urb (urb);
- break;
- case PIPE_INTERRUPT:
- ret = uhci_submit_int_urb (urb);
- break;
- case PIPE_CONTROL:
- //dump_urb (urb);
- ret = uhci_submit_control_urb (urb);
- break;
- case PIPE_BULK:
- ret = uhci_submit_bulk_urb (urb);
- break;
- default:
- ret = -EINVAL;
+ urb_priv->next_queued_urb = NULL;
+ urb_priv->prev_queued_urb = NULL;
+ urb_priv->bottom_qh = NULL;
+ urb_priv->next_qh = NULL;
+
+ if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+
+ if (bulk_urb) {
+ while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb) // find last queued bulk
+ bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+
+ ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+ }
+ atomic_inc (&s->avoid_bulk);
+ ret = uhci_submit_bulk_urb (urb, bulk_urb);
+ atomic_dec (&s->avoid_bulk);
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ }
+ else {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ ret = uhci_submit_iso_urb (urb);
+ break;
+ case PIPE_INTERRUPT:
+ ret = uhci_submit_int_urb (urb);
+ break;
+ case PIPE_CONTROL:
+ ret = uhci_submit_control_urb (urb);
+ break;
+ default:
+ ret = -EINVAL;
+ }
}
dbg("submit_urb: scheduled with ret: %d", ret);
-
if (ret != 0) {
usb_dec_dev_use (urb->dev);
#ifdef DEBUG_SLAB
@@ -1262,41 +1558,43 @@ _static int uhci_submit_urb (urb_t *urb)
return 0;
}
-#ifdef USE_RECLAMATION_LOOP
-// Removes bandwidth reclamation if URB idles too long
-void check_idling_urbs(uhci_t *s)
+
+// Checks for URB timeout and removes bandwidth reclamation
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
{
struct list_head *p,*p2;
urb_t *urb;
int type;
- //dbg("check_idling_urbs: enter i:%d",in_interrupt());
-
- spin_lock (&s->urb_list_lock);
p = s->urb_list.prev;
while (p != &s->urb_list) {
+ urb_priv_t *hcpriv;
+
p2 = p;
p = p->prev;
- urb=list_entry (p2, urb_t, urb_list);
- type=usb_pipetype (urb->pipe);
-
-#if 0
- err("URB timers: %li now: %li %i\n",
- ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
- type);
-#endif
- if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
- (((urb_priv_t*)urb->hcpriv)->use_loop) &&
- ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
- disable_desc_loop(s,urb);
+ urb = list_entry (p2, urb_t, urb_list);
+ type = usb_pipetype (urb->pipe);
+
+ hcpriv = (urb_priv_t*)urb->hcpriv;
+
+ if ( urb->timeout &&
+ ((hcpriv->started + urb->timeout) < jiffies)) {
+ urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+ async_dbg("uhci_check_timeout: timeout for %p",urb);
+ uhci_unlink_urb_async(s, urb);
+ }
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
+ (hcpriv->use_loop) &&
+ ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+ disable_desc_loop(s, urb);
+#endif
}
- spin_unlock (&s->urb_list_lock);
-
- //dbg("check_idling_urbs: finished");
}
-#endif
+
/*-------------------------------------------------------------------
Virtual Root Hub
-------------------------------------------------------------------*/
@@ -1396,7 +1694,6 @@ _static int rh_send_irq (urb_t *urb)
dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
urb->complete (urb);
-
}
return 0;
}
@@ -1411,10 +1708,6 @@ _static void rh_int_timer_do (unsigned long ptr)
urb_t *urb = (urb_t*) ptr;
uhci_t *uhci = urb->dev->bus->hcpriv;
-#ifdef USE_RECLAMATION_LOOP
- check_idling_urbs(uhci);
-#endif
-
if (uhci->rh.send) {
len = rh_send_irq (urb);
if (len > 0) {
@@ -1427,7 +1720,9 @@ _static void rh_int_timer_do (unsigned long ptr)
}
/*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
_static int rh_init_int_timer (urb_t *urb)
{
uhci_t *uhci = urb->dev->bus->hcpriv;
@@ -1436,7 +1731,7 @@ _static int rh_init_int_timer (urb_t *urb)
init_timer (&uhci->rh.rh_int_timer);
uhci->rh.rh_int_timer.function = rh_int_timer_do;
uhci->rh.rh_int_timer.data = (unsigned long) urb;
- uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+ uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
add_timer (&uhci->rh.rh_int_timer);
return 0;
@@ -1640,7 +1935,6 @@ _static int rh_submit_urb (urb_t *urb)
stat = -EPIPE;
}
-
dbg("Root-Hub stat port1: %x port2: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
@@ -1716,13 +2010,19 @@ _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_
p = s->urb_list.prev;
while (p != &s->urb_list) {
p2 = p;
- p = p->prev;
+ p = p->prev ;
urb = list_entry (p2, urb_t, urb_list);
- dbg("urb: %p", urb);
+ dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+
+ //urb->transfer_flags |=USB_ASYNC_UNLINK;
+
if (remove_all || (usb_dev == urb->dev)) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
warn("forced removing of queued URB %p due to disconnect",urb);
uhci_unlink_urb(urb);
- urb->dev = NULL; // avoid further processing of this URB
+ urb->dev = NULL; // avoid further processing of this UR
+ spin_lock_irqsave (&s->urb_list_lock, flags);
+ p = s->urb_list.prev;
}
}
spin_unlock_irqrestore (&s->urb_list_lock, flags);
@@ -1732,13 +2032,11 @@ _static int uhci_free_dev (struct usb_device *usb_dev)
{
uhci_t *s;
- dbg("uhci_free_dev");
if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
return -EINVAL;
- s=(uhci_t*) usb_dev->bus->hcpriv;
-
+ s=(uhci_t*) usb_dev->bus->hcpriv;
uhci_unlink_urbs(s, usb_dev, 0);
return 0;
@@ -1769,12 +2067,10 @@ struct usb_operations uhci_device_operations =
* have announced. This leads to a queue abort due to the short packet,
* the status stage is not executed. If this happens, the status stage
* is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
+ * mode: 0: QHs already unlinked
*/
-_static int process_transfer (uhci_t *s, urb_t *urb)
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
{
int ret = 0;
urb_priv_t *urb_priv = urb->hcpriv;
@@ -1784,25 +2080,21 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle
-
-
- // extracted and remapped info from TD
- int maxlength;
+ int maxlength; // extracted and remapped info from TD
int actual_length;
int status = 0;
- dbg("process_transfer: urb contains bulk/control request");
-
+ //dbg("process_transfer: urb contains bulk/control request");
/* if the status phase has been retriggered and the
queue is empty or the last status-TD is inactive, the retriggered
status stage is completed
*/
-#if 1
+
if (urb_priv->short_control_packet &&
((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE))))
goto transfer_finished;
-#endif
+
urb->actual_length=0;
for (; p != &qh->vertical; p = p->next) {
@@ -1810,22 +2102,20 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs
return ret;
-
- // extract transfer parameters from TD
- actual_length = (desc->hw.td.status + 1) & 0x7ff;
+
+ actual_length = (desc->hw.td.status + 1) & 0x7ff; // extract transfer parameters from TD
maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
- // see if EP is stalled
- if (status == -EPIPE) {
+ if (status == -EPIPE) { // see if EP is stalled
// set up stalled condition
usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
}
- // if any error occured stop processing of further TDs
- if (status != 0) {
+ if (status != 0) { // if any error occured stop processing of further TDs
// only set ret if status returned an error
- uhci_show_td (desc);
+ if (status != -EPIPE)
+ uhci_show_td (desc);
ret = status;
urb->error_count++;
break;
@@ -1833,11 +2123,6 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
urb->actual_length += actual_length;
-#if 0
- // if (i++==0)
- uhci_show_td (desc); // show first TD of each transfer
-#endif
-
// got less data than requested
if ( (actual_length < maxlength)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
@@ -1852,8 +2137,8 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
- uhci_show_td (desc);
- uhci_show_td (last_desc);
+ //uhci_show_td (desc);
+ //uhci_show_td (last_desc);
urb_priv->short_control_packet=1;
return 0;
}
@@ -1864,34 +2149,24 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
}
data_toggle = uhci_toggle (desc->hw.td.info);
- //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
+ queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
}
+
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
- transfer_finished:
- unlink_qh (s, qh);
- //delete_qh (s, qh);
- qh->last_used=UHCI_GET_CURRENT_FRAME(s);
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ transfer_finished:
+
+ uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
urb->status = status;
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
disable_desc_loop(s,urb);
#endif
- dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
+ queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
- //dbg("process_transfer: exit");
-#if 0
- if (urb->actual_length){
- char *uu;
- uu=urb->transfer_buffer;
- dbg("%x %x %x %x %x %x %x %x",
- *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
- }
-#endif
return ret;
}
@@ -1934,15 +2209,13 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
// if any error occured: ignore this td, and continue
if (status != 0) {
- uhci_show_td (desc);
+ //uhci_show_td (desc);
urb->error_count++;
goto recycle;
}
else
urb->actual_length = actual_length;
- // FIXME: SPD?
-
recycle:
if (urb->complete) {
//dbg("process_interrupt: calling completion, status %i",status);
@@ -1962,6 +2235,7 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
if (status==0) {
+ ((urb_priv_t*)urb->hcpriv)->started=jiffies;
desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
@@ -1981,8 +2255,8 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
return ret;
}
-
-_static int process_iso (uhci_t *s, urb_t *urb)
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
{
int i;
int ret = 0;
@@ -1991,16 +2265,18 @@ _static int process_iso (uhci_t *s, urb_t *urb)
uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
dbg("urb contains iso request");
- if (desc->hw.td.status & TD_CTRL_ACTIVE)
+ if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
return -EXDEV; // last TD not finished
urb->error_count = 0;
urb->actual_length = 0;
urb->status = 0;
+ dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+ urb->number_of_packets,mode,desc->hw.td.status);
for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {
desc = list_entry (p, uhci_desc_t, desc_list);
-
+
//uhci_show_td(desc);
if (desc->hw.td.status & TD_CTRL_ACTIVE) {
// means we have completed the last TD, but not the TDs before
@@ -2013,7 +2289,8 @@ _static int process_iso (uhci_t *s, urb_t *urb)
goto err;
}
- unlink_td (s, desc, 1);
+ if (!mode)
+ unlink_td (s, desc, 1);
if (urb->number_of_packets <= i) {
dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
@@ -2038,13 +2315,14 @@ _static int process_iso (uhci_t *s, urb_t *urb)
urb->error_count++;
urb->status = urb->iso_frame_desc[i].status;
}
- dbg("process_iso: len:%d status:%x",
- urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status);
+ dbg("process_iso: %i: len:%d %08x status:%x",
+ i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
delete_desc (desc);
list_del (p);
}
- dbg("process_iso: exit %i (%d)", i, ret);
+
+ dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
return ret;
}
@@ -2056,15 +2334,20 @@ _static int process_urb (uhci_t *s, struct list_head *p)
urb=list_entry (p, urb_t, urb_list);
- dbg("found queued urb: %p", urb);
+ //dbg("process_urb: found queued urb: %p", urb);
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL:
+ ret = process_transfer (s, urb, 1);
+ break;
case PIPE_BULK:
- ret = process_transfer (s, urb);
+ if (!s->avoid_bulk.counter)
+ ret = process_transfer (s, urb, 1);
+ else
+ return 0;
break;
case PIPE_ISOCHRONOUS:
- ret = process_iso (s, urb);
+ ret = process_iso (s, urb, 0);
break;
case PIPE_INTERRUPT:
ret = process_interrupt (s, urb);
@@ -2158,23 +2441,18 @@ _static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
if (status != 1) {
warn("interrupt, status %x, frame# %i", status,
UHCI_GET_CURRENT_FRAME(s));
- //uhci_show_queue(s->control_chain);
+
// remove host controller halted state
if ((status&0x20) && (s->running)) {
- // more to be done - check TDs for invalid entries
- // but TDs are only invalid if somewhere else is a (memory ?) problem
outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
}
//uhci_show_status (s);
}
- //beep(1000);
/*
- * the following is very subtle and was blatantly wrong before
* traverse the list in *reverse* direction, because new entries
* may be added at the end.
* also, because process_urb may unlink the current urb,
* we need to advance the list before
- * - Thomas Sailer
*/
spin_lock (&s->urb_list_lock);
@@ -2186,18 +2464,23 @@ restart:
p2 = p;
p = p->prev;
process_urb (s, p2);
- if(s->unlink_urb_done)
- {
+ if (s->unlink_urb_done) {
s->unlink_urb_done=0;
goto restart;
}
}
- spin_unlock (&s->urb_list_lock);
- clean_descs(s,0);
+ if ((s->frame_counter & 63) == 0)
+ uhci_check_timeouts(s);
+ clean_descs(s,0);
+ uhci_cleanup_unlink(s, 0);
+
+ spin_unlock (&s->urb_list_lock);
+
+ s->frame_counter++;
outw (status, io_addr + USBSTS);
- dbg("done");
+ //dbg("uhci_interrupt: done");
}
_static void reset_hc (uhci_t *s)
@@ -2249,15 +2532,19 @@ _static void __exit uhci_cleanup_dev(uhci_t *s)
{
struct usb_device *root_hub = s->bus->root_hub;
+ s->running = 0; // Don't allow submit_urb
+
if (root_hub)
usb_disconnect (&root_hub);
- uhci_unlink_urbs(s, 0, 1); // Forced unlink of remaining URBs
+ reset_hc (s);
+ wait_ms (1);
+ uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs
+ uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs
+
usb_deregister_bus (s->bus);
- s->running = 0;
- reset_hc (s);
release_region (s->io_addr, s->io_size);
free_irq (s->irq, s);
usb_free_bus (s->bus);
@@ -2285,6 +2572,7 @@ _static int __init uhci_start_usb (uhci_t *s)
return 0;
}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
{
uhci_t *s = (uhci_t*) dev->data;
@@ -2301,6 +2589,7 @@ _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
}
return 0;
}
+#endif
_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
{
@@ -2315,14 +2604,17 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
memset (s, 0, sizeof (uhci_t));
INIT_LIST_HEAD (&s->free_desc);
INIT_LIST_HEAD (&s->urb_list);
+ INIT_LIST_HEAD (&s->urb_unlinked);
spin_lock_init (&s->urb_list_lock);
spin_lock_init (&s->qh_lock);
spin_lock_init (&s->td_lock);
+ atomic_set(&s->avoid_bulk, 0);
s->irq = -1;
s->io_addr = io_addr;
s->io_size = io_size;
s->next = devs; //chain new uhci device into global list
-
+ s->frame_counter = 0;
+
bus = usb_alloc_bus (&uhci_device_operations);
if (!bus) {
kfree (s);
@@ -2389,11 +2681,11 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
//chain new uhci device into global list
devs = s;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
if (pmdev)
pmdev->data = s;
-
+#endif
return 0;
}
@@ -2407,7 +2699,7 @@ _static int __init start_uhci (struct pci_dev *dev)
unsigned int io_addr = dev->resource[i].start;
unsigned int io_size =
dev->resource[i].end - dev->resource[i].start + 1;
- if (!(dev->resource[i].flags & IORESOURCE_IO))
+ if (!(dev->resource[i].flags & 1))
continue;
#else
unsigned int io_addr = dev->base_address[i];
@@ -2422,6 +2714,10 @@ _static int __init start_uhci (struct pci_dev *dev)
break;
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+ if(dev->vendor==0x8086) {
+ info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
+ }
return alloc_uhci(dev, dev->irq, io_addr, io_size);
}
return -1;
@@ -2452,6 +2748,9 @@ int __init uhci_init (void)
#endif
info(VERSTR);
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+ info("High bandwidth mode enabled");
+#endif
for (;;) {
dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
if (!dev)
@@ -2506,7 +2805,9 @@ int init_module (void)
void cleanup_module (void)
{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
pm_unregister_all (handle_pm_event);
+#endif
uhci_cleanup ();
}
diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h
index e74e23bd8..93173f2c2 100644
--- a/drivers/usb/usb-uhci.h
+++ b/drivers/usb/usb-uhci.h
@@ -2,10 +2,11 @@
#define __LINUX_UHCI_H
/*
- $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
+ $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $
*/
#define MODNAME "usb-uhci"
-#define VERSTR "version v1.184 time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__
+#define UHCI_LATENCY_TIMER 0
static __inline__ void uhci_wait_ms(unsigned int ms)
{
@@ -154,9 +155,13 @@ typedef struct {
typedef struct {
struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
- int short_control_packet;
unsigned long started;
- int use_loop;
+ urb_t *next_queued_urb; // next queued urb for this EP
+ urb_t *prev_queued_urb;
+ uhci_desc_t *bottom_qh;
+ uhci_desc_t *next_qh; // next helper QH
+ char use_loop;
+ char short_control_packet;
} urb_priv_t, *purb_priv_t;
struct virt_root_hub {
@@ -186,12 +191,14 @@ typedef struct uhci {
spinlock_t urb_list_lock; // lock to keep consistency
int unlink_urb_done;
+ atomic_t avoid_bulk;
struct usb_bus *bus; // our bus
__u32 *framelist;
uhci_desc_t **iso_td;
uhci_desc_t *int_chain[8];
+ uhci_desc_t *ls_control_chain;
uhci_desc_t *control_chain;
uhci_desc_t *bulk_chain;
uhci_desc_t *chain_end;
@@ -200,6 +207,9 @@ typedef struct uhci {
spinlock_t td_lock;
struct virt_root_hub rh; //private data of the virtual root hub
int loop_usage; // URBs using bandwidth reclamation
+
+ struct list_head urb_unlinked; // list of all unlinked urbs
+ int frame_counter;
} uhci_t, *puhci_t;
diff --git a/drivers/video/aty128.h b/drivers/video/aty128.h
index 7b6342e6a..d934d0c2d 100644
--- a/drivers/video/aty128.h
+++ b/drivers/video/aty128.h
@@ -264,6 +264,8 @@
/* DAC_CNTL bit constants */
#define DAC_8BIT_EN 0x00000100
#define DAC_MASK 0xFF000000
+#define DAC_BLANKING 0x00000004
+#define DAC_RANGE_CNTL 0x00000003
/* GEN_RESET_CNTL bit constants */
#define SOFT_RESET_GUI 0x00000001
diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c
index d7d4116c0..fcc0f8c5c 100644
--- a/drivers/video/aty128fb.c
+++ b/drivers/video/aty128fb.c
@@ -48,13 +48,13 @@
#include <linux/ioport.h>
#include <asm/io.h>
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <video/macmodes.h>
#ifdef CONFIG_NVRAM
#include <linux/nvram.h>
#endif
-#include <video/macmodes.h>
#endif
#ifdef CONFIG_FB_COMPAT_XPMAC
@@ -69,7 +69,7 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
-#endif /* CONFIG_MTRR */
+#endif
#include "aty128.h"
@@ -158,13 +158,18 @@ struct aty128_meminfo {
u8 LoopLatency;
u8 DspOn;
u8 Rloop;
+ const char *name;
};
/* various memory configurations */
-const struct aty128_meminfo sdr_128 = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 };
-const struct aty128_meminfo sdr_64 = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 };
-const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 };
-const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 };
+const struct aty128_meminfo sdr_128 =
+ { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_64 =
+ { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_sgram =
+ { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+const struct aty128_meminfo ddr_sgram =
+ { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
static int currcon = 0;
@@ -176,20 +181,23 @@ static unsigned int initdepth __initdata = 8;
#ifndef MODULE
static const char *mode_option __initdata = NULL;
#endif
+#ifndef CONFIG_PPC
+static void *bios_seg = NULL;
+#endif
#ifdef CONFIG_PPC
#ifdef CONFIG_NVRAM_NOT_DEFINED
+static int default_vmode __initdata = VMODE_640_480_60;
+static int default_cmode __initdata = CMODE_8;
+#else
static int default_vmode __initdata = VMODE_NVRAM;
static int default_cmode __initdata = CMODE_NVRAM;
-#else
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_8;
#endif
#endif
#ifdef CONFIG_MTRR
static int mtrr = 1;
-#endif /* CONFIG_MTRR */
+#endif
/* PLL constants */
struct aty128_constants {
@@ -236,17 +244,13 @@ struct aty128fb_par {
struct fb_info_aty128 {
struct fb_info fb_info;
struct fb_info_aty128 *next;
- struct aty128_constants constants;
- unsigned long regbase_phys; /* mmio */
- unsigned long frame_buffer_phys; /* framebuffer memory */
+ struct aty128_constants constants; /* PLL and others */
+ unsigned long regbase_phys; /* physical mmio */
+ void *regbase; /* remapped mmio */
+ unsigned long frame_buffer_phys; /* physical fb memory */
unsigned long frame_buffer; /* remaped framebuffer */
- void *regbase;
const struct aty128_meminfo *mem; /* onboard mem info */
u32 vram_size; /* onboard video ram */
-#ifndef CONFIG_PPC
- void *bios_seg; /* video BIOS segment */
-#endif
- unsigned short card_revision; /* video card revision */
struct aty128fb_par default_par, current_par;
struct display disp;
struct display_switch dispsw; /* for cursor and font */
@@ -268,7 +272,7 @@ struct fb_info_aty128 {
#endif
#ifdef CONFIG_MTRR
struct { int vram; int vram_valid; } mtrr;
-#endif /* CONFIG_MTRR */
+#endif
};
static struct fb_info_aty128 *board_list = NULL;
@@ -321,12 +325,18 @@ static int aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info);
static void do_install_cmap(int con, struct fb_info *info);
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+ const struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par,
+ const struct fb_info_aty128 *info);
static int aty128_pci_register(struct pci_dev *pdev,
const struct aty128_chip_info *aci);
static struct fb_info_aty128 *aty128_board_list_add(struct fb_info_aty128
*board_list, struct fb_info_aty128 *new_node);
-#ifndef CONFIG_PPC
static int aty128find_ROM(struct fb_info_aty128 *info);
+#ifndef CONFIG_PPC
static void aty128_get_pllinfo(struct fb_info_aty128 *info);
#endif
static void aty128_timings(struct fb_info_aty128 *info);
@@ -513,7 +523,7 @@ aty_pll_writeupdate(const struct fb_info_aty128 *info)
/* write to the scratch register to test r/w functionality */
-static u32
+static int __init
register_test(const struct fb_info_aty128 *info)
{
u32 val, flag = 0;
@@ -720,6 +730,9 @@ aty128_set_crtc(const struct aty128_crtc *crtc,
aty_st_le32(CRTC_PITCH, crtc->pitch);
aty_st_le32(CRTC_OFFSET, crtc->offset);
aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+
+ /* Disable ATOMIC updating. Is this the right place? */
+ aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
}
@@ -818,7 +831,7 @@ aty128_var_to_crtc(const struct fb_var_screeninfo *var,
c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
- crtc->gen_cntl = 0x03000000L | c_sync | (depth << 8);
+ crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
crtc->h_total = h_total | (h_disp << 16);
crtc->v_total = v_total | (v_disp << 16);
@@ -896,7 +909,7 @@ aty128_bpp_to_var(int pix_width, struct fb_var_screeninfo *var)
break;
default:
printk(KERN_ERR "Invalid pixel width\n");
- return -1;
+ return -EINVAL;
}
return 0;
@@ -913,31 +926,31 @@ aty128_crtc_to_var(const struct aty128_crtc *crtc,
u32 pix_width;
/* fun with masking */
- h_total = crtc->h_total & 0x1ff;
- h_disp = (crtc->h_total>>16) & 0xff;
+ h_total = crtc->h_total & 0x1ff;
+ h_disp = (crtc->h_total>>16) & 0xff;
h_sync_strt = (crtc->h_sync_strt_wid>>3) & 0x1ff;
- h_sync_dly = crtc->h_sync_strt_wid & 0x7;
- h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
- h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
- v_total = crtc->v_total & 0x7ff;
- v_disp = (crtc->v_total>>16) & 0x7ff;
+ h_sync_dly = crtc->h_sync_strt_wid & 0x7;
+ h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
+ h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
+ v_total = crtc->v_total & 0x7ff;
+ v_disp = (crtc->v_total>>16) & 0x7ff;
v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
- v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
- v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
- c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
- pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
-
- xres = (h_disp+1)*8;
- yres = v_disp+1;
- left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
+ v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
+ v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
+ c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+ pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+ xres = (h_disp+1) << 3;
+ yres = v_disp+1;
+ left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
right = (h_sync_strt-h_disp)*8+h_sync_dly;
hslen = h_sync_wid*8;
upper = v_total-v_sync_strt-v_sync_wid;
lower = v_sync_strt-v_disp;
vslen = v_sync_wid;
- sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
- (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
- (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+ sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+ (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+ (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
aty128_bpp_to_var(pix_width, var);
@@ -1007,7 +1020,7 @@ aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
const struct fb_info_aty128 *info)
{
const struct aty128_constants c = info->constants;
- unsigned char post_dividers [] = {1,2,4,8,3,6,12};
+ unsigned char post_dividers[] = {1,2,4,8,3,6,12};
u32 output_freq;
u32 vclk; /* in .01 MHz */
int i;
@@ -1110,6 +1123,7 @@ aty128_ddafifo(struct aty128_ddafifo *dsp,
n <<= (11 - p);
x = round_div(n, d);
roff = x * (fifo_depth - 4);
+
if ((ron + m->Rloop) >= roff) {
printk(KERN_ERR "aty128fb: Mode out of range!\n");
return -EINVAL;
@@ -1134,7 +1148,14 @@ aty128_set_par(struct aty128fb_par *par,
struct fb_info_aty128 *info)
{
u32 config;
-
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* enable this when macmodes gets updated */
+ struct vc_mode disp_info;
+#endif
+ struct fb_var_screeninfo var;
+ int cmode, vmode;
+#endif
+
info->current_par = *par;
if (info->blitter_may_be_busy)
@@ -1174,6 +1195,46 @@ aty128_set_par(struct aty128fb_par *par,
if (par->accel_flags & FB_ACCELF_TEXT)
aty128_init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* use this when macmodes gets updated */
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ disp_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ disp_info.depth = par->crtc.bpp;
+ disp_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ disp_info.mode = 0;
+ else
+ disp_info.mode = vmode;
+ strcpy(disp_info.name, aty128fb_name);
+ disp_info.fb_address = info->frame_buffer_phys;
+ disp_info.cmap_adr_address = 0;
+ disp_info.cmap_data_address = 0;
+ disp_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(disp_info);
+ }
+#else
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
+ display_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+ display_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+ display_info.depth = par->crtc.bpp;
+ display_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+ aty128_encode_var(&var, par, info);
+ if (mac_var_to_vmode(&var, &vmode, &cmode))
+ display_info.mode = 0;
+ else
+ display_info.mode = vmode;
+ strcpy(display_info.name, aty128fb_name);
+ display_info.fb_address = info->frame_buffer_phys;
+ display_info.cmap_adr_address = 0;
+ display_info.cmap_data_address = 0;
+ display_info.disp_reg_address = info->regbase_phys;
+ register_compat_xpmac(display_info);
+ }
+#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
}
@@ -1293,16 +1354,23 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
var->xres_virtual = var->xres;
if (var->yres > var->yres_virtual)
var->yres_virtual = var->yres;
- if (var->bits_per_pixel <= 8)
- var->bits_per_pixel = 8;
- else if (var->bits_per_pixel <= 16)
- var->bits_per_pixel = 16;
- else if (var->bits_per_pixel <= 24)
- var->bits_per_pixel = 24;
- else if (var->bits_per_pixel <= 32)
- var->bits_per_pixel = 32;
- else
- return -EINVAL;
+
+ switch (var->bits_per_pixel) {
+ case 0 ... 8:
+ var->bits_per_pixel = 8;
+ break;
+ case 9 ... 16:
+ var->bits_per_pixel = 16;
+ break;
+ case 17 ... 24:
+ var->bits_per_pixel = 24;
+ break;
+ case 25 ... 32:
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
if ((err = aty128_decode_var(var, &par, info)))
return err;
@@ -1357,26 +1425,6 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
do_install_cmap(con, &info->fb_info);
}
-#ifdef CONFIG_FB_COMPAT_XPMAC
- if (!console_fb_info || console_fb_info == &info->fb_info) {
- int vmode, cmode;
-
- display_info.width = var->xres;
- display_info.height = var->yres;
- display_info.depth = var->bits_per_pixel;
- display_info.pitch = (var->xres_virtual)*(var->bits_per_pixel)/8;
- if (mac_var_to_vmode(var, &vmode, &cmode))
- display_info.mode = 0;
- else
- display_info.mode = vmode;
- strcpy(info->fb_info.modename, aty128fb_name);
- display_info.fb_address = info->frame_buffer_phys;
- display_info.cmap_adr_address = 0;
- display_info.cmap_data_address = 0;
- display_info.disp_reg_address = info->regbase_phys;
- }
-#endif
-
return 0;
}
@@ -1435,14 +1483,14 @@ aty128_encode_fix(struct fb_fix_screeninfo *fix,
fix->smem_len = (u32)info->vram_size;
fix->mmio_len = 0x1fff;
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->type_aux = 0;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
- fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
- : FB_VISUAL_DIRECTCOLOR;
+ fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+ : FB_VISUAL_DIRECTCOLOR;
fix->ywrapstep = 0;
- fix->xpanstep = 8;
- fix->ypanstep = 1;
+ fix->xpanstep = 8;
+ fix->ypanstep = 1;
fix->accel = FB_ACCEL_ATI_RAGE128;
@@ -1540,6 +1588,7 @@ aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
disp = &fb_display[con];
else
disp = info->disp;
+
if (!disp->cmap.len) { /* no colormap allocated? */
int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
@@ -1614,7 +1663,7 @@ aty128fb_setup(char *options)
mtrr = 0;
}
#endif /* CONFIG_MTRR */
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
/* vmode and cmode depreciated */
else if (!strncmp(this_opt, "vmode:", 6)) {
unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
@@ -1660,27 +1709,22 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
const struct aty128_chip_info *aci = &aty128_pci_probe_list[0];
char *video_card = "Rage128";
- if (!register_test(info)) {
- printk(KERN_ERR "aty128fb: Can't write to video registers\n");
- return 0;
- }
-
if (!info->vram_size) /* may have already been probed */
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ /* Get the chip revision */
chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
/* put a name with the face */
while (aci->name && info->pdev->device != aci->device) { aci++; }
video_card = (char *)aci->name;
- printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
- video_card, chip_rev, info->card_revision);
+ printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
if (info->vram_size % (1024 * 1024) == 0)
- printk("%dM\n", info->vram_size / (1024*1024));
+ printk("%dM %s\n", info->vram_size / (1024*1024), info->mem->name);
else
- printk("%dk\n", info->vram_size / 1024);
+ printk("%dk %s\n", info->vram_size / 1024, info->mem->name);
/* fill in info */
strcpy(info->fb_info.modename, aty128fb_name);
@@ -1697,32 +1741,34 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
var = default_var;
#else
memset(&var, 0, sizeof(var));
+#ifdef CONFIG_FB_COMPAT_XPMAC /* CONFIG_PPC implied */
+ if (_machine == _MACH_Pmac) {
+ if (mode_option) {
+ if (!mac_find_mode(&var, &info->fb_info, mode_option, 8))
+ var = default_var;
+ } else {
#ifdef CONFIG_NVRAM
- if (default_vmode == VMODE_NVRAM) {
- default_vmode = nvram_read_byte(NV_VMODE);
- if (default_vmode <= 0 || default_vmode > VMODE_MAX)
- default_vmode = VMODE_CHOOSE;
- }
-#endif
-#ifdef CONFIG_PPC
- if (default_vmode == VMODE_CHOOSE) {
- var = default_var;
-#endif /* CONFIG_PPC */
+ if (default_vmode == VMODE_NVRAM)
+ default_vmode = nvram_read_byte(NV_VMODE);
- if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
- &defaultmode, initdepth))
- var = default_var;
-
-#ifdef CONFIG_PPC
-#ifdef CONFIG_NVRAM
- if (default_cmode == CMODE_NVRAM)
- default_cmode = nvram_read_byte(NV_CMODE);
+ if (default_cmode == CMODE_NVRAM)
+ default_cmode = nvram_read_byte(NV_CMODE);
#endif
- } else if (_machine == _MACH_Pmac)
- if (mac_vmode_to_var(default_vmode, default_cmode, &var))
- var = default_var;
+ if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+ default_vmode = VMODE_640_480_60;
-#endif
+ if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+ default_cmode = CMODE_8;
+
+ if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+ var = default_var;
+ }
+ }
+#else
+ if (fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
+ &defaultmode, initdepth) == 0)
+ var = default_var;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
#endif /* MODULE */
if (noaccel)
@@ -1743,8 +1789,8 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
info->palette[j].blue = default_blu[k];
}
- dac = aty_ld_le32(DAC_CNTL) & 15; /* preserve lower three bits */
- dac |= DAC_8BIT_EN; /* set 8 bit dac */
+ dac = aty_ld_le32(DAC_CNTL);
+ dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
dac |= DAC_MASK; /* set DAC mask */
aty_st_le32(DAC_CNTL, dac);
@@ -1845,40 +1891,51 @@ aty128_pci_register(struct pci_dev *pdev,
}
memset(info, 0, sizeof(struct fb_info_aty128));
+ /* Copy PCI device info into info->pdev */
info->pdev = pdev;
+ /* Virtualize mmio region */
info->regbase_phys = reg_addr;
info->regbase = ioremap(reg_addr, 0x1FFF);
if (!info->regbase)
goto err_out;
- pci_read_config_word(pdev, 0x08, &tmp);
- info->card_revision = tmp;
-
info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+ pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+ if (!(tmp & PCI_COMMAND_MEMORY)) {
+ tmp |= PCI_COMMAND_MEMORY;
+ pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ }
+
+ /* Virtualize the framebuffer */
info->frame_buffer_phys = fb_addr;
info->frame_buffer = (unsigned long)ioremap(fb_addr, info->vram_size);
if (!info->frame_buffer)
goto err_out;
- pci_read_config_word(pdev, PCI_COMMAND, &tmp);
- if (!(tmp & PCI_COMMAND_MEMORY)) {
- tmp |= PCI_COMMAND_MEMORY;
- pci_write_config_word(pdev, PCI_COMMAND, tmp);
+ /* If we can't test scratch registers, something is seriously wrong */
+ if (!register_test(info)) {
+ printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+ goto err_out;
}
-#if defined(CONFIG_PPC)
- aty128_timings(info);
-#else
if (!aty128find_ROM(info)) {
- printk(KERN_INFO "Rage128 BIOS not located. Guessing...\n");
+ printk(KERN_INFO "aty128fb: Rage128 BIOS not located. Guessing...\n");
aty128_timings(info);
}
- else
+#ifndef CONFIG_PPC
+ else
aty128_get_pllinfo(info);
+
+ /* free up to-be unused resources. bios_seg is mapped by
+ * aty128find_ROM() and used by aty128_get_pllinfo()
+ *
+ * TODO: make more elegant. doesn't need to be global */
+ if (bios_seg)
+ iounmap(bios_seg);
#endif
#ifdef CONFIG_MTRR
if (mtrr) {
@@ -1910,27 +1967,26 @@ unmap_out:
#endif /* CONFIG_PCI */
-#ifndef CONFIG_PPC
+/* PPC cannot read video ROM, so we fail by default */
static int __init
aty128find_ROM(struct fb_info_aty128 *info)
{
- u32 segstart;
+ int flag = 0;
+#ifndef CONFIG_PPC
+ u32 segstart;
char *rom_base;
- char *rom_base1;
char *rom;
- int stage;
- int i;
+ int stage;
+ int i;
char aty_rom_sig[] = "761295520"; /* ATI ROM Signature */
char R128_sig[] = "R128"; /* Rage128 ROM identifier */
- int flag = 0;
for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
stage = 1;
rom_base = (char *) ioremap(segstart, 0x1000);
- rom_base1 = (char *) (rom_base+1);
- if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa))
+ if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
stage = 2;
if (stage != 2) {
@@ -1967,16 +2023,18 @@ aty128find_ROM(struct fb_info_aty128 *info)
continue;
}
+ bios_seg = rom_base;
printk(KERN_INFO "aty128fb: Rage128 BIOS located at segment %4.4X\n",
- (u32)rom_base);
- info->bios_seg = rom_base;
+ (unsigned int)rom_base);
flag = 1;
break;
}
+#endif /* !CONFIG_PPC */
return (flag);
}
+#ifndef CONFIG_PPC
static void __init
aty128_get_pllinfo(struct fb_info_aty128 *info)
{
@@ -1985,16 +2043,16 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
u16 bios_header_offset, pll_info_offset;
PLL_BLOCK pll;
- bios_header = info->bios_seg + 0x48L;
- header_ptr = bios_header;
+ bios_header = bios_seg + 0x48L;
+ header_ptr = bios_header;
bios_header_offset = readw(header_ptr);
- bios_header = info->bios_seg + bios_header_offset;
+ bios_header = bios_seg + bios_header_offset;
bios_header += 0x30;
header_ptr = bios_header;
pll_info_offset = readw(header_ptr);
- header_ptr = info->bios_seg + pll_info_offset;
+ header_ptr = bios_seg + pll_info_offset;
memcpy_fromio(&pll, header_ptr, 50);
@@ -2032,10 +2090,6 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
info->mem = &sdr_sgram;
}
- /* free up to-be unused resources */
- if (info->bios_seg)
- iounmap(info->bios_seg);
-
return;
}
#endif /* ! CONFIG_PPC */
@@ -2056,7 +2110,7 @@ aty128_timings(struct fb_info_aty128 *info)
info->constants.ppll_max = 25000; /* 23000 on some cards? */
#if 1
- /* XXX TODO. Calculuate properly. Fix OF's pll ideas. */
+ /* XXX TODO. Calculuate properly. */
if (!info->constants.ref_divider)
info->constants.ref_divider = 0x3b;
aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider);
@@ -2473,10 +2527,9 @@ static struct display_switch fbcon_aty128_32 = {
};
#endif
-#if defined(MODULE)
-MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>, Anthony Tong "
- "<atong@uiuc.edu>");
-MODULE_DESCRIPTION("FBDev driver for ATI Rage128 cards");
+#ifdef MODULE
+MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
int __init
init_module(void)
diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c
index f7fe19965..b2efe721f 100644
--- a/drivers/video/atyfb.c
+++ b/drivers/video/atyfb.c
@@ -1,4 +1,4 @@
-/* $Id: atyfb.c,v 1.140 2000/02/25 05:46:27 davem Exp $
+/* $Id: atyfb.c,v 1.141 2000/03/12 03:53:16 davem Exp $
* linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
*
* Copyright (C) 1997-1998 Geert Uytterhoeven
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 0a227193b..b51014eea 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -32,6 +32,7 @@
#include <video/fbcon-cfb24.h>
#define MMIO_SIZE 0x000c0000
+/*#define CFB16_IS_CFB15*/
static char *CyberRegs;
@@ -275,18 +276,8 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
#endif
#ifdef FBCON_HAS_CFB16
- case 15:
- if (regno < 32) {
- cyber2000_outb(regno << 3, 0x3c8);
- cyber2000_outb(red, 0x3c9);
- cyber2000_outb(green, 0x3c9);
- cyber2000_outb(blue, 0x3c9);
- }
- if (regno < 16)
- current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
- break;
-
case 16:
+#ifndef CFB16_IS_CFB15
if (regno < 64) {
/* write green */
cyber2000_outb(regno << 2, 0x3c8);
@@ -308,6 +299,19 @@ cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
break;
#endif
+ case 15:
+ if (regno < 32) {
+ cyber2000_outb(regno << 3, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+ }
+ if (regno < 16)
+ current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+ break;
+
+#endif
+
#ifdef FBCON_HAS_CFB24
case 24:
cyber2000_outb(regno, 0x3c8);
@@ -345,6 +349,7 @@ struct par_info {
* Other
*/
unsigned int visual;
+ unsigned char palette_ctrl;
};
static const char crtc_idx[] = {
@@ -418,7 +423,7 @@ static void cyber2000fb_set_timing(struct par_info *hw)
cyber2000_outb(0x56, 0x3ce);
i = cyber2000_inb(0x3cf);
cyber2000_outb(i | 4, 0x3cf);
- cyber2000_outb(0x04, 0x3c6);
+ cyber2000_outb(hw->palette_ctrl, 0x3c6);
cyber2000_outb(i, 0x3cf);
cyber2000_outb(0x20, 0x3c0);
@@ -776,37 +781,76 @@ cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, struct par_info *
int err;
hw->width = var->xres_virtual;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+
switch (var->bits_per_pixel) {
#ifdef FBCON_HAS_CFB8
case 8: /* PSEUDOCOLOUR, 256 */
- hw->visual = FB_VISUAL_PSEUDOCOLOR;
- hw->pixformat = PIXFORMAT_8BPP;
- hw->visualid = VISUALID_256;
- hw->pitch = hw->width >> 3;
+ var->bits_per_pixel = 8;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ hw->visual = FB_VISUAL_PSEUDOCOLOR;
+ hw->pixformat = PIXFORMAT_8BPP;
+ hw->visualid = VISUALID_256;
+ hw->pitch = hw->width >> 3;
+ hw->palette_ctrl = 0x04;
break;
#endif
#ifdef FBCON_HAS_CFB16
+ case 16:/* DIRECTCOLOUR, 64k */
+#ifndef CFB16_IS_CFB15
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ hw->visual = FB_VISUAL_DIRECTCOLOR;
+ hw->pixformat = PIXFORMAT_16BPP;
+ hw->visualid = VISUALID_64K;
+ hw->pitch = hw->width >> 2;
+ hw->palette_ctrl = 0x14;
+ break;
+#endif
case 15:/* DIRECTCOLOUR, 32k */
- hw->visual = FB_VISUAL_DIRECTCOLOR;
- hw->pixformat = PIXFORMAT_16BPP;
- hw->visualid = VISUALID_32K;
- hw->pitch = hw->width >> 2;
+ var->bits_per_pixel = 15;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ hw->visual = FB_VISUAL_DIRECTCOLOR;
+ hw->pixformat = PIXFORMAT_16BPP;
+ hw->visualid = VISUALID_32K;
+ hw->pitch = hw->width >> 2;
+ hw->palette_ctrl = 0x14;
break;
- case 16:/* DIRECTCOLOUR, 64k */
- hw->visual = FB_VISUAL_DIRECTCOLOR;
- hw->pixformat = PIXFORMAT_16BPP;
- hw->visualid = VISUALID_64K;
- hw->pitch = hw->width >> 2;
- break;
#endif
#ifdef FBCON_HAS_CFB24
case 24:/* TRUECOLOUR, 16m */
- hw->visual = FB_VISUAL_TRUECOLOR;
- hw->pixformat = PIXFORMAT_24BPP;
- hw->visualid = VISUALID_16M;
- hw->width *= 3;
- hw->pitch = hw->width >> 3;
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ hw->visual = FB_VISUAL_TRUECOLOR;
+ hw->pixformat = PIXFORMAT_24BPP;
+ hw->visualid = VISUALID_16M;
+ hw->width *= 3;
+ hw->pitch = hw->width >> 3;
+ hw->palette_ctrl = 0x14;
break;
#endif
default:
diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c
index 8da8117c9..aedbc8b17 100644
--- a/drivers/video/fbcon.c
+++ b/drivers/video/fbcon.c
@@ -111,10 +111,6 @@
#define LOGO_W 80
#define LOGO_LINE (LOGO_W/8)
-static int first_fb_vc = 0;
-static int last_fb_vc = MAX_NR_CONSOLES-1;
-static int fbcon_is_default = 1;
-
struct display fb_display[MAX_NR_CONSOLES];
char con2fb_map[MAX_NR_CONSOLES];
static int logo_lines;
@@ -132,8 +128,6 @@ static int softback_lines;
#define FNTSUM(fd) (((int *)(fd))[-4])
#define FONT_EXTRA_WORDS 4
-static char fontname[40] __initdata = { 0 }; /* default font name */
-
#define CM_SOFTBACK (8)
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
@@ -203,8 +197,7 @@ static int fbcon_scrolldelta(struct vc_data *conp, int lines);
* Internal routines
*/
-static int __init fbcon_setup(char *options);
-static void fbcon_set_disp(int con, int init, int logo);
+static void fbcon_setup(int con, int init, int logo);
static __inline__ int real_y(struct display *p, int ypos);
static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
static __inline__ void updatescrollmode(struct display *p);
@@ -300,8 +293,6 @@ void set_con2fb_map(int unit, int newidx)
newfb = registered_fb[newidx];
if (newfb->fbops->fb_open(newfb,0))
return;
- newfb->count++;
- oldfb->count--;
oldfb->fbops->fb_release(oldfb,0);
conp = fb_display[unit].conp;
fontdata = fb_display[unit].fontdata;
@@ -332,57 +323,6 @@ void set_con2fb_map(int unit, int newidx)
}
}
-static int __init fbcon_setup(char *options)
-{
- int i, j;
-
- if (!options || !*options)
- return 0;
-
- if (!strncmp(options, "font:", 5))
- strcpy(fontname, options+5);
-
- if (!strncmp(options, "scrollback:", 11)) {
- options += 11;
- if (*options) {
- fbcon_softback_size = simple_strtoul(options, &options, 0);
- if (*options == 'k' || *options == 'K') {
- fbcon_softback_size *= 1024;
- options++;
- }
- if (*options != ',')
- return 0;
- options++;
- } else
- return 0;
- }
-
- if (!strncmp(options, "map:", 4)) {
- options += 4;
- if (*options)
- for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
- if (!options[j])
- j = 0;
- con2fb_map[i] = (options[j++]-'0') % FB_MAX;
- }
- return 0;
- }
-
- if (!strncmp(options, "vc:", 3)) {
- options += 3;
- if (*options)
- first_fb_vc = simple_strtoul(options, &options, 10) - 1;
- if (first_fb_vc < 0)
- first_fb_vc = 0;
- if (*options++ == '-')
- last_fb_vc = simple_strtoul(options, &options, 10) - 1;
- fbcon_is_default = 0;
- }
- return 0;
-}
-
-__setup("fbcon=", fbcon_setup);
-
/*
* Low Level Operations
*/
@@ -479,23 +419,15 @@ static const char *fbcon_startup(void)
return display_desc;
}
+
static void fbcon_init(struct vc_data *conp, int init)
{
- int j, unit = conp->vc_num;
+ int unit = conp->vc_num;
struct fb_info *info;
-
+
/* on which frame buffer will we open this console? */
info = registered_fb[(int)con2fb_map[unit]];
- /*
- * We assume initial frame buffer devices can be opened this
- * many times
- */
- for (j = 0; j < (last_fb_vc - first_fb_vc + 1); j++) {
- info->fbops->fb_open(info,0);
- info->count++;
- }
-
info->changevar = &fbcon_changevar;
fb_display[unit] = *(info->disp); /* copy from default */
DPRINTK("mode: %s\n",info->modename);
@@ -511,8 +443,8 @@ static void fbcon_init(struct vc_data *conp, int init)
fb_display[unit].cmap.green = 0;
fb_display[unit].cmap.blue = 0;
fb_display[unit].cmap.transp = 0;
- fbcon_set_disp(unit, init, !init);
- /* Must be done after fbcon_set_disp to prevent excess updates */
+ fbcon_setup(unit, init, !init);
+ /* Must be done after fbcon_setup to prevent excess updates */
conp->vc_display_fg = &info->display_fg;
if (!info->display_fg)
info->display_fg = conp;
@@ -526,7 +458,6 @@ static void fbcon_deinit(struct vc_data *conp)
fbcon_free_font(p);
p->dispsw = &fbcon_dummy;
- p->fb_info->count = 0;
p->conp = 0;
}
@@ -534,7 +465,7 @@ static void fbcon_deinit(struct vc_data *conp)
static int fbcon_changevar(int con)
{
if (fb_display[con].conp)
- fbcon_set_disp(con, 0, 0);
+ fbcon_setup(con, 0, 0);
return 0;
}
@@ -573,7 +504,7 @@ static void fbcon_font_widths(struct display *p)
#define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
-static void fbcon_set_disp(int con, int init, int logo)
+static void fbcon_setup(int con, int init, int logo)
{
struct display *p = &fb_display[con];
struct vc_data *conp = p->conp;
@@ -637,8 +568,9 @@ static void fbcon_set_disp(int con, int init, int logo)
}
if (!p->fontdata) {
- if (!fontname[0] || !(font = fbcon_find_font(fontname)))
- font = fbcon_get_default_font(p->var.xres, p->var.yres);
+ if (!p->fb_info->fontname[0] ||
+ !(font = fbcon_find_font(p->fb_info->fontname)))
+ font = fbcon_get_default_font(p->var.xres, p->var.yres);
p->_fontwidth = font->width;
p->_fontheight = font->height;
p->fontdata = font->data;
@@ -654,7 +586,7 @@ static void fbcon_set_disp(int con, int init, int logo)
#endif
{
/* ++Geert: changed from panic() to `correct and continue' */
- printk(KERN_ERR "fbcon_set_disp: No support for fontwidth %d\n", fontwidth(p));
+ printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p));
p->dispsw = &fbcon_dummy;
}
}
@@ -737,7 +669,7 @@ static void fbcon_set_disp(int con, int init, int logo)
}
if (p->dispsw == &fbcon_dummy)
- printk(KERN_WARNING "fbcon_set_disp: type %d (aux %d, depth %d) not "
+ printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
"supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
p->dispsw->setup(p);
@@ -2462,13 +2394,6 @@ struct consw fb_con = {
con_getxy: fbcon_getxy,
};
-void __init fbconsole_init(void)
-{
- if (!num_registered_fb || (first_fb_vc > last_fb_vc))
- return;
-
- take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
-}
/*
* Dummy Low Level Operations
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 1f5492d4f..a63b4fd2c 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -239,8 +239,14 @@ extern const char *global_mode_option;
static initcall_t pref_init_funcs[FB_MAX];
static int num_pref_init_funcs __initdata = 0;
+
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb = 0;
+extern int fbcon_softback_size;
+
+static int first_fb_vc = 0;
+static int last_fb_vc = MAX_NR_CONSOLES-1;
+static int fbcon_is_default = 1;
static int fbmem_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *private)
@@ -418,10 +424,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
+ unsigned long off;
+#if !defined(__sparc__) || defined(__sparc_v9__)
struct fb_fix_screeninfo fix;
struct fb_var_screeninfo var;
- unsigned long start, off;
+ unsigned long start;
u32 len;
+#endif
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
@@ -520,7 +529,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
#endif /* !sparc32 */
}
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
int GET_FB_IDX(kdev_t rdev)
{
int fbidx = MINOR(rdev);
@@ -550,8 +559,6 @@ fb_open(struct inode *inode, struct file *file)
#endif /* CONFIG_KMOD */
if (!(info = registered_fb[fbidx]))
return -ENODEV;
- if (info->flags & FBINFO_FLAG_OPEN) return -EBUSY;
- info->flags |= FBINFO_FLAG_OPEN;
return info->fbops->fb_open(info,1);
}
@@ -562,7 +569,6 @@ fb_release(struct inode *inode, struct file *file)
struct fb_info *info = registered_fb[fbidx];
info->fbops->fb_release(info,1);
- info->flags &= ~FBINFO_FLAG_OPEN;
return 0;
}
@@ -580,8 +586,10 @@ static devfs_handle_t devfs_handle = NULL;
int
register_framebuffer(struct fb_info *fb_info)
{
+ int i, j;
char name_buf[8];
- int i;
+ static int fb_ever_opened[FB_MAX];
+ static int first = 1;
if (num_registered_fb == FB_MAX)
return -ENXIO;
@@ -590,9 +598,22 @@ register_framebuffer(struct fb_info *fb_info)
if (!registered_fb[i])
break;
fb_info->node = MKDEV(FB_MAJOR, i);
- fb_info->flags &= ~FBINFO_FLAG_OPEN;
- fb_info->count = 0;
registered_fb[i] = fb_info;
+ if (!fb_ever_opened[i]) {
+ /*
+ * We assume initial frame buffer devices can be opened this
+ * many times
+ */
+ for (j = 0; j < MAX_NR_CONSOLES; j++)
+ if (con2fb_map[j] == i)
+ fb_info->fbops->fb_open(fb_info,0);
+ fb_ever_opened[i] = 1;
+ }
+
+ if (first) {
+ first = 0;
+ take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
+ }
sprintf (name_buf, "%d", i);
fb_info->devfs_handle =
devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
@@ -605,12 +626,12 @@ register_framebuffer(struct fb_info *fb_info)
int
unregister_framebuffer(struct fb_info *fb_info)
{
- int i;
+ int i, j;
i = GET_FB_IDX(fb_info->node);
-
- if (fb_info->count || (fb_info->flags & FBINFO_FLAG_OPEN))
- return -EBUSY;
+ for (j = 0; j < MAX_NR_CONSOLES; j++)
+ if (con2fb_map[j] == i)
+ return -EBUSY;
if (!registered_fb[i])
return -EINVAL;
devfs_unregister (fb_info->devfs_handle);
@@ -654,6 +675,43 @@ int __init video_setup(char *options)
if (!options || !*options)
return 0;
+
+ if (!strncmp(options, "scrollback:", 11)) {
+ options += 11;
+ if (*options) {
+ fbcon_softback_size = simple_strtoul(options, &options, 0);
+ if (*options == 'k' || *options == 'K') {
+ fbcon_softback_size *= 1024;
+ options++;
+ }
+ if (*options != ',')
+ return 0;
+ options++;
+ } else
+ return 0;
+ }
+
+ if (!strncmp(options, "map:", 4)) {
+ options += 4;
+ if (*options)
+ for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!options[j])
+ j = 0;
+ con2fb_map[i] = (options[j++]-'0') % FB_MAX;
+ }
+ return 0;
+ }
+
+ if (!strncmp(options, "vc:", 3)) {
+ options += 3;
+ if (*options)
+ first_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ if (first_fb_vc < 0)
+ first_fb_vc = 0;
+ if (*options++ == '-')
+ last_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ fbcon_is_default = 0;
+ }
if (num_pref_init_funcs == FB_MAX)
return 0;
@@ -695,6 +753,6 @@ EXPORT_SYMBOL(register_framebuffer);
EXPORT_SYMBOL(unregister_framebuffer);
EXPORT_SYMBOL(registered_fb);
EXPORT_SYMBOL(num_registered_fb);
-#if 1 /* to go away in 2.4.0 */
+#if 1 /* to go away in 2.5.0 */
EXPORT_SYMBOL(GET_FB_IDX);
#endif
diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c
index bffa6b02e..dead9038b 100644
--- a/drivers/video/hgafb.c
+++ b/drivers/video/hgafb.c
@@ -7,6 +7,8 @@
*
* History:
*
+ * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc
+ * minor fixes
* - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for
* HGA-only systems
* - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure
@@ -27,6 +29,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
@@ -53,7 +56,7 @@
#define DPRINTK(args...)
#endif
-#if 1
+#if 0
#define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; }
#else
#define CHKINFO(ret)
@@ -97,6 +100,10 @@ static char *hga_type_name;
#define HGA_GFX_MODE_EN 0x01
#define HGA_GFX_PAGE_EN 0x02
+/* Global locks */
+
+spinlock_t hga_reg_lock = SPIN_LOCK_UNLOCKED;
+
/* Framebuffer driver structures */
static struct fb_var_screeninfo hga_default_var = {
@@ -158,55 +165,46 @@ static int nologo = 0;
static void write_hga_b(unsigned int val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT);
outb_p(val, HGA_VALUE_PORT);
-
- restore_flags(flags);
}
static void write_hga_w(unsigned int val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT); outb_p(val >> 8, HGA_VALUE_PORT);
outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT);
-
- restore_flags(flags);
}
static int test_hga_b(unsigned char val, unsigned char reg)
{
- unsigned long flags;
-
- save_flags(flags); cli();
-
outb_p(reg, HGA_INDEX_PORT);
outb (val, HGA_VALUE_PORT);
-
udelay(20); val = (inb_p(HGA_VALUE_PORT) == val);
-
- restore_flags(flags);
-
return val;
}
static void hga_clear_screen(void)
{
+ unsigned char fillchar = 0xbf; /* magic */
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
if (hga_mode == HGA_TXT)
- memset((char *)hga_vram_base, ' ', hga_vram_len);
+ fillchar = ' ';
else if (hga_mode == HGA_GFX)
- memset((char *)hga_vram_base, 0, hga_vram_len);
+ fillchar = 0x00;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
+ if (fillchar != 0xbf)
+ memset((char *)hga_vram_base, fillchar, hga_vram_len);
}
+#ifdef MODULE
static void hga_txt_mode(void)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT);
outb_p(0x00, HGA_GFX_PORT);
outb_p(0x00, HGA_STATUS_PORT);
@@ -230,10 +228,15 @@ static void hga_txt_mode(void)
write_hga_w(0x0000, 0x0e); /* cursor location */
hga_mode = HGA_TXT;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
}
+#endif /* MODULE */
static void hga_gfx_mode(void)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
outb_p(0x00, HGA_STATUS_PORT);
outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT);
outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
@@ -257,6 +260,7 @@ static void hga_gfx_mode(void)
write_hga_w(0x0000, 0x0e); /* cursor location */
hga_mode = HGA_GFX;
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
}
#ifdef MODULE
@@ -274,12 +278,29 @@ static void hga_show_logo(void)
static void hga_pan(unsigned int xoffset, unsigned int yoffset)
{
unsigned int base;
+ unsigned long flags;
+
base = (yoffset / 8) * 90 + xoffset;
+ spin_lock_irqsave(&hga_reg_lock, flags);
write_hga_w(base, 0x0c); /* start address */
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
DPRINTK("hga_pan: base:%d\n", base);
}
-static int hga_card_detect(void)
+static void hga_blank(int blank_mode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hga_reg_lock, flags);
+ if (blank_mode) {
+ outb_p(0x00, HGA_MODE_PORT); /* disable video */
+ } else {
+ outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
+ }
+ spin_unlock_irqrestore(&hga_reg_lock, flags);
+}
+
+static int __init hga_card_detect(void)
{
int count=0;
u16 *p, p_save;
@@ -288,11 +309,6 @@ static int hga_card_detect(void)
hga_vram_base = VGA_MAP_MEM(0xb0000);
hga_vram_len = 0x08000;
- if (!request_mem_region(hga_vram_base, hga_vram_len, "hgafb")) {
- printk(KERN_ERR "hgafb: cannot reserve video memory at 0x%lX\n",
- hga_vram_base);
- return 0;
- }
if (request_region(0x3b0, 12, "hgafb"))
release_io_ports = 1;
if (request_region(0x3bf, 1, "hgafb"))
@@ -598,11 +614,7 @@ static void hgafbcon_blank(int blank_mode, struct fb_info *info)
CHKINFO( );
DPRINTK("hga_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
- if (blank_mode) {
- outb_p(0x00, HGA_MODE_PORT); /* disable video */
- } else {
- outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT);
- }
+ hga_blank(blank_mode);
}
@@ -642,7 +654,12 @@ int __init hgafb_init(void)
disp.line_length = hga_fix.line_length;
disp.can_soft_blank = 1;
disp.inverse = 0;
+#ifdef FBCON_HAS_HGA
disp.dispsw = &fbcon_hga;
+#else
+#warning HGAFB will not work as a console!
+ disp.dispsw = &fbcon_dummy;
+#endif
disp.dispsw_data = NULL;
disp.scrollmode = SCROLL_YREDRAW;
@@ -723,7 +740,6 @@ static void hgafb_cleanup(struct fb_info *info)
unregister_framebuffer(info);
if (release_io_ports) release_region(0x3b0, 12);
if (release_io_port) release_region(0x3bf, 1);
- release_mem_region(hga_vram_base, hga_vram_len);
}
#endif /* MODULE */