diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-03-19 01:28:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-03-19 01:28:40 +0000 |
commit | 8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch) | |
tree | b88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers | |
parent | f01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff) |
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers')
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(¤t->sigmask_lock); signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); - - if (signr == SIGUSR2) { - usb_stor_debug = !usb_stor_debug; - printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug); - } else { - break; /* exit the loop on any other signal */ - } - } - } + } /* if (singal_pending(current)) */ + } /* for (;;) */ // MOD_DEC_USE_COUNT; printk("usb_stor_control_thread exiting\n"); - /* FIXME: this is a hack to allow for debugging */ - // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt); - return 0; } @@ -1498,7 +1452,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) unsigned int flags = 0; GUID(guid); /* Global Unique Identifier */ struct us_data *prev; - Scsi_Host_Template *htmplt; int protocol = 0; int subclass = 0; struct usb_interface_descriptor *altsetting = @@ -1565,14 +1518,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) return NULL; } memset(ss, 0, sizeof(struct us_data)); + + /* Initialize the mutexes only when the struct is new */ + init_MUTEX_LOCKED(&(ss->sleeper)); + init_MUTEX(&(ss->queue_exclusion)); } - /* Initialize the us_data structure with some useful info */ + /* establish the connection to the new device */ interface = altsetting; ss->flags = flags; ss->ifnum = ifnum; - ss->pusb_dev = dev; ss->attention_done = 0; + ss->pusb_dev = dev; /* If the device has subclass and protocol, then use that. Otherwise, * take data from the specific interface. @@ -1596,7 +1553,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) case US_PR_CBI: US_DEBUGPX("Control/Bulk/Interrupt\n"); - ss->transport = CB_transport; + ss->transport = CBI_transport; ss->transport_reset = CB_reset; break; @@ -1620,7 +1577,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) */ for (i = 0; i < interface->bNumEndpoints; i++) { /* is it an BULK endpoint? */ - if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN) ss->ep_in = interface->endpoint[i].bEndpointAddress & @@ -1646,7 +1603,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { US_DEBUGP("Problems with device\n"); if (ss->host) { - scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt); kfree(ss->htmplt->name); kfree(ss->htmplt); } @@ -1667,11 +1623,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) US_DEBUGP("Protocol: "); switch (ss->subclass) { case US_SC_RBC: - US_DEBUGPX("Reduced Block Commands\n"); + US_DEBUGPX("Reduced Block Commands (RBC)\n"); + ss->proto_handler = transparent_scsi_command; break; case US_SC_8020: - US_DEBUGPX("8020\n"); + US_DEBUGPX("8020i\n"); + ss->proto_handler = ATAPI_command; break; case US_SC_QIC: @@ -1679,7 +1637,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) break; case US_SC_8070: - US_DEBUGPX("8070\n"); + US_DEBUGPX("8070i\n"); + ss->proto_handler = ATAPI_command; break; case US_SC_SCSI: @@ -1697,22 +1656,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) break; } - /* We only handle certain protocols. Currently, these are - *the only ones that devices use. - */ - if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) { - US_DEBUGP("Sorry, we do not support that protocol yet.\n"); - US_DEBUGP("If you have a device which uses one of the unsupported\n"); - US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n"); - - kfree(ss); - return NULL; - } - /* Allocate memory for the SCSI Host Template */ - if ((htmplt = (Scsi_Host_Template *) - kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) { - + if ((ss->htmplt = (Scsi_Host_Template *) + kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) { printk(KERN_WARNING USB_STORAGE "Out of memory\n"); kfree(ss); @@ -1720,7 +1666,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } /* Initialize the host template based on the default one */ - memcpy(htmplt, &my_host_template, sizeof(my_host_template)); + memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template)); /* Grab the next host number */ ss->host_number = my_host_number++; @@ -1729,32 +1675,34 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) * can pass the ss pointer to the host controler thread * in us_detect */ - (struct us_data *)htmplt->proc_dir = ss; + (struct us_data *)ss->htmplt->proc_dir = ss; /* shuttle E-USB */ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { __u8 qstat[2]; int result; - - result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), + + result = usb_control_msg(ss->pusb_dev, + usb_rcvctrlpipe(dev,0), 1, 0xC0, 0, ss->ifnum, qstat, 2, HZ*5); US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]); - init_waitqueue_head(&ss->ip_waitq); + init_MUTEX_LOCKED(&(ss->ip_waitq)); ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); - result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq, - 255, (void *)ss, &ss->irq_handle); - if (result) + result = usb_request_irq(ss->pusb_dev, ss->irqpipe, + CBI_irq, 255, (void *)ss, + &ss->irq_handle); + if (result < 0) return NULL; - - interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6); - } else if (ss->protocol == US_PR_CBI) - { + /* FIXME: what is this?? */ + down(&(ss->ip_waitq)); + } else if (ss->protocol == US_PR_CBI) { int result; - - init_waitqueue_head(&ss->ip_waitq); + + /* set up so we'll wait for notification */ + init_MUTEX_LOCKED(&(ss->ip_waitq)); /* set up the IRQ pipe and handler */ /* FIXME: This needs to get the period from the device */ @@ -1768,18 +1716,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } - /* start up our thread */ + /* start up our thread */ { DECLARE_MUTEX_LOCKED(sem); - init_waitqueue_head(&ss->waitq); - ss->notify = &sem; ss->pid = kernel_thread(usb_stor_control_thread, ss, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (ss->pid < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); - kfree(htmplt); + kfree(ss->htmplt); kfree(ss); return NULL; @@ -1790,17 +1736,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } /* now register - our detect function will be called */ - scsi_register_module(MODULE_SCSI_HA, htmplt); + ss->htmplt->module = &__this_module; + scsi_register_module(MODULE_SCSI_HA, ss->htmplt); /* put us in the list */ - prev = (struct us_data *)&us_list; - while (prev->next) - prev = prev->next; - prev->next = ss; + ss->next = us_list; + us_list = ss; } - printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n"); - printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum); + printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n"); + printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum); return ss; } @@ -1814,7 +1759,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) return; ss->pusb_dev = NULL; - // MOD_DEC_USE_COUNT; } @@ -1824,8 +1768,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) int __init usb_stor_init(void) { - // MOD_INC_USE_COUNT; - if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) { printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ; printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", @@ -1844,6 +1786,14 @@ int __init usb_stor_init(void) void __exit usb_stor_exit(void) { + static struct us_data *ptr; + + // FIXME: this needs to be put back to free _all_ the hosts + // for (ptr = us_list; ptr != NULL; ptr = ptr->next) + // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt); + printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt)); + scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt); + usb_deregister(&storage_driver) ; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 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 */ |