diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-12-04 03:58:56 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-12-04 03:58:56 +0000 |
commit | 1d67e90f19a7acfd9a05dc59678e7d0c5090bd0d (patch) | |
tree | 357efc7b93f8f5102110d20d293f41360ec212fc /drivers | |
parent | aea27b2e18d69af87e673972246e66657b4fa274 (diff) |
Merge with Linux 2.3.21.
Diffstat (limited to 'drivers')
201 files changed, 25414 insertions, 6157 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 1a74fa394..b31d2beb4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,7 +11,7 @@ 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 \ macintosh video dio zorro fc4 usb \ - nubus tc ap1000 atm + nubus tc ap1000 atm pcmcia ifdef CONFIG_DIO SUB_DIRS += dio @@ -22,8 +22,12 @@ ifdef CONFIG_PCI SUB_DIRS += pci endif -ifdef CONFIG_PCMCIA +ifeq ($(CONFIG_PCMCIA),y) SUB_DIRS += pcmcia +else + ifeq ($(CONFIG_PCMCIA),m) + MOD_SUB_DIRS += pcmcia + endif endif ifdef CONFIG_SBUS @@ -117,7 +121,6 @@ endif ifeq ($(CONFIG_AP1000),y) SUB_DIRS += ap1000 -ALL_SUB_DIRS += ap1000 endif ifeq ($(CONFIG_FC4),y) diff --git a/drivers/acorn/block/Config.in b/drivers/acorn/block/Config.in index 6ed6cf6a3..be2d2432f 100644 --- a/drivers/acorn/block/Config.in +++ b/drivers/acorn/block/Config.in @@ -5,11 +5,11 @@ mainmenu_option next_comment comment 'Acorn-specific block devices' if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then - tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772 - tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM - if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then - bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT - fi + tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772 + tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM + if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then + bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT + fi fi endmenu diff --git a/drivers/acorn/scsi/Config.in b/drivers/acorn/scsi/Config.in index 68d4f920b..28030bc32 100644 --- a/drivers/acorn/scsi/Config.in +++ b/drivers/acorn/scsi/Config.in @@ -3,21 +3,21 @@ # dep_tristate 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 $CONFIG_SCSI if [ "$CONFIG_SCSI_ACORNSCSI_3" != "n" ]; then - bool ' Support SCSI 2 Tagged queueing' CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - bool ' Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC + bool ' Support SCSI 2 Tagged queueing' CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + bool ' Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI - dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI - dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI - dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI + dep_tristate 'ARXE SCSI support (EXPERIMENTAL)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI + dep_tristate 'CumanaSCSI II support (EXPERIMENTAL)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI + dep_tristate 'EESOX support (EXPERIMENTAL)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI + dep_tristate 'PowerTec support (EXPERIMENTAL)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI - comment 'The following drivers are not fully supported' + comment 'The following drivers are not fully supported' - dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI - if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then - dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI $CONFIG_SCSI - fi - dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK1 $CONFIG_SCSI + dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI + if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then + dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI $CONFIG_SCSI + fi + dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK1 $CONFIG_SCSI fi diff --git a/drivers/atm/Config.in b/drivers/atm/Config.in index af401ed29..c0112f839 100644 --- a/drivers/atm/Config.in +++ b/drivers/atm/Config.in @@ -4,44 +4,44 @@ mainmenu_option next_comment comment 'ATM drivers' if [ "$CONFIG_INET" = "y" ]; then - tristate 'ATM over TCP' CONFIG_ATM_TCP y + tristate 'ATM over TCP' CONFIG_ATM_TCP fi if [ "$CONFIG_PCI" = "y" ]; then - tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI y - if [ ! "$CONFIG_ATM_ENI" = "n" ]; then - bool ' Enable extended debugging' CONFIG_ATM_ENI_DEBUG n - bool ' Fine-tune burst settings' CONFIG_ATM_ENI_TUNE_BURST n - if [ "$CONFIG_ATM_ENI_TUNE_BURST" = "y" ]; then - bool ' Enable 16W TX bursts (discouraged)' CONFIG_ATM_ENI_BURST_TX_16W n - bool ' Enable 8W TX bursts (recommended)' CONFIG_ATM_ENI_BURST_TX_8W y - bool ' Enable 4W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_4W n - bool ' Enable 2W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_2W n - bool ' Enable 16W RX bursts (discouraged)' CONFIG_ATM_ENI_BURST_RX_16W n - bool ' Enable 8W RX bursts (discouraged)' CONFIG_ATM_ENI_BURST_RX_8W n - bool ' Enable 4W RX bursts (recommended)' CONFIG_ATM_ENI_BURST_RX_4W y - bool ' Enable 2W RX bursts (optional)' CONFIG_ATM_ENI_BURST_RX_2W n - fi - fi - tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM y - if [ ! "$CONFIG_ATM_ZATM" = "n" ]; then - bool ' Enable extended debugging' CONFIG_ATM_ZATM_DEBUG n - bool ' Enable usec resolution timestamps' CONFIG_ATM_ZATM_EXACT_TS y - fi -# bool 'Rolfs TI TNETA1570' CONFIG_ATM_TNETA1570 y -# if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then -# bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n -# fi - tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR - if [ "$CONFIG_ATM_NICSTAR" != "n" ]; then - bool ' Use suni PHY driver' CONFIG_ATM_NICSTAR_USE_SUNI - fi - tristate 'Madge Ambassador (Collage PCI 155 Server)' CONFIG_ATM_AMBASSADOR - if [ "$CONFIG_ATM_AMBASSADOR" != "n" ]; then - bool ' Enable debugging messages' CONFIG_ATM_AMBASSADOR_DEBUG - fi - tristate 'Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)' CONFIG_ATM_HORIZON y - if [ "$CONFIG_ATM_HORIZON" != "n" ]; then - bool ' Enable debugging messages' CONFIG_ATM_HORIZON_DEBUG - fi + tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI + if [ ! "$CONFIG_ATM_ENI" = "n" ]; then + bool ' Enable extended debugging' CONFIG_ATM_ENI_DEBUG + bool ' Fine-tune burst settings' CONFIG_ATM_ENI_TUNE_BURST + if [ "$CONFIG_ATM_ENI_TUNE_BURST" = "y" ]; then + bool ' Enable 16W TX bursts (discouraged)' CONFIG_ATM_ENI_BURST_TX_16W + bool ' Enable 8W TX bursts (recommended)' CONFIG_ATM_ENI_BURST_TX_8W + bool ' Enable 4W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_4W + bool ' Enable 2W TX bursts (optional)' CONFIG_ATM_ENI_BURST_TX_2W + bool ' Enable 16W RX bursts (discouraged)' CONFIG_ATM_ENI_BURST_RX_16W + bool ' Enable 8W RX bursts (discouraged)' CONFIG_ATM_ENI_BURST_RX_8W + bool ' Enable 4W RX bursts (recommended)' CONFIG_ATM_ENI_BURST_RX_4W + bool ' Enable 2W RX bursts (optional)' CONFIG_ATM_ENI_BURST_RX_2W + fi + fi + tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM + if [ ! "$CONFIG_ATM_ZATM" = "n" ]; then + bool ' Enable extended debugging' CONFIG_ATM_ZATM_DEBUG + bool ' Enable usec resolution timestamps' CONFIG_ATM_ZATM_EXACT_TS + fi +# bool 'Rolfs TI TNETA1570' CONFIG_ATM_TNETA1570 y +# if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then +# bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n +# fi + tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR + if [ "$CONFIG_ATM_NICSTAR" != "n" ]; then + bool ' Use suni PHY driver' CONFIG_ATM_NICSTAR_USE_SUNI + fi + tristate 'Madge Ambassador (Collage PCI 155 Server)' CONFIG_ATM_AMBASSADOR + if [ "$CONFIG_ATM_AMBASSADOR" != "n" ]; then + bool ' Enable debugging messages' CONFIG_ATM_AMBASSADOR_DEBUG + fi + tristate 'Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)' CONFIG_ATM_HORIZON + if [ "$CONFIG_ATM_HORIZON" != "n" ]; then + bool ' Enable debugging messages' CONFIG_ATM_HORIZON_DEBUG + fi fi endmenu diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 7c00c4dd2..c23b8d094 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -6,198 +6,200 @@ comment 'Block devices' tristate 'Normal PC floppy disk support' CONFIG_BLK_DEV_FD if [ "$CONFIG_AMIGA" = "y" ]; then - tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY + tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY fi if [ "$CONFIG_ATARI" = "y" ]; then - tristate 'Atari floppy support' CONFIG_ATARI_FLOPPY + tristate 'Atari floppy support' CONFIG_ATARI_FLOPPY fi if [ "$CONFIG_MAC" = "y" ]; then - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP - fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP + fi fi tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE comment 'Please see Documentation/ide.txt for help/info on IDE drives' if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then - bool 'Old hard disk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY + 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 ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE - if [ "$CONFIG_BLK_DEV_IDECD" = "y" ]; then - bool ' Include CD-Changer Reporting' CONFIG_IDECD_SLOTS - fi - 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 - 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_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 ' 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)' IDEDMA_NEW_DRIVE_LISTINGS - define_bool IDEDMA_PCI_EXPERIMENTAL y - else - define_bool IDEDMA_PCI_EXPERIMENTAL n - fi - fi - bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD - bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 - if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_X86" = "y" ]; then - bool ' ALI M15x3 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_ALI15X3 - fi - bool ' CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646 - bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 - fi - bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X - if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then - bool ' HPT34X DMA support (DANGEROUS)' CONFIG_BLK_DEV_HPT34X_DMA - fi - bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 - if [ "$CONFIG_X86" = "y" ]; then - bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX - if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_PIIX" = "y" ]; then - bool ' PIIXn Tuning support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX_TUNING - fi - fi - if [ "$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_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then - bool ' Special UDMA Feature (EXPERIMENTAL)' PDC202XX_FORCE_BURST_BIT - bool ' Special Mode Feature (DANGEROUS)' PDC202XX_FORCE_MASTER_MODE - fi - fi - if [ "$CONFIG_X86" = "y" ]; then - bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 - fi - if [ "$IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then - bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 - if [ "$CONFIG_X86" = "y" ]; then - bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 - fi - fi + 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 ' 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_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then - bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 + 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 ' 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 + bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD + bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 + if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_X86" = "y" ]; then + bool ' ALI M15x3 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_ALI15X3 + fi + bool ' CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646 + bool ' CY82C693 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CY82C693 + fi + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then + bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X + if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ + "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then + bool ' HPT34X DMA support (DANGEROUS)' CONFIG_BLK_DEV_HPT34X_DMA + fi + bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 + fi + if [ "$CONFIG_X86" = "y" ]; then + bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX + if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ + "$CONFIG_BLK_DEV_PIIX" = "y" ]; then + bool ' PIIXn Tuning support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX_TUNING + 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_EXPERIMENTAL" = "y" -a \ + "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then + bool ' Special UDMA Feature (EXPERIMENTAL)' CONFIG_PDC202XX_FORCE_BURST_BIT + bool ' Special Mode Feature (DANGEROUS)' CONFIG_PDC202XX_FORCE_MASTER_MODE + fi + fi + if [ "$CONFIG_X86" = "y" ]; then + bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 + fi + if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then + bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 + if [ "$CONFIG_X86" = "y" ]; then + bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 + fi + fi + fi + if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then + bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 + fi 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 + 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 - 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 + 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 - 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 - bool ' Amiga IDE Doubler support' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE + 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 - fi - if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' Buddha/Catweasel IDE interface support' 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 + 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 + tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2 fi if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM + tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM fi if [ "$CONFIG_ATARI" = "y" ]; then - tristate 'Atari ACSI support' CONFIG_ATARI_ACSI - if [ "$CONFIG_ATARI_ACSI" != "n" ]; then - comment 'Some devices (e.g. CD jukebox) support multiple LUNs' - bool 'Probe all LUNs on each ACSI device' CONFIG_ACSI_MULTI_LUN - dep_tristate 'Atari SLM laser printer support' CONFIG_ATARI_SLM $CONFIG_ATARI_ACSI - fi -fi -tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA + tristate 'Atari ACSI support' CONFIG_ATARI_ACSI + if [ "$CONFIG_ATARI_ACSI" != "n" ]; then + comment 'Some devices (e.g. CD jukebox) support multiple LUNs' + bool ' Probe all LUNs on each ACSI device' CONFIG_ACSI_MULTI_LUN + tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM + fi +fi +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA +fi comment 'Additional Block Devices' tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP if [ "$CONFIG_NET" = "y" ]; then - tristate 'Network block device support' CONFIG_BLK_DEV_NBD + tristate 'Network block device support' CONFIG_BLK_DEV_NBD fi bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then - tristate ' Linear (append) mode' CONFIG_MD_LINEAR - tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED - tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING - tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 + tristate ' Linear (append) mode' CONFIG_MD_LINEAR + tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then - bool ' Boot support (linear, striped)' CONFIG_MD_BOOT + bool ' Boot support (linear, striped)' CONFIG_MD_BOOT fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then - bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD + bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi tristate 'XT hard disk support' CONFIG_BLK_DEV_XD @@ -205,14 +207,14 @@ tristate 'XT hard disk support' CONFIG_BLK_DEV_XD # PARIDE must also be a module. The bogus CONFIG_PARIDE_PARPORT option # controls the choices given to the user ... -if [ "$CONFIG_PARPORT" = "y" -o "$CONFIG_PARPORT" = "n" ] ; then +if [ "$CONFIG_PARPORT" = "y" -o "$CONFIG_PARPORT" = "n" ]; then define_bool CONFIG_PARIDE_PARPORT y else define_bool CONFIG_PARIDE_PARPORT m 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 + source drivers/block/paride/Config.in fi if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \ @@ -226,15 +228,15 @@ if [ "$CONFIG_IDE_CHIPSETS" = "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 + define_bool CONFIG_BLK_DEV_IDE_MODES y else - define_bool CONFIG_BLK_DEV_IDE_MODES n + 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 + define_bool CONFIG_BLK_DEV_HD y else - define_bool CONFIG_BLK_DEV_HD n + define_bool CONFIG_BLK_DEV_HD n fi endmenu diff --git a/drivers/block/Makefile b/drivers/block/Makefile index abc2242dc..8519318d9 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 cmos-probe.o +L_OBJS := genhd.o ide-geometry.o M_OBJS := MOD_LIST_NAME := BLOCK_MODULES LX_OBJS := ll_rw_blk.o blkpg.o @@ -248,7 +248,7 @@ ifeq ($(CONFIG_BLK_DEV_IDE),y) else ifeq ($(CONFIG_BLK_DEV_IDE),m) MIX_OBJS += ide.o $(IDE_OBJS) - M_OBJS += ide-mod.o ide-probe.o + M_OBJS += ide-mod.o ide-probe-mod.o endif endif @@ -376,3 +376,7 @@ include $(TOPDIR)/Rules.make ide-mod.o: ide.o $(IDE_OBJS) $(LD) $(LD_RFLAG) -r -o $@ ide.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/cmos-probe.c b/drivers/block/cmos-probe.c deleted file mode 100644 index f0147d5e3..000000000 --- a/drivers/block/cmos-probe.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * linux/drivers/block/cmos-probe.c Version 1.00 August 16, 1999 - * - * Copyright (C) 1994-1999 Linus Torvalds & authors (see below) - */ - -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/major.h> -#include <linux/errno.h> -#include <linux/genhd.h> -#include <linux/malloc.h> -#include <linux/delay.h> -#include <linux/ide.h> - - -#include <asm/byteorder.h> -#include <asm/irq.h> -#include <asm/uaccess.h> -#include <asm/io.h> - -/* - * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc - * controller that is BIOS compatible with ST-506, and thus showing up in our - * BIOS table, but not register compatible, and therefore not present in CMOS. - * - * Furthermore, we will assume that our ST-506 drives <if any> are the primary - * drives in the system -- the ones reflected as drive 1 or 2. The first - * drive is stored in the high nibble of CMOS byte 0x12, the second in the low - * nibble. This will be either a 4 bit drive type or 0xf indicating use byte - * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value - * means we have an AT controller hard disk for that drive. - * - * Of course, there is no guarantee that either drive is actually on the - * "primary" IDE interface, but we don't bother trying to sort that out here. - * If a drive is not actually on the primary interface, then these parameters - * will be ignored. This results in the user having to supply the logical - * drive geometry as a boot parameter for each drive not on the primary i/f. - * - * The only "perfect" way to handle this would be to modify the setup.[cS] code - * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info - * for us during initialization. I have the necessary docs -- any takers? -ml - */ -void probe_cmos_for_drives (ide_hwif_t *hwif) -{ -#ifdef __i386__ - extern struct drive_info_struct drive_info; - byte cmos_disks, *BIOS = (byte *) &drive_info; - int unit; - -#ifdef CONFIG_BLK_DEV_PDC4030 - if (hwif->chipset == ide_pdc4030 && hwif->channel != 0) - return; -#endif /* CONFIG_BLK_DEV_PDC4030 */ - outb_p(0x12,0x70); /* specify CMOS address 0x12 */ - cmos_disks = inb_p(0x71); /* read the data from 0x12 */ - /* Extract drive geometry from CMOS+BIOS if not already setup */ - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; - if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) { - drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS; - drive->head = drive->bios_head = *(BIOS+2); - drive->sect = drive->bios_sect = *(BIOS+14); - drive->ctl = *(BIOS+8); - drive->present = 1; - } - BIOS += 16; - } -#endif -} diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 1b2be20dd..579fbc13a 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -212,6 +212,7 @@ struct file_operations ida_fops = { */ static void ida_procinit(int i) { +#ifdef CONFIG_PROC_FS struct proc_dir_entry *pd; if (proc_array == NULL) { @@ -224,6 +225,7 @@ static void ida_procinit(int i) if (!pd) return; pd->read_proc = ida_proc_get_info; pd->data = hba[i]; +#endif } /* @@ -356,7 +358,9 @@ void cleanup_module(void) } } } +#ifdef CONFIG_PROC_FS remove_proc_entry("array", &proc_root); +#endif kfree(ida); kfree(ida_sizes); kfree(ida_hardsizes); diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index 2ea7d5b51..d2941a1ab 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -94,42 +94,40 @@ static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsign * * Returns: 1 if lba_capacity looks sensible * 0 otherwise + * + * It is called only once for each drive. */ static int lba_capacity_is_ok (struct hd_driveid *id) { - unsigned long lba_sects = id->lba_capacity; - unsigned long chs_sects = id->cyls * id->heads * id->sectors; - unsigned long _10_percent = chs_sects / 10; + unsigned long lba_sects, chs_sects, head, tail; /* - * very large drives (8GB+) may lie about the number of cylinders - * This is a split test for drives 8 Gig and Bigger only. + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. */ - if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) && - (id->heads == 16) && (id->sectors == 63)) { - id->cyls = lba_sects / (16 * 63); /* correct cyls */ - return 1; /* lba_capacity is our only option */ - } - /* - * ... and at least one TLA VBC has POS instead of brain and can't - * tell 16 from 15. - */ - if ((id->lba_capacity >= 15481935) && (id->cyls == 0x3fff) && - (id->heads == 15) && (id->sectors == 63)) { - id->cyls = lba_sects / (15 * 63); /* correct cyls */ - return 1; /* lba_capacity is our only option */ - } - /* perform a rough sanity check on lba_sects: within 10% is "okay" */ - if ((lba_sects - chs_sects) < _10_percent) { - return 1; /* lba_capacity is good */ - } + if (id->cyls == 16383 && id->sectors == 63 && + (id->heads == 15 || id->heads == 16) && + id->lba_capacity >= 16383*63*id->heads) + return 1; + + lba_sects = id->lba_capacity; + chs_sects = id->cyls * id->heads * id->sectors; + + /* perform a rough sanity check on lba_sects: within 10% is OK */ + if ((lba_sects - chs_sects) < chs_sects/10) + return 1; + /* some drives have the word order reversed */ - lba_sects = (lba_sects << 16) | (lba_sects >> 16); - if ((lba_sects - chs_sects) < _10_percent) { - id->lba_capacity = lba_sects; /* fix it */ + head = ((lba_sects >> 16) & 0xffff); + tail = (lba_sects & 0xffff); + lba_sects = (head | (tail << 16)); + if ((lba_sects - chs_sects) < chs_sects/10) { + id->lba_capacity = lba_sects; return 1; /* lba_capacity is (now) good */ } - return 0; /* lba_capacity value is bad */ + + return 0; /* lba_capacity value may be bad */ } /* @@ -450,24 +448,28 @@ static int idedisk_media_change (ide_drive_t *drive) } /* - * current_capacity() returns the capacity (in sectors) of a drive - * according to its current geometry/LBA settings. + * Compute drive->capacity, the full capacity of the drive + * Called with drive->id != NULL. */ -static unsigned long idedisk_capacity (ide_drive_t *drive) +static void init_idedisk_capacity (ide_drive_t *drive) { struct hd_driveid *id = drive->id; unsigned long capacity = drive->cyl * drive->head * drive->sect; drive->select.b.lba = 0; + /* Determine capacity, and use LBA if the drive properly supports it */ - if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) { - if (id->lba_capacity >= capacity) { - drive->cyl = id->lba_capacity / (drive->head * drive->sect); - capacity = id->lba_capacity; - drive->select.b.lba = 1; - } + if ((id->capability & 2) && lba_capacity_is_ok(id)) { + capacity = id->lba_capacity; + drive->cyl = capacity / (drive->head * drive->sect); + drive->select.b.lba = 1; } - return (capacity - drive->sect0); + drive->capacity = capacity; +} + +static unsigned long idedisk_capacity (ide_drive_t *drive) +{ + return (drive->capacity - drive->sect0); } static void idedisk_special (ide_drive_t *drive) @@ -628,7 +630,7 @@ static void idedisk_add_settings(ide_drive_t *drive) int major = HWIF(drive)->major; int minor = drive->select.b.unit << PARTN_BITS; - ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); @@ -679,7 +681,7 @@ static int idedisk_cleanup (ide_drive_t *drive) static void idedisk_setup (ide_drive_t *drive) { struct hd_driveid *id = drive->id; - unsigned long capacity, check; + unsigned long capacity; idedisk_add_settings(drive); @@ -705,66 +707,33 @@ static void idedisk_setup (ide_drive_t *drive) drive->head = drive->bios_head = id->heads; drive->sect = drive->bios_sect = id->sectors; } + /* Handle logical geometry translation by the drive */ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) { - /* - * Extract the physical drive geometry for our use. - * Note that we purposely do *not* update the bios info. - * This way, programs that use it (like fdisk) will - * still have the same logical view as the BIOS does, - * which keeps the partition table from being screwed. - * - * An exception to this is the cylinder count, - * which we reexamine later on to correct for 1024 limitations. - */ drive->cyl = id->cur_cyls; drive->head = id->cur_heads; drive->sect = id->cur_sectors; - - /* check for word-swapped "capacity" field in id information */ - capacity = drive->cyl * drive->head * drive->sect; - check = (id->cur_capacity0 << 16) | id->cur_capacity1; - if (check == capacity) { /* was it swapped? */ - /* yes, bring it into little-endian order: */ - id->cur_capacity0 = (capacity >> 0) & 0xffff; - id->cur_capacity1 = (capacity >> 16) & 0xffff; - } } + /* Use physical geometry if what we have still makes no sense */ - if ((!drive->head || drive->head > 16) && - id->heads && id->heads <= 16) { - if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) { - id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors))); - } - drive->cyl = id->cur_cyls = id->cyls; - drive->head = id->cur_heads = id->heads; - drive->sect = id->cur_sectors = id->sectors; + if (drive->head > 16 && id->heads && id->heads <= 16) { + drive->cyl = id->cyls; + drive->head = id->heads; + drive->sect = id->sectors; } /* calculate drive capacity, and select LBA if possible */ - capacity = idedisk_capacity (drive); + init_idedisk_capacity (drive); /* * if possible, give fdisk access to more of the drive, * by correcting bios_cyls: */ + capacity = idedisk_capacity (drive); if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && - (!drive->forced_geom) && drive->bios_sect && drive->bios_head) { + (!drive->forced_geom) && drive->bios_sect && drive->bios_head) drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; -#ifdef DEBUG - printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n", - drive->id->cur_cyls, - drive->id->cur_heads, - drive->id->cur_sectors, - drive->bios_cyl, - drive->bios_head, - drive->bios_sect); -#endif - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - } #if 0 /* done instead for entire identify block in arch/ide.h stuff */ /* fix byte-ordering of buffer size field */ @@ -791,19 +760,6 @@ static void idedisk_setup (ide_drive_t *drive) } printk("\n"); - if (drive->select.b.lba) { - if (*(int *)&id->cur_capacity0 < id->lba_capacity) { -#ifdef DEBUG - printk(" CurSects=%d, LBASects=%d, ", - *(int *)&id->cur_capacity0, id->lba_capacity); -#endif - *(int *)&id->cur_capacity0 = id->lba_capacity; -#ifdef DEBUG - printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0); -#endif - } - } - drive->mult_count = 0; if (id->max_multsect) { #ifdef CONFIG_IDEDISK_MULTI_MODE diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c index cd631c621..d3c657516 100644 --- a/drivers/block/ide-dma.c +++ b/drivers/block/ide-dma.c @@ -91,7 +91,7 @@ #include <asm/io.h> #include <asm/irq.h> -#ifdef IDEDMA_NEW_DRIVE_LISTINGS +#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS struct drive_list_entry { char * id_model; @@ -130,7 +130,7 @@ int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) return 0; } -#else /* !IDEDMA_NEW_DRIVE_LISTINGS */ +#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ /* * good_dma_drives() lists the model names (from "hdparm -i") @@ -162,7 +162,7 @@ const char *bad_dma_drives[] = {"WDC AC11000H", "WDC AC31600H", NULL}; -#endif /* IDEDMA_NEW_DRIVE_LISTINGS */ +#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ /* * Our Physical Region Descriptor (PRD) table should be large enough @@ -314,7 +314,7 @@ int check_drive_lists (ide_drive_t *drive, int good_bad) { struct hd_driveid *id = drive->id; -#ifdef IDEDMA_NEW_DRIVE_LISTINGS +#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS if (good_bad) { return in_drive_list(id, drive_whitelist); } else { @@ -323,7 +323,7 @@ int check_drive_lists (ide_drive_t *drive, int good_bad) printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model); return(blacklist); } -#else /* !IDEDMA_NEW_DRIVE_LISTINGS */ +#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ const char **list; if (good_bad) { @@ -344,7 +344,7 @@ int check_drive_lists (ide_drive_t *drive, int good_bad) } } } -#endif /* IDEDMA_NEW_DRIVE_LISTINGS */ +#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ return 0; } diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c index 66b71b3a2..f1c8baef8 100644 --- a/drivers/block/ide-floppy.c +++ b/drivers/block/ide-floppy.c @@ -1498,7 +1498,7 @@ static void idefloppy_add_settings(ide_drive_t *drive) int major = HWIF(drive)->major; int minor = drive->select.b.unit << PARTN_BITS; - ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); diff --git a/drivers/block/ide-geometry.c b/drivers/block/ide-geometry.c new file mode 100644 index 000000000..974fb24f1 --- /dev/null +++ b/drivers/block/ide-geometry.c @@ -0,0 +1,225 @@ +/* + * linux/drivers/block/ide-geometry.c + */ +#include <linux/ide.h> + +#include <asm/io.h> + +/* + * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc + * controller that is BIOS compatible with ST-506, and thus showing up in our + * BIOS table, but not register compatible, and therefore not present in CMOS. + * + * Furthermore, we will assume that our ST-506 drives <if any> are the primary + * drives in the system -- the ones reflected as drive 1 or 2. The first + * drive is stored in the high nibble of CMOS byte 0x12, the second in the low + * nibble. This will be either a 4 bit drive type or 0xf indicating use byte + * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value + * means we have an AT controller hard disk for that drive. + * + * Of course, there is no guarantee that either drive is actually on the + * "primary" IDE interface, but we don't bother trying to sort that out here. + * If a drive is not actually on the primary interface, then these parameters + * will be ignored. This results in the user having to supply the logical + * drive geometry as a boot parameter for each drive not on the primary i/f. + */ +/* + * The only "perfect" way to handle this would be to modify the setup.[cS] code + * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info + * for us during initialization. I have the necessary docs -- any takers? -ml + */ +/* + * I did this, but it doesnt work - there is no reasonable way to find the + * correspondence between the BIOS numbering of the disks and the Linux + * numbering. -aeb + * + * The code below is bad. One of the problems is that drives 1 and 2 + * may be SCSI disks (even when IDE disks are present), so that + * the geometry we read here from BIOS is attributed to the wrong disks. + * Consequently, also the "drive->present = 1" below is a mistake. + * + * Eventually the entire routine below should be removed. + */ +void probe_cmos_for_drives (ide_hwif_t *hwif) +{ +#ifdef __i386__ + extern struct drive_info_struct drive_info; + byte cmos_disks, *BIOS = (byte *) &drive_info; + int unit; + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (hwif->chipset == ide_pdc4030 && hwif->channel != 0) + return; +#endif /* CONFIG_BLK_DEV_PDC4030 */ + outb_p(0x12,0x70); /* specify CMOS address 0x12 */ + cmos_disks = inb_p(0x71); /* read the data from 0x12 */ + /* Extract drive geometry from CMOS+BIOS if not already setup */ + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + if ((cmos_disks & (0xf0 >> (unit*4))) && + !drive->present && !drive->nobios) { + drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS; + drive->head = drive->bios_head = *(BIOS+2); + drive->sect = drive->bios_sect = *(BIOS+14); + drive->ctl = *(BIOS+8); +#if 0 + drive->present = 1; +#endif + } + BIOS += 16; + } +#endif +} + + +/* + * If heads is nonzero: find a translation with this many heads and S=63. + * Otherwise: find out how OnTrack Disk Manager would translate the disk. + */ +static void +ontrack(ide_drive_t *drive, int heads, int *c, int *h, int *s) { + struct hd_driveid *id = drive->id; + static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0}; + const byte *headp = dm_head_vals; + unsigned long total, tracks; + + /* + * The specs say: take geometry as obtained from Identify, + * compute total capacity C*H*S from that, and truncate to + * 1024*255*63. Now take S=63, H the first in the sequence + * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total. + */ + if (id) + total = id->cyls * id->heads * id->sectors; + else + total = drive->cyl * drive->head * drive->sect; + + *s = 63; + + if (heads) { + *h = heads; + *c = total / (63 * heads); + return; + } + +#if 0 + while (63 * headp[0] * 1024 < total && headp[1] != 0) + headp++; + *h = headp[0]; + *c = total / (63 * headp[0]); +#else + /* The code below differs in two aspects: + (i) It will not produce geometries like C/H/S = 1024/64/63 + because of the `>='. This follows OnTracks text (which + claims that 512 <= C <= 1023), but not OnTracks code. + (ii) It starts dividing by 63, so that a rounding down occurs. + For example, with C=11159, H=10, S=37 we find total=4128830 + and DM would make C=512, H=128, S=63, but we make 1024/64/63 + if `>=' is replaced by `>'. + The reason we use this code is mainly that we have done so for + a long time without getting complaints. + */ + + tracks = total / 63; + while (*c >= 1024) { + *h = *headp; + *c = tracks / *h; + if (*++headp == 0) + break; + } +#endif +} + +/* + * This routine is called from the partition-table code in pt/msdos.c. + * It has two tasks: + * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors, + * or to handle EZdrive by remapping sector 0 to sector 1. + * (ii) to invent a translated geometry. + * Part (i) is suppressed if the user specifies the "noremap" option + * on the command line. + * Part (ii) is suppressed if the user specifies an explicit geometry. + * + * The ptheads parameter is either 0 or tells about the number of + * heads shown by the end of the first nonempty partition. + * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it. + * + * The xparm parameter has the following meaning: + * 0 = convert to CHS with fewer than 1024 cyls + * using the same method as Ontrack DiskManager. + * 1 = same as "0", plus offset everything by 63 sectors. + * -1 = similar to "0", plus redirect sector 0 to sector 1. + * 2 = convert to a CHS geometry with "ptheads" heads. + * + * Returns 0 if the translation was not possible, if the device was not + * an IDE disk drive, or if a geometry was "forced" on the commandline. + * Returns 1 if the geometry translation was successful. + */ +int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg) +{ + ide_drive_t *drive; + const char *msg1 = ""; + int heads = 0; + int c, h, s; + int transl = 1; /* try translation */ + int ret = 0; + + drive = get_info_ptr(i_rdev); + if (!drive) + return 0; + + /* remap? */ + if (drive->remap_0_to_1 != 2) { + if (xparm == 1) { /* DM */ + drive->sect0 = 63; + msg1 = " [remap +63]"; + ret = 1; + } else if (xparm == -1) { /* EZ-Drive */ + if (drive->remap_0_to_1 == 0) { + drive->remap_0_to_1 = 1; + msg1 = " [remap 0->1]"; + ret = 1; + } + } + } + + /* There used to be code here that assigned drive->id->CHS + to drive->CHS and that to drive->bios_CHS. However, + some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB. + In such cases that code was wrong. Moreover, + there seems to be no reason to do any of these things. */ + + /* translate? */ + if (drive->forced_geom) + transl = 0; + + /* does ptheads look reasonable? */ + if (ptheads == 32 || ptheads == 64 || ptheads == 128 || + ptheads == 240 || ptheads == 255) + heads = ptheads; + + if (xparm == 2) { + if (!heads || + (drive->bios_head >= heads && drive->bios_sect == 63)) + transl = 0; + } + if (xparm == -1) { + if (drive->bios_head > 16) + transl = 0; /* we already have a translation */ + } + + if (transl) { + ontrack(drive, heads, &c, &h, &s); + drive->bios_cyl = c; + drive->bios_head = h; + drive->bios_sect = s; + ret = 1; + } + + drive->part[0].nr_sects = current_capacity(drive); + + if (ret) + printk("%s%s [%d/%d/%d]", msg, msg1, + drive->bios_cyl, drive->bios_head, drive->bios_sect); + return ret; +} diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c index 3ba39f600..311bd470b 100644 --- a/drivers/block/ide-pci.c +++ b/drivers/block/ide-pci.c @@ -507,7 +507,7 @@ check_if_enabled: if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL) continue; /* no room in ide_hwifs[] */ if (hwif->io_ports[IDE_DATA_OFFSET] != base) { - ide_init_hwif_ports(&hwif->hw, base, (ctl + 2), NULL); + ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL); memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; } diff --git a/drivers/block/ide-pmac.c b/drivers/block/ide-pmac.c index 90fbf9cbc..1ad5b3b96 100644 --- a/drivers/block/ide-pmac.c +++ b/drivers/block/ide-pmac.c @@ -30,8 +30,9 @@ #include <asm/mediabay.h> #include <asm/feature.h> #ifdef CONFIG_PMAC_PBOOK -#include <asm/adb.h> -#include <asm/pmu.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <asm/irq.h> #endif #include "ide_modes.h" @@ -50,9 +51,9 @@ static int pmac_ide_build_dmatable(ide_drive_t *drive, int wr); #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ #ifdef CONFIG_PMAC_PBOOK -static int idepmac_notify(struct notifier_block *, unsigned long, void *); -struct notifier_block idepmac_sleep_notifier = { - idepmac_notify +static int idepmac_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier idepmac_sleep_notifier = { + idepmac_notify, SLEEP_LEVEL_BLOCK, }; #endif /* CONFIG_PMAC_PBOOK */ @@ -224,7 +225,7 @@ void __init pmac_ide_probe(void) pmac_ide_count = i; #ifdef CONFIG_PMAC_PBOOK - notifier_chain_register(&sleep_notifier_list, &idepmac_sleep_notifier); + pmu_register_sleep_notifier(&idepmac_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ } @@ -384,29 +385,107 @@ int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ #ifdef CONFIG_PMAC_PBOOK -static int idepmac_notify(struct notifier_block *this, - unsigned long code, void *p) +static void idepmac_sleep_disk(int i, unsigned long base) { - int i, timeout; + int j; + + /* Reset to PIO 0 */ + out_le32((unsigned *)(base + 0x200 + _IO_BASE), 0x2f8526); + + /* FIXME: We only handle the master IDE */ + if (ide_hwifs[i].drives[0].media == ide_disk) { + /* Spin down the drive */ + outb(0xa0, base+0x60); + outb(0x0, base+0x30); + outb(0x0, base+0x20); + outb(0x0, base+0x40); + outb(0x0, base+0x50); + outb(0xe0, base+0x70); + outb(0x2, base+0x160); + for (j = 0; j < 10; j++) { + int status; + mdelay(100); + status = inb(base+0x70); + if (!(status & BUSY_STAT) && (status & DRQ_STAT)) + break; + } + } +} + +static void idepmac_wake_disk(int i, unsigned long base) +{ + int j; + + /* Revive IDE disk and controller */ + feature_set(pmac_ide_node[i], FEATURE_IDE_enable); + mdelay(1); + feature_set(pmac_ide_node[i], FEATURE_IDE_DiskPower); + mdelay(100); + feature_set(pmac_ide_node[i], FEATURE_IDE_Reset); + mdelay(1); + /* Make sure we are still PIO0 */ + out_le32((unsigned *)(base + 0x200 + _IO_BASE), 0x2f8526); + mdelay(100); + + /* Wait up to 10 seconds (enough for recent drives) */ + for (j = 0; j < 100; j++) { + int status; + mdelay(100); + status = inb(base + 0x70); + if (!(status & BUSY_STAT)) + break; + } +} - switch (code) { - case PBOOK_SLEEP: - /* do anything here?? */ +/* Here we handle media bay devices */ +static void +idepmac_wake_bay(int i, unsigned long base) +{ + int timeout; + + timeout = 5000; + while ((inb(base + 0x70) & BUSY_STAT) && timeout) { + mdelay(1); + --timeout; + } +} + +static int idepmac_notify(struct pmu_sleep_notifier *self, int when) +{ + int i, ret; + unsigned long base; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + break; + case PBOOK_SLEEP_REJECT: + break; + case PBOOK_SLEEP_NOW: + for (i = 0; i < pmac_ide_count; ++i) { + if ((base = pmac_ide_regbase[i]) == 0) + continue; + /* Disable irq during sleep */ + disable_irq(pmac_ide_irq[i]); + ret = check_media_bay_by_base(base, MB_CD); + if (ret == -ENODEV) + /* not media bay - put the disk to sleep */ + idepmac_sleep_disk(i, base); + } break; case PBOOK_WAKE: - /* wait for the controller(s) to become ready */ - timeout = 5000; for (i = 0; i < pmac_ide_count; ++i) { - unsigned long base = pmac_ide_regbase[i]; - if (check_media_bay_by_base(base, MB_CD) == -EINVAL) + if ((base = pmac_ide_regbase[i]) == 0) continue; - while ((inb(base + 0x70) & BUSY_STAT) && timeout) { - mdelay(1); - --timeout; - } + /* We don't handle media bay devices this way */ + ret = check_media_bay_by_base(base, MB_CD); + if (ret == -ENODEV) + idepmac_wake_disk(i, base); + else if (ret == 0) + idepmac_wake_bay(i, base); + enable_irq(pmac_ide_irq[i]); } break; } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 74b3f71c6..d2cb4b34b 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -526,7 +526,7 @@ void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int t * current_capacity() returns the capacity (in sectors) of a drive * according to its current geometry/LBA settings. */ -static unsigned long current_capacity (ide_drive_t *drive) +unsigned long current_capacity (ide_drive_t *drive) { if (!drive->present) return 0; @@ -1067,13 +1067,16 @@ static inline void start_request (ide_drive_t *drive) goto kill_rq; } block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0; -#if FAKE_FDISK_FOR_EZDRIVE - if (block == 0 && drive->remap_0_to_1) + + /* Yecch - this will shift the entire interval, + possibly killing some innocent following sector */ + if (block == 0 && drive->remap_0_to_1 == 1) block = 1; /* redirect MBR access to EZ-Drive partn table */ -#endif /* FAKE_FDISK_FOR_EZDRIVE */ + #if (DISK_RECOVERY_TIME > 0) while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME); #endif + SELECT_DRIVE(hwif, drive); if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { printk("%s: drive not ready for command\n", drive->name); @@ -1513,7 +1516,7 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs) * get_info_ptr() returns the (ide_drive_t *) for a given device number. * It returns NULL if the given device number does not match any present drives. */ -static ide_drive_t *get_info_ptr (kdev_t i_rdev) +ide_drive_t *get_info_ptr (kdev_t i_rdev) { int major = MAJOR(i_rdev); unsigned int h; @@ -1609,11 +1612,8 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio } spin_unlock_irqrestore(&io_request_lock, flags); do_hwgroup_request(hwgroup); - save_flags(flags); /* all CPUs; overkill? */ - cli(); /* all CPUs; overkill? */ if (action == ide_wait && rq->rq_status != RQ_INACTIVE) down(&sem); /* wait for it to be serviced */ - restore_flags(flags); /* all CPUs; overkill? */ return rq->errors ? -EIO : 0; /* return -EIO if errors */ } @@ -2321,10 +2321,11 @@ static int ide_ioctl (struct inode *inode, struct file *file, case HDIO_GETGEO: { struct hd_geometry *loc = (struct hd_geometry *) arg; + unsigned short bios_cyl = drive->bios_cyl; /* truncate */ if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT; if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT; - if (put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; + if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; @@ -2343,7 +2344,8 @@ static int ide_ioctl (struct inode *inode, struct file *file, return -EINVAL; if (drive->id == NULL) return -ENOMSG; - if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142)) + if (copy_to_user((char *)arg, (char *)drive->id, + (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142)) return -EFAULT; return 0; @@ -2629,6 +2631,7 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive * "hdx=cdrom" : drive is present, and is a cdrom drive * "hdx=cyl,head,sect" : disk drive is present, with specified geometry + * "hdx=noremap" : do not remap 0->1 even though EZD was detected * "hdx=autotune" : driver will attempt to tune interface speed * to the fastest PIO mode supported, * if possible for this drive only. @@ -2735,7 +2738,8 @@ void __init ide_setup (char *s) if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", "serialize", "autotune", "noautotune", - "slow", "swapdata", "bswap", "flash", NULL}; + "slow", "swapdata", "bswap", "flash", + "remap", "noremap", NULL}; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; unit = unit % MAX_DRIVES; @@ -2772,13 +2776,19 @@ void __init ide_setup (char *s) case -8: /* "slow" */ drive->slow = 1; goto done; - case -9: /* swapdata or bswap */ + case -9: /* "swapdata" or "bswap" */ case -10: drive->bswap = 1; goto done; - case -11: + case -11: /* "flash" */ drive->ata_flash = 1; goto done; + case -12: /* "remap" */ + drive->remap_0_to_1 = 1; + goto done; + case -13: /* "noremap" */ + drive->remap_0_to_1 = 2; + goto done; case 3: /* cyl,head,sect */ drive->media = ide_disk; drive->cyl = drive->bios_cyl = vals[0]; @@ -3023,113 +3033,6 @@ done: } /* - * This routine is called from the partition-table code in genhd.c - * to "convert" a drive to a logical geometry with fewer than 1024 cyls. - * - * The second parameter, "xparm", determines exactly how the translation - * will be handled: - * 0 = convert to CHS with fewer than 1024 cyls - * using the same method as Ontrack DiskManager. - * 1 = same as "0", plus offset everything by 63 sectors. - * -1 = similar to "0", plus redirect sector 0 to sector 1. - * >1 = convert to a CHS geometry with "xparm" heads. - * - * Returns 0 if the translation was not possible, if the device was not - * an IDE disk drive, or if a geometry was "forced" on the commandline. - * Returns 1 if the geometry translation was successful. - */ - -int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg) -{ - ide_drive_t *drive; - - static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0}; - const byte *heads = head_vals; - unsigned long tracks; - - drive = get_info_ptr(i_rdev); - if (!drive) - return 0; - - if (drive->forced_geom) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - return 0; - } - - if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - return 0; /* we already have a translation */ - } - - printk("%s ", msg); - - if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - return 0; /* small disk: no translation needed */ - } - - if (drive->id) { - drive->cyl = drive->id->cyls; - drive->head = drive->id->heads; - drive->sect = drive->id->sectors; - } - drive->bios_cyl = drive->cyl; - drive->bios_head = drive->head; - drive->bios_sect = drive->sect; - drive->special.b.set_geometry = 1; - - tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63; - drive->bios_sect = 63; - if (xparm > 1) { - drive->bios_head = xparm; - drive->bios_cyl = tracks / drive->bios_head; - } else { - while (drive->bios_cyl >= 1024) { - drive->bios_head = *heads; - drive->bios_cyl = tracks / drive->bios_head; - if (0 == *++heads) - break; - } -#if FAKE_FDISK_FOR_EZDRIVE - if (xparm == -1) { - drive->remap_0_to_1 = 1; - printk("[remap 0->1] "); - } else -#endif /* FAKE_FDISK_FOR_EZDRIVE */ - if (xparm == 1) { - drive->sect0 = 63; - drive->bios_cyl = (tracks - 1) / drive->bios_head; - printk("[remap +63] "); - } - } - - drive->part[0].nr_sects = current_capacity(drive); - printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect); - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - return 1; -} - -/* * probe_for_hwifs() finds/initializes "known" IDE interfaces */ static void __init probe_for_hwifs (void) @@ -3524,6 +3427,9 @@ EXPORT_SYMBOL(ide_register); EXPORT_SYMBOL(ide_unregister); EXPORT_SYMBOL(ide_setup_ports); +EXPORT_SYMBOL(get_info_ptr); +EXPORT_SYMBOL(current_capacity); + /* * This is gets invoked once during initialization, to set *everything* up */ diff --git a/drivers/block/pdc202xx.c b/drivers/block/pdc202xx.c index 5bbd0c3e7..aeffcc9ba 100644 --- a/drivers/block/pdc202xx.c +++ b/drivers/block/pdc202xx.c @@ -14,7 +14,7 @@ * 8 are UDMA supported and 4 are limited to DMA mode 2 multi-word. * The 8/4 ratio is a BIOS code limit by promise. * - * UNLESS you enable "PDC202XX_FORCE_BURST_BIT" + * UNLESS you enable "CONFIG_PDC202XX_FORCE_BURST_BIT" * * There is only one BIOS in the three contollers. * @@ -521,15 +521,15 @@ unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) (primary_mode & 1) ? "MASTER" : "PCI", (secondary_mode & 1) ? "MASTER" : "PCI" ); -#ifdef PDC202XX_FORCE_BURST_BIT +#ifdef CONFIG_PDC202XX_FORCE_BURST_BIT if (!(udma_speed_flag & 1)) { printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1)); outb(udma_speed_flag|1, high_16 + 0x001f); printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA"); } -#endif /* PDC202XX_FORCE_BURST_BIT */ +#endif /* CONFIG_PDC202XX_FORCE_BURST_BIT */ -#ifdef PDC202XX_FORCE_MASTER_MODE +#ifdef CONFIG_PDC202XX_FORCE_MASTER_MODE if (!(primary_mode & 1)) { printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ", name, primary_mode, (primary_mode|1)); @@ -543,7 +543,7 @@ unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) outb(secondary_mode|1, high_16 + 0x001b); printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI"); } -#endif /* PDC202XX_FORCE_MASTER_MODE */ +#endif /* CONFIG_PDC202XX_FORCE_MASTER_MODE */ return dev->irq; } diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c index eaf1a8357..95c25b519 100644 --- a/drivers/char/adbmouse.c +++ b/drivers/char/adbmouse.c @@ -35,8 +35,8 @@ #include <linux/random.h> #include <linux/poll.h> #include <linux/init.h> +#include <linux/adb_mouse.h> -#include <asm/adb_mouse.h> #ifdef __powerpc__ #include <asm/processor.h> #endif diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index 4620a3d60..42c2642f6 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -144,7 +144,7 @@ busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons) wake_up(&mse->wait); if (mse->fasyncptr) - kill_fasync(mse->fasyncptr, SIGIO); + kill_fasync(mse->fasyncptr, SIGIO, POLL_IN); } } @@ -450,13 +450,10 @@ bus_mouse_init(void) #ifdef CONFIG_ATARIMOUSE atari_mouse_init(); #endif -#ifdef CONFIG_MAC_MOUSE - mac_mouse_init(); -#endif #ifdef CONFIG_SUN_MOUSE sun_mouse_init(); #endif -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE adb_mouse_init(); #endif #ifdef CONFIG_RPCMOUSE diff --git a/drivers/char/console.c b/drivers/char/console.c index d0503cdcb..f452237ab 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -681,7 +681,7 @@ int vc_resize(unsigned int lines, unsigned int cols, else { unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER); if (!p) { - for (i = 0; i< currcons; i++) + for (i = first; i < currcons; i++) if (newscreens[i]) kfree_s(newscreens[i], ss); return -ENOMEM; diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c index 42fae3568..d8ac176c2 100644 --- a/drivers/char/defkeymap.c +++ b/drivers/char/defkeymap.c @@ -233,7 +233,6 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'A', 'A', '\305'}, {'a', 'a', '\345'}, {'A', 'E', '\306'}, {'a', 'e', '\346'}, {',', 'C', '\307'}, {',', 'c', '\347'}, - {'\'', 'C', '\307'}, {'\'', 'c', '\347'}, {'`', 'E', '\310'}, {'`', 'e', '\350'}, {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, {'^', 'E', '\312'}, {'^', 'e', '\352'}, @@ -260,4 +259,4 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'s', 'z', '\337'}, {'i', 'j', '\377'}, }; -unsigned int accent_table_size = 70; +unsigned int accent_table_size = 68; diff --git a/drivers/char/dn_keyb.c b/drivers/char/dn_keyb.c index 97b237ca3..38010e29b 100644 --- a/drivers/char/dn_keyb.c +++ b/drivers/char/dn_keyb.c @@ -469,7 +469,7 @@ static void dn_keyb_process_mouse_event(unsigned char mouse_data) { if (mouse_dy > 2048) mouse_dy = 2048; if (mouse_fasyncptr) - kill_fasync(mouse_fasyncptr, SIGIO); + kill_fasync(mouse_fasyncptr, SIGIO, POLL_IN); } mouse_byte_count=0; /* printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */ diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c index 12ab4d628..55deade81 100644 --- a/drivers/char/drm/fops.c +++ b/drivers/char/drm/fops.c @@ -197,7 +197,7 @@ int drm_write_string(drm_device_t *dev, const char *s) send -= count; } - if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_OUT); DRM_DEBUG("waking\n"); wake_up_interruptible(&dev->buf_readers); return 0; diff --git a/drivers/char/ftape/Config.in b/drivers/char/ftape/Config.in index 2f837be5b..59004dcf2 100644 --- a/drivers/char/ftape/Config.in +++ b/drivers/char/ftape/Config.in @@ -1,17 +1,17 @@ # # Ftape configuration # -dep_tristate 'Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE +dep_tristate ' Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE if [ "$CONFIG_ZFTAPE" != "n" ]; then - int 'Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240 - comment 'The compressor will be built as a module only!' - define_bool CONFIG_ZFT_COMPRESSOR m + int ' Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240 + comment ' The compressor will be built as a module only!' + define_bool CONFIG_ZFT_COMPRESSOR m fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - int 'Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3 + int ' Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3 fi if [ "$CONFIG_PROC_FS" = "y" ]; then - bool 'Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS y + bool ' Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS fi choice 'Debugging output' \ "Normal CONFIG_FT_NORMAL_DEBUG \ @@ -25,14 +25,14 @@ choice 'Floppy tape controllers' \ FC-10/FC-20 CONFIG_FT_PROBE_FC10 \ Alt/82078 CONFIG_FT_ALT_FDC" Standard if [ "$CONFIG_FT_STD_FDC" != "y" ]; then - comment ' Consult the manuals of your tape drive for the correct settings!' - hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0 - int ' IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0 - int ' DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0 + comment ' Consult the manuals of your tape drive for the correct settings!' + hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0 + int ' IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0 + int ' DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - int 'Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8 - int 'Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000 + int ' Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8 + int ' Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000 fi comment 'ONLY for DEC Alpha architectures' -int 'CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0 +int ' CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0 diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in index a41eaac33..810decada 100644 --- a/drivers/char/joystick/Config.in +++ b/drivers/char/joystick/Config.in @@ -2,18 +2,18 @@ # Joystick lowlevel driver configuration # -dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK -dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK -dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK -dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK -dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK -dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK -dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK +dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK +dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK +dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK +dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK +dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK +dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK +dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT + dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT fi if [ "$CONFIG_AMIGA" = "y" ]; then - dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK + dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK fi diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index def0b7c19..5989fbff5 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -32,6 +32,7 @@ #include <linux/string.h> #include <linux/random.h> #include <linux/init.h> +#include <linux/module.h> #include <asm/keyboard.h> #include <asm/bitops.h> diff --git a/drivers/char/mem.c b/drivers/char/mem.c index ff3158e96..9be0a51f5 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -50,7 +50,7 @@ extern void prom_con_init(void); #ifdef CONFIG_MDA_CONSOLE extern void mda_console_init(void); #endif -#if defined(CONFIG_PPC) || defined(CONFIG_MAC) +#if defined(CONFIG_ADB) extern void adbdev_init(void); #endif #ifdef CONFIG_USB @@ -268,7 +268,8 @@ static ssize_t write_kmem(struct file * file, const char * buf, return do_write_mem(file, (void*)p, p, buf, count, ppos); } -#if !defined(CONFIG_PPC) && !defined(__mc68000__) +#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \ + defined(CONFIG_HAVE_IO_PORTS) static ssize_t read_port(struct file * file, char * buf, size_t count, loff_t *ppos) { @@ -517,7 +518,8 @@ static struct file_operations null_fops = { NULL /* fsync */ }; -#if !defined(CONFIG_PPC) && !defined(__mc68000__) +#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \ + defined(CONFIG_HAVE_IO_PORTS) static struct file_operations port_fops = { memory_lseek, read_port, @@ -571,7 +573,8 @@ static int memory_open(struct inode * inode, struct file * filp) case 3: filp->f_op = &null_fops; break; -#if !defined(CONFIG_PPC) && !defined(__mc68000__) +#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \ + defined(CONFIG_HAVE_IO_PORTS) case 4: filp->f_op = &port_fops; break; @@ -667,7 +670,7 @@ int __init chr_dev_init(void) #ifdef CONFIG_VIDEO_BT848 i2c_init(); #endif -#if defined(CONFIG_PPC) || defined(CONFIG_MAC) +#if defined(CONFIG_ADB) adbdev_init(); #endif #ifdef CONFIG_VIDEO_DEV diff --git a/drivers/char/misc.c b/drivers/char/misc.c index e9eec975c..c30f10594 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -261,6 +261,12 @@ int __init misc_init(void) #ifdef CONFIG_NWFLASH nwflash_init(); #endif +#ifdef CONFIG_SGI_NEWPORT_GFX + gfx_register (); +#endif +#ifdef CONFIG_SGI + streamable_init (); +#endif if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 7251a4e05..0717fa418 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -9,7 +9,7 @@ * Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> * * Original release 01/11/99 - * ==FILEDATE 19990524== + * ==FILEDATE 19990901== * * This code is released under the GNU General Public License (GPL) * @@ -21,8 +21,13 @@ * 1. tty write calls represent one complete transmit frame of data * The device driver should accept the complete frame or none of * the frame (busy) in the write method. Each write call should have - * a byte count in the range of 2-4096 bytes (2 is min HDLC frame - * with 1 addr byte and 1 ctrl byte). + * a byte count in the range of 2-65535 bytes (2 is min HDLC frame + * with 1 addr byte and 1 ctrl byte). The max byte count of 65535 + * should include any crc bytes required. For example, when using + * CCITT CRC32, 4 crc bytes are required, so the maximum size frame + * the application may transmit is limited to 65531 bytes. For CCITT + * CRC16, the maximum application frame size would be 65533. + * * * 2. receive callbacks from the device driver represents * one received frame. The device driver should bypass @@ -73,7 +78,7 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "1.2" +#define HDLC_VERSION "1.11" #include <linux/version.h> #include <linux/config.h> @@ -100,6 +105,7 @@ #include <linux/malloc.h> #include <linux/tty.h> #include <linux/errno.h> +#include <linux/sched.h> /* to get the struct task_struct */ #include <linux/string.h> /* used in new tty drivers */ #include <linux/signal.h> /* used in new tty drivers */ #include <asm/system.h> @@ -113,6 +119,13 @@ #include <linux/kerneld.h> #endif +#if LINUX_VERSION_CODE < VERSION(2,3,0) +typedef struct wait_queue *wait_queue_head_t; +#define DECLARE_WAITQUEUE(name,task) struct wait_queue (name) = {(task),NULL} +#define init_waitqueue_head(head) *(head) = NULL +#define set_current_state(a) current->state = (a) +#endif + #if LINUX_VERSION_CODE >= VERSION(2,1,4) #include <asm/segment.h> #define GET_USER(error,value,addr) error = get_user(value,addr) @@ -189,18 +202,21 @@ typedef size_t rw_count_t; /* * Buffers for individual HDLC frames */ -#define MAX_HDLC_FRAME_SIZE 4096 +#define MAX_HDLC_FRAME_SIZE 65535 #define DEFAULT_RX_BUF_COUNT 10 -#define MAX_RX_BUF_COUNT 30 +#define MAX_RX_BUF_COUNT 60 #define DEFAULT_TX_BUF_COUNT 1 + typedef struct _n_hdlc_buf { struct _n_hdlc_buf *link; int count; - char buf[MAX_HDLC_FRAME_SIZE]; + char buf[1]; } N_HDLC_BUF; +#define N_HDLC_BUF_SIZE (sizeof(N_HDLC_BUF)+maxframe) + typedef struct _n_hdlc_buf_list { N_HDLC_BUF *head; @@ -246,12 +262,16 @@ static struct n_hdlc *n_hdlc_alloc (void); #if LINUX_VERSION_CODE >= VERSION(2,1,19) MODULE_PARM(debuglevel, "i"); +MODULE_PARM(maxframe, "i"); #endif /* debug level can be set by insmod for debugging purposes */ #define DEBUG_LEVEL_INFO 1 int debuglevel=0; +/* max frame size for memory allocations */ +ssize_t maxframe=4096; + /* TTY callbacks */ static rw_ret_t n_hdlc_tty_read(struct tty_struct *, @@ -353,6 +373,9 @@ static void n_hdlc_tty_close(struct tty_struct *tty) printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n"); return; } +#if defined(TTY_NO_WRITE_SPLIT) + clear_bit(TTY_NO_WRITE_SPLIT,&tty->flags); +#endif tty->disc_data = NULL; if (tty == n_hdlc->backup_tty) n_hdlc->backup_tty = 0; @@ -383,7 +406,9 @@ static int n_hdlc_tty_open (struct tty_struct *tty) struct n_hdlc *n_hdlc = tty2n_hdlc (tty); if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_open() called\n",__FILE__,__LINE__); + printk("%s(%d)n_hdlc_tty_open() called (major=%u,minor=%u)\n", + __FILE__,__LINE__, + MAJOR(tty->device), MINOR(tty->device)); /* There should not be an existing table for this slot. */ if (n_hdlc) { @@ -399,9 +424,14 @@ static int n_hdlc_tty_open (struct tty_struct *tty) tty->disc_data = n_hdlc; n_hdlc->tty = tty; - + MOD_INC_USE_COUNT; +#if defined(TTY_NO_WRITE_SPLIT) + /* change tty_io write() to not split large writes into 8K chunks */ + set_bit(TTY_NO_WRITE_SPLIT,&tty->flags); +#endif + /* Flush any pending characters in the driver and discipline. */ if (tty->ldisc.flush_buffer) @@ -597,18 +627,26 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, return; } + if ( count>maxframe ) { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d) rx count>maxframesize, data discarded\n", + __FILE__,__LINE__); + return; + } + /* get a free HDLC buffer */ buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); if (!buf) { /* no buffers in free list, attempt to allocate another rx buffer */ /* unless the maximum count has been reached */ if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) - buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_ATOMIC); + buf = (N_HDLC_BUF*)kmalloc(N_HDLC_BUF_SIZE,GFP_ATOMIC); } if (!buf) { - printk("%s(%d) no more rx buffers, data discarded\n", - __FILE__,__LINE__); + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d) no more rx buffers, data discarded\n", + __FILE__,__LINE__); return; } @@ -622,7 +660,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, /* wake up any blocked reads and perform async signalling */ wake_up_interruptible (&n_hdlc->read_wait); if (n_hdlc->tty->fasync != NULL) - kill_fasync (n_hdlc->tty->fasync, SIGIO); + kill_fasync (n_hdlc->tty->fasync, SIGIO, POLL_IN); } /* end of n_hdlc_tty_receive() */ @@ -678,12 +716,11 @@ static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, if (file->f_flags & O_NONBLOCK) return -EAGAIN; - /* TODO: no timeout? current->timeout = 0;*/ interruptible_sleep_on (&n_hdlc->read_wait); if (signal_pending(current)) return -EINTR; } - + if (rbuf->count > nr) { /* frame too large for caller's buffer (discard frame) */ ret = (rw_ret_t)-EOVERFLOW; @@ -739,13 +776,13 @@ static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, return -EIO; /* verify frame size */ - if (count > MAX_HDLC_FRAME_SIZE) { + if (count > maxframe ) { if (debuglevel & DEBUG_LEVEL_INFO) printk (KERN_WARNING "n_hdlc_tty_write: truncating user packet " "from %lu to %d\n", (unsigned long) count, - MAX_HDLC_FRAME_SIZE); - count = MAX_HDLC_FRAME_SIZE; + maxframe ); + count = maxframe; } /* Allocate transmit buffer */ @@ -754,8 +791,7 @@ static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, /* sleep until transmit buffer available */ add_wait_queue(&n_hdlc->write_wait, &wait); while (!tbuf) { - /* TODO: no timeout? current->timeout = 0;*/ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule(); n_hdlc = tty2n_hdlc (tty); @@ -773,7 +809,7 @@ static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&n_hdlc->write_wait, &wait); } @@ -1000,16 +1036,20 @@ static struct n_hdlc *n_hdlc_alloc (void) /* allocate free rx buffer list */ for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { - buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL); + buf = (N_HDLC_BUF*)kmalloc(N_HDLC_BUF_SIZE,GFP_KERNEL); if (buf) n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf); + else if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_alloc(), kalloc() failed for rx buffer %d\n",__FILE__,__LINE__, i); } - /* allocate free rx buffer list */ + /* allocate free tx buffer list */ for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) { - buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL); + buf = (N_HDLC_BUF*)kmalloc(N_HDLC_BUF_SIZE,GFP_KERNEL); if (buf) n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf); + else if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_alloc(), kalloc() failed for tx buffer %d\n",__FILE__,__LINE__, i); } /* Initialize the control block */ @@ -1108,7 +1148,14 @@ int init_module(void) static struct tty_ldisc n_hdlc_ldisc; int status; - printk("HDLC line discipline: version %s\n", szVersion); + /* range check maxframe arg */ + if ( maxframe<4096) + maxframe=4096; + else if ( maxframe>65535) + maxframe=65535; + + printk("HDLC line discipline: version %s, maxframe=%u\n", + szVersion, maxframe); /* Register the tty discipline */ diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index d95cda30e..9027aa67e 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -604,7 +604,7 @@ send_signal: tty->canon_head = tty->read_head; tty->canon_data++; if (tty->fasync) - kill_fasync(tty->fasync, SIGIO); + kill_fasync(tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); return; @@ -706,7 +706,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { if (tty->fasync) - kill_fasync(tty->fasync, SIGIO); + kill_fasync(tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); } @@ -854,6 +854,7 @@ static inline int copy_from_read_buf(struct tty_struct *tty, retval = 0; n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail)); if (n) { + mb(); retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); n -= retval; tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c index 0c00dcfa5..7e4518e15 100644 --- a/drivers/char/pc110pad.c +++ b/drivers/char/pc110pad.c @@ -30,6 +30,8 @@ #include <asm/signal.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/semaphore.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> #include "pc110pad.h" @@ -51,7 +53,7 @@ static struct pc110pad_params current_params; static wait_queue_head_t queue; static struct fasync_struct *asyncptr; static int active=0; /* number of concurrent open()s */ - +static struct semaphore read_lock; /* * Utility to reset a timer to go off some time in the future. @@ -75,7 +77,7 @@ static void wake_readers(void) { wake_up_interruptible(&queue); if(asyncptr) - kill_fasync(asyncptr, SIGIO); + kill_fasync(asyncptr, SIGIO, POLL_IN); } @@ -503,10 +505,13 @@ static int close_pad(struct inode * inode, struct file * file) */ static int open_pad(struct inode * inode, struct file * file) { + unsigned long flags; + if (active++) return 0; MOD_INC_USE_COUNT; + save_flags(flags); cli(); outb(0x30, current_params.io+2); /* switch off digitiser */ pad_irq(0,0,0); /* read to flush any pending bytes */ @@ -522,7 +527,7 @@ static int open_pad(struct inode * inode, struct file * file) synthesize_tap=0; del_timer(&bounce_timer); del_timer(&tap_timer); - sti(); + restore_flags(flags); return 0; } @@ -556,14 +561,19 @@ static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t { int r; + down(&read_lock); for(r=0; r<count; r++) { if(!read_byte_count) new_sample(read_bytes); if(put_user(read_bytes[read_byte_count], buffer+r)) - return -EFAULT; + { + r = -EFAULT; + break; + } read_byte_count = (read_byte_count+1)%3; } + up(&read_lock); return r; } @@ -681,6 +691,7 @@ static void pc110pad_unload(void) int init_module(void) { + init_MUTEX(&read_lock); return pc110pad_init(); } diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 2acab7d8f..1118cf950 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -37,6 +37,8 @@ #include <asm/irq.h> #include <asm/system.h> +#include <asm/io.h> + /* Some configuration switches are present in the include file... */ #include <linux/pc_keyb.h> @@ -100,7 +102,6 @@ static unsigned char mouse_reply_expected = 0; static void kb_wait(void) { unsigned long timeout = KBC_TIMEOUT; - unsigned char status; do { /* @@ -404,7 +405,7 @@ static inline void handle_mouse_event(unsigned char scancode) if (head != queue->tail) { queue->head = head; if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); + kill_fasync(queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); } } @@ -421,32 +422,43 @@ static inline void handle_mouse_event(unsigned char scancode) static unsigned char handle_kbd_event(void) { unsigned char status = kbd_read_status(); + unsigned int work = 10000; while (status & KBD_STAT_OBF) { unsigned char scancode; scancode = kbd_read_input(); - if (status & KBD_STAT_MOUSE_OBF) { handle_mouse_event(scancode); } else { +#ifdef CONFIG_VT if (do_acknowledge(scancode)) handle_scancode(scancode, !(scancode & 0x80)); +#endif mark_bh(KEYBOARD_BH); } status = kbd_read_status(); + + if(!work--) + { + printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", + status); + break; + } } return status; } + static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; +#ifdef CONFIG_VT kbd_pt_regs = regs; - +#endif spin_lock_irqsave(&kbd_controller_lock, flags); handle_kbd_event(); spin_unlock_irqrestore(&kbd_controller_lock, flags); @@ -660,9 +672,9 @@ static char * __init initialize_kbd(void) kbd_write_command_w(KBD_CCMD_WRITE_MODE); kbd_write_output_w(KBD_MODE_KBD_INT - | KBD_MODE_SYS - | KBD_MODE_DISABLE_MOUSE - | KBD_MODE_KCC); + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ kbd_write_command_w(KBD_CCMD_READ_MODE); @@ -697,6 +709,8 @@ static char * __init initialize_kbd(void) void __init pckbd_init_hw(void) { + kbd_request_region(); + /* Flush any pending input. */ kbd_clear_input(); @@ -832,7 +846,7 @@ static int release_aux(struct inode * inode, struct file * file) return 0; kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE); - aux_free_irq(inode); + aux_free_irq(AUX_DEV); return 0; } @@ -847,7 +861,7 @@ static int open_aux(struct inode * inode, struct file * file) return 0; } queue->head = queue->tail = 0; /* Flush input queue */ - if (aux_request_irq(keyboard_interrupt, inode)) { + if (aux_request_irq(keyboard_interrupt, AUX_DEV)) { aux_count--; return -EBUSY; } diff --git a/drivers/char/pms.c b/drivers/char/pms.c index 72653aa19..c96570b73 100644 --- a/drivers/char/pms.c +++ b/drivers/char/pms.c @@ -22,6 +22,7 @@ #include <linux/malloc.h> #include <linux/mm.h> #include <linux/ioport.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/sched.h> #include <linux/videodev.h> @@ -1024,21 +1025,11 @@ static int init_mediavision(void) return 0; } -static void shutdown_mediavision(void) -{ - release_region(io_port,3); - release_region(0x9A01, 1); -} - /* - * Module stuff + * Initialization and module stuff */ -#ifdef MODULE -int init_module(void) -#else -int init_pms_cards(struct video_init *v) -#endif +static int __init init_pms_cards(void) { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); @@ -1057,15 +1048,21 @@ int init_pms_cards(struct video_init *v) return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER); } -#ifdef MODULE - MODULE_PARM(io_port,"i"); MODULE_PARM(mem_base,"i"); -void cleanup_module(void) +static void __exit shutdown_mediavision(void) +{ + release_region(io_port,3); + release_region(0x9A01, 1); +} + +static void __exit cleanup_pms_module(void) { shutdown_mediavision(); video_unregister_device((struct video_device *)&pms_device); } -#endif +module_init(init_pms_cards); +module_exit(cleanup_pms_module); + diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index c210b0785..d73f128d8 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -34,8 +34,12 @@ * YIELD parport_yield_blocking * WCTLONIRQ on interrupt, set control lines * CLRIRQ clear (and return) interrupt count + * SETTIME sets device timeout (struct timeval) + * GETTIME gets device timeout (struct timeval) * read/write read or write in current IEEE 1284 protocol * select wait for interrupt (in readfds) + * + * Added SETTIME/GETTIME ioctl, Fred Barnes 1999. */ #include <linux/module.h> @@ -74,6 +78,9 @@ struct pp_struct { #define PP_BUFFER_SIZE 256 #define PARDEVICE_MAX 8 +/* ROUND_UP macro from fs/select.c */ +#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) + static inline void enable_irq (struct pp_struct *pp) { struct parport *port = pp->pdev->port; @@ -356,6 +363,8 @@ static int pp_ioctl(struct inode *inode, struct file *file, unsigned char mask; int mode; int ret; + struct timeval par_timeout; + long to_jiffies; case PPRSTATUS: reg = parport_read_status (port); @@ -451,6 +460,33 @@ static int pp_ioctl(struct inode *inode, struct file *file, atomic_sub (ret, &pp->irqc); return 0; + case PPSETTIME: + if (copy_from_user (&par_timeout, (struct timeval *)arg, + sizeof(struct timeval))) { + return -EFAULT; + } + /* Convert to jiffies, place in pp->pdev->timeout */ + if ((par_timeout.tv_sec < 0) || (par_timeout.tv_usec < 0)) { + return -EINVAL; + } + to_jiffies = ROUND_UP(par_timeout.tv_usec, 1000000/HZ); + to_jiffies += par_timeout.tv_sec * (long)HZ; + if (to_jiffies <= 0) { + return -EINVAL; + } + pp->pdev->timeout = to_jiffies; + return 0; + + case PPGETTIME: + to_jiffies = pp->pdev->timeout; + par_timeout.tv_sec = to_jiffies / HZ; + par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ); + if (copy_to_user ((struct timeval *)arg, &par_timeout, + sizeof(struct timeval))) { + return -EFAULT; + } + return 0; + default: printk (KERN_DEBUG CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd); diff --git a/drivers/char/ppdev.h b/drivers/char/ppdev.h index 976374aed..f466f11e6 100644 --- a/drivers/char/ppdev.h +++ b/drivers/char/ppdev.h @@ -10,6 +10,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * Added PPGETTIME/PPSETTIME, Fred Barnes, 1999 */ #define PP_MAJOR 99 @@ -72,3 +73,9 @@ struct ppdev_frob_struct { /* Set the IEEE 1284 phase that we're in (e.g. IEEE1284_PH_FWD_IDLE) */ #define PPSETPHASE _IOW(PP_IOCTL, 0x94, int) + +/* Set and get port timeout (struct timeval's) */ +#define PPGETTIME _IOR(PP_IOCTL, 0x95, struct timeval) +#define PPSETTIME _IOW(PP_IOCTL, 0x96, struct timeval) + + diff --git a/drivers/char/qpmouse.c b/drivers/char/qpmouse.c index 8d5f9aa34..3de17ad5c 100644 --- a/drivers/char/qpmouse.c +++ b/drivers/char/qpmouse.c @@ -134,7 +134,7 @@ static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) } queue->head = head; if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); + kill_fasync(queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 7d99d5b68..17711734f 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -661,25 +661,31 @@ static inline ssize_t do_tty_write( if (down_interruptible(&inode->i_sem)) { return -ERESTARTSYS; } - for (;;) { - unsigned long size = PAGE_SIZE*2; - if (size > count) - size = count; + if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) { lock_kernel(); - ret = write(tty, file, buf, size); + written = write(tty, file, buf, count); unlock_kernel(); - if (ret <= 0) - break; - written += ret; - buf += ret; - count -= ret; - if (!count) - break; - ret = -ERESTARTSYS; - if (signal_pending(current)) - break; - if (current->need_resched) - schedule(); + } else { + for (;;) { + unsigned long size = PAGE_SIZE*2; + if (size > count) + size = count; + lock_kernel(); + ret = write(tty, file, buf, size); + unlock_kernel(); + if (ret <= 0) + break; + written += ret; + buf += ret; + count -= ret; + if (!count) + break; + ret = -ERESTARTSYS; + if (signal_pending(current)) + break; + if (current->need_resched) + schedule(); + } } if (written) { file->f_dentry->d_inode->i_mtime = CURRENT_TIME; @@ -1996,7 +2002,8 @@ int tty_unregister_driver(struct tty_driver *driver) { int retval; struct tty_driver *p; - int found = 0; + int i, found = 0; + struct termios *tp; const char *othername = NULL; if (*driver->refcount) @@ -2027,6 +2034,23 @@ int tty_unregister_driver(struct tty_driver *driver) if (driver->next) driver->next->prev = driver->prev; + /* + * Free the termios and termios_locked structures because + * we don't want to get memory leaks when modular tty + * drivers are removed from the kernel. + */ + for (i = 0; i < driver->num; i++) { + tp = driver->termios[i]; + if (tp) { + driver->termios[i] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tp = driver->termios_locked[i]; + if (tp) { + driver->termios_locked[i] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + } proc_tty_unregister_driver(driver); return 0; } diff --git a/drivers/fc4/Config.in b/drivers/fc4/Config.in index 8328f32bd..a11ab5b3e 100644 --- a/drivers/fc4/Config.in +++ b/drivers/fc4/Config.in @@ -6,22 +6,22 @@ comment 'Fibre Channel support' tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4 if [ ! "$CONFIG_FC4" = "n" ]; then - comment 'FC4 drivers' - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC - tristate 'Sun SOC+ (aka SOCAL)' CONFIG_FC4_SOCAL - fi - comment 'FC4 targets' - dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO $CONFIG_SCSI - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - dep_tristate 'Sun Enterprise Network Array (A5000 and EX500)' CONFIG_SCSI_FCAL $CONFIG_SCSI - else - dep_tristate 'Generic FC-AL disk driver' CONFIG_SCSI_FCAL $CONFIG_SCSI - fi + comment 'FC4 drivers' + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC + tristate 'Sun SOC+ (aka SOCAL)' CONFIG_FC4_SOCAL + fi + comment 'FC4 targets' + dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO $CONFIG_SCSI + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + dep_tristate 'Sun Enterprise Network Array (A5000 and EX500)' CONFIG_SCSI_FCAL $CONFIG_SCSI + else + dep_tristate 'Generic FC-AL disk driver' CONFIG_SCSI_FCAL $CONFIG_SCSI + fi else - define_bool CONFIG_FC4_SOC n - define_bool CONFIG_FC4_SOCAL n - define_bool CONFIG_SCSI_PLUTO n - define_bool CONFIG_SCSI_FCAL n + define_bool CONFIG_FC4_SOC n + define_bool CONFIG_FC4_SOCAL n + define_bool CONFIG_SCSI_PLUTO n + define_bool CONFIG_SCSI_FCAL n fi endmenu diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 9a5759fef..dc14d2ce6 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -13,13 +13,11 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) L_TARGET := macintosh.a +L_OBJS := M_OBJS := -ifndef CONFIG_MBX -L_OBJS := via-cuda.o macio-adb.o via-pmu.o mediabay.o -endif -ifeq ($(CONFIG_MAC_KEYBOARD),y) -LX_OBJS := adb.o +ifeq ($(CONFIG_PMAC_PBOOK),y) + L_OBJS += mediabay.o endif ifeq ($(CONFIG_MAC_SERIAL),y) @@ -38,8 +36,36 @@ else endif endif -ifdef CONFIG_MAC_KEYBOARD -L_OBJS += mac_keyb.o +ifdef CONFIG_ADB + LX_OBJS := adb.o +endif + +ifdef CONFIG_ADB_KEYBOARD + L_OBJS += mac_keyb.o +endif + +ifdef CONFIG_ADB_MACII + L_OBJS += via-macii.o +endif + +ifdef CONFIG_ADB_MACIISI + L_OBJS += via-maciisi.o +endif + +ifdef CONFIG_ADB_CUDA + L_OBJS += via-cuda.o +endif + +ifdef CONFIG_ADB_IOP + L_OBJS += adb-iop.o +endif + +ifdef CONFIG_ADB_PMU + L_OBJS += via-pmu.o +endif + +ifdef CONFIG_ADB_MACIO + L_OBJS += macio-adb.o endif include $(TOPDIR)/Rules.make diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index be5c45a6c..73a53a6f4 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -24,29 +24,59 @@ #include <linux/fs.h> #include <linux/mm.h> #include <linux/sched.h> +#include <linux/adb.h> +#include <linux/cuda.h> +#include <linux/pmu.h> +#include <linux/notifier.h> #include <linux/wait.h> -#include <asm/prom.h> -#include <asm/adb.h> -#include <asm/cuda.h> -#include <asm/pmu.h> +#include <linux/init.h> #include <asm/uaccess.h> +#ifdef CONFIG_PPC +#include <asm/prom.h> #include <asm/hydra.h> -#include <linux/init.h> +#endif EXPORT_SYMBOL(adb_controller); EXPORT_SYMBOL(adb_client_list); -EXPORT_SYMBOL(adb_hardware); -struct adb_controller *adb_controller = NULL; +extern struct adb_driver via_macii_driver; +extern struct adb_driver via_maciisi_driver; +extern struct adb_driver via_cuda_driver; +extern struct adb_driver adb_iop_driver; +extern struct adb_driver via_pmu_driver; +extern struct adb_driver macio_adb_driver; + +static struct adb_driver *adb_driver_list[] = { +#ifdef CONFIG_ADB_MACII + &via_macii_driver, +#endif +#ifdef CONFIG_ADB_MACIISI + &via_maciisi_driver, +#endif +#ifdef CONFIG_ADB_CUDA + &via_cuda_driver, +#endif +#ifdef CONFIG_ADB_IOP + &adb_iop_driver, +#endif +#ifdef CONFIG_ADB_PMU + &via_pmu_driver, +#endif +#ifdef CONFIG_ADB_MACIO + &macio_adb_driver, +#endif + NULL +}; + +struct adb_driver *adb_controller; struct notifier_block *adb_client_list = NULL; -enum adb_hw adb_hardware = ADB_NONE; +static int adb_got_sleep = 0; #ifdef CONFIG_PMAC_PBOOK -static int adb_notify_sleep(struct notifier_block *, unsigned long, void *); -static struct notifier_block adb_sleep_notifier = { +static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, - NULL, - 0 + SLEEP_LEVEL_ADB, }; #endif @@ -109,6 +139,15 @@ static int adb_scan_bus(void) adb_request(&req, NULL, ADBREQ_SYNC, 3, (i<< 4) | 0xb, (highFree | 0x60), 0xfe); /* + * See if anybody actually moved. This is suggested + * by HW TechNote 01: + * + * http://developer.apple.com/technotes/hw/hw_01.html + */ + adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, + (highFree << 4) | 0xf); + if (req.reply_len <= 1) continue; + /* * Test whether there are any device(s) left * at address i. */ @@ -159,49 +198,73 @@ static int adb_scan_bus(void) return devmask; } -void adb_init(void) +int __init adb_init(void) { + struct adb_driver *driver; + int i; + +#ifdef CONFIG_PPC if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return; + return 0; +#endif +#ifdef CONFIG_MAC + if (!MACH_IS_MAC) + return 0; +#endif - via_cuda_init(); - via_pmu_init(); - macio_adb_init(); - - if (adb_controller == NULL) + adb_controller = NULL; + + i = 0; + while ((driver = adb_driver_list[i++]) != NULL) { + if (!driver->probe()) { + adb_controller = driver; + break; + } + } + if ((adb_controller == NULL) || adb_controller->init()) { printk(KERN_WARNING "Warning: no ADB interface detected\n"); - else - { - adb_hardware = adb_controller->kind; + } else { #ifdef CONFIG_PMAC_PBOOK - notifier_chain_register(&sleep_notifier_list, - &adb_sleep_notifier); + pmu_register_sleep_notifier(&adb_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ adb_reset_bus(); } + return 0; } +__initcall(adb_init); #ifdef CONFIG_PMAC_PBOOK /* * notify clients before sleep and reset bus afterwards */ int -adb_notify_sleep(struct notifier_block *this, unsigned long code, void *x) +adb_notify_sleep(struct pmu_sleep_notifier *self, int when) { int ret; - switch (code) { - case PBOOK_SLEEP: - ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); - if (ret & NOTIFY_STOP_MASK) - return -EBUSY; - case PBOOK_WAKE: + switch (when) { + case PBOOK_SLEEP_REQUEST: + adb_got_sleep = 1; + ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); + if (ret & NOTIFY_STOP_MASK) + return PBOOK_SLEEP_REFUSE; + break; + case PBOOK_SLEEP_REJECT: + if (adb_got_sleep) { + adb_got_sleep = 0; adb_reset_bus(); - break; + } + break; + + case PBOOK_SLEEP_NOW: + break; + case PBOOK_WAKE: + adb_reset_bus(); + break; } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ @@ -277,6 +340,9 @@ adb_request(struct adb_request *req, void (*done)(struct adb_request *), req->data[i+1] = va_arg(list, int); va_end(list); + if (flags & ADBREQ_NOSEND) + return 0; + return adb_controller->send_request(req, flags & ADBREQ_SYNC); } @@ -413,7 +479,7 @@ static int adb_open(struct inode *inode, struct file *file) { struct adbdev_state *state; - if (MINOR(inode->i_rdev) > 0 || (adb_controller == NULL)/*adb_hardware == ADB_NONE*/) + if (MINOR(inode->i_rdev) > 0 || adb_controller == NULL) return -ENXIO; state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL); if (state == 0) @@ -540,6 +606,7 @@ static ssize_t adb_write(struct file *file, const char *buf, goto out; atomic_inc(&state->n_pending); + if (adb_controller == NULL) return -ENXIO; /* Special case for ADB_BUSRESET request, all others are sent to the controller */ @@ -582,8 +649,15 @@ static struct file_operations adb_fops = { void adbdev_init() { +#ifdef CONFIG_PPC if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return; + return; +#endif +#ifdef CONFIG_MAC + if (!MACH_IS_MAC) + return; +#endif + if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); } diff --git a/drivers/macintosh/mac_keyb.c b/drivers/macintosh/mac_keyb.c index 76c118474..93be8f08f 100644 --- a/drivers/macintosh/mac_keyb.c +++ b/drivers/macintosh/mac_keyb.c @@ -39,12 +39,13 @@ #include <linux/init.h> #include <linux/tty_flip.h> #include <linux/config.h> +#include <linux/notifier.h> #include <asm/bitops.h> -#include <asm/adb.h> -#include <asm/cuda.h> -#include <asm/pmu.h> +#include <linux/adb.h> +#include <linux/cuda.h> +#include <linux/pmu.h> #include <linux/kbd_kern.h> #include <linux/kbd_ll.h> @@ -239,7 +240,7 @@ static void init_trackball(int id); static void init_turbomouse(int id); static void init_microspeed(int id); -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE /* XXX: Hook for mouse driver */ void (*adb_mouse_interrupt_hook)(unsigned char *, int); int adb_emulate_buttons = 0; @@ -250,6 +251,7 @@ int adb_button3_keycode = 0x7c; /* right option key */ extern int console_loglevel; extern struct kbd_struct kbd_table[]; +extern wait_queue_head_t keypress_wait; extern void handle_scancode(unsigned char, int); @@ -334,7 +336,7 @@ input_keycode(int keycode, int repeat) if (!repeat) del_timer(&repeat_timer); -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE /* * XXX: Add mouse button 2+3 fake codes here if mouse open. * Keep track of 'button' states here as we only send @@ -364,7 +366,7 @@ input_keycode(int keycode, int repeat) } return; } -#endif /* CONFIG_ADBMOUSE */ +#endif /* CONFIG_ADB_MOUSE */ if (kbd->kbdmode != VC_RAW) { if (!up_flag && !dont_repeat[keycode]) { @@ -420,7 +422,7 @@ static void mac_put_queue(int ch) } } -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE static void mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) { @@ -552,7 +554,7 @@ mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) } } } -#endif /* CONFIG_ADBMOUSE */ +#endif /* CONFIG_ADB_MOUSE */ /* XXX Needs to get rid of this, see comments in pmu.c */ extern int backlight_level; @@ -566,7 +568,11 @@ buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) */ /* Ignore data from register other than 0 */ +#if 0 if ((adb_hardware != ADB_VIAPMU) || (data[0] & 0x3) || (nb < 2)) +#else + if ((data[0] & 0x3) || (nb < 2)) +#endif return; switch (data[1]&0xf ) @@ -592,22 +598,26 @@ buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) /* brightness decrease */ case 0xa: /* down event */ +#ifdef CONFIG_PPC if ( data[1] == (data[1]&0xf) ) { if (backlight_level > 2) pmu_set_brightness(backlight_level-2); else pmu_set_brightness(0); } +#endif break; /* brightness increase */ case 0x9: /* down event */ +#ifdef CONFIG_PPC if ( data[1] == (data[1]&0xf) ) { if (backlight_level < 0x1e) pmu_set_brightness(backlight_level+2); else pmu_set_brightness(0x1f); } +#endif break; } } @@ -672,8 +682,14 @@ static void leds_done(struct adb_request *req) void __init mackbd_init_hw(void) { +#ifdef CONFIG_PPC if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return; + return; +#endif +#ifdef CONFIG_MAC + if (!MACH_IS_MAC) + return; +#endif /* setup key map */ memcpy(key_maps[0], macplain_map, sizeof(plain_map)); @@ -684,7 +700,7 @@ void __init mackbd_init_hw(void) memcpy(key_maps[8], macalt_map, sizeof(plain_map)); memcpy(key_maps[12], macctrl_alt_map, sizeof(plain_map)); -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE /* initialize mouse interrupt hook */ adb_mouse_interrupt_hook = NULL; #endif @@ -720,9 +736,9 @@ mackeyb_probe(void) struct adb_request req; int i; -#ifdef CONFIG_ADBMOUSE +#ifdef CONFIG_ADB_MOUSE adb_register(ADB_MOUSE, 0, &mouse_ids, mouse_input); -#endif /* CONFIG_ADBMOUSE */ +#endif /* CONFIG_ADB_MOUSE */ adb_register(ADB_KEYBOARD, 0, &keyboard_ids, keyboard_input); adb_register(0x07, 0x1F, &buttons_ids, buttons_input); diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c index 2a9fc5ad8..52fc92198 100644 --- a/drivers/macintosh/macio-adb.c +++ b/drivers/macintosh/macio-adb.c @@ -8,7 +8,7 @@ #include <linux/delay.h> #include <linux/sched.h> #include <asm/prom.h> -#include <asm/adb.h> +#include <linux/adb.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/hydra.h> @@ -59,28 +59,38 @@ static volatile struct adb_regs *adb; static struct adb_request *current_req, *last_req; static unsigned char adb_rbuf[16]; +static int macio_probe(void); +static int macio_init(void); static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs); -static int macio_adb_send_request(struct adb_request *req, int sync); +static int macio_send_request(struct adb_request *req, int sync); static int macio_adb_autopoll(int devs); static void macio_adb_poll(void); static int macio_adb_reset_bus(void); static void completed(void); -static struct adb_controller macio_controller = { - ADB_MACIO, - macio_adb_send_request, +struct adb_driver macio_adb_driver = { + "MACIO", + macio_probe, + macio_init, + macio_send_request, + /*macio_write,*/ macio_adb_autopoll, - macio_adb_reset_bus, - macio_adb_poll + macio_adb_poll, + macio_adb_reset_bus }; -void macio_adb_init(void) +int macio_probe(void) +{ + return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV; +} + +int macio_init(void) { struct device_node *adbs; adbs = find_compatible_devices("adb", "chrp,adb0"); if (adbs == 0) - return; + return -ENXIO; #if 0 { int i; @@ -101,7 +111,7 @@ void macio_adb_init(void) 0, "ADB", (void *)0)) { printk(KERN_ERR "ADB: can't get irq %d\n", adbs->intrs[0].line); - return; + return -EAGAIN; } out_8(&adb->ctrl.r, 0); @@ -112,12 +122,7 @@ void macio_adb_init(void) out_8(&adb->autopoll.r, APE); out_8(&adb->intr_enb.r, DFB | TAG); - adb_controller = &macio_controller; -// adb_hardware = ADB_MACIO; - -// adb_send_request = macio_adb_send_request; -// adb_autopoll = macio_adb_autopoll; -// adb_reset_bus = macio_reset_bus; + return 0; } static int macio_adb_autopoll(int devs) @@ -143,7 +148,7 @@ static int macio_adb_reset_bus(void) } /* Send an ADB command */ -static int macio_adb_send_request(struct adb_request *req, int sync) +static int macio_send_request(struct adb_request *req, int sync) { unsigned long mflags; int i; diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 93fe07774..53590a42a 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -36,14 +36,22 @@ #include <asm/segment.h> #include <asm/bitops.h> #include <asm/feature.h> -#include <asm/adb.h> -#include <asm/pmu.h> +#include <linux/adb.h> +#include <linux/pmu.h> #ifdef CONFIG_KGDB #include <asm/kgdb.h> #endif #include "macserial.h" +#ifdef CONFIG_PMAC_PBOOK +static int serial_notify_sleep(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier serial_sleep_notifier = { + serial_notify_sleep, + SLEEP_LEVEL_MISC, +}; +#endif + /* * It would be nice to dynamically allocate everything that * depends on NUM_SERIAL, so we could support any number of @@ -1909,6 +1917,10 @@ probe_sccs() } *pp = 0; zs_channels_found = n; +#ifdef CONFIG_PMAC_PBOOK + if (n) + pmu_register_sleep_notifier(&serial_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ } /* rs_init inits the driver */ @@ -2470,3 +2482,40 @@ void __init zs_kgdb_hook(int tty_num) set_debug_traps(); /* init stub */ } #endif /* ifdef CONFIG_KGDB */ + +#ifdef CONFIG_PMAC_PBOOK +/* + * notify clients before sleep and reset bus afterwards + */ +int +serial_notify_sleep(struct pmu_sleep_notifier *self, int when) +{ + int i; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + case PBOOK_SLEEP_REJECT: + break; + + case PBOOK_SLEEP_NOW: + for (i=0; i<zs_channels_found; i++) { + struct mac_serial *info = &zs_soft[i]; + if (info->flags & ZILOG_INITIALIZED) { + shutdown(info); + info->flags |= ZILOG_SLEEPING; + } + } + break; + case PBOOK_WAKE: + for (i=0; i<zs_channels_found; i++) { + struct mac_serial *info = &zs_soft[i]; + if (info->flags & ZILOG_SLEEPING) { + info->flags &= ~ZILOG_SLEEPING; + startup(info, 0); + } + } + break; + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h index 135d5a970..e2f137f73 100644 --- a/drivers/macintosh/macserial.h +++ b/drivers/macintosh/macserial.h @@ -68,6 +68,7 @@ struct serial_struct { #define ZILOG_CLOSING 0x08000000 /* Serial port is closing */ #define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */ #define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */ +#define ZILOG_SLEEPING 0x01000000 /* have shut it down for sleep */ /* Software state per channel */ diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index 0936b8bfb..83fd1d140 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -26,7 +26,16 @@ #include <asm/feature.h> #include <asm/mediabay.h> #include <asm/init.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#ifdef CONFIG_PMAC_PBOOK +static int mb_notify_sleep(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier mb_sleep_notifier = { + mb_notify_sleep, + SLEEP_LEVEL_MEDIABAY, +}; +#endif #undef MB_USE_INTERRUPTS @@ -77,13 +86,13 @@ int media_bay_count = 0; * Hold the media-bay reset signal true for this many ticks * after a device is inserted before releasing it. */ -#define MB_RESET_COUNT 20 +#define MB_RESET_COUNT 40 /* * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted * (or until the device is ready) before registering the IDE interface. */ -#define MB_IDE_WAIT 1000 +#define MB_IDE_WAIT 1500 static void poll_media_bay(int which); static void set_media_bay(int which, int id); @@ -157,6 +166,10 @@ media_bay_init(void) { printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count); +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&mb_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + kernel_thread(media_bay_task, NULL, 0); } } @@ -298,9 +311,16 @@ poll_media_bay(int which) int id = MB_CONTENTS(which); if (id == media_bays[which].last_value) { - if (id != media_bays[which].content_id - && ++media_bays[which].value_count >= MB_STABLE_COUNT) - set_media_bay(which, id); + if (id != media_bays[which].content_id + && ++media_bays[which].value_count >= MB_STABLE_COUNT) { + /* If the device type changes without going thru "MB_NO", we force + a pass by "MB_NO" to make sure things are properly reset */ + if ((id != MB_NO) && (media_bays[which].content_id != MB_NO)) { + set_media_bay(which, MB_NO); + udelay(500); + } + set_media_bay(which, id); + } } else { media_bays[which].last_value = id; media_bays[which].value_count = 0; @@ -319,27 +339,28 @@ set_media_bay(int which, int id) switch (id) { case MB_CD: - feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable); feature_set(bay->dev_node, FEATURE_Mediabay_enable); - feature_set(bay->dev_node, FEATURE_CD_power); feature_set(bay->dev_node, FEATURE_Mediabay_IDE_enable); + udelay(500); + feature_set(bay->dev_node, FEATURE_CD_power); printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which); break; case MB_FD: - feature_clear(bay->dev_node, FEATURE_CD_power); feature_set(bay->dev_node, FEATURE_Mediabay_enable); feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable); feature_set(bay->dev_node, FEATURE_SWIM3_enable); printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which); break; case MB_NO: - feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable); feature_clear(bay->dev_node, FEATURE_CD_power); + feature_clear(bay->dev_node, FEATURE_Mediabay_enable); + feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable); + feature_clear(bay->dev_node, FEATURE_Mediabay_IDE_enable); + feature_clear(bay->dev_node, FEATURE_SWIM3_enable); + feature_set(bay->dev_node, FEATURE_Mediabay_reset); printk(KERN_INFO "media bay %d is empty\n", which); break; default: - feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable); - feature_clear(bay->dev_node, FEATURE_CD_power); feature_set(bay->dev_node, FEATURE_Mediabay_enable); printk(KERN_INFO "media bay %d contains an unknown device (%d)\n", which, id); @@ -348,3 +369,62 @@ set_media_bay(int which, int id) udelay(500); } + +#ifdef CONFIG_PMAC_PBOOK +/* + * notify clients before sleep and reset bus afterwards + */ +int __pmac +mb_notify_sleep(struct pmu_sleep_notifier *self, int when) +{ + volatile struct media_bay_info* bay; + int i; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + case PBOOK_SLEEP_REJECT: + break; + + case PBOOK_SLEEP_NOW: + for (i=0; i<media_bay_count; i++) { + bay = &media_bays[i]; + feature_clear(bay->dev_node, FEATURE_Mediabay_enable); + feature_clear(bay->dev_node, FEATURE_Mediabay_IDE_enable); + feature_clear(bay->dev_node, FEATURE_SWIM3_enable); + feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable); + feature_set(bay->dev_node, FEATURE_Mediabay_reset); + feature_clear(bay->dev_node, FEATURE_CD_power); + out_8(&media_bays[i].addr->contents, 0x70); + } + break; + case PBOOK_WAKE: + for (i=0; i<media_bay_count; i++) { + bay = &media_bays[i]; + feature_set(bay->dev_node, FEATURE_Mediabay_enable); + /* I suppose this is enough delay to stabilize MB_CONTENT ... */ + mdelay(10); + /* We re-enable the bay using it's previous content only if + it did not change */ + if (MB_CONTENTS(i) == bay->content_id) { + set_media_bay(i, bay->content_id); + if (bay->content_id != MB_NO) { + mdelay(400); + /* Clear the bay reset */ + feature_clear(bay->dev_node, FEATURE_Mediabay_reset); + /* This small delay makes sure the device has time + to assert the BUSY bit (used by IDE sleep) */ + udelay(100); + /* We reset the state machine timers in case we were in the + middle of a wait loop */ + if (bay->reset_timer) + bay->reset_timer = MB_RESET_COUNT; + if (bay->cd_timer) + bay->cd_timer = MB_IDE_WAIT; + } + } + } + break; + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 1742b1620..2e2dbf66c 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -14,16 +14,31 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/sched.h> +#include <linux/adb.h> +#include <linux/cuda.h> +#ifdef CONFIG_PPC #include <asm/prom.h> -#include <asm/adb.h> -#include <asm/cuda.h> +#include <asm/machdep.h> +#else +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/machw.h> +#include <asm/mac_via.h> +#endif #include <asm/io.h> -#include <asm/pgtable.h> #include <asm/system.h> #include <linux/init.h> static volatile unsigned char *via; +#ifdef CONFIG_MAC +#define CUDA_IRQ IRQ_MAC_ADB +#define __openfirmware +#define eieio() +#else +#define CUDA_IRQ vias->intrs[0].line +#endif + /* VIA registers - spaced 0x200 bytes apart */ #define RS 0x200 /* skip between registers */ #define B 0 /* B-side data */ @@ -73,27 +88,38 @@ static unsigned char cuda_rbuf[16]; static unsigned char *reply_ptr; static int reading_reply; static int data_index; +#ifdef CONFIG_PPC static struct device_node *vias; +#endif static int cuda_fully_inited = 0; -static int init_via(void); +static int cuda_probe(void); +static int cuda_init(void); +static int cuda_init_via(void); static void cuda_start(void); -static void via_interrupt(int irq, void *arg, struct pt_regs *regs); +static void cuda_interrupt(int irq, void *arg, struct pt_regs *regs); static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs); -static int cuda_adb_send_request(struct adb_request *req, int sync); +static int cuda_send_request(struct adb_request *req, int sync); static int cuda_adb_autopoll(int devs); -static int cuda_adb_reset_bus(void); -static int cuda_send_request(struct adb_request *req); - - -static struct adb_controller cuda_controller = { - ADB_VIACUDA, - cuda_adb_send_request, +void cuda_poll(void); +static int cuda_reset_adb_bus(void); +static int cuda_write(struct adb_request *req); + +int cuda_request(struct adb_request *req, + void (*done)(struct adb_request *), int nbytes, ...); + +struct adb_driver via_cuda_driver = { + "CUDA", + cuda_probe, + cuda_init, + cuda_send_request, + /*cuda_write,*/ cuda_adb_autopoll, - cuda_adb_reset_bus, - cuda_poll + cuda_poll, + cuda_reset_adb_bus }; +#ifdef CONFIG_PPC void find_via_cuda() { @@ -106,7 +132,7 @@ find_via_cuda() #if 0 { int i; - printk("via_cuda_init: node = %p, addrs =", vias->node); + printk("find_via_cuda: node = %p, addrs =", vias->node); for (i = 0; i < vias->n_addrs; ++i) printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size); printk(", intrs ="); @@ -124,31 +150,56 @@ find_via_cuda() via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000); cuda_state = idle; + sys_ctrler = SYS_CTRLER_CUDA; +} +#endif /* CONFIG_PPC */ - if (!init_via()) { - printk(KERN_ERR "init_via failed\n"); - via = NULL; - } - - adb_controller = &cuda_controller; +static int +cuda_probe() +{ +#ifdef CONFIG_PPC + if (sys_ctrler != SYS_CTRLER_CUDA) + return -ENODEV; +#else + if (macintosh_config->adb_type != MAC_ADB_CUDA) + return -ENODEV; + via = via1; +#endif + return 0; } -void -via_cuda_init(void) +static int +cuda_init(void) { + int err; + if (via == NULL) - return; + return -ENODEV; - if (request_irq(vias->intrs[0].line, via_interrupt, 0, "VIA", (void *)0)) { - printk(KERN_ERR "VIA: can't get irq %d\n", vias->intrs[0].line); - return; + err = cuda_init_via(); + if (err) { + printk(KERN_ERR "cuda_probe: init_via() failed\n"); + via = NULL; + return err; } - /* Clear and enable interrupts */ + /* Clear and enable interrupts, but only on PPC. On 68K it's done */ + /* for us by the the main VIA driver in arch/m68k/mac/via.c */ + +#ifndef CONFIG_MAC via[IFR] = 0x7f; eieio(); /* clear interrupts by writing 1s */ via[IER] = IER_SET|SR_INT; eieio(); /* enable interrupt from SR */ +#endif + + if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) { + printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ); + return -EAGAIN; + } + + printk("adb: CUDA driver v0.5 for Unified ADB.\n"); cuda_fully_inited = 1; + return 0; } #define WAIT_FOR(cond, what) \ @@ -156,14 +207,14 @@ via_cuda_init(void) for (x = 1000; !(cond); --x) { \ if (x == 0) { \ printk("Timeout waiting for " what); \ - return 0; \ + return -ENXIO; \ } \ udelay(100); \ } \ } while (0) static int -init_via() +cuda_init_via() { int x; @@ -172,7 +223,9 @@ init_via() via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; /* SR data in */ eieio(); x = via[SR]; eieio(); /* clear any left-over data */ +#ifndef CONFIG_MAC via[IER] = 0x7f; eieio(); /* disable interrupts from VIA */ +#endif eieio(); /* delay 4ms and then clear any pending interrupt */ @@ -198,12 +251,12 @@ init_via() x = via[SR]; eieio(); via[B] |= TIP; eieio(); /* should be unnecessary */ - return 1; + return 0; } /* Send an ADB command */ static int -cuda_adb_send_request(struct adb_request *req, int sync) +cuda_send_request(struct adb_request *req, int sync) { int i; @@ -214,7 +267,7 @@ cuda_adb_send_request(struct adb_request *req, int sync) req->reply_expected = 1; - i = cuda_send_request(req); + i = cuda_write(req); if (i) return i; @@ -243,7 +296,7 @@ cuda_adb_autopoll(int devs) /* Reset adb bus - how do we do this?? */ static int -cuda_adb_reset_bus(void) +cuda_reset_adb_bus(void) { struct adb_request req; @@ -276,11 +329,11 @@ cuda_request(struct adb_request *req, void (*done)(struct adb_request *), req->data[i] = va_arg(list, int); va_end(list); req->reply_expected = 1; - return cuda_send_request(req); + return cuda_write(req); } static int -cuda_send_request(struct adb_request *req) +cuda_write(struct adb_request *req) { unsigned long flags; @@ -336,17 +389,17 @@ cuda_start() void cuda_poll() { - int ie; + unsigned long flags; - __save_flags(ie); - __cli(); + save_flags(flags); + cli(); if (via[IFR] & SR_INT) - via_interrupt(0, 0, 0); - __restore_flags(ie); + cuda_interrupt(0, 0, 0); + restore_flags(flags); } static void -via_interrupt(int irq, void *arg, struct pt_regs *regs) +cuda_interrupt(int irq, void *arg, struct pt_regs *regs) { int x, status; struct adb_request *req; @@ -355,7 +408,7 @@ via_interrupt(int irq, void *arg, struct pt_regs *regs) return; status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio(); - /* printk("via_interrupt: state=%d status=%x\n", cuda_state, status); */ + /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */ switch (cuda_state) { case idle: /* CUDA has sent us the first byte of data - unsolicited */ @@ -469,7 +522,7 @@ via_interrupt(int irq, void *arg, struct pt_regs *regs) break; default: - printk("via_interrupt: unknown cuda_state %d?\n", cuda_state); + printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state); } } @@ -490,9 +543,3 @@ cuda_input(unsigned char *buf, int nb, struct pt_regs *regs) printk("\n"); } } - -int -cuda_present(void) -{ - return (adb_controller && (adb_controller->kind == ADB_VIACUDA) && via); -} diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 94cc54635..cff3f5cb7 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -21,10 +21,12 @@ #include <linux/blkdev.h> #include <linux/pci.h> #include <linux/malloc.h> +#include <linux/poll.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <linux/cuda.h> #include <asm/prom.h> -#include <asm/adb.h> -#include <asm/pmu.h> -#include <asm/cuda.h> +#include <asm/machdep.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -32,6 +34,7 @@ #include <asm/irq.h> #include <asm/feature.h> #include <asm/uaccess.h> +#include <asm/mmu_context.h> /* Misc minor number allocated for /dev/pmu */ #define PMU_MINOR 154 @@ -70,6 +73,7 @@ static volatile unsigned char *via; #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ +#define CB2_INT 0x08 #define CB1_INT 0x10 /* transition on CB1 input */ static enum pmu_state { @@ -98,11 +102,13 @@ static int pmu_fully_inited = 0; int asleep; struct notifier_block *sleep_notifier_list; +static int pmu_probe(void); +static int pmu_init(void); static int init_pmu(void); static int pmu_queue_request(struct adb_request *req); static void pmu_start(void); static void via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs); -static int pmu_adb_send_request(struct adb_request *req, int sync); +static int pmu_send_request(struct adb_request *req, int sync); static int pmu_adb_autopoll(int devs); static int pmu_adb_reset_bus(void); static void send_byte(int x); @@ -112,15 +118,31 @@ static void pmu_done(struct adb_request *req); static void pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs); static void set_volume(int level); +#ifdef CONFIG_PMAC_PBOOK +static void pmu_pass_intr(unsigned char *data, int len); +#endif -static struct adb_controller pmu_controller = { - ADB_VIAPMU, - pmu_adb_send_request, +struct adb_driver via_pmu_driver = { + "PMU", + pmu_probe, + pmu_init, + pmu_send_request, + /*pmu_queue_request,*/ pmu_adb_autopoll, - pmu_adb_reset_bus, - pmu_poll + pmu_poll, + pmu_adb_reset_bus }; +extern void low_sleep_handler(void); +extern void sleep_save_intrs(int); +extern void sleep_restore_intrs(void); + +extern int grackle_pcibios_read_config_word(unsigned char bus, + unsigned char dev_fn, unsigned char offset, unsigned short *val); + +extern int grackle_pcibios_write_config_word(unsigned char bus, + unsigned char dev_fn, unsigned char offset, unsigned short val); + /* * This table indicates for each PMU opcode: * - the number of data bytes to be sent with the command, or -1 @@ -164,13 +186,21 @@ static s8 pmu_data_len[256][2] __openfirmwaredata = { /*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, }; +static char *pbook_type[] = { + "Unknown PowerBook", + "PowerBook 2400/3400/3500(G3)", + "PowerBook G3 Series", + "1999 PowerBook G3", +}; -void __openfirmware +int __openfirmware find_via_pmu() { + if (via != 0) + return 1; vias = find_devices("via-pmu"); if (vias == 0) - return; + return 0; if (vias->next != 0) printk(KERN_WARNING "Warning: only using 1st via-pmu\n"); @@ -179,7 +209,7 @@ find_via_pmu() #if 0 { int i; - printk("via_pmu_init: node = %p, addrs =", vias->node); + printk("find_via_pmu: node = %p, addrs =", vias->node); for (i = 0; i < vias->n_addrs; ++i) printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size); printk(", intrs ="); @@ -192,12 +222,14 @@ find_via_pmu() printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n", vias->n_addrs, vias->n_intrs); if (vias->n_addrs < 1 || vias->n_intrs < 1) - return; + return 0; } if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0) || device_is_compatible(vias->parent, "ohare"))) pmu_kind = PMU_OHARE_BASED; + else if (device_is_compatible(vias->parent, "paddington")) + pmu_kind = PMU_PADDINGTON_BASED; else if (device_is_compatible(vias->parent, "heathrow")) pmu_kind = PMU_HEATHROW_BASED; else @@ -209,23 +241,28 @@ find_via_pmu() pmu_state = idle; - if (!init_pmu()) + if (!init_pmu()) { via = NULL; + return 0; + } - adb_controller = &pmu_controller; + printk(KERN_INFO "PMU driver initialized for %s\n", + pbook_type[pmu_kind]); + sys_ctrler = SYS_CTRLER_PMU; + return 1; +} - if (via) - printk(KERN_INFO "PMU driver initialized for %s\n", - (pmu_kind == PMU_OHARE_BASED) ? "PowerBook 2400/3400/3500(G3)" : - ((pmu_kind == PMU_HEATHROW_BASED) ? "PowerBook G3 Series" : - "Unknown PowerBook")); +static int __openfirmware +pmu_probe() +{ + return vias == NULL? -ENODEV: 0; } -void __openfirmware -via_pmu_init(void) +static int __openfirmware +pmu_init(void) { if (vias == NULL) - return; + return -ENXIO; bright_req_1.complete = 1; bright_req_2.complete = 1; @@ -235,7 +272,7 @@ via_pmu_init(void) (void *)0)) { printk(KERN_ERR "VIA-PMU: can't get irq %d\n", vias->intrs[0].line); - return; + return -ENXIO; } /* Enable interrupts */ @@ -245,6 +282,8 @@ via_pmu_init(void) /* Enable backlight */ pmu_enable_backlight(1); + + return 0; } static int __openfirmware @@ -292,20 +331,19 @@ pmu_get_model(void) /* Send an ADB command */ static int __openfirmware -pmu_adb_send_request(struct adb_request *req, int sync) +pmu_send_request(struct adb_request *req, int sync) { - int i, ret; + int i, ret; + + if ((vias == NULL) || (!pmu_fully_inited)) { + req->complete = 1; + return -ENXIO; + } - if ((vias == NULL) || (!pmu_fully_inited)) - { - req->complete = 1; - return -ENXIO; - } + ret = -EINVAL; - ret = -EINVAL; - - switch (req->data[0]) { - case PMU_PACKET: + switch (req->data[0]) { + case PMU_PACKET: for (i = 0; i < req->nbytes - 1; ++i) req->data[i] = req->data[i+1]; --req->nbytes; @@ -316,7 +354,7 @@ pmu_adb_send_request(struct adb_request *req, int sync) req->reply_len = 0; ret = pmu_queue_request(req); break; - case CUDA_PACKET: + case CUDA_PACKET: switch (req->data[1]) { case CUDA_GET_TIME: if (req->nbytes != 2) @@ -344,7 +382,7 @@ pmu_adb_send_request(struct adb_request *req, int sync) break; } break; - case ADB_PACKET: + case ADB_PACKET: for (i = req->nbytes - 1; i > 1; --i) req->data[i+2] = req->data[i]; req->data[3] = req->nbytes - 2; @@ -356,19 +394,17 @@ pmu_adb_send_request(struct adb_request *req, int sync) req->reply_len = 0; ret = pmu_queue_request(req); break; - } - if (ret) - { - req->complete = 1; - return ret; - } - - if (sync) { - while (!req->complete) - pmu_poll(); - } + } + if (ret) { + req->complete = 1; + return ret; + } - return 0; + if (sync) + while (!req->complete) + pmu_poll(); + + return 0; } /* Enable/disable autopolling */ @@ -513,19 +549,25 @@ pmu_queue_request(struct adb_request *req) static void __openfirmware send_byte(int x) { - out_8(&via[ACR], 0x1c); - out_8(&via[SR], x); - out_8(&via[B], via[B] & ~0x10); /* assert TREQ */ + volatile unsigned char *v = via; + + out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT); + out_8(&v[SR], x); + out_8(&v[B], in_8(&v[B]) & ~TREQ); /* assert TREQ */ } static void __openfirmware recv_byte() { - out_8(&via[ACR], 0x0c); - in_8(&via[SR]); /* resets SR */ - out_8(&via[B], via[B] & ~0x10); + volatile unsigned char *v = via; + + out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT); + in_8(&v[SR]); /* resets SR */ + out_8(&v[B], in_8(&v[B]) & ~0x10); } +static int disable_poll; + static void __openfirmware pmu_start() { @@ -545,7 +587,9 @@ pmu_start() data_len = pmu_data_len[req->data[0]][0]; /* set the shift register to shift out and send a byte */ + ++disable_poll; send_byte(req->data[0]); + --disable_poll; out: restore_flags(flags); @@ -554,13 +598,15 @@ out: void __openfirmware pmu_poll() { - int ie; + unsigned long flags; - __save_flags(ie); - __cli(); + if (disable_poll) + return; + save_flags(flags); + cli(); if (via[IFR] & (SR_INT | CB1_INT)) via_pmu_interrupt(0, 0, 0); - __restore_flags(ie); + restore_flags(flags); } static void __openfirmware @@ -569,6 +615,7 @@ via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) int intr; int nloop = 0; + ++disable_poll; while ((intr = in_8(&via[IFR])) != 0) { if (++nloop > 1000) { printk(KERN_DEBUG "PMU: stuck in intr loop, " @@ -580,11 +627,9 @@ via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) else if (intr & CB1_INT) { adb_int_pending = 1; out_8(&via[IFR], CB1_INT); - } else - { - /* -- Disabled printk, will happen _really_ often on - PowerBooks ((CB2 interrupts) -- - printk(KERN_DEBUG "PMU: spurrious interrupt intr=%x\n", intr); */ + } + intr &= ~(SR_INT | CB1_INT); + if (intr != 0) { out_8(&via[IFR], intr); } } @@ -597,6 +642,7 @@ via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) pmu_start(); } } + --disable_poll; } static void __openfirmware @@ -605,17 +651,17 @@ pmu_sr_intr(struct pt_regs *regs) struct adb_request *req; int bite, timeout; + if (via[B] & TREQ) { + printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); + out_8(&via[IFR], SR_INT); + return; + } if (via[B] & TACK) - printk(KERN_DEBUG "PMU: sr_intr but ack still high! (%x)\n", + printk(KERN_ERR "PMU: sr_intr but ack still high! (%x)\n", via[B]); - /* if reading grab the byte, and reset the interrupt */ - if ((via[ACR] & SR_OUT) == 0) - bite = in_8(&via[SR]); - out_8(&via[IFR], SR_INT); - /* reset TREQ and wait for TACK to go high */ - out_8(&via[B], via[B] | TREQ); + out_8(&via[B], in_8(&via[B]) | TREQ); timeout = 3200; while ((in_8(&via[B]) & TACK) == 0) { if (--timeout < 0) { @@ -625,6 +671,11 @@ pmu_sr_intr(struct pt_regs *regs) udelay(10); } + /* if reading grab the byte, and reset the interrupt */ + if (pmu_state == reading || pmu_state == reading_intr) + bite = in_8(&via[SR]); + out_8(&via[IFR], SR_INT); + switch (pmu_state) { case sending: req = current_req; @@ -707,8 +758,6 @@ pmu_done(struct adb_request *req) static void __openfirmware pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) { - static int show_pmu_ints = 1; - asleep = 0; if (len < 1) { adb_int_pending = 0; @@ -730,21 +779,34 @@ pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) } pmu_done(req); } else { - adb_input(data+1, len-1, regs, 1); +#ifdef CONFIG_XMON + if (len == 4 && data[1] == 0x2c) { + extern int xmon_wants_key, xmon_pmu_keycode; + if (xmon_wants_key) { + xmon_pmu_keycode = data[2]; + return; + } + } +#endif /* CONFIG_XMON */ + /* + * XXX On the [23]400 the PMU gives us an up + * event for keycodes 0x74 or 0x75 when the PC + * card eject buttons are released, so we + * ignore those events. + */ + if (!(pmu_kind == PMU_OHARE_BASED && len == 4 + && data[1] == 0x2c && data[3] == 0xff + && (data[2] & ~1) == 0xf4)) + adb_input(data+1, len-1, regs, 1); } + } else if (data[0] == 0x08 && len == 3) { + /* sound/brightness buttons pressed */ + pmu_set_brightness(data[1] >> 3); + set_volume(data[2]); } else { - if (data[0] == 0x08 && len == 3) { - /* sound/brightness buttons pressed */ - pmu_set_brightness(data[1] >> 3); - set_volume(data[2]); - } else if (show_pmu_ints - && !(data[0] == PMU_INT_TICK && len == 1)) { - int i; - printk(KERN_DEBUG "pmu intr"); - for (i = 0; i < len; ++i) - printk(" %.2x", data[i]); - printk("\n"); - } +#ifdef CONFIG_PMAC_PBOOK + pmu_pass_intr(data, len); +#endif } } @@ -759,38 +821,46 @@ pmu_enable_backlight(int on) struct adb_request req; if (vias == NULL) - return ; + return; - if (on) { - /* first call: get current backlight value */ - if (backlight_level < 0) { - switch(pmu_kind) { - case PMU_OHARE_BASED: + /* first call: get current backlight value */ + if (on && backlight_level < 0) { + switch (pmu_kind) { + case PMU_OHARE_BASED: pmu_request(&req, NULL, 2, 0xd9, 0); while (!req.complete) pmu_poll(); backlight_level = req.reply[1] >> 3; - printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]); break; - case PMU_HEATHROW_BASED: + case PMU_HEATHROW_BASED: + /* We cannot use nvram_read_byte here (not yet initialized) */ pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe); while (!req.complete) pmu_poll(); - printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]); backlight_level = req.reply[1]; + printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", backlight_level); + break; + case PMU_PADDINGTON_BASED: + /* the G3 PB 1999 has a backlight node + and chrp-structured nvram */ + /* XXX should read macos's "blkt" property in nvram + for this node. For now this ensures that the + backlight doesn't go off as soon as linux boots. */ + backlight_level = 20; break; - default: + default: backlight_enabled = 0; return; } - } - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, - LEVEL_TO_BRIGHT(backlight_level)); - while (!req.complete) - pmu_poll(); + } + if (on) { + pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, + LEVEL_TO_BRIGHT(backlight_level)); + while (!req.complete) + pmu_poll(); } pmu_request(&req, NULL, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); + PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); while (!req.complete) pmu_poll(); backlight_enabled = on; @@ -847,7 +917,7 @@ pmu_restart(void) { struct adb_request req; - __cli(); + cli(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); @@ -866,7 +936,7 @@ pmu_shutdown(void) { struct adb_request req; - __cli(); + cli(); pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB | PMU_INT_TICK ); @@ -881,13 +951,69 @@ pmu_shutdown(void) ; } +#ifdef CONFIG_PMAC_PBOOK + +static LIST_HEAD(sleep_notifiers); + int -pmu_present(void) +pmu_register_sleep_notifier(struct pmu_sleep_notifier *n) { - return (adb_controller && (adb_controller->kind == ADB_VIAPMU) && vias); + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.next; list != &sleep_notifiers; + list = list->next) { + current = list_entry(list, struct pmu_sleep_notifier, list); + if (n->priority > current->priority) + break; + } + __list_add(&n->list, list->prev, list); + return 0; } -#ifdef CONFIG_PMAC_PBOOK +int +pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n) +{ + if (n->list.next == 0) + return -ENOENT; + list_del(&n->list); + n->list.next = 0; + return 0; +} + +/* Sleep is broadcast last-to-first */ +static int +broadcast_sleep(int when, int can_cancel) +{ + int ret = PBOOK_SLEEP_OK; + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.prev; list != &sleep_notifiers; + list = list->prev) { + current = list_entry(list, struct pmu_sleep_notifier, list); + ret = current->notifier_call(current, when); + if (can_cancel && (ret != PBOOK_SLEEP_OK)) + return ret; + } + return ret; +} + +/* Wake is broadcast first-to-last */ +static int +broadcast_wake(void) +{ + int ret = PBOOK_SLEEP_OK; + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.next; list != &sleep_notifiers; + list = list->next) { + current = list_entry(list, struct pmu_sleep_notifier, list); + current->notifier_call(current, PBOOK_WAKE); + } + return ret; +} /* * This struct is used to store config register values for @@ -897,10 +1023,11 @@ static struct pci_save { u16 command; u16 cache_lat; u16 intr; + u32 rom_address; } *pbook_pci_saves; static int n_pbook_pci_saves; -static inline void __openfirmware +static void __openfirmware pbook_pci_save(void) { int npci; @@ -922,12 +1049,13 @@ pbook_pci_save(void) pci_read_config_word(pd, PCI_COMMAND, &ps->command); pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat); pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr); + pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address); ++ps; --npci; } } -static inline void __openfirmware +static void __openfirmware pbook_pci_restore(void) { u16 cmd; @@ -948,7 +1076,7 @@ pbook_pci_restore(void) PCI_BASE_ADDRESS_0 + j*4, pd->resource[j].start); pci_write_config_dword(pd, PCI_ROM_ADDRESS, - pd->resource[PCI_ROM_RESOURCE].start); + ps->rom_address); pci_write_config_word(pd, PCI_CACHE_LINE_SIZE, ps->cache_lat); pci_write_config_word(pd, PCI_INTERRUPT_LINE, @@ -963,43 +1091,147 @@ pbook_pci_restore(void) /* * Put the powerbook to sleep. */ -#define IRQ_ENABLE ((unsigned int *)0xf3000024) -#define MEM_CTRL ((unsigned int *)0xf8000070) + +#define FEATURE_CTRL(base) ((unsigned int *)(base + 0x38)) +#define GRACKLE_PM (1<<7) +#define GRACKLE_DOZE (1<<5) +#define GRACKLE_NAP (1<<4) +#define GRACKLE_SLEEP (1<<3) + +int __openfirmware powerbook_sleep_G3(void) +{ + int ret; + unsigned long save_l2cr; + unsigned long save_fcr; + unsigned long wait; + unsigned short pmcr1; + struct adb_request sleep_req; + struct device_node *macio; + unsigned long macio_base = 0; + + macio = find_devices("mac-io"); + if (macio != 0 && macio->n_addrs > 0) + macio_base = (unsigned long) + ioremap(macio->addrs[0].address, 0x40); -int __openfirmware powerbook_sleep(void) + /* Sync the disks. */ + /* XXX It would be nice to have some way to ensure that + * nobody is dirtying any new buffers while we wait. */ + fsync_dev(0); + + /* Notify device drivers */ + ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); + if (ret != PBOOK_SLEEP_OK) { + broadcast_sleep(PBOOK_SLEEP_REJECT, 0); + printk("pmu: sleep rejected\n"); + return -EBUSY; + } + broadcast_sleep(PBOOK_SLEEP_NOW, 0); + + /* Give the disks a little time to actually finish writing */ + for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) + mb(); + + /* Disable all interrupts except pmu */ + sleep_save_intrs(vias->intrs[0].line); + + /* Make sure the decrementer won't interrupt us */ + asm volatile("mtdec %0" : : "r" (0x7fffffff)); +#if 0 + /* Save the state of PCI config space for some slots */ + pbook_pci_save(); +#endif + /* For 750, save backside cache setting and disable it */ + save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ + if (save_l2cr) + _set_L2CR(0); + + if (macio_base != 0) { + save_fcr = in_le32(FEATURE_CTRL(macio_base)); + /* Check if this is still valid on older powerbooks */ + out_le32(FEATURE_CTRL(macio_base), save_fcr & ~(0x00000140UL)); + } + + if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0) + giveup_fpu(current); + + grackle_pcibios_read_config_word(0,0,0x70,&pmcr1); + /* Apparently, MacOS uses NAP mode for Grackle ??? */ + pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); + pmcr1 |= GRACKLE_PM|GRACKLE_NAP; + grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + + /* Ask the PMU to put us to sleep */ + pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); + while (!sleep_req.complete) + mb(); + + cli(); + while (pmu_state != idle) + pmu_poll(); + + /* Call low-level ASM sleep handler */ + low_sleep_handler(); + + /* We're awake again, stop grackle PM */ + grackle_pcibios_read_config_word(0, 0, 0x70, &pmcr1); + pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); + grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + + sti(); +#if 0 + /* According to someone from Apple, this should not be needed, + at least not for all devices. Let's keep it for now until we + have something that works. */ + pbook_pci_restore(); +#endif + set_context(current->mm->context); + + /* Restore L2 cache */ + if (save_l2cr) + _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */ + + /* reenable interrupts */ + sleep_restore_intrs(); + + /* Notify drivers */ + broadcast_wake(); + + return 0; +} + +#define PB3400_MEM_CTRL ((unsigned int *)0xf8000070) + +int __openfirmware powerbook_sleep_3400(void) { int ret, i, x; - static int save_backlight; - static unsigned int save_irqen; unsigned long msr; unsigned int hid0; unsigned long p, wait; struct adb_request sleep_req; - /* Notify device drivers */ - ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL); - if (ret & NOTIFY_STOP_MASK) - return -EBUSY; - /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that * nobody is dirtying any new buffers while we wait. */ fsync_dev(0); - /* Turn off the display backlight */ - save_backlight = backlight_enabled; - if (save_backlight) - pmu_enable_backlight(0); + /* Notify device drivers */ + ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); + if (ret != PBOOK_SLEEP_OK) { + broadcast_sleep(PBOOK_SLEEP_REJECT, 0); + printk("pmu: sleep rejected\n"); + return -EBUSY; + } + broadcast_sleep(PBOOK_SLEEP_NOW, 0); /* Give the disks a little time to actually finish writing */ for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) mb(); /* Disable all interrupts except pmu */ - save_irqen = in_le32(IRQ_ENABLE); - for (i = 0; i < 32; ++i) - if (i != vias->intrs[0].line && (save_irqen & (1 << i))) - disable_irq(i); + sleep_save_intrs(vias->intrs[0].line); + + /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Save the state of PCI config space for some slots */ @@ -1008,9 +1240,9 @@ int __openfirmware powerbook_sleep(void) /* Set the memory controller to keep the memory refreshed while we're asleep */ for (i = 0x403f; i >= 0x4000; --i) { - out_be32(MEM_CTRL, i); + out_be32(PB3400_MEM_CTRL, i); do { - x = (in_be32(MEM_CTRL) >> 16) & 0x3ff; + x = (in_be32(PB3400_MEM_CTRL) >> 16) & 0x3ff; } while (x == 0); if (x >= 0x100) break; @@ -1020,6 +1252,7 @@ int __openfirmware powerbook_sleep(void) pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!sleep_req.complete) mb(); + /* displacement-flush the L2 cache - necessary? */ for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000) i = *(volatile int *)p; @@ -1035,7 +1268,7 @@ int __openfirmware powerbook_sleep(void) udelay(10); /* OK, we're awake again, start restoring things */ - out_be32(MEM_CTRL, 0x3f); + out_be32(PB3400_MEM_CTRL, 0x3f); pbook_pci_restore(); /* wait for the PMU interrupt sequence to complete */ @@ -1043,21 +1276,10 @@ int __openfirmware powerbook_sleep(void) mb(); /* reenable interrupts */ - for (i = 0; i < 32; ++i) - if (i != vias->intrs[0].line && (save_irqen & (1 << i))) - enable_irq(i); + sleep_restore_intrs(); /* Notify drivers */ - notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); - - /* reenable ADB autopoll */ - pmu_adb_autopoll(adb_dev_map); - - /* Turn on the screen backlight, if it was on before */ - if (save_backlight) - pmu_enable_backlight(1); - - /* Wait for the hard disk to spin up */ + broadcast_wake(); return 0; } @@ -1065,15 +1287,112 @@ int __openfirmware powerbook_sleep(void) /* * Support for /dev/pmu device */ +#define RB_SIZE 10 +struct pmu_private { + struct list_head list; + int rb_get; + int rb_put; + struct rb_entry { + unsigned short len; + unsigned char data[16]; + } rb_buf[RB_SIZE]; + wait_queue_head_t wait; + spinlock_t lock; +}; + +static LIST_HEAD(all_pmu_pvt); +static spinlock_t all_pvt_lock = SPIN_LOCK_UNLOCKED; + +static void pmu_pass_intr(unsigned char *data, int len) +{ + struct pmu_private *pp; + struct list_head *list; + int i; + unsigned long flags; + + if (len > sizeof(pp->rb_buf[0].data)) + len = sizeof(pp->rb_buf[0].data); + spin_lock_irqsave(&all_pvt_lock, flags); + for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) { + pp = list_entry(list, struct pmu_private, list); + i = pp->rb_put + 1; + if (i >= RB_SIZE) + i = 0; + if (i != pp->rb_get) { + struct rb_entry *rp = &pp->rb_buf[pp->rb_put]; + rp->len = len; + memcpy(rp->data, data, len); + pp->rb_put = i; + wake_up_interruptible(&pp->wait); + } + } + spin_unlock_irqrestore(&all_pvt_lock, flags); +} + static int __openfirmware pmu_open(struct inode *inode, struct file *file) { + struct pmu_private *pp; + unsigned long flags; + + pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL); + if (pp == 0) + return -ENOMEM; + pp->rb_get = pp->rb_put = 0; + spin_lock_init(&pp->lock); + init_waitqueue_head(&pp->wait); + spin_lock_irqsave(&all_pvt_lock, flags); + list_add(&pp->list, &all_pmu_pvt); + spin_unlock_irqrestore(&all_pvt_lock, flags); + file->private_data = pp; return 0; } static ssize_t __openfirmware pmu_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - return 0; + struct pmu_private *pp = file->private_data; + DECLARE_WAITQUEUE(wait, current); + int ret; + + if (count < 1 || pp == 0) + return -EINVAL; + ret = verify_area(VERIFY_WRITE, buf, count); + if (ret) + return ret; + + add_wait_queue(&pp->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + for (;;) { + ret = -EAGAIN; + spin_lock(&pp->lock); + if (pp->rb_get != pp->rb_put) { + int i = pp->rb_get; + struct rb_entry *rp = &pp->rb_buf[i]; + ret = rp->len; + if (ret > count) + ret = count; + if (ret > 0 && copy_to_user(buf, rp->data, ret)) + ret = -EFAULT; + if (++i >= RB_SIZE) + i = 0; + pp->rb_get = i; + } + spin_unlock(&pp->lock); + if (ret >= 0) + break; + + if (file->f_flags & O_NONBLOCK) + break; + ret = -ERESTARTSYS; + if (signal_pending(current)) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&pp->wait, &wait); + + return ret; } static ssize_t __openfirmware pmu_write(struct file *file, const char *buf, @@ -1082,26 +1401,65 @@ static ssize_t __openfirmware pmu_write(struct file *file, const char *buf, return 0; } +static unsigned int pmu_fpoll(struct file *filp, poll_table *wait) +{ + struct pmu_private *pp = filp->private_data; + unsigned int mask = 0; + + if (pp == 0) + return 0; + poll_wait(filp, &pp->wait, wait); + spin_lock(&pp->lock); + if (pp->rb_get != pp->rb_put) + mask |= POLLIN; + spin_unlock(&pp->lock); + return mask; +} + +static int pmu_release(struct inode *inode, struct file *file) +{ + struct pmu_private *pp = file->private_data; + unsigned long flags; + + if (pp != 0) { + file->private_data = 0; + spin_lock_irqsave(&all_pvt_lock, flags); + list_del(&pp->list); + spin_unlock_irqrestore(&all_pvt_lock, flags); + kfree(pp); + } + return 0; +} + /* Note: removed __openfirmware here since it causes link errors */ -static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp, +static int pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { int error; __u32 value; switch (cmd) { - case PMU_IOC_SLEEP: - if (pmu_kind != PMU_OHARE_BASED) - return -ENOSYS; - return powerbook_sleep(); - case PMU_IOC_GET_BACKLIGHT: + case PMU_IOC_SLEEP: + switch (pmu_kind) { + case PMU_OHARE_BASED: + error = powerbook_sleep_3400(); + break; + case PMU_HEATHROW_BASED: + case PMU_PADDINGTON_BASED: + error = powerbook_sleep_G3(); + break; + default: + error = ENOSYS; + } + return error; + case PMU_IOC_GET_BACKLIGHT: return put_user(backlight_level, (__u32 *)arg); - case PMU_IOC_SET_BACKLIGHT: + case PMU_IOC_SET_BACKLIGHT: error = get_user(value, (__u32 *)arg); if (!error) pmu_set_brightness(value); return error; - case PMU_IOC_GET_MODEL: + case PMU_IOC_GET_MODEL: return put_user(pmu_kind, (__u32 *)arg); } return -EINVAL; @@ -1112,12 +1470,12 @@ static struct file_operations pmu_device_fops = { pmu_read, pmu_write, NULL, /* no readdir */ - NULL, /* no poll yet */ + pmu_fpoll, pmu_ioctl, NULL, /* no mmap */ pmu_open, NULL, /* flush */ - NULL /* no release */ + pmu_release, }; static struct miscdevice pmu_device = { diff --git a/drivers/misc/Config.in b/drivers/misc/Config.in index 9467c5e45..5b793e214 100644 --- a/drivers/misc/Config.in +++ b/drivers/misc/Config.in @@ -4,13 +4,6 @@ mainmenu_option next_comment comment 'Misc devices' -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Generic ACPI support' CONFIG_ACPI -fi - -# PIIX4 ACPI requires PCI for setup and a hardcoded TSC for timing -if [ "$CONFIG_PCI" = "y" -a "$CONFIG_X86_TSC" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'PIIX4 ACPI support' CONFIG_PIIX4_ACPI -fi +tristate 'Generic ACPI support' CONFIG_ACPI endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8368614ad..6183d1231 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -26,10 +26,6 @@ else endif endif -ifdef CONFIG_PIIX4_ACPI -O_OBJS += piix4_acpi.o -endif - include $(TOPDIR)/Rules.make fastdep: diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c index 27137fa40..99911093b 100644 --- a/drivers/misc/acpi.c +++ b/drivers/misc/acpi.c @@ -18,6 +18,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * See http://www.geocities.com/SiliconValley/Hardware/3165/ + * for the user-level ACPI stuff + */ + #include <linux/module.h> #include <linux/init.h> #include <linux/miscdevice.h> @@ -46,6 +51,10 @@ static struct acpi_facp *acpi_facp = NULL; static unsigned long acpi_facp_addr = 0; static unsigned long acpi_dsdt_addr = 0; + +static volatile u32 acpi_pm1_status = 0; +static volatile u32 acpi_gpe_status = 0; +static volatile u32 acpi_gpe_level = 0; static DECLARE_WAIT_QUEUE_HEAD(acpi_wait_event); /* @@ -73,6 +82,19 @@ static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value) } /* + * Get the value of the fixed event enable register + */ +static u32 acpi_read_pm1_enable(struct acpi_facp *facp) +{ + int offset = facp->pm1_evt_len >> 1; + u32 value = inw(facp->pm1a_evt + offset); + if (facp->pm1b_evt) { + value |= inw(facp->pm1b_evt + offset); + } + return value; +} + +/* * Set the value of the fixed event enable register (enable events) */ static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value) @@ -128,6 +150,28 @@ static void acpi_write_gpe_status(struct acpi_facp *facp, u32 value) } /* + * Get the value of the general-purpose event enable register + */ +static u32 acpi_read_gpe_enable(struct acpi_facp *facp) +{ + u32 value = 0; + int i, size, offset; + + offset = facp->gpe0_len >> 1; + if (facp->gpe1) { + size = facp->gpe1_len >> 1; + for (i = size - 1; i >= 0; i--) { + value = (value << 8) | inb(facp->gpe1 + offset + i); + } + } + size = facp->gpe0_len >> 1; + for (i = size - 1; i >= 0; i--) { + value = (value << 8) | inb(facp->gpe0 + offset + i); + } + return value; +} + +/* * Set the value of the general-purpose event enable register (enable events) */ static void acpi_write_gpe_enable(struct acpi_facp *facp, u32 value) @@ -157,7 +201,8 @@ static struct acpi_table *__init acpi_map_table(u32 addr) if (addr) { // map table header to determine size table = (struct acpi_table *) - ioremap_nocache((unsigned long) addr, sizeof(struct acpi_table)); + ioremap_nocache((unsigned long) addr, + sizeof(struct acpi_table)); if (table) { unsigned long table_size = table->length; iounmap(table); @@ -201,10 +246,13 @@ static int __init acpi_map_tables(void) // strip trailing space and print OEM identifier memcpy_fromio(oem, rsdp->oem, 6); oem[6] = '\0'; - for (j = 5; j > 0 && (oem[j] == '\0' || oem[j] == ' '); j--) { + for (j = 5; + j > 0 && (oem[j] == '\0' || oem[j] == ' '); + j--) { oem[j] = '\0'; } - printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n", oem, (void *) i); + printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n", + oem, (void *) i); break; } @@ -252,6 +300,7 @@ static int __init acpi_map_tables(void) */ static void acpi_unmap_tables(void) { + acpi_idle = NULL; acpi_dsdt_addr = 0; acpi_facp_addr = 0; acpi_unmap_table((struct acpi_table *) acpi_facp); @@ -263,17 +312,32 @@ static void acpi_unmap_tables(void) */ static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) { - u32 status; - - // detect and disable any fixed events - status = acpi_read_pm1_status(acpi_facp); - acpi_write_pm1_enable(acpi_facp, ~status); - - // detect and disable any general-purpose events - status = acpi_read_gpe_status(acpi_facp); - acpi_write_gpe_enable(acpi_facp, ~status); - + u32 pm1_status, gpe_status, gpe_level, gpe_edge; + // detect and clear fixed events + pm1_status = (acpi_read_pm1_status(acpi_facp) + & acpi_read_pm1_enable(acpi_facp)); + acpi_write_pm1_status(acpi_facp, pm1_status); + + // detect and handle general-purpose events + gpe_status = (acpi_read_gpe_status(acpi_facp) + & acpi_read_gpe_enable(acpi_facp)); + gpe_level = gpe_status & acpi_gpe_level; + if (gpe_level) { + // disable level-triggered events + acpi_write_gpe_enable( + acpi_facp, + acpi_read_gpe_enable(acpi_facp) & ~gpe_level); + } + gpe_edge = gpe_status & ~gpe_level; + if (gpe_edge) { + // clear edge-triggered events + while (acpi_read_gpe_status(acpi_facp) & gpe_edge) + acpi_write_gpe_status(acpi_facp, gpe_edge); + } + // notify process reading /dev/acpi + acpi_pm1_status |= pm1_status; + acpi_gpe_status |= gpe_status; wake_up_interruptible(&acpi_wait_event); } @@ -311,17 +375,79 @@ static int acpi_ioctl(struct inode *inode, (void *) arg, sizeof(struct acpi_find_tables)); if (!status) { - struct acpi_find_tables *rqst = (struct acpi_find_tables *) arg; + struct acpi_find_tables *rqst + = (struct acpi_find_tables *) arg; put_user(acpi_facp_addr, &rqst->facp); put_user(acpi_dsdt_addr, &rqst->dsdt); status = 0; } break; + case ACPI_ENABLE_EVENT: + status = verify_area(VERIFY_READ, + (void *) arg, + sizeof(struct acpi_enable_event)); + if (!status) { + struct acpi_enable_event *rqst + = (struct acpi_enable_event *) arg; + u32 pm1_enable, gpe_enable, gpe_level; + u32 pm1_enabling, gpe_enabling; + + get_user(pm1_enable, &rqst->pm1_enable); + get_user(gpe_enable, &rqst->gpe_enable); + get_user(gpe_level, &rqst->gpe_level); + gpe_level &= gpe_enable; + + // clear previously disabled events before enabling + pm1_enabling = (pm1_enable + & ~acpi_read_pm1_enable(acpi_facp)); + acpi_write_pm1_status(acpi_facp, pm1_enabling); + gpe_enabling = (gpe_enable & + ~acpi_read_gpe_enable(acpi_facp)); + while (acpi_read_gpe_status(acpi_facp) & gpe_enabling) + acpi_write_gpe_status(acpi_facp, gpe_enabling); + + acpi_write_pm1_enable(acpi_facp, pm1_enable); + acpi_write_gpe_enable(acpi_facp, gpe_enable); + acpi_gpe_level = gpe_level; + + status = 0; + } + break; case ACPI_WAIT_EVENT: - interruptible_sleep_on(&acpi_wait_event); - if (signal_pending(current)) - return -ERESTARTSYS; - status = 0; + status = verify_area(VERIFY_WRITE, + (void *) arg, + sizeof(struct acpi_wait_event)); + if (!status) { + struct acpi_wait_event *rqst + = (struct acpi_wait_event *) arg; + u32 pm1_status = 0; + u32 gpe_status = 0; + + for (;;) { + unsigned long flags; + + // we need an atomic exchange here + save_flags(flags); + cli(); + pm1_status = acpi_pm1_status; + acpi_pm1_status = 0; + gpe_status = acpi_gpe_status; + acpi_gpe_status = 0; + restore_flags(flags); + + if (pm1_status || gpe_status) + break; + + // wait for an event to arrive + interruptible_sleep_on(&acpi_wait_event); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + put_user(pm1_status, &rqst->pm1_status); + put_user(gpe_status, &rqst->gpe_status); + status = 0; + } break; } return status; @@ -355,6 +481,48 @@ static struct miscdevice acpi_device = NULL }; +/* Make it impossible to enter L2/L3 until after we've initialized */ +static unsigned long acpi_p_lvl2_lat = ~0UL; +static unsigned long acpi_p_lvl3_lat = ~0UL; + +/* Initialize to guaranteed harmless port read */ +static u16 acpi_p_lvl2 = 0x80; +static u16 acpi_p_lvl3 = 0x80; + +static void acpi_idle_handler(void) +{ + unsigned long time; + static int sleep_level = 1; + + time = inl(acpi_facp->pm_tmr); + switch (sleep_level) { + case 1: + __asm__ __volatile__("sti ; hlt": : :"memory"); + break; + case 2: + inb(acpi_p_lvl2); + break; + case 3: + /* Disable PCI arbitration while sleeping, to avoid DMA corruption? */ + if (acpi_facp->pm2_cnt) { + unsigned int port = acpi_facp->pm2_cnt; + outb(inb(port) | ACPI_ARB_DIS, port); + inb(acpi_p_lvl3); + outb(inb(port) & ~ACPI_ARB_DIS, port); + break; + } + inb(acpi_p_lvl3); + } + time = (inl(acpi_facp->pm_tmr) - time) & ACPI_TMR_MASK; + + if (time > acpi_p_lvl3_lat) + sleep_level = 3; + else if (time > acpi_p_lvl2_lat) + sleep_level = 2; + else + sleep_level = 1; +} + /* * Initialize and enable ACPI */ @@ -376,6 +544,17 @@ static int __init acpi_init(void) if (misc_register(&acpi_device)) { printk(KERN_ERR "ACPI: misc. register failed\n"); } + + /* + * Set up the ACPI idle function. Note that we can't really + * do this with multiple CPU's, we'd need a per-CPU ACPI + * device.. + */ +#ifdef __SMP__ + if (smp_num_cpus > 1) + return 0; +#endif + acpi_idle = acpi_idle_handler; return 0; } @@ -389,7 +568,8 @@ static void __exit acpi_exit(void) // disable and clear any pending events acpi_write_gpe_enable(acpi_facp, 0); while (acpi_read_gpe_status(acpi_facp)) { - acpi_write_gpe_status(acpi_facp, acpi_read_gpe_status(acpi_facp)); + acpi_write_gpe_status(acpi_facp, + acpi_read_gpe_status(acpi_facp)); } acpi_write_pm1_enable(acpi_facp, 0); acpi_write_pm1_status(acpi_facp, acpi_read_pm1_status(acpi_facp)); @@ -405,6 +585,7 @@ static void __exit acpi_exit(void) module_init(acpi_init) module_exit(acpi_exit) + #else __initcall(acpi_init); diff --git a/drivers/misc/piix4_acpi.c b/drivers/misc/piix4_acpi.c deleted file mode 100644 index 9cd3625b9..000000000 --- a/drivers/misc/piix4_acpi.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * linux/drivers/misc/piix4_acpi.c - * - * (C) Copyright 1999 Linus Torvalds - * - * A PM driver for the ACPI portion of the Intel PIIX4 - * chip. - * - * This has been known to occasionally work on some laptops. - * - * It probably only works on Intel PII machines that support - * the STPCLK protocol. - */ - -#include <linux/sched.h> -#include <linux/pci.h> -#include <linux/init.h> - -#include <asm/io.h> - -extern void (*acpi_idle)(void); - -/* - * This first part should be common to all ACPI - * CPU sleep functionality. Assuming we get the - * timing heuristics in a better shape than "none" ;) - */ - -typedef void (*sleep_fn_t)(void); - -/* - * Our "sleep mode" is a fixed point number - * with two binary places, ranging between - * [0 .. 3[ - */ -#define Cx_SHIFT 2 -#define MAXMODE ((3 << Cx_SHIFT)-1) - -/* - * NOTE! - * - * Right now this always defaults to C3, which is just broken. - * The exit latency is usually too high for much busy IO activity, - * and generally it's not always the best thing to do. - * - * We should just read the cycle counter around all the cases, - * and if we pause for a long time we go to a deeper sleep, while - * a short wait makes us go into a lighter sleep. - */ -static void common_acpi_idle(sleep_fn_t *sleep) -{ - int mode = MAXMODE; - - while (1) { - while (!current->need_resched) { - unsigned int time; - - time = get_cycles(); - sleep[(mode) >> Cx_SHIFT](); - time = get_cycles() - time; - - /* - * Yeah, yeah, yeah. - * if (time > Large && mode < MAXMODE) mode++; - * if (time < Small && mode > 0) mode--; - * Yadda-yadda-yadda. - * - * "Large" is on the order of half a timer tick. - * "Small" is on the order of Large >> 2 or so. - * - * Somebody should _really_ look at the exact - * details. The ACPI bios would give some made-up - * numbers, they might be useful (or maybe not: - * they are probably tuned for whatever Windows - * does, so don't take them for granted). - */ - } - schedule(); - check_pgt_cache(); - } -} - -/* Ok, here starts the magic PIIX4 knowledge */ - -/* - * Ehh.. We "know" about the northbridge - * bus arbitration stuff. Maybe somebody - * should actually verify this some day? - */ -#define NORTHBRIDGE_CONTROL 0x22 -#define NB_ARBITRATE 0x01 - -/* - * PIIX4 ACPI IO offsets and defines - */ -#define PMEN 0x02 -#define PMCNTRL 0x04 -#define PMTMR 0x08 -#define GPSTS 0x0c -#define GPEN 0x0E - -#define PCNTRL 0x10 -#define CC_EN 0x0200 -#define BST_EN 0x0400 -#define SLEEP_EN 0x0800 -#define STPCLK_EN 0x1000 -#define CLKRUN_EN 0x2000 - -#define PLVL2 0x14 -#define PLVL3 0x15 - -/* - * PIIX4 ACPI PCI configurations offsets and defines - */ -#define DEVACTB 0x58 -#define BRLD_EN_IRQ0 0x01 -#define BRLD_EN_IRQ 0x02 - -#define PMREGMISC 0x80 -#define PMIOSE 0x01 - -static unsigned int piix4_base_address = 0; - -static void piix4_c1_sleep(void) -{ - asm volatile("sti ; hlt" : : : "memory"); -} - -static void piix4_c2_sleep(void) -{ - outl(CLKRUN_EN | CC_EN, piix4_base_address + PCNTRL); - inb(piix4_base_address + PLVL2); -} - -static void piix4_c3_sleep(void) -{ - __cli(); - outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL); - outb(NB_ARBITRATE, NORTHBRIDGE_CONTROL); - inb(piix4_base_address + PLVL3); - outb(0, NORTHBRIDGE_CONTROL); - __sti(); -} - -static sleep_fn_t piix4_sleep[] = { - piix4_c1_sleep, /* low-latency C1 (ie "sti ; hlt") */ - piix4_c2_sleep, /* medium latency C2 (ie LVL2 stopckl) */ - piix4_c3_sleep /* high-latency C3 (ie LVL3 sleep) */ -}; - -static void piix4_acpi_idle(void) -{ - common_acpi_idle(piix4_sleep); -} - -static int __init piix4_acpi_init(void) -{ - /* This is the PIIX4 ACPI device */ - struct pci_dev *dev; - u32 base, val; - u16 cmd; - u8 pmregmisc; - -#ifdef __SMP__ - /* - * We can't really do idle things with multiple CPU's, I'm - * afraid. We'd need a per-CPU ACPI device. - */ - if (smp_num_cpus > 1) - return -1; -#endif - dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); - - if (!dev) - return -1; - - /* - * Read the IO base value, and verify that it makes sense - * - * We could enable this if it wasn't enabled before, but - * let's walk before we run.. - */ - pci_read_config_word(dev, PCI_COMMAND, &cmd); - if (!(cmd & PCI_COMMAND_IO)) - return -1; - - pci_read_config_byte(dev, PMREGMISC, &pmregmisc); - if (!(pmregmisc & PMIOSE)) - return -1; - - pci_read_config_dword(dev, 0x40, &base); - if (!(base & PCI_BASE_ADDRESS_SPACE_IO)) - return -1; - - base &= PCI_BASE_ADDRESS_IO_MASK; - if (!base) - return -1; - - printk("Found PIIX4 ACPI device at %04x\n", base); - piix4_base_address = base; - - /* Enable stopcklock, sleep and bursts, along with clock control */ - outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL); - - /* Make all unmasked interrupts be BREAK events */ - pci_read_config_dword(dev, DEVACTB, &val); - pci_write_config_dword(dev, DEVACTB, val | BRLD_EN_IRQ0 | BRLD_EN_IRQ); - - /* Set up the new idle handler.. */ - acpi_idle = piix4_acpi_idle; - return 0; -} - -__initcall(piix4_acpi_init); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 1902e5070..b091fc5f0 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -7,12 +7,12 @@ comment 'ARCnet devices' tristate 'ARCnet support' CONFIG_ARCNET if [ "$CONFIG_ARCNET" != "n" ]; then - bool ' Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH - bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051 - dep_tristate ' ARCnet COM90xx (normal) chipset driver' CONFIG_ARCNET_COM90xx $CONFIG_ARCNET - dep_tristate ' ARCnet COM90xx (IO mapped) chipset driver' CONFIG_ARCNET_COM90xxIO $CONFIG_ARCNET - dep_tristate ' ARCnet COM90xx (RIM I) chipset driver' CONFIG_ARCNET_RIM_I $CONFIG_ARCNET - dep_tristate ' ARCnet COM20020 chipset driver' CONFIG_ARCNET_COM20020 $CONFIG_ARCNET + bool ' Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH + bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051 + dep_tristate ' ARCnet COM90xx (normal) chipset driver' CONFIG_ARCNET_COM90xx $CONFIG_ARCNET + dep_tristate ' ARCnet COM90xx (IO mapped) chipset driver' CONFIG_ARCNET_COM90xxIO $CONFIG_ARCNET + dep_tristate ' ARCnet COM90xx (RIM I) chipset driver' CONFIG_ARCNET_RIM_I $CONFIG_ARCNET + dep_tristate ' ARCnet COM20020 chipset driver' CONFIG_ARCNET_COM20020 $CONFIG_ARCNET fi endmenu @@ -20,9 +20,9 @@ endmenu tristate 'Dummy net driver support' CONFIG_DUMMY tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_NETLINK" = "y" ]; then - tristate 'Ethertap network tap' CONFIG_ETHERTAP - fi + if [ "$CONFIG_NETLINK" = "y" ]; then + tristate 'Ethertap network tap (EXPERIMENTAL)' CONFIG_ETHERTAP + fi fi tristate 'General Instruments Surfboard 1000' CONFIG_NET_SB1000 @@ -36,145 +36,154 @@ comment 'Ethernet (10 or 100Mbit)' bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET if [ "$CONFIG_NET_ETHERNET" = "y" ]; then - if [ "$CONFIG_ARM" = "y" ]; then - if [ "$CONFIG_ARCH_ACORN" != "y" ]; then - tristate 'AM79C961A support' CONFIG_ARM_AM79C961A - else - source drivers/acorn/net/Config.in - fi - fi - if [ "$CONFIG_PPC" = "y" ]; then - tristate 'MACE (Power Mac ethernet) support' CONFIG_MACE - tristate 'BMAC (G3 ethernet) support' CONFIG_BMAC - fi - if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'Ariadne support' CONFIG_ARIADNE - tristate 'Ariadne II support' CONFIG_ARIADNE2 - tristate 'A2065 support' CONFIG_A2065 - tristate 'Hydra support' CONFIG_HYDRA - fi - if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then - tristate 'MIPS JAZZ onboard SONIC Ethernet support' CONFIG_MIPS_JAZZ_SONIC - fi - bool '3COM cards' CONFIG_NET_VENDOR_3COM - if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then - tristate '3c501 support' CONFIG_EL1 - tristate '3c503 support' CONFIG_EL2 - tristate '3c505 support' CONFIG_ELPLUS - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate '3c507 support' CONFIG_EL16 + if [ "$CONFIG_ARM" = "y" ]; then + if [ "$CONFIG_ARCH_ACORN" != "y" ]; then + tristate ' AM79C961A support' CONFIG_ARM_AM79C961A + else + source drivers/acorn/net/Config.in + fi + fi + if [ "$CONFIG_PPC" = "y" ]; then + tristate ' MACE (Power Mac ethernet) support' CONFIG_MACE + tristate ' BMAC (G3 ethernet) support' CONFIG_BMAC + fi + if [ "$CONFIG_ZORRO" = "y" ]; then + tristate ' Ariadne support' CONFIG_ARIADNE + tristate ' Ariadne II support' CONFIG_ARIADNE2 + tristate ' A2065 support' CONFIG_A2065 + tristate ' Hydra support' CONFIG_HYDRA + fi + if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then + tristate ' MIPS JAZZ onboard SONIC Ethernet support' CONFIG_MIPS_JAZZ_SONIC + fi + bool ' 3COM cards' CONFIG_NET_VENDOR_3COM + if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then + tristate ' 3c501 support' CONFIG_EL1 + tristate ' 3c503 support' CONFIG_EL2 + tristate ' 3c505 support' CONFIG_ELPLUS + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' 3c507 support (EXPERIMENTAL)' CONFIG_EL16 + fi + tristate ' 3c509/3c529 (MCA)/3c579 support' CONFIG_EL3 + tristate ' 3c515 ISA Fast EtherLink' CONFIG_3C515 if [ "$CONFIG_MCA" = "y" ]; then - tristate '3c523 support' CONFIG_ELMC - tristate '3c527 support' CONFIG_ELMC_II + tristate ' 3c523 support' CONFIG_ELMC + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' 3c527 support (EXPERIMENTAL)' CONFIG_ELMC_II + fi fi - fi - tristate '3c509/3c579 support' CONFIG_EL3 - tristate '3c515 ISA Fast EtherLink' CONFIG_3C515 - tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX - fi - tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE - bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC - if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - tristate 'WD80*3 support' CONFIG_WD80x3 - if [ "$CONFIG_MCA" = "y" ]; then - tristate 'SMC Ultra MCA support' CONFIG_ULTRAMCA - fi - tristate 'SMC Ultra support' CONFIG_ULTRA - tristate 'SMC Ultra32 EISA support' CONFIG_ULTRA32 - tristate 'SMC 9194 support' CONFIG_SMC9194 + tristate ' 3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX + fi + tristate ' AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE + bool ' Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC + if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then + tristate ' WD80*3 support' CONFIG_WD80x3 + if [ "$CONFIG_MCA" = "y" ]; then + tristate ' SMC Ultra MCA support' CONFIG_ULTRAMCA + fi + tristate ' SMC Ultra support' CONFIG_ULTRA + tristate ' SMC Ultra32 EISA support' CONFIG_ULTRA32 + tristate ' SMC 9194 support' CONFIG_SMC9194 fi - bool 'Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL + bool ' Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'NI5010 support' CONFIG_NI5010 + tristate ' NI5010 support (EXPERIMENTAL)' CONFIG_NI5010 fi - tristate 'NI5210 support' CONFIG_NI52 - tristate 'NI6510 support' CONFIG_NI65 + tristate ' NI5210 support' CONFIG_NI52 + tristate ' NI6510 support' CONFIG_NI65 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 - tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900 - tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN - tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC + # tristate ' Packet Engines Hamachi GNIC-II support (EXPERIMENTAL)' CONFIG_HAMACHI + tristate ' Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN + tristate ' RealTek 8129/8139 (not 8019/8029!) support (EXPERIMENTAL)' CONFIG_RTL8139 + tristate ' SiS 900 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_SIS900 + tristate ' DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102 + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' AT1700/1720 support (EXPERIMENTAL)' CONFIG_AT1700 + fi + tristate ' DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA + bool ' Other ISA cards' CONFIG_NET_ISA + if [ "$CONFIG_NET_ISA" = "y" ]; then + tristate ' Cabletron E21xx support' CONFIG_E2100 + tristate ' EtherWORKS 3 (DE203, DE204, DE205) support' CONFIG_EWRK3 + tristate ' EtherExpress 16 support' CONFIG_EEXPRESS + tristate ' EtherExpressPro support' CONFIG_EEXPRESS_PRO + tristate ' FMV-181/182/183/184 support' CONFIG_FMV18X + tristate ' HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS + tristate ' HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN + tristate ' HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' ICL EtherTeam 16i/32 support (EXPERIMENTAL)' CONFIG_ETH16I + fi + tristate ' NE2000/NE1000 support' CONFIG_NE2000 + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005 + fi + bool ' SK_G16 support' CONFIG_SK_G16 + fi + if [ "$CONFIG_MCA" = "y" ]; then + tristate ' SKnet MCA support' CONFIG_SKMC + tristate ' NE/2 (ne2000 MCA version) support' CONFIG_NE2_MCA + fi + bool ' EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA + if [ "$CONFIG_NET_EISA" = "y" ]; then + tristate ' AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32 +# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE +# fi + tristate ' Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC if [ "$CONFIG_ACENIC" != "n" ]; then - bool 'Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I + bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi - fi - bool 'Other ISA cards' CONFIG_NET_ISA - if [ "$CONFIG_NET_ISA" = "y" ]; then - tristate 'AT1700/1720 support (EXPERIMENTAL)' CONFIG_AT1700 - tristate 'Cabletron E21xx support' CONFIG_E2100 - tristate 'DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA - tristate 'EtherWORKS 3 (DE203, DE204, DE205) support' CONFIG_EWRK3 - tristate 'EtherExpress 16 support' CONFIG_EEXPRESS - tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO - tristate 'FMV-181/182/183/184 support' CONFIG_FMV18X - tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS - tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN - tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I - fi - tristate 'NE2000/NE1000 support' CONFIG_NE2000 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005 - fi - bool 'SK_G16 support' CONFIG_SK_G16 - fi - if [ "$CONFIG_MCA" = "y" ]; then - tristate 'NE/2 (ne2000 MCA version) support' CONFIG_NE2_MCA - tristate 'SKnet MCA support' CONFIG_SKMC - fi - bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA - if [ "$CONFIG_NET_EISA" = "y" ]; then - tristate 'AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 - fi - tristate 'Apricot Xen-II on board Ethernet' CONFIG_APRICOT - tristate 'CS89x0 support' CONFIG_CS89x0 - tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 - tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP - tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS - tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 - tristate 'Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 - fi - tristate 'PCI NE2000 support' CONFIG_NE2K_PCI - tristate 'TI ThunderLAN support' CONFIG_TLAN - tristate 'VIA Rhine support' CONFIG_VIA_RHINE - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 - tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100 - bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET - fi - tristate 'Adaptec Starfire support' CONFIG_ADAPTEC_STARFIRE - fi - bool 'Pocket and portable adaptors' CONFIG_NET_POCKET - if [ "$CONFIG_NET_POCKET" = "y" ]; then - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP - tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 - tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 - fi + tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT + tristate ' CS89x0 support' CONFIG_CS89x0 + tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 + tristate ' DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP + tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS + tristate ' EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 + tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 + fi + tristate ' PCI NE2000 support' CONFIG_NE2K_PCI + # tristate ' Sundance Alta support' CONFIG_ALTA + tristate ' TI ThunderLAN support' CONFIG_TLAN + tristate ' VIA Rhine support' CONFIG_VIA_RHINE + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 + tristate ' SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100 + bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET + fi + fi + bool ' Pocket and portable adapters' CONFIG_NET_POCKET + if [ "$CONFIG_NET_POCKET" = "y" ]; then + bool ' AT-LAN-TEC/RealTek pocket adapter support' CONFIG_ATP + tristate ' D-Link DE600 pocket adapter support' CONFIG_DE600 + tristate ' D-Link DE620 pocket adapter support' CONFIG_DE620 + fi fi endmenu bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then - bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX + bool ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX 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 - fi - fi + 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 # @@ -182,133 +191,66 @@ fi # 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' + dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK + dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK + if [ "$CONFIG_COPS" != "n" ]; then + bool ' Dayna firmware support' CONFIG_COPS_DAYNA + bool ' Tangent firmware support' CONFIG_COPS_TANGENT + fi + dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK + if [ "$CONFIG_IPDDP" != "n" ]; then + bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP + bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP + fi + endmenu fi if [ ! "$CONFIG_PARPORT" = "n" ]; then - dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT + dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT fi tristate 'PPP (point-to-point protocol) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then - dep_tristate 'PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP - dep_tristate 'PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP - dep_tristate 'PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m + dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP + dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP + dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP m fi tristate 'SLIP (serial line) support' CONFIG_SLIP if [ "$CONFIG_SLIP" != "n" ]; then - bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED - bool ' Keepalive and linefill' CONFIG_SLIP_SMART - bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED + bool ' Keepalive and linefill' CONFIG_SLIP_SMART + bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 fi +mainmenu_option next_comment +comment 'Wireless LAN (non-hamradio)' + bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO if [ "$CONFIG_NET_RADIO" = "y" ]; then - dep_tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP $CONFIG_INET - tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN - tristate 'Aironet Arlan 655 & IC2200 DS support' CONFIG_ARLAN - -fi - -mainmenu_option next_comment -comment 'Token ring devices' + dep_tristate ' STRIP (Metricom starmode radio IP)' CONFIG_STRIP $CONFIG_INET + tristate ' AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN + tristate ' Aironet Arlan 655 & IC2200 DS support' CONFIG_ARLAN -bool 'Token Ring driver support' CONFIG_TR -if [ "$CONFIG_TR" = "y" ]; then - tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR -# tristate 'IBM Lanstreamer PCI adaptor support' CONFIG_IBMLS - tristate 'IBM Olympic chipset PCI adapter support' CONFIG_IBMOL - tristate 'SysKonnect adapter support' CONFIG_SKTR fi endmenu +source drivers/net/tokenring/Config.in + bool 'Fibre Channel driver support' CONFIG_NET_FC if [ "$CONFIG_NET_FC" = "y" ]; then - tristate 'Interphase 5526 Tachyon chipset based adaptor support' CONFIG_IPHASE5526 + dep_tristate ' Interphase 5526 Tachyon chipset based adapter support' CONFIG_IPHASE5526 $CONFIG_SCSI fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI - tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER -fi - -# -# WAN drivers support -# - -mainmenu_option next_comment -comment 'Wan interfaces' - - -# There is no way to detect a comtrol sv11 - force it modular for now. -# -dep_tristate 'Comtrol Hostess SV-11 support' CONFIG_HOSTESS_SV11 m -# -# The COSA/SRP driver has not been tested as non-modular yet. -# -dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m -# -# There is no way to detect a Sealevel board. Force it modular -# -dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m - -tristate 'Frame relay DLCI support' CONFIG_DLCI -if [ "$CONFIG_DLCI" != "n" ]; then - int ' Max open DLCI' CONFIG_DLCI_COUNT 24 - int ' Max DLCI per device' CONFIG_DLCI_MAX 8 - dep_tristate ' SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI + tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI + tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER fi -# -# Wan router core. -# - -if [ "$CONFIG_WAN_ROUTER" != "n" ]; then - bool 'WAN drivers' CONFIG_WAN_DRIVERS - if [ "$CONFIG_WAN_DRIVERS" = "y" ]; then - dep_tristate 'Sangoma WANPIPE(tm) multiprotocol cards' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_DRIVERS - if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then - int ' Maximum number of cards' CONFIG_WANPIPE_CARDS 1 - bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 - bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR - bool ' WANPIPE PPP support' CONFIG_WANPIPE_PPP - fi - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_DRIVERS - if [ "$CONFIG_CYCLADES_SYNC" != "n" ]; then - bool ' Cyclom 2X X.25 support (EXPERIMENTAL)' CONFIG_CYCLOMX_X25 - fi - fi - fi -fi - -endmenu - - -# -# X.25 network drivers -# -if [ "$CONFIG_X25" != "n" ]; then -if [ "$CONFIG_LAPB" != "n" ]; then - dep_tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER $CONFIG_LAPB - dep_tristate 'X.25 async driver' CONFIG_X25_ASY $CONFIG_LAPB -fi -fi +source drivers/net/wan/Config.in if [ "$CONFIG_PCMCIA" != "n" ]; then source drivers/net/pcmcia/Config.in diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e1abc4c0e..4589d0ee3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -5,7 +5,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc pcmcia +ALL_SUB_DIRS := $(SUB_DIRS) fc hamradio irda pcmcia tokenring wan L_TARGET := net.a L_OBJS := auto_irq.o @@ -24,10 +24,6 @@ CONFIG_7990_BUILTIN := CONFIG_7990_MODULE := CONFIG_82596_BUILTIN := CONFIG_82596_MODULE := -CONFIG_85230_BUILTIN := -CONFIG_85230_MODULE := -CONFIG_SYNCPPP_BUILTIN := -CONFIG_SYNCPPP_MODULE := ifeq ($(CONFIG_PCMCIA),y) SUB_DIRS += pcmcia @@ -60,38 +56,6 @@ ifeq ($(CONFIG_SEEQ8005),y) L_OBJS += seeq8005.o endif -ifeq ($(CONFIG_IBMTR),y) -L_OBJS += ibmtr.o -else - ifeq ($(CONFIG_IBMTR),m) - M_OBJS += ibmtr.o - endif -endif - -ifeq ($(CONFIG_IBMLS),y) -L_OBJS += lanstreamer.o -else - ifeq ($(CONFIG_IBMLS),m) - M_OBJS += lanstreamer.o - endif -endif - -ifeq ($(CONFIG_IBMOL),y) -L_OBJS += olympic.o -else - ifeq ($(CONFIG_IBMOL),m) - M_OBJS += olympic.o - endif -endif - -ifeq ($(CONFIG_SKTR),y) -L_OBJS += sktr.o -else - ifeq ($(CONFIG_SKTR),m) - M_OBJS += sktr.o - endif -endif - ifeq ($(CONFIG_ETHERTAP),y) L_OBJS += ethertap.o else @@ -123,6 +87,13 @@ else endif endif +ifeq ($(CONFIG_PCMCIA_PCNET),y) +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_PCMCIA_PCNET),m) + CONFIG_8390_MODULE = y + endif +endif ifeq ($(CONFIG_SHAPER),y) L_OBJS += shaper.o @@ -609,6 +580,15 @@ else endif endif +ifeq ($(CONFIG_DM9102),y) +L_OBJS += dmfe.o +else + ifeq ($(CONFIG_DM9102),m) + M_OBJS += dmfe.o + endif +endif + + ifeq ($(CONFIG_YELLOWFIN),y) L_OBJS += yellowfin.o else @@ -811,14 +791,6 @@ else endif endif -ifeq ($(CONFIG_LAPBETHER),y) -L_OBJS += lapbether.o -else - ifeq ($(CONFIG_LAPBETHER),m) - M_OBJS += lapbether.o - endif -endif - ifeq ($(CONFIG_EPIC100),y) L_OBJS += epic100.o else @@ -827,63 +799,6 @@ else endif endif -ifeq ($(CONFIG_HOSTESS_SV11),y) -L_OBJS += hostess_sv11.o -CONFIG_85230_BUILTIN = y -CONFIG_SYNCPPP_BUILTIN = y -else - ifeq ($(CONFIG_HOSTESS_SV11),m) - CONFIG_85230_MODULE = y - CONFIG_SYNCPPP_MODULE = y - M_OBJS += hostess_sv11.o - endif -endif - -ifeq ($(CONFIG_SEALEVEL_4021),y) -L_OBJS += sealevel.o -CONFIG_85230_BUILTIN = y -CONFIG_SYNCPPP_BUILTIN = y -else - ifeq ($(CONFIG_SEALEVEL_4021),m) - CONFIG_85230_MODULE = y - CONFIG_SYNCPPP_MODULE = y - M_OBJS += sealevel.o - endif -endif - - -ifeq ($(CONFIG_COSA),y) -L_OBJS += cosa.o -CONFIG_SYNCPPP_BUILTIN = y -else - ifeq ($(CONFIG_COSA),m) - CONFIG_SYNCPPP_MODULE = y - M_OBJS += cosa.o - endif -endif - -# If anything built-in uses syncppp, then build it into the kernel also. -# If not, but a module uses it, build as a module. - -ifdef CONFIG_SYNCPPP_BUILTIN -LX_OBJS += syncppp.o -else - ifdef CONFIG_SYNCPPP_MODULE - MX_OBJS += syncppp.o - endif -endif - -# If anything built-in uses Z85230, then build it into the kernel also. -# If not, but a module uses it, build as a module. - -ifdef CONFIG_85230_BUILTIN -LX_OBJS += z85230.o -else - ifdef CONFIG_85230_MODULE - MX_OBJS += z85230.o - endif -endif - # If anything built-in uses slhc, then build it into the kernel also. # If not, but a module uses it, build as a module. ifdef CONFIG_SLHC_BUILTIN @@ -914,6 +829,14 @@ else endif endif +ifeq ($(CONFIG_PCMCIA_PCNET),y) +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_PCMCIA_PCNET),m) + CONFIG_8390_MODULE = y + endif +endif + # If anything built-in uses the 8390, then build it into the kernel also. # If not, but a module uses it, build as a module. ifdef CONFIG_8390_BUILTIN @@ -1036,22 +959,6 @@ else endif endif -ifeq ($(CONFIG_SDLA),y) -L_OBJS += sdla.o -else - ifeq ($(CONFIG_SDLA),m) - M_OBJS += sdla.o - endif -endif - -ifeq ($(CONFIG_DLCI),y) -L_OBJS += dlci.o -else - ifeq ($(CONFIG_DLCI),m) - M_OBJS += dlci.o - endif -endif - ifeq ($(CONFIG_ARIADNE),y) L_OBJS += ariadne.o else @@ -1128,68 +1035,6 @@ else endif endif -ifeq ($(CONFIG_ADAPTEC_STARFIRE),y) -L_OBJS += starfire.o -else - ifeq ($(CONFIG_ADAPTEC_STARFIRE),m) - M_OBJS += starfire.o - endif -endif - -ifeq ($(CONFIG_VENDOR_SANGOMA),y) - LX_OBJS += sdladrv.o - L_OBJS += sdlamain.o - ifeq ($(CONFIG_WANPIPE_X25),y) - L_OBJS += sdla_x25.o - endif - ifeq ($(CONFIG_WANPIPE_FR),y) - L_OBJS += sdla_fr.o - endif - ifeq ($(CONFIG_WANPIPE_PPP),y) - L_OBJS += sdla_ppp.o - endif -endif - -ifeq ($(CONFIG_VENDOR_SANGOMA),m) - MX_OBJS += sdladrv.o - M_OBJS += wanpipe.o - WANPIPE_OBJS = sdlamain.o - ifeq ($(CONFIG_WANPIPE_X25),y) - WANPIPE_OBJS += sdla_x25.o - endif - ifeq ($(CONFIG_WANPIPE_FR),y) - WANPIPE_OBJS += sdla_fr.o - endif - ifeq ($(CONFIG_WANPIPE_PPP),y) - WANPIPE_OBJS += sdla_ppp.o - endif -endif - -ifeq ($(CONFIG_CYCLADES_SYNC),y) - LX_OBJS += cycx_drv.o - L_OBJS += cycx_main.o - ifeq ($(CONFIG_CYCLOMX_X25),y) - L_OBJS += cycx_x25.o - endif -endif - -ifeq ($(CONFIG_CYCLADES_SYNC),m) - MX_OBJS += cycx_drv.o - M_OBJS += cyclomx.o - CYCLOMX_OBJS = cycx_main.o - ifeq ($(CONFIG_CYCLOMX_X25),y) - CYCLOMX_OBJS += cycx_x25.o - endif -endif - -ifeq ($(CONFIG_X25_ASY),y) -L_OBJS += x25_asy.o -else - ifeq ($(CONFIG_X25_ASY),m) - M_OBJS += x25_asy.o - endif -endif - # # HIPPI adapters # @@ -1211,6 +1056,24 @@ else endif endif +ifeq ($(CONFIG_TR),y) +SUB_DIRS += tokenring +MOD_IN_SUB_DIRS += tokenring +else + ifeq ($(CONFIG_TR),m) + MOD_IN_SUB_DIRS += tokenring + endif +endif + +ifeq ($(CONFIG_WAN),y) +SUB_DIRS += wan +MOD_IN_SUB_DIRS += wan +else + ifeq ($(CONFIG_WAN),m) + MOD_IN_SUB_DIRS += wan + endif +endif + ifeq ($(CONFIG_NET_FC),y) SUB_DIRS += fc MOD_IN_SUB_DIRS += fc @@ -1225,11 +1088,5 @@ include $(TOPDIR)/Rules.make clean: rm -f core *.o *.a *.s -wanpipe.o: $(WANPIPE_OBJS) - ld -r -o $@ $(WANPIPE_OBJS) - -cyclomx.o: $(CYCLOMX_OBJS) - ld -r -o $@ $(CYCLOMX_OBJS) - rcpci.o: rcpci45.o rclanmtl.o $(LD) -r -o rcpci.o rcpci45.o rclanmtl.o diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 27c431e14..a10d519f5 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -12,6 +12,8 @@ * Donald J. Becker, <becker@super.org> * * Changelog: + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 09/1999 + * - fix sbni: s/device/net_device/ * Paul Gortmaker (06/98): * - sort probes in a sane way, make sure all (safe) probes * get run once & failed autoprobes don't autoprobe again. @@ -104,6 +106,7 @@ extern int pamsnet_probe(struct net_device *); extern int tlan_probe(struct net_device *); extern int mace_probe(struct net_device *); extern int bmac_probe(struct net_device *); +extern int ncr885e_probe(struct net_device *); extern int cs89x0_probe(struct net_device *dev); extern int ethertap_probe(struct net_device *dev); extern int ether1_probe (struct net_device *dev); @@ -118,11 +121,12 @@ extern int bagetlance_probe(struct net_device *); extern int dec_lance_probe(struct net_device *); extern int mvme147lance_probe(struct net_device *dev); extern int via_rhine_probe(struct net_device *dev); -extern int starfire_probe(struct net_device *dev); extern int tc515_probe(struct net_device *dev); extern int lance_probe(struct net_device *dev); +extern int starfire_probe(struct net_device *dev); extern int rcpci_probe(struct net_device *); extern int mac_onboard_sonic_probe(struct net_device *dev); +extern int dmfe_reg_board(struct net_device *); /* Gigabit Ethernet adapters */ extern int yellowfin_probe(struct net_device *dev); @@ -143,6 +147,9 @@ extern int rr_hippi_probe(struct net_device *); /* Fibre Channel adapters */ extern int iph5526_probe(struct net_device *dev); +/* SBNI adapters */ +extern int sbni_probe(struct net_device *); + struct devprobe { int (*probe)(struct net_device *dev); @@ -216,6 +223,11 @@ struct devprobe pci_probes[] __initdata = { #ifdef CONFIG_SIS900 {sis900_probe, 0}, #endif + +#ifdef CONFIG_DM9102 + {dmfe_reg_board, 0}, +#endif + #ifdef CONFIG_YELLOWFIN {yellowfin_probe, 0}, #endif @@ -293,10 +305,10 @@ struct devprobe mca_probes[] __initdata = { /* * ISA probes that touch addresses < 0x400 (including those that also - * look for EISA/PCI cards in addition to ISA cards). + * look for EISA/PCI/MCA cards in addition to ISA cards). */ struct devprobe isa_probes[] __initdata = { -#ifdef CONFIG_EL3 /* ISA, EISA (MCA someday) 3c5x9 */ +#ifdef CONFIG_EL3 /* ISA, EISA, MCA 3c5x9 */ {el3_probe, 0}, #endif #ifdef CONFIG_HP100 /* ISA, EISA & PCI */ @@ -458,6 +470,9 @@ struct devprobe ppc_probes[] __initdata = { #ifdef CONFIG_BMAC {bmac_probe, 0}, #endif +#ifdef CONFIG_NCR885E + {ncr885e_probe, 0}, +#endif {NULL, 0}, }; @@ -781,6 +796,7 @@ struct net_device eql_dev = { /* Token-ring device probe */ extern int ibmtr_probe(struct net_device *); extern int olympic_probe(struct net_device *); +extern int sktr_probe(struct net_device *); static int trif_probe(struct net_device *dev) @@ -876,6 +892,29 @@ static struct net_device tr0_dev = { # undef NEXT_DEV # define NEXT_DEV (&fc0_dev) #endif + + +#ifdef CONFIG_SBNI + static struct net_device sbni7_dev = + {"sbni7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sbni_probe}; + static struct net_device sbni6_dev = + {"sbni6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni7_dev, sbni_probe}; + static struct net_device sbni5_dev = + {"sbni5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni6_dev, sbni_probe}; + static struct net_device sbni4_dev = + {"sbni4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni5_dev, sbni_probe}; + static struct net_device sbni3_dev = + {"sbni3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni4_dev, sbni_probe}; + static struct net_device sbni2_dev = + {"sbni2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni3_dev, sbni_probe}; + static struct net_device sbni1_dev = + {"sbni1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni2_dev, sbni_probe}; + static struct net_device sbni0_dev = + {"sbni0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &sbni1_dev, sbni_probe}; + +#undef NEXT_DEV +#define NEXT_DEV (&sbni0_dev) +#endif #ifdef CONFIG_NET_SB1000 diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c index d8bddd665..353b44dc8 100644 --- a/drivers/net/arcnet.c +++ b/drivers/net/arcnet.c @@ -474,10 +474,12 @@ arcnet_open(struct net_device *dev) lp->sdev=(struct net_device *)kmalloc(sizeof(struct net_device)+10,GFP_KERNEL); if(lp->sdev == NULL) { +#ifdef CONFIG_ARCNET_ETH if(lp->edev) kfree(lp->edev); lp->edev=NULL; return -ENOMEM; +#endif } memcpy(lp->sdev,dev,sizeof(struct net_device)); lp->sdev->name=(char *)(lp+1); diff --git a/drivers/net/ncr885_debug.h b/drivers/net/ncr885_debug.h new file mode 100644 index 000000000..bd1fead2d --- /dev/null +++ b/drivers/net/ncr885_debug.h @@ -0,0 +1,54 @@ +#ifndef _H_NCR885_DEBUG +#define _H_NCR885_DEBUG + +struct ncr885e_regs { + unsigned long tx_status; + unsigned long rx_status; + unsigned long mac_config; + unsigned long tx_control; + unsigned long rx_control; + unsigned long tx_cmd_ptr; + unsigned long rx_cmd_ptr; + unsigned long int_status; +}; + +#ifndef __KERNEL__ + +struct ncr885e_private { + + struct dbdma_cmd *head; + struct dbdma_cmd *tx_cmds; + struct dbdma_cmd *rx_cmds; + struct dbdma_cmd *stop_cmd; + + struct sk_buff *tx_skbufs[NR_TX_RING]; + struct sk_buff *rx_skbufs[NR_RX_RING]; + + int rx_current; + int rx_dirty; + + int tx_dirty; + int tx_current; + + unsigned short tx_status[NR_TX_RING]; + + unsigned char tx_fullup; + unsigned char tx_active; + + struct net_device_stats stats; + + struct device *dev; + + struct timer_list tx_timeout; + int timeout_active; + + spinlock_t lock; +}; + +#endif /* __KERNEL__ */ + + +#define NCR885E_GET_PRIV _IOR('N',1,sizeof( struct ncr885e_private )) +#define NCR885E_GET_REGS _IOR('N',2,sizeof( struct ncr885e_regs )) + +#endif diff --git a/drivers/net/ncr885e.c b/drivers/net/ncr885e.c new file mode 100644 index 000000000..277f92520 --- /dev/null +++ b/drivers/net/ncr885e.c @@ -0,0 +1,1458 @@ +/* + * An Ethernet driver for the dual-function NCR 53C885 SCSI/Ethernet + * controller. + * + * + * 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. + * + */ + +static const char *version = +"ncr885e.c:v0.8 11/30/98 dan@synergymicro.com\n"; + +#include <linux/config.h> + +#ifdef MODULE +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/dbdma.h> +#include <asm/uaccess.h> + +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "ncr885e.h" +#include "ncr885_debug.h" + +static const char *chipname = "ncr885e"; + +/* debugging flags */ +#if 0 +#define DEBUG_FUNC 0x0001 +#define DEBUG_PACKET 0x0002 +#define DEBUG_CMD 0x0004 +#define DEBUG_CHANNEL 0x0008 +#define DEBUG_INT 0x0010 +#define DEBUG_RX 0x0020 +#define DEBUG_TX 0x0040 +#define DEBUG_DMA 0x0080 +#define DEBUG_MAC 0x0100 +#define DEBUG_DRIVER 0x0200 +#define DEBUG_ALL 0x1fff +#endif + +#ifdef DEBUG_NCR885E +#define NCR885E_DEBUG 0 +#else +#define NCR885E_DEBUG 0 +#endif + +/* The 885's Ethernet PCI device id. */ +#ifndef PCI_DEVICE_ID_NCR_53C885_ETHERNET +#define PCI_DEVICE_ID_NCR_53C885_ETHERNET 0x0701 +#endif + +#define NR_RX_RING 8 +#define NR_TX_RING 8 +#define MAX_TX_ACTIVE (NR_TX_RING-1) +#define NCMDS_TX NR_TX_RING + +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT 5*HZ + +#define NCR885E_TOTAL_SIZE 0xe0 + +#define TXSR (1<<6) /* tx: xfer status written */ +#define TXABORT (1<<7) /* tx: abort */ +#define EOP (1<<7) /* rx: end of packet written to buffer */ + +int ncr885e_debug = NCR885E_DEBUG; +static int print_version = 0; + +struct ncr885e_private { + + /* preserve a 1-1 marking with buffs */ + struct dbdma_cmd *head; + struct dbdma_cmd *tx_cmds; + struct dbdma_cmd *rx_cmds; + struct dbdma_cmd *stop_cmd; + + struct sk_buff *tx_skbufs[NR_TX_RING]; + struct sk_buff *rx_skbufs[NR_RX_RING]; + + int rx_current; + int rx_dirty; + + int tx_dirty; + int tx_current; + + unsigned short tx_status[NR_TX_RING]; + + unsigned char tx_fullup; + unsigned char tx_active; + + struct net_device_stats stats; + + struct net_device *dev; + + struct timer_list tx_timeout; + int timeout_active; + + spinlock_t lock; +}; + +#ifdef MODULE +static struct net_device *root_dev = NULL; +#endif + + +static int ncr885e_open( struct net_device *dev ); +static int ncr885e_close( struct net_device *dev ); +static void ncr885e_rx( struct net_device *dev ); +static void ncr885e_tx( struct net_device *dev ); +static int ncr885e_probe1( struct net_device *dev, unsigned long ioaddr, + unsigned char irq ); +static int ncr885e_xmit_start( struct sk_buff *skb, struct net_device *dev ); +static struct net_device_stats *ncr885e_stats( struct net_device *dev ); +static void ncr885e_set_multicast( struct net_device *dev ); +static void ncr885e_config( struct net_device *dev ); +static int ncr885e_set_address( struct net_device *dev, void *addr ); +static void ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs ); +static void show_dbdma_cmd( volatile struct dbdma_cmd *cmd ); +#if 0 +static int read_eeprom( unsigned int ioadddr, int location ); +#endif + +#ifdef NCR885E_DEBUG_MII +static void show_mii( unsigned long ioaddr ); +static int read_mii( unsigned long ioaddr, int reg ); +static void write_mii( unsigned long ioaddr, int reg, int data ); +#endif /* NCR885E_DEBUG_MII */ + +#define TX_RESET_FLAGS (TX_CHANNEL_RUN|TX_CHANNEL_PAUSE|TX_CHANNEL_WAKE) +#define RX_RESET_FLAGS (RX_CHANNEL_RUN|RX_CHANNEL_PAUSE|RX_CHANNEL_WAKE) + + +#if 0 +static int +debug_ioctl( struct net_device *dev, struct ifreq *req, int cmd ) +{ + unsigned long ioaddr = dev->base_addr; + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct ncr885e_private *data; + struct ncr885e_regs *regs; + unsigned long flags; + + union { + struct ncr885e_regs dump; + struct ncr885e_private priv; + } temp; + + switch( cmd ) { + + /* dump the rx ring status */ + case NCR885E_GET_PRIV: + + data = (struct ncr885e_private *) &req->ifr_data; + + if ( verify_area(VERIFY_WRITE, &req->ifr_data, + sizeof( struct ncr885e_private ))) + return -EFAULT; + + memcpy((char *) &temp.priv, sp, sizeof( struct ncr885e_private )); + copy_to_user( data, (char *) &temp.priv, sizeof( struct ncr885e_private)); + break; + + case NCR885E_GET_REGS: + + regs = (struct ncr885e_regs *) &req->ifr_data; + + if ( verify_area( VERIFY_WRITE, &req->ifr_data, + sizeof( struct ncr885e_regs ))) + return -EFAULT; + + spin_lock_irqsave( &sp->lock, flags ); + + temp.dump.tx_status = inl( ioaddr + TX_CHANNEL_STATUS ); + temp.dump.rx_status = inl( ioaddr + RX_CHANNEL_STATUS ); + temp.dump.mac_config = inl( ioaddr + MAC_CONFIG ); + temp.dump.tx_control = inl( ioaddr + TX_CHANNEL_CONTROL ); + temp.dump.rx_control = inl( ioaddr + RX_CHANNEL_CONTROL ); + temp.dump.tx_cmd_ptr = inl( ioaddr + TX_CMD_PTR_LO ); + temp.dump.rx_cmd_ptr = inl( ioaddr + RX_CMD_PTR_LO ); + temp.dump.int_status = inl( ioaddr + INTERRUPT_STATUS_REG ); + + spin_unlock_irqrestore( &sp->lock, flags ); + copy_to_user( regs, (char *) &temp.dump, sizeof( struct ncr885e_regs )); + + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} +#endif + +/* Enable interrupts on the 53C885 */ +static inline void +ncr885e_enable( struct net_device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + unsigned short reg; + + reg = inw(ioaddr + INTERRUPT_ENABLE); + outw(reg | INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE); +} + +/* Disable interrupts on the 53c885 */ +static inline void +ncr885e_disable( struct net_device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + unsigned short reg; + + reg = inw( ioaddr + INTERRUPT_ENABLE ); + outw( reg & ~INTERRUPT_INTE, ioaddr + INTERRUPT_ENABLE ); +} + + +static inline void +ncr885e_reset( struct net_device *dev ) + +{ + unsigned short reg; + unsigned long cntl; + int i; + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 1) + printk( KERN_INFO "%s: Resetting 53C885...\n", dev->name ); + + /* disable interrupts on the 53C885 */ + ncr885e_disable( dev ); + + /* disable rx in the MAC */ + reg = inw( ioaddr + MAC_CONFIG ); + outw( reg & ~MAC_CONFIG_RXEN, ioaddr + MAC_CONFIG ); + + for( i=0; i < 100; i++ ) { + + if ( !(inw( ioaddr + MAC_CONFIG ) & MAC_CONFIG_RXEN )) + break; + udelay( 10 ); + } + + reg = inw( ioaddr + MAC_CONFIG ); + outw( reg | MAC_CONFIG_SRST, ioaddr + MAC_CONFIG ); + outw( reg, ioaddr + MAC_CONFIG ); + + /* disable both rx and tx DBDMA channels */ + outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL ); + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + for( i=0; i < 100; i++ ) { + + if ( !(inw( ioaddr + TX_CHANNEL_STATUS ) & TX_DBDMA_ENABLE ) && + !(inw( ioaddr + RX_CHANNEL_STATUS ) & RX_DBDMA_ENABLE )) + break; + udelay( 10 ); + } + + /* perform a "software reset" */ + cntl = inl( ioaddr + DBDMA_CONTROL ); + outl( cntl | DBDMA_SRST, ioaddr + DBDMA_CONTROL ); + + for( i=0; i < 100; i++ ) { + + if ( !(inl( ioaddr + DBDMA_CONTROL ) & DBDMA_SRST )) + break; + udelay( 10 ); + } + + /* books says that a software reset should be done to the MAC, as + well. This true??? */ + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: reset complete\n", dev->name ); + +} + + +/* configure the 53C885 chip. + + The DBDMA command descriptors on the 53C885 can be programmed to + branch, interrupt or pause conditionally or always by using the + interrupt, branch and wait select registers. */ + +static void +ncr885e_config( struct net_device *dev ) + +{ + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: Configuring 53C885.\n", dev->name ); + + ncr885e_reset( dev ); + + /* The 53C885 can be programmed to perform conditional DBDMA + branches, interrupts or waits. + + Neither channel makes use of "wait", as it requires that the + DBDMA engine to be restarted. Don't go there. The rx channel + will branch upon the successful reception of a packet ('EOP' in + the xfer_status field). The branch address is to the STOP + DBDMA command descriptor, which shuts down the rx channel until + the interrupt is serviced. */ + + /* cause tx channel to stop after "status received" */ + outl( 0, ioaddr + TX_INT_SELECT ); + outl( (TX_WAIT_STAT_RECV << 16) | TX_WAIT_STAT_RECV, + ioaddr + TX_WAIT_SELECT ); + outl( 0, ioaddr + TX_BRANCH_SELECT ); + + /* cause rx channel to branch to the STOP descriptor on "End-of-Packet" */ +#if 0 + outl( (RX_INT_SELECT_EOP << 16) | RX_INT_SELECT_EOP, + ioaddr + RX_INT_SELECT ); +#else + outl( 0, ioaddr + RX_INT_SELECT ); +#endif +#if 0 + outl( 0, ioaddr + RX_WAIT_SELECT ); +#else + outl( (RX_WAIT_SELECT_EOP << 16) | RX_WAIT_SELECT_EOP, + ioaddr + RX_WAIT_SELECT ); +#endif +#if 1 + outl( 0, ioaddr + RX_BRANCH_SELECT ); +#else + outl( (RX_BRANCH_SELECT_EOP << 16) | RX_BRANCH_SELECT_EOP, + ioaddr + RX_BRANCH_SELECT ); +#endif + + /* configure DBDMA */ + outl( (DBDMA_BE | DBDMA_DPMRLE | DBDMA_TDPCE | + DBDMA_DDPE | DBDMA_TDPE | + (DBDMA_BURST_4 << DBDMA_TX_BST_SHIFT) | + (DBDMA_BURST_4 << DBDMA_RX_BST_SHIFT) | + (DBDMA_TX_ARBITRATION_DEFAULT) | + (DBDMA_RX_ARBITRATION_DEFAULT)), ioaddr + DBDMA_CONTROL ); + + outl( 0, ioaddr + TX_THRESHOLD ); + + /* disable MAC loopback */ + outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL | + MAC_CONFIG_PADEN | (0x18 << 16)), + ioaddr + MAC_CONFIG ); + + /* configure MAC */ + outl( (MAC_CONFIG_ITXA | MAC_CONFIG_RXEN | MAC_CONFIG_RETRYL | + MAC_CONFIG_PADEN | ( 0x18 << 16)), ioaddr + MAC_CONFIG ); + + outw( (0x1018), ioaddr + NBTOB_INTP_GAP ); + + /* clear and enable interrupts */ + inw( ioaddr + INTERRUPT_CLEAR ); + ncr885e_enable( dev ); + + /* and enable them in the chip */ + outl( (INTERRUPT_INTE|INTERRUPT_TX_MASK|INTERRUPT_RX_MASK)<<16, + ioaddr + INTERRUPT_ENABLE - 2); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: 53C885 config complete.\n", dev->name ); + + return; +} + + + +/* + transmit interrupt */ + +static void +ncr885e_tx( struct net_device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp, *dp; + unsigned short txbits, xfer; + int i; + + del_timer( &sp->tx_timeout ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: ncr885e_tx: active=%d, dirty=%d, current=%d\n", + dev->name, sp->tx_active, sp->tx_dirty, sp->tx_current ); + + sp->timeout_active = 0; + + i = sp->tx_dirty; + cp = sp->tx_cmds + (i*3); + dp = cp+1; + sp->tx_active--; + + xfer = inw( &dp->xfer_status ); + txbits = inw( &sp->tx_status[i] ); + + if (ncr885e_debug > 4) { + show_dbdma_cmd( cp ); + show_dbdma_cmd( dp ); + } + + /* get xmit result */ + txbits = inw( &sp->tx_status[i] ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: tx xfer=%04x, txbits=%04x\n", dev->name, + xfer, txbits ); + + /* look for any channel status (?) */ + if ( xfer ) { + + dev_kfree_skb( sp->tx_skbufs[i] ); + mark_bh( NET_BH ); + + if ( txbits & TX_STATUS_TXOK ) { + sp->stats.tx_packets++; + sp->stats.tx_bytes += inw( &cp->req_count ); + } + + /* dropped packets */ + if ( txbits & (TX_STATUS_TDLC|TX_STATUS_TDEC) ) { + sp->stats.tx_dropped++; + } + + /* add the collisions */ + sp->stats.collisions += ( txbits & 0x04 ); + + } + + dev->tbusy = 0; + + return; +} + +/* rx interrupt handling */ +static void +ncr885e_rx( struct net_device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp; + struct sk_buff *skb; + int i, nb; + unsigned short status; + unsigned char *data, *stats; + unsigned long rxbits, ioaddr = dev->base_addr; + + i = sp->rx_current; + cp = sp->rx_cmds + (i*2); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: ncr885e_rx dirty=%d, current=%d (cp@%p)\n", + dev->name, sp->rx_dirty, sp->rx_current, cp ); + + nb = inw( &cp->req_count ) - inw( &cp->res_count ); + status = inw( &cp->xfer_status ); + + if (ncr885e_debug > 3) + printk( KERN_INFO "%s: (rx %d) bytes=%d, xfer_status=%04x\n", + dev->name, i, nb, status ); + + if ( status ) { + + skb = sp->rx_skbufs[i]; + data = skb->data; + stats = data + nb - 3; + rxbits = (stats[0]|stats[1]<<8|stats[2]<<16); + + if (ncr885e_debug > 3) + printk( KERN_INFO " rx_bits=%06lx\n", rxbits ); + + skb->dev = dev; + skb_put( skb, nb-3 ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + sp->rx_skbufs[i] = 0; + + if ( rxbits & RX_STATUS_RXOK ) { + sp->stats.rx_packets++; + sp->stats.rx_bytes += nb; + } + + if ( rxbits & RX_STATUS_MCAST ) + sp->stats.multicast++; + + } + + sp->rx_dirty = sp->rx_current; + + if ( ++sp->rx_current >= NR_RX_RING ) + sp->rx_current = 0; + + /* fix up the one we just trashed */ + cp = sp->rx_cmds + (sp->rx_dirty * 2); + + skb = dev_alloc_skb( RX_BUFLEN + 2 ); + if ( skb != 0 ) { + skb_reserve( skb, 2 ); + sp->rx_skbufs[sp->rx_dirty] = skb; + } + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: ncr885e_rx: using ring index %d, filling cp @ %p\n", + dev->name, sp->rx_current, cp ); + + outw( RX_BUFLEN, &cp->req_count ); + outw( 0, &cp->res_count ); + data = skb->data; + outl( virt_to_bus( data ), &cp->phy_addr ); + outw( 0, &cp->xfer_status ); + + cp = sp->rx_cmds + (sp->rx_current * 2); + + /* restart rx DMA */ + outl( virt_to_bus( cp ), ioaddr + RX_CMD_PTR_LO ); + outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + return; +} + +static void +ncr885e_misc_ints( struct net_device *dev, unsigned short status ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct dbdma_cmd *cp; + unsigned long ioaddr = dev->base_addr; + + if (ncr885e_debug > 1) + printk( KERN_INFO "miscellaneous interrupt handled; status=%02x\n", + status ); + + /* various transmit errors */ + if ( status & + (INTERRUPT_PPET | INTERRUPT_PBFT | INTERRUPT_IIDT) ) { + + /* illegal instruction in tx dma */ + if ( status & INTERRUPT_IIDT ) { + + cp = (struct dbdma_cmd *) bus_to_virt( inl( ioaddr + TX_CMD_PTR_LO )); + printk( KERN_INFO "%s: tx illegal insn:\n", dev->name ); + printk( KERN_INFO " tx DBDMA - cmd = %p, status = %04x\n", + cp, inw( ioaddr + TX_CHANNEL_STATUS )); + printk( KERN_INFO " command = %04x, phy_addr=%08x, req_count=%04x\n", + inw( &cp->command ), inw( &cp->phy_addr ), inw( &cp->req_count )); + } + + if ( status & INTERRUPT_PPET ) + printk( KERN_INFO "%s: tx PCI parity error\n", dev->name ); + + if ( status & INTERRUPT_PBFT ) + printk( KERN_INFO "%s: tx PCI bus fault\n", dev->name ); + } + + /* look for rx errors */ + if ( status & + (INTERRUPT_PPER | INTERRUPT_PBFR | INTERRUPT_IIDR)) { + + /* illegal instruction in rx dma */ + if ( status & INTERRUPT_IIDR ) { +#if 0 + cmd = inl( ioaddr + RX_CMD_PTR_LO ); +#endif + printk( KERN_ERR "%s: rx illegal DMA instruction:\n", dev->name ); + printk( KERN_ERR " channel status=%04x,\n", + inl( ioaddr + RX_CHANNEL_STATUS )); +#if 0 + show_dbdma_cmd( bus_to_virt( inl( ioaddr + RX_CMD_PTR_LO ))); + printk( KERN_ERR " instr (%08x) %08x %08x %08x\n", + (int) cmd, cmd[0], cmd[1], cmd[2] ); +#endif + } + + /* PCI parity error */ + if ( status & INTERRUPT_PPER ) + printk( KERN_INFO "%s: rx PCI parity error\n", dev->name ); + + if ( status & INTERRUPT_PBFR ) + printk( KERN_INFO "%s: rx PCI bus fault\n", dev->name ); + + sp->stats.rx_errors++; + } + + if ( status & INTERRUPT_WI ) { + printk( KERN_INFO "%s: link pulse\n", dev->name ); + } + + /* bump any counters */ + + + return; +} + +static void +ncr885e_interrupt( int irq, void *dev_id, struct pt_regs *regs ) + +{ + struct net_device *dev = (struct net_device *) dev_id; + struct ncr885e_private *sp; + unsigned short status; + int ioaddr; + + if ( dev == NULL ) { + printk( KERN_ERR "symba: Interrupt IRQ %d for unknown device\n", irq ); + return; + } + + ioaddr = dev->base_addr; + sp = (struct ncr885e_private *) dev->priv; + spin_lock( &sp->lock ); + + if ( dev->interrupt ) { + printk( KERN_ERR "%s: Re-entering interrupt handler...\n", + dev->name ); + } + + dev->interrupt = 1; + status = inw( ioaddr + INTERRUPT_CLEAR ); + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: 53C885 interrupt 0x%02x\n", dev->name, status ); + + /* handle non-tx and rx interrupts first */ + if ( status & ~(INTERRUPT_DIT|INTERRUPT_DIR)) + ncr885e_misc_ints( dev, status ); + + /* look for tx interrupt: more to transmit, DBDMA stopped, or tx done */ + if ( ( status & INTERRUPT_DIT ) ) { + + if (ncr885e_debug > 2) + printk( KERN_INFO "%s: tx int; int=%02x, chan stat=%02x\n", + dev->name, status, inw( ioaddr + TX_CHANNEL_STATUS )); + + /* turn off timer */ + del_timer( &sp->tx_timeout ); + sp->timeout_active = 0; + + /* stop DMA */ + outl( TX_DBDMA_ENABLE << 16, ioaddr + TX_CHANNEL_CONTROL ); + + ncr885e_tx( dev ); + } + + if ( status & INTERRUPT_DIR ) { + + if ( ncr885e_debug > 2 ) + printk( KERN_INFO "%s: rx interrupt; int=%02x, rx channel stat=%02x\n", + dev->name, status, inw( ioaddr + RX_CHANNEL_STATUS )); + + /* stop DMA */ + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + /* and handle the interrupt */ + ncr885e_rx( dev ); + } + + dev->interrupt = 0; + spin_unlock( &sp->lock ); + + return; +} + + +/* doesn't set the address permanently, however... */ +static int +ncr885e_set_address( struct net_device *dev, void *addr ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + struct sockaddr *saddr = addr; + unsigned long flags; + unsigned short reg[3]; + unsigned char *ioaddr, *p; + int i; + + memcpy( dev->dev_addr, saddr->sa_data, dev->addr_len ); + + p = (unsigned char *) dev->dev_addr; + printk( KERN_INFO "%s: setting new MAC address - ", dev->name ); +#if 0 + for( p = (unsigned char *) dev->dev_addr, i=0; i < 6; i++, p++ ) + printk("%c%2.2x", i ? ':' : ' ', *p ); +#endif + + + p = (unsigned char *) ® + for( i=0; i < 6; i++ ) + p[i] = dev->dev_addr[i]; + +#if 0 + printk("%s: Setting new mac address - ", dev->name ); + for( i=0; i < 6; i++ ) { + printk("%02x", i ? ':' : ' ', p[i] ); + } + + printk("\n"); +#endif + + /* stop rx for the change */ + outl( RX_DBDMA_ENABLE << 16, ioaddr + RX_CHANNEL_CONTROL ); + + spin_lock_irqsave( &sp->lock, flags ); + + ioaddr = (unsigned char *) dev->base_addr; + + for( i = 0; i < 3; i++ ) { + reg[i] = ((reg[i] & 0xff) << 8) | ((reg[i] >> 8) & 0xff); + printk("%04x ", reg[i] ); + outw( reg[i], ioaddr + STATION_ADDRESS_0 + (i*2)); + } + printk("\n"); + + spin_unlock_irqrestore( &sp->lock, flags ); + + /* restart rx */ + outl((RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + return 0; +} + +static void +ncr885e_tx_timeout( unsigned long data ) + +{ + struct net_device *dev = (struct net_device *) data; + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long flags, ioaddr; + int i; + + save_flags( flags ); + cli(); + + ioaddr = dev->base_addr; + sp->timeout_active = 0; + i = sp->tx_dirty; + + /* if we weren't active, bail... */ + if ( sp->tx_active == 0 ) { + printk( KERN_INFO "%s: ncr885e_timeout...tx not active!\n", dev->name ); + goto out; + } + + printk( KERN_ERR "%s: 53C885 timed out. Resetting...\n", dev->name ); + + /* disable rx and tx DMA */ + outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL ); + outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL ); + + /* reset the chip */ + ncr885e_config( dev ); + ncr885e_enable( dev ); + + /* clear the wedged skb in the tx ring */ + sp->tx_active = 0; + ++sp->stats.tx_errors; + + if ( sp->tx_skbufs[i] ) { + dev_kfree_skb( sp->tx_skbufs[i] ); + sp->tx_skbufs[i] = 0; + } + + /* start anew from the beginning of the ring buffer (why not?) */ + sp->tx_current = 0; + dev->tbusy = 0; + mark_bh( NET_BH ); + + /* restart rx dma */ + outl( (RX_DBDMA_ENABLE << 16) | RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + out: + + restore_flags( flags ); +} + +static inline void +ncr885e_set_timeout( struct net_device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + + if ( sp->timeout_active ) + del_timer( &sp->tx_timeout ); + + sp->tx_timeout.expires = jiffies + TX_TIMEOUT; + sp->tx_timeout.function = ncr885e_tx_timeout; + sp->tx_timeout.data = (unsigned long) dev; + add_timer( &sp->tx_timeout ); + sp->timeout_active = 1; + restore_flags( flags ); +} + + +/* + * The goal is to set up DBDMA such that the rx ring contains only + * one DMA descriptor per ring element and the tx ring has two (using + * the cool features of branch- and wait-select. However, I'm not sure + * if it's possible. For now, we plod through it with 3 descriptors + * for tx, and two for rx. + */ + +static int +ncr885e_open( struct net_device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + unsigned long ioaddr = dev->base_addr; + struct sk_buff *skb; + int i, size; + char *data; + struct dbdma_cmd *cp; + unsigned long flags; + + /* allocate enough space for the tx and rx rings and a STOP descriptor */ + size = (sizeof( struct dbdma_cmd ) * + ((NR_TX_RING * 3) + (NR_RX_RING * 2) + 1)); + + cp = kmalloc( size, GFP_KERNEL ); + + if ( cp == 0 ) { + printk( KERN_ERR "Insufficient memory (%d bytes) for DBDMA\n", size ); + return -ENOMEM; + } + + spin_lock_init( &sp->lock ); + spin_lock_irqsave( &sp->lock, flags ); + + memset((char *) cp, 0, size ); + sp->head = cp; + + sp->stop_cmd = cp; + outl( DBDMA_STOP, &cp->command ); + + sp->rx_cmds = ++cp; + + for( i = 0; i < NR_RX_RING; i++ ) { + + cp = sp->rx_cmds + (i*2); + skb = dev_alloc_skb( RX_BUFLEN + 2 ); + + /* if there is insufficient memory, make this last ring use a + static buffer and leave the loop with that skb as final one */ + if ( skb == 0 ) { + printk( KERN_ERR "%s: insufficient memory for rx ring buffer\n", + dev->name ); + break; + } + + skb_reserve( skb, 2 ); + sp->rx_skbufs[i] = skb; + data = skb->data; + + /* The DMA commands here are done such that an EOP is the only + way that we should get an interrupt. This means that we could + fill more than one skbuff before getting the interrupt at EOP. */ + + /* Handle rx DMA such that it always interrupts.... */ + outw( (INPUT_MORE|INTR_ALWAYS), &cp->command ); + outw( RX_BUFLEN, &cp->req_count ); + outw( 0, &cp->res_count ); + outl( virt_to_bus( data ), &cp->phy_addr ); + outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep ); + outw( 0, &cp->xfer_status ); +#if 0 + printk( KERN_INFO "rx at %p\n", cp ); + show_dbdma_cmd( cp ); +#endif + ++cp; + + outw( DBDMA_STOP, &cp->command ); + + } + + /* initialize to all rx buffers are available, fill limit is the end */ + sp->rx_dirty = 0; + sp->rx_current = 0; + + /* fill the tx ring */ + sp->tx_cmds = cp+1; + + for( i = 0; i < NR_TX_RING; i++ ) { + + /* minimal setup for tx command */ + cp = sp->tx_cmds + (i*3); + outw( OUTPUT_LAST, &cp->command ); + if (ncr885e_debug > 3) { + printk( KERN_INFO "tx OUTPUT_LAST at %p\n", cp ); + show_dbdma_cmd( cp ); + } + + /* full setup for the status cmd */ + cp++; + outw( INPUT_LAST|INTR_ALWAYS|WAIT_IFCLR, &cp->command ); + outl( virt_to_bus( &sp->tx_status[i] ), &cp->phy_addr ); + outw( 2, &cp->req_count ); + if ( ncr885e_debug > 3) { + printk( KERN_INFO "tx INPUT_LAST cmd at %p\n", cp ); + show_dbdma_cmd( cp ); + } + + ++cp; + outw( DBDMA_STOP, &cp->command ); + + } +#if 0 + /* chain the last tx DMA command to the STOP cmd */ + outw((INPUT_LAST|INTR_ALWAYS|BR_ALWAYS), &cp->command ); + outl( virt_to_bus( sp->stop_cmd ), &cp->cmd_dep ); +#endif + sp->tx_active = 0; + sp->tx_current = 0; + sp->tx_dirty = 0; + + spin_unlock_irqrestore( &sp->lock, flags ); + + /* the order seems important here for some reason. If the MPIC isn't + enabled before the ethernet chip is enabled, shrapnel from the + bootloader causes us to receive interrupts even though we've not + yet enabled the tx channel. Go figure. It'd be better to configure + the chip in the probe1() routine, but then we don't see interrupts + at all. Everything looks all right on the logic analyzer, but... */ + + ncr885e_config( dev ); + + /* enable ethernet interrupts */ + if ( request_irq( dev->irq, &ncr885e_interrupt, SA_SHIRQ, chipname, dev )) { + printk( KERN_ERR "%s: can't get irq %d\n", dev->name, dev->irq ); + return -EAGAIN; + } + + (void) inw( ioaddr + INTERRUPT_CLEAR ); + + ncr885e_enable( dev ); + + /* start rx DBDMA */ + outl( virt_to_bus( sp->rx_cmds ), ioaddr + RX_CMD_PTR_LO ); + outl( (RX_DBDMA_ENABLE << 16)|RX_CHANNEL_RUN, + ioaddr + RX_CHANNEL_CONTROL ); + + dev->start = 1; + dev->tbusy = 0; + dev->interrupt = 0; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int +ncr885e_xmit_start( struct sk_buff *skb, struct net_device *dev ) + +{ + struct ncr885e_private *sp = (struct ncr885e_private *) dev->priv; + volatile struct dbdma_cmd *cp, *dp; + unsigned long flags, ioaddr = dev->base_addr; + int len, next, fill, entry; + + if ( ncr885e_debug > 3) + printk( KERN_INFO "%s: xmit_start len=%d, dirty=%d, current=%d, active=%d\n", + dev->name, skb->len, sp->tx_dirty, sp->tx_current, sp->tx_active ); + + spin_lock_irqsave( &sp->lock, flags ); + + /* find the free slot in the ring buffer */ + fill = sp->tx_current; + next = fill + 1; + + if ( next >= NR_TX_RING ) + next = 0; + + /* mark ourselves as busy, even if we have too many packets waiting */ + dev->tbusy = 1; + + /* see if it's necessary to defer this packet */ + if ( sp->tx_active >= MAX_TX_ACTIVE ) { + spin_unlock_irqrestore( &sp->lock, flags ); + return -1; + } + + sp->tx_active++; /* bump "active tx" count */ + sp->tx_current = next; /* and show that we've used this buffer */ + sp->tx_dirty = fill; /* and mark this one to get picked up */ + + len = skb->len; + + if ( len > ETH_FRAME_LEN ) { + printk( KERN_DEBUG "%s: xmit frame too long (%d)\n", dev->name, len ); + len = ETH_FRAME_LEN; + } + + /* get index into the tx DBDMA chain */ + entry = fill * 3; + sp->tx_skbufs[fill] = skb; + cp = sp->tx_cmds + entry; + dp = cp + 1; + + /* update the rest of the OUTPUT_MORE descriptor */ + outw( len, &cp->req_count ); + outl( virt_to_bus( skb->data ), &cp->phy_addr ); + outw( 0, &cp->xfer_status ); + outw( 0, &cp->res_count ); + + /* and finish off the INPUT_MORE */ + outw( 0, &dp->xfer_status ); + outw( 0, &dp->res_count ); + sp->tx_status[fill] = 0; + outl( virt_to_bus( &sp->tx_status[fill] ), &dp->phy_addr ); + + if ( ncr885e_debug > 2 ) + printk(KERN_INFO "%s: xmit_start: active %d, tx_current %d, tx_dirty %d\n", + dev->name, sp->tx_active, sp->tx_current, sp->tx_dirty ); + + if ( ncr885e_debug > 4 ) { + show_dbdma_cmd( cp ); + show_dbdma_cmd( dp ); + } + + + /* restart the tx DMA engine */ + outl( virt_to_bus( cp ), ioaddr + TX_CMD_PTR_LO ); + outl( (TX_DBDMA_ENABLE << 16)|TX_CHANNEL_RUN, + ioaddr + TX_CHANNEL_CONTROL ); + + ncr885e_set_timeout( dev ); + + spin_unlock_irqrestore( &sp->lock, flags ); + dev->trans_start = jiffies; + + return 0; +} + +static int +ncr885e_close(struct net_device *dev) + +{ + int i; + struct ncr885e_private *np = (struct ncr885e_private *) dev->priv; + unsigned long ioaddr = dev->base_addr; + + dev->start = 0; + dev->tbusy = 1; + + spin_lock( &np->lock ); + + printk(KERN_INFO "%s: NCR885E Ethernet closing...\n", dev->name ); + + if (ncr885e_debug > 1) + printk(KERN_DEBUG "%s: Shutting down Ethernet chip\n", dev->name); + + ncr885e_disable(dev); + + del_timer(&np->tx_timeout); + + /* flip off rx and tx */ + outl( (RX_DBDMA_ENABLE << 16), ioaddr + RX_CHANNEL_CONTROL ); + outl( (TX_DBDMA_ENABLE << 16), ioaddr + TX_CHANNEL_CONTROL ); + + /* free up the IRQ */ + free_irq( dev->irq, dev ); + + for( i = 0; i < NR_RX_RING; i++ ) { + if (np->rx_skbufs[i]) + dev_kfree_skb( np->rx_skbufs[i] ); + np->rx_skbufs[i] = 0; + } +#if 0 + for (i = 0; i < NR_TX_RING; i++) { + if (np->tx_skbufs[i]) + dev_kfree_skb(np->tx_skbufs[i]); + np->tx_skbufs[i] = 0; + } +#endif + spin_unlock( &np->lock ); + + kfree( np->head ); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +/* + * multicast promiscuous mode isn't used here. Allow code in the + * IP stack to determine which multicast packets are good or bad.... + * (this avoids having to use the hash table registers) + */ +static void +ncr885e_set_multicast( struct net_device *dev ) + +{ + int ioaddr = dev->base_addr; + + if ( ncr885e_debug > 3 ) + printk("%s: set_multicast: dev->flags = %x, AF=%04x\n", + dev->name, dev->flags, inw( ioaddr + ADDRESS_FILTER )); + + if ( dev->flags & IFF_PROMISC ) { + printk( KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name ); + outw( ADDRESS_RPPRO, ioaddr + ADDRESS_FILTER ); + } + + /* accept all multicast packets without checking the mc_list. */ + else if ( dev->flags & IFF_ALLMULTI ) { + printk( KERN_INFO "%s: Enabling all multicast packets.\n", + dev->name ); + outw( ADDRESS_RPPRM, ioaddr + ADDRESS_FILTER ); + } + + /* enable broadcast rx */ + else { + outw( ADDRESS_RPABC, ioaddr + ADDRESS_FILTER ); + } +} + +static struct net_device_stats * +ncr885e_stats( struct net_device *dev ) + +{ + struct ncr885e_private *np = (struct ncr885e_private *) dev->priv; + + return &np->stats; +} + +/* By this function, we're certain that we have a 885 Ethernet controller + * so we finish setting it up and wrap up all the required Linux ethernet + * configuration. + */ + +static int +ncr885e_probe1( struct net_device *dev, unsigned long ioaddr, unsigned char irq ) + +{ + struct ncr885e_private *sp; + unsigned short station_addr[3], val; + unsigned char *p; + int i; + + dev = init_etherdev( dev, 0 ); + + /* construct private data for the 885 ethernet */ + dev->priv = kmalloc( sizeof( struct ncr885e_private ), GFP_KERNEL ); + + if ( dev->priv == NULL ) + return -ENOMEM; + + sp = (struct ncr885e_private *) dev->priv; + memset( sp, 0, sizeof( struct ncr885e_private )); + + /* snag the station address and display it */ + for( i = 0; i < 3; i++ ) { + val = inw( ioaddr + STATION_ADDRESS_0 + (i*2)); + station_addr[i] = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); + } + + printk( KERN_INFO "%s: %s at %08lx,", dev->name, chipname, ioaddr ); + + p = (unsigned char *) &station_addr; + + for( i=0; i < 6; i++ ) { + dev->dev_addr[i] = *p; + printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i] ); + p++; + } + + printk(", IRQ %d.\n", irq ); + + request_region( ioaddr, NCR885E_TOTAL_SIZE, dev->name ); + + /* set up a timer */ + init_timer( &sp->tx_timeout ); + sp->timeout_active = 0; + + dev->base_addr = ioaddr; + dev->irq = irq; + + ether_setup( dev ); + + /* everything else */ + dev->open = ncr885e_open; + dev->stop = ncr885e_close; + dev->get_stats = ncr885e_stats; + dev->hard_start_xmit = ncr885e_xmit_start; + dev->set_multicast_list = ncr885e_set_multicast; + dev->set_mac_address = ncr885e_set_address; + + return 0; +} + +/* Since the NCR 53C885 is a multi-function chip, I'm not worrying about + * trying to get the the device(s) in slot order. For our (Synergy's) + * purpose, there's just a single 53C885 on the board and we don't + * worry about the rest. + */ + +int __init ncr885e_probe( struct net_device *dev ) +{ + struct pci_dev *pdev = NULL; + unsigned int ioaddr, chips = 0; + unsigned short cmd; + unsigned char irq, latency; + + while(( pdev = pci_find_device( PCI_VENDOR_ID_NCR, + PCI_DEVICE_ID_NCR_53C885_ETHERNET, + pdev )) != NULL ) { + + if ( !print_version ) { + print_version++; + printk( KERN_INFO "%s", version ); + } + + /* Use I/O space */ + pci_read_config_dword( pdev, PCI_BASE_ADDRESS_0, &ioaddr ); + pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq ); + + ioaddr &= ~3; + /* Adjust around the Grackle... */ +#ifdef CONFIG_GEMINI + ioaddr |= 0xfe000000; +#endif + + if ( check_region( ioaddr, NCR885E_TOTAL_SIZE )) + continue; + + /* finish off the probe */ + if ( !(ncr885e_probe1( dev, ioaddr, irq ))) { + + chips++; + + /* Access is via I/O space, bus master enabled... */ + pci_read_config_word( pdev, PCI_COMMAND, &cmd ); + + if ( !(cmd & PCI_COMMAND_MASTER) ) { + printk( KERN_INFO " PCI master bit not set! Now setting.\n"); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_word( pdev, PCI_COMMAND, cmd ); + } + + if ( !(cmd & PCI_COMMAND_IO) ) { + printk( KERN_INFO " Enabling I/O space.\n" ); + cmd |= PCI_COMMAND_IO; + pci_write_config_word( pdev, PCI_COMMAND, cmd ); + } + + pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &latency ); + + if ( latency < 10 ) { + printk( KERN_INFO " PCI latency timer (CFLT) is unreasonably" + " low at %d. Setting to 255.\n", latency ); + pci_write_config_byte( pdev, PCI_LATENCY_TIMER, 255 ); + } + } + } + + if ( !chips ) + return -ENODEV; + else + return 0; +} + +/* debugging to peek at dma descriptors */ +static void +show_dbdma_cmd( volatile struct dbdma_cmd *cmd ) + +{ + printk( KERN_INFO " cmd %04x, physaddr %08x, req_count %04x\n", + inw( &cmd->command ), inl( &cmd->phy_addr ), inw( &cmd->req_count )); + printk( KERN_INFO " res_count %04x, xfer_status %04x, branch %08x\n", + inw( &cmd->res_count ), inw( &cmd->xfer_status ),inl( &cmd->cmd_dep )); +} + +#if 0 +static int +read_eeprom( unsigned int ioaddr, int location ) + +{ + int loop; + unsigned char val; + + outb( (location & 0xff), ioaddr + EE_WORD_ADDR ); + + /* take spillover from location in control reg */ + outb(EE_CONTROL_RND_READB | (location & (0x7<<8)), ioaddr + EE_CONTROL); + + loop = 1000; + while( (inb( ioaddr + EE_STATUS) & EE_SEB) && + (loop > 0) ) { + udelay( 10 ); + loop--; + } + + if ( inb( ioaddr + EE_STATUS ) & EE_SEE ) { + printk("%s: Serial EEPROM read error\n", chipname); + val = 0xff; + } + + else + val = inb( ioaddr + EE_READ_DATA ); + + return (int) val; +} +#endif + +#ifdef NCR885E_DEBUG_MII +static void +show_mii( unsigned long ioaddr ) + +{ + int phyctrl, phystat, phyadvert, phypartner, phyexpan; + + phyctrl = read_mii( ioaddr, MII_AUTO_NEGOTIATION_CONTROL ); + phystat = read_mii( ioaddr, MII_AUTO_NEGOTIATION_STATUS ); + phyadvert = read_mii( ioaddr, MII_AUTO_NEGOTIATION_ADVERTISEMENT ); + phypartner = read_mii( ioaddr, MII_AUTO_NEGOTIATION_LINK_PARTNER ); + phyexpan = read_mii( ioaddr, MII_AUTO_NEGOTIATION_EXPANSION ); + + printk( KERN_INFO "PHY: advert=%d %s, partner=%s %s, link=%d, %s%s\n", + (phyadvert & MANATECH_100BASETX_FULL_DUPLEX ? 100 : 10), + (phyctrl & MANC_AUTO_NEGOTIATION_ENABLE ? "auto" : "fixed"), + (phypartner & MANLP_ACKNOWLEDGE ? + (phypartner & MANATECH_100BASETX_FULL_DUPLEX ? "100" : "10") : + "?"), + (phyexpan & MANE_LINK_PARTNER_AUTO_ABLE ? "auto" : "fixed"), + (phyctrl & MANC_PHY_SPEED_100 ? 100 : 10), + (phystat & MANS_LINK_STATUS ? "up" : "down"), + (phyexpan & MANE_PARALLEL_DETECTION_FAULT ? " PD-fault" : "" )); + return; +} + + +static int +read_mii( unsigned long ioaddr, int reg ) + +{ + int timeout; + + + timeout = 100000; + + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + + if ( timeout-- < 0 ) { + printk( KERN_INFO "Timed out waiting for MII\n" ); + return -1; + } + } + + outw( (1<<8) + reg, ioaddr + MII_ADDRESS ); + outw( MIIM_RSTAT, ioaddr + MIIM_COMMAND ); + + timeout = 100000; + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + if ( timeout-- < 0 ) { + printk( KERN_INFO "Timed out waiting for MII\n" ); + return -1; + } + } + + return( inw( ioaddr + MII_READ_DATA )); +} + +static void +write_mii( unsigned long ioaddr, int reg, int data ) + +{ + int timeout=100000; + + printk( KERN_INFO "MII indicator: %02x\n", inw( ioaddr + MII_INDICATOR )); + + while( inw( ioaddr + MII_INDICATOR ) & MII_BUSY ) { + if ( timeout-- <= 0 ) { + printk( KERN_INFO "Timeout waiting to write to MII\n" ); + return; + } + udelay( 10 ); + } + + outw( (1<<8) + reg, ioaddr + MII_ADDRESS ); + outw( data, ioaddr + MII_WRITE_DATA ); + + return; +} + +#endif /* NCR885E_DEBUG_MII */ + +#ifdef MODULE +#if defined(LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("dan@synergymicro.com"); +MODULE_DESCRIPTION("Symbios 53C885 Ethernet driver"); +MODULE_PARM(debug, "i"); +#endif + +static int debug = 1; + +int +init_module(void) +{ + if ( debug >= 0) + ncr885e_debug = debug; + + return ncr885e_probe( NULL ); +} + +void +cleanup_module(void) +{ + struct ncr885e_private *np; + + if ( root_dev ) { + + unregister_netdev( root_dev ); + np = (struct ncr885e_private *) root_dev->priv; + release_region( root_dev->base_addr, NCR885E_TOTAL_SIZE ); + kfree( root_dev->priv ); + root_dev = NULL; + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O6 -c symba.c" + * End: + */ diff --git a/drivers/net/ncr885e.h b/drivers/net/ncr885e.h new file mode 100644 index 000000000..bbcc82e11 --- /dev/null +++ b/drivers/net/ncr885e.h @@ -0,0 +1,367 @@ +#ifndef _NET_H_SYMBA +#define _NET_H_SYMBA + +/* transmit status bit definitions */ +#define TX_STATUS_TXOK (1<<13) /* success */ +#define TX_STATUS_TDLC (1<<12) /* dropped for late colls */ +#define TX_STATUS_TCXSDFR (1<<11) /* excessive deferral */ +#define TX_STATUS_TDEC (1<<10) /* excessive collisions */ +#define TX_STATUS_TAUR (1<<9) /* abort on underrun/"jumbo" */ +#define TX_STATUS_PDFRD (1<<8) /* packet deferred */ +#define TX_STATUS_BCAST (1<<7) /* broadcast ok */ +#define TX_STATUS_MCAST (1<<6) /* multicast ok */ +#define TX_STATUS_CRCERR (1<<5) /* CRC error */ +#define TX_STATUS_LC (1<<4) /* late collision */ +#define TX_STATUS_CCNT_MASK 0xf /* collision count */ + +#define T_TXOK (1<<13) +#define T_TDLC (1<<12) +#define T_TCXSDFR (1<<11) +#define T_TDEC (1<<10) +#define T_TAUR (1<<9) +#define T_PDFRD (1<<8) +#define T_BCAST (1<<7) +#define T_MCAST (1<<6) +#define T_LC (1<<4) +#define T_CCNT_MASK 0xf + +/* receive status bit definitions */ +#define RX_STATUS_RXOVRN (1<<23) /* overrun */ +#define RX_STATUS_CEPS (1<<22) /* carrier event already seen */ +#define RX_STATUS_RXOK (1<<21) /* success */ +#define RX_STATUS_BCAST (1<<20) /* broadcast ok */ +#define RX_STATUS_MCAST (1<<19) /* multicast ok */ +#define RX_STATUS_CRCERR (1<<18) /* CRC error */ +#define RX_STATUS_DR (1<<17) /* dribble nibble */ +#define RX_STATUS_RCV (1<<16) /* rx code violation */ +#define RX_STATUS_PTL (1<<15) /* pkt > 1518 bytes */ +#define RX_STATUS_PTS (1<<14) /* pkt < 64 bytes */ +#define RX_STATUS_LEN_MASK 0x1fff /* length mask */ + +#define EEPROM_LENGTH 100 + + +/* Serial EEPROM interface */ +#define EE_STATUS 0xf0 +#define EE_CONTROL 0xf1 +#define EE_WORD_ADDR 0xf2 +#define EE_READ_DATA 0xf3 +#define EE_WRITE_DATA 0xf4 +#define EE_FEATURE_ENB 0xf5 + +/* Use on EE_STATUS */ +#define EE_SEB (1<<8) +#define EE_SEE 1 + +/* Serial EEPROM commands */ +#define EE_CONTROL_SEQ_READB (1<<4) +#define EE_CONTROL_RND_WRITEB (1<<5) +#define EE_CONTROL_RND_READB ((1<<4)|(1<<5)) + +/* Enable writing to serial EEPROM */ +#define EE_WRITE_ENB 1 + +/* The 885 configuration register */ +#define MAC_CONFIG 0xa0 +#define MAC_CONFIG_SRST 1<<15 +#define MAC_CONFIG_ITXA 1<<13 +#define MAC_CONFIG_RXEN 1<<12 +#define MAC_CONFIG_INTLB 1<<10 +#define MAC_CONFIG_MODE_MASK (1<<8|1<<9) +#define MAC_CONFIG_MODE_TP 1<<8 +#define MAC_CONFIG_HUGEN 1<<5 +#define MAC_CONFIG_RETRYL 1<<4 +#define MAC_CONFIG_CRCEN 1<<3 +#define MAC_CONFIG_PADEN 1<<2 +#define MAC_CONFIG_FULLD 1<<1 +#define MAC_CONFIG_NOCFR 1<<0 + + + + + +#define TX_WAIT_SELECT 0x18 +#define RX_CHANNEL_CONTROL 0x40 + +/* Tx channel status */ +#define TX_DBDMA_REG 0x00 +#define TX_CHANNEL_CONTROL 0x00 +#define TX_CHANNEL_STATUS 0x04 +#define TX_STATUS_RUN 1<<15 +#define TX_STATUS_PAUSE 1<<14 +#define TX_STATUS_WAKE 1<<12 +#define TX_STATUS_DEAD 1<<11 +#define TX_STATUS_ACTIVE 1<<10 +#define TX_STATUS_BT 1<<8 +#define TX_STATUS_TXABORT 1<<7 +#define TX_STATUS_TXSR 1<<6 + +#define TX_CHANNEL_RUN TX_STATUS_RUN +#define TX_CHANNEL_PAUSE TX_STATUS_PAUSE +#define TX_CHANNEL_WAKE TX_STATUS_WAKE +#define TX_CHANNEL_DEAD TX_STATUS_DEAD +#define TX_CHANNEL_ACTIVE TX_STATUS_ACTIVE +#define TX_CHANNEL_BT TX_STATUS_BT +#define TX_CHANNEL_TXABORT TX_STATUS_TXABORT +#define TX_CHANNEL_TXSR TX_STATUS_TXSR + +#define TX_DBDMA_ENABLE (TX_CHANNEL_WAKE | TX_CHANNEL_PAUSE | \ + TX_CHANNEL_RUN ) + +/* Transmit command ptr lo register */ +#define TX_CMD_PTR_LO 0x0c + +/* Transmit interrupt select register */ +#define TX_INT_SELECT 0x10 + +/* Transmit branch select register */ +#define TX_BRANCH_SELECT 0x14 + +/* Transmit wait select register */ +#define TX_WAIT_SELECT 0x18 +#define TX_WAIT_STAT_RECV 0x40 + +/* Rx channel status */ +#define RX_DBDMA_REG 0x40 +#define RX_CHANNEL_CONTROL 0x40 +#define RX_CHANNEL_STATUS 0x44 +#define RX_STATUS_RUN 1<<15 +#define RX_STATUS_PAUSE 1<<14 +#define RX_STATUS_WAKE 1<<12 +#define RX_STATUS_DEAD 1<<11 +#define RX_STATUS_ACTIVE 1<<10 +#define RX_STATUS_BT 1<<8 +#define RX_STATUS_EOP 1<<6 + +#define RX_CHANNEL_RUN RX_STATUS_RUN +#define RX_CHANNEL_PAUSE RX_STATUS_PAUSE +#define RX_CHANNEL_WAKE RX_STATUS_WAKE +#define RX_CHANNEL_DEAD RX_STATUS_DEAD +#define RX_CHANNEL_ACTIVE RX_STATUS_ACTIVE +#define RX_CHANNEL_BT RX_STATUS_BT +#define RX_CHANNEL_EOP RX_STATUS_EOP + +#define RX_DBDMA_ENABLE (RX_CHANNEL_WAKE | RX_CHANNEL_PAUSE | \ + RX_CHANNEL_RUN) + +/* Receive command ptr lo */ +#define RX_CMD_PTR_LO 0x4c + +/* Receive interrupt select register */ +#define RX_INT_SELECT 0x50 +#define RX_INT_SELECT_EOP 0x40 + +/* Receive branch select */ +#define RX_BRANCH_SELECT 0x54 +#define RX_BRANCH_SELECT_EOP 0x40 + +/* Receive wait select */ +#define RX_WAIT_SELECT 0x58 +#define RX_WAIT_SELECT_EOP 0x40 + +/* Event status register */ +#define EVENT_STATUS 0x80 +#define EVENT_TXSR 1<<2 +#define EVENT_EOP 1<<1 +#define EVENT_TXABORT 1<<0 + +/* Interrupt enable register */ +#define INTERRUPT_ENABLE 0x82 + +/* Interrupt clear register */ +#define INTERRUPT_CLEAR 0x84 + +/* Interrupt status register */ +#define INTERRUPT_STATUS_REG 0x86 + +/* bits for the above three interrupt registers */ +#define INTERRUPT_INTE 1<<15 /* interrupt enable */ +#define INTERRUPT_WI 1<<9 /* wakeup interrupt */ +#define INTERRUPT_ERI 1<<8 /* early recieve interrupt */ +#define INTERRUPT_PPET 1<<7 /* PCI Tx parity error */ +#define INTERRUPT_PBFT 1<<6 /* PCI Tx bus fault */ +#define INTERRUPT_IIDT 1<<5 /* illegal instruction Tx */ +#define INTERRUPT_DIT 1<<4 /* DBDMA Tx interrupt */ +#define INTERRUPT_PPER 1<<3 /* PCI Rx parity error */ +#define INTERRUPT_PBFR 1<<2 /* PCI Rx bus fault */ +#define INTERRUPT_IIDR 1<<1 /* illegal instruction Rx */ +#define INTERRUPT_DIR 1<<0 /* DBDMA Rx interrupt */ + +#define INTERRUPT_TX_MASK (INTERRUPT_PBFT|INTERRUPT_IIDT| \ + INTERRUPT_PPET|INTERRUPT_DIT) +#define INTERRUPT_RX_MASK (INTERRUPT_PBFR|INTERRUPT_IIDR| \ + INTERRUPT_PPER|INTERRUPT_DIR) + +/* chip revision register */ +#define CHIP_REVISION_REG 0x8c +#define CHIP_PCIREV_MASK (0xf<<16) +#define CHIP_PCIDEV_MASK 0xff + +/* Tx threshold register */ +#define TX_THRESHOLD 0x94 + +/* General purpose register */ +#define GEN_PURPOSE_REG 0x9e + +/* General purpose pin control reg */ +#define GEN_PIN_CONTROL_REG 0x9f + +/* DBDMA control register */ +#define DBDMA_CONTROL 0x90 +#define DBDMA_SRST 1<<31 +#define DBDMA_TDPCE 1<<23 +#define DBDMA_BE 1<<22 +#define DBDMA_TAP_MASK (1<<19|1<<20|1<<21) +#define DBDMA_RAP_MASK (1<<16|1<<17|1<<18) +#define DBDMA_DPMRLE 1<<15 +#define DBDMA_WIE 1<<14 +#define DBDMA_MP 1<<13 +#define DBDMA_SME 1<<12 +#define DBDMA_CME 1<<11 +#define DBDMA_DDPE 1<<10 +#define DBDMA_TDPE 1<<9 +#define DBDMA_EXTE 1<<8 +#define DBDMA_BST_MASK (1<<4|1<<5|1<<6) +#define DBDMA_BSR_MASK (1<<0|1<<1|1<<2) + +#define DBDMA_BURST_1 (0x00) +#define DBDMA_BURST_2 (0x01) +#define DBDMA_BURST_4 (0x02) +#define DBDMA_BURST_8 (0x03) +#define DBDMA_BURST_16 (0x04) +#define DBDMA_BURST_32 (0x05) +#define DBDMA_BURST_64 (0x06) +#define DBDMA_BURST_128 (0x07) + +#define DBDMA_TX_BST_SHIFT (4) +#define DBDMA_RX_BST_SHIFT (0) + +#define DBDMA_TX_ARBITRATION_DEFAULT ( 1 << 19 ) +#define DBDMA_RX_ARBITRATION_DEFAULT ( 2 << 16 ) + + +/* Back-to-back interpacket gap register */ +#define BTOB_INTP_GAP 0xa2 +#define BTOB_INTP_DEFAULT 0x18 + +/* Non-back-to-back interpacket gap register */ +#define NBTOB_INTP_GAP 0xa4 + +/* MIIM command register */ +#define MIIM_COMMAND 0xa6 +#define MIIM_SCAN 1<<1 +#define MIIM_RSTAT 1<<0 + +/* MII address register */ +#define MII_ADDRESS 0xa8 +#define MII_FIAD_MASK (1<<8|1<<9|1<<10|1<<11|1<<12) +#define MII_RGAD_MASK (1<<0|1<<1|1<<2|1<<3|1<<4) + +#define TPPMD_CONTROL_REG 0xa8 +#define TPPMD_FO 1<<1 +#define TPPMD_LB 1<<0 + +/* MII read and write registers */ +#define MII_WRITE_DATA 0xaa +#define MII_READ_DATA 0xac + +/* MII indicators */ +#define MII_INDICATOR 0xae +#define MII_NVALID 1<<2 +#define MII_SCAN 1<<1 +#define MII_BUSY 1<<0 + +/* Address filter */ +#define ADDRESS_FILTER 0xd0 +#define ADDRESS_RPPRM 1<<3 /* multicast promis. mode */ +#define ADDRESS_RPPRO 1<<2 /* promiscuous mode */ +#define ADDRESS_RPAMC 1<<1 /* accept multicasts */ +#define ADDRESS_RPABC 1<<0 /* accept broadcasts */ + +/* Station addresses + + Note that if the serial EEPROM is disabled, these values are all + zero. If, like us, you get the chips when they're fresh, they're + also zero and you have to initialize the address */ +#define STATION_ADDRESS_0 0xd2 +#define STATION_ADDRESS_1 0xd4 +#define STATION_ADDRESS_2 0xd6 + +/* Hash tables */ +#define HASH_TABLE_0 0xd8 +#define HASH_TABLE_1 0xda +#define HASH_TABLE_2 0xdc +#define HASH_TABLE_3 0xde + +/* PHY indentifiers */ +#define PHY_IDENTIFIER_0 0xe4 +#define PHY_IDENTIFIER_1 0xe6 + +/* MII Auto-negotiation register definitions */ + +#define MII_AUTO_NEGOTIATION_CONTROL (0x0000) +#define MANC_PHY_RESET (0x8000) +#define MANC_PHY_LOOPBACK_ENABLE (0x4000) +#define MANC_PHY_LOOPBACK_DISABLE (0x0000) +#define MANC_PHY_SPEED_100 (0x2000) +#define MANC_PHY_SPEED_10 (0x0000) +#define MANC_AUTO_NEGOTIATION_ENABLE (0x1000) +#define MANC_AUTO_NEGOTIATION_DISABLE (0x0000) +#define MANC_PHY_POWER_DOWN (0x0800) +#define MANC_PHY_POWER_UP (0x0000) +#define MANC_ISOLATE_ENABLE (0x0400) +#define MANC_ISOLATE_DISABLE (0x0000) +#define MANC_RESTART_AUTO_NEGOTIATION (0x0200) +#define MANC_FULL_DUPLEX (0x0100) +#define MANC_HALF_DUPLEX (0x0000) + +#define MII_AUTO_NEGOTIATION_STATUS (0x0001) +#define MANS_100BASE_T4_HALF_DUPLEX (0x8000) +#define MANS_100BASE_X_FULL_DUPLEX (0x4000) +#define MANS_100BASE_X_HALF_DUPLEX (0x2000) +#define MANS_10MBS_FULL_DUPLEX (0x1000) +#define MANS_10MBS_HALF_DUPLEX (0x0800) +#define MANS_AUTO_NEGOTIATION_COMPLETE (0x0020) +#define MANS_REMOTE_FAULT (0x0010) +#define MANS_AUTO_NEGOTIATION_ABILITY (0x0008) +#define MANS_LINK_STATUS (0x0004) +#define MANS_JABBER_DETECT (0x0002) +#define MANS_EXTENDED_CAPABILITY (0x0001) + +#define MII_PHY_IDENTIFIER_1 (0x0002) +#define MII_PHY_IDENTIFIER_2 (0x0003) + +#define MII_AUTO_NEGOTIATION_ADVERTISEMENT (0x0004) +#define MANA_NEXT_PAGE (0x8000) +#define MANA_REMOTE_FAULT (0x2000) +#define MANA_TECHNOLOGY_ABILITY_MASK (0x1FE0) +#define MANATECH_10BASET_HALF_DUPLEX (0x0020) +#define MANATECH_10BASET_FULL_DUPLEX (0x0040) +#define MANATECH_100BASETX_HALF_DUPLEX (0x0080) +#define MANATECH_100BASETX_FULL_DUPLEX (0x0100) +#define MANATECH_100BASET4 (0x0200) +#define MANA_SELECTOR_MASK (0x001F) +#define MANASELECTOR_802_3 (0x0001) + +#define MII_AUTO_NEGOTIATION_LINK_PARTNER (0x0005) +#define MANLP_NEXT_PAGE (0x8000) +#define MANLP_ACKNOWLEDGE (0x4000) +#define MANLP_REMOTE_FAULT (0x2000) +#define MANLP_TECHNOLOGY_ABILITY_MASK (0x1FE0) +#define MANLP_SELECTOR_MASK (0x001F) + +#define MII_AUTO_NEGOTIATION_EXPANSION (0x0006) +#define MANE_PARALLEL_DETECTION_FAULT (0x0010) +#define MANE_LINK_PARTNER_NEXT_PAGE_ABLE (0x0008) +#define MANE_NEXT_PAGE_ABLE (0x0004) +#define MANE_PAGE_RECEIVED (0x0002) +#define MANE_LINK_PARTNER_AUTO_ABLE (0x0001) + +#define MII_AUTO_NEGOTIATION_NEXT_PAGE_TRANSMIT (0x0007) +#define MANNPT_NEXT_PAGE (0x8000) +#define MANNPT_MESSAGE_PAGE (0x2000) +#define MANNPT_ACKNOWLEDGE_2 (0x1000) +#define MANNPT_TOGGLE (0x0800) +#define MANNPT_MESSAGE_FIELD_MASK (0x07FF) + +#endif diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c index 8af63739c..81bba9e60 100644 --- a/drivers/net/pcmcia/ray_cs.c +++ b/drivers/net/pcmcia/ray_cs.c @@ -57,21 +57,17 @@ left out. If you compile with PCMCIA_DEBUG=0, the debug code will be present but disabled -- but it can then be enabled for specific modules at load time with a 'pc_debug=#' option to insmod. - - I found that adding -DPCMCIA_DEBUG to the compile options during - the 'make config' resulted in cardmgr not finding any sockets. - Therefore, this module uses RAYLINK_DEBUG instead. - The module option to use is ray_debug=# - where # is 1 for modest output - 2 for more output - ... */ #ifdef RAYLINK_DEBUG -static int ray_debug = RAYLINK_DEBUG; -MODULE_PARM(ray_debug, "i"); -/* #define DEBUG(n, args...) if (ray_debug>(n)) printk(KERN_DEBUG args); */ -#define DEBUG(n, args...) if (ray_debug>(n)) printk(args); +#define PCMCIA_DEBUG RAYLINK_DEBUG +#endif +#ifdef PCMCIA_DEBUG +static int ray_debug = 0; +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +/* #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); */ +#define DEBUG(n, args...) if (pc_debug>(n)) printk(args); #else #define DEBUG(n, args...) #endif @@ -137,7 +133,6 @@ void start_net(u_long local); /* void start_net(ray_dev_t *local); */ int ray_cs_proc_read(char *buf, char **start, off_t off, int len, int spare); - /* Create symbol table for registering with kernel in init_module */ EXPORT_SYMBOL(ray_dev_ioctl); EXPORT_SYMBOL(ray_rx); @@ -147,40 +142,30 @@ EXPORT_SYMBOL(ray_rx); /* Bit map of interrupts to choose from */ /* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ static u_long irq_mask = 0xdeb8; -MODULE_PARM(irq_mask,"i"); /* ADHOC=0, Infrastructure=1 */ static int net_type = ADHOC; -MODULE_PARM(net_type,"i"); /* Hop dwell time in Kus (1024 us units defined by 802.11) */ static int hop_dwell = 128; -MODULE_PARM(hop_dwell,"i"); /* Beacon period in Kus */ static int beacon_period = 256; -MODULE_PARM(beacon_period,"i"); /* power save mode (0 = off, 1 = save power) */ static int psm = 0; -MODULE_PARM(psm,"i"); /* String for network's Extended Service Set ID. 32 Characters max */ static char *essid = NULL; -MODULE_PARM(essid,"s"); /* Default to encapsulation unless translation requested */ static int translate = 1; -MODULE_PARM(translate,"i"); static int country = USA; -MODULE_PARM(country,"i"); static int sniffer = 0; -MODULE_PARM(sniffer,"i"); static int bc = 0; -MODULE_PARM(bc,"i"); /* 48 bit physical card address if overriding card's real physical * address is required. Since IEEE 802.11 addresses are 48 bits @@ -193,7 +178,6 @@ MODULE_PARM(bc,"i"); * things will happen if it is not 0 in a card address. */ static char *phy_addr = NULL; -MODULE_PARM(phy_addr,"s"); /* The dev_info variable is the "key" that is used to match up this @@ -214,7 +198,22 @@ static dev_link_t *dev_list = NULL; 'priv' pointer in a dev_link_t structure can be used to point to a device-specific private data structure, like this. */ -static const unsigned int ray_mem_speed = 0x2A; +static unsigned int ray_mem_speed = 0x2A; + +MODULE_AUTHOR("Corey Thomas <corey@world.std.com>"); +MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver"); +MODULE_PARM(irq_mask,"i"); +MODULE_PARM(net_type,"i"); +MODULE_PARM(hop_dwell,"i"); +MODULE_PARM(beacon_period,"i"); +MODULE_PARM(psm,"i"); +MODULE_PARM(essid,"s"); +MODULE_PARM(translate,"i"); +MODULE_PARM(country,"i"); +MODULE_PARM(sniffer,"i"); +MODULE_PARM(bc,"i"); +MODULE_PARM(phy_addr,"s"); +MODULE_PARM(ray_mem_speed, "i"); static UCHAR b5_default_startup_parms[] = { 0, 0, /* Adhoc station */ @@ -284,7 +283,7 @@ static UCHAR b4_default_startup_parms[] = { /*===========================================================================*/ static unsigned char eth2_llc[] = {0xaa, 0xaa, 3, 0, 0, 0}; -static char rcsid[] = " $Id: ray_cs.c,v 1.60 1999/09/01 20:58:45 corey Exp $ - Corey Thomas corey@world.std.com"; +static char rcsid[] = "Raylink/WebGear wireless LAN - Corey <Thomas corey@world.std.com>"; #ifdef CONFIG_PROC_FS struct proc_dir_entry ray_cs_proc_entry = { @@ -358,7 +357,7 @@ dev_link_t *ray_attach(void) local->finder = link; link->dev = &local->node; local->card_status = CARD_INSERTED; - local->authentication_state = UNAUTHENTICATED; + local->authentication_state = UNAUTHENTICATED; local->num_multi = 0; DEBUG(2,"ray_attach link = %p, dev = %p, local = %p, intr = %p\n", link,dev,local,&ray_interrupt); @@ -442,8 +441,6 @@ void ray_detach(dev_link_t *link) if (link->state & DEV_CONFIG) { ray_release((u_long)link); if(link->state & DEV_STALE_CONFIG) { - DEBUG(0,"ray_cs: detach postponed, '%s' " - "still locked\n", link->dev->dev_name); link->state |= DEV_STALE_LINK; return; } @@ -472,7 +469,7 @@ void ray_detach(dev_link_t *link) =============================================================================*/ #define CS_CHECK(fn, args...) \ while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed -#define MAX_TUPLE_SIZE 80 +#define MAX_TUPLE_SIZE 128 void ray_config(dev_link_t *link) { client_handle_t handle = link->handle; @@ -480,7 +477,7 @@ void ray_config(dev_link_t *link) cisparse_t parse; int last_fn, last_ret; int i; - u_char buf[80]; + u_char buf[MAX_TUPLE_SIZE]; win_req_t req; memreq_t mem; struct net_device *dev = (struct net_device *)link->priv; @@ -499,6 +496,19 @@ void ray_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; + /* Determine card type and firmware version */ + buf[0] = buf[MAX_TUPLE_SIZE - 1] = 0; + tuple.DesiredTuple = CISTPL_VERS_1; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleData = buf; + tuple.TupleDataMax = MAX_TUPLE_SIZE; + tuple.TupleOffset = 2; + CS_CHECK(GetTupleData, handle, &tuple); + + for (i=0; i<tuple.TupleDataLen - 4; i++) + if (buf[i] == 0) buf[i] = ' '; + printk(KERN_INFO "ray_cs Detected: %s\n",buf); + /* Configure card */ link->state |= DEV_CONFIG; @@ -562,7 +572,10 @@ void ray_config(dev_link_t *link) } link->state &= ~DEV_CONFIG_PENDING; - DEBUG(0, "ray_cs device loaded\n"); + printk(KERN_INFO "%s: RayLink, irq %d, hw_addr ", + dev->name, dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); return; @@ -594,7 +607,8 @@ int ray_init(struct net_device *dev) /* Check Power up test status and get mac address from card */ if (local->startup_res.startup_word != 0x80) { -DEBUG(0,"ray_init ERROR card status = %2x\n", local->startup_res.startup_word); + printk(KERN_INFO "ray_init ERROR card status = %2x\n", + local->startup_res.startup_word); local->card_status = CARD_INIT_ERROR; return -1; } @@ -619,16 +633,12 @@ DEBUG(0,"ray_init ERROR card status = %2x\n", local->startup_res.startup_word); if (parse_addr(phy_addr, local->sparm.b4.a_mac_addr)) { p = local->sparm.b4.a_mac_addr; - DEBUG(1,"ray_cs phy address overridden = %2x %2x %2x %2x %2x %2x\n",\ - p[0],p[1],p[2],p[3],p[4],p[5]); } else { memcpy(&local->sparm.b4.a_mac_addr, &local->startup_res.station_addr, ADDRLEN); p = local->sparm.b4.a_mac_addr; - DEBUG(1,"ray_cs phy addr= %2x %2x %2x %2x %2x %2x\n",\ - p[0],p[1],p[2],p[3],p[4],p[5]); } clear_interrupt(local); /* Clear any interrupt from the card */ @@ -647,7 +657,7 @@ int dl_startup_params(struct net_device *dev) DEBUG(1,"dl_startup_params entered\n"); if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs dl_startup_params - device not present\n"); + DEBUG(2,"ray_cs dl_startup_params - device not present\n"); return -1; } @@ -661,14 +671,15 @@ int dl_startup_params(struct net_device *dev) /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) == -1) return -1; + if ((ccsindex = get_free_ccs(local)) < 0) return -1; local->dl_param_ccs = ccsindex; pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd); DEBUG(2,"dl_startup_params start ccsindex = %d\n", local->dl_param_ccs); /* Interrupt the firmware to process the command */ if (interrupt_ecf(local, ccsindex)) { - DEBUG(0,"ray dl_startup_params failed - ECF not ready for intr\n"); + printk(KERN_INFO "ray dl_startup_params failed - " + "ECF not ready for intr\n"); local->card_status = CARD_DL_PARAM_ERROR; writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); return -2; @@ -723,11 +734,11 @@ void init_startup_params(ray_dev_t *local) local->sparm.b4.a_curr_country_code = country; local->sparm.b4.a_hop_pattern_length = hop_pattern_length[(int)country] - 1; - if (bc) - { - local->sparm.b4.a_ack_timeout = 0x50; - local->sparm.b4.a_sifs = 0x3f; - } + if (bc) + { + local->sparm.b4.a_ack_timeout = 0x50; + local->sparm.b4.a_sifs = 0x3f; + } } else { /* Version 5 uses real kus values */ memcpy((UCHAR *)&local->sparm.b5, b5_default_startup_parms, @@ -756,30 +767,29 @@ void verify_dl_startup(u_long data) ray_dev_t *local = (ray_dev_t *)data; struct ccs *pccs = ((struct ccs *)(local->sram + CCS_BASE)) + local->dl_param_ccs; UCHAR status; -/* UCHAR *p = local->sram + HOST_TO_ECF_BASE; */ dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs verify_dl_startup - device not present\n"); + DEBUG(2,"ray_cs verify_dl_startup - device not present\n"); return; } -#ifdef RAYLINK_DEBUG - { - int i; - DEBUG(2,"verify_dl_startup parameters sent via ccs %d:\n",\ - local->dl_param_ccs); - for (i=0; i<sizeof(struct b5_startup_params); i++) - { - DEBUG(1," %2x ", readb(local->sram + HOST_TO_ECF_BASE + i)); +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + int i; + printk(KERN_DEBUG "verify_dl_startup parameters sent via ccs %d:\n", + local->dl_param_ccs); + for (i=0; i<sizeof(struct b5_startup_params); i++) { + printk(" %2x", readb(local->sram + HOST_TO_ECF_BASE + i)); } - DEBUG(1,"\n"); + printk("\n"); } #endif status = readb(&pccs->buffer_status); if (status!= CCS_BUFFER_FREE) { - DEBUG(0,"Download startup params failed. Status = %d\n",status); + printk(KERN_INFO "Download startup params failed. Status = %d\n", + status); local->card_status = CARD_DL_PARAM_ERROR; return; } @@ -799,11 +809,11 @@ void start_net(u_long data) int ccsindex; dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs start_net - device not present\n"); + DEBUG(2,"ray_cs start_net - device not present\n"); return; } /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) == -1) return; + if ((ccsindex = get_free_ccs(local)) < 0) return; pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; writeb(CCS_START_NETWORK, &pccs->cmd); writeb(0, &pccs->var.start_network.update_param); @@ -827,11 +837,11 @@ void join_net(u_long data) dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs join_net - device not present\n"); + DEBUG(2,"ray_cs join_net - device not present\n"); return; } /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) == -1) return; + if ((ccsindex = get_free_ccs(local)) < 0) return; pccs = ((struct ccs *)(local->sram + CCS_BASE)) + ccsindex; writeb(CCS_JOIN_NETWORK, &pccs->cmd); writeb(0, &pccs->var.join_network.update_param); @@ -867,7 +877,7 @@ void ray_release(u_long arg) link->state |= DEV_STALE_CONFIG; return; } - del_timer(&local->timer); + del_timer(&local->timer); if (link->dev != '\0') unregister_netdev(dev); /* Unlink the device chain */ link->dev = NULL; @@ -962,13 +972,14 @@ int ray_dev_init(struct net_device *dev) DEBUG(1,"ray_dev_init(dev=%p)\n",dev); if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_dev_init - device not present\n"); + DEBUG(2,"ray_dev_init - device not present\n"); return -1; } /* Download startup parameters */ if ( (i = dl_startup_params(dev)) < 0) { - DEBUG(0,"ray_dev_init dl_startup_params failed - returns 0x%x/n",i); + printk(KERN_INFO "ray_dev_init dl_startup_params failed - " + "returns 0x%x/n",i); return -1; } @@ -976,15 +987,6 @@ int ray_dev_init(struct net_device *dev) memcpy(&dev->dev_addr, &local->sparm.b4.a_mac_addr, ADDRLEN); memset(dev->broadcast, 0xff, ETH_ALEN); -#ifdef RAYLINK_DEBUG - { - UCHAR *p; - p = (UCHAR *)(local->startup_res.station_addr); - DEBUG(1,"ray_dev_init card hardware mac addr = %2x %2x %2x %2x %2x %2x\n",\ - p[0],p[1],p[2],p[3],p[4],p[5]); - } -#endif - DEBUG(2,"ray_dev_init ending\n"); return 0; } @@ -996,7 +998,7 @@ int ray_dev_config(struct net_device *dev, struct ifmap *map) /* Dummy routine to satisfy device structure */ DEBUG(1,"ray_dev_config(dev=%p,ifmap=%p)\n",dev,map); if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_dev_config - device not present\n"); + DEBUG(2,"ray_dev_config - device not present\n"); return -1; } @@ -1010,13 +1012,13 @@ int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) short length; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_dev_start_xmit - device not present\n"); + DEBUG(2,"ray_dev_start_xmit - device not present\n"); return -1; } DEBUG(3,"ray_dev_start_xmit(skb=%p, dev=%p)\n",skb,dev); if (dev->tbusy) { - DEBUG(2,"ray_dev_start_xmit busy\n"); + printk(KERN_NOTICE "ray_dev_start_xmit busy\n"); return 1; } if (local->authentication_state == NEED_TO_AUTH) { @@ -1031,7 +1033,7 @@ int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; switch (ray_hw_xmit( skb->data, length, dev, DATA_TYPE)) { case XMIT_NO_CCS: - case XMIT_NEED_AUTH: + case XMIT_NEED_AUTH: dev->tbusy = 1; return 1; case XMIT_NO_INTR: @@ -1058,15 +1060,20 @@ int ray_hw_xmit(unsigned char* data, int len, struct net_device* dev, DEBUG(3,"ray_hw_xmit(data=%p, len=%d, dev=%p)\n",data,len,dev); if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) { - DEBUG(0,"ray_hw_xmit packet to large %d bytes\n",len); + printk(KERN_INFO "ray_hw_xmit packet too large: %d bytes\n",len); return XMIT_MSG_BAD; } - if ((ccsindex = get_free_tx_ccs(local)) == -1) - { - DEBUG(2,"ray_hw_xmit - No free tx ccs\n"); + switch (ccsindex = get_free_tx_ccs(local)) { + case ECCSBUSY: + DEBUG(2,"ray_hw_xmit tx_ccs table busy\n"); + case ECCSFULL: + DEBUG(2,"ray_hw_xmit No free tx ccs\n"); + case ECARDGONE: dev->tbusy = 1; return XMIT_NO_CCS; - } + default: + break; + } addr = TX_BUF_BASE + (ccsindex << 11); if (msg_type == DATA_TYPE) { @@ -1186,7 +1193,7 @@ int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) int err = 0; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_dev_ioctl - device not present\n"); + DEBUG(2,"ray_dev_ioctl - device not present\n"); return -1; } DEBUG(2,"ray_cs IOCTL dev=%p, ifr=%p, cmd = 0x%x\n",dev,ifr,cmd); @@ -1258,16 +1265,14 @@ void ray_reset(struct net_device *dev) { int interrupt_ecf(ray_dev_t *local, int ccs) { int i = 50; -/* UCHAR *p = (local->amem + CIS_OFFSET + ECF_INTR_OFFSET); */ dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs interrupt_ecf - device not present\n"); + DEBUG(2,"ray_cs interrupt_ecf - device not present\n"); return -1; } DEBUG(2,"interrupt_ecf(local=%p, ccs = 0x%x\n",local,ccs); -/* while ( i && (*p & ECF_INTR_SET)) i--; */ while ( i && (readb(local->amem + CIS_OFFSET + ECF_INTR_OFFSET) & ECF_INTR_SET)) i--; @@ -1275,8 +1280,8 @@ int interrupt_ecf(ray_dev_t *local, int ccs) DEBUG(2,"ray_cs interrupt_ecf card not ready for interrupt\n"); return -1; } - - *(local->sram + SCB_BASE) = ccs; + /* Fill the mailbox, then kick the card */ + writeb(ccs, local->sram + SCB_BASE); writeb(ECF_INTR_SET, local->amem + CIS_OFFSET + ECF_INTR_OFFSET); return 0; } /* interrupt_ecf */ @@ -1290,19 +1295,26 @@ int get_free_tx_ccs(ray_dev_t *local) dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs get_free_tx_ccs - device not present\n"); - return -1; + DEBUG(2,"ray_cs get_free_tx_ccs - device not present\n"); + return ECARDGONE; } + if (test_and_set_bit(0,&local->tx_ccs_lock)) { + DEBUG(1,"ray_cs tx_ccs_lock busy\n"); + return ECCSBUSY; + } + for (i=0; i < NUMBER_OF_TX_CCS; i++) { if (readb(&(pccs+i)->buffer_status) == CCS_BUFFER_FREE) { writeb(CCS_BUFFER_BUSY, &(pccs+i)->buffer_status); writeb(CCS_END_LIST, &(pccs+i)->link); + local->tx_ccs_lock = 0; return i; } } - DEBUG(1,"ray_cs ERROR no free tx CCS for raylink card\n"); - return -1; + local->tx_ccs_lock = 0; + DEBUG(2,"ray_cs ERROR no free tx CCS for raylink card\n"); + return ECCSFULL; } /* get_free_tx_ccs */ /*===========================================================================*/ /* Get next free CCS */ @@ -1314,25 +1326,33 @@ int get_free_ccs(ray_dev_t *local) dev_link_t *link = local->finder; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs get_free_ccs - device not present\n"); - return -1; + DEBUG(2,"ray_cs get_free_ccs - device not present\n"); + return ECARDGONE; } + if (test_and_set_bit(0,&local->ccs_lock)) { + DEBUG(1,"ray_cs ccs_lock busy\n"); + return ECCSBUSY; + } + for (i = NUMBER_OF_TX_CCS; i < NUMBER_OF_CCS; i++) { if (readb(&(pccs+i)->buffer_status) == CCS_BUFFER_FREE) { writeb(CCS_BUFFER_BUSY, &(pccs+i)->buffer_status); writeb(CCS_END_LIST, &(pccs+i)->link); + local->ccs_lock = 0; return i; } } + local->ccs_lock = 0; DEBUG(1,"ray_cs ERROR no free CCS for raylink card\n"); - return -1; + return ECCSFULL; } /* get_free_ccs */ /*===========================================================================*/ void authenticate_timeout(u_long data) { ray_dev_t *local = (ray_dev_t *)data; del_timer(&local->timer); - DEBUG(0,"ray_cs Authentication with access point failed - timeout\n"); + printk(KERN_INFO "ray_cs Authentication with access point failed" + " - timeout\n"); join_net((u_long)local); } /*===========================================================================*/ @@ -1381,26 +1401,26 @@ struct enet_statistics *ray_get_stats(struct net_device *dev) dev_link_t *link = local->finder; struct status *p = (struct status *)(local->sram + STATUS_BASE); if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_cs enet_statistics - device not present\n"); + DEBUG(2,"ray_cs enet_statistics - device not present\n"); return &local->stats; } - if (p->mrx_overflow_for_host) + if (readb(&p->mrx_overflow_for_host)) { - local->stats.rx_over_errors += ntohs(p->mrx_overflow); - p->mrx_overflow = 0; - p->mrx_overflow_for_host = 0; + local->stats.rx_over_errors += ntohs(readb(&p->mrx_overflow)); + writeb(0,&p->mrx_overflow); + writeb(0,&p->mrx_overflow_for_host); } - if (p->mrx_checksum_error_for_host) + if (readb(&p->mrx_checksum_error_for_host)) { - local->stats.rx_crc_errors += ntohs(p->mrx_checksum_error); - p->mrx_checksum_error = 0; - p->mrx_checksum_error_for_host = 0; + local->stats.rx_crc_errors += ntohs(readb(&p->mrx_checksum_error)); + writeb(0,&p->mrx_checksum_error); + writeb(0,&p->mrx_checksum_error_for_host); } - if (p->rx_hec_error_for_host) + if (readb(&p->rx_hec_error_for_host)) { - local->stats.rx_frame_errors += ntohs(p->rx_hec_error); - p->rx_hec_error = 0; - p->rx_hec_error_for_host = 0; + local->stats.rx_frame_errors += ntohs(readb(&p->rx_hec_error)); + writeb(0,&p->rx_hec_error); + writeb(0,&p->rx_hec_error_for_host); } return &local->stats; } @@ -1414,11 +1434,11 @@ void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len) struct ccs *pccs; if (!(link->state & DEV_PRESENT)) { - DEBUG(0,"ray_update_parm - device not present\n"); + DEBUG(2,"ray_update_parm - device not present\n"); return; } - if ((ccsindex = get_free_ccs(local)) == -1) + if ((ccsindex = get_free_ccs(local)) < 0) { DEBUG(0,"ray_update_parm - No free ccs\n"); return; @@ -1449,12 +1469,12 @@ static void ray_update_multi_list(struct net_device *dev, int all) UCHAR *p = local->sram + HOST_TO_ECF_BASE; if (!(link->state & DEV_PRESENT)) { - DEBUG(1,"ray_update_multi_list - device not present\n"); + DEBUG(2,"ray_update_multi_list - device not present\n"); return; } else - DEBUG(1,"ray_update_multi_list(%p)\n",dev); - if ((ccsindex = get_free_ccs(local)) == -1) + DEBUG(2,"ray_update_multi_list(%p)\n",dev); + if ((ccsindex = get_free_ccs(local)) < 0) { DEBUG(1,"ray_update_multi - No free ccs\n"); return; @@ -1491,7 +1511,7 @@ static void set_multicast_list(struct net_device *dev) ray_dev_t *local = (ray_dev_t *)dev->priv; UCHAR promisc; - DEBUG(1,"ray_cs set_multicast_list(%p)\n",dev); + DEBUG(2,"ray_cs set_multicast_list(%p)\n",dev); if (dev->flags & IFF_PROMISC) { @@ -1534,30 +1554,23 @@ void ray_interrupt(int irq, void *dev_id, struct pt_regs * regs) UCHAR cmd; UCHAR status; - if (dev == NULL) { - link = dev_list; - dev = (struct net_device *)link->priv; - DEBUG(4,"ray_cs interrupt dev = %p, link = %p\n",dev,link); - if (dev->irq != irq) - { - DEBUG(0,"ray_cs interrupt irq %d for unknown device.\n", irq); - return; - } - } + if ((dev == NULL) || !dev->start) + return; + DEBUG(4,"ray_cs: interrupt for *dev=%p\n",dev); - if (dev->interrupt) { + if (test_and_set_bit(0,&dev->interrupt)) { printk("ray_cs Reentering interrupt handler not allowed\n"); return; } - dev->interrupt = 1; + local = (ray_dev_t *)dev->priv; link = (dev_link_t *)local->finder; if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) { - DEBUG(1,"ray_cs interrupt from device not present or suspended.\n"); + DEBUG(2,"ray_cs interrupt from device not present or suspended.\n"); return; } - rcsindex = ((struct scb *)(local->sram))->rcs_index; + rcsindex = readb(&((struct scb *)(local->sram))->rcs_index); if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { @@ -1602,7 +1615,7 @@ void ray_interrupt(int irq, void *dev_id, struct pt_regs * regs) case CCS_JOIN_NETWORK: if (status == CCS_COMMAND_COMPLETE) { if (readb(&pccs->var.start_network.net_initiated) == 1) { - DEBUG(0,"ray_cs interrupt network \"%s\"started\n",\ + DEBUG(0,"ray_cs interrupt network \"%s\" started\n",\ local->sparm.b4.a_current_ess_id); } else { @@ -1741,16 +1754,16 @@ void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs *prcs) switch(readb(pmsg)) { case DATA_TYPE: - DEBUG(4,"ray_rx data type\n"); + DEBUG(4,"ray_rx data type\n"); rx_data(dev, prcs, pkt_addr, rx_len); break; case AUTHENTIC_TYPE: - DEBUG(4,"ray_rx authentic type\n"); + DEBUG(4,"ray_rx authentic type\n"); if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); else rx_authenticate(local, prcs, pkt_addr, rx_len); break; case DEAUTHENTIC_TYPE: - DEBUG(4,"ray_rx deauth type\n"); + DEBUG(4,"ray_rx deauth type\n"); if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); else rx_deauthenticate(local, prcs, pkt_addr, rx_len); break; @@ -1758,7 +1771,7 @@ void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs *prcs) DEBUG(3,"ray_cs rx NULL msg\n"); break; case BEACON_TYPE: - DEBUG(4,"ray_rx beacon type\n"); + DEBUG(4,"ray_rx beacon type\n"); if (sniffer) rx_data(dev, prcs, pkt_addr, rx_len); copy_from_rx_buff(local, (UCHAR *)&local->last_bcn, pkt_addr, @@ -1909,19 +1922,24 @@ void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) struct ethhdr *peth; UCHAR srcaddr[ADDRLEN]; UCHAR destaddr[ADDRLEN]; - int i; - if (local->sparm.b5.a_acting_as_ap_status != TYPE_STA) + if (local->sparm.b5.a_acting_as_ap_status != TYPE_STA) memcpy(destaddr, pmac->addr_3, ADDRLEN); else memcpy(destaddr, pmac->addr_1, ADDRLEN); memcpy(srcaddr, pmac->addr_2, ADDRLEN); - DEBUG(3,"skb->data before untranslate"); +#ifdef PCMCIA_DEBUG + if (pc_debug > 3) { + int i; + printk(KERN_DEBUG "skb->data before untranslate"); for (i=0;i<64;i++) - DEBUG(3,"%02x ",skb->data[i]); - DEBUG(3,"\ntype = %08x, xsap = %08x, org = %08x\n",type,xsap,org); - DEBUG(3,"untranslate skb->data = %p\n",skb->data); + printk("%02x ",skb->data[i]); + printk("\n" KERN_DEBUG "type = %08x, xsap = %08x, org = %08x\n", + type,xsap,org); + printk(KERN_DEBUG "untranslate skb->data = %p\n",skb->data); + } +#endif if ( xsap != SNAP_ID) { /* not a snap type so leave it alone */ @@ -1971,10 +1989,15 @@ void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) DEBUG(3,"untranslate after skb_pull(%d), skb->data = %p\n",delta,skb->data); memcpy(peth->h_dest, destaddr, ADDRLEN); memcpy(peth->h_source, srcaddr, ADDRLEN); - DEBUG(3,"skb->data after untranslate:"); - for (i=0;i<64;i++) - DEBUG(3,"%02x ",skb->data[i]); - DEBUG(3,"\n"); +#ifdef PCMCIA_DEBUG + if (pc_debug > 3) { + int i; + printk(KERN_DEBUG "skb->data after untranslate:"); + for (i=0;i<64;i++) + printk("%02x ",skb->data[i]); + printk("\n"); + } +#endif } /* end untranslate */ /*===========================================================================*/ /* Copy data from circular receive buffer to PC memory. @@ -2020,7 +2043,7 @@ void authenticate(ray_dev_t *local) dev_link_t *link = local->finder; DEBUG(0,"ray_cs Starting authentication.\n"); if (!(link->state & DEV_PRESENT)) { - DEBUG(1,"ray_cs authenticate - device not present\n"); + DEBUG(2,"ray_cs authenticate - device not present\n"); return; } @@ -2046,37 +2069,37 @@ void rx_authenticate(ray_dev_t *local, struct rcs *prcs, del_timer(&local->timer); copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); - /* if we are trying to get authenticated */ + /* if we are trying to get authenticated */ if (local->sparm.b4.a_network_type == ADHOC) { - DEBUG(1,"ray_cs rx_auth var= %02x %02x %02x %02x %02x %02x\n", msg->var[0],msg->var[1],msg->var[2],msg->var[3],msg->var[4],msg->var[5]); - if (msg->var[2] == 1) { + DEBUG(1,"ray_cs rx_auth var= %02x %02x %02x %02x %02x %02x\n", msg->var[0],msg->var[1],msg->var[2],msg->var[3],msg->var[4],msg->var[5]); + if (msg->var[2] == 1) { DEBUG(0,"ray_cs Sending authentication response.\n"); if (!build_auth_frame (local, msg->mac.addr_2, OPEN_AUTH_RESPONSE)) { local->authentication_state = NEED_TO_AUTH; memcpy(local->auth_id, msg->mac.addr_2, ADDRLEN); } - } - } - else /* Infrastructure network */ - { - if (local->authentication_state == AWAITING_RESPONSE) { - /* Verify authentication sequence #2 and success */ - if (msg->var[2] == 2) { - if ((msg->var[3] | msg->var[4]) == 0) { - DEBUG(1,"Authentication successful\n"); - local->card_status = CARD_AUTH_COMPLETE; - associate(local); - local->authentication_state = AUTHENTICATED; - } - else { - DEBUG(0,"Authentication refused\n"); - local->card_status = CARD_AUTH_REFUSED; - join_net((u_long)local); - local->authentication_state = UNAUTHENTICATED; - } - } - } - } + } + } + else /* Infrastructure network */ + { + if (local->authentication_state == AWAITING_RESPONSE) { + /* Verify authentication sequence #2 and success */ + if (msg->var[2] == 2) { + if ((msg->var[3] | msg->var[4]) == 0) { + DEBUG(1,"Authentication successful\n"); + local->card_status = CARD_AUTH_COMPLETE; + associate(local); + local->authentication_state = AUTHENTICATED; + } + else { + DEBUG(0,"Authentication refused\n"); + local->card_status = CARD_AUTH_REFUSED; + join_net((u_long)local); + local->authentication_state = UNAUTHENTICATED; + } + } + } + } } /* end rx_authenticate */ /*===========================================================================*/ @@ -2087,11 +2110,11 @@ void associate(ray_dev_t *local) struct net_device *dev = link->priv; int ccsindex; if (!(link->state & DEV_PRESENT)) { - DEBUG(1,"ray_cs associate - device not present\n"); + DEBUG(2,"ray_cs associate - device not present\n"); return; } /* If no tx buffers available, return*/ - if ((ccsindex = get_free_ccs(local)) == -1) + if ((ccsindex = get_free_ccs(local)) < 0) { /* TBD should never be here but... what if we are? */ DEBUG(1,"ray_cs associate - No free ccs\n"); @@ -2125,7 +2148,7 @@ void rx_deauthenticate(ray_dev_t *local, struct rcs *prcs, struct rx_msg *msg = (struct rx_msg *)buff; */ DEBUG(0,"Deauthentication frame received\n"); - local->authentication_state = UNAUTHENTICATED; + local->authentication_state = UNAUTHENTICATED; /* Need to reauthenticate or rejoin depending on reason code */ /* copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); */ @@ -2256,9 +2279,8 @@ int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type) int ccsindex; /* If no tx buffers available, return */ - if ((ccsindex = get_free_tx_ccs(local)) == -1) + if ((ccsindex = get_free_tx_ccs(local)) < 0) { -/* TBD should never be here but... what if we are? */ DEBUG(1,"ray_cs send authenticate - No free tx ccs\n"); return -1; } diff --git a/drivers/net/pcmcia/ray_cs.h b/drivers/net/pcmcia/ray_cs.h index 28502954e..ae28bc71a 100644 --- a/drivers/net/pcmcia/ray_cs.h +++ b/drivers/net/pcmcia/ray_cs.h @@ -17,9 +17,14 @@ struct beacon_rx { + sizeof(struct tim_element)]; }; +/* Return values for get_free{,_tx}_ccs */ +#define ECCSFULL (-1) +#define ECCSBUSY (-2) +#define ECARDGONE (-3) + typedef struct ray_dev_t { int card_status; - int authentication_state; + int authentication_state; dev_node_t node; window_handle_t amem_handle; /* handle to window for attribute memory */ window_handle_t rmem_handle; /* handle to window for rx buffer on card */ @@ -28,6 +33,8 @@ typedef struct ray_dev_t { UCHAR *rmem; /* pointer to receive buffer window */ dev_link_t *finder; /* pointer back to dev_link_t for card */ struct timer_list timer; + int tx_ccs_lock; + int ccs_lock; int dl_param_ccs; union { struct b4_startup_params b4; diff --git a/drivers/net/pcmcia/rayctl.h b/drivers/net/pcmcia/rayctl.h index a301b0bd2..49d9b267b 100644 --- a/drivers/net/pcmcia/rayctl.h +++ b/drivers/net/pcmcia/rayctl.h @@ -142,8 +142,6 @@ struct adhoc_beacon }; /*****************************************************************************/ /*****************************************************************************/ - - /* #define C_MAC_HDR_2_WEP 0x40 */ /* TX/RX CCS constants */ #define TX_HEADER_LENGTH 0x1C @@ -151,6 +149,9 @@ struct adhoc_beacon #define TX_AUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 6) #define TX_AUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) #define TX_AUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) +#define TX_DEAUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 2) +#define TX_DEAUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) +#define TX_DEAUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) #define FCS_LEN 4 #define ADHOC 0 @@ -324,17 +325,23 @@ struct adhoc_beacon #define CARD_ASSOC_COMPLETE (6) #define CARD_ASSOC_FAILED (16) -/*** Values for authentication_state */ +/*** Values for authentication_state ***********************************/ #define UNAUTHENTICATED (0) #define AWAITING_RESPONSE (1) #define AUTHENTICATED (2) #define NEED_TO_AUTH (3) -/*** Values for authentication type */ +/*** Values for authentication type ************************************/ #define OPEN_AUTH_REQUEST (1) #define OPEN_AUTH_RESPONSE (2) - - +#define BROADCAST_DEAUTH (0xc0) +/*** Values for timer functions ****************************************/ +#define TODO_NOTHING (0) +#define TODO_VERIFY_DL_START (-1) +#define TODO_START_NET (-2) +#define TODO_JOIN_NET (-3) +#define TODO_AUTHENTICATE_TIMEOUT (-4) +#define TODO_SEND_CCS (-5) /***********************************************************************/ /* Parameter passing structure for update/report parameter CCS's */ struct object_id { diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index 234242746..a13789668 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -1,4 +1,4 @@ -/* $Id: sgiseeq.c,v 1.11 1999/10/09 00:01:24 ralf Exp $ +/* $Id: sgiseeq.c,v 1.12 1999/10/21 00:23:05 ralf Exp $ * * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. * @@ -71,12 +71,12 @@ static char *sgiseeqstr = "SGI Seeq8003"; struct sgiseeq_rx_desc { struct hpc_dma_desc rdma; - unsigned long buf_vaddr; + signed int buf_vaddr; }; struct sgiseeq_tx_desc { struct hpc_dma_desc tdma; - unsigned long buf_vaddr; + signed int buf_vaddr; }; /* Warning: This structure is layed out in a certain way because @@ -87,7 +87,7 @@ struct sgiseeq_init_block { /* Note the name ;-) */ /* Ptrs to the descriptors in KSEG1 uncached space. */ struct sgiseeq_rx_desc *rx_desc; struct sgiseeq_tx_desc *tx_desc; - unsigned long _padding[30]; /* Pad out to largest cache line size. */ + unsigned int _padding[30]; /* Pad out to largest cache line size. */ struct sgiseeq_rx_desc rxvector[SEEQ_RX_BUFFERS]; struct sgiseeq_tx_desc txvector[SEEQ_TX_BUFFERS]; @@ -212,28 +212,28 @@ void sgiseeq_dump_rings(void) once++; printk("RING DUMP:\n"); for(i = 0; i < SEEQ_RX_BUFFERS; i++) { - printk("RX [%d]: @(%p) [%08lx,%08lx,%08lx] ", + printk("RX [%d]: @(%p) [%08x,%08x,%08x] ", i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo, r[i].rdma.pnext); i += 1; - printk("-- [%d]: @(%p) [%08lx,%08lx,%08lx]\n", + printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n", i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo, r[i].rdma.pnext); } for(i = 0; i < SEEQ_TX_BUFFERS; i++) { - printk("TX [%d]: @(%p) [%08lx,%08lx,%08lx] ", + printk("TX [%d]: @(%p) [%08x,%08x,%08x] ", i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo, t[i].tdma.pnext); i += 1; - printk("-- [%d]: @(%p) [%08lx,%08lx,%08lx]\n", + printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n", i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo, t[i].tdma.pnext); } printk("INFO: [rx_new = %d rx_old=%d] [tx_new = %d tx_old = %d]\n", gpriv->rx_new, gpriv->rx_old, gpriv->tx_new, gpriv->tx_old); - printk("RREGS: rx_cbptr[%08lx] rx_ndptr[%08lx] rx_ctrl[%08lx]\n", + printk("RREGS: rx_cbptr[%08x] rx_ndptr[%08x] rx_ctrl[%08x]\n", hregs->rx_cbptr, hregs->rx_ndptr, hregs->rx_ctrl); - printk("TREGS: tx_cbptr[%08lx] tx_ndptr[%08lx] tx_ctrl[%08lx]\n", + printk("TREGS: tx_cbptr[%08x] tx_ndptr[%08x] tx_ctrl[%08x]\n", hregs->tx_cbptr, hregs->tx_ndptr, hregs->tx_ctrl); } #endif @@ -309,7 +309,7 @@ static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp /* Service every received packet. */ for_each_rx(rd, sp) { len = (PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3); - pkt_pointer = (unsigned char *)rd->buf_vaddr; + pkt_pointer = (unsigned char *)(long)rd->buf_vaddr; pkt_status = pkt_pointer[len + 2]; if(pkt_status & SEEQ_RSTAT_FIG) { @@ -364,8 +364,7 @@ static inline void kick_tx(struct sgiseeq_tx_desc *td, */ while((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) == (HPCDMA_XIU | HPCDMA_ETXD)) - td = (struct sgiseeq_tx_desc *) - KSEG1ADDR(td->tdma.pnext); + td = (struct sgiseeq_tx_desc *)(long) KSEG1ADDR(td->tdma.pnext); if(td->tdma.cntinfo & HPCDMA_XIU) { hregs->tx_ndptr = PHYSADDR(td); hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE; @@ -562,7 +561,7 @@ static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev) * entry and the HPC got to the end of the chain before we * added this new entry and restarted it. */ - memcpy((char *)td->buf_vaddr, skb->data, skblen); + memcpy((char *)(long)td->buf_vaddr, skb->data, skblen); td->tdma.cntinfo = ((len) & HPCDMA_BCNT) | (HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX); if(sp->tx_old != sp->tx_new) { @@ -733,7 +732,8 @@ int sgiseeq_probe(struct net_device *dev) /* First get the ethernet address of the onboard * interface from ARCS. - * (This is fragile; PROM doesn't like running from cache.) + * This is fragile; PROM doesn't like running from cache. + * On MIPS64 it crashes for some other, yet unknown reason. */ ep = romvec->get_evar("eaddr"); str2eaddr(onboard_eth_addr, ep); diff --git a/drivers/net/sgiseeq.h b/drivers/net/sgiseeq.h index 4f684f5a1..0b5d39c68 100644 --- a/drivers/net/sgiseeq.h +++ b/drivers/net/sgiseeq.h @@ -1,4 +1,4 @@ -/* $Id: sgiseeq.h,v 1.1 1997/06/09 08:34:32 ralf Exp $ +/* $Id: sgiseeq.h,v 1.4 1999/10/09 00:01:24 ralf Exp $ * sgiseeq.h: Defines for the Seeq8003 ethernet controller. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -7,27 +7,27 @@ #define _SGISEEQ_H struct sgiseeq_wregs { - volatile unsigned long multicase_high[2]; - volatile unsigned long frame_gap; - volatile unsigned long control; + volatile unsigned int multicase_high[2]; + volatile unsigned int frame_gap; + volatile unsigned int control; }; struct sgiseeq_rregs { - volatile unsigned long collision_tx[2]; - volatile unsigned long collision_all[2]; - volatile unsigned long _unused0; - volatile unsigned long rflags; + volatile unsigned int collision_tx[2]; + volatile unsigned int collision_all[2]; + volatile unsigned int _unused0; + volatile unsigned int rflags; }; struct sgiseeq_regs { union { - volatile unsigned long eth_addr[6]; - volatile unsigned long multicast_low[6]; + volatile unsigned int eth_addr[6]; + volatile unsigned int multicast_low[6]; struct sgiseeq_wregs wregs; struct sgiseeq_rregs rregs; } rw; - volatile unsigned long rstat; - volatile unsigned long tstat; + volatile unsigned int rstat; + volatile unsigned int tstat; }; /* Seeq8003 receive status register */ diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c index c11248958..cdee2d6e6 100644 --- a/drivers/net/sk_mca.c +++ b/drivers/net/sk_mca.c @@ -71,6 +71,7 @@ History: #include <linux/version.h> #include <linux/kernel.h> +#include <linux/version.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> diff --git a/drivers/net/tokenring/.cvsignore b/drivers/net/tokenring/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/net/tokenring/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in new file mode 100644 index 000000000..8b3065c8a --- /dev/null +++ b/drivers/net/tokenring/Config.in @@ -0,0 +1,15 @@ +# +# Token Ring driver configuration +# + +mainmenu_option next_comment +comment 'Token Ring driver support' + +bool 'Token Ring driver support' CONFIG_TR +if [ "$CONFIG_TR" = "y" ]; then + tristate ' IBM Tropic chipset based adapter support' CONFIG_IBMTR + tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL + tristate ' SysKonnect adapter support' CONFIG_SKTR +fi + +endmenu diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile new file mode 100644 index 000000000..038c58b86 --- /dev/null +++ b/drivers/net/tokenring/Makefile @@ -0,0 +1,51 @@ +# +# Makefile for drivers/net/tokenring +# +# 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 := tr.a +L_OBJS := +M_OBJS := + +ifeq ($(CONFIG_IBMTR),y) + L_OBJS += ibmtr.o +else + ifeq ($(CONFIG_IBMTR),m) + M_OBJS += ibmtr.o + endif +endif + +ifeq ($(CONFIG_IBMOL),y) + L_OBJS += olympic.o +else + ifeq ($(CONFIG_IBMOL),m) + M_OBJS += olympic.o + endif +endif + +ifeq ($(CONFIG_SKTR),y) + L_OBJS += sktr.o +else + ifeq ($(CONFIG_SKTR),m) + M_OBJS += sktr.o + endif +endif + +include $(TOPDIR)/Rules.make + diff --git a/drivers/net/ibmtr.c b/drivers/net/tokenring/ibmtr.c index cf2514fa6..85cdfb774 100644 --- a/drivers/net/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -79,6 +79,10 @@ * * Changes by Tim Hockin (thockin@isunix.it.ilstu.edu) : * + added spinlocks for SMP sanity (10 March 1999) + * + * Changes by Jochen Friedrich to enable RFC1469 Option 2 multicasting + * i.e. using functional address C0 00 00 04 00 00 to transmit and + * receive multicast packets. */ /* change the define of IBMTR_DEBUG_MESSAGES to a nonzero value @@ -211,6 +215,7 @@ static int tok_open(struct net_device *dev); static int tok_close(struct net_device *dev); static int tok_send_packet(struct sk_buff *skb, struct net_device *dev); static struct net_device_stats * tok_get_stats(struct net_device *dev); +static void tok_set_multicast_list(struct net_device *dev); void ibmtr_readlog(struct net_device *dev); void ibmtr_reset_timer(struct timer_list *tmr, struct net_device *dev); int ibmtr_change_mtu(struct net_device *dev, int mtu); @@ -778,7 +783,7 @@ static int __init trdev_init(struct net_device *dev) dev->stop = tok_close; dev->hard_start_xmit = tok_send_packet; dev->get_stats = tok_get_stats; - dev->set_multicast_list = NULL; + dev->set_multicast_list = tok_set_multicast_list; dev->change_mtu = ibmtr_change_mtu; #ifndef MODULE @@ -790,6 +795,43 @@ static int __init trdev_init(struct net_device *dev) } +static void tok_set_multicast_list(struct net_device *dev) +{ + struct tok_info *ti=(struct tok_info *)dev->priv; + struct dev_mc_list *mclist; + unsigned char address[4]; + + int i; + + address[0] = address[1] = address[2] = address[3] = 0; + + mclist = dev->mc_list; + for (i=0; i< dev->mc_count; i++) + { + address[0] |= mclist->dmi_addr[2]; + address[1] |= mclist->dmi_addr[3]; + address[2] |= mclist->dmi_addr[4]; + address[3] |= mclist->dmi_addr[5]; + mclist = mclist->next; + } + SET_PAGE(ti->srb); + for (i=0; i<sizeof(struct srb_set_funct_addr); i++) + writeb(0, ti->srb+i); + + writeb(DIR_SET_FUNC_ADDR, + ti->srb + offsetof(struct srb_set_funct_addr, command)); + + DPRINTK("Setting functional address: "); + + for (i=0; i<4; i++) + { + writeb(address[i], + ti->srb + offsetof(struct srb_set_funct_addr, funct_address)+i); + printk("%02X ", address[i]); + } + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + printk("\n"); +} static int tok_open(struct net_device *dev) { @@ -1621,12 +1663,10 @@ static void tr_rx(struct net_device *dev) ti->tr_stats.rx_packets++; skb->protocol = tr_type_trans(skb,dev); - - if (IPv4_p){ + if (IPv4_p){ skb->csum = chksum; skb->ip_summed = 1; } - netif_rx(skb); } diff --git a/drivers/net/ibmtr.h b/drivers/net/tokenring/ibmtr.h index f51174c4f..3f3fa6aed 100644 --- a/drivers/net/ibmtr.h +++ b/drivers/net/tokenring/ibmtr.h @@ -444,6 +444,6 @@ struct srb_set_funct_addr { unsigned char reserved1; unsigned char ret_code; unsigned char reserved2[3]; - __u32 funct_address; + unsigned char funct_address[4]; }; diff --git a/drivers/net/olympic.c b/drivers/net/tokenring/olympic.c index 460480c48..460480c48 100644 --- a/drivers/net/olympic.c +++ b/drivers/net/tokenring/olympic.c diff --git a/drivers/net/olympic.h b/drivers/net/tokenring/olympic.h index d5a06423a..d5a06423a 100644 --- a/drivers/net/olympic.h +++ b/drivers/net/tokenring/olympic.h diff --git a/drivers/net/sktr.c b/drivers/net/tokenring/sktr.c index f9b877f66..81b2df4bc 100644 --- a/drivers/net/sktr.c +++ b/drivers/net/tokenring/sktr.c @@ -24,7 +24,8 @@ * * Maintainer(s): * JS Jay Schulist jschlst@samba.anu.edu.au - * CG Christoph Goos cgoos@syskonnect.de + * CG Christoph Goos cgoos@syskonnect.de + * AF Adam Fritzler mid@auk.cx * * Modification History: * 29-Aug-97 CG Created @@ -33,10 +34,14 @@ * 27-May-98 JS Formated to Linux Kernel Format * 31-May-98 JS Hacked in PCI support * 16-Jun-98 JS Modulized for multiple cards with one driver + * 21-Sep-99 CG Fixed source routing issues for 2.2 kernels + * 21-Sep-99 AF Added multicast changes recommended by + * Jochen Friedrich <jochen@nwe.de> (untested) + * Added detection of compatible Compaq PCI card * * To do: * 1. Selectable 16 Mbps or 4Mbps - * 2. Multi/Broadcast packet handling + * 2. Multi/Broadcast packet handling (might be done) * */ @@ -135,7 +140,6 @@ static void sktr_enable_interrupts(struct net_device *dev); static void sktr_exec_cmd(struct net_device *dev, unsigned short Command); static void sktr_exec_sifcmd(struct net_device *dev, unsigned int WriteValue); /* "F" */ -static unsigned char *sktr_fix_srouting(unsigned char *buf, short *FrameLen); /* "G" */ static struct enet_statistics *sktr_get_stats(struct net_device *dev); /* "H" */ @@ -254,10 +258,17 @@ static int __init sktr_pci_chk_card(struct net_device *dev) /* Remove I/O space marker in bit 0. */ pci_ioaddr &= ~3; - if(vendor != PCI_VENDOR_ID_SK) + if((vendor != PCI_VENDOR_ID_SK) && + (vendor != PCI_VENDOR_ID_COMPAQ)) continue; - if(device != PCI_DEVICE_ID_SK_TR) + + if((vendor == PCI_VENDOR_ID_SK) && + (device != PCI_DEVICE_ID_SK_TR)) + continue; + else if((vendor == PCI_VENDOR_ID_COMPAQ) && + (device != PCI_DEVICE_ID_COMPAQ_TOKENRING)) continue; + if(check_region(pci_ioaddr, SKTR_IO_EXTENT)) continue; request_region(pci_ioaddr, SKTR_IO_EXTENT, pci_cardname); @@ -390,6 +401,7 @@ static int __init sktr_probe1(struct net_device *dev, int ioaddr) { static unsigned version_printed = 0; struct net_local *tp; + int DeviceType = SK_PCI; int err; if(sktr_debug && version_printed++ == 0) @@ -407,6 +419,7 @@ static int __init sktr_probe1(struct net_device *dev, int ioaddr) err = sktr_isa_chk_card(dev, ioaddr); if(err < 0) return (-ENODEV); + DeviceType = SK_ISA; } /* Setup this devices private information structure */ @@ -414,6 +427,7 @@ static int __init sktr_probe1(struct net_device *dev, int ioaddr) if(tp == NULL) return (-ENOMEM); memset(tp, 0, sizeof(struct net_local)); + tp->DeviceType = DeviceType; init_waitqueue_head(&tp->wait_for_tok_int); dev->priv = tp; @@ -691,7 +705,9 @@ static void sktr_init_net_local(struct net_device *dev) skb_put(tp->Rpl[i].Skb, tp->MaxPacketSize); /* data unreachable for DMA ? then use local buffer */ - if(virt_to_bus(tp->Rpl[i].Skb->data) + tp->MaxPacketSize > ISA_MAX_ADDRESS) + if(tp->DeviceType == SK_ISA && + virt_to_bus(tp->Rpl[i].Skb->data) + + tp->MaxPacketSize > ISA_MAX_ADDRESS) { tp->Rpl[i].SkbStat = SKB_DATA_COPY; tp->Rpl[i].FragList[0].DataAddr = htonl(virt_to_bus(tp->LocalRxBuffers[i])); @@ -749,7 +765,7 @@ static void sktr_init_opb(struct net_local *tp) tp->ocpl.OPENOptions = 0; tp->ocpl.OPENOptions |= ENABLE_FULL_DUPLEX_SELECTION; - tp->ocpl.OPENOptions |= PAD_ROUTING_FIELD; +/* tp->ocpl.OPENOptions |= PAD_ROUTING_FIELD; no more needed */ tp->ocpl.FullDuplex = 0; tp->ocpl.FullDuplex |= OPEN_FULL_DUPLEX_OFF; @@ -827,32 +843,6 @@ static void sktr_exec_cmd(struct net_device *dev, unsigned short Command) } /* - * Linux always gives 18 byte of source routing information in the frame header. - * But the length field can indicate shorter length. Then cut header - * appropriate. - */ -static unsigned char *sktr_fix_srouting(unsigned char *buf, short *FrameLen) -{ - struct trh_hdr *trh = (struct trh_hdr *)buf; - int len; - - if(buf[8] & TR_RII) - { - trh->rcf &= ~SWAPB((unsigned short) TR_RCF_LONGEST_FRAME_MASK); - trh->rcf |= SWAPB((unsigned short) TR_RCF_FRAME4K); - len = (SWAPB(trh->rcf) & TR_RCF_LEN_MASK) >> 8; - if(len < 18) - { - memcpy(&buf[18-len],buf,sizeof(struct trh_hdr)-18+len); - *FrameLen -= (18 - len); - } - return (&buf[18-len]); - } - - return (buf); -} - -/* * Gets skb from system, queues it and checks if it can be sent */ static int sktr_send_packet(struct sk_buff *skb, struct net_device *dev) @@ -934,7 +924,8 @@ static void sktr_hardware_send_packet(struct net_device *dev, struct net_local* tp->QueueSkb++; /* Is buffer reachable for Busmaster-DMA? */ - if(virt_to_bus((void*)(((long) skb->data) + skb->len)) + if(tp->DeviceType == SK_ISA && + virt_to_bus((void*)(((long) skb->data) + skb->len)) > ISA_MAX_ADDRESS) { /* Copy frame to local buffer */ @@ -942,13 +933,13 @@ static void sktr_hardware_send_packet(struct net_device *dev, struct net_local* length = skb->len; buf = tp->LocalTxBuffers[i]; memcpy(buf, skb->data, length); - newbuf = sktr_fix_srouting(buf, &length); + newbuf = buf; } else { /* Send direct from skb->data */ length = skb->len; - newbuf = sktr_fix_srouting(skb->data, &length); + newbuf = skb->data; } /* Source address in packet? */ @@ -1486,53 +1477,65 @@ static struct enet_statistics *sktr_get_stats(struct net_device *dev) static void sktr_set_multicast_list(struct net_device *dev) { struct net_local *tp = (struct net_local *)dev->priv; - unsigned int OpenOptions; - - OpenOptions = tp->ocpl.OPENOptions & - ~(PASS_ADAPTER_MAC_FRAMES - | PASS_ATTENTION_FRAMES - | PASS_BEACON_MAC_FRAMES - | COPY_ALL_MAC_FRAMES - | COPY_ALL_NON_MAC_FRAMES); - - if(dev->flags & IFF_PROMISC) - /* Enable promiscuous mode */ - OpenOptions |= COPY_ALL_NON_MAC_FRAMES | COPY_ALL_MAC_FRAMES; - else - { - if(dev->flags & IFF_ALLMULTI) - /* || dev->mc_count > HW_MAX_ADDRS) */ - { - /* Disable promiscuous mode, use normal mode. */ - } - else - { - if(dev->mc_count) - { - /* Walk the address list, and load the filter */ - } - } - } - - tp->ocpl.OPENOptions = OpenOptions; - sktr_exec_cmd(dev, OC_MODIFY_OPEN_PARMS); - - return; + unsigned int OpenOptions; + + OpenOptions = tp->ocpl.OPENOptions & + ~(PASS_ADAPTER_MAC_FRAMES + | PASS_ATTENTION_FRAMES + | PASS_BEACON_MAC_FRAMES + | COPY_ALL_MAC_FRAMES + | COPY_ALL_NON_MAC_FRAMES); + + tp->ocpl.FunctAddr = 0; + + if(dev->flags & IFF_PROMISC) + /* Enable promiscuous mode */ + OpenOptions |= COPY_ALL_NON_MAC_FRAMES | + COPY_ALL_MAC_FRAMES; + else + { + if(dev->flags & IFF_ALLMULTI) + { + /* Disable promiscuous mode, use normal mode. */ + tp->ocpl.FunctAddr = 0xFFFFFFFF; + + } + else + { + int i; + struct dev_mc_list *mclist = dev->mc_list; + for (i=0; i< dev->mc_count; i++) + { + ((char *)(&tp->ocpl.FunctAddr))[0] |= + mclist->dmi_addr[2]; + ((char *)(&tp->ocpl.FunctAddr))[1] |= + mclist->dmi_addr[3]; + ((char *)(&tp->ocpl.FunctAddr))[2] |= + mclist->dmi_addr[4]; + ((char *)(&tp->ocpl.FunctAddr))[3] |= + mclist->dmi_addr[5]; + mclist = mclist->next; + } + } + sktr_exec_cmd(dev, OC_SET_FUNCT_ADDR); + } + + tp->ocpl.OPENOptions = OpenOptions; + sktr_exec_cmd(dev, OC_MODIFY_OPEN_PARMS); + return; } /* * Wait for some time (microseconds) + * + * udelay() is a bit harsh, but using a looser timer causes + * the bring-up-diags to stall indefinitly. + * */ + static void sktr_wait(unsigned long time) { - long tmp; - - tmp = jiffies + time/(1000000/HZ); - do { - current->state = TASK_INTERRUPTIBLE; - tmp = schedule_timeout(tmp); - } while(time_after(tmp, jiffies)); - + udelay(time); return; } @@ -2451,8 +2454,6 @@ static void sktr_rcv_status_irq(struct net_device *dev) /* Drop frames sent by myself */ if(sktr_chk_frame(dev, rpl->MData)) { - printk(KERN_INFO "%s: Received my own frame\n", - dev->name); if(rpl->Skb != NULL) dev_kfree_skb(rpl->Skb); } @@ -2464,9 +2465,10 @@ static void sktr_rcv_status_irq(struct net_device *dev) printk("%s: Packet Length %04X (%d)\n", dev->name, Length, Length); - /* Indicate the received frame to system the - * adapter does the Source-Routing padding for - * us. See: OpenOptions in sktr_init_opb() + /* Indicate the received frame to system. + * The source routing padding is no more + * necessary with 2.2.x kernel. + * See: OpenOptions in sktr_init_opb() */ skb = rpl->Skb; if(rpl->SkbStat == SKB_UNAVAILABLE) @@ -2497,6 +2499,7 @@ static void sktr_rcv_status_irq(struct net_device *dev) /* Deliver frame to system */ rpl->Skb = NULL; skb_trim(skb,Length); + skb->dev = dev; skb->protocol = tr_type_trans(skb,dev); netif_rx(skb); } @@ -2529,7 +2532,8 @@ static void sktr_rcv_status_irq(struct net_device *dev) skb_put(rpl->Skb, tp->MaxPacketSize); /* Data unreachable for DMA ? then use local buffer */ - if(virt_to_bus(rpl->Skb->data) + tp->MaxPacketSize + if(tp->DeviceType == SK_ISA && + virt_to_bus(rpl->Skb->data) + tp->MaxPacketSize > ISA_MAX_ADDRESS) { rpl->SkbStat = SKB_DATA_COPY; diff --git a/drivers/net/sktr.h b/drivers/net/tokenring/sktr.h index 4c2a3bf36..90b5c382f 100644 --- a/drivers/net/sktr.h +++ b/drivers/net/tokenring/sktr.h @@ -16,6 +16,9 @@ #define TR_RCF_LONGEST_FRAME_MASK 0x0070 #define TR_RCF_FRAME4K 0x0030 +#define SK_ISA 0 +#define SK_PCI 1 + /*------------------------------------------------------------------*/ /* Bit order for adapter communication with DMA */ /* -------------------------------------------------------------- */ @@ -642,7 +645,7 @@ typedef struct { * but possibly multiple TPLs for one frame) the length of the TPLs has to be * initialized in the OPL. (OPEN parameter list) */ -#define TPL_NUM 3 /* Number of Transmit Parameter Lists. +#define TPL_NUM 9 /* Number of Transmit Parameter Lists. * !! MUST BE >= 3 !! */ @@ -1063,6 +1066,8 @@ typedef struct net_local { unsigned char ScbInUse; unsigned short CMDqueue; + unsigned int DeviceType; + unsigned long AdapterOpenFlag:1; unsigned long AdapterVirtOpenFlag:1; unsigned long OpenCommandIssued:1; diff --git a/drivers/net/sktr_firmware.h b/drivers/net/tokenring/sktr_firmware.h index 25dd973d4..25dd973d4 100644 --- a/drivers/net/sktr_firmware.h +++ b/drivers/net/tokenring/sktr_firmware.h diff --git a/drivers/net/wan/.cvsignore b/drivers/net/wan/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/net/wan/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in new file mode 100644 index 000000000..503854a0f --- /dev/null +++ b/drivers/net/wan/Config.in @@ -0,0 +1,64 @@ +# +# wan devices configuration +# + +mainmenu_option next_comment +comment 'Wan interfaces' + +bool 'Wan interfaces support' CONFIG_WAN +if [ "$CONFIG_WAN" = "y" ]; then + + # There is no way to detect a comtrol sv11 - force it modular for now. + + dep_tristate 'Comtrol Hostess SV-11 support' CONFIG_HOSTESS_SV11 m + + # The COSA/SRP driver has not been tested as non-modular yet. + + dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m + + # There is no way to detect a Sealevel board. Force it modular + + dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m + + tristate 'Frame relay DLCI support' CONFIG_DLCI + if [ "$CONFIG_DLCI" != "n" ]; then + int 'Max open DLCI' CONFIG_DLCI_COUNT 24 + int 'Max DLCI per device' CONFIG_DLCI_MAX 8 + dep_tristate ' SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI + fi + + # Wan router core. + + if [ "$CONFIG_WAN_ROUTER" != "n" ]; then + bool 'WAN router drivers' CONFIG_WAN_ROUTER_DRIVERS + if [ "$CONFIG_WAN_ROUTER_DRIVERS" = "y" ]; then + dep_tristate ' Sangoma WANPIPE(tm) multiprotocol cards' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_ROUTER_DRIVERS + if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then + int 'Maximum number of cards' CONFIG_WANPIPE_CARDS 1 + bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 + bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR + bool ' WANPIPE PPP support' CONFIG_WANPIPE_PPP + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Cyclom 2X(tm) cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_ROUTER_DRIVERS + if [ "$CONFIG_CYCLADES_SYNC" != "n" ]; then + bool ' Cyclom 2X X.25 support' CONFIG_CYCLOMX_X25 + fi + fi + fi + fi + + # X.25 network drivers + + if [ "$CONFIG_X25" != "n" ]; then + if [ "$CONFIG_LAPB" != "n" ]; then + dep_tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER $CONFIG_LAPB + dep_tristate 'X.25 async driver' CONFIG_X25_ASY $CONFIG_LAPB + fi + fi + + tristate 'SBNI12-xx support' CONFIG_SBNI +fi + +endmenu + diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile new file mode 100644 index 000000000..73060f5b9 --- /dev/null +++ b/drivers/net/wan/Makefile @@ -0,0 +1,181 @@ +# File: drivers/net/wan/Makefile +# +# Makefile for the Linux network (wan) 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. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +L_TARGET := wan.a +L_OBJS := +M_OBJS := + +# Need these to keep track of whether the 82530 or SYNCPPP +# modules should really go in the kernel or a module. +CONFIG_85230_BUILTIN := +CONFIG_85230_MODULE := +CONFIG_SYNCPPP_BUILTIN := +CONFIG_SYNCPPP_MODULE := + +ifeq ($(CONFIG_HOSTESS_SV11),y) +L_OBJS += hostess_sv11.o +CONFIG_85230_BUILTIN = y +CONFIG_SYNCPPP_BUILTIN = y +else + ifeq ($(CONFIG_HOSTESS_SV11),m) + CONFIG_85230_MODULE = y + CONFIG_SYNCPPP_MODULE = y + M_OBJS += hostess_sv11.o + endif +endif + +ifeq ($(CONFIG_SEALEVEL_4021),y) +L_OBJS += sealevel.o +CONFIG_85230_BUILTIN = y +CONFIG_SYNCPPP_BUILTIN = y +else + ifeq ($(CONFIG_SEALEVEL_4021),m) + CONFIG_85230_MODULE = y + CONFIG_SYNCPPP_MODULE = y + M_OBJS += sealevel.o + endif +endif + +ifeq ($(CONFIG_COSA),y) +L_OBJS += cosa.o +CONFIG_SYNCPPP_BUILTIN = y +else + ifeq ($(CONFIG_COSA),m) + CONFIG_SYNCPPP_MODULE = y + M_OBJS += cosa.o + endif +endif + +# If anything built-in uses syncppp, then build it into the kernel also. +# If not, but a module uses it, build as a module. + +ifdef CONFIG_SYNCPPP_BUILTIN +LX_OBJS += syncppp.o +else + ifdef CONFIG_SYNCPPP_MODULE + MX_OBJS += syncppp.o + endif +endif + +# If anything built-in uses Z85230, then build it into the kernel also. +# If not, but a module uses it, build as a module. + +ifdef CONFIG_85230_BUILTIN +LX_OBJS += z85230.o +else + ifdef CONFIG_85230_MODULE + MX_OBJS += z85230.o + endif +endif + +ifeq ($(CONFIG_DLCI),y) +L_OBJS += dlci.o +else + ifeq ($(CONFIG_DLCI),m) + M_OBJS += dlci.o + endif +endif + +ifeq ($(CONFIG_SDLA),y) + L_OBJS += sdla.o +else + ifeq ($(CONFIG_SDLA),m) + M_OBJS += sdla.o +endif + +ifeq ($(CONFIG_VENDOR_SANGOMA),y) + LX_OBJS += sdladrv.o + L_OBJS += sdlamain.o + ifeq ($(CONFIG_WANPIPE_X25),y) + L_OBJS += sdla_x25.o + endif + ifeq ($(CONFIG_WANPIPE_FR),y) + L_OBJS += sdla_fr.o + endif + ifeq ($(CONFIG_WANPIPE_PPP),y) + L_OBJS += sdla_ppp.o + endif +endif + +endif + +ifeq ($(CONFIG_VENDOR_SANGOMA),m) + MX_OBJS += sdladrv.o + M_OBJS += wanpipe.o + WANPIPE_OBJS = sdlamain.o + ifeq ($(CONFIG_WANPIPE_X25),y) + WANPIPE_OBJS += sdla_x25.o + endif + ifeq ($(CONFIG_WANPIPE_FR),y) + WANPIPE_OBJS += sdla_fr.o + endif + ifeq ($(CONFIG_WANPIPE_PPP),y) + WANPIPE_OBJS += sdla_ppp.o + endif +endif + +ifeq ($(CONFIG_CYCLADES_SYNC),y) + LX_OBJS += cycx_drv.o + L_OBJS += cycx_main.o + ifeq ($(CONFIG_CYCLOMX_X25),y) + L_OBJS += cycx_x25.o + endif +endif + +ifeq ($(CONFIG_CYCLADES_SYNC),m) + MX_OBJS += cycx_drv.o + M_OBJS += cyclomx.o + CYCLOMX_OBJS = cycx_main.o + ifeq ($(CONFIG_CYCLOMX_X25),y) + CYCLOMX_OBJS += cycx_x25.o + endif +endif + +ifeq ($(CONFIG_X25_ASY),y) +L_OBJS += x25_asy.o +else + ifeq ($(CONFIG_X25_ASY),m) + M_OBJS += x25_asy.o + endif +endif + +ifeq ($(CONFIG_LAPBETHER),y) +L_OBJS += lapbether.o +else + ifeq ($(CONFIG_LAPBETHER),m) + M_OBJS += lapbether.o + endif +endif + +ifeq ($(CONFIG_SBNI),y) +L_OBJS += sbni.o +else + ifeq ($(CONFIG_SBNI),m) + M_OBJS += sbni.o + endif +endif + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s + +wanpipe.o: $(WANPIPE_OBJS) + ld -r -o $@ $(WANPIPE_OBJS) + +cyclomx.o: $(CYCLOMX_OBJS) + ld -r -o $@ $(CYCLOMX_OBJS) + diff --git a/drivers/net/cosa.c b/drivers/net/wan/cosa.c index 863cd4b59..863cd4b59 100644 --- a/drivers/net/cosa.c +++ b/drivers/net/wan/cosa.c diff --git a/drivers/net/cosa.h b/drivers/net/wan/cosa.h index 7b5a39018..7b5a39018 100644 --- a/drivers/net/cosa.h +++ b/drivers/net/wan/cosa.h diff --git a/drivers/net/cycx_drv.c b/drivers/net/wan/cycx_drv.c index 1629bdcf0..1629bdcf0 100644 --- a/drivers/net/cycx_drv.c +++ b/drivers/net/wan/cycx_drv.c diff --git a/drivers/net/cycx_main.c b/drivers/net/wan/cycx_main.c index 52a2abd04..52a2abd04 100644 --- a/drivers/net/cycx_main.c +++ b/drivers/net/wan/cycx_main.c diff --git a/drivers/net/cycx_x25.c b/drivers/net/wan/cycx_x25.c index 7e79943c0..7e79943c0 100644 --- a/drivers/net/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c diff --git a/drivers/net/dlci.c b/drivers/net/wan/dlci.c index a8c52f0d6..a8c52f0d6 100644 --- a/drivers/net/dlci.c +++ b/drivers/net/wan/dlci.c diff --git a/drivers/net/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index 7b24901ce..ba4e49c83 100644 --- a/drivers/net/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -320,7 +320,7 @@ static struct sv11_device *sv11_init(int iobase, int irq) for(i=0;i<999;i++) { sprintf(sv->name,"hdlc%d", i); - if(dev_get(sv->name)==NULL) + if(dev_get(sv->name)==0) { struct net_device *d=dev->chanA.netdevice; diff --git a/drivers/net/lapbether.c b/drivers/net/wan/lapbether.c index a4564afcb..a4564afcb 100644 --- a/drivers/net/lapbether.c +++ b/drivers/net/wan/lapbether.c diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c new file mode 100644 index 000000000..9d5fba082 --- /dev/null +++ b/drivers/net/wan/sbni.c @@ -0,0 +1,1533 @@ +/* + * Driver for Granch SBNI-12 leased line network adapters. + * + * Copyright 1997 - 1999, Granch ltd. + * Written 1999 by Yaroslav Polyakov (xenon@granch.ru). + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * // Whole developers team: + * // Yaroslav Polyakov (xenon@granch.ru) + * // - main developer of this version + * // Alexey Zverev (zverev@granch.ru) + * // - previous SBNI driver for linux + * // Alexey Chirkov (chirkov@granch.ru) + * // - all the hardware work and consulting + * // Max Khon (max@iclub.nsu.ru) + * // - first SBNI driver for linux + * // -------------------------------------------- + * // also I thank: + * // Max Krasnyansky (max@uznet.net) + * // - for bug hunting and many ideas + * // Alan Cox (Alan.Cox@linux.org) + * // - for consulting in some hardcore questions + * // Donald Becker (becker@cesdis.gsfc.nasa.gov) + * // - for pretty nice skeleton + * + * More info and useful utilities to work w/ SBNI you can find at + * http://www.granch.ru. + * + * 3.0.0 = Initial Revision, Yaroslav Polyakov (24 Feb 1999) + * - added pre-calculation for CRC, fixed bug with "len-2" frames, + * - removed outbound fragmentation (MTU=1000), written CRC-calculation + * - on asm, added work with hard_headers and now we have our own cache + * - for them, optionally supported word-interchange on some chipsets, + * - something else I cant remember ;) + * + * 3.0.1 = just fixed some bugs (14 apr 1999). + * - fixed statistical tx bug + * - fixed wrong creation dates (1998 -> 1999) in driver source code ;) + * - fixed source address bug. + * - fixed permanent nirvana bug + * + * 3.1.0 = (Katyusha) (26 apr 1999) + * - Added balancing feature + * + * 3.1.1 = (Medea) (5 aug 1999) + * - Fixed mac.raw bug + * - Thanks to tolix@olviko.ru and + * - to Barnaul Brewery, producers of my favorite beer "Medea". + * + * + */ + + +#undef GOODBUS16 +#define CRCASM +#define KATYUSHA + +#include <linux/version.h> + +#if LINUX_VERSION_CODE >=0x020200 +#define v22 +#endif + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/fcntl.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/errno.h> + +#include <asm/io.h> +#include <asm/types.h> +#include <asm/byteorder.h> + + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/timer.h> +#include <linux/config.h> /* for CONFIG_INET. do we need this?*/ + +#include <net/arp.h> + + + +#ifdef v22 +#include <asm/uaccess.h> +#include <linux/init.h> +#endif + +#include "sbni.h" + + +static const char *version = +"sbni.c: ver. 3.1.1 Medea 5 Aug 1999 Yaroslav Polyakov (xenon@granch.ru)\n"; + +int sbni_probe(struct net_device *dev); +static int sbni_probe1(struct net_device *dev, int ioaddr); +static int sbni_open(struct net_device *dev); +static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev); +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 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); +static inline void sbni_get_packet(struct net_device* dev); +static void sbni_watchdog(unsigned long arg); +static void set_multicast_list(struct net_device *dev); +static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int sbni_set_mac_address(struct net_device *dev, void *addr); +unsigned long calc_crc(char *mem, int len, unsigned initial); +void sbni_nirvana(struct net_device *dev); +static int sbni_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len); + +static int sbni_rebuild_header(struct sk_buff *skb); +static int sbni_header_cache(struct neighbour *neigh, struct hh_cache *hh); + +static inline void sbni_outs(int port, void *data, int len); +static inline void sbni_ins(int port, void *data, int len); + + + +#define SIZE_OF_TIMEOUT_RXL_TAB 4 +static u_char timeout_rxl_tab[] = { + 0x03, 0x05, 0x08, 0x0b +}; + +static u_char rxl_tab[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f +}; + +/* A zero-terminated list of I/O addresses to be probed */ +static unsigned int netcard_portlist[] = { + 0x210, 0x2c0, 0x2d0, 0x2f0, 0x220, 0x230, 0x240, 0x250, + 0x260, 0x290, 0x2a0, 0x2b0, 0x224, 0x234, 0x244, 0x254, + 0x264, 0x294, 0x2a4, 0x2b4, 0}; + +static unsigned char magic_reply[] = { + 0x5a,0x06,0x30,0x00,0x00,0x50,0x65,0x44,0x20 +}; + +static int def_baud = DEF_RATE; +static int def_rxl = DEF_RXL_DELTA; +static long def_mac = 0; + + +/* + * CRC-32 stuff + */ + +#define CRC32(c,crc) (crc32tab[((size_t)(crc) ^ (c)) & 0xff] ^ (((crc) >> 8) & 0x00FFFFFF)) +/* CRC generator 0xEDB88320 */ +/* CRC remainder 0x2144DF1C */ +/* CRC initial value 0x00000000 */ +#define CRC32_REMAINDER 0x2144DF1C +#define CRC32_INITIAL 0x00000000 + +static unsigned long crc32tab[] = { + 0xD202EF8D, 0xA505DF1B, 0x3C0C8EA1, 0x4B0BBE37, + 0xD56F2B94, 0xA2681B02, 0x3B614AB8, 0x4C667A2E, + 0xDCD967BF, 0xABDE5729, 0x32D70693, 0x45D03605, + 0xDBB4A3A6, 0xACB39330, 0x35BAC28A, 0x42BDF21C, + 0xCFB5FFE9, 0xB8B2CF7F, 0x21BB9EC5, 0x56BCAE53, + 0xC8D83BF0, 0xBFDF0B66, 0x26D65ADC, 0x51D16A4A, + 0xC16E77DB, 0xB669474D, 0x2F6016F7, 0x58672661, + 0xC603B3C2, 0xB1048354, 0x280DD2EE, 0x5F0AE278, + 0xE96CCF45, 0x9E6BFFD3, 0x0762AE69, 0x70659EFF, + 0xEE010B5C, 0x99063BCA, 0x000F6A70, 0x77085AE6, + 0xE7B74777, 0x90B077E1, 0x09B9265B, 0x7EBE16CD, + 0xE0DA836E, 0x97DDB3F8, 0x0ED4E242, 0x79D3D2D4, + 0xF4DBDF21, 0x83DCEFB7, 0x1AD5BE0D, 0x6DD28E9B, + 0xF3B61B38, 0x84B12BAE, 0x1DB87A14, 0x6ABF4A82, + 0xFA005713, 0x8D076785, 0x140E363F, 0x630906A9, + 0xFD6D930A, 0x8A6AA39C, 0x1363F226, 0x6464C2B0, + 0xA4DEAE1D, 0xD3D99E8B, 0x4AD0CF31, 0x3DD7FFA7, + 0xA3B36A04, 0xD4B45A92, 0x4DBD0B28, 0x3ABA3BBE, + 0xAA05262F, 0xDD0216B9, 0x440B4703, 0x330C7795, + 0xAD68E236, 0xDA6FD2A0, 0x4366831A, 0x3461B38C, + 0xB969BE79, 0xCE6E8EEF, 0x5767DF55, 0x2060EFC3, + 0xBE047A60, 0xC9034AF6, 0x500A1B4C, 0x270D2BDA, + 0xB7B2364B, 0xC0B506DD, 0x59BC5767, 0x2EBB67F1, + 0xB0DFF252, 0xC7D8C2C4, 0x5ED1937E, 0x29D6A3E8, + 0x9FB08ED5, 0xE8B7BE43, 0x71BEEFF9, 0x06B9DF6F, + 0x98DD4ACC, 0xEFDA7A5A, 0x76D32BE0, 0x01D41B76, + 0x916B06E7, 0xE66C3671, 0x7F6567CB, 0x0862575D, + 0x9606C2FE, 0xE101F268, 0x7808A3D2, 0x0F0F9344, + 0x82079EB1, 0xF500AE27, 0x6C09FF9D, 0x1B0ECF0B, + 0x856A5AA8, 0xF26D6A3E, 0x6B643B84, 0x1C630B12, + 0x8CDC1683, 0xFBDB2615, 0x62D277AF, 0x15D54739, + 0x8BB1D29A, 0xFCB6E20C, 0x65BFB3B6, 0x12B88320, + 0x3FBA6CAD, 0x48BD5C3B, 0xD1B40D81, 0xA6B33D17, + 0x38D7A8B4, 0x4FD09822, 0xD6D9C998, 0xA1DEF90E, + 0x3161E49F, 0x4666D409, 0xDF6F85B3, 0xA868B525, + 0x360C2086, 0x410B1010, 0xD80241AA, 0xAF05713C, + 0x220D7CC9, 0x550A4C5F, 0xCC031DE5, 0xBB042D73, + 0x2560B8D0, 0x52678846, 0xCB6ED9FC, 0xBC69E96A, + 0x2CD6F4FB, 0x5BD1C46D, 0xC2D895D7, 0xB5DFA541, + 0x2BBB30E2, 0x5CBC0074, 0xC5B551CE, 0xB2B26158, + 0x04D44C65, 0x73D37CF3, 0xEADA2D49, 0x9DDD1DDF, + 0x03B9887C, 0x74BEB8EA, 0xEDB7E950, 0x9AB0D9C6, + 0x0A0FC457, 0x7D08F4C1, 0xE401A57B, 0x930695ED, + 0x0D62004E, 0x7A6530D8, 0xE36C6162, 0x946B51F4, + 0x19635C01, 0x6E646C97, 0xF76D3D2D, 0x806A0DBB, + 0x1E0E9818, 0x6909A88E, 0xF000F934, 0x8707C9A2, + 0x17B8D433, 0x60BFE4A5, 0xF9B6B51F, 0x8EB18589, + 0x10D5102A, 0x67D220BC, 0xFEDB7106, 0x89DC4190, + 0x49662D3D, 0x3E611DAB, 0xA7684C11, 0xD06F7C87, + 0x4E0BE924, 0x390CD9B2, 0xA0058808, 0xD702B89E, + 0x47BDA50F, 0x30BA9599, 0xA9B3C423, 0xDEB4F4B5, + 0x40D06116, 0x37D75180, 0xAEDE003A, 0xD9D930AC, + 0x54D13D59, 0x23D60DCF, 0xBADF5C75, 0xCDD86CE3, + 0x53BCF940, 0x24BBC9D6, 0xBDB2986C, 0xCAB5A8FA, + 0x5A0AB56B, 0x2D0D85FD, 0xB404D447, 0xC303E4D1, + 0x5D677172, 0x2A6041E4, 0xB369105E, 0xC46E20C8, + 0x72080DF5, 0x050F3D63, 0x9C066CD9, 0xEB015C4F, + 0x7565C9EC, 0x0262F97A, 0x9B6BA8C0, 0xEC6C9856, + 0x7CD385C7, 0x0BD4B551, 0x92DDE4EB, 0xE5DAD47D, + 0x7BBE41DE, 0x0CB97148, 0x95B020F2, 0xE2B71064, + 0x6FBF1D91, 0x18B82D07, 0x81B17CBD, 0xF6B64C2B, + 0x68D2D988, 0x1FD5E91E, 0x86DCB8A4, 0xF1DB8832, + 0x616495A3, 0x1663A535, 0x8F6AF48F, 0xF86DC419, + 0x660951BA, 0x110E612C, 0x88073096, 0xFF000000 +}; + +static inline void sbni_outs(int port, void *data, int len) +{ +#ifdef GOODBUS16 + outsw(port,data,len/2); + if(len & 1) + outb(((char*)data)[len - 1],port); +#else + outsb(port,data,len); +#endif +} + +static inline void sbni_ins(int port, void *data, int len) +{ +#ifdef GOODBUS16 + insw(port,data,len/2); + if(len & 1) + ((char*)data)[len - 1] = inb(port); +#else + insb(port,data,len); +#endif +} + + +static int sbni_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + struct sbni_hard_header *hh = (struct sbni_hard_header *) + skb_push(skb, sizeof(struct sbni_hard_header)); + + + if(type!=ETH_P_802_3) + hh->h_proto = htons(type); + else + hh->h_proto = htons(len); + + if(saddr) + memcpy(hh->h_source,saddr,dev->addr_len); + else + memcpy(hh->h_source,dev->dev_addr,dev->addr_len); + + if(daddr) + { + memcpy(hh->h_dest,daddr,dev->addr_len); + return dev->hard_header_len; + } + return -dev->hard_header_len; +} + + +int sbni_header_cache(struct neighbour *neigh, struct hh_cache *hh) +{ + unsigned short type = hh->hh_type; + struct sbni_hard_header *sbni = (struct sbni_hard_header*) + (((u8*)hh->hh_data) - 8); + struct net_device *dev = neigh->dev; + + + if (type == __constant_htons(ETH_P_802_3)) + return -1; + + sbni->h_proto = type; + memcpy(sbni->h_source, dev->dev_addr, dev->addr_len); + memcpy(sbni->h_dest, neigh->ha, dev->addr_len); + return 0; +} + +static int sbni_rebuild_header(struct sk_buff *skb) +{ + struct sbni_hard_header *hh = (struct sbni_hard_header *)skb; + /* + * Only ARP/IP is currently supported + */ + + /* + * Try to get ARP to resolve the header. + */ + +#ifdef CONFIG_INET + return arp_find((unsigned char*)hh->h_dest, skb)? 1 : 0; +#else + return 0; +#endif +} + +static void sbni_header_cache_update(struct hh_cache *hh, struct net_device *dev, unsigned char * haddr) +{ + memcpy(((u8*)hh->hh_data) + 2, haddr, dev->addr_len); +} + + + +#ifdef HAVE_DEVLIST +struct netdev_entry sbni_drv = { + "sbni", sbni_probe1, SBNI_IO_EXTENT, netcard_portlist +}; + +#else + +int __init sbni_probe(struct net_device *dev) +{ + int i; + int base_addr = dev ? dev->base_addr : 0; + + DP( printk("%s: sbni_probe\n", dev->name); ) + + if(base_addr > 0x1ff) /* Check a single specified location. */ + return sbni_probe1(dev, base_addr); + else if(base_addr != 0) /* Don't probe at all. */ + return ENXIO; + for(i = 0; (base_addr = netcard_portlist[i]); i++) + { + if(!check_region(base_addr, SBNI_IO_EXTENT) && base_addr != 1) + { + /* Lock this address, or later we'll try it again */ + netcard_portlist[i] = 1; + if(sbni_probe1(dev, base_addr) == 0) + return 0; + } + } + return ENODEV; +} + +#endif /* have devlist*/ + +/* + * The actual probe. + */ + +/* + Valid combinations in CSR0 (for probing): + + VALID_DECODER 0000,0011,1011,1010 + + ; 0 ; - + TR_REQ ; 1 ; + + TR_RDY ; 2 ; - + TR_RDY TR_REQ ; 3 ; + + BU_EMP ; 4 ; + + BU_EMP TR_REQ ; 5 ; + + BU_EMP TR_RDY ; 6 ; - + BU_EMP TR_RDY TR_REQ ; 7 ; + + RC_RDY ; 8 ; + + RC_RDY TR_REQ ; 9 ; + + RC_RDY TR_RDY ; 10 ; - + RC_RDY TR_RDY TR_REQ ; 11 ; - + RC_RDY BU_EMP ; 12 ; - + RC_RDY BU_EMP TR_REQ ; 13 ; - + RC_RDY BU_EMP TR_RDY ; 14 ; - + RC_RDY BU_EMP TR_RDY TR_REQ ; 15 ; - +*/ +#define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200) + +static int __init sbni_probe1(struct net_device *dev, int ioaddr) + +{ + int autoirq = 0; + int bad_card = 0; + unsigned char csr0; + struct net_local* lp; + static int version_printed = 0; + + DP( printk("%s: sbni_probe1 ioaddr=%d\n", dev->name, ioaddr); ) + + if(check_region(ioaddr, SBNI_IO_EXTENT) < 0) + return -ENODEV; + if(version_printed++ == 0) + printk(version); + + /* check for valid combination in CSR0 */ + csr0 = inb(ioaddr + CSR0); + if(csr0 == 0xff || csr0 == 0) + bad_card = 1; + else + { + csr0 &= ~EN_INT; + if(csr0 & BU_EMP) + csr0 |= EN_INT; + if((VALID_DECODER & (1 << (csr0 >> 4))) == 0) + bad_card = 1; + } + + if(bad_card) + return ENODEV; + else + outb(0, ioaddr + CSR0); + if(dev->irq < 2) + { + DP( printk("%s: autoprobing\n", dev->name); ); + autoirq_setup(5); + outb(EN_INT | TR_REQ, ioaddr + CSR0); + outb(PR_RES, ioaddr + CSR1); + autoirq = autoirq_report(5); + + if(autoirq == 0) + { + printk("sbni probe at %#x failed to detect IRQ line\n", ioaddr); + return EAGAIN; + } + } + /* clear FIFO buffer */ + outb(0, ioaddr + CSR0); + + if(autoirq) + dev->irq = autoirq; + + { + int irqval=request_irq(dev->irq, sbni_interrupt, 0, dev->name, dev); + if (irqval) + { + printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); + return EAGAIN; + } + } + + /* + * Initialize the device structure. + */ + + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if(dev->priv == NULL) + { + DP( printk("%s: cannot allocate memory\n", dev->name); ) + return -ENOMEM; + } + + memset(dev->priv, 0, sizeof(struct net_local)); + dev->base_addr = ioaddr; + request_region(ioaddr, SBNI_IO_EXTENT, "sbni"); + + /* + * generate Ethernet address (0x00ff01xxxxxx) + */ + + *(u16*)dev->dev_addr = htons(0x00ff); + *(u32*)(dev->dev_addr+2) = htonl(((def_mac ? def_mac : (u32) dev->priv) & 0x00ffffff) | 0x01000000); + + lp = dev->priv; + if(def_rxl < 0) + { + /* autodetect receive level */ + lp->rxl_curr = 0xf; + lp->rxl_delta = -1; + } else { + /* fixed receive level */ + lp->rxl_curr = def_rxl & 0xf; + lp->rxl_delta = 0; + } + lp->csr1.rxl = rxl_tab[lp->rxl_curr]; + lp->csr1.rate = def_baud & 3; + lp->frame_len = DEF_FRAME_LEN; + printk("%s: sbni adapter at %#lx, using %sIRQ %d, MAC: 00:ff:01:%x:%x:%x\n", + dev->name, dev->base_addr, autoirq ? "auto":"assigned ", dev->irq, + *(unsigned char*)(dev->dev_addr+3), + *(unsigned char*)(dev->dev_addr+4), + *(unsigned char*)(dev->dev_addr+5) + ); + + printk("%s: receive level: ", dev->name); + if(lp->rxl_delta == 0) + printk ("%#1x (fixed)", lp->rxl_curr); + else + printk ("autodetect"); + printk(", baud rate: %u\n", (unsigned)lp->csr1.rate); + + /* + * The SBNI-specific entries in the device structure. + */ + dev->open = &sbni_open; + dev->hard_start_xmit = &sbni_start_xmit; + dev->stop = &sbni_close; + dev->get_stats = &sbni_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->set_mac_address = &sbni_set_mac_address; + dev->do_ioctl = &sbni_ioctl; + + /* + * Setup the generic properties + */ + + ether_setup(dev); + + dev->hard_header = sbni_header; + dev->hard_header_len = sizeof(struct sbni_hard_header); + dev->rebuild_header=sbni_rebuild_header; + dev->mtu = DEF_FRAME_LEN; + + dev->hard_header_cache = sbni_header_cache; + dev->header_cache_update = sbni_header_cache_update; + + lp->m=dev; + lp->me=dev; + lp->next_lp=NULL; + + return 0; +} + +/* + * Open/initialize the board. + */ + +static int sbni_open(struct net_device *dev) +{ + struct net_local* lp = (struct net_local*)dev->priv; + struct timer_list* watchdog = &lp->watchdog; + + + DP( printk("%s: sbni_open\n", dev->name); ) + + cli(); + lp->currframe = NULL; + + card_start(dev); + dev->start = 1; + /* set timer watchdog */ + init_timer(watchdog); + watchdog->expires = jiffies + SBNI_TIMEOUT; + watchdog->data = (unsigned long)dev; + watchdog->function = sbni_watchdog; + add_timer(watchdog); + DP( printk("%s: sbni timer watchdog initialized\n", dev->name); ); + + sti(); + + MOD_INC_USE_COUNT; + return 0; +} + +static int sbni_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct net_local* lp = (struct net_local*) dev->priv; + struct timer_list* watchdog = &lp->watchdog; + + + DP( printk("%s: sbni_close\n", dev->name); ) + + cli(); + + sbni_drop_tx_queue(dev); + + dev->tbusy = 1; + dev->start = 0; + + del_timer(watchdog); + + outb(0, ioaddr + CSR0); + sti(); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp = (struct net_local*)dev->priv; + struct sbni_hard_header *hh=(struct sbni_hard_header *)skb->data; + +#ifdef KATYUSHA + struct net_local *nl; + int stop; +#endif + + DP( printk("%s: sbni_start_xmit In \n", dev->name); ); + + + if(lp->me != dev) + panic("sbni: lp->me != dev !!!\nMail to developer (xenon@granch.ru) if you noticed this error\n"); + + if(dev->interrupt) + { + DP( printk("sbni_xmit_start: interrupt\n"); ) + /* May be unloading, don't stamp on */ + return 1; /* the packet buffer this time */ + } + + hh->number = 1; + hh->reserv = 0; + + hh->packetlen = (skb->len - sizeof (unsigned short) - + (sizeof(struct sbni_hard_header) - SBNI_HH_SZ)) + | PACKET_SEND_OK | PACKET_FIRST_FRAME; + + /* we should use hairy method to calculate crc because of extra bytes are + livin between hard header and data*/ + hh->crc = calc_crc((void*)&hh->packetlen, SBNI_HH_SZ - sizeof(unsigned), CRC32_INITIAL); + hh->crc = calc_crc(skb->data + sizeof(struct sbni_hard_header), + skb->len - sizeof(struct sbni_hard_header), + hh->crc); + +#ifdef KATYUSHA + /* looking for first idle device */ + for (stop=0,nl=lp; nl && !stop; nl=nl->next_lp) + { + if((!nl->currframe) && (nl->carrier)) /* if idle */ + { + skb->dev = lp->me; + nl->currframe = skb; + /* set request for transmit */ + outb(inb(nl->me->base_addr + CSR0) | TR_REQ, + nl->me->base_addr + CSR0); + stop=1; + } + } + + if(!stop) /* we havent found any idle.*/ + { + skb_queue_tail(&lp->queue,skb); + outb(inb(dev->base_addr + CSR0) | TR_REQ, dev->base_addr + CSR0); + + } +#else + if (lp->currframe || 1) + { + skb_queue_tail(&lp->queue,skb); + + } + else + { + lp->currframe = skb; + } + /* set request for transmit */ + outb(inb(dev->base_addr + CSR0) | TR_REQ, dev->base_addr + CSR0); +#endif + return 0; +} + +void card_start(struct net_device *dev) +{ + struct net_local *lp = (struct net_local*)dev->priv; + + DP( printk("%s: card_start\n",dev->name); ) + lp->wait_frame_number = 0; + lp->inppos = lp->outpos = 0; + lp->eth_trans_buffer_len = 0; + lp->tr_err = TR_ERROR_COUNT; + lp->last_receive_OK = FALSE; + lp->tr_resend = FALSE; + lp->timer_ticks = CHANGE_LEVEL_START_TICKS; + lp->timeout_rxl = 0; + + lp->waitack=0; + skb_queue_head_init(&lp->queue); + sbni_drop_tx_queue(dev); + dev->tbusy = 0; + + dev->interrupt = 0; + /* Reset the card and set start parameters */ + outb(PR_RES | *(char*)&lp->csr1, dev->base_addr + CSR1); + outb(EN_INT, dev->base_addr + CSR0); +} + +void sbni_nirvana(struct net_device *dev) +{ + sbni_outs(dev->base_addr+DAT,magic_reply,9); +} + +static inline unsigned short sbni_recv(struct net_device *dev) +{ + struct net_local *lp = (struct net_local*)dev->priv; + unsigned long crc; + unsigned short packetlen = 0; + unsigned short packetinf, packetfirst, receiveframeresend; + unsigned char current_frame; + unsigned int i, j; + unsigned char delme,rcv_res=RCV_WR; + + lp->in_stats.all_rx_number++; + + if((delme=inb(dev->base_addr + DAT)) == SBNI_SIG) + { + crc = CRC32_INITIAL; + *(((unsigned char *)&packetlen) + 0) = inb(dev->base_addr + DAT); + crc = CRC32(*(((unsigned char *)&packetlen) + 0), crc); + *(((unsigned char *)&packetlen) + 1) = inb(dev->base_addr + DAT); + crc = CRC32(*(((unsigned char *)&packetlen) + 1), crc); + packetinf = packetlen & PACKET_INF_MASK; + packetfirst = packetlen & PACKET_FIRST_FRAME; + receiveframeresend = packetlen & RECEIVE_FRAME_RESEND; + packetlen = packetlen & PACKET_LEN_MASK; + + + if((packetlen <= SB_MAX_BUFFER_ARRAY - 3) && (packetlen >= 6)) + { + /* read frame number */ + current_frame = inb(dev->base_addr + DAT); + crc = CRC32(current_frame, crc); + /* read HandShake counter */ + lp->HSCounter = inb(dev->base_addr + DAT); + crc = CRC32(lp->HSCounter, crc); + packetlen -= 2; + + sbni_ins(dev->base_addr + DAT, lp->eth_rcv_buffer + lp->inppos, packetlen); + + for(i = lp->inppos; i < (packetlen + lp->inppos); i++) + { + crc = CRC32(lp->eth_rcv_buffer[i], crc); + } + + if(crc == CRC32_REMAINDER) + { + if(packetlen > 4) + rcv_res=RCV_OK; + else if(packetlen == 4) + rcv_res=RCV_NO; + + if(lp->waitack && packetinf == PACKET_RESEND) + lp->in_stats.resend_tx_number++; + + + switch(packetinf) + { + case PACKET_SEND_OK: + { + lp->tr_err = TR_ERROR_COUNT; + lp->tr_resend = FALSE; + /* if(lp->trans_frame_number){ */ + lp->outpos += lp->realframelen; + + /* SendComplete + * not supported + */ + DP( printk("%s: sbni_recv SendComplete\n",dev->name); ); + /* + * We sucessfully sent current packet + */ + + if(lp->waitack) + { + dev_kfree_skb(lp->currframe); + lp->stats.tx_packets++; +#ifdef KATYUSHA + lp->currframe=skb_dequeue(&(((struct net_local*) (lp->m->priv))->queue)); +#else + lp->currframe=skb_dequeue(&lp->queue); +#endif + lp->in_stats.all_tx_number++; + lp->waitack=0; + } + + /* + * reset output active flags + */ + dev->tbusy = 0; + mark_bh(NET_BH); + /*} if */ + } + case PACKET_RESEND: + { + if(lp->tr_err) /**/ + lp->tr_err--; + if(lp->ok_curr < 0xffffffff) + lp->ok_curr++; + if(packetlen > 4 && !(lp->last_receive_OK && receiveframeresend)) + { + if(packetfirst) + { + if(lp->wait_frame_number) + { + for(i = lp->inppos, j = 0; + i < (lp->inppos + packetlen - 4); + i++, j++) + lp->eth_rcv_buffer[j] = lp->eth_rcv_buffer[i]; + } + lp->wait_frame_number = current_frame; + lp->inppos = 0; + } + if(current_frame == lp->wait_frame_number) + { + lp->inppos += (packetlen - 4); + if(lp->wait_frame_number == 1) + { + sbni_get_packet(dev); + lp->inppos = 0; + } + lp->wait_frame_number--; + } + } + lp->last_receive_OK = TRUE; + break; + } + default: + break; + } + } + else + { + DP(printk("%s: bad CRC32\n",dev->name)); + change_level(dev); + } + } + else + { + DP(printk("%s: bad len\n ",dev->name)); + change_level(dev); + lp->stats.rx_over_errors++; + } + } + else + { + DP(printk("%s: bad sig\n",dev->name)); + change_level(dev); + } + outb(inb(dev->base_addr + CSR0) ^ CT_ZER, dev->base_addr + CSR0); + return (rcv_res); +} + +void change_level(struct net_device *dev) +{ + struct net_local *lp = (struct net_local*)dev->priv; + + lp->in_stats.bad_rx_number++; + lp->stats.tx_errors++; + if(lp->rxl_delta == 0) + return; + /* + * set new rxl_delta value + */ + if(lp->rxl_curr == 0) + lp->rxl_delta = 1; + else if(lp->rxl_curr == 0xf) + lp->rxl_delta = -1; + else if(lp->ok_curr < lp->ok_prev) + lp->rxl_delta = -lp->rxl_delta; + /* + * set new rxl_curr value + */ + lp->csr1.rxl = rxl_tab[lp->rxl_curr += lp->rxl_delta]; + outb(*(char*)&lp->csr1, dev->base_addr + CSR1); + + + /* + * update ok_prev/ok_curr counters + */ + lp->ok_prev = lp->ok_curr; + lp->ok_curr = 0; + + DP( printk("%s: receive error, rxl_curr = %d, rxl_delta = %d\n",\ + dev->name,lp->rxl_curr, lp->rxl_delta); ) + +} + +static inline void sbni_xmit(struct net_device *dev) +{ + struct net_local* lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + + skb=lp->currframe; + + DP( printk("%s: sbni_xmit CSR0=%02x\n",dev->name, (unsigned char)inb(dev->base_addr + CSR0)); ); + + /* push signature*/ + outb(SBNI_SIG, dev->base_addr + DAT); + + /* push frame w/o crc [HAiRY]*/ + sbni_outs(dev->base_addr + DAT, + &((struct sbni_hard_header *)(skb->data))->packetlen, + SBNI_HH_SZ - sizeof(unsigned)); + + sbni_outs(dev->base_addr + DAT, + skb->data + sizeof(struct sbni_hard_header), + skb->len - sizeof(struct sbni_hard_header)); /* ÕÓÐÅÅÍ ÅÝÅ */ + + /* push crc */ + sbni_outs(dev->base_addr + DAT, skb->data, sizeof(unsigned)); + + lp->waitack=1; +} + +/* + * The typical workload of the driver: + * Handle the ether interface interrupts. + */ +static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct net_local* lp; + u_char csr0; + unsigned short rcv_res = RCV_NO; + + + if(dev == NULL || dev->irq != irq) + { + printk("sbni: irq %d for unknown device\n", irq); + return; + } + + if(dev->interrupt) + { + printk("%s: Reentering the interrupt driver!\n", dev->name); + return; + } + dev->interrupt = 1; + + csr0 = inb(dev->base_addr + CSR0); + DP( printk("%s: entering interrupt handler, CSR0 = %02x\n", dev->name, csr0); ) + + lp=dev->priv; + + if(!lp->carrier) + lp->carrier=1; + + /* + * Disable adapter interrupts + */ + outb((csr0 & ~EN_INT) | TR_REQ, dev->base_addr + CSR0); + lp->timer_ticks = CHANGE_LEVEL_START_TICKS; + csr0 = inb(dev->base_addr + CSR0); + + if(csr0 & (TR_RDY | RC_RDY)) + { + if(csr0 & RC_RDY) + rcv_res = sbni_recv(dev); + + if((lp->currframe) && (rcv_res != RCV_WR)) + sbni_xmit(dev); + else if (rcv_res == RCV_OK) + sbni_nirvana(dev); + + csr0 = inb(dev->base_addr + CSR0); + DP( printk("%s: CSR0 = %02x\n",dev->name, (u_int)csr0); ); + } + + + DP( printk("%s: leaving interrupt handler, CSR0 = %02x\n",dev->name, csr0 | EN_INT); ); + + /* here we should send pong */ + outb(inb(dev->base_addr+CSR0) & ~TR_REQ, dev->base_addr + CSR0); + if(lp->currframe) + outb(inb(dev->base_addr+CSR0) | TR_REQ, dev->base_addr + CSR0); + else + csr0 = inb(dev->base_addr + CSR0); + + /* + * Enable adapter interrupts + */ + + outb(csr0 | EN_INT, dev->base_addr + CSR0); + dev->interrupt = 0; +} + +static struct enet_statistics *sbni_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + return &lp->stats; +} + +static inline void sbni_get_packet(struct net_device* dev) +{ + struct net_local* lp = (struct net_local*)dev->priv; + struct sk_buff* skb; + unsigned char *rawp; + + + + skb = dev_alloc_skb(lp->inppos - ETH_HLEN + sizeof(struct sbni_hard_header)); + + if(skb == NULL) + { + DP( printk("%s: Memory squeeze, dropping packet.\n", dev->name); ) + lp->stats.rx_dropped++; + return; + } else { +#ifdef KATYUSHA + skb->dev = lp->m; +#else + skb->dev = dev; +#endif + memcpy((unsigned char*)skb_put(skb, lp->inppos + 8)+8, + lp->eth_rcv_buffer, + lp->inppos); + + + skb->mac.raw = skb->data + 8; + + if((*(char*)lp->eth_rcv_buffer) & 1) + { + if(memcmp(lp->eth_rcv_buffer,dev->broadcast, ETH_ALEN)==0) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_MULTICAST; + } + else if(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) + { + if(memcmp(lp->eth_rcv_buffer,dev->dev_addr, ETH_ALEN)) + skb->pkt_type=PACKET_OTHERHOST; + } + + if( htons(*((unsigned short*)(&lp->eth_rcv_buffer[2*ETH_ALEN]))) >= 1536) + skb->protocol = *((unsigned short*)(&lp->eth_rcv_buffer[2*ETH_ALEN])); + else + { + rawp = (unsigned char*)(&lp->eth_rcv_buffer[2*ETH_ALEN]); + if (*(unsigned short *)rawp == 0xFFFF) + skb->protocol=htons(ETH_P_802_3); + else + skb->protocol=htons(ETH_P_802_2); + } + + + skb_pull(skb,SBNI_HH_SZ); + + netif_rx(skb); + lp->stats.rx_packets++; + } + return; +} + +static void sbni_watchdog(unsigned long arg) +{ + struct net_device* dev = (struct net_device*)arg; + struct net_local* lp = (struct net_local *)dev->priv; + u_char csr0; + + + + DP( printk("%s: watchdog start\n",dev->name); ) + /* + * if no pong received and transmission is not in progress + * then assume error + */ + cli(); + csr0 = inb(dev->base_addr + CSR0); + if(csr0 & (RC_CHK | TR_REQ)) + { + if(lp->timer_ticks) + { + if(csr0 & (RC_RDY | BU_EMP)) + { + lp->timer_ticks--; + } + } + else + { + if(lp->rxl_delta) + { + lp->ok_prev = lp->ok_curr; + lp->ok_curr = 0; + lp->rxl_curr = timeout_rxl_tab[lp->timeout_rxl]; + lp->timeout_rxl++; + if(lp->timeout_rxl > SIZE_OF_TIMEOUT_RXL_TAB - 1) + lp->timeout_rxl = 0; + lp->csr1.rxl = rxl_tab[lp->rxl_curr]; + /* + * update ok_prev/ok_curr counters + */ + lp->ok_prev = lp->ok_curr; + lp->ok_curr = 0; + } + if(lp->tr_err) + lp->tr_err--; + else + { + /* Drop the queue of tx packets */ + sbni_drop_tx_queue(dev); + lp->carrier=0; + } + + /* + * send pong + */ + + csr0 = inb(dev->base_addr + CSR0); + outb(csr0 & ~TR_REQ, dev->base_addr + CSR0); + outb(*(char*)(&lp->csr1) | PR_RES, dev->base_addr + CSR1); + lp->in_stats.timeout_number++; + } + } + sti(); + outb(csr0 | RC_CHK, dev->base_addr + CSR0); + if(dev->start) + { + struct timer_list* watchdog = &lp->watchdog; + init_timer(watchdog); + watchdog->expires = jiffies + SBNI_TIMEOUT; + watchdog->data = arg; + watchdog->function = sbni_watchdog; + add_timer(watchdog); + } +} + +static void sbni_drop_tx_queue(struct net_device *dev) +{ + struct net_local* lp = (struct net_local *)dev->priv,*nl; + struct sk_buff *tmp; + + /* first of all, we should try to gift our packets to another interface */ + + nl=(struct net_local *)lp->m->priv; + if(nl==lp) + nl=lp->next_lp; + + if(nl) + { + /* we found device*/ + if(lp->currframe) + { + if(!nl->currframe) + { + nl->currframe=lp->currframe; + } + else + { + skb_queue_head(&((struct net_local*)(lp->m->priv))->queue,lp->currframe); + } + } + lp->currframe=NULL; + + if(!nl->currframe) + nl->currframe=skb_dequeue(&(((struct net_local*)(lp->m->priv))->queue)); + + /* set request for transmit */ + outb(inb(nl->me->base_addr + CSR0) | TR_REQ, nl->me->base_addr + CSR0); + + } + else + { + /* *sigh*, we should forget this packets */ + nl=lp->m->priv; + + while((tmp = skb_dequeue(&nl->queue)) != NULL) + { + dev_kfree_skb(tmp); + lp->stats.tx_packets++; + } + + if (lp->currframe) + { + dev_kfree_skb(lp->currframe); + lp->currframe = NULL; + lp->stats.tx_packets++; + } + } + lp->waitack=0; + dev->tbusy = 0; + + mark_bh(NET_BH); + DP( printk("%s: queue dropping stoped\n",dev->name); ); +} + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ + +static void set_multicast_list(struct net_device *dev) +{ + /* + * always enabled promiscuous mode. + */ + return; +} + +static int sbni_set_mac_address(struct net_device *dev, void *addr) +{ + /* struct net_local *lp = (struct net_local *)dev->priv; */ + struct sockaddr *saddr = addr; + + if(dev->start) + { + /* Only possible while card isn't started */ + return -EBUSY; + } + memcpy(dev->dev_addr, saddr->sa_data, dev->addr_len); + return (0); +} + +static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct net_local* lp = (struct net_local *)dev->priv,*tlp; + struct net_device *slave; + int error = 0; + char tmpstr[6]; + + + switch(cmd) + { + case SIOCDEVGETINSTATS: + { + struct sbni_in_stats *in_stats = (struct sbni_in_stats *)ifr->ifr_data; + DP( printk("%s: SIOCDEVGETINSTATS %08x\n",dev->name,(unsigned)in_stats);) + if(copy_to_user((void *)in_stats, (void *)(&(lp->in_stats)), sizeof(struct sbni_in_stats))) + return -EFAULT; + break; + } + case SIOCDEVRESINSTATS: + { + DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); ) + lp->in_stats.all_rx_number = 0; + lp->in_stats.bad_rx_number = 0; + lp->in_stats.timeout_number = 0; + lp->in_stats.all_tx_number = 0; + lp->in_stats.resend_tx_number = 0; + break; + } + case SIOCDEVGHWSTATE: + { + struct sbni_flags flags; + flags.rxl = lp->rxl_curr; + flags.rate = lp->csr1.rate; + flags.fixed_rxl = (lp->rxl_delta == 0); + flags.fixed_rate = 1; + ifr->ifr_data = *(caddr_t*)&flags; + DP( printk("%s: get flags (0x%02x)\n",dev->name, (unsigned char)ifr->ifr_data); ) + break; + } + case SIOCDEVSHWSTATE: + { + struct sbni_flags flags; + DP( printk("%s: SIOCDEVSHWSTATE flags=0x%02x\n",dev->name, (unsigned char)ifr->ifr_data); ) + /* root only */ + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + flags = *(struct sbni_flags*)&ifr->ifr_data; + if(flags.fixed_rxl) + { + lp->rxl_delta = 0; + lp->rxl_curr = flags.rxl; + } + else + { + lp->rxl_delta = DEF_RXL_DELTA; + lp->rxl_curr = DEF_RXL; + } + lp->csr1.rxl = rxl_tab[lp->rxl_curr]; + if(flags.fixed_rate) + lp->csr1.rate = flags.rate; + else + lp->csr1.rate = DEF_RATE; + /* + * Don't be afraid... + */ + outb(*(char*)(&lp->csr1) | PR_RES, dev->base_addr + CSR1); + + DP( printk("%s: set flags (0x%02x)\n receive level: %u, baud rate: %u\n",\ + dev->name, (unsigned char)ifr->ifr_data, (unsigned)lp->rxl_curr, (unsigned)lp->csr1.rate); ) + break; + } + + case SIOCDEVENSLAVE: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if(copy_from_user( tmpstr, ifr->ifr_data, 6)) + return -EFAULT; + slave=dev_get(tmpstr); + if(!(slave && slave->flags & IFF_UP && dev->flags & IFF_UP)) + { + printk("%s: Both devices should be UP to enslave!\n",dev->name); + return -EINVAL; + } + + if(slave) + { + if(!((dev->flags & IFF_SLAVE) || (slave->flags & IFF_SLAVE))) + { + /* drop queue*/ + sbni_drop_tx_queue(slave); + slave->flags |= IFF_SLAVE; + ((struct net_local *)(slave->priv))->m=dev; + while(lp->next_lp) //tail it after last slave + lp=lp->next_lp; + lp->next_lp=slave->priv; + lp=(struct net_local *)dev->priv; + dev->flags |= IFF_MASTER; + } + else + { + printk("%s: one of devices is already slave!\n",dev->name); + return -EBUSY; + } + } + else + { + printk("%s: can't find device %s to enslave\n",dev->name,ifr->ifr_data); + return -ENOENT; + } + break; + + case SIOCDEVEMANSIPATE: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_SLAVE) + { + dev->flags &= ~IFF_SLAVE; + /* exclude us from masters slavelist*/ + for(tlp=lp->m->priv;tlp->next_lp!=lp && tlp->next_lp;tlp=tlp->next_lp); + if(tlp->next_lp) + { + tlp->next_lp = lp->next_lp; + if(!((struct net_local *)lp->m->priv)->next_lp) + { + lp->m->flags &= ~IFF_MASTER; + } + lp->next_lp=NULL; + lp->m=dev; + } + else + { + printk("%s: Ooops. drivers structure is mangled!\n",dev->name); + return -EIO; + } + } + else + { + printk("%s: isn't slave device!\n",dev->name); + return -EINVAL; + } + break; + + default: + DP( printk("%s: invalid ioctl: 0x%x\n",dev->name, cmd); ) + error = -EINVAL; + } + return (error); +} + + + +#ifdef CRCASM + +unsigned long calc_crc(char *mem, int len, unsigned initial) +{ + + __asm__ ( + "xorl %%eax,%%eax\n\t" + "1:\n\t" + "lodsb\n\t" + "xorb %%dl,%%al\n\t" + "shrl $8,%%edx\n\t" + "xorl (%%edi,%%eax,4),%%edx\n\t" + "loop 1b\n\t" + "movl %%edx,%%eax" + : + : "S" (mem), "D" (&crc32tab[0]), "c" (len), "d" (initial) + : "eax", "edx", "ecx" + ); + /* return crc; */ +} + +#else + +unsigned long calc_crc(char *mem, int len, unsigned initial) +{ + unsigned crc; + crc = initial; + + for(;len;mem++,len--) + { + crc = CRC32(*mem, crc); + } + return(crc); +} +#endif /* CRCASM */ +#ifdef MODULE + +static int io[SBNI_MAX_NUM_CARDS] = { 0 }; +static int irq[SBNI_MAX_NUM_CARDS] = { 0 }; +static int rxl[SBNI_MAX_NUM_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +static int baud[SBNI_MAX_NUM_CARDS] = { 0 }; +static long mac[SBNI_MAX_NUM_CARDS] = { 0 }; + +#ifdef v22 +MODULE_PARM(io, "1-" __MODULE_STRING(SBNI_MAX_NUM_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(SBNI_MAX_NUM_CARDS) "i"); +MODULE_PARM(rxl, "1-" __MODULE_STRING(SBNI_MAX_NUM_CARDS) "i"); +MODULE_PARM(baud, "1-" __MODULE_STRING(SBNI_MAX_NUM_CARDS) "i"); +MODULE_PARM(mac, "1-" __MODULE_STRING(SBNI_MAX_NUM_CARDS) "i"); +#endif + + +static int sbniautodetect = -1; + +static struct net_device dev_sbni[SBNI_MAX_NUM_CARDS] = { + { + "sbni0", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni1", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni2", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni3", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni4", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni5", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni6", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + }, + { + "sbni7", + 0, 0, 0, 0, /* memory */ + 0, 0, /* base, irq */ + 0, 0, 0, NULL, sbni_probe + } +}; + +int init_module(void) +{ + int devices = 0; + int installed = 0; + int i; + + /* My simple plug for this huge init_module. "XenON */ + + if(sbniautodetect != -1) + { + /* Autodetect mode */ + printk("sbni: Autodetect mode (not recommended!) ...\n"); + if(!sbniautodetect) + sbniautodetect=SBNI_MAX_NUM_CARDS; + printk("Trying to find %d SBNI cards...\n", sbniautodetect); + if(sbniautodetect > SBNI_MAX_NUM_CARDS) + { + sbniautodetect = SBNI_MAX_NUM_CARDS; + printk("sbni: You want to detect too many cards. Truncated to %d\n", SBNI_MAX_NUM_CARDS); + } + for(i = 0; i < sbniautodetect; i++) + { + if(!register_netdev(&dev_sbni[i])) + installed++; + } + if(installed) + return 0; + else + return -EIO; + } + + /* Manual mode */ + for(i = 0; i < SBNI_MAX_NUM_CARDS; i++) + { + if((io[i] != 0) || (irq[i] != 0)) + devices++; + } + for(i = 0; i < devices; i++) + { + dev_sbni[i].irq = irq[i]; + dev_sbni[i].base_addr = io[i]; + def_rxl = rxl[i]; + def_baud = baud[i]; + def_mac = mac[i]; + if(register_netdev(&dev_sbni[i])) + printk("sbni: card not found!\n"); + else + installed++; + } + if(installed) + return 0; + else + return -EIO; +} + +void cleanup_module(void) +{ + int i; + for(i = 0; i < 4; i++) + { + if(dev_sbni[i].priv) + { + free_irq(dev_sbni[i].irq, &dev_sbni[i]); + release_region(dev_sbni[i].base_addr, SBNI_IO_EXTENT); + unregister_netdev(&dev_sbni[i]); + kfree(dev_sbni[i].priv); + dev_sbni[i].priv = NULL; + } + } +} +#endif /* MODULE */ diff --git a/drivers/net/wan/sbni.h b/drivers/net/wan/sbni.h new file mode 100644 index 000000000..2e34d8d34 --- /dev/null +++ b/drivers/net/wan/sbni.h @@ -0,0 +1,194 @@ +/* + * sbni.h - header file for sbni linux device driver + * + * Copyright (C) 1999 Granch ltd., Yaroslav Polyakov (xenon@granch.ru). + * + */ + +/* + * SBNI12 definitions + * + * Revision 2.0.0 1997/08/27 + * Initial revision + * + * Revision 2.1.0 1999/04/26 + * dev_priv structure changed to support balancing and some other features. + * + */ + +#ifndef __SBNI_H +#define __SBNI_H + +#define SBNI_DEBUG 0 + +#if SBNI_DEBUG +#define DP( A ) A +#else +#define DP( A ) +#endif + +typedef unsigned char BOOLEAN; + +#define TRUE 1 +#define FALSE 0 + +#define SBNI_IO_EXTENT 0x4 +#define SB_MAX_BUFFER_ARRAY 1023 + +#define CSR0 0 +#define CSR1 1 + +#define DAT 2 + +/* CSR0 mapping */ +#define BU_EMP (1 << 1) /* r z */ +#define RC_CHK (1 << 2) /* rw */ +#define CT_ZER (1 << 3) /* w */ +#define TR_REQ (1 << 4) /* rwz* */ + +#define TR_RDY (1 << 5) /* r z */ +#define EN_INT (1 << 6) /* rwz* */ +#define RC_RDY (1 << 7) /* r z */ + +/* CSR1 mapping */ +#define PR_RES (1 << 7) /* w */ + +struct sbni_csr1 { + unsigned rxl:5; + unsigned rate:2; + unsigned:1; +}; + +#define DEF_RXL_DELTA -1 +#define DEF_RXL 0xf +#define DEF_RATE 0 +#define DEF_FRAME_LEN (1023 - 14 - 9) + +#ifdef MODULE + +#define SBNI_MAX_NUM_CARDS 8 +#define SBNI_MAX_SLAVES 8 + + +#endif /* MODULE */ + +#define SBNI_SIG 0x5a + +#define SB_ETHER_MIN_LEN 60 + +#define SB_FILLING_CHAR (unsigned char)0x00 +#define TR_ERROR_COUNT 32 +#define CHANGE_LEVEL_START_TICKS 4 +#define SBNI_INTERNAL_QUEUE_SIZE 10 /* 100 ? */ + +#define PACKET_FIRST_FRAME (unsigned short)0x8000 +#define RECEIVE_FRAME_RESEND (unsigned short)0x0800 +#define PACKET_RESEND 0x4000 +#define PACKET_SEND_OK 0x3000 +#define PACKET_LEN_MASK (unsigned short)0x03ff +#define PACKET_INF_MASK (unsigned short)0x7000 + +#define ETHER_ADDR_LEN 6 + +#define SBNI_TIMEOUT HZ/10 /* ticks to wait for pong or packet */ + /* sbni watchdog called SBNI_HZ times per sec. */ + +struct sbni_in_stats { + unsigned int all_rx_number; + unsigned int bad_rx_number; + unsigned int timeout_number; + unsigned int all_tx_number; + unsigned int resend_tx_number; +}; + + +/* + * Board-specific info in dev->priv. + */ +struct net_local { + struct enet_statistics stats; + + struct timer_list watchdog; + unsigned int realframelen; /* the current size of the SB-frame */ + unsigned int eth_trans_buffer_len; /* tx buffer length */ + unsigned int outpos; + unsigned int inppos; + unsigned int frame_len; /* The set SB-frame size */ + unsigned int tr_err; + unsigned int timer_ticks; + BOOLEAN last_receive_OK; + BOOLEAN tr_resend; + + unsigned char wait_frame_number; + unsigned char eth_trans_buffer[1520]; /* tx buffer */ + unsigned char HSCounter; /* Reserved field */ + unsigned char eth_rcv_buffer[2600]; /* rx buffer */ + struct sbni_csr1 csr1; + /* Internal Statistics */ + struct sbni_in_stats in_stats; + + int rxl_curr; /* current receive level value [0..0xf] */ + int rxl_delta; /* receive level delta (+1, -1) + rxl_delta == 0 - receive level + autodetection + disabled */ + unsigned int ok_curr; /* current ok frames received */ + unsigned int ok_prev; /* previous ok frames received */ + unsigned int timeout_rxl; + + struct sk_buff_head queue; + struct sk_buff *currframe; + BOOLEAN waitack; + + struct net_device *m; /* master */ + struct net_device *me; /* me */ + struct net_local *next_lp; /* next lp */ + + int carrier; + + +}; + + +struct sbni_hard_header { + + /* internal sbni stuff */ + unsigned int crc; /* 4 */ + unsigned short packetlen; /* 2 */ + unsigned char number; /* 1 */ + unsigned char reserv; /* 1 */ + + /* 8 */ + + /* ethernet stuff */ + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ + /* +14 */ + /* 22 */ + +}; + +#define SBNI_HH_SZ 22 + +struct sbni_flags { + unsigned rxl:4; + unsigned rate:2; + unsigned fixed_rxl:1; + unsigned fixed_rate:1; +}; + +#define RCV_NO 0 +#define RCV_OK 1 +#define RCV_WR 2 + + +#define SIOCDEVGETINSTATS SIOCDEVPRIVATE +#define SIOCDEVRESINSTATS SIOCDEVPRIVATE+1 +#define SIOCDEVGHWSTATE SIOCDEVPRIVATE+2 +#define SIOCDEVSHWSTATE SIOCDEVPRIVATE+3 +#define SIOCDEVENSLAVE SIOCDEVPRIVATE+4 +#define SIOCDEVEMANSIPATE SIOCDEVPRIVATE+5 + + +#endif /* __SBNI_H */ diff --git a/drivers/net/sdla.c b/drivers/net/wan/sdla.c index 9b5152366..9b5152366 100644 --- a/drivers/net/sdla.c +++ b/drivers/net/wan/sdla.c diff --git a/drivers/net/sdla_fr.c b/drivers/net/wan/sdla_fr.c index 6b2201a81..6b2201a81 100644 --- a/drivers/net/sdla_fr.c +++ b/drivers/net/wan/sdla_fr.c diff --git a/drivers/net/sdla_ppp.c b/drivers/net/wan/sdla_ppp.c index d35ac7c18..d35ac7c18 100644 --- a/drivers/net/sdla_ppp.c +++ b/drivers/net/wan/sdla_ppp.c diff --git a/drivers/net/sdla_x25.c b/drivers/net/wan/sdla_x25.c index 270c5a59f..270c5a59f 100644 --- a/drivers/net/sdla_x25.c +++ b/drivers/net/wan/sdla_x25.c diff --git a/drivers/net/sdladrv.c b/drivers/net/wan/sdladrv.c index 7182dff42..5e1f6b99e 100644 --- a/drivers/net/sdladrv.c +++ b/drivers/net/wan/sdladrv.c @@ -5,6 +5,7 @@ * used by all Sangoma drivers. * * Author: Gene Kozin <genek@compuserve.com> +* Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * * Copyright: (c) 1995-1996 Sangoma Technologies Inc. * diff --git a/drivers/net/sdlamain.c b/drivers/net/wan/sdlamain.c index ca98eeff9..23b811fc9 100644 --- a/drivers/net/sdlamain.c +++ b/drivers/net/wan/sdlamain.c @@ -3,6 +3,7 @@ * * Author: Gene Kozin <genek@compuserve.com> * Jaspreet Singh <jaspreet@sangoma.com> +* Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * * Copyright: (c) 1995-1997 Sangoma Technologies Inc. * diff --git a/drivers/net/sealevel.c b/drivers/net/wan/sealevel.c index 1dc146711..5cbbff8c9 100644 --- a/drivers/net/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -340,7 +340,7 @@ static struct slvl_board *slvl_init(int iobase, int irq, int txdma, int rxdma, i for(i=0;i<999;i++) { sprintf(sv->name,"hdlc%d", i); - if(dev_get(sv->name)==NULL) + if(dev_get(sv->name)==0) { struct net_device *d=sv->chan->netdevice; diff --git a/drivers/net/syncppp.c b/drivers/net/wan/syncppp.c index eee3fceb8..eee3fceb8 100644 --- a/drivers/net/syncppp.c +++ b/drivers/net/wan/syncppp.c diff --git a/drivers/net/syncppp.h b/drivers/net/wan/syncppp.h index c03c720ca..c03c720ca 100644 --- a/drivers/net/syncppp.h +++ b/drivers/net/wan/syncppp.h diff --git a/drivers/net/x25_asy.c b/drivers/net/wan/x25_asy.c index 49d041abc..49d041abc 100644 --- a/drivers/net/x25_asy.c +++ b/drivers/net/wan/x25_asy.c diff --git a/drivers/net/x25_asy.h b/drivers/net/wan/x25_asy.h index 5abeceb20..5abeceb20 100644 --- a/drivers/net/x25_asy.h +++ b/drivers/net/wan/x25_asy.h diff --git a/drivers/net/z85230.c b/drivers/net/wan/z85230.c index a802170ce..a802170ce 100644 --- a/drivers/net/z85230.c +++ b/drivers/net/wan/z85230.c diff --git a/drivers/net/z85230.h b/drivers/net/wan/z85230.h index 0b4b48748..0b4b48748 100644 --- a/drivers/net/z85230.h +++ b/drivers/net/wan/z85230.h diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index 5a29ff459..8acf46f58 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -151,8 +151,6 @@ static void parport_ieee1284_terminate (struct parport *port) { port = port->physport; - port->ieee1284.phase = IEEE1284_PH_TERMINATE; - /* EPP terminates differently. */ switch (port->ieee1284.mode) { case IEEE1284_MODE_EPP: @@ -161,17 +159,42 @@ static void parport_ieee1284_terminate (struct parport *port) /* Terminate from EPP mode. */ /* Event 68: Set nInit low */ - parport_frob_control (port, - PARPORT_CONTROL_INIT, - PARPORT_CONTROL_INIT); + parport_frob_control (port, PARPORT_CONTROL_INIT, 0); udelay (50); /* Event 69: Set nInit high, nSelectIn low */ parport_frob_control (port, - PARPORT_CONTROL_SELECT, - PARPORT_CONTROL_SELECT); + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); break; - + + case IEEE1284_MODE_ECP: + case IEEE1284_MODE_ECPRLE: + case IEEE1284_MODE_ECPSWE: + /* In ECP we can only terminate from fwd idle phase. */ + if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE) { + /* Event 47: Set nInit high */ + parport_frob_control (port, + PARPORT_CONTROL_INIT + | PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_INIT + | PARPORT_CONTROL_AUTOFD); + + /* Event 49: PError goes high */ + parport_wait_peripheral (port, + PARPORT_STATUS_PAPEROUT, + PARPORT_STATUS_PAPEROUT); + + parport_data_forward (port); + DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n", + port->name); + port->ieee1284.phase = IEEE1284_PH_FWD_IDLE; + } + + /* fall-though.. */ + default: /* Terminate from all other modes. */ diff --git a/drivers/parport/ieee1284_ops.c b/drivers/parport/ieee1284_ops.c index 41780752c..4d460a58d 100644 --- a/drivers/parport/ieee1284_ops.c +++ b/drivers/parport/ieee1284_ops.c @@ -9,8 +9,10 @@ * Note: Make no assumptions about hardware or architecture in this file! * * Author: Tim Waugh <tim@cyberelk.demon.co.uk> + * Fixed AUTOFD polarity in ecp_forward_to_reverse(). Fred Barnes, 1999 */ + #include <linux/config.h> #include <linux/parport.h> #include <linux/delay.h> @@ -336,7 +338,7 @@ int ecp_forward_to_reverse (struct parport *port) /* Event 38: Set nAutoFd low */ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, - 0); + PARPORT_CONTROL_AUTOFD); parport_data_reverse (port); udelay (5); @@ -524,12 +526,12 @@ size_t parport_ieee1284_ecp_read_data (struct parport *port, if (count && dev->port->irq != PARPORT_IRQ_NONE) { parport_release (dev); current->state = TASK_INTERRUPTIBLE; - schedule_timeout ((HZ + 99) / 25); + schedule_timeout ((HZ + 24) / 25); parport_claim_or_block (dev); } else /* We must have the device claimed here. */ - parport_wait_event (port, (HZ + 99) / 25); + parport_wait_event (port, (HZ + 24) / 25); /* Is there a signal pending? */ if (signal_pending (current)) @@ -610,10 +612,11 @@ size_t parport_ieee1284_ecp_read_data (struct parport *port, count += rle_count; DPRINTK (KERN_DEBUG "%s: decompressed to %d bytes\n", port->name, rle_count); - } - else + } else { /* Normal data byte. */ - *buf++ = byte, count++; + *buf = byte; + buf++, count++; + } } out: diff --git a/drivers/parport/init.c b/drivers/parport/init.c index 7874519c7..432fede95 100644 --- a/drivers/parport/init.c +++ b/drivers/parport/init.c @@ -91,6 +91,8 @@ static int __init parport_setup (char *str) if (sep++) { if (!strncmp (sep, "auto", 4)) dma[parport_setup_ptr] = PARPORT_DMA_AUTO; + else if (!strncmp (sep, "nofifo", 6)) + dma[parport_setup_ptr] = PARPORT_DMA_NOFIFO; else if (strncmp (sep, "none", 4)) { val = simple_strtoul (sep, &endptr, 0); if (endptr == sep) { @@ -209,6 +211,8 @@ EXPORT_SYMBOL(parport_device_coords); EXPORT_SYMBOL(parport_daisy_deselect_all); EXPORT_SYMBOL(parport_daisy_select); EXPORT_SYMBOL(parport_daisy_init); +EXPORT_SYMBOL(parport_find_device); +EXPORT_SYMBOL(parport_find_class); #endif void inc_parport_count(void) diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 3b5d18127..a451d85b0 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -10,6 +10,7 @@ * * Cleaned up include files - Russell King <linux@arm.uk.linux.org> * DMA support - Bert De Jonghe <bert@sophis.be> + * Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999 */ /* This driver should work with any hardware that is broadly compatible @@ -73,7 +74,12 @@ static void frob_econtrol (struct parport *pb, unsigned char m, unsigned char v) { - outb ((inb (ECONTROL (pb)) & ~m) ^ v, ECONTROL (pb)); + unsigned char ectr = inb (ECONTROL (pb)); +#ifdef DEBUG_PARPORT + printk (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n", + m, v, ectr, (ectr & ~m) ^ v); +#endif + outb ((ectr & ~m) ^ v, ECONTROL (pb)); } #ifdef CONFIG_PARPORT_PC_FIFO @@ -94,11 +100,8 @@ static int change_mode(struct parport *p, int m) oecr = inb (ecr); mode = (oecr >> 5) & 0x7; if (mode == m) return 0; - if (mode && m) - /* We have to go through mode 000 */ - change_mode (p, ECR_SPP); - if (m < 2 && !(parport_read_control (p) & 0x20)) { + if (mode >= 2 && !(priv->ctr & 0x20)) { /* This mode resets the FIFO, so we may * have to wait for it to drain first. */ long expire = jiffies + p->physport->cad->timeout; @@ -127,6 +130,13 @@ static int change_mode(struct parport *p, int m) } } + if (mode >= 2 && m >= 2) { + /* We have to go through mode 001 */ + oecr &= ~(7 << 5); + oecr |= ECR_PS2 << 5; + outb (oecr, ecr); + } + /* Set the mode. */ oecr &= ~(7 << 5); oecr |= m << 5; @@ -160,11 +170,11 @@ static int get_fifo_residue (struct parport *p) residue); /* Reset the FIFO. */ - frob_econtrol (p, 0xe0, 0x20); + frob_econtrol (p, 0xe0, ECR_PS2 << 5); parport_frob_control (p, PARPORT_CONTROL_STROBE, 0); /* Now change to config mode and clean up. FIXME */ - frob_econtrol (p, 0xe0, 0xe0); + frob_econtrol (p, 0xe0, ECR_CNF << 5); cnfga = inb (CONFIGA (p)); printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga); @@ -177,7 +187,7 @@ static int get_fifo_residue (struct parport *p) * PWord != 1 byte. */ /* Back to PS2 mode. */ - frob_econtrol (p, 0xe0, 0x20); + frob_econtrol (p, 0xe0, ECR_PS2 << 5); return residue; } @@ -209,9 +219,9 @@ static int clear_epp_timeout(struct parport *pb) /* * Access functions. * - * These aren't static because they may be used by the parport_xxx_yyy - * macros. extern __inline__ versions of several of these are in - * parport_pc.h. + * Most of these aren't static because they may be used by the + * parport_xxx_yyy macros. extern __inline__ versions of several + * of these are in parport_pc.h. */ static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -229,21 +239,6 @@ unsigned char parport_pc_read_data(struct parport *p) return inb (DATA (p)); } -unsigned char __frob_control (struct parport *p, unsigned char mask, - unsigned char val) -{ - const unsigned char wm = (PARPORT_CONTROL_STROBE | - PARPORT_CONTROL_AUTOFD | - PARPORT_CONTROL_INIT | - PARPORT_CONTROL_SELECT); - struct parport_pc_private *priv = p->physport->private_data; - unsigned char ctr = priv->ctr; - ctr = (ctr & ~mask) ^ val; - ctr &= priv->ctr_writable; /* only write writable bits. */ - outb (ctr, CONTROL (p)); - return priv->ctr = ctr & wm; /* update soft copy */ -} - void parport_pc_write_control(struct parport *p, unsigned char d) { const unsigned char wm = (PARPORT_CONTROL_STROBE | @@ -253,18 +248,22 @@ void parport_pc_write_control(struct parport *p, unsigned char d) /* Take this out when drivers have adapted to the newer interface. */ if (d & 0x20) { - printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", - p->name, p->cad->name); - parport_pc_data_reverse (p); + printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", + p->name, p->cad->name); + parport_pc_data_reverse (p); } - __frob_control (p, wm, d & wm); + __parport_pc_frob_control (p, wm, d & wm); } unsigned char parport_pc_read_control(struct parport *p) { + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); const struct parport_pc_private *priv = p->physport->private_data; - return priv->ctr; /* Use soft copy */ + return priv->ctr & wm; /* Use soft copy */ } unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask, @@ -277,16 +276,20 @@ unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask, /* Take this out when drivers have adapted to the newer interface. */ if (mask & 0x20) { - printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", - p->name, p->cad->name); + printk (KERN_DEBUG "%s (%s): use data_%s for this!\n", + p->name, p->cad->name, + (val & 0x20) ? "reverse" : "forward"); + if (val & 0x20) parport_pc_data_reverse (p); + else + parport_pc_data_forward (p); } /* Restrict mask and val to control lines. */ mask &= wm; val &= wm; - return __frob_control (p, mask, val); + return __parport_pc_frob_control (p, mask, val); } unsigned char parport_pc_read_status(struct parport *p) @@ -296,22 +299,22 @@ unsigned char parport_pc_read_status(struct parport *p) void parport_pc_disable_irq(struct parport *p) { - __frob_control (p, 0x10, 0); + __parport_pc_frob_control (p, 0x10, 0); } void parport_pc_enable_irq(struct parport *p) { - __frob_control (p, 0x10, 0x10); + __parport_pc_frob_control (p, 0x10, 0x10); } void parport_pc_data_forward (struct parport *p) { - __frob_control (p, 0x20, 0); + __parport_pc_frob_control (p, 0x20, 0); } void parport_pc_data_reverse (struct parport *p) { - __frob_control (p, 0x20, 0x20); + __parport_pc_frob_control (p, 0x20, 0x20); } void parport_pc_init_state(struct pardevice *dev, struct parport_state *s) @@ -469,7 +472,7 @@ static size_t parport_pc_fifo_write_block_pio (struct parport *port, frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */ /* Forward mode. */ - parport_pc_data_forward (port); + parport_pc_data_forward (port); /* Must be in PS2 mode */ while (left) { unsigned char byte; @@ -559,7 +562,7 @@ static size_t parport_pc_fifo_write_block_dma (struct parport *port, frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */ /* Forward mode. */ - parport_pc_data_forward (port); + parport_pc_data_forward (port); /* Must be in PS2 mode */ while (left) { long expire = jiffies + port->physport->cad->timeout; @@ -656,8 +659,8 @@ size_t parport_pc_compat_write_block_pio (struct parport *port, length, flags); /* Set up parallel port FIFO mode.*/ + parport_pc_data_forward (port); /* Must be in PS2 mode */ change_mode (port, ECR_PPF); /* Parallel port FIFO */ - parport_pc_data_forward (port); port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; /* Write the data to the FIFO. */ @@ -687,8 +690,8 @@ size_t parport_pc_compat_write_block_pio (struct parport *port, outb (0, FIFO (port)); } - /* Reset the FIFO. */ - frob_econtrol (port, 0xe0, 0); + /* Reset the FIFO and return to PS2 mode. */ + frob_econtrol (port, 0xe0, ECR_PS2 << 5); /* De-assert strobe. */ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0); @@ -727,8 +730,8 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, } /* Set up ECP parallel port mode.*/ + parport_pc_data_forward (port); /* Must be in PS2 mode */ change_mode (port, ECR_ECP); /* ECP FIFO */ - parport_pc_data_forward (port); port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; /* Write the data to the FIFO. */ @@ -758,17 +761,20 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, outb (0, FIFO (port)); } - /* Reset the FIFO. */ - frob_econtrol (port, 0xe0, 0); + /* Reset the FIFO and return to PS2 mode. */ + frob_econtrol (port, 0xe0, ECR_PS2 << 5); + + /* De-assert strobe. */ parport_frob_control (port, PARPORT_CONTROL_STROBE, 0); /* Host transfer recovery. */ + parport_pc_data_reverse (port); /* Must be in PS2 mode */ + udelay (5); + parport_frob_control (port, PARPORT_CONTROL_INIT, 0); + parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); parport_frob_control (port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); - parport_pc_data_reverse (port); - parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); - parport_frob_control (port, PARPORT_CONTROL_INIT, 0); parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, PARPORT_STATUS_PAPEROUT); @@ -819,21 +825,21 @@ size_t parport_pc_ecp_read_block_pio (struct parport *port, parport_frob_control (port, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); - parport_pc_data_reverse (port); + parport_pc_data_reverse (port); /* Must be in PS2 mode */ udelay (5); /* Event 39: Set nInit low to initiate bus reversal */ parport_frob_control (port, PARPORT_CONTROL_INIT, - PARPORT_CONTROL_INIT); + 0); /* Event 40: PError goes low */ parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0); } /* Set up ECP parallel port mode.*/ + parport_pc_data_reverse (port); /* Must be in PS2 mode */ change_mode (port, ECR_ECP); /* ECP FIFO */ - parport_pc_data_reverse (port); port->ieee1284.phase = IEEE1284_PH_REV_DATA; /* Do the transfer. */ @@ -1054,7 +1060,6 @@ static int __maybe_init parport_ECR_present(struct parport *pb) struct parport_pc_private *priv = pb->private_data; unsigned char r = 0xc; - priv->ecr = 0; outb (r, CONTROL (pb)); if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) { outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */ @@ -1120,9 +1125,9 @@ static int __maybe_init parport_PS2_supported(struct parport *pb) /* cancel input mode */ parport_pc_data_forward (pb); - if (ok) + if (ok) { pb->modes |= PARPORT_MODE_TRISTATE; - else { + } else { struct parport_pc_private *priv = pb->private_data; priv->ctr_writable &= ~0x20; } @@ -1180,8 +1185,8 @@ static int __maybe_init parport_ECP_supported(struct parport *pb) priv->writeIntrThreshold = i; /* Find out readIntrThreshold */ - frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO */ - parport_pc_data_reverse (pb); + frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO and enable PS2 */ + parport_pc_data_reverse (pb); /* Must be in PS2 mode */ frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */ frob_econtrol (pb, 1<<2, 1<<2); frob_econtrol (pb, 1<<2, 0); @@ -1544,12 +1549,10 @@ struct parport *__maybe_init parport_pc_probe_port (unsigned long int base, if (base_hi && !check_region(base_hi,3)) { parport_ECR_present(p); parport_ECP_supported(p); - parport_ECPPS2_supported(p); } if (base != 0x3bc) { if (!check_region(base+0x3, 5)) { - parport_EPP_supported(p); - if (!(p->modes & PARPORT_MODE_EPP)) + if (!parport_EPP_supported(p)) parport_ECPEPP_supported(p); } } @@ -1558,8 +1561,10 @@ struct parport *__maybe_init parport_pc_probe_port (unsigned long int base, kfree (priv); return NULL; } - - parport_PS2_supported (p); + if (priv->ecr) + parport_ECPPS2_supported(p); + else + parport_PS2_supported (p); if (!(p = parport_register_port(base, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, ops))) { @@ -1599,7 +1604,8 @@ struct parport *__maybe_init parport_pc_probe_port (unsigned long int base, p->dma = PARPORT_DMA_NONE; #ifdef CONFIG_PARPORT_PC_FIFO - if (priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) { + if (p->dma != PARPORT_DMA_NOFIFO && + priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) { p->ops->compat_write_data = parport_pc_compat_write_block_pio; #ifdef CONFIG_PARPORT_1284 p->ops->ecp_write_data = parport_pc_ecp_write_block_pio; @@ -1672,9 +1678,10 @@ struct parport *__maybe_init parport_pc_probe_port (unsigned long int base, /* Done probing. Now put the port into a sensible start-up state. * SELECT | INIT also puts IEEE1284-compliant devices into * compatibility mode. */ - if (p->modes & PARPORT_MODE_ECP) + if (priv->ecr) /* * Put the ECP detected port in PS2 mode. + * Do this also for ports that have ECR but don't do ECP. */ outb (0x34, ECONTROL (p)); @@ -1751,6 +1758,8 @@ static int __init parport_pc_init_pci (int irq, int dma) { { 0, -1 }, } }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1, { { 0, -1 }, } }, + { PCI_VENDOR_ID_EXSYS, PCI_DEVICE_ID_EXSYS_4014, 2, + { { 2, -1 }, { 3, -1 }, } }, { 0, } }; diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 94b9f035b..7c127fd18 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -633,7 +633,7 @@ void parport_release(struct pardevice *dev) } static int parport_parse_params (int nports, const char *str[], int val[], - int automatic, int none) + int automatic, int none, int nofifo) { unsigned int i; for (i = 0; i < nports && str[i]; i++) { @@ -641,6 +641,8 @@ static int parport_parse_params (int nports, const char *str[], int val[], val[i] = automatic; else if (!strncmp(str[i], "none", 4)) val[i] = none; + else if (nofifo && !strncmp(str[i], "nofifo", 4)) + val[i] = nofifo; else { char *ep; unsigned long r = simple_strtoul(str[i], &ep, 0); @@ -659,11 +661,11 @@ static int parport_parse_params (int nports, const char *str[], int val[], int parport_parse_irqs(int nports, const char *irqstr[], int irqval[]) { return parport_parse_params (nports, irqstr, irqval, PARPORT_IRQ_AUTO, - PARPORT_IRQ_NONE); + PARPORT_IRQ_NONE, 0); } int parport_parse_dmas(int nports, const char *dmastr[], int dmaval[]) { return parport_parse_params (nports, dmastr, dmaval, PARPORT_DMA_AUTO, - PARPORT_DMA_NONE); + PARPORT_DMA_NONE, PARPORT_DMA_NOFIFO); } diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 45197e4c3..0503f4af1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -25,6 +25,10 @@ ifdef CONFIG_PROC_FS L_OBJS += proc.o endif -L_OBJS += compat.o quirks.o names.o syscall.o setup.o +L_OBJS += compat.o quirks.o names.o helper.o + +ifndef CONFIG_X86 +L_OBJS += syscall.o setup.o +endif include $(TOPDIR)/Rules.make diff --git a/drivers/pci/devlist.h b/drivers/pci/devlist.h index f0a86af6d..3b4cce5ce 100644 --- a/drivers/pci/devlist.h +++ b/drivers/pci/devlist.h @@ -989,6 +989,10 @@ VENDOR( ATRONICS, "Atronics" ) DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL") ENDVENDOR() +VENDOR( EXSYS, "Exsys" ) + DEVICE( EXSYS, EXSYS_4014, "EX-4014") +ENDVENDOR() + VENDOR( TIGERJET, "TigerJet" ) DEVICE( TIGERJET, TIGERJET_300, "Tiger300 ISDN") ENDVENDOR() diff --git a/drivers/pci/helper.c b/drivers/pci/helper.c new file mode 100644 index 000000000..928cec4b5 --- /dev/null +++ b/drivers/pci/helper.c @@ -0,0 +1,69 @@ +/* + * $Id$ + * + * drivers/pci/helper.c + * + * Copyright 1999 Jeff Garzik <jgarzik@pobox.com> + * This software is free. See the file COPYING for licensing details. + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/pci.h> + + +int pci_simple_probe (struct pci_simple_probe_entry *list, size_t match_limit, + pci_simple_probe_callback cb, void *drvr_data) +{ + struct pci_dev *dev; + struct pci_simple_probe_entry *ent; + size_t matches = 0; + unsigned short vendor, device; + int rc; + + if (!list || !cb) + return -1; + + dev = pci_find_device (PCI_ANY_ID, PCI_ANY_ID, NULL); + while (dev) { + ent = list; + while (ent->vendor && ent->device) { + vendor = ent->vendor; + device = ent->device; + + if (((vendor != 0xFFFF) && + (vendor != dev->vendor)) || + ((device != 0xFFFF) && + (device != dev->device))) { + ent++; + continue; + } + + if (((ent->subsys_vendor) && + (ent->subsys_vendor != dev->subsystem_vendor)) || + ((ent->subsys_device) && + (ent->subsys_device != dev->subsystem_device))) { + ent++; + continue; + } + + rc = (* cb) (dev, matches, ent, drvr_data); + if (rc < 0) + return rc; + + matches++; + + if (match_limit && match_limit == matches) + return matches; + + ent++; + } + + dev = pci_find_device (PCI_ANY_ID, PCI_ANY_ID, dev); + } + + return matches; +} + + diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 51c229c25..5cf991521 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -17,7 +17,6 @@ #include <linux/malloc.h> #include <linux/ioport.h> -#include <asm/pci.h> #include <asm/page.h> #undef DEBUG @@ -125,26 +124,20 @@ pci_find_parent_resource(struct pci_dev *dev, struct resource *res) int i; struct resource *best = NULL; - while (bus) { - for(i=0; i<4; i++) { - struct resource *r = bus->resource[i]; - if (!r) - continue; - if (res->start && !(res->start >= r->start && res->end <= r->end)) - continue; /* Not contained */ - if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) - continue; /* Wrong type */ - if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) - return r; /* Exact match */ - if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH)) - best = r; /* Approximating prefetchable by non-prefetchable */ - } - if (best) - return best; - bus = bus->parent; + for(i=0; i<4; i++) { + struct resource *r = bus->resource[i]; + if (!r) + continue; + if (res->start && !(res->start >= r->start && res->end <= r->end)) + continue; /* Not contained */ + if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) + continue; /* Wrong type */ + if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) + return r; /* Exact match */ + if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH)) + best = r; /* Approximating prefetchable by non-prefetchable */ } - printk(KERN_ERR "PCI: Bug: Parent resource not found!\n"); - return NULL; + return best; } @@ -193,47 +186,18 @@ pci_set_master(struct pci_dev *dev) pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_MASTER)) { - printk("PCI: Enabling bus mastering for device %s\n", dev->name); + printk("PCI: Enabling bus mastering for device %s\n", dev->slot_name); cmd |= PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); } pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); if (lat < 16) { - printk("PCI: Increasing latency timer of device %s to 64\n", dev->name); + printk("PCI: Increasing latency timer of device %s to 64\n", dev->slot_name); pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); } } /* - * Assign new address to PCI resource. We hope our resource information - * is complete. On the PC, we don't re-assign resources unless we are - * forced to do so or the driver asks us to. - * - * Expects start=0, end=size-1, flags=resource type. - */ -int __init pci_assign_resource(struct pci_dev *dev, int i) -{ - struct resource *r = &dev->resource[i]; - struct resource *pr = pci_find_parent_resource(dev, r); - unsigned long size = r->end + 1; - - if (!pr) - return -EINVAL; - if (r->flags & IORESOURCE_IO) { - if (size > 0x100) - return -EFBIG; - if (allocate_resource(pr, r, size, 0x1000, ~0, 1024)) - return -EBUSY; - } else { - if (allocate_resource(pr, r, size, 0x10000000, ~0, size)) - return -EBUSY; - } - if (i < 6) - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4*i, r->start); - return 0; -} - -/* * Translate the low bits of the PCI base * to the resource type */ @@ -296,7 +260,7 @@ void __init pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) res->end = res->start + (((unsigned long) ~l) << 32); #else if (l) { - printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->name); + printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); res->start = 0; res->flags = 0; continue; @@ -305,6 +269,7 @@ void __init pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } if (rom) { + dev->rom_base_reg = rom; res = &dev->resource[PCI_ROM_RESOURCE]; pci_read_config_dword(dev, rom, &l); pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE); @@ -324,8 +289,9 @@ void __init pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) pci_write_config_word(dev, PCI_COMMAND, cmd); } -void __init pci_read_bridge_bases(struct pci_dev *dev, struct pci_bus *child) +void __init pci_read_bridge_bases(struct pci_bus *child) { + struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; u16 mem_base_lo, mem_limit_lo, io_base_hi, io_limit_hi; u32 mem_base_hi, mem_limit_hi; @@ -333,6 +299,9 @@ void __init pci_read_bridge_bases(struct pci_dev *dev, struct pci_bus *child) struct resource *res; int i; + if (!dev) /* It's a host bus, nothing to read */ + return; + for(i=0; i<3; i++) child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; @@ -425,31 +394,27 @@ static unsigned int __init pci_do_scan_bus(struct pci_bus *bus) dev_cache = NULL; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; - sprintf(dev->name, "%02x:%02x.%d", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); + sprintf(dev->slot_name, "%02x:%02x.%d", bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); pci_name_device(dev); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); class >>= 8; /* upper 3 bytes */ dev->class = class; class >>= 8; - dev->hdr_type = hdr_type; + dev->hdr_type = hdr_type & 0x7f; - switch (hdr_type & 0x7f) { /* header type */ + switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) goto bad; /* - * If the card generates interrupts, read IRQ number - * (some architectures change it during pcibios_fixup()) + * Read interrupt line and base address registers. + * The architecture-dependent code can tweak these, of course. */ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); if (irq) pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); dev->irq = irq; - /* - * read base address registers, again pcibios_fixup() can - * tweak these - */ pci_read_bases(dev, 6, PCI_ROM_ADDRESS); pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); @@ -468,8 +433,8 @@ static unsigned int __init pci_do_scan_bus(struct pci_bus *bus) break; default: /* unknown header */ bad: - printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n", - bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type); + printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n", + dev->slot_name, hdr_type); continue; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ec8fcb7a8..d25a0ed29 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -31,7 +31,7 @@ static void __init quirk_passive_release(struct pci_dev *dev) while ((d = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) { pci_read_config_byte(d, 0x82, &dlc); if (!(dlc & 1<<1)) { - printk("PCI: PIIX3: Enabling Passive Release on %s\n", d->name); + printk("PCI: PIIX3: Enabling Passive Release on %s\n", d->slot_name); dlc |= 1<<1; pci_write_config_byte(d, 0x82, dlc); } @@ -99,7 +99,7 @@ static void pci_do_fixups(struct pci_dev *dev, int pass, struct pci_fixup *f) (f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { #ifdef DEBUG - printk("PCI: Calling quirk %p for %s\n", f->hook, dev->name); + printk("PCI: Calling quirk %p for %s\n", f->hook, dev->slot_name); #endif f->hook(dev); } diff --git a/drivers/pci/setup.c b/drivers/pci/setup.c index 53bf9f582..9c752d0de 100644 --- a/drivers/pci/setup.c +++ b/drivers/pci/setup.c @@ -16,7 +16,6 @@ #include <linux/ioport.h> #include <asm/cache.h> -#include <asm/pci.h> #define DEBUG_CONFIG 0 @@ -108,9 +107,12 @@ pdev_assign_unassigned_resources(struct pci_dev *dev, u32 min_io, u32 min_mem) (ie. do not respond to memory space writes) when it is left enabled. A good example are QlogicISP adapters. */ - pci_read_config_dword(dev, PCI_ROM_ADDRESS, ®); - reg &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(dev, PCI_ROM_ADDRESS, reg); + if (dev->rom_base_reg) { + pci_read_config_dword(dev, dev->rom_base_reg, ®); + reg &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, dev->rom_base_reg, reg); + dev->resource[PCI_ROM_RESOURCE].flags &= ~PCI_ROM_ADDRESS_ENABLE; + } /* All of these (may) have I/O scattered all around and may not use I/O base address registers at all. So we just have to diff --git a/drivers/pnp/Config.in b/drivers/pnp/Config.in index a350c4526..de0fa6cfa 100644 --- a/drivers/pnp/Config.in +++ b/drivers/pnp/Config.in @@ -6,6 +6,6 @@ comment 'Plug and Play configuration' tristate 'Plug and Play support' CONFIG_PNP -dep_tristate 'ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP +dep_tristate ' ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP endmenu diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c index dc34fe4cd..3857c487d 100644 --- a/drivers/sbus/char/pcikbd.c +++ b/drivers/sbus/char/pcikbd.c @@ -764,7 +764,7 @@ void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) queue->head = head; aux_ready = 1; if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); + kill_fasync(queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); } diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index e8e6a168c..2b3a99604 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -1273,7 +1273,7 @@ push_kbd (int scan) kbd_head = next; } if (kb_fasync) - kill_fasync (kb_fasync, SIGIO); + kill_fasync (kb_fasync, SIGIO, POLL_IN); wake_up_interruptible (&kbd_wait); } diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 77fe2ee77..c3d8462e6 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -137,7 +137,7 @@ push_char (char c) } sunmouse.ready = 1; if (sunmouse.fasync) - kill_fasync (sunmouse.fasync, SIGIO); + kill_fasync (sunmouse.fasync, SIGIO, POLL_IN); wake_up_interruptible (&sunmouse.proc_list); } @@ -334,7 +334,7 @@ sun_mouse_inbyte(unsigned char byte) */ sunmouse.ready = 1; if (sunmouse.fasync) - kill_fasync (sunmouse.fasync, SIGIO); + kill_fasync (sunmouse.fasync, SIGIO, POLL_IN); wake_up_interruptible(&sunmouse.proc_list); } return; diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index b11de29eb..ccf2268d8 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -92,7 +92,7 @@ static struct miscdevice uctrl_dev = { #ifdef MODULE int init_module(void) #else -__initfunc(int uctrl_init(void)) +int __init uctrl_init(void) #endif { struct uctrl_driver *driver = &drv; diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx index d5fbbd59a..d543e5199 100644 --- a/drivers/scsi/ChangeLog.ncr53c8xx +++ b/drivers/scsi/ChangeLog.ncr53c8xx @@ -1,3 +1,33 @@ +Sat Sep 11 18:00 1999 Gerard Roudier (groudier@club-internet.fr) + * revision 3.2c + - Handle correctly (hopefully) jiffies wrap-around. + - Restore the entry used to detect 875 until revision 0xff. + (I removed it inadvertently, it seems :) ) + - Replace __initfunc() which is deprecated stuff by __init which + is not yet so. ;-) + - Add support of some 'resource handling' for linux-2.3.13. + Basically the BARs have been changed to something more complex + in the pci_dev structure. + - Remove some deprecated code. + +Sat May 10 11:00 1999 Gerard Roudier (groudier@club-internet.fr) + * revision pre-3.2b-1 + - Support for the 53C895A by Pamela Delaney <pam.delaney@lsil.com> + The 53C895A contains all of the features of the 896 but has only + one channel and has a 32 bit PCI bus. It does 64 bit PCI addressing + using dual cycle PCI data transfers. + - Miscellaneous minor fixes. + - Some additions to the README.ncr53c8xx file. + +Sun Apr 11 10:00 1999 Gerard Roudier (groudier@club-internet.fr) + * revision 3.2a + - Add 'hostid:#id' boot option. This option allows to change the + default SCSI id the driver uses for controllers. + - Remove nvram layouts and driver set-up structures from the C source, + and use the one defined in sym53c8xx_defs.h file. + (shared by both drivers). + - Set for now MAX LUNS to 16 (instead of 8). + Thu Mar 11 23:00 1999 Gerard Roudier (groudier@club-internet.fr) * revision 3.2 (8xx-896 driver bundle) - Only define the host template in ncr53c8xx.h and include the diff --git a/drivers/scsi/ChangeLog.sym53c8xx b/drivers/scsi/ChangeLog.sym53c8xx index 98656d359..260c6b5aa 100644 --- a/drivers/scsi/ChangeLog.sym53c8xx +++ b/drivers/scsi/ChangeLog.sym53c8xx @@ -1,3 +1,132 @@ +Sat Sep 11 11:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5e + - New linux-2.3.13 __setup scheme support added. + - Cleanup of the extended error status handling: + Use 1 bit per error type. + - Also save the extended error status prior to auto-sense. + - Add the FE_DIFF chip feature bit to indicate support of + diff probing from GPIO3 (825/825A/876/875). + - Remove the quirk handling that has been useless since day one. + - Work-around PCI chips being reported twice on some platforms. + - Add some redundant PCI reads in order to deal with common + bridge misbehaviour regarding posted write flushing. + - Add some other conditionnal code for people who have to deal + with really broken bridges (they will have to edit a source + file to try these options). + - Handle correctly (hopefully) jiffies wrap-around. + - Restore the entry used to detect 875 until revision 0xff. + (I removed it inadvertently, it seems :) ) + - Replace __initfunc() which is deprecated stuff by __init which + is not yet so. ;-) + - Rewrite the MESSAGE IN scripts more generic by using a MOVE + table indirect. Extended messages of any size are accepted now. + (Size is limited to 8 for now, but a constant is just to be + increased if necessary) + - Fix some bug in the fully untested MDP handling:) and share + some code between MDP handling and residual calculation. + - Calculate the data transfer residual as the 2's complement + integer (A positive value in returned on data overrun, and + a negative one on underrun). + - Add support of some 'resource handling' for linux-2.3.13. + Basically the BARs have been changed to something more complex + in the pci_dev structure. + - Remove some deprecated code. + +Sat Jun 5 11:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5c + - Donnot negotiate on auto-sense if we are currently using 8 bit + async transfer for the target. + - Only check for SISL/RAID on i386 platforms. + (A problem has been reported on PPC with that code). + - On MSG REJECT for a negotiation, the driver attempted to restart + the SCRIPT processor when this one was already running. + +Sat May 29 12:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5b + - Force negotiation prior auto-sense. + This ensures that the driver will be able to grab the sense data + from a device that has received a BUS DEVICE RESET message from + another initiator. + - Complete all disconnected CCBs for a logical UNIT if we are told + about a UNIT ATTENTION for a RESET condition by this target. + - Add the control command 'cleardev' that allows to send a ABORT + message to a logical UNIT (for test purpose). + +Tue May 25 23:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5a + - Add support for task abort and bus device reset SCSI message + and implement proper synchonisation with SCRIPTS to handle + correctly task abortion without races. + - Send an ABORT message (if untagged) or ABORT TAG message (if tagged) + when the driver is told to abort a command that is disconnected and + complete the command with appropriate error. + If the aborted command is not yet started, remove it from the start + queue and complete it with error. + - Add the control command 'resetdev' that allows to send a BUS + DEVICE RESET message to a target (for test purpose). + - Clean-up some unused or useless code. + +Fri May 21 23:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.5 + - Add support for CHMOV with Wide controllers. + - Handling of the SWIDE (low byte residue at the end of a CHMOV + in DATA IN phase with WIDE transfer when the byte count gets odd). + - Handling of the IGNORE WIDE RESIDUE message. + Handled from SCRIPTS as possible with some optimizations when both + a wide device and the controller are odd at the same time (SWIDE + present and IGNORE WIDE RESIDUE message on the BUS at the same time). + - Check against data OVERRUN/UNDERRUN condition at the end of a data + transfer, whatever a SWIDE is present (OVERRUN in DATA IN phase) + or the SODL is full (UNDERRUN in DATA out phase). + - Handling of the MODIFY DATA POINTER message. + This one cannot be handled from SCRIPTS, but hopefully it will not + happen very often. :) + - Large rewrite of the SCSI MESSAGE handling. + +Sun May 9 11:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.4 + - Support for IMMEDIATE ARBITRATION. + See the README file for detailed information about this feature. + Requires both a compile option and a boot option. + - Minor SCRIPTS optimization in reselection pattern for LUN 0. + - Simpler algorithm to deal with SCSI command starvation. + Just use 2 tag counters in flip/flop and switch to the other + one every 3 seconds. + - Do some work in SCRIPTS after the SELECT instruction and prior + to testing for a PHASE. SYMBIOS say this feature is working fine. + (Btw, only problems with Toshiba 3401B had been reported). + - Measure the PCI clock speed and donnot attach controllers if + result is greater than 37 MHz. Since the precision of the + algorithm (from Stefan Esser) is better than 2%, this should + be fine. + - Fix the misdetection of SYM53C875E (was detected as a 876). + - Fix the misdetection of SYM53C810 not A (was detected as a 810A). + - Support for up to 256 TAGS per LUN (CMD_PER_LUN). + Currently limited to 255 due to Linux limitation. :) + - Support for up to 508 active commands (CAN_QUEUE). + - Support for the 53C895A by Pamela Delaney <pam.delaney@lsil.com> + The 53C895A contains all of the features of the 896 but has only + one channel and has a 32 bit PCI bus. It does 64 bit PCI addressing + using dual cycle PCI data transfers. + - Miscellaneous minor fixes. + - Some additions to the README.ncr53c8xx file. + +Tue Apr 15 10:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.3e + - Support for any number of LUNs (64) (SPI2-compliant). + (Btw, this may only be ever usefull under linux-2.2 ;-)) + +Sun Apr 11 10:00 1999 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.3d + - Add 'hostid:#id' boot option. This option allows to change the + default SCSI id the driver uses for controllers. + - Make SCRIPTS not use self-mastering for PCI. + There were still 2 places the driver used this feature of the + 53C8XX family. + - Move some data structures (nvram layouts and driver set-up) to + the sym53c8xx_defs.h file. So, the both drivers will share them. + - Set MAX LUNS to 16 (instead of 8). + Sat Mar 20 21:00 1999 Gerard Roudier (groudier@club-internet.fr) * version sym53c8xx-1.3b - Add support for NCR PQS PDS. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index abb2af5ee..15ba33c8f 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -129,6 +129,14 @@ else endif endif +ifeq ($(CONFIG_SCSI_SIM710),y) +L_OBJS += sim710.o +else + ifeq ($(CONFIG_SCSI_SIM710),m) + M_OBJS += sim710.o + endif +endif + ifeq ($(CONFIG_A4000T_SCSI),y) L_OBJS += amiga7xx.o 53c7xx.o else @@ -685,6 +693,17 @@ include $(TOPDIR)/Rules.make 53c7xx.o : 53c7xx_d.h +sim710_d.h: sim710.scr script_asm.pl + ln -sf sim710.scr fake7.c + $(CPP) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family + mv script.h sim710_d.h + mv scriptu.h sim710_u.h + rm fake7.c + +sim710_u.h: sim710_d.h + +sim710.o : sim710_d.h + initio.o: ini9100u.c i91uscsi.c $(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o $(CC) $(CFLAGS) -c i91uscsi.c -o i91uscsi.o diff --git a/drivers/scsi/README.aic7xxx b/drivers/scsi/README.aic7xxx index 0497bbb10..100f9ff95 100644 --- a/drivers/scsi/README.aic7xxx +++ b/drivers/scsi/README.aic7xxx @@ -20,11 +20,13 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD AHA-2920C AHA-2930 AHA-2930U + AHA-2930CU AHA-2930U2 AHA-2940 AHA-2940W AHA-2940U AHA-2940UW + AHA-2940UW-PRO AHA-2940AU AHA-2940U2W AHA-2940U2 @@ -37,6 +39,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD AHA-2950U2 AHA-2950U2W AHA-2950U2B + AHA-29160M AHA-3940 AHA-3940U AHA-3940W @@ -45,6 +48,8 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD AHA-3940U2W AHA-3950U2B AHA-3950U2D + AHA-3960D + AHA-39160M AHA-3985 AHA-3985U AHA-3985W @@ -159,6 +164,12 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD on your controller. This helps those people who have cards without a SEEPROM make sure that linux and all other operating systems think the same way about your hard drives. + + "aic7xxx=scbram" - Some cards have external SCB RAM that can be used to + give the card more hardware SCB slots. This allows the driver to use + that SCB RAM. Without this option, the driver won't touch the SCB + RAM because it is known to cause problems on a few cards out there + (such as 3985 class cards). "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel to use the correct IRQ type for your card. This only applies to EISA @@ -464,10 +475,9 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD FTP sites ------------------------------ ftp://ftp.redhat.com/pub/aic/ - - Primary site for Doug Ledford developed driver releases - ftp://ftp.dialnet.net/pub/linux/aic7xxx - - Temporary mirror of the redhat.com ftp site while people - get used to the new address + - Out of date. I used to keep stuff here, but too many people + complained about having a hard time getting into Red Hat's ftp + server. So use the web site below instead. ftp://ftp.pcnet.com/users/eischen/Linux/ - Dan Eischen's driver distribution area ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ @@ -475,10 +485,8 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD Web sites ------------------------------ - http://developer.redhat.com/aic7xxx/ - - Primary web site maintained by Doug Ledford. I haven't actually - put anything up yet....but I'm planning on it. This information - is put here as an add for the vapor page :) + http://people.redhat.com/dledford/aic7xxx.html + - Primary web site maintained by Doug Ledford. Dean W. Gehnert deang@teleport.com diff --git a/drivers/scsi/README.ncr53c8xx b/drivers/scsi/README.ncr53c8xx index 2fa1956a2..858673c33 100644 --- a/drivers/scsi/README.ncr53c8xx +++ b/drivers/scsi/README.ncr53c8xx @@ -1,10 +1,10 @@ -The Linux NCR53C8XX driver README file +The Linux NCR53C8XX/SYM53C8XX drivers README file Written by Gerard Roudier <groudier@club-internet.fr> 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -10 March 1999 +29 May 1999 =============================================================================== 1. Introduction @@ -25,14 +25,39 @@ Written by Gerard Roudier <groudier@club-internet.fr> 8.6 Clear profile counters 8.7 Set flag (no_disc) 8.8 Set verbose level + 8.9 Reset all logical units of a target + 8.10 Abort all tasks of all logical units of a target 9. Configuration parameters 10. Boot setup commands 10.1 Syntax 10.2 Available arguments + 10.2.1 Master parity checking + 10.2.2 Scsi parity checking + 10.2.3 Scsi disconnections + 10.2.4 Special features + 10.2.5 Ultra SCSI support + 10.2.6 Default number of tagged commands + 10.2.7 Default synchronous period factor + 10.2.8 Negotiate synchronous with all devices + 10.2.9 Verbosity level + 10.2.10 Debug mode + 10.2.11 Burst max + 10.2.12 LED support + 10.2.13 Max wide + 10.2.14 Differential mode + 10.2.15 IRQ mode + 10.2.16 Reverse probe + 10.2.17 Fix up PCI configuration space + 10.2.18 Serial NVRAM + 10.2.19 Check SCSI BUS + 10.2.20 Exclude a host from being attached + 10.2.21 Suggest a default SCSI id for hosts + 10.2.22 Enable use of IMMEDIATE ARBITRATION 10.3 Advised boot setup commands 10.4 PCI configuration fix-up boot option 10.5 Serial NVRAM support boot option 10.6 SCSI BUS checking boot option + 10.7 IMMEDIATE ARBITRATION boot option 11. Some constants and flags of the ncr53c8xx.h header file 12. Installation 13. Architecture dependent features @@ -43,6 +68,8 @@ Written by Gerard Roudier <groudier@club-internet.fr> 14.4 Possible data corruption during a Memory Write and Invalidate 14.5 IRQ sharing problems 15. SCSI problem troubleshooting + 15.1 Problem tracking + 15.2 Understanding hardware error reports 16. Synchonous transfer negotiation tables 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers 16.2 Synchronous timings for fast SCSI-2 53C8XX controllers @@ -69,11 +96,12 @@ The original driver has been written for 386bsd and FreeBSD by: It is now available as a bundle of 2 drivers: - ncr53c8xx generic driver that supports all the SYM53C8XX family including - the ealiest 810 rev. 1 and the latest 896 2 channels LVD SCSI controller. + the ealiest 810 rev. 1, the latest 896 (2 channel LVD SCSI controller) and + the new 895A (1 channel LVD SCSI controller). - sym53c8xx enhanced driver (a.k.a. 896 drivers) that drops support of oldest chips in order to gain advantage of new features, as LOAD/STORE intructions available since the 810A and hardware phase mismatch available with the - latest 896. + 896 and the 895A. You can find technical information about the NCR 8xx family in the PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by @@ -95,15 +123,17 @@ Usefull SCSI tools written by Eric Youngdale are available at tsx-11: These tools are not ALPHA but quite clean and work quite well. It is essential you have the 'scsiinfo' package. -This short documentation only describes the features of the NCR53C8XX -driver, configuration parameters and control commands available -through the proc SCSI file system read / write operations. +This short documentation describes the features of the generic and enhanced +drivers, configuration parameters and control commands available through +the proc SCSI file system read / write operations. This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC. Latest driver version and patches are available at: ftp://ftp.tux.org/pub/people/gerard-roudier +or + ftp://ftp.symbios.com/mirror/ftp.tux.org/pub/tux/roudier/drivers I am not a native speaker of English and there are probably lots of mistakes in this README file. Any help will be welcome. @@ -136,6 +166,7 @@ Chip SDMS BIOS Wide SCSI std. Max. sync driver driver 875 Y Y FAST20 40 MB/s Y Y 876 Y Y FAST20 40 MB/s Y Y 895 Y Y FAST40 80 MB/s Y Y +895A Y Y FAST40 80 MB/s Y Y 896 Y Y FAST40 80 MB/s Y Y @@ -156,21 +187,25 @@ Serial NVRAM: Symbios and Tekram formats 3.1 Optimized SCSI SCRIPTS. -The 810A, 825A, 875, 895 and newest 896 support new SCSI SCRIPTS instructions -named LOAD and STORE that allow to move 1 DWORD from/to an IO register to/from -memory much faster that the MOVE MEMORY instruction that is supported by the -53c7xx and 53c8xx family. The LOAD/STORE instructions support absolute and -DSA relative addressing modes. The SCSI SCRIPTS had been entirely rewritten -using LOAD/STORE instead of MOVE MEMORY instructions. +The 810A, 825A, 875, 895, 896 and 895A support new SCSI SCRIPTS instructions +named LOAD and STORE that allow to move up to 1 DWORD from/to an IO register +to/from memory much faster that the MOVE MEMORY instruction that is supported +by the 53c7xx and 53c8xx family. +The LOAD/STORE instructions support absolute and DSA relative addressing +modes. The SCSI SCRIPTS had been entirely rewritten using LOAD/STORE instead +of MOVE MEMORY instructions. 3.2 New features of the SYM53C896 (64 bit PCI dual LVD SCSI controller) -The 896 allows to handle the phase mismatch context saving from SCRIPTS -(avoids the phase mismatch interrupt that stops the SCSI processor +The 896 and the 895A allows handling of the phase mismatch context from +SCRIPTS (avoids the phase mismatch interrupt that stops the SCSI processor until the C code has saved the context of the transfer). Implementing this without using LOAD/STORE instructions would be painfull -and I did'nt even try it. This chip also supports 64 bit PCI transactions -and addressing. The SCRIPTS processor is not true 64 bit, but uses segment +and I did'nt even want to try it. + +The 896 chip supports 64 bit PCI transactions and addressing, while the +895A supports 32 bit PCI transactions and 64 bit addressing. +The SCRIPTS processor of these chips is not true 64 bit, but uses segment registers for bit 32-63. Another interesting feature is that LOAD/STORE instructions that address the on-chip RAM (8k) remain internal to the chip. @@ -219,9 +254,13 @@ The maximum number of simultaneous tagged commands queued to a device is currently set to 8 by default. This value is suitable for most SCSI disks. With large SCSI disks (>= 2GB, cache >= 512KB, average seek time <= 10 ms), using a larger value may give better performances. -The driver supports up to 64 commands per device, but using more than -32 is generally not worth it, unless you are using a very large disk -or disk array. + +The sym53c8xx driver supports up to 255 commands per device, and the +generic ncr53c8xx driver supports up to 64, but using more than 32 is +generally not worth-while, unless you are using a very large disk or disk +array. It is noticeable that most of recent hard disks seem not to accept +more than 64 simultaneous commands. So, using more than 64 queued commands +is probably just resource wasting. If your controller does not have NVRAM or if it is managed by the SDMS BIOS/SETUP, you can configure tagged queueing feature and device queue @@ -491,6 +530,24 @@ Available commands: The driver default verbose level is 1. This command allows to change th driver verbose level after boot-up. +8.9 Reset all logical units of a target + + resetdev <target> + + target: target number + The driver will try to send a BUS DEVICE RESET message to the target. + (Only supported by the SYM53C8XX driver and provided for test purpose) + +8.10 Abort all tasks of all logical units of a target + + cleardev <target> + + target: target number + The driver will try to send a ABORT message to all the logical units + of the target. + (Only supported by the SYM53C8XX driver and provided for test purpose) + + 9. Configuration parameters If the firmware of all your devices is perfect enough, all the @@ -566,10 +623,11 @@ CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT Setup commands can be passed to the driver either at boot time or as a string variable using 'insmod'. -A boot setup command for the ncr53c8xx driver begins with the driver name -"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of -integers separated with comma followed by an optionnal list of comma- -separated strings. Example of boot setup command under lilo prompt: +A boot setup command for the ncr53c8xx (sym53c8xx) driver begins with the +driver name "ncr53c8xx="(sym53c8xx). The kernel syntax parser then expects +an optionnal list of integers separated with comma followed by an optional +list of comma-separated strings. Example of boot setup command under lilo +prompt: lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200 @@ -582,7 +640,7 @@ Since comma seems not to be allowed when defining a string variable using The following command will install driver module with the same options as above. -insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200" + insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200" For the moment, the integer list of arguments is discarded by the driver. It will be used in the future in order to allow a per controller setup. @@ -590,40 +648,53 @@ It will be used in the future in order to allow a per controller setup. Each string argument must be specified as "keyword:value". Only lower-case characters and digits are allowed. +In a system that contains multiple 53C8xx adapters insmod will install the +specified driver on each adapter. To exclude a chip use the 'excl' keyword. + +The sequence of commands, + + insmod sym53c8xx sym53c8xx=excl:0x1400 + insmod ncr53c8xx + +installs the sym53c8xx driver on all adapters except the one at IO port +address 0x1400 and then installs the ncr53c8xx driver to the adapter at IO +port address 0x1400. + + 10.2 Available arguments -Master parity checking - mpar:y enabled - mpar:n disabled +10.2.1 Master parity checking + mpar:y enabled + mpar:n disabled -Scsi parity checking - spar:y enabled - spar:n disabled +10.2.2 Scsi parity checking + spar:y enabled + spar:n disabled -Scsi disconnections - disc:y enabled - disc:n disabled +10.2.3 Scsi disconnections + disc:y enabled + disc:n disabled -Special features +10.2.4 Special features Only apply to 810A, 825A, 860, 875 and 895 controllers. Have no effect with other ones. - specf:y (or 1) enabled - specf:n (or 0) disabled - specf:3 enabled except Memory Write And Invalidate + specf:y (or 1) enabled + specf:n (or 0) disabled + specf:3 enabled except Memory Write And Invalidate The default driver setup is 'specf:3'. As a consequence, option 'specf:y' must be specified in the boot setup command to enable Memory Write And Invalidate. -Ultra SCSI support +10.2.5 Ultra SCSI support Only apply to 860, 875 and 895 controllers. Have no effect with other ones. - ultra:2 Ultra2 enabled - ultra:1 Ultra enabled - ultra:n disabled + ultra:2 Ultra2 enabled + ultra:1 Ultra enabled + ultra:n disabled -Default number of tagged commands - tags:0 (or tags:1 ) tagged command queuing disabled - tags:#tags (#tags > 1) tagged command queuing enabled +10.2.6 Default number of tagged commands + tags:0 (or tags:1 ) tagged command queuing disabled + tags:#tags (#tags > 1) tagged command queuing enabled #tags will be truncated to the max queued commands configuration parameter. This option also allows to specify a command queue depth for each device that support tagged command queueing. @@ -635,9 +706,9 @@ Default number of tagged commands - controller #1 target #1 logical unit #2 -> 32 commands, - all other logical units (all targets, all controllers) -> 10 commands. -Default synchronous period factor - sync:255 disabled (asynchronous transfer mode) - sync:#factor +10.2.7 Default synchronous period factor + sync:255 disabled (asynchronous transfer mode) + sync:#factor #factor = 10 Ultra-2 SCSI 40 Mega-transfers / second #factor = 11 Ultra-2 SCSI 33 Mega-transfers / second #factor < 25 Ultra SCSI 20 Mega-transfers / second @@ -646,19 +717,19 @@ Default synchronous period factor In all cases, the driver will use the minimum transfer period supported by controllers according to NCR53C8XX chip type. -Negotiate synchronous with all devices - (force sync nego) - fsn:y enabled - fsn:n disabled +10.2.8 Negotiate synchronous with all devices + (force sync nego) + fsn:y enabled + fsn:n disabled -Verbosity level - verb:0 minimal - verb:1 normal - verb:2 too much +10.2.9 Verbosity level + verb:0 minimal + verb:1 normal + verb:2 too much -Debug mode - debug:0 clear debug flags - debug:#x set debug flags +10.2.10 Debug mode + debug:0 clear debug flags + debug:#x set debug flags #x is an integer value combining the following power-of-2 values: DEBUG_ALLOC 0x1 DEBUG_PHASE 0x2 @@ -677,10 +748,10 @@ Debug mode You can play safely with DEBUG_NEGO. However, some of these flags may generate bunches of syslog messages. -Burst max - burst:0 burst disabled - burst:255 get burst length from initial IO register settings. - burst:#x burst enabled (1<<#x burst transfers max) +10.2.11 Burst max + burst:0 burst disabled + burst:255 get burst length from initial IO register settings. + burst:#x burst enabled (1<<#x burst transfers max) #x is an integer value which is log base 2 of the burst transfers max. The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7). Other chips only support up to 16 (#x = 4). @@ -688,42 +759,42 @@ Burst max and revision ids. By default the driver uses the maximum value supported by the chip. -LED support - led:1 enable LED support - led:0 disable LED support +10.2.12 LED support + led:1 enable LED support + led:0 disable LED support Donnot enable LED support if your scsi board does not use SDMS BIOS. (See 'Configuration parameters') -Max wide - wide:1 wide scsi enabled - wide:0 wide scsi disabled +10.2.13 Max wide + wide:1 wide scsi enabled + wide:0 wide scsi disabled Some scsi boards use a 875 (ultra wide) and only supply narrow connectors. If you have connected a wide device with a 50 pins to 68 pins cable converter, any accepted wide negotiation will break further data transfers. In such a case, using "wide:0" in the bootup command will be helpfull. -Differential mode - diff:0 never set up diff mode - diff:1 set up diff mode if BIOS set it - diff:2 always set up diff mode - diff:3 set diff mode if GPIO3 is not set +10.2.14 Differential mode + diff:0 never set up diff mode + diff:1 set up diff mode if BIOS set it + diff:2 always set up diff mode + diff:3 set diff mode if GPIO3 is not set -IRQ mode - irqm:0 always open drain - irqm:1 same as initial settings (assumed BIOS settings) - irqm:2 always totem pole - irqm:0x10 driver will not use SA_SHIRQ flag when requesting irq - irqm:0x20 driver will not use SA_INTERRUPT flag when requesting irq +10.2.15 IRQ mode + irqm:0 always open drain + irqm:1 same as initial settings (assumed BIOS settings) + irqm:2 always totem pole + irqm:0x10 driver will not use SA_SHIRQ flag when requesting irq + irqm:0x20 driver will not use SA_INTERRUPT flag when requesting irq (Bits 0x10 and 0x20 can be combined with hardware irq mode option) -Reverse probe - revprob:n probe chip ids from the PCI configuration in this order: - 810, 815, 820, 860, 875, 885, 895, 896 - revprob:y probe chip ids in the reverse order. +10.2.16 Reverse probe + revprob:n probe chip ids from the PCI configuration in this order: + 810, 815, 820, 860, 875, 885, 895, 896 + revprob:y probe chip ids in the reverse order. -Fix up PCI configuration space - pcifix:<option bits> +10.2.17 Fix up PCI configuration space + pcifix:<option bits> Available option bits: 0x0: No attempt to fix PCI configuration space registers values. @@ -733,25 +804,52 @@ Fix up PCI configuration space Use 'pcifix:7' in order to allow the driver to fix up all PCI features. -Serial NVRAM - nvram:n do not look for serial NVRAM - nvram:y test controllers for onboard serial NVRAM +10.2.18 Serial NVRAM + nvram:n do not look for serial NVRAM + nvram:y test controllers for onboard serial NVRAM + (alternate binary form) + mvram=<bits options> + 0x01 look for NVRAM (equivalent to nvram=y) + 0x02 ignore NVRAM "Synchronous negotiation" parameters for all devices + 0x04 ignore NVRAM "Wide negotiation" parameter for all devices + 0x08 ignore NVRAM "Scan at boot time" parameter for all devices + 0x80 also attach controllers set to OFF in the NVRAM (sym53c8xx only) -Check SCSI BUS - buschk:<option bits> +10.2.19 Check SCSI BUS + buschk:<option bits> Available option bits: 0x0: No check. 0x1: Check and donnot attach the controller on error. 0x2: Check and just warn on error. -Exclude hosts from being attached - excl=<io_address> +10.2.20 Exclude a host from being attached + excl=<io_address> Prevent host at a given io address from being attached. For example 'ncr53c8xx=excl:0xb400,excl:0xc000' indicate to the ncr53c8xx driver not to attach hosts at address 0xb400 and 0xc000. +10.2.21 Suggest a default SCSI id for hosts + hostid:255 no id suggested. + hostid:#x (0 < x < 7) x suggested for hosts SCSI id. + + If a host SCSI id is available from the NVRAM, the driver will ignore + any value suggested as boot option. Otherwise, if a suggested value + different from 255 has been supplied, it will use it. Otherwise, it will + try to deduce the value previously set in the hardware and use value + 7 if the hardware value is zero. + +10.2.22 Enable use of IMMEDIATE ARBITRATION + (only supported by the sym53c8xx driver. See 10.7 for more details) + iarb:0 donnot use this feature. + iarb:#x use this feature according to bit fields as follow: + + bit 0 (1) : enable IARB each time the initiator has been reselected + when it arbitrated for the SCSI BUS. + (#x >> 4) : maximum number of successive settings of IARB if the initiator + win arbitration and it has other commands to send to a device. + Boot fail safe safe:y load the following assumed fail safe initial setup @@ -775,6 +873,7 @@ Boot fail safe differential support from BIOS settings diff:1 irq mode from BIOS settings irqm:1 SCSI BUS check donnot attach on error buschk:1 + immediate arbitration disabled iarb:0 10.3 Advised boot setup commands @@ -837,7 +936,11 @@ use them too. nvram:n do not look for serial NVRAM nvram:y test controllers for onboard serial NVRAM -This option is described below (see 17. Serial NVRAM support). +This option can also been entered as an hexadecimal value that allows +to control what information the driver will get from the NVRAM and what +information it will ignore. +For details see '17. Serial NVRAM support'. + When this option is enabled, the driver tries to detect all boards using a Serial NVRAM. This memory is used to hold user set up parameters. @@ -873,12 +976,17 @@ mvram=<bits options> 0x02 ignore NVRAM "Synchronous negotiation" parameters for all devices 0x04 ignore NVRAM "Wide negotiation" parameter for all devices 0x08 ignore NVRAM "Scan at boot time" parameter for all devices + 0x80 also attach controllers set to OFF in the NVRAM (sym53c8xx only) -My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer -mode. However, it works flawlessly at 20 MB/second with the driver. -Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver -use its setup for synchronous and wide negotiations. +Option 0x80 is only supported by the sym53c8xx driver and is disabled by +default. Result is that, by default (option not set), the sym53c8xx driver +will not attach controllers set to OFF in the NVRAM. +The ncr53c8xx always tries to attach all the controllers. Option 0x80 has +not been added to the ncr53c8xx driver, since it has been reported to +confuse users who use this driver since a long time. If you desire a +controller not to be attached by the ncr53c8xx driver at Linux boot, you +must use the 'excl' driver boot option. 10.6 SCSI BUS checking boot option. @@ -894,6 +1002,45 @@ Unfortunately, the following common SCSI BUS problems are not detected: On the other hand, either bad cabling, broken devices, not conformant devices, ... may cause a SCSI signal to be wrong when te driver reads it. +10.7 IMMEDIATE ARBITRATION boot option + +This option is only supported by the SYM53C8XX driver (not by the NCR53C8XX). + +SYMBIOS 53C8XX chips are able to arbitrate for the SCSI BUS as soon as they +have detected an expected disconnection (BUS FREE PHASE). For this process +to be started, bit 1 of SCNTL1 IO register must be set when the chip is +connected to the SCSI BUS. + +When this feature has been enabled for the current connection, the chip has +every chance to win arbitration if only devices with lower priority are +competing for the SCSI BUS. By the way, when the chip is using SCSI id 7, +then it will for sure win the next SCSI BUS arbitration. + +Since, there is no way to know what devices are trying to arbitrate for the +BUS, using this feature can be extremally unfair. So, you are not advised +to enable it, or at most enable this feature for the case the chip lost +the previous arbitration (boot option 'iarb:1'). + +This feature has the following advantages: + +a) Allow the initiator with ID 7 to win arbitration when it wants so. +b) Overlap at least 4 micro-seconds of arbitration time with the execution + of SCRIPTS that deal with the end of the current connection and that + starts the next job. + +Hmmm... But (a) may just prevent other devices from reselecting the initiator, +and delay data transfers or status/completions, and (b) may just waste +SCSI BUS bandwidth if the SCRIPTS execution lasts more than 4 micro-seconds. + +The use of IARB needs the SCSI_NCR_IARB_SUPPORT option to have been defined +at compile time and the 'iarb' boot option to have been set to a non zero +value at boot time. It is not that usefull for real work, but can be used +to stress SCSI devices or for some applications that can gain advantage of +it. By the way, if you experience badnesses like 'unexpected disconnections', +'bad reselections', etc... when using IARB on heavy IO load, you should not +be surprised, because force-feeding anything and blocking its arse at the +same time cannot work for a long time. :-)) + 11. Some constants and flags of the ncr53c8xx.h header file @@ -1107,6 +1254,8 @@ then the request of the IRQ obviously will not succeed for all the drivers. 15. SCSI problem troubleshooting +15.1 Problem tracking + Most SCSI problems are due to a non conformant SCSI bus or to buggy devices. If infortunately you have SCSI problems, you can check the following things: @@ -1153,6 +1302,77 @@ Try to enable one feature at a time with control commands. For example: Once you have found the device and the feature that cause problems, just disable that feature for that device. +15.2 Understanding hardware error reports + +When the driver detects an unexpected error condition, it may display a +message of the following pattern. + +sym53c876-0:1: ERROR (0:48) (1-21-65) (f/95) @ (script 7c0:19000000). +sym53c876-0: script cmd = 19000000 +sym53c876-0: regdump: da 10 80 95 47 0f 01 07 75 01 81 21 80 01 09 00. + +Some fields in such a message may help you understand the cause of the +problem, as follows: + +sym53c876-0:1: ERROR (0:48) (1-21-65) (f/95) @ (script 7c0:19000000). +............A.........B.C....D.E..F....G.H.......I.....J...K....... + +Field A : target number. + SCSI ID of the device the controller was talking with at the moment the + error occurs. + +Field B : DSTAT io register (DMA STATUS) + Bit 0x40 : MDPE Master Data Parity Error + Data parity error detected on the PCI BUS. + Bit 0x20 : BF Bus Fault + PCI bus fault condition detected + Bit 0x01 : IID Illegal Instruction Detected + Set by the chip when it detects an Illegal Instruction format + on some condition that makes an instruction illegal. + Bit 0x80 : DFE Dma Fifo Empty + Pure status bit that does not indicate an error. + If the reported DSTAT value contains a combination of MDPE (0x40), + BF (0x20), then the cause may be likely due to a PCI BUS problem. + +Field C : SIST io register (SCSI Interrupt Status) + Bit 0x08 : SGE SCSI GROSS ERROR + Indicates that the chip detected a severe error condition + on the SCSI BUS that prevents the SCSI protocol from functionning + properly. + Bit 0x04 : UDC Undexpected Disconnection + Indicates that the device released the SCSI BUS when the chip + was not expecting this to happen. A device may behave so to + indicate the SCSI initiator that an error condition not reportable using the SCSI protocol has occured. + Bit 0x02 : RST SCSI BUS Reset + Generally SCSI targets donnot reset the SCSI BUS, although any + device on the BUS can reset it at any time. + Bit 0x01 : PAR Parity + SCSI parity error detected. + On a faulty SCSI BUS, any error condition among SGE (0x08), UDC (0x04) and + PAR (0x01) may be detected by the chip. If your SCSI system sometimes + encounters such error conditions, especially SCSI GROSS ERROR, then a SCSI + BUS problem is likely the cause of these errors. + +For fields D,E,F,G and H, you may look into the sym53c8xx_defs.h file +that contains some minimal comments on IO register bits. +Field D : SOCL Scsi Output Control Latch + This register reflects the state of the SCSI control lines the + chip want to drive or compare against. +Field E : SBCL Scsi Bus Control Lines + Actual value of control lines on the SCSI BUS. +Field F : SBDL Scsi Bus Data Lines + Actual value of data lines on the SCSI BUS. +Field G : SXFER SCSI Transfer + Contains the setting of the Synchronous Period for output and + the current Synchronous offset (offset 0 means asynchronous). +Field H : SCNTL3 Scsi Control Register 3 + Contains the setting of timing values for both asynchronous and + synchronous data transfers. + +Understanding Fields I, J, K and dumps requires to have good knowledge of +SCSI standards, chip cores functionnals and internal driver data structures. +You are not required to decode and understand them, unless you want to help +maintain the driver code. 16. Synchonous transfer negotiation tables diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 812870cda..760dcc75b 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -269,7 +269,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = { 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.1.19" +#define AIC7XXX_C_VERSION "5.1.20" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -878,13 +878,14 @@ typedef enum { AHC_SG_PRELOAD = 0x0080, AHC_SPIOCAP = 0x0100, AHC_ULTRA3 = 0x0200, + AHC_NEW_AUTOTERM = 0x0400, AHC_AIC7770_FE = AHC_FENONE, AHC_AIC7850_FE = AHC_SPIOCAP, AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP, AHC_AIC7870_FE = AHC_FENONE, AHC_AIC7880_FE = AHC_ULTRA, AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2| - AHC_QUEUE_REGS|AHC_SG_PRELOAD, + AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM, AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, AHC_AIC7896_FE = AHC_AIC7890_FE, AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_ULTRA3, @@ -1351,7 +1352,14 @@ static int aic7xxx_dump_sequencer = 0; * would result in never finding any devices :) */ static int aic7xxx_no_probe = 0; - +/* + * On some machines, enabling the external SCB RAM isn't reliable yet. I + * haven't had time to make test patches for things like changing the + * timing mode on that external RAM either. Some of those changes may + * fix the problem. Until then though, we default to external SCB RAM + * off and give a command line option to enable it. + */ +static int aic7xxx_scbram = 0; /* * So that insmod can find the variable and make it point to something */ @@ -1450,13 +1458,12 @@ aic_inb(struct aic7xxx_host *p, long port) unsigned char x; if(p->maddr) { - x = p->maddr[port]; + x = readb(p->maddr + port); } else { x = inb(p->base + port); } - mb(); return(x); #else return(inb(p->base + port)); @@ -1469,7 +1476,7 @@ aic_outb(struct aic7xxx_host *p, unsigned char val, long port) #ifdef MMAPIO if(p->maddr) { - p->maddr[port] = val; + writeb(val, p->maddr + port); } else { @@ -1513,6 +1520,7 @@ aic7xxx_setup(char *s, int *dummy) { "pci_parity", &aic7xxx_pci_parity }, { "dump_card", &aic7xxx_dump_card }, { "dump_sequencer", &aic7xxx_dump_sequencer }, + { "scbram", &aic7xxx_scbram }, { "tag_info", NULL } }; @@ -6193,7 +6201,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) cmd->result = 0; scb = NULL; } - if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) + else if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) { /* * Turn off the needsdtr, needwdtr, and needppr bits since this device @@ -6541,6 +6549,105 @@ aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer) } #endif + +/*+F************************************************************************* + * Function: + * aic7xxx_handle_command_completion_intr + * + * Description: + * SCSI command completion interrupt handler. + *-F*************************************************************************/ +static void +aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) +{ + struct aic7xxx_scb *scb = NULL; + Scsi_Cmnd *cmd; + unsigned char scb_index; + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) + printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); +#endif + + /* + * Read the INTSTAT location after clearing the CMDINT bit. This forces + * any posted PCI writes to flush to memory. Gerard Roudier suggested + * this fix to the possible race of clearing the CMDINT bit but not + * having all command bytes flushed onto the qoutfifo. + */ + aic_outb(p, CLRCMDINT, CLRINT); + aic_inb(p, INTSTAT); + /* + * The sequencer will continue running when it + * issues this interrupt. There may be >1 commands + * finished, so loop until we've processed them all. + */ + + while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL) + { + scb_index = p->qoutfifo[p->qoutfifonext]; + p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; + if ( scb_index >= p->scb_data->numscbs ) + scb = NULL; + else + scb = p->scb_data->scb_array[scb_index]; + if (scb == NULL) + { + printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, + -1, -1, -1, scb_index); + continue; + } + else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " + "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, + (unsigned long) scb->cmd); + continue; + } + else if (scb->flags & SCB_QUEUED_ABORT) + { + pause_sequencer(p); + if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && + (aic_inb(p, SCB_TAG) == scb->hscb->tag) ) + { + unpause_sequencer(p, FALSE); + continue; + } + aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel, + scb->cmd->lun, scb->hscb->tag); + scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT | + SCB_QUEUED_ABORT); + unpause_sequencer(p, FALSE); + } + else if (scb->flags & SCB_ABORT) + { + /* + * We started to abort this, but it completed on us, let it + * through as successful + */ + scb->flags &= ~(SCB_ABORT|SCB_RESET); + } + switch (status_byte(scb->hscb->target_status)) + { + case QUEUE_FULL: + case BUSY: + scb->hscb->target_status = 0; + scb->cmd->result = 0; + aic7xxx_error(scb->cmd) = DID_OK; + break; + default: + cmd = scb->cmd; + if (scb->hscb->residual_SG_segment_count != 0) + { + aic7xxx_calculate_residual(p, scb); + } + cmd->result |= (aic7xxx_error(cmd) << 16); + aic7xxx_done(p, scb); + break; + } + } +} + /*+F************************************************************************* * Function: * aic7xxx_isr @@ -6600,95 +6707,7 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) */ if (intstat & CMDCMPLT) { - struct aic7xxx_scb *scb = NULL; - Scsi_Cmnd *cmd; - unsigned char scb_index; - -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) - printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); -#endif - - /* - * Clear interrupt status before running the completion loop. - * This eliminates a race condition whereby a command could - * complete between the last check of qoutfifo and the - * CLRCMDINT statement. This would result in us thinking the - * qoutfifo was empty when it wasn't, and in actuality be a lost - * completion interrupt. With multiple devices or tagged queueing - * this could be very bad if we caught all but the last completion - * and no more are imediately sent. - */ - aic_outb(p, CLRCMDINT, CLRINT); - /* - * The sequencer will continue running when it - * issues this interrupt. There may be >1 commands - * finished, so loop until we've processed them all. - */ - - while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL) - { - scb_index = p->qoutfifo[p->qoutfifonext]; - p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; - if ( scb_index >= p->scb_data->numscbs ) - scb = NULL; - else - scb = p->scb_data->scb_array[scb_index]; - if (scb == NULL) - { - printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, - -1, -1, -1, scb_index); - continue; - } - else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " - "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, - (unsigned long) scb->cmd); - continue; - } - else if (scb->flags & SCB_QUEUED_ABORT) - { - pause_sequencer(p); - if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && - (aic_inb(p, SCB_TAG) == scb->hscb->tag) ) - { - unpause_sequencer(p, FALSE); - continue; - } - aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel, - scb->cmd->lun, scb->hscb->tag); - scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT | - SCB_QUEUED_ABORT); - unpause_sequencer(p, FALSE); - } - else if (scb->flags & SCB_ABORT) - { - /* - * We started to abort this, but it completed on us, let it - * through as successful - */ - scb->flags &= ~(SCB_ABORT|SCB_RESET); - } - switch (status_byte(scb->hscb->target_status)) - { - case QUEUE_FULL: - case BUSY: - scb->hscb->target_status = 0; - scb->cmd->result = 0; - aic7xxx_error(scb->cmd) = DID_OK; - break; - default: - cmd = scb->cmd; - if (scb->hscb->residual_SG_segment_count != 0) - { - aic7xxx_calculate_residual(p, scb); - } - cmd->result |= (aic7xxx_error(cmd) << 16); - aic7xxx_done(p, scb); - break; - } - } + aic7xxx_handle_command_completion_intr(p); } if (intstat & BRKADRINT) @@ -7619,9 +7638,10 @@ configure_termination(struct aic7xxx_host *p) aic_outb(p, SEEMS | SEECS, SEECTL); sxfrctl1 &= ~STPWEN; if ( (p->adapter_control & CFAUTOTERM) || - (p->features & AHC_ULTRA2) ) + (p->features & AHC_NEW_AUTOTERM) ) { - if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) ) + if ( (p->adapter_control & CFAUTOTERM) && + !(p->features & AHC_NEW_AUTOTERM) ) { printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", p->host_no); @@ -7635,7 +7655,7 @@ configure_termination(struct aic7xxx_host *p) } /* Configure auto termination. */ - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) { if (aic7xxx_override_term == -1) aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, @@ -7668,7 +7688,7 @@ configure_termination(struct aic7xxx_host *p) if (max_target <= 8) internal68_present = 0; - if ( !(p->features & AHC_ULTRA2) ) + if ( !(p->features & AHC_NEW_AUTOTERM) ) { if (max_target > 8) { @@ -7698,7 +7718,7 @@ configure_termination(struct aic7xxx_host *p) * SE Low Term Enable = BRDDAT5 (7890) * LVD High Term Enable = BRDDAT4 (7890) */ - if ( !(p->features & AHC_ULTRA2) && + if ( !(p->features & AHC_NEW_AUTOTERM) && (internal50_present && internal68_present && external_present) ) { printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", @@ -7731,7 +7751,7 @@ configure_termination(struct aic7xxx_host *p) (external_present ? 1 : 0)) <= 1) || (enableSE_low != 0) ) { - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) brddat |= BRDDAT5; else sxfrctl1 |= STPWEN; @@ -7762,7 +7782,7 @@ configure_termination(struct aic7xxx_host *p) { if (p->adapter_control & CFSTERM) { - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) brddat |= BRDDAT5; else sxfrctl1 |= STPWEN; @@ -9409,7 +9429,7 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, @@ -9539,7 +9559,7 @@ aic7xxx_detect(Scsi_Host_Template *template) temp_p->base = pdev->resource[0].start; temp_p->mbase = pdev->resource[1].start; current_p = list_p; - while(current_p) + while(current_p && temp_p) { if ( ((current_p->pci_bus == temp_p->pci_bus) && (current_p->pci_device_fn == temp_p->pci_device_fn)) || @@ -9958,7 +9978,8 @@ aic7xxx_detect(Scsi_Host_Template *template) #endif if (temp_p->features & AHC_ULTRA2) { - if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) && + (aic7xxx_scbram) ) { aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2, @@ -9966,12 +9987,33 @@ aic7xxx_detect(Scsi_Host_Template *template) temp_p->flags |= AHC_EXTERNAL_SRAM; devconfig |= EXTSCBPEN; } + else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + { + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: external SCB RAM detected, " + "but not enabled\n"); + } } - else if (devconfig & RAMPSM) + else { - devconfig &= ~SCBRAMSEL; - devconfig |= EXTSCBPEN; - temp_p->flags |= AHC_EXTERNAL_SRAM; + if ((devconfig & RAMPSM) && (aic7xxx_scbram)) + { + devconfig &= ~SCBRAMSEL; + devconfig |= EXTSCBPEN; + temp_p->flags |= AHC_EXTERNAL_SRAM; + } + else if (devconfig & RAMPSM) + { + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: external SCB RAM detected, " + "but not enabled\n"); + } } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_dword(pdev, DEVCONFIG, devconfig); @@ -11588,13 +11630,17 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) if ( aic7xxx_scb_on_qoutfifo(p, scb) ) { if(aic7xxx_verbose & VERBOSE_RESET_RETURN) - printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no, + printk(INFO_LEAD "SCB on qoutfifo, completing.\n", p->host_no, CTL_OF_SCB(scb)); - aic7xxx_run_done_queue(p, TRUE); + if ((aic_inb(p,INTSTAT) & CMDCMPLT) == 0) + printk(INFO_LEAD "missed CMDCMPLT interrupt!\n", p->host_no, + CTL_OF_SCB(scb)); + aic7xxx_handle_command_completion_intr(p); + aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); unpause_sequencer(p, FALSE); DRIVER_UNLOCK - return(SCSI_RESET_NOT_RUNNING); + return(SCSI_RESET_SUCCESS); } if ( flags & SCSI_RESET_SUGGEST_HOST_RESET ) { diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index d5d2765a7..abc86e123 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -63,6 +63,10 @@ #include "bvme6000.h" #endif +#ifdef CONFIG_SCSI_SIM710 +#include "sim710.h" +#endif + #ifdef CONFIG_A3000_SCSI #include "a3000.h" #endif @@ -455,6 +459,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_BVME6000_SCSI BVME6000_SCSI, #endif +#ifdef CONFIG_SCSI_SIM710 + SIM710_SCSI, +#endif #ifdef CONFIG_SCSI_ADVANSYS ADVANSYS, #endif diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c index fa8844b67..7b2944b30 100644 --- a/drivers/scsi/imm.c +++ b/drivers/scsi/imm.c @@ -166,16 +166,9 @@ int imm_detect(Scsi_Host_Template * host) */ imm_hosts[i].mode = IMM_NIBBLE; - if (modes & PARPORT_MODE_PCPS2) + if (modes & PARPORT_MODE_TRISTATE) imm_hosts[i].mode = IMM_PS2; - if (modes & PARPORT_MODE_PCECPPS2) { - w_ecr(ppb, 0x20); - imm_hosts[i].mode = IMM_PS2; - } - if (modes & PARPORT_MODE_PCECPEPP) - w_ecr(ppb, 0x80); - /* Done configuration */ imm_pb_release(i); diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 168299e0b..671a70e92 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -73,7 +73,7 @@ */ /* -** March 7 1999, version 3.2 +** Sep 10 1999, version 3.2c ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -83,7 +83,7 @@ ** Parity checking ** Etc... ** -** Supported NCR chips: +** Supported NCR/SYMBIOS chips: ** 53C810 (8 bits, Fast SCSI-2, no rom BIOS) ** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS) ** 53C820 (Wide, Fast SCSI-2, no rom BIOS) @@ -91,6 +91,8 @@ ** 53C860 (8 bits, Fast 20, no rom BIOS) ** 53C875 (Wide, Fast 20, on board rom BIOS) ** 53C895 (Wide, Fast 40, on board rom BIOS) +** 53C895A (Wide, Fast 40, on board rom BIOS) +** 53C896 (Wide, Fast 40, on board rom BIOS) ** ** Other features: ** Memory mapped IO (linux-1.3.X and above only) @@ -101,7 +103,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - 3.2c" #define SCSI_NCR_DEBUG_FLAGS (0) @@ -121,8 +123,10 @@ #include <asm/dma.h> #include <asm/io.h> #include <asm/system.h> -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) #include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +#include <asm/spinlock.h> #endif #include <linux/delay.h> #include <linux/signal.h> @@ -147,11 +151,14 @@ #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35) #include <linux/init.h> -#else +#endif + +#ifndef __init +#define __init +#endif #ifndef __initdata #define __initdata #endif -#endif #if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) #include <linux/bios32.h> @@ -368,8 +375,9 @@ static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head) ** We need to deal with power of 2, for alignment constraints. */ #if SCSI_NCR_MAX_TAGS > 64 -#undef SCSI_NCR_MAX_TAGS -#define SCSI_NCR_MAX_TAGS (64) +#define MAX_TAGS (64) +#else +#define MAX_TAGS SCSI_NCR_MAX_TAGS #endif #define NO_TAG (255) @@ -377,7 +385,7 @@ static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head) /* ** Choose appropriate type for tag bitmap. */ -#if SCSI_NCR_MAX_TAGS > 32 +#if MAX_TAGS > 32 typedef u_int64 tagmap_t; #else typedef u_int32 tagmap_t; @@ -427,7 +435,17 @@ typedef u_int32 tagmap_t; #ifdef SCSI_NCR_CAN_QUEUE #define MAX_START (SCSI_NCR_CAN_QUEUE + 4) #else -#define MAX_START (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS) +#define MAX_START (MAX_TARGET + 7 * MAX_TAGS) +#endif + +/* +** We limit the max number of pending IO to 250. +** since we donnot want to allocate more than 1 +** PAGE for 'scripth'. +*/ +#if MAX_START > 250 +#undef MAX_START +#define MAX_START 250 #endif /* @@ -737,34 +755,6 @@ static int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset, ** This structure is initialized from linux config options. ** It can be overridden at boot-up by the boot command line. */ -#define SCSI_NCR_MAX_EXCLUDES 8 -struct ncr_driver_setup { - u_char master_parity; - u_char scsi_parity; - u_char disconnection; - u_char special_features; - u_char ultra_scsi; - u_char force_sync_nego; - u_char reverse_probe; - u_char pci_fix_up; - u_char use_nvram; - u_char verbose; - u_char default_tags; - u_short default_sync; - u_short debug; - u_char burst_max; - u_char led_pin; - u_char max_wide; - u_char settle_delay; - u_char diff_support; - u_char irqm; - u_char bus_check; - u_char optimize; - u_char recovery; - u_int excludes[SCSI_NCR_MAX_EXCLUDES]; - char tag_ctrl[100]; -}; - static struct ncr_driver_setup driver_setup = SCSI_NCR_DRIVER_SETUP; @@ -794,108 +784,7 @@ static void ncr53c8xx_timeout(unsigned long np); #define bootverbose (driver_setup.verbose) #ifdef SCSI_NCR_NVRAM_SUPPORT -/* -** Symbios NvRAM data format -*/ -#define SYMBIOS_NVRAM_SIZE 368 -#define SYMBIOS_NVRAM_ADDRESS 0x100 - -struct Symbios_nvram { -/* Header 6 bytes */ - u_short start_marker; /* 0x0000 */ - u_short byte_count; /* excluding header/trailer */ - u_short checksum; - -/* Controller set up 20 bytes */ - u_short word0; /* 0x3000 */ - u_short word2; /* 0x0000 */ - u_short word4; /* 0x0000 */ - u_short flags; -#define SYMBIOS_SCAM_ENABLE (1) -#define SYMBIOS_PARITY_ENABLE (1<<1) -#define SYMBIOS_VERBOSE_MSGS (1<<2) -#define SYMBIOS_CHS_MAPPING (1<<3) - u_short flags1; -#define SYMBIOS_SCAN_HI_LO (1) - u_short word10; /* 0x00 */ - u_short flags3; /* 0x00 */ -#define SYMBIOS_REMOVABLE_FLAGS (3) /* 0=none, 1=bootable, 2=all */ - u_char host_id; - u_char byte15; /* 0x04 */ - u_short word16; /* 0x0410 */ - u_short word18; /* 0x0000 */ - -/* Boot order 14 bytes * 4 */ - struct Symbios_host{ - u_char word0; /* 0x0004:ok / 0x0000:nok */ - u_short device_id; /* PCI device id */ - u_short vendor_id; /* PCI vendor id */ - u_char byte6; /* 0x00 */ - u_char device_fn; /* PCI device/function number << 3*/ - u_short word8; - u_short flags; -#define SYMBIOS_INIT_SCAN_AT_BOOT (1) - u_short io_port; /* PCI io_port address */ - } host[4]; - -/* Targets 8 bytes * 16 */ - struct Symbios_target { - u_short flags; -#define SYMBIOS_DISCONNECT_ENABLE (1) -#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1) -#define SYMBIOS_SCAN_LUNS (1<<2) -#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3) - u_char bus_width; /* 0x08/0x10 */ - u_char sync_offset; - u_char sync_period; /* 4*period factor */ - u_char byte6; /* 0x00 */ - u_short timeout; - } target[16]; - u_char spare_devices[19*8]; - u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */ -}; -typedef struct Symbios_nvram Symbios_nvram; -typedef struct Symbios_host Symbios_host; -typedef struct Symbios_target Symbios_target; - -/* -** Tekram NvRAM data format. -*/ -#define TEKRAM_NVRAM_SIZE 64 -#define TEKRAM_NVRAM_ADDRESS 0 - -struct Tekram_nvram { - struct Tekram_target { - u_char flags; -#define TEKRAM_PARITY_CHECK (1) -#define TEKRAM_SYNC_NEGO (1<<1) -#define TEKRAM_DISCONNECT_ENABLE (1<<2) -#define TEKRAM_START_CMD (1<<3) -#define TEKRAM_TAGGED_COMMANDS (1<<4) -#define TEKRAM_WIDE_NEGO (1<<5) - u_char sync_index; - u_short word2; - } target[16]; - u_char host_id; - u_char flags; -#define TEKRAM_MORE_THAN_2_DRIVES (1) -#define TEKRAM_DRIVES_SUP_1GB (1<<1) -#define TEKRAM_RESET_ON_POWER_ON (1<<2) -#define TEKRAM_ACTIVE_NEGATION (1<<3) -#define TEKRAM_IMMEDIATE_SEEK (1<<4) -#define TEKRAM_SCAN_LUNS (1<<5) -#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */ - u_char boot_delay_index; - u_char max_tags_index; - u_short flags1; -#define TEKRAM_F2_F6_ENABLED (1) - u_short spare[29]; -}; -typedef struct Tekram_nvram Tekram_nvram; -typedef struct Tekram_target Tekram_target; - static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21}; - #endif /* SCSI_NCR_NVRAM_SUPPORT */ /* @@ -934,6 +823,7 @@ typedef struct { ncr_slot slot; ncr_chip chip; ncr_nvram *nvram; + u_char host_id; int attach_done; } ncr_device; @@ -946,7 +836,6 @@ typedef struct { #define DEBUG_ALLOC (0x0001) #define DEBUG_PHASE (0x0002) -#define DEBUG_POLL (0x0004) #define DEBUG_QUEUE (0x0008) #define DEBUG_RESULT (0x0010) #define DEBUG_SCATTER (0x0020) @@ -955,8 +844,6 @@ typedef struct { #define DEBUG_TIMING (0x0100) #define DEBUG_NEGO (0x0200) #define DEBUG_TAGS (0x0400) -#define DEBUG_FREEZE (0x0800) -#define DEBUG_RESTART (0x1000) /* ** Enable/Disable debug messages. @@ -1509,7 +1396,7 @@ struct lcb { */ u_char ia_tag; /* Allocation index */ u_char if_tag; /* Freeing index */ - u_char cb_tags[SCSI_NCR_MAX_TAGS]; /* Circular tags buffer */ + u_char cb_tags[MAX_TAGS]; /* Circular tags buffer */ u_char usetags; /* Command queuing is active */ u_char maxtags; /* Max nr of tags asked by user */ u_char numtags; /* Current number of tags */ @@ -1755,7 +1642,6 @@ struct ccb { **---------------------------------------------------------------- */ Scsi_Cmnd *cmd; /* SCSI command */ - u_long tlimit; /* Deadline for this job */ int data_len; /* Total data length */ /*---------------------------------------------------------------- @@ -3777,7 +3663,8 @@ flush_cache_all(); **========================================================== */ -static void __init ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) +static void __init +ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) { ncrcmd opcode, new, old, tmp1, tmp2; ncrcmd *start, *end; @@ -4043,7 +3930,7 @@ ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255; tp->usrwide = tn->bus_width == 0x10 ? 1 : 0; tp->usrtags = - (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0; + (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? MAX_TAGS : 0; if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE)) tp->usrflag |= UF_NODISC; @@ -4238,9 +4125,11 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) /* ** Get SCSI addr of host adapter (set by bios?). */ - if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07; - if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; - + if (np->myaddr == 255) { + np->myaddr = INB(nc_scid) & 0x07; + if (!np->myaddr) + np->myaddr = SCSI_NCR_MYADDR; + } #endif /* SCSI_NCR_TRUST_BIOS_SETTING */ @@ -4250,30 +4139,53 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) ncr_init_burst(np, burst_max); /* - ** Set differential mode and LED support. - ** Ignore these features for boards known to use a - ** specific GPIO wiring (Tekram only for now). - ** Probe initial setting of GPREG and GPCNTL for - ** other ones. - */ - if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) { + ** Set SCSI BUS mode. + ** + ** - ULTRA2 chips (895/895A/896) report the current + ** BUS mode through the STEST4 IO register. + ** - For previous generation chips (825/825A/875), + ** user has to tell us how to check against HVD, + ** since a 100% safe algorithm is not possible. + */ + np->scsi_mode = SMODE_SE; + if (np->features & FE_ULTRA2) + np->scsi_mode = (np->sv_stest4 & SMODE); + else if (np->features & FE_DIFF) { switch(driver_setup.diff_support) { - case 3: + case 4: /* Trust previous settings if present, then GPIO3 */ + if (np->sv_scntl3) { + if (np->sv_stest2 & 0x20) + np->scsi_mode = SMODE_HVD; + break; + } + case 3: /* SYMBIOS controllers report HVD through GPIO3 */ + if (nvram && nvram->type != SCSI_NCR_SYMBIOS_NVRAM) + break; if (INB(nc_gpreg) & 0x08) + break; + case 2: /* Set HVD unconditionally */ + np->scsi_mode = SMODE_HVD; + case 1: /* Trust previous settings for HVD */ + if (np->sv_stest2 & 0x20) + np->scsi_mode = SMODE_HVD; break; - case 2: - np->rv_stest2 |= 0x20; - break; - case 1: - np->rv_stest2 |= (np->sv_stest2 & 0x20); - break; - default: + default:/* Don't care about HVD */ break; } } + if (np->scsi_mode == SMODE_HVD) + np->rv_stest2 |= 0x20; + + /* + ** Set LED support from SCRIPTS. + ** Ignore this feature for boards known to use a + ** specific GPIO wiring and for the 895A or 896 + ** that drive the LED directly. + ** Also probe initial setting of GPIO0 as output. + */ if ((driver_setup.led_pin || (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) && - !(np->sv_gpcntl & 0x01)) + !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01)) np->features |= FE_LED0; /* @@ -4323,7 +4235,7 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) #endif tp->usrsync = driver_setup.default_sync; tp->usrwide = driver_setup.max_wide; - tp->usrtags = SCSI_NCR_MAX_TAGS; + tp->usrtags = MAX_TAGS; if (!driver_setup.disconnection) np->target[i].usrflag = UF_NODISC; } @@ -4507,6 +4419,7 @@ printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", np->clock_divn = device->chip.nr_divisor; np->maxoffs = device->chip.offset_max; np->maxburst = device->chip.burst_max; + np->myaddr = device->host_id; np->script0 = (struct script *) (((u_long) &host_data->script_data) & CACHE_LINE_MASK); @@ -4594,6 +4507,7 @@ printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", ** Fill Linux host instance structure */ instance->max_channel = 0; + instance->this_id = np->myaddr; instance->max_id = np->maxwide ? 16 : 8; instance->max_lun = SCSI_NCR_MAX_LUN; #ifndef NCR_IOMAPPED @@ -4604,6 +4518,8 @@ printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n", instance->io_port = device->slot.io_port; instance->n_io_port = 128; instance->dma_channel = 0; + instance->cmd_per_lun = MAX_TAGS; + instance->can_queue = (MAX_START-4); instance->select_queue_depths = ncr53c8xx_select_queue_depths; /* @@ -4885,9 +4801,10 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** **---------------------------------------------------- */ - if (np->settle_time && cmd->timeout_per_command >= HZ && - np->settle_time > jiffies + cmd->timeout_per_command - HZ) { - np->settle_time = jiffies + cmd->timeout_per_command - HZ; + if (np->settle_time && cmd->timeout_per_command >= HZ) { + u_long tlimit = ktime_get(cmd->timeout_per_command - HZ); + if (ktime_dif(np->settle_time, tlimit) > 0) + np->settle_time = tlimit; } if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) { @@ -4990,7 +4907,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** Force ordered tag if necessary to avoid timeouts ** and to preserve interactivity. */ - if (lp && lp->tags_stime + (3*HZ) <= jiffies) { + if (lp && ktime_exp(lp->tags_stime)) { if (lp->tags_smap) { order = M_ORDERED_TAG; if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ @@ -4998,7 +4915,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) printk("ordered tag forced.\n"); } } - lp->tags_stime = jiffies; + lp->tags_stime = ktime_get(3*HZ); lp->tags_smap = lp->tags_umap; } @@ -5221,12 +5138,6 @@ flush_cache_all(); /* ** activate this job. */ - - /* Compute a time limit greater than the middle-level driver one */ - if (cmd->timeout_per_command > 0) - cp->tlimit = jiffies + cmd->timeout_per_command + HZ; - else - cp->tlimit = jiffies + 86400 * HZ;/* No timeout=24 hours */ cp->magic = CCB_MAGIC; //printk("cp == %08lx\n", cp); dma_cache_wback_inv((unsigned long)cp, sizeof(*cp)); @@ -5337,7 +5248,7 @@ static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay) u_int32 term; int retv = 0; - np->settle_time = jiffies + settle_delay * HZ; + np->settle_time = ktime_get(settle_delay * HZ); if (bootverbose > 1) printk("%s: resetting, " @@ -5512,7 +5423,6 @@ static int ncr_abort_command (ncb_p np, Scsi_Cmnd *cmd) ** script to abort the command. */ - cp->tlimit = 0; switch(cp->host_status) { case HS_BUSY: case HS_NEGOTIATE: @@ -5710,7 +5620,7 @@ void ncr_complete (ncb_p np, ccb_p cp) #endif if (DEBUG_FLAGS & DEBUG_TINY) - printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp & 0xfff, + printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp, cp->host_status,cp->scsi_status); /* @@ -6732,7 +6642,7 @@ static void ncr_usercmd (ncb_p np) static void ncr_timeout (ncb_p np) { - u_long thistime = jiffies; + u_long thistime = ktime_get(0); /* ** If release process in progress, let's go @@ -6745,7 +6655,7 @@ static void ncr_timeout (ncb_p np) return; } - np->timer.expires = jiffies + SCSI_NCR_TIMER_INTERVAL; + np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL); add_timer(&np->timer); /* @@ -7040,8 +6950,9 @@ flush_cache_all(); ** We are more soft for UDC. **========================================================= */ - if (jiffies - np->regtime > 10*HZ) { - np->regtime = jiffies; + + if (ktime_exp(np->regtime)) { + np->regtime = ktime_get(10*HZ); for (i = 0; i<sizeof(np->regdump); i++) ((char*)&np->regdump)[i] = INB_OFF(i); np->regdump.nc_dstat = dstat; @@ -7159,7 +7070,7 @@ static int ncr_int_sbmc (ncb_p np) ** Suspend command processing for 1 second and ** reinitialize all except the chip. */ - np->settle_time = jiffies + HZ; + np->settle_time = ktime_get(1*HZ); ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET); return 1; } @@ -8292,7 +8203,7 @@ static ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln) if (lp) { if (tag != NO_TAG) { ++lp->ia_tag; - if (lp->ia_tag == SCSI_NCR_MAX_TAGS) + if (lp->ia_tag == MAX_TAGS) lp->ia_tag = 0; lp->tags_umap |= (((tagmap_t) 1) << tag); } @@ -8340,7 +8251,7 @@ static void ncr_free_ccb (ncb_p np, ccb_p cp) if (lp) { if (cp->tag != NO_TAG) { lp->cb_tags[lp->if_tag++] = cp->tag; - if (lp->if_tag == SCSI_NCR_MAX_TAGS) + if (lp->if_tag == MAX_TAGS) lp->if_tag = 0; lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag); lp->tags_smap &= lp->tags_umap; @@ -8722,10 +8633,10 @@ static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data) for (i = 0 ; i < 64 ; i++) lp->jump_ccb[i] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q)); - for (i = 0 ; i < SCSI_NCR_MAX_TAGS ; i++) + for (i = 0 ; i < MAX_TAGS ; i++) lp->cb_tags[i] = i; - lp->maxnxs = SCSI_NCR_MAX_TAGS; - lp->tags_stime = jiffies; + lp->maxnxs = MAX_TAGS; + lp->tags_stime = ktime_get(3*HZ); } /* @@ -9254,8 +9165,7 @@ static void __init ncr_getclock (ncb_p np, int mult) #define ARG_SEP ',' #endif - -void __init ncr53c8xx_setup(char *str, int *ints) +int __init ncr53c8xx_setup(char *str) { #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT char *cur = str; @@ -9339,6 +9249,8 @@ void __init ncr53c8xx_setup(char *str, int *ints) 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); @@ -9346,8 +9258,15 @@ void __init ncr53c8xx_setup(char *str, int *ints) ++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, uchar bus, uchar device_fn, ncr_device *device); @@ -9517,7 +9436,7 @@ int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt) #if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE) if (ncr53c8xx) - ncr53c8xx_setup(ncr53c8xx, (int *) 0); + ncr53c8xx_setup(ncr53c8xx); #endif if (initverbose >= 2) @@ -9564,6 +9483,7 @@ if (ncr53c8xx) #else device[count].nvram = 0; #endif + device[count].host_id = driver_setup.host_id; if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) { device[count].nvram = 0; continue; @@ -9614,10 +9534,40 @@ if (ncr53c8xx) ** Return the offset immediately after the base address that has ** been read. Btw, we blindly assume that the high 32 bits of 64 bit ** base addresses are set to zero on 32 bit architectures. -** (the pci generic code now does this for us) ** */ +#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) +static int __init +pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) +{ + u_int32 tmp; + pcibios_read_config_dword(bus, device_fn, offset, &tmp); + *base = tmp; + offset += sizeof(u_int32); + if ((tmp & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + pcibios_read_config_dword(bus, device_fn, offset, &tmp); + *base |= (((u_long)tmp) << 32); +#endif + offset += sizeof(u_int32); + } + return offset; +} +#elif 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->base_address[index++]; + if ((*base & 0x7) == 0x4) { +#if BITS_PER_LONG > 32 + *base |= (((u_long)pdev->base_address[index]) << 32); +#endif + ++index; + } + return index; +} +#else /* LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) */ static int __init pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) { @@ -9626,6 +9576,7 @@ pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) ++index; return ++index; } +#endif /* ** Read and check the PCI configuration for any detected NCR @@ -9633,7 +9584,6 @@ pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) ** been detected. */ - static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, uchar bus, uchar device_fn, ncr_device *device) { @@ -9809,6 +9759,9 @@ static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, * 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"); @@ -9837,7 +9790,7 @@ static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, } if (!latency_timer) { - latency_timer = 64; + latency_timer = 128; if (initverbose >= 2) printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer); pcibios_write_config_byte(bus, device_fn, @@ -9849,8 +9802,6 @@ static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, /* * Check availability of IO space, memory space and master capability. - * No need to test BARs flags since they are hardwired to the - * expected value. */ if (command & PCI_COMMAND_IO) io_port &= PCI_BASE_ADDRESS_IO_MASK; @@ -10140,8 +10091,8 @@ static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de device->queue_depth = numtags; if (device->queue_depth < 2) device->queue_depth = 2; - if (device->queue_depth > SCSI_NCR_MAX_TAGS) - device->queue_depth = SCSI_NCR_MAX_TAGS; + if (device->queue_depth > MAX_TAGS) + device->queue_depth = MAX_TAGS; /* ** Since the queue depth is not tunable under Linux, @@ -10627,8 +10578,6 @@ printk("ncr_user_command: data=%ld\n", uc->data); uc->data |= DEBUG_ALLOC; else if ((arg_len = is_keyword(ptr, len, "phase"))) uc->data |= DEBUG_PHASE; - else if ((arg_len = is_keyword(ptr, len, "poll"))) - uc->data |= DEBUG_POLL; else if ((arg_len = is_keyword(ptr, len, "queue"))) uc->data |= DEBUG_QUEUE; else if ((arg_len = is_keyword(ptr, len, "result"))) @@ -10645,10 +10594,6 @@ printk("ncr_user_command: data=%ld\n", uc->data); uc->data |= DEBUG_NEGO; else if ((arg_len = is_keyword(ptr, len, "tags"))) uc->data |= DEBUG_TAGS; - else if ((arg_len = is_keyword(ptr, len, "freeze"))) - uc->data |= DEBUG_FREEZE; - else if ((arg_len = is_keyword(ptr, len, "restart"))) - uc->data |= DEBUG_RESTART; else return -EINVAL; ptr += arg_len; len -= arg_len; @@ -10764,7 +10709,7 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len) (u_long) np->reg); #endif copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync); - copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS); + copy_info(&info, "max commands per lun %d\n", MAX_TAGS); if (driver_setup.debug || driver_setup.verbose > 1) { copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug); @@ -10941,8 +10886,8 @@ static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) nvram_stop(np, &gpreg); #ifdef SCSI_NCR_DEBUG_NVRAM -printk("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n", - nvram->start_marker, +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, @@ -10950,7 +10895,7 @@ printk("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d ch #endif /* check valid NVRAM signature, verify byte count and checksum */ - if (nvram->start_marker == 0 && + if (nvram->type == 0 && !memcmp(nvram->trailer, Symbios_trailer, 6) && nvram->byte_count == sizeof(*nvram) - 12 && csum == nvram->checksum) @@ -10966,8 +10911,8 @@ out: /* * 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) +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; @@ -10996,9 +10941,8 @@ static void __init nvram_start(ncr_slot *np, u_char *gpreg) * 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) +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; @@ -11012,9 +10956,8 @@ static void __init nvram_write_byte(ncr_slot *np, u_char *ack_data, * 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) +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; @@ -11032,8 +10975,8 @@ static void __init nvram_read_byte(ncr_slot *np, u_char *read_data, * 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) +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); @@ -11044,8 +10987,8 @@ static void __init nvram_writeAck(ncr_slot *np, u_char write_bit, * 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) +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); @@ -11056,8 +10999,7 @@ static void __init nvram_readAck(ncr_slot *np, u_char *read_bit, * 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) +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); @@ -11079,8 +11021,8 @@ static void __init nvram_stop(ncr_slot *np, u_char *gpreg) /* * 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) +static void __init +nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) { UDELAY (5); switch (bit_mode){ @@ -11166,8 +11108,8 @@ static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) /* * 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) +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; @@ -11192,8 +11134,7 @@ static u_short __init Tnvram_read_data(ncr_slot *np, u_short *data, int len, /* * 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) +static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg) { int x; @@ -11207,8 +11148,7 @@ static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, /* * READ a byte from the NVRAM */ -static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, - u_char *gpreg) +static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) { int x; u_char read_bit; @@ -11227,8 +11167,8 @@ static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, /* * Read bit from NVRAM */ -static void __init Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, - u_char *gpreg) +static void __init +Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) { UDELAY (2); Tnvram_Clk(np, gpreg); @@ -11238,8 +11178,8 @@ static void __init Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, /* * Write bit to GPIO0 */ -static void __init Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, - u_char *gpreg) +static void __init +Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) { if (write_bit & 0x01) *gpreg |= 0x02; diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index dbd078299..d9abefc1b 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -176,10 +176,38 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd, } /* - * This interface is depreciated - users should use the scsi generics + * This interface is depreciated - users should use the scsi generic (sg) * interface instead, as this is a more flexible approach to performing * generic SCSI commands on a device. + * + * The structure that we are passed should look like: + * + * struct sdata { + * unsigned int inlen; [i] Length of data to be written to device + * unsigned int outlen; [i] Length of data to be read from device + * unsigned char cmd[x]; [i] SCSI command (6 <= x <= 12). + * [o] Data read from device starts here. + * [o] On error, sense buffer starts here. + * unsigned char wdata[y]; [i] Data written to device starts here. + * }; + * Notes: + * - The SCSI command length is determined by examining the 1st byte + * of the given command. There is no way to override this. + * - Data transfers are limited to PAGE_SIZE (4K on i386, 8K on alpha). + * - The length (x + y) must be at least OMAX_SB_LEN bytes long to + * accomodate the sense buffer when an error occurs. + * The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that + * old code will not be surprised. + * - If a Unix error occurs (e.g. ENOMEM) then the user will receive + * a negative return and the Unix error code in 'errno'. + * If the SCSI command succeeds then 0 is returned. + * Positive numbers returned are the compacted SCSI error codes (4 + * bytes in one int) where the lowest byte is the SCSI status. + * See the drivers/scsi/scsi.h file for more information on this. + * */ +#define OMAX_SB_LEN 16 /* Old sense buffer length */ + int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) { unsigned long flags; @@ -195,8 +223,6 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) if (!sic) return -EINVAL; - - /* * Verify that we can read at least this much. */ @@ -204,23 +230,13 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) if (result) return result; - /* - * The structure that we are passed should look like: - * - * struct sdata { - * unsigned int inlen; - * unsigned int outlen; - * unsigned char cmd[]; # However many bytes are used for cmd. - * unsigned char data[]; - * }; - */ get_user(inlen, &sic->inlen); get_user(outlen, &sic->outlen); /* * We do not transfer more than MAX_BUF with this interface. * If the user needs to transfer more data than this, they - * should use scsi_generics instead. + * should use scsi_generics (sg) instead. */ if (inlen > MAX_BUF) return -EINVAL; @@ -249,19 +265,16 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) */ cmdlen = COMMAND_SIZE(opcode); - result = verify_area(VERIFY_READ, cmd_in, - cmdlen + inlen > MAX_BUF ? MAX_BUF : cmdlen + inlen); + result = verify_area(VERIFY_READ, cmd_in, cmdlen + inlen); if (result) return result; - copy_from_user((void *) cmd, cmd_in, cmdlen); + copy_from_user(cmd, cmd_in, cmdlen); /* * Obtain the data to be sent to the device (if any). */ - copy_from_user((void *) buf, - (void *) (cmd_in + cmdlen), - inlen); + copy_from_user(buf, cmd_in + cmdlen, inlen); /* * Set the lun field to the correct value. @@ -311,19 +324,18 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) * If there was an error condition, pass the info back to the user. */ if (SCpnt->result) { - result = verify_area(VERIFY_WRITE, - cmd_in, - sizeof(SCpnt->sense_buffer)); + int sb_len = sizeof(SCpnt->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((void *) cmd_in, - SCpnt->sense_buffer, - sizeof(SCpnt->sense_buffer)); + copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len); } else { result = verify_area(VERIFY_WRITE, cmd_in, outlen); if (result) return result; - copy_to_user((void *) cmd_in, buf, outlen); + copy_to_user(cmd_in, buf, outlen); } result = SCpnt->result; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9864b94ef..4a2258cb5 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -16,41 +16,28 @@ * * Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book. */ - static char * sg_version_str = "Version: 2.1.34 (990603)"; - static int sg_version_num = 20134; /* 2 digits for each component */ + static char * sg_version_str = "Version: 2.3.35 (990708)"; + static int sg_version_num = 20335; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First * the kernel/module needs to be built with CONFIG_SCSI_LOGGING - * (otherwise the macros compile to empty statements), then do - * something like: 'echo "scsi log all" > /proc/scsi/scsi' to log - * everything or 'echo "scsi log {token} #N" > /proc/scsi/scsi' - * where {token} is one of [error,timeout,scan,mlqueue,mlcomplete, - * llqueue,llcomplete,hlqueue,hlcomplete,ioctl] and #N is 0...7 - * (with 0 meaning off). For example: 'scsi log timeout 7 > - * /proc/scsi/scsi' to get all logging messages from this driver. - * Should use hlcomplete but it is too "noisy" (sd uses it). - * - * - This driver obtains memory (heap) for the low-level driver to - * transfer/dma to and from. It is obtained from up to 3 sources: - * - obtain heap via get_free_pages() - * - obtain heap from the shared scsi dma pool - * - obtain heap from kernel directly (kmalloc) [last choice] - * Each open() attempts to obtain a "reserve" buffer of - * SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The - * amount actually obtained [which could be 0 bytes] can be found from - * the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can - * be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this - * is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE - * ioctl() to find out how much was actually obtained. - * A subsequent write() to this file descriptor will use the - * reserved buffer unless: - * - it is already in use (eg during command queuing) - * - or the write() needs a buffer size larger than the - * reserved size - * In these cases the write() will attempt to get the required memory - * for the duration of this request but, if memory is low, it may - * fail with ENOMEM. + * (otherwise the macros compile to empty statements). + * Then before running the program to be debugged enter: + * # echo "scsi log timeout 7" > /proc/scsi/scsi + * This will send copious output to the console and the log which + * is usually /var/log/messages. To turn off debugging enter: + * # echo "scsi log timeout 0" > /proc/scsi/scsi + * The 'timeout' token was chosen because it is relatively unused. + * The token 'hlcomplete' should be used but that triggers too + * much output from the sd device driver. To dump the current + * state of the SCSI mid level data structures enter: + * # echo "scsi dump 1" > /proc/scsi/scsi + * To dump the state of sg's data structures get the 'sg_debug' + * program from the utilities and enter: + * # sg_debug /dev/sga + * or any valid sg device name. The state of _all_ sg devices + * will be sent to the console and the log. * * - The 'alt_address' field in the scatter_list structure and the * related 'mem_src' indicate the source of the heap allocation. @@ -146,7 +133,8 @@ typedef struct sg_fd /* holds the state of a file descriptor */ { struct sg_fd * nextfp; /* NULL when last opened fd on this device */ struct sg_device * parentdp; /* owning device */ - wait_queue_head_t read_wait; /* queue read until command done */ + wait_queue_head_t read_wait; /* queue read until command done */ + wait_queue_head_t write_wait; /* write waits on pending read */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ Sg_scatter_hold reserve; /* buffer held for this file descriptor */ unsigned save_scat_len; /* original length of trunc. scat. element */ @@ -160,7 +148,7 @@ typedef struct sg_fd /* holds the state of a file descriptor */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */ char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ -} Sg_fd; /* 1208 bytes long on i386 */ +} Sg_fd; /* 1212 bytes long on i386 */ typedef struct sg_device /* holds the state of each scsi generic device */ { @@ -212,7 +200,6 @@ static Sg_device * sg_dev_arr = NULL; static const int size_sg_header = sizeof(struct sg_header); - static int sg_open(struct inode * inode, struct file * filp) { int dev = MINOR(inode->i_rdev); @@ -360,6 +347,8 @@ static ssize_t sg_read(struct file * filp, char * buf, count = (srp->header.result == 0) ? 0 : -EIO; sg_finish_rem_req(srp, NULL, 0); } + if (! sfp->cmd_q) + wake_up_interruptible(&sfp->write_wait); return count; } @@ -391,10 +380,21 @@ static ssize_t sg_write(struct file * filp, const char * buf, if (count < (size_sg_header + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ - srp = sg_add_request(sfp); - if (! srp) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full, domain error\n")); - return -EDOM; + if (! (srp = sg_add_request(sfp))) { + if (sfp->cmd_q) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); + return -EDOM; + } + else { /* old semantics: wait for pending read() to finish */ + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + k = 0; + __wait_event_interruptible(sfp->write_wait, + (srp = sg_add_request(sfp)), + k); + if (k) + return k; /* -ERESTARTSYS because signal hit process */ + } } __copy_from_user(&srp->header, buf, size_sg_header); buf += size_sg_header; @@ -404,7 +404,7 @@ static ssize_t sg_write(struct file * filp, const char * buf, if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); sfp->next_cmd_len = 0; - return -EDOM; + return -EIO; } cmd_size = sfp->next_cmd_len; sfp->next_cmd_len = 0; /* reset so only this write() effected */ @@ -490,7 +490,12 @@ static int sg_ioctl(struct inode * inode, struct file * filp, switch(cmd_in) { case SG_SET_TIMEOUT: - return get_user(sfp->timeout, (int *)arg); + result = get_user(val, (int *)arg); + if (result) return result; + if (val < 0) + return -EIO; + sfp->timeout = val; + return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ return sfp->timeout; /* strange ..., for backward compatibility */ case SG_SET_FORCE_LOW_DMA: @@ -519,9 +524,12 @@ static int sg_ioctl(struct inode * inode, struct file * filp, __put_user((int)sdp->device->id, &sg_idp->scsi_id); __put_user((int)sdp->device->lun, &sg_idp->lun); __put_user((int)sdp->device->type, &sg_idp->scsi_type); + __put_user((short)sdp->device->host->cmd_per_lun, + &sg_idp->h_cmd_per_lun); + __put_user((short)sdp->device->queue_depth, + &sg_idp->d_queue_depth); __put_user(0, &sg_idp->unused1); __put_user(0, &sg_idp->unused2); - __put_user(0, &sg_idp->unused3); return 0; } case SG_SET_FORCE_PACK_ID: @@ -605,6 +613,13 @@ static int sg_ioctl(struct inode * inode, struct file * filp, return put_user(sg_version_num, (int *)arg); case SG_EMULATED_HOST: return put_user(sdp->device->host->hostt->emulated, (int *)arg); + case SG_SCSI_RESET: + if (! scsi_block_when_processing_errors(sdp->device)) + return -EBUSY; + result = get_user(val, (int *)arg); + if (result) return result; + /* Don't do anything till scsi mod level visibility */ + return 0; case SCSI_IOCTL_SEND_COMMAND: /* Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the user already has read/write access to the generic device and so @@ -686,6 +701,9 @@ static void sg_command_done(Scsi_Cmnd * SCpnt) Sg_fd * sfp; Sg_request * srp = NULL; int closed = 0; + static const int min_sb_len = + SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ? + sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE; if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)) { SCSI_LOG_TIMEOUT(1, printk("sg__done: bad args dev=%d\n", dev)); @@ -727,8 +745,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt) SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", dev, (int)status_byte(SCpnt->result), (int)SCpnt->result)); - memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, - sizeof(SCpnt->sense_buffer)); + memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, min_sb_len); switch (host_byte(SCpnt->result)) { /* This setup of 'result' is for backward compatibility and is best ignored by the user who should use target, host + driver status */ @@ -798,7 +815,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt) /* Now wake up any sg_read() that is waiting for this packet. */ wake_up_interruptible(&sfp->read_wait); if ((sfp->async_qp) && (! closed)) - kill_fasync(sfp->async_qp, SIGPOLL); + kill_fasync(sfp->async_qp, SIGPOLL, POLL_IN); } static void sg_debug_all(const Sg_fd * sfp) @@ -1491,13 +1508,15 @@ static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev, int get_reserved) return sdp->headfp; } sfp = (Sg_fd *)sg_low_malloc(sizeof(Sg_fd), 0, SG_HEAP_KMAL, 0); - if (!sfp) - return NULL; - - memset(sfp, 0, sizeof(Sg_fd)); - sfp->my_mem_src = SG_HEAP_KMAL; - - init_waitqueue_head(&sfp->read_wait); + if (sfp) { + memset(sfp, 0, sizeof(Sg_fd)); + sfp->my_mem_src = SG_HEAP_KMAL; + init_waitqueue_head(&sfp->read_wait); + init_waitqueue_head(&sfp->write_wait); + } + else + return NULL; + sfp->timeout = SG_DEFAULT_TIMEOUT; sfp->force_packid = SG_DEF_FORCE_PACK_ID; sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c new file mode 100644 index 000000000..68785fea6 --- /dev/null +++ b/drivers/scsi/sim710.c @@ -0,0 +1,1605 @@ +/* + * sim710.c - Copyright (C) 1999 Richard Hirst <richard@sleepie.demon.co.uk> + * + *---------------------------------------------------------------------------- + * 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. + *---------------------------------------------------------------------------- + * + * MCA card detection code by Trent McNair. + * + * Various bits of code in this driver have been copied from 53c7,8xx,c, + * which is coyright Drew Eckhardt. The scripts for the SCSI chip are + * compiled with the script compiler written by Drew. + * + * This is a simple driver for the NCR53c710. More complex drivers + * for this chip (e.g. 53c7xx.c) require that the scsi chip be able to + * do DMA block moves between memory and on-chip registers, which can + * be a problem if those registers are in the I/O address space. There + * can also be problems on hardware where the registers are memory + * mapped, if the design is such that memory-to-memory transfers initiated + * by the scsi chip cannot access the chip registers. + * + * This driver is designed to avoid these problems and is intended to + * work with any Intel machines using 53c710 chips, including various + * Compaq and NCR machines. It was initially written for the Tadpole + * TP34V VME board which is 68030 based. + * + * The driver supports boot-time parameters similar to + * sim710=addr:0x9000,irq:15 + * and insmod parameters similar to + * sim710="addr:0x9000 irq:15" + * + * The complete list of options are: + * + * addr:0x9000 Specifies the base I/O port (or address) of the 53C710. + * irq:15 Specifies the IRQ number used by the 53c710. + * debug:0xffff Generates lots of debug output. + * ignore:0x0a Makes the driver ignore SCSI IDs 0 and 2. + * nodisc:0x70 Prevents disconnects from IDs 6, 5 and 4. + * noneg:0x10 Prevents SDTR negotiation on ID 4. + * + * Current limitations: + * + * o Async only + * o Severely lacking in error recovery + * o Auto detection of IRQs and chip addresses only on MCA architectures + * + */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) +#include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +#include <asm/spinlock.h> +#endif +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> + +#ifdef CONFIG_TP34V_SCSI + +#include <asm/tp34vhw.h> +#define MEM_MAPPED + +#elif defined(CONFIG_MCA) + +#define IO_MAPPED + +/* + * For each known microchannel card using the 53c710 we need a list + * of possible IRQ and IO settings, as well as their corresponding + * bit assignment in pos[]. This might get cumbersome if there + * are more than a few cards (I only know of 2 at this point). + */ + +#define MCA_53C710_IDS { 0x01bb, 0x01ba, 0x004f } + +/* CARD ID 01BB and 01BA use the same pos values */ + +#define MCA_01BB_IO_PORTS { 0x0000, 0x0000, 0x0800, 0x0C00, 0x1000, 0x1400, \ + 0x1800, 0x1C00, 0x2000, 0x2400, 0x2800, \ + 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, \ + 0x4000, 0x4400, 0x4800, 0x4C00, 0x5000 } + +#define MCA_01BB_IRQS { 3, 5, 11, 14 } + +/* CARD ID 004f */ + +#define MCA_004F_IO_PORTS { 0x0000, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600 } + +#define MCA_004F_IRQS { 5, 9, 14 } + +#else + +/* Assume an Intel platform */ + +#define IO_MAPPED + +#endif + +#include "scsi.h" +#include "hosts.h" +#include "sim710.h" + +#include<linux/stat.h> + +#define DEBUG +#undef DEBUG_LIMIT_INTS /* Define to 10 to hang driver after 10 ints */ + +/* Debug options available via the "debug:0x1234" parameter */ + +#define DEB_NONE 0x0000 /* Nothing */ +#define DEB_HALT 0x0001 /* Detailed trace of chip halt funtion */ +#define DEB_REGS 0x0002 /* All chip register read/writes */ +#define DEB_SYNC 0x0004 /* Sync/async negotiation */ +#define DEB_PMM 0x0008 /* Phase mis-match handling */ +#define DEB_INTS 0x0010 /* General interrupt trace */ +#define DEB_TOUT 0x0020 /* Selection timeouts */ +#define DEB_RESUME 0x0040 /* Resume addresses for the script */ +#define DEB_CMND 0x0080 /* Commands and status returned */ +#define DEB_FIXUP 0x0100 /* Fixup of scsi addresses */ +#define DEB_DISC 0x0200 /* Disconnect/reselect handling */ + +#define DEB_ANY 0xffff /* Any and all debug options */ + +#ifdef DEBUG +#define DEB(m,x) if (sim710_debug & m) x +int sim710_debug = 0; +#else +#define DEB(m,x) +#endif + +/* Redefine scsi_done to force renegotiation of (a)sync transfers + * following any failed command. + */ + +#define SCSI_DONE(cmd) { \ + DEB(DEB_CMND, printk("scsi%d: Complete %08x\n", \ + host->host_no, cmd->result)); \ + if (cmd->result) \ + hostdata->negotiate |= (1 << cmd->target); \ + cmd->scsi_done(cmd); \ + } + +#ifndef offsetof +#define offsetof(t, m) ((size_t) (&((t *)0)->m)) +#endif + + +struct proc_dir_entry proc_scsi_sim710 = { + PROC_SCSI_SIM710, 6, "sim710", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +#define STATE_INITIALISED 0 +#define STATE_HALTED 1 +#define STATE_IDLE 2 +#define STATE_BUSY 3 +#define STATE_DISABLED 4 + +#define MAXBOARDS 2 /* Increase this and the sizes of the + arrays below, if you need more.. */ + +#ifdef MODULE + +char *sim710; /* command line passed by insmod */ + +MODULE_AUTHOR("Richard Hirst"); +MODULE_DESCRIPTION("Simple NCR53C710 driver"); +MODULE_PARM(sim710, "s"); + +#endif + +static int sim710_errors = 0; /* Count of error interrupts */ +static int sim710_intrs = 0; /* Count of all interrupts */ +static int ignore_ids = 0; /* Accept all SCSI IDs */ +static int opt_nodisc = 0; /* Allow disconnect on all IDs */ +static int opt_noneg = 0; /* Allow SDTR negotiation on all IDs */ + +#ifdef CONFIG_TP34V_SCSI + +/* Special hardwired case for Tadpole TP34V at the moment, otherwise + * boot parameters 'sim710=addr:0x8000,irq:15' (for example) must be given. + */ + +static int no_of_boards = 2; + +static unsigned int bases[MAXBOARDS] = { + TP34V_SCSI0_BASE, TP34V_SCSI1_BASE +}; +static unsigned int irq_vectors[MAXBOARDS] = { + TP34V_SCSI0_VECTOR, TP34V_SCSI1_VECTOR +}; +static unsigned int irq_index[MAXBOARDS] = { + TP34V_SCSI0_IRQ_INDEX, TP34V_SCSI1_IRQ_INDEX +}; + +#else + +/* All other cases use boot/module params, or auto-detect */ + +static int no_of_boards = 0; + +static unsigned int bases[MAXBOARDS] = { + 0 +}; +static unsigned int irq_vectors[MAXBOARDS] = { + 0 +}; + +#endif + +/* The SCSI Script!!! */ + +#include "sim710_d.h" + +/* Now define offsets in the DSA, as (A_dsa_xxx/4) */ + +#define DSA_SELECT (A_dsa_select/4) +#define DSA_MSGOUT (A_dsa_msgout/4) +#define DSA_CMND (A_dsa_cmnd/4) +#define DSA_STATUS (A_dsa_status/4) +#define DSA_MSGIN (A_dsa_msgin/4) +#define DSA_DATAIN (A_dsa_datain/4) +#define DSA_DATAOUT (A_dsa_dataout/4) +#define DSA_SIZE (A_dsa_size/4) + +#define MAX_SG 128 /* Scatter/Gather elements */ + + +#define MAX_MSGOUT 8 +#define MAX_MSGIN 8 +#define MAX_CMND 12 +#define MAX_STATUS 1 + +struct sim710_hostdata{ + int state; + Scsi_Cmnd * issue_queue; + Scsi_Cmnd * running; + int chip; + u8 negotiate; + u8 reselected_identify; + u8 msgin_buf[MAX_MSGIN]; + + struct sim710_target { + Scsi_Cmnd *cur_cmd; + u32 resume_offset; + u32 data_in_jump; + u32 data_out_jump; + u32 dsa[DSA_SIZE]; /* SCSI Script DSA area */ + u8 dsa_msgout[MAX_MSGOUT]; + u8 dsa_msgin[MAX_MSGIN]; + u8 dsa_cdb[MAX_CMND]; + u8 dsa_status[MAX_STATUS]; + } target[8]; + + u32 script[sizeof(SCRIPT)/4] __attribute__ ((aligned (4))); +}; + + +/* Template to request asynchronous transfers */ + +static const unsigned char async_message[] = { + EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */}; + + +static void sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs); +static void do_sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs); +static __inline__ void run_process_issue_queue(struct sim710_hostdata *); +static void process_issue_queue (struct sim710_hostdata *, unsigned long flags); +static int full_reset(struct Scsi_Host * host); + +/* + * Function: void sim710_setup(char *str, int *ints) + */ + +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +void +sim710_setup(char *str, int *ints) +{ + char *cur = str; + char *pc, *pv; + int val; + int base; + int c; + + no_of_boards = 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, "addr:", 5)) { + bases[0] = val; + no_of_boards = 1; + } + else if (!strncmp(cur, "irq:", 4)) + irq_vectors[0] = val; + else if (!strncmp(cur, "ignore:", 7)) + ignore_ids = val; + else if (!strncmp(cur, "nodisc:", 7)) + opt_nodisc = val; + else if (!strncmp(cur, "noneg:", 6)) + opt_noneg = val; + else if (!strncmp(cur, "disabled:", 5)) { + no_of_boards = -1; + return; + } +#ifdef DEBUG + else if (!strncmp(cur, "debug:", 6)) { + sim710_debug = val; + } +#endif + else + printk("sim710_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur); + + if ((cur = strchr(cur, ARG_SEP)) != NULL) + ++cur; + } +} + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) +#ifndef MODULE +__setup("sim710=", sim710_setup); +#endif +#endif + +/* + * Function: static const char *sbcl_to_phase (int sbcl) + */ + +static const char * +sbcl_to_phase (int sbcl) { + switch (sbcl & SBCL_PHASE_MASK) { + case SBCL_PHASE_DATAIN: + return "DATAIN"; + case SBCL_PHASE_DATAOUT: + return "DATAOUT"; + case SBCL_PHASE_MSGIN: + return "MSGIN"; + case SBCL_PHASE_MSGOUT: + return "MSGOUT"; + case SBCL_PHASE_CMDOUT: + return "CMDOUT"; + case SBCL_PHASE_STATIN: + return "STATUSIN"; + default: + return "unknown"; + } +} + + +/* + * Function: static void disable (struct Scsi_Host *host) + */ + +static void +disable (struct Scsi_Host *host) +{ + struct sim710_hostdata *hostdata = (struct sim710_hostdata *) + host->hostdata[0]; + + hostdata->state = STATE_DISABLED; + printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n", + host->host_no); +} + + +/* + * Function : static int ncr_halt (struct Scsi_Host *host) + * + * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip + * + * Inputs : host - SCSI chip to halt + * + * Returns : 0 on success + */ + +static int +ncr_halt (struct Scsi_Host *host) +{ + unsigned long flags; + unsigned char istat, tmp; + struct sim710_hostdata *hostdata = (struct sim710_hostdata *) + host->hostdata[0]; + int stage; + + save_flags(flags); + cli(); + /* Stage 0 : eat all interrupts + Stage 1 : set ABORT + Stage 2 : eat all but abort interrupts + Stage 3 : eat all interrupts + */ + for (stage = 0;;) { + if (stage == 1) { + DEB(DEB_HALT, printk("ncr_halt: writing ISTAT_ABRT\n")); + NCR_write8(ISTAT_REG, ISTAT_ABRT); + ++stage; + } + istat = NCR_read8 (ISTAT_REG); + if (istat & ISTAT_SIP) { + DEB(DEB_HALT, printk("ncr_halt: got ISTAT_SIP, istat=%02x\n", istat)); + tmp = NCR_read8(SSTAT0_REG); + DEB(DEB_HALT, printk("ncr_halt: got SSTAT0_REG=%02x\n", tmp)); + } else if (istat & ISTAT_DIP) { + DEB(DEB_HALT, printk("ncr_halt: got ISTAT_DIP, istat=%02x\n", istat)); + tmp = NCR_read8(DSTAT_REG); + DEB(DEB_HALT, printk("ncr_halt: got DSTAT_REG=%02x\n", tmp)); + if (stage == 2) { + if (tmp & DSTAT_ABRT) { + DEB(DEB_HALT, printk("ncr_halt: got DSTAT_ABRT, clearing istat\n")); + NCR_write8(ISTAT_REG, 0); + ++stage; + } else { + printk(KERN_ALERT "scsi%d : could not halt NCR chip\n", + host->host_no); + disable (host); + } + } + } + if (!(istat & (ISTAT_SIP|ISTAT_DIP))) { + if (stage == 0) + ++stage; + else if (stage == 3) + break; + } + } + hostdata->state = STATE_HALTED; + restore_flags(flags); + return 0; +} + +/* + * Function : static void sim710_soft_reset (struct Scsi_Host *host) + * + * Purpose : perform a soft reset of the NCR53c7xx chip + * + * Inputs : host - pointer to this host adapter's structure + * + * Preconditions : sim710_init must have been called for this + * host. + * + */ + +static void +sim710_soft_reset (struct Scsi_Host *host) +{ + unsigned long flags; +#ifdef CONFIG_TP34V_SCSI + struct sim710_hostdata *hostdata = (struct sim710_hostdata *) + host->hostdata[0]; +#endif + + save_flags(flags); + cli(); +#ifdef CONFIG_TP34V_SCSI + tpvic.loc_icr[irq_index[hostdata->chip]].icr = 0x80; +#endif + /* + * Do a soft reset of the chip so that everything is + * reinitialized to the power-on state. + * + * Basically follow the procedure outlined in the NCR53c700 + * data manual under Chapter Six, How to Use, Steps Necessary to + * Start SCRIPTS, with the exception of actually starting the + * script and setting up the synchronous transfer gunk. + */ + + /* XXX Should we reset the scsi bus here? */ + + NCR_write8(SCNTL1_REG, SCNTL1_RST); /* Reset the bus */ + udelay(50); + NCR_write8(SCNTL1_REG, 0); + + udelay(500); + + NCR_write8(ISTAT_REG, ISTAT_10_SRST); /* Reset the chip */ + udelay(50); + NCR_write8(ISTAT_REG, 0); + + mdelay(1000); /* Let devices recover */ + + NCR_write8(DCNTL_REG, DCNTL_10_COM | DCNTL_700_CF_3); + NCR_write8(CTEST7_REG, CTEST7_10_CDIS|CTEST7_STD); + NCR_write8(DMODE_REG, DMODE_10_BL_8 | DMODE_10_FC2); + NCR_write8(SCID_REG, 1 << host->this_id); + NCR_write8(SBCL_REG, 0); + NCR_write8(SXFER_REG, 0); + NCR_write8(SCNTL1_REG, SCNTL1_ESR_700); + NCR_write8(SCNTL0_REG, SCNTL0_EPC | SCNTL0_EPG_700 | SCNTL0_ARB1 | + SCNTL0_ARB2); + + NCR_write8(DIEN_REG, DIEN_700_BF | + DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC); + + NCR_write8(SIEN_REG_700, + SIEN_PAR | SIEN_700_STO | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA); + + +#ifdef CONFIG_TP34V_SCSI + tpvic.loc_icr[irq_index[hostdata->chip]].icr = 0x30 | TP34V_SCSI0n1_IPL; +#endif + + restore_flags(flags); +} + + +/* + * Function : static void sim710_driver_init (struct Scsi_Host *host) + * + * Purpose : Initialize internal structures, as required on startup, or + * after a SCSI bus reset. + * + * Inputs : host - pointer to this host adapter's structure + */ + +static void +sim710_driver_init (struct Scsi_Host *host) +{ + struct sim710_hostdata *hostdata = (struct sim710_hostdata *) + host->hostdata[0]; + int i; + + hostdata->running = NULL; + memcpy (hostdata->script, SCRIPT, sizeof(SCRIPT)); + for (i = 0; i < PATCHES; i++) + hostdata->script[LABELPATCHES[i]] += virt_to_bus(hostdata->script); + patch_abs_32 (hostdata->script, 0, reselected_identify, + virt_to_bus((void *)&(hostdata->reselected_identify))); + patch_abs_32 (hostdata->script, 0, msgin_buf, + virt_to_bus((void *)&(hostdata->msgin_buf[0]))); + hostdata->state = STATE_INITIALISED; + hostdata->negotiate = 0xff; +} + + +/* Handle incoming Synchronous data transfer request. If our negotiate + * flag is set then this is a response to our request, otherwise it is + * spurious request from the target. Don't really expect target initiated + * SDTRs, because we always negotiate on the first command. Could still + * get them though.. + * The chip is currently paused with ACK asserted o the last byte of the + * SDTR. + * resa is the resume address if the message is in response to our outgoing + * SDTR. Only possible on initial identify. + * resb is the resume address if the message exchange is initiated by the + * target. + */ + +static u32 +handle_sdtr (struct Scsi_Host * host, Scsi_Cmnd * cmd, u32 resa, u32 resb) +{ + struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; + struct sim710_target *targdata = hostdata->target + cmd->target; + u32 resume_offset; + + if (resa && hostdata->negotiate & (1 << cmd->target)) { + DEB(DEB_SYNC, printk("scsi%d: Response to host SDTR = %02x %02x\n", + host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4])); + /* We always issue an SDTR with the identify, so we must issue + * the CDB next. + */ + resume_offset = resa; + hostdata->negotiate &= ~(1 << cmd->target); + } + else { + DEB(DEB_SYNC, printk("scsi%d: Target initiated SDTR = %02x %02x\n", + host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4])); + memcpy(targdata->dsa_msgout, async_message, sizeof(async_message)); + targdata->dsa[DSA_MSGOUT] = sizeof(async_message); + /* I guess the target could do this anytime; we have to send our + * response, and then continue (sending the CDB if not already done). + */ + resume_offset = resb; + } + return resume_offset; +} + + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. + * + * Inputs : host - SCSI host + */ + +static int +datapath_residual (struct Scsi_Host *host) { + int count, synchronous, sstat; + unsigned int ddir; + + count = ((NCR_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) - + (NCR_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK; + synchronous = NCR_read8 (SXFER_REG) & SXFER_MO_MASK; + ddir = NCR_read8 (CTEST0_REG_700) & CTEST0_700_DDIR; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (NCR_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; + else + if (NCR_read8 (SSTAT1_REG) & SSTAT1_ILF) + ++count; + } else { + /* Send */ + sstat = NCR_read8 (SSTAT1_REG); + if (sstat & SSTAT1_OLF) + ++count; + if (synchronous && (sstat & SSTAT1_ORF)) + ++count; + } + return count; +} + + +static u32 +handle_idd (struct Scsi_Host * host, Scsi_Cmnd * cmd) +{ + struct sim710_hostdata *hostdata = + (struct sim710_hostdata *)host->hostdata[0]; + struct sim710_target *targdata = hostdata->target + cmd->target; + u32 resume_offset = 0, index; + + index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script); + + switch (index) { + case Ent_wait_disc_complete/4 + 2: + cmd->result = targdata->dsa_status[0]; + SCSI_DONE(cmd); + targdata->cur_cmd = NULL; + resume_offset = Ent_reselect; + break; + case Ent_wait_disc2/4 + 2: + /* Disconnect after command - just wait for a reselect */ + targdata->resume_offset = Ent_resume_msgin2a; + resume_offset = Ent_reselect; + break; + case Ent_wait_disc3/4 + 2: + /* Disconnect after the data phase */ + targdata->resume_offset = Ent_resume_msgin3a; + resume_offset = Ent_reselect; + break; + case Ent_wait_disc1/4 + 2: + /* Disconnect before command - not expected */ + targdata->resume_offset = Ent_resume_msgin1a; + resume_offset = Ent_reselect; + break; + default: + printk("scsi%d: Unexpected Illegal Instruction, script[%04x]\n", + host->host_no, index); + sim710_errors++; + /* resume_offset is zero, which will cause host reset */ + } + return resume_offset; +} + + +/* Handle a phase mismatch. + */ + +static u32 +handle_phase_mismatch (struct Scsi_Host * host, Scsi_Cmnd * cmd) +{ + struct sim710_hostdata *hostdata = + (struct sim710_hostdata *)host->hostdata[0]; + struct sim710_target *targdata = hostdata->target + cmd->target; + u32 resume_offset = 0, index; + unsigned char sbcl; + + sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; + index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script); + + DEB(DEB_PMM, printk("scsi%d: Phase mismatch, phase %s (%x) at script[0x%x]\n", + host->host_no, sbcl_to_phase(sbcl), sbcl, index)); + DEB(DEB_PMM, print_command(cmd->cmnd)); + + if (index == Ent_done_ident/4) { + /* Sending initial message out - probably rejecting our sync + * negotiation request. + */ + NCR_write8(SOCL_REG, 0); /* Negate ATN */ + if (sbcl == SBCL_PHASE_MSGIN) + resume_offset = Ent_resume_rej_ident; + else if (sbcl == SBCL_PHASE_CMDOUT) { + /* Some old devices (SQ555) switch to cmdout after the first + * byte of an identify message, regardless of whether we + * have more bytes to send! + */ + printk("scsi%d: Unexpected switch to CMDOUT during IDENTIFY\n", + host->host_no); + resume_offset = Ent_resume_cmd; + } + else { + printk("scsi%d: Unexpected phase change to %s on initial msgout\n", + host->host_no, sbcl_to_phase(sbcl)); + /* resume_offset is zero, which will cause a host reset */ + } + hostdata->negotiate &= ~(1 << cmd->target); + } + else if (index > Ent_patch_input_data/4 && + index < Ent_patch_output_data/4) { + /* DataIn transfer phase */ + u32 sg_id, oaddr, olen, naddr, nlen; + int residual; + + sg_id = (index - Ent_patch_input_data/4 - 4) / 2; + targdata->data_in_jump = hostdata->script[Ent_patch_input_data/4+1] = + virt_to_bus(hostdata->script + Ent_patch_input_data/4 + sg_id * 2 + 2); + olen = targdata->dsa[DSA_DATAIN + sg_id * 2]; + oaddr = targdata->dsa[DSA_DATAIN + sg_id * 2 + 1]; + residual = datapath_residual (host); + if (residual) + printk("scsi%d: Residual count %d on DataIn - NOT expected!!!", + host->host_no, residual); + naddr = NCR_read32(DNAD_REG) - residual; + nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual; + DEB(DEB_PMM, printk("scsi%d: DIN sg %d, old %08x/%08x, new %08x/%08x (%d)\n", + host->host_no, sg_id, oaddr, olen, naddr, nlen, residual)); + if (oaddr+olen != naddr+nlen) { + printk("scsi%d: PMM DIN counts error: 0x%x + 0x%x != 0x%x + 0x%x", + host->host_no, oaddr, olen, naddr, nlen); + } + else { + targdata->dsa[DSA_DATAIN + sg_id * 2] = nlen; + targdata->dsa[DSA_DATAIN + sg_id * 2 + 1] = naddr; + resume_offset = Ent_resume_pmm; + } + } + else if (index > Ent_patch_output_data/4 && + index <= Ent_end_data_trans/4) { + /* Dataout transfer phase */ + u32 sg_id, oaddr, olen, naddr, nlen; + int residual; + + sg_id = (index - Ent_patch_output_data/4 - 4) / 2; + targdata->data_out_jump = hostdata->script[Ent_patch_output_data/4+1] = + virt_to_bus(hostdata->script + Ent_patch_output_data/4 + sg_id * 2 + 2); + olen = targdata->dsa[DSA_DATAOUT + sg_id * 2]; + oaddr = targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1]; + residual = datapath_residual (host); + naddr = NCR_read32(DNAD_REG) - residual; + nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual; + DEB(DEB_PMM, printk("scsi%d: DOUT sg %d, old %08x/%08x, new %08x/%08x (%d)\n", + host->host_no, sg_id, oaddr, olen, naddr, nlen, residual)); + if (oaddr+olen != naddr+nlen) { + printk("scsi%d: PMM DOUT counts error: 0x%x + 0x%x != 0x%x + 0x%x", + host->host_no, oaddr, olen, naddr, nlen); + } + else { + targdata->dsa[DSA_DATAOUT + sg_id * 2] = nlen; + targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1] = naddr; + resume_offset = Ent_resume_pmm; + } + } + else { + printk("scsi%d: Unexpected phase change to %s at index 0x%x\n", + host->host_no, sbcl_to_phase(sbcl), index); + /* resume_offset is zero, which will cause a host reset */ + } + /* Flush DMA FIFO */ + NCR_write8 (CTEST8_REG, CTEST8_10_CLF); + while (NCR_read8 (CTEST8_REG) & CTEST8_10_CLF); + + return resume_offset; +} + + +static u32 +handle_script_int(struct Scsi_Host * host, Scsi_Cmnd * cmd) +{ + struct sim710_hostdata *hostdata = + (struct sim710_hostdata *)host->hostdata[0]; + struct sim710_target *targdata = hostdata->target + cmd->target; + u32 dsps, resume_offset = 0; + unsigned char sbcl; + + dsps = NCR_read32(DSPS_REG); + + switch (dsps) { + case A_int_cmd_complete: + cmd->result = targdata->dsa_status[0]; + SCSI_DONE(cmd); + targdata->cur_cmd = NULL; + resume_offset = Ent_reselect; + break; + case A_int_msg_sdtr1: + resume_offset = handle_sdtr(host, cmd, + Ent_resume_msgin1a, Ent_resume_msgin1b); + break; + case A_int_msg_sdtr2: + resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin2b); + break; + case A_int_msg_sdtr3: + resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin3b); + break; + case A_int_disc1: + /* Disconnect before command - not expected */ + targdata->resume_offset = Ent_resume_msgin1a; + resume_offset = Ent_reselect; + break; + case A_int_disc2: + /* Disconnect after command - just wait for a reselect */ + targdata->resume_offset = Ent_resume_msgin2a; + resume_offset = Ent_reselect; + break; + case A_int_disc3: + /* Disconnect after the data phase */ + targdata->resume_offset = Ent_resume_msgin3a; + resume_offset = Ent_reselect; + break; + case A_int_reselected: + hostdata->script[Ent_patch_output_data/4+1] = targdata->data_out_jump; + hostdata->script[Ent_patch_input_data/4+1] = targdata->data_in_jump; + NCR_write32(DSA_REG, virt_to_bus(targdata->dsa)); + resume_offset = targdata->resume_offset; + break; + case A_int_data_bad_phase: + sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; + printk("scsi%d: int_data_bad_phase, phase %s (%x)\n", + host->host_no, sbcl_to_phase(sbcl), sbcl); + break; + case A_int_bad_extmsg1a: + case A_int_bad_extmsg1b: + case A_int_bad_extmsg2a: + case A_int_bad_extmsg2b: + case A_int_bad_extmsg3a: + case A_int_bad_extmsg3b: + case A_int_bad_msg1: + case A_int_bad_msg2: + case A_int_bad_msg3: + case A_int_cmd_bad_phase: + case A_int_no_msgout1: + case A_int_no_msgout2: + case A_int_no_msgout3: + case A_int_not_cmd_complete: + case A_int_sel_no_ident: + case A_int_sel_not_cmd: + case A_int_status_not_msgin: + case A_int_resel_not_msgin: + case A_int_selected: + case A_int_not_rej: + default: + sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; + printk("scsi%d: Unimplemented script interrupt: %08x, phase %s\n", + host->host_no, dsps, sbcl_to_phase(sbcl)); + sim710_errors++; + /* resume_offset is zero, which will cause a host reset */ + } + return resume_offset; +} + + +/* A quick wrapper for sim710_intr_handle to grab the spin lock */ + +static void +do_sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + sim710_intr_handle(irq, dev_id, regs); + spin_unlock_irqrestore(&io_request_lock, flags); +} + + +/* A "high" level interrupt handler */ + +static void +sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int flags; + struct Scsi_Host * host = (struct Scsi_Host *)dev_id; + struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; + Scsi_Cmnd * cmd; + unsigned char istat, dstat; + unsigned char sstat0; + u32 dsps, resume_offset = 0; + + save_flags(flags); + cli(); + sim710_intrs++; + while ((istat = NCR_read8(ISTAT_REG)) & (ISTAT_SIP|ISTAT_DIP)) { + dsps = NCR_read32(DSPS_REG); + hostdata->state = STATE_HALTED; + sstat0 = dstat = 0; + if (istat & ISTAT_SIP) { + sstat0 = NCR_read8(SSTAT0_REG); + } + if (istat & ISTAT_DIP) { + udelay(10); /* Some comment somewhere about 10cycles + * between accesses to sstat0 and dstat ??? */ + dstat = NCR_read8(DSTAT_REG); + } + DEB(DEB_INTS, printk("scsi%d: Int %d, istat %02x, sstat0 %02x " + "dstat %02x, dsp [%04x], scratch %02x\n", + host->host_no, sim710_intrs, istat, sstat0, dstat, + (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script, + NCR_read32(SCRATCH_REG))); + if ((dstat & DSTAT_SIR) && dsps == A_int_reselected) { + /* Reselected. Identify the target from LCRC_REG, and + * update current command. If we were trying to select + * a device, then that command needs to go back on the + * issue_queue for later. + */ + unsigned char lcrc = NCR_read8(LCRC_REG_10); + int id = 0; + + if (!(lcrc & 0x7f)) { + printk("scsi%d: Reselected with LCRC = %02x\n", + host->host_no, lcrc); + cmd = NULL; + } + else { + while (!(lcrc & 1)) { + id++; + lcrc >>= 1; + } + DEB(DEB_DISC, printk("scsi%d: Reselected by ID %d\n", + host->host_no, id)); + if (hostdata->running) { + /* Clear SIGP */ + (void)NCR_read8(CTEST2_REG_700); + + DEB(DEB_DISC, printk("scsi%d: Select of %d interrupted " + "by reselect from %d (%p)\n", + host->host_no, hostdata->running->target, + id, hostdata->target[id].cur_cmd)); + cmd = hostdata->running; + hostdata->target[cmd->target].cur_cmd = NULL; + cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; + hostdata->issue_queue = cmd; + } + cmd = hostdata->running = hostdata->target[id].cur_cmd; + } + } + else + cmd = hostdata->running; + + if (!cmd) { + printk("scsi%d: No active command!\n", host->host_no); + printk("scsi%d: Int %d, istat %02x, sstat0 %02x " + "dstat %02x, dsp [%04x], scratch %02x, dsps %08x\n", + host->host_no, sim710_intrs, istat, sstat0, dstat, + (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script, + NCR_read32(SCRATCH_REG), dsps); + /* resume_offset is zero, which will cause a host reset */ + } + else if (sstat0 & SSTAT0_700_STO) { + DEB(DEB_TOUT, printk("scsi%d: Selection timeout\n", host->host_no)); + cmd->result = DID_NO_CONNECT << 16; + SCSI_DONE(cmd); + hostdata->target[cmd->target].cur_cmd = NULL; + resume_offset = Ent_reselect; + } + else if (dstat & DSTAT_SIR) + resume_offset = handle_script_int(host, cmd); + else if (sstat0 & SSTAT0_MA) { + resume_offset = handle_phase_mismatch(host, cmd); + } + else if (sstat0 & (SSTAT0_MA|SSTAT0_SGE|SSTAT0_UDC|SSTAT0_RST|SSTAT0_PAR)) { + printk("scsi%d: Serious error, sstat0 = %02x\n", host->host_no, + sstat0); + sim710_errors++; + /* resume_offset is zero, which will cause a host reset */ + } + else if (dstat & (DSTAT_BF|DSTAT_ABRT|DSTAT_SSI|DSTAT_WTD)) { + printk("scsi%d: Serious error, dstat = %02x\n", host->host_no, + dstat); + sim710_errors++; + /* resume_offset is zero, which will cause a host reset */ + } + else if (dstat & DSTAT_IID) { + /* This can be due to a quick reselect while doing a WAIT + * DISCONNECT. + */ + resume_offset = handle_idd(host, cmd); + } + else { + sim710_errors++; + printk("scsi%d: Spurious interrupt!\n", host->host_no); + /* resume_offset is zero, which will cause a host reset */ + } + } + + if (resume_offset) { + if (resume_offset == Ent_reselect) { + hostdata->running = NULL; + hostdata->state = STATE_IDLE; + } + else + hostdata->state = STATE_BUSY; + DEB(DEB_RESUME, printk("scsi%d: Resuming at script[0x%x]\n", + host->host_no, resume_offset/4)); +#ifdef DEBUG_LIMIT_INTS + if (sim710_intrs < DEBUG_LIMIT_INTS) +#endif + NCR_write32(DSP_REG, virt_to_bus(hostdata->script+resume_offset/4)); + if (resume_offset == Ent_reselect) + run_process_issue_queue(hostdata); + } + else { + printk("scsi%d: Failed to handle interrupt. Failing commands " + "and resetting SCSI bus and chip\n", host->host_no); + mdelay(4000); /* Give chance to read screen!! */ + full_reset(host); + } + + restore_flags(flags); +} + + +static void +run_command (struct sim710_hostdata *hostdata, Scsi_Cmnd *cmd) +{ + struct Scsi_Host *host = cmd->host; + struct sim710_target *targdata = hostdata->target + cmd->target; + int i, datain, dataout, sg_start; + u32 *dip, *dop, dsa; + + DEB(DEB_CMND, printk("scsi%d: id%d starting ", host->host_no, + cmd->target)); + DEB(DEB_CMND, print_command(cmd->cmnd)); + + switch (cmd->cmnd[0]) { + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_CAPACITY: + case REQUEST_SENSE: + case READ_BLOCK_LIMITS: + case READ_TOC: + datain = 1; + dataout = 0; + break; + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + datain = 0; + dataout = 1; + break; + case TEST_UNIT_READY: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + datain = dataout = 0; + break; + default: + datain = dataout = 1; + } + + memcpy(targdata->dsa_cdb, cmd->cmnd, MAX_CMND); + + targdata->dsa_msgout[0] = + IDENTIFY((opt_nodisc & (1<<cmd->target)) ? 0 : 1 ,0); + if (hostdata->negotiate & (1 << cmd->target)) { + if (opt_noneg & (1 << cmd->target)) { + hostdata->negotiate ^= (1 << cmd->target); + targdata->dsa[DSA_MSGOUT] = 1; + } + else { + DEB(DEB_SYNC, printk("scsi%d: Negotiating async transfers " + "for ID %d\n", + host->host_no, cmd->target)); + memcpy(targdata->dsa_msgout+1, async_message, sizeof(async_message)); + targdata->dsa[DSA_MSGOUT] = sizeof(async_message) + 1; + } + } + else + targdata->dsa[DSA_MSGOUT] = 1; + + targdata->dsa_msgin[0] = 0xff; + targdata->dsa_status[0] = 0xff; + + targdata->dsa[DSA_SELECT] = (1 << cmd->target) << 16; + targdata->dsa[DSA_MSGOUT+1] = virt_to_bus(targdata->dsa_msgout); + targdata->dsa[DSA_CMND] = cmd->cmd_len; + targdata->dsa[DSA_CMND+1] = virt_to_bus(targdata->dsa_cdb); + targdata->dsa[DSA_STATUS] = 1; + targdata->dsa[DSA_STATUS+1] = virt_to_bus(targdata->dsa_status); + targdata->dsa[DSA_MSGIN] = 1; + targdata->dsa[DSA_MSGIN+1] = virt_to_bus(targdata->dsa_msgin); + + sg_start = (MAX_SG - (cmd->use_sg ? cmd->use_sg : 1)) * 2; + dip = targdata->dsa + DSA_DATAIN + sg_start; + dop = targdata->dsa + DSA_DATAOUT + sg_start; + + for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; i++) { + u32 vbuf = cmd->use_sg ? + (u32)(((struct scatterlist *)cmd->buffer)[i].address) : + (u32)(cmd->request_buffer); + u32 bbuf = virt_to_bus((void *)vbuf); + u32 cnt = cmd->use_sg ? + ((struct scatterlist *)cmd->buffer)[i].length : + cmd->request_bufflen; + + if (datain) { +#ifdef CONFIG_TP34V_SCSI + cache_clear(virt_to_phys((void *)vbuf), cnt); +#endif + *dip++ = cnt; + *dip++ = bbuf; + } + if (dataout) { +#ifdef CONFIG_TP34V_SCSI + cache_push(virt_to_phys((void *)vbuf), cnt); +#endif + *dop++ = cnt; + *dop++ = bbuf; + } + } + targdata->data_out_jump = hostdata->script[Ent_patch_output_data/4+1] = + virt_to_bus(hostdata->script + Ent_patch_output_data/4 + sg_start + 2); + targdata->data_in_jump = hostdata->script[Ent_patch_input_data/4+1] = + virt_to_bus(hostdata->script + Ent_patch_input_data/4 + sg_start + 2); + + for (i = 0, dsa = virt_to_bus(targdata->dsa); i < 4; i++) { + u32 v = hostdata->script[Ent_patch_new_dsa/4 + i * 2]; + + v &= ~0x0000ff00; + v |= (dsa & 0xff) << 8; + hostdata->script[Ent_patch_new_dsa/4 + i * 2] = v; + dsa >>= 8; + } + hostdata->running = targdata->cur_cmd = cmd; + hostdata->state = STATE_BUSY; + + NCR_write8(ISTAT_REG, ISTAT_10_SIGP); +} + + +static volatile int process_issue_queue_running = 0; + +static __inline__ void +run_process_issue_queue(struct sim710_hostdata *hostdata) +{ + unsigned long flags; + save_flags (flags); + cli(); + if (!process_issue_queue_running) { + process_issue_queue_running = 1; + process_issue_queue(hostdata, flags); + /* + * process_issue_queue_running is cleared in process_issue_queue + * once it can't do more work, and process_issue_queue exits with + * interrupts disabled. + */ + } + restore_flags (flags); +} + + +/* + * Function : process_issue_queue (hostdata, flags) + * + * Purpose : Start next command for any idle target. + * + * NOTE : process_issue_queue exits with interrupts *disabled*, so the + * caller must reenable them if it desires. + * + * NOTE : process_issue_queue should be called from both + * sim710_queue_command() and from the interrupt handler + * after command completion. + */ + +static void +process_issue_queue (struct sim710_hostdata *hostdata, unsigned long flags) +{ + Scsi_Cmnd *tmp, *prev; + int done; + + /* + * We run (with interrupts disabled) until we're sure that none of + * the host adapters have anything that can be done, at which point + * we set process_issue_queue_running to 0 and exit. + * + * Interrupts are enabled before doing various other internal + * instructions, after we've decided that we need to run through + * the loop again. + * + */ + + do { + cli(); /* Freeze request queues */ + done = 1; + if (hostdata->issue_queue) { + if (hostdata->state == STATE_DISABLED) { + tmp = (Scsi_Cmnd *) hostdata->issue_queue; + hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; + tmp->result = (DID_BAD_TARGET << 16); + tmp->scsi_done (tmp); + done = 0; + } + else if (hostdata->state == STATE_IDLE) { + for (tmp = hostdata->issue_queue, prev = NULL; tmp; + prev = tmp, tmp = (Scsi_Cmnd *) tmp->SCp.ptr) { + if (hostdata->target[tmp->target].cur_cmd == NULL) { + if (prev) + prev->SCp.ptr = tmp->SCp.ptr; + else + hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr; + tmp->SCp.ptr = NULL; + run_command (hostdata, tmp); + done = 0; + } /* if target/lun is not busy */ + } /* scan issue queue for work */ + } /* host is idle */ + } /* if hostdata->issue_queue */ + if (!done) + restore_flags (flags); + } while (!done); + process_issue_queue_running = 0; +} + + +int +sim710_queuecommand(Scsi_Cmnd * cmd, void (*done)(Scsi_Cmnd *)) +{ + struct Scsi_Host *host = cmd->host; + struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; + Scsi_Cmnd *tmp; + unsigned long flags; + + if (cmd->lun) { + /* Silently ignore luns other than zero! */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return 0; + } + + DEB(DEB_CMND, printk("scsi%d: id%d queuing ", host->host_no, + cmd->target)); + DEB(DEB_CMND, print_command(cmd->cmnd)); + + cmd->scsi_done = done; + cmd->host_scribble = NULL; + cmd->SCp.ptr = NULL; + cmd->SCp.buffer = NULL; + + save_flags(flags); + cli(); + + if (ignore_ids & (1 << cmd->target)) { + printk("scsi%d: ignoring target %d\n", host->host_no, cmd->target); + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + restore_flags (flags); + return 0; + } +#ifdef DEBUG_LIMIT_INTS + if (sim710_intrs > DEBUG_LIMIT_INTS) { + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + restore_flags (flags); + return 0; + } +#endif + if (cmd->use_sg > MAX_SG) + panic ("cmd->use_sg = %d\n", cmd->use_sg); + + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; + hostdata->issue_queue = cmd; + } else { + for (tmp = hostdata->issue_queue; tmp->SCp.ptr; + tmp = (Scsi_Cmnd *) tmp->SCp.ptr); + tmp->SCp.ptr = (unsigned char *) cmd; + } + restore_flags (flags); + run_process_issue_queue(hostdata); + return 0; +} + + +int +sim710_detect(Scsi_Host_Template * tpnt) +{ + unsigned char irq_vector; + unsigned char scsi_id; + unsigned int base_addr; + struct Scsi_Host * host = NULL; + struct sim710_hostdata *hostdata; + int chips = 0; + int indx; + int revision; + int order, size; + +#ifdef MODULE + if (sim710) + sim710_setup(sim710, (int *)0); +#endif + + if (no_of_boards < 0) { + printk("sim710: NCR53C710 driver disabled\n"); + return 0; + } + +#ifdef CONFIG_MCA + /* If board details have been specified via boot/module parameters, + * then don't bother probing. + */ + if (no_of_boards == 0) { + int slot; + int pos[3]; + int mca_53c710_ids[] = MCA_53C710_IDS; + int *id_to_check = mca_53c710_ids; + static int io_004f_by_pos[] = MCA_004F_IO_PORTS; + static int irq_004f_by_pos[] = MCA_004F_IRQS; + static int io_01bb_by_pos[] = MCA_01BB_IO_PORTS; + static int irq_01bb_by_pos[] = MCA_01BB_IRQS; + + while ( *id_to_check && no_of_boards < MAXBOARDS) { + if (!MCA_bus) + return 0; + + if ((slot = mca_find_adapter(*id_to_check, 0)) != MCA_NOTFOUND) { + + pos[0] = mca_read_stored_pos(slot, 2); + pos[1] = mca_read_stored_pos(slot, 3); + pos[2] = mca_read_stored_pos(slot, 4); + + /* + * 01BB & 01BA port base by bits 7,6,5,4,3,2 in pos[2] + * + * 000000 <disabled> 001010 0x2800 + * 000001 <invalid> 001011 0x2C00 + * 000010 0x0800 001100 0x3000 + * 000011 0x0C00 001101 0x3400 + * 000100 0x1000 001110 0x3800 + * 000101 0x1400 001111 0x3C00 + * 000110 0x1800 010000 0x4000 + * 000111 0x1C00 010001 0x4400 + * 001000 0x2000 010010 0x4800 + * 001001 0x2400 010011 0x4C00 + * 010100 0x5000 + * + * 00F4 port base by bits 3,2,1 in pos[0] + * + * 000 <disabled> 001 0x200 + * 010 0x300 011 0x400 + * 100 0x500 101 0x600 + * + * 01BB & 01BA IRQ is specified in pos[0] bits 7 and 6: + * + * 00 3 10 11 + * 01 5 11 14 + * + * 00F4 IRQ specified by bits 6,5,4 in pos[0] + * + * 100 5 101 9 + * 110 14 + */ + + if ( *id_to_check == 0x01bb || *id_to_check == 0x01ba ) { + bases[no_of_boards] = io_01bb_by_pos[(pos[2] & 0xFC) >> 2]; + irq_vectors[no_of_boards] = + irq_01bb_by_pos[((pos[0] & 0xC0) >> 6)]; + if (bases[no_of_boards] == 0x0000) + printk("sim710: NCR53C710 Adapter ID 0x01bb is disabled.\n"); + else { + no_of_boards++; + if ( *id_to_check == 0x01bb ) + mca_set_adapter_name( slot, + "NCR 3360/3430 SCSI SubSystem" ); + else + mca_set_adapter_name(slot, + "NCR Dual SIOP SCSI Host Adapter Board"); + } + } + else if ( *id_to_check == 0x004f ) { + bases[no_of_boards] = io_004f_by_pos[((pos[0] & 0x0E) >> 1)]; + irq_vectors[no_of_boards] = + irq_004f_by_pos[((pos[0] & 0x70) >> 4) - 4]; + if (bases[no_of_boards] == 0x0000) + printk("sim710: NCR53C710 Adapter ID 0x004f is disabled.\n"); + else { + no_of_boards++; + mca_set_adapter_name(slot, + "NCR 53c710 SCSI Host Adapter Board"); + } + } + } + id_to_check++; + } + } +#endif + + if (!no_of_boards) { + printk("sim710: No NCR53C710 adapter found.\n"); + return 0; + } + + size = sizeof(struct sim710_hostdata); + order = 0; + while (size > (PAGE_SIZE << order)) + order++; + size = PAGE_SIZE << order; + + DEB(DEB_ANY, printk("sim710: hostdata %d bytes, size %d, order %d\n", + sizeof(struct sim710_hostdata), size, order)); + + tpnt->proc_dir = &proc_scsi_sim710; + + for(indx = 0; indx < no_of_boards; indx++) { + host = scsi_register(tpnt, 4); + host->hostdata[0] = __get_free_pages(GFP_ATOMIC, order); + if (host->hostdata[0] == 0) + panic ("sim710: Couldn't get hostdata memory"); + hostdata = (struct sim710_hostdata *)host->hostdata[0]; + memset(hostdata, 0, size); +#ifdef CONFIG_TP34V_SCSI + cache_push(virt_to_phys(hostdata), size); + cache_clear(virt_to_phys(hostdata), size); + kernel_set_cachemode((void *)hostdata,size,IOMAP_NOCACHE_SER); +#endif + scsi_id = 7; + base_addr = bases[indx]; + irq_vector = irq_vectors[indx]; + printk("sim710: Configuring Sim710 (SCSI-ID %d) at %x, IRQ %d\n", + scsi_id, base_addr, irq_vector); + DEB(DEB_ANY, printk("sim710: hostdata = %p (%d bytes), dsa0 = %p\n", + hostdata, sizeof(struct sim710_hostdata), + hostdata->target[0].dsa)); + hostdata->chip = indx; + host->irq = irq_vector; + host->this_id = scsi_id; + host->unique_id = base_addr; + host->base = (char *)base_addr; + + ncr_halt(host); + + revision = (NCR_read8(CTEST8_REG) & 0xF0) >> 4; + printk("scsi%d: Revision 0x%x\n",host->host_no,revision); + + sim710_soft_reset(host); + + sim710_driver_init(host); + +#ifdef CONFIG_TP34V_SCSI + if (request_irq(irq_vector,do_sim710_intr_handle, 0, "sim710", host)) +#else + if (request_irq(irq_vector,do_sim710_intr_handle, SA_INTERRUPT, "sim710", host)) +#endif + { + printk("scsi%d : IRQ%d not free, detaching\n", + host->host_no, host->irq); + + scsi_unregister (host); + } + else { +#ifdef IO_MAPPED + request_region((u32)host->base, 64, "sim710"); +#endif + chips++; + } + NCR_write32(DSP_REG, virt_to_bus(hostdata->script+Ent_reselect/4)); + hostdata->state = STATE_IDLE; + } + return chips; +} + +int +sim710_abort(Scsi_Cmnd * cmd) +{ + struct Scsi_Host * host = cmd->host; + + printk("scsi%d: Unable to abort command for target %d\n", + host->host_no, cmd->target); + return FAILED; +} + +/* + * This is a device reset. Need to select and send a Bus Device Reset msg. + */ + +int +sim710_dev_reset(Scsi_Cmnd * SCpnt) +{ + struct Scsi_Host * host = SCpnt->host; + + printk("scsi%d: Unable to send Bus Device Reset for target %d\n", + host->host_no, SCpnt->target); + return FAILED; +} + +/* + * This is bus reset. We need to reset the bus and fail any active commands. + */ + +int +sim710_bus_reset(Scsi_Cmnd * SCpnt) +{ + struct Scsi_Host * host = SCpnt->host; + + printk("scsi%d: Unable to do SCSI bus reset\n", host->host_no); + return FAILED; +} + +static int +full_reset(struct Scsi_Host * host) +{ + struct sim710_hostdata *hostdata = (struct sim710_hostdata *) + host->hostdata[0]; + int target; + Scsi_Cmnd *cmd; + + ncr_halt(host); + printk("scsi%d: dsp = %08x (script[0x%04x]), scratch = %08x\n", + host->host_no, NCR_read32(DSP_REG), + ((u32)bus_to_virt(NCR_read32(DSP_REG)) - (u32)hostdata->script)/4, + NCR_read32(SCRATCH_REG)); + + for (target = 0; target < 7; target++) { + if ((cmd = hostdata->target[target].cur_cmd)) { + printk("scsi%d: Failing command for ID%d\n", + host->host_no, target); + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + hostdata->target[target].cur_cmd = NULL; + } + } + + sim710_soft_reset(host); + sim710_driver_init(host); + + NCR_write32(DSP_REG, virt_to_bus(hostdata->script+Ent_reselect/4)); + hostdata->state = STATE_IDLE; + + run_process_issue_queue(hostdata); + + return SUCCESS; +} + +/* + * This is host reset. We need to reset the chip and the bus. + */ + +int +sim710_host_reset(Scsi_Cmnd * SCpnt) +{ + struct Scsi_Host * host = SCpnt->host; + + printk("scsi%d: >>>>>>>>>>>> Host reset <<<<<<<<<<<<\n", host->host_no); + + return full_reset(host); +} + +#ifdef MODULE + +int +sim710_release(struct Scsi_Host *host) +{ + free_irq(host->irq, host); +#ifdef IO_MAPPED + release_region((u32)host->base, 64); +#endif + return 1; +} + +Scsi_Host_Template driver_template = SIM710_SCSI; + +#include "scsi_module.c" +#endif diff --git a/drivers/scsi/sim710.h b/drivers/scsi/sim710.h new file mode 100644 index 000000000..5511ed455 --- /dev/null +++ b/drivers/scsi/sim710.h @@ -0,0 +1,845 @@ +#ifndef _SIM710_H +#define _SIM710_H + +/* + * sim710.h - Copyright (C) 1999 Richard Hirst + */ + +#include <linux/types.h> + +int sim710_detect(Scsi_Host_Template *); +int sim710_command(Scsi_Cmnd *); +int sim710_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int sim710_abort(Scsi_Cmnd * SCpnt); +int sim710_bus_reset(Scsi_Cmnd * SCpnt); +int sim710_dev_reset(Scsi_Cmnd * SCpnt); +int sim710_host_reset(Scsi_Cmnd * SCpnt); +int sim710_biosparam(Disk *, kdev_t, int*); +#ifdef MODULE +int sim710_release(struct Scsi_Host *); +#else +#define sim710_release NULL +#endif + +#if defined(HOSTS_C) || defined(MODULE) +#include <scsi/scsicam.h> + +extern struct proc_dir_entry proc_scsi_sim710; + +#define SIM710_SCSI { proc_dir: &proc_scsi_sim710, \ + name: "Simple 53c710", \ + detect: sim710_detect, \ + release: sim710_release, \ + queuecommand: sim710_queuecommand, \ + eh_abort_handler: sim710_abort, \ + eh_device_reset_handler: sim710_dev_reset, \ + eh_bus_reset_handler: sim710_bus_reset, \ + eh_host_reset_handler: sim710_host_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 8, \ + this_id: 7, \ + sg_tablesize: 128, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, \ + use_new_eh_code: 1} + +#endif + +#ifndef HOSTS_C + +#ifdef __BIG_ENDIAN +#define bE 3 /* 0 for little endian, 3 for big endian */ +#else +#define bE 0 +#endif + +/* SCSI control 0 rw, default = 0xc0 */ +#define SCNTL0_REG (0x00^bE) +#define SCNTL0_ARB1 0x80 /* 0 0 = simple arbitration */ +#define SCNTL0_ARB2 0x40 /* 1 1 = full arbitration */ +#define SCNTL0_STRT 0x20 /* Start Sequence */ +#define SCNTL0_WATN 0x10 /* Select with ATN */ +#define SCNTL0_EPC 0x08 /* Enable parity checking */ +/* Bit 2 is reserved on 800 series chips */ +#define SCNTL0_EPG_700 0x04 /* Enable parity generation */ +#define SCNTL0_AAP 0x02 /* ATN/ on parity error */ +#define SCNTL0_TRG 0x01 /* Target mode */ + +/* SCSI control 1 rw, default = 0x00 */ + +#define SCNTL1_REG (0x01^bE) +#define SCNTL1_EXC 0x80 /* Extra Clock Cycle of Data setup */ +#define SCNTL1_ADB 0x40 /* contents of SODL on bus */ +#define SCNTL1_ESR_700 0x20 /* Enable SIOP response to selection + and reselection */ +#define SCNTL1_CON 0x10 /* Connected */ +#define SCNTL1_RST 0x08 /* SCSI RST/ */ +#define SCNTL1_AESP 0x04 /* Force bad parity */ +#define SCNTL1_SND_700 0x02 /* Start SCSI send */ +#define SCNTL1_IARB_800 0x02 /* Immediate Arbitration, start + arbitration immediately after + busfree is detected */ +#define SCNTL1_RCV_700 0x01 /* Start SCSI receive */ +#define SCNTL1_SST_800 0x01 /* Start SCSI transfer */ + +/* SCSI control 2 rw, */ + +#define SCNTL2_REG_800 (0x02^bE) +#define SCNTL2_800_SDU 0x80 /* SCSI disconnect unexpected */ + +/* SCSI control 3 rw */ + +#define SCNTL3_REG_800 (0x03^bE) +#define SCNTL3_800_SCF_SHIFT 4 +#define SCNTL3_800_SCF_MASK 0x70 +#define SCNTL3_800_SCF2 0x40 /* Synchronous divisor */ +#define SCNTL3_800_SCF1 0x20 /* 0x00 = SCLK/3 */ +#define SCNTL3_800_SCF0 0x10 /* 0x10 = SCLK/1 */ + /* 0x20 = SCLK/1.5 + 0x30 = SCLK/2 + 0x40 = SCLK/3 */ + +#define SCNTL3_800_CCF_SHIFT 0 +#define SCNTL3_800_CCF_MASK 0x07 +#define SCNTL3_800_CCF2 0x04 /* 0x00 50.01 to 66 */ +#define SCNTL3_800_CCF1 0x02 /* 0x01 16.67 to 25 */ +#define SCNTL3_800_CCF0 0x01 /* 0x02 25.01 - 37.5 + 0x03 37.51 - 50 + 0x04 50.01 - 66 */ + +/* + * SCSI destination ID rw - the appropriate bit is set for the selected + * target ID. This is written by the SCSI SCRIPTS processor. + * default = 0x00 + */ +#define SDID_REG_700 (0x02^bE) +#define SDID_REG_800 (0x06^bE) + +#define GP_REG_800 (0x07^bE) /* General purpose IO */ +#define GP_800_IO1 0x02 +#define GP_800_IO2 0x01 + +/* SCSI interrupt enable rw, default = 0x00 */ +#define SIEN_REG_700 (0x03^bE) +#define SIEN0_REG_800 (0x40^bE) +#define SIEN_MA 0x80 /* Phase mismatch (ini) or ATN (tgt) */ +#define SIEN_FC 0x40 /* Function complete */ +#define SIEN_700_STO 0x20 /* Selection or reselection timeout */ +#define SIEN_800_SEL 0x20 /* Selected */ +#define SIEN_700_SEL 0x10 /* Selected or reselected */ +#define SIEN_800_RESEL 0x10 /* Reselected */ +#define SIEN_SGE 0x08 /* SCSI gross error */ +#define SIEN_UDC 0x04 /* Unexpected disconnect */ +#define SIEN_RST 0x02 /* SCSI RST/ received */ +#define SIEN_PAR 0x01 /* Parity error */ + +/* + * SCSI chip ID rw + * NCR53c700 : + * When arbitrating, the highest bit is used, when reselection or selection + * occurs, the chip responds to all IDs for which a bit is set. + * default = 0x00 + */ +#define SCID_REG (0x04^bE) +/* Bit 7 is reserved on 800 series chips */ +#define SCID_800_RRE 0x40 /* Enable response to reselection */ +#define SCID_800_SRE 0x20 /* Enable response to selection */ +/* Bits four and three are reserved on 800 series chips */ +#define SCID_800_ENC_MASK 0x07 /* Encoded SCSI ID */ + +/* SCSI transfer rw, default = 0x00 */ +#define SXFER_REG (0x05^bE) +#define SXFER_DHP 0x80 /* Disable halt on parity */ + +#define SXFER_TP2 0x40 /* Transfer period msb */ +#define SXFER_TP1 0x20 +#define SXFER_TP0 0x10 /* lsb */ +#define SXFER_TP_MASK 0x70 +/* FIXME : SXFER_TP_SHIFT == 5 is right for '8xx chips */ +#define SXFER_TP_SHIFT 5 +#define SXFER_TP_4 0x00 /* Divisors */ +#define SXFER_TP_5 0x10<<1 +#define SXFER_TP_6 0x20<<1 +#define SXFER_TP_7 0x30<<1 +#define SXFER_TP_8 0x40<<1 +#define SXFER_TP_9 0x50<<1 +#define SXFER_TP_10 0x60<<1 +#define SXFER_TP_11 0x70<<1 + +#define SXFER_MO3 0x08 /* Max offset msb */ +#define SXFER_MO2 0x04 +#define SXFER_MO1 0x02 +#define SXFER_MO0 0x01 /* lsb */ +#define SXFER_MO_MASK 0x0f +#define SXFER_MO_SHIFT 0 + +/* + * SCSI output data latch rw + * The contents of this register are driven onto the SCSI bus when + * the Assert Data Bus bit of the SCNTL1 register is set and + * the CD, IO, and MSG bits of the SOCL register match the SCSI phase + */ +#define SODL_REG_700 (0x06^bE) +#define SODL_REG_800 (0x54^bE) + + +/* + * SCSI output control latch rw, default = 0 + * Note that when the chip is being manually programmed as an initiator, + * the MSG, CD, and IO bits must be set correctly for the phase the target + * is driving the bus in. Otherwise no data transfer will occur due to + * phase mismatch. + */ + +#define SOCL_REG (0x07^bE) +#define SOCL_REQ 0x80 /* REQ */ +#define SOCL_ACK 0x40 /* ACK */ +#define SOCL_BSY 0x20 /* BSY */ +#define SOCL_SEL 0x10 /* SEL */ +#define SOCL_ATN 0x08 /* ATN */ +#define SOCL_MSG 0x04 /* MSG */ +#define SOCL_CD 0x02 /* C/D */ +#define SOCL_IO 0x01 /* I/O */ + +/* + * SCSI first byte received latch ro + * This register contains the first byte received during a block MOVE + * SCSI SCRIPTS instruction, including + * + * Initiator mode Target mode + * Message in Command + * Status Message out + * Data in Data out + * + * It also contains the selecting or reselecting device's ID and our + * ID. + * + * Note that this is the register the various IF conditionals can + * operate on. + */ +#define SFBR_REG (0x08^bE) + +/* + * SCSI input data latch ro + * In initiator mode, data is latched into this register on the rising + * edge of REQ/. In target mode, data is latched on the rising edge of + * ACK/ + */ +#define SIDL_REG_700 (0x09^bE) +#define SIDL_REG_800 (0x50^bE) + +/* + * SCSI bus data lines ro + * This register reflects the instantaneous status of the SCSI data + * lines. Note that SCNTL0 must be set to disable parity checking, + * otherwise reading this register will latch new parity. + */ +#define SBDL_REG_700 (0x0a^bE) +#define SBDL_REG_800 (0x58^bE) + +#define SSID_REG_800 (0x0a^bE) +#define SSID_800_VAL 0x80 /* Exactly two bits asserted at sel */ +#define SSID_800_ENCID_MASK 0x07 /* Device which performed operation */ + + + +/* + * SCSI bus control lines rw, + * instantaneous readout of control lines + */ +#define SBCL_REG (0x0b^bE) +#define SBCL_REQ 0x80 /* REQ ro */ +#define SBCL_ACK 0x40 /* ACK ro */ +#define SBCL_BSY 0x20 /* BSY ro */ +#define SBCL_SEL 0x10 /* SEL ro */ +#define SBCL_ATN 0x08 /* ATN ro */ +#define SBCL_MSG 0x04 /* MSG ro */ +#define SBCL_CD 0x02 /* C/D ro */ +#define SBCL_IO 0x01 /* I/O ro */ +#define SBCL_PHASE_CMDOUT SBCL_CD +#define SBCL_PHASE_DATAIN SBCL_IO +#define SBCL_PHASE_DATAOUT 0 +#define SBCL_PHASE_MSGIN (SBCL_CD|SBCL_IO|SBCL_MSG) +#define SBCL_PHASE_MSGOUT (SBCL_CD|SBCL_MSG) +#define SBCL_PHASE_STATIN (SBCL_CD|SBCL_IO) +#define SBCL_PHASE_MASK (SBCL_CD|SBCL_IO|SBCL_MSG) +/* + * Synchronous SCSI Clock Control bits + * 0 - set by DCNTL + * 1 - SCLK / 1.0 + * 2 - SCLK / 1.5 + * 3 - SCLK / 2.0 + */ +#define SBCL_SSCF1 0x02 /* wo, -66 only */ +#define SBCL_SSCF0 0x01 /* wo, -66 only */ +#define SBCL_SSCF_MASK 0x03 + +/* + * XXX note : when reading the DSTAT and STAT registers to clear interrupts, + * insure that 10 clocks elapse between the two + */ +/* DMA status ro */ +#define DSTAT_REG (0x0c^bE) +#define DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define DSTAT_800_MDPE 0x40 /* Master Data Parity Error */ +#define DSTAT_BF 0x20 /* Bus Fault */ +#define DSTAT_ABRT 0x10 /* Aborted - set on error */ +#define DSTAT_SSI 0x08 /* SCRIPTS single step interrupt */ +#define DSTAT_SIR 0x04 /* SCRIPTS interrupt received - + set when INT instruction is + executed */ +#define DSTAT_WTD 0x02 /* Watchdog timeout detected */ +#define DSTAT_OPC 0x01 /* Illegal instruction */ +#define DSTAT_IID 0x01 /* Same thing, different name */ + + +#define SSTAT0_REG (0x0d^bE) /* SCSI status 0 ro */ +#define SIST0_REG_800 (0x42^bE) /* SCSI status 0 ro */ +#define SSTAT0_MA 0x80 /* ini : phase mismatch, + * tgt : ATN/ asserted + */ +#define SSTAT0_CMP 0x40 /* function complete */ +#define SSTAT0_700_STO 0x20 /* Selection or reselection timeout */ +#define SSTAT0_800_SEL 0x20 /* Selected */ +#define SSTAT0_700_SEL 0x10 /* Selected or reselected */ +#define SIST0_800_RSL 0x10 /* Reselected */ +#define SSTAT0_SGE 0x08 /* SCSI gross error */ +#define SSTAT0_UDC 0x04 /* Unexpected disconnect */ +#define SSTAT0_RST 0x02 /* SCSI RST/ received */ +#define SSTAT0_PAR 0x01 /* Parity error */ + +#define SSTAT1_REG (0x0e^bE) /* SCSI status 1 ro */ +#define SSTAT1_ILF 0x80 /* SIDL full */ +#define SSTAT1_ORF 0x40 /* SODR full */ +#define SSTAT1_OLF 0x20 /* SODL full */ +#define SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define SSTAT1_LOA 0x08 /* Lost arbitration */ +#define SSTAT1_WOA 0x04 /* Won arbitration */ +#define SSTAT1_RST 0x02 /* Instant readout of RST/ */ +#define SSTAT1_SDP 0x01 /* Instant readout of SDP/ */ + +#define SSTAT2_REG (0x0f^bE) /* SCSI status 2 ro */ +#define SSTAT2_FF3 0x80 /* number of bytes in synchronous */ +#define SSTAT2_FF2 0x40 /* data FIFO */ +#define SSTAT2_FF1 0x20 +#define SSTAT2_FF0 0x10 +#define SSTAT2_FF_MASK 0xf0 +#define SSTAT2_FF_SHIFT 4 + +/* + * Latched signals, latched on the leading edge of REQ/ for initiators, + * ACK/ for targets. + */ +#define SSTAT2_SDP 0x08 /* SDP */ +#define SSTAT2_MSG 0x04 /* MSG */ +#define SSTAT2_CD 0x02 /* C/D */ +#define SSTAT2_IO 0x01 /* I/O */ +#define SSTAT2_PHASE_CMDOUT SSTAT2_CD +#define SSTAT2_PHASE_DATAIN SSTAT2_IO +#define SSTAT2_PHASE_DATAOUT 0 +#define SSTAT2_PHASE_MSGIN (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG) +#define SSTAT2_PHASE_MSGOUT (SSTAT2_CD|SSTAT2_MSG) +#define SSTAT2_PHASE_STATIN (SSTAT2_CD|SSTAT2_IO) +#define SSTAT2_PHASE_MASK (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG) + + +#define DSA_REG 0x10 /* DATA structure address */ + +#define CTEST0_REG_700 (0x14^bE) /* Chip test 0 ro */ +#define CTEST0_REG_800 (0x18^bE) /* Chip test 0 ro */ +/* 0x80 - 0x04 are reserved */ +#define CTEST0_700_RTRG 0x02 /* Real target mode */ +#define CTEST0_700_DDIR 0x01 /* Data direction, 1 = + * SCSI bus to host, 0 = + * host to SCSI. + */ + +#define CTEST1_REG_700 (0x15^bE) /* Chip test 1 ro */ +#define CTEST1_REG_800 (0x19^bE) /* Chip test 1 ro */ +#define CTEST1_FMT3 0x80 /* Identify which byte lanes are empty */ +#define CTEST1_FMT2 0x40 /* in the DMA FIFO */ +#define CTEST1_FMT1 0x20 +#define CTEST1_FMT0 0x10 + +#define CTEST1_FFL3 0x08 /* Identify which bytes lanes are full */ +#define CTEST1_FFL2 0x04 /* in the DMA FIFO */ +#define CTEST1_FFL1 0x02 +#define CTEST1_FFL0 0x01 + +#define CTEST2_REG_700 (0x16^bE) /* Chip test 2 ro */ +#define CTEST2_REG_800 (0x1a^bE) /* Chip test 2 ro */ + +#define CTEST2_800_DDIR 0x80 /* 1 = SCSI->host */ +#define CTEST2_800_SIGP 0x40 /* A copy of SIGP in ISTAT. + Reading this register clears */ +#define CTEST2_800_CIO 0x20 /* Configured as IO */. +#define CTEST2_800_CM 0x10 /* Configured as memory */ + +/* 0x80 - 0x40 are reserved on 700 series chips */ +#define CTEST2_700_SOFF 0x20 /* SCSI Offset Compare, + * As an initiator, this bit is + * one when the synchronous offset + * is zero, as a target this bit + * is one when the synchronous + * offset is at the maximum + * defined in SXFER + */ +#define CTEST2_700_SFP 0x10 /* SCSI FIFO parity bit, + * reading CTEST3 unloads a byte + * from the FIFO and sets this + */ +#define CTEST2_700_DFP 0x08 /* DMA FIFO parity bit, + * reading CTEST6 unloads a byte + * from the FIFO and sets this + */ +#define CTEST2_TEOP 0x04 /* SCSI true end of process, + * indicates a totally finished + * transfer + */ +#define CTEST2_DREQ 0x02 /* Data request signal */ +/* 0x01 is reserved on 700 series chips */ +#define CTEST2_800_DACK 0x01 + +/* + * Chip test 3 ro + * Unloads the bottom byte of the eight deep SCSI synchronous FIFO, + * check SSTAT2 FIFO full bits to determine size. Note that a GROSS + * error results if a read is attempted on this register. Also note + * that 16 and 32 bit reads of this register will cause corruption. + */ +#define CTEST3_REG_700 (0x17^bE) +/* Chip test 3 rw */ +#define CTEST3_REG_800 (0x1b^bE) +#define CTEST3_800_V3 0x80 /* Chip revision */ +#define CTEST3_800_V2 0x40 +#define CTEST3_800_V1 0x20 +#define CTEST3_800_V0 0x10 +#define CTEST3_800_FLF 0x08 /* Flush DMA FIFO */ +#define CTEST3_800_CLF 0x04 /* Clear DMA FIFO */ +#define CTEST3_800_FM 0x02 /* Fetch mode pin */ +/* bit 0 is reserved on 800 series chips */ + +#define CTEST4_REG_400 (0x18^bE) /* Chip test 4 rw */ +#define CTEST4_REG_800 (0x21^bE) /* Chip test 4 rw */ +/* 0x80 is reserved on 700 series chips */ +#define CTEST4_800_BDIS 0x80 /* Burst mode disable */ +#define CTEST4_ZMOD 0x40 /* High impedance mode */ +#define CTEST4_SZM 0x20 /* SCSI bus high impedance */ +#define CTEST4_700_SLBE 0x10 /* SCSI loopback enabled */ +#define CTEST4_800_SRTM 0x10 /* Shadow Register Test Mode */ +#define CTEST4_700_SFWR 0x08 /* SCSI FIFO write enable, + * redirects writes from SODL + * to the SCSI FIFO. + */ +#define CTEST4_800_MPEE 0x08 /* Enable parity checking + during master cycles on PCI + bus */ + +/* + * These bits send the contents of the CTEST6 register to the appropriate + * byte lane of the 32 bit DMA FIFO. Normal operation is zero, otherwise + * the high bit means the low two bits select the byte lane. + */ +#define CTEST4_FBL2 0x04 +#define CTEST4_FBL1 0x02 +#define CTEST4_FBL0 0x01 +#define CTEST4_FBL_MASK 0x07 +#define CTEST4_FBL_0 0x04 /* Select DMA FIFO byte lane 0 */ +#define CTEST4_FBL_1 0x05 /* Select DMA FIFO byte lane 1 */ +#define CTEST4_FBL_2 0x06 /* Select DMA FIFO byte lane 2 */ +#define CTEST4_FBL_3 0x07 /* Select DMA FIFO byte lane 3 */ +#define CTEST4_800_SAVE (CTEST4_800_BDIS) + + +#define CTEST5_REG_700 (0x19^bE) /* Chip test 5 rw */ +#define CTEST5_REG_800 (0x22^bE) /* Chip test 5 rw */ +/* + * Clock Address Incrementor. When set, it increments the + * DNAD register to the next bus size boundary. It automatically + * resets itself when the operation is complete. + */ +#define CTEST5_ADCK 0x80 +/* + * Clock Byte Counter. When set, it decrements the DBC register to + * the next bus size boundary. + */ +#define CTEST5_BBCK 0x40 +/* + * Reset SCSI Offset. Setting this bit to 1 clears the current offset + * pointer in the SCSI synchronous offset counter (SSTAT). This bit + * is set to 1 if a SCSI Gross Error Condition occurs. The offset should + * be cleared when a synchronous transfer fails. When written, it is + * automatically cleared after the SCSI synchronous offset counter is + * reset. + */ +/* Bit 5 is reserved on 800 series chips */ +#define CTEST5_700_ROFF 0x20 +/* + * Master Control for Set or Reset pulses. When 1, causes the low + * four bits of register to set when set, 0 causes the low bits to + * clear when set. + */ +#define CTEST5_MASR 0x10 +#define CTEST5_DDIR 0x08 /* DMA direction */ +/* + * Bits 2-0 are reserved on 800 series chips + */ +#define CTEST5_700_EOP 0x04 /* End of process */ +#define CTEST5_700_DREQ 0x02 /* Data request */ +#define CTEST5_700_DACK 0x01 /* Data acknowledge */ + +/* + * Chip test 6 rw - writing to this register writes to the byte + * lane in the DMA FIFO as determined by the FBL bits in the CTEST4 + * register. + */ +#define CTEST6_REG_700 (0x1a^bE) +#define CTEST6_REG_800 (0x23^bE) + +#define CTEST7_REG (0x1b^bE) /* Chip test 7 rw */ +#define CTEST7_10_CDIS 0x80 /* Cache burst disable */ +#define CTEST7_10_SC1 0x40 /* Snoop control bits */ +#define CTEST7_10_SC0 0x20 +#define CTEST7_10_SC_MASK 0x60 +#define CTEST7_STD 0x10 /* Selection timeout disable */ +#define CTEST7_DFP 0x08 /* DMA FIFO parity bit for CTEST6 */ +#define CTEST7_EVP 0x04 /* 1 = host bus even parity, 0 = odd */ +#define CTEST7_10_TT1 0x02 /* Transfer type */ +#define CTEST7_DIFF 0x01 /* Differential mode */ + +#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF ) + + +#define TEMP_REG 0x1c /* through 0x1f Temporary stack rw */ + +#define DFIFO_REG (0x20^bE) /* DMA FIFO rw */ +/* + * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been + * moved into the CTEST8 register. + */ +#define DFIFO_BO6 0x40 +#define DFIFO_BO5 0x20 +#define DFIFO_BO4 0x10 +#define DFIFO_BO3 0x08 +#define DFIFO_BO2 0x04 +#define DFIFO_BO1 0x02 +#define DFIFO_BO0 0x01 +#define DFIFO_10_BO_MASK 0x7f /* 7 bit counter */ + +/* + * Interrupt status rw + * Note that this is the only register which can be read while SCSI + * SCRIPTS are being executed. + */ +#define ISTAT_REG_700 (0x21^bE) +#define ISTAT_REG_800 (0x14^bE) +#define ISTAT_ABRT 0x80 /* Software abort, write + *1 to abort, wait for interrupt. */ +#define ISTAT_10_SRST 0x40 /* software reset */ +#define ISTAT_10_SIGP 0x20 /* signal script */ +#define ISTAT_CON 0x08 /* 1 when connected */ +#define ISTAT_800_INTF 0x04 /* Interrupt on the fly */ +#define ISTAT_700_PRE 0x04 /* Pointer register empty. + * Set to 1 when DSPS and DSP + * registers are empty in pipeline + * mode, always set otherwise. + */ +#define ISTAT_SIP 0x02 /* SCSI interrupt pending from + * SCSI portion of SIOP see + * SSTAT0 + */ +#define ISTAT_DIP 0x01 /* DMA interrupt pending + * see DSTAT + */ + +#define CTEST8_REG (0x22^bE) /* Chip test 8 rw */ +#define CTEST8_10_V3 0x80 /* Chip revision */ +#define CTEST8_10_V2 0x40 +#define CTEST8_10_V1 0x20 +#define CTEST8_10_V0 0x10 +#define CTEST8_10_V_MASK 0xf0 +#define CTEST8_10_FLF 0x08 /* Flush FIFOs */ +#define CTEST8_10_CLF 0x04 /* Clear FIFOs */ +#define CTEST8_10_FM 0x02 /* Fetch pin mode */ +#define CTEST8_10_SM 0x01 /* Snoop pin mode */ + + +#define LCRC_REG_10 (0x23^bE) + +/* + * 0x24 through 0x27 are the DMA byte counter register. Instructions + * write their high 8 bits into the DCMD register, the low 24 bits into + * the DBC register. + * + * Function is dependent on the command type being executed. + */ + + +#define DBC_REG 0x24 +/* + * For Block Move Instructions, DBC is a 24 bit quantity representing + * the number of bytes to transfer. + * For Transfer Control Instructions, DBC is bit fielded as follows : + */ +/* Bits 20 - 23 should be clear */ +#define DBC_TCI_TRUE (1 << 19) /* Jump when true */ +#define DBC_TCI_COMPARE_DATA (1 << 18) /* Compare data */ +#define DBC_TCI_COMPARE_PHASE (1 << 17) /* Compare phase with DCMD field */ +#define DBC_TCI_WAIT_FOR_VALID (1 << 16) /* Wait for REQ */ +/* Bits 8 - 15 are reserved on some implementations ? */ +#define DBC_TCI_MASK_MASK 0xff00 /* Mask for data compare */ +#define DBC_TCI_MASK_SHIFT 8 +#define DBC_TCI_DATA_MASK 0xff /* Data to be compared */ +#define DBC_TCI_DATA_SHIFT 0 + +#define DBC_RWRI_IMMEDIATE_MASK 0xff00 /* Immediate data */ +#define DBC_RWRI_IMMEDIATE_SHIFT 8 /* Amount to shift */ +#define DBC_RWRI_ADDRESS_MASK 0x3f0000 /* Register address */ +#define DBC_RWRI_ADDRESS_SHIFT 16 + + +/* + * DMA command r/w + */ +#define DCMD_REG (0x27^bE) +#define DCMD_TYPE_MASK 0xc0 /* Masks off type */ +#define DCMD_TYPE_BMI 0x00 /* Indicates a Block Move instruction */ +#define DCMD_BMI_IO 0x01 /* I/O, CD, and MSG bits selecting */ +#define DCMD_BMI_CD 0x02 /* the phase for the block MOVE */ +#define DCMD_BMI_MSG 0x04 /* instruction */ + +#define DCMD_BMI_OP_MASK 0x18 /* mask for opcode */ +#define DCMD_BMI_OP_MOVE_T 0x00 /* MOVE */ +#define DCMD_BMI_OP_MOVE_I 0x08 /* MOVE Initiator */ + +#define DCMD_BMI_INDIRECT 0x20 /* Indirect addressing */ + +#define DCMD_TYPE_TCI 0x80 /* Indicates a Transfer Control + instruction */ +#define DCMD_TCI_IO 0x01 /* I/O, CD, and MSG bits selecting */ +#define DCMD_TCI_CD 0x02 /* the phase for the block MOVE */ +#define DCMD_TCI_MSG 0x04 /* instruction */ +#define DCMD_TCI_OP_MASK 0x38 /* mask for opcode */ +#define DCMD_TCI_OP_JUMP 0x00 /* JUMP */ +#define DCMD_TCI_OP_CALL 0x08 /* CALL */ +#define DCMD_TCI_OP_RETURN 0x10 /* RETURN */ +#define DCMD_TCI_OP_INT 0x18 /* INT */ + +#define DCMD_TYPE_RWRI 0x40 /* Indicates I/O or register Read/Write + instruction */ +#define DCMD_RWRI_OPC_MASK 0x38 /* Opcode mask */ +#define DCMD_RWRI_OPC_WRITE 0x28 /* Write SFBR to register */ +#define DCMD_RWRI_OPC_READ 0x30 /* Read register to SFBR */ +#define DCMD_RWRI_OPC_MODIFY 0x38 /* Modify in place */ + +#define DCMD_RWRI_OP_MASK 0x07 +#define DCMD_RWRI_OP_MOVE 0x00 +#define DCMD_RWRI_OP_SHL 0x01 +#define DCMD_RWRI_OP_OR 0x02 +#define DCMD_RWRI_OP_XOR 0x03 +#define DCMD_RWRI_OP_AND 0x04 +#define DCMD_RWRI_OP_SHR 0x05 +#define DCMD_RWRI_OP_ADD 0x06 +#define DCMD_RWRI_OP_ADDC 0x07 + +#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction + (three words) */ + + +#define DNAD_REG 0x28 /* through 0x2b DMA next address for + data */ +#define DSP_REG 0x2c /* through 0x2f DMA SCRIPTS pointer rw */ +#define DSPS_REG 0x30 /* through 0x33 DMA SCRIPTS pointer + save rw */ +#define DMODE_BL1 0x80 /* Burst length bits */ +#define DMODE_BL0 0x40 +#define DMODE_BL_MASK 0xc0 +/* Burst lengths (800) */ +#define DMODE_BL_2 0x00 /* 2 transfer */ +#define DMODE_BL_4 0x40 /* 4 transfers */ +#define DMODE_BL_8 0x80 /* 8 transfers */ +#define DMODE_BL_16 0xc0 /* 16 transfers */ + +#define DMODE_10_BL_1 0x00 /* 1 transfer */ +#define DMODE_10_BL_2 0x40 /* 2 transfers */ +#define DMODE_10_BL_4 0x80 /* 4 transfers */ +#define DMODE_10_BL_8 0xc0 /* 8 transfers */ +#define DMODE_10_FC2 0x20 /* Driven to FC2 pin */ +#define DMODE_10_FC1 0x10 /* Driven to FC1 pin */ +#define DMODE_710_PD 0x08 /* Program/data on FC0 pin */ +#define DMODE_710_UO 0x02 /* User prog. output */ + +#define DMODE_MAN 0x01 /* Manual start mode, + * requires a 1 to be written + * to the start DMA bit in the DCNTL + * register to run scripts + */ + +/* NCR53c800 series only */ +#define SCRATCHA_REG_800 0x34 /* through 0x37 Scratch A rw */ +/* NCR53c710 only */ +#define SCRATCHB_REG_10 0x34 /* through 0x37 scratch rw */ + +#define DMODE_REG (0x38^bE) /* DMA mode rw, NCR53c710 and newer */ +#define DMODE_800_SIOM 0x20 /* Source IO = 1 */ +#define DMODE_800_DIOM 0x10 /* Destination IO = 1 */ +#define DMODE_800_ERL 0x08 /* Enable Read Line */ + +#define DIEN_REG (0x39^bE) /* DMA interrupt enable rw */ +/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */ +#define DIEN_800_MDPE 0x40 /* Master data parity error */ +#define DIEN_800_BF 0x20 /* BUS fault */ +#define DIEN_700_BF 0x20 /* BUS fault */ +#define DIEN_ABRT 0x10 /* Enable aborted interrupt */ +#define DIEN_SSI 0x08 /* Enable single step interrupt */ +#define DIEN_SIR 0x04 /* Enable SCRIPTS INT command + * interrupt + */ +#define DIEN_700_WTD 0x02 /* Enable watchdog timeout interrupt */ +#define DIEN_700_OPC 0x01 /* Enable illegal instruction + * interrupt + */ +#define DIEN_800_IID 0x01 /* Same meaning, different name */ + +/* + * DMA watchdog timer rw + * set in 16 CLK input periods. + */ +#define DWT_REG (0x3a^bE) + +/* DMA control rw */ +#define DCNTL_REG (0x3b^bE) +#define DCNTL_700_CF1 0x80 /* Clock divisor bits */ +#define DCNTL_700_CF0 0x40 +#define DCNTL_700_CF_MASK 0xc0 +/* Clock divisors Divisor SCLK range (MHZ) */ +#define DCNTL_700_CF_2 0x00 /* 2.0 37.51-50.00 */ +#define DCNTL_700_CF_1_5 0x40 /* 1.5 25.01-37.50 */ +#define DCNTL_700_CF_1 0x80 /* 1.0 16.67-25.00 */ +#define DCNTL_700_CF_3 0xc0 /* 3.0 50.01-66.67 (53c700-66) */ + +#define DCNTL_700_S16 0x20 /* Load scripts 16 bits at a time */ +#define DCNTL_SSM 0x10 /* Single step mode */ +#define DCNTL_700_LLM 0x08 /* Low level mode, can only be set + * after selection */ +#define DCNTL_800_IRQM 0x08 /* Totem pole IRQ pin */ +#define DCNTL_STD 0x04 /* Start DMA / SCRIPTS */ +/* 0x02 is reserved */ +#define DCNTL_10_COM 0x01 /* 700 software compatibility mode */ +#define DCNTL_10_EA 0x20 /* Enable Ack - needed for MVME16x */ + +#define SCRATCHB_REG_800 0x5c /* through 0x5f scratch b rw */ +/* NCR53c710 only */ +#define ADDER_REG_10 0x3c /* Adder, NCR53c710 only */ + +#define SIEN1_REG_800 (0x41^bE) +#define SIEN1_800_STO 0x04 /* selection/reselection timeout */ +#define SIEN1_800_GEN 0x02 /* general purpose timer */ +#define SIEN1_800_HTH 0x01 /* handshake to handshake */ + +#define SIST1_REG_800 (0x43^bE) +#define SIST1_800_STO 0x04 /* selection/reselection timeout */ +#define SIST1_800_GEN 0x02 /* general purpose timer */ +#define SIST1_800_HTH 0x01 /* handshake to handshake */ + +#define SLPAR_REG_800 (0x44^bE) /* Parity */ + +#define MACNTL_REG_800 (0x46^bE) /* Memory access control */ +#define MACNTL_800_TYP3 0x80 +#define MACNTL_800_TYP2 0x40 +#define MACNTL_800_TYP1 0x20 +#define MACNTL_800_TYP0 0x10 +#define MACNTL_800_DWR 0x08 +#define MACNTL_800_DRD 0x04 +#define MACNTL_800_PSCPT 0x02 +#define MACNTL_800_SCPTS 0x01 + +#define GPCNTL_REG_800 (0x47^bE) /* General Purpose Pin Control */ + +/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */ +#define STIME0_REG_800 (0x48^bE) /* SCSI Timer Register 0 */ +#define STIME0_800_HTH_MASK 0xf0 /* Handshake to Handshake timeout */ +#define STIME0_800_HTH_SHIFT 4 +#define STIME0_800_SEL_MASK 0x0f /* Selection timeout */ +#define STIME0_800_SEL_SHIFT 0 + +#define STIME1_REG_800 (0x49^bE) +#define STIME1_800_GEN_MASK 0x0f /* General purpose timer */ + +#define RESPID_REG_800 (0x4a^bE) /* Response ID, bit fielded. 8 + bits on narrow chips, 16 on WIDE */ + +#define STEST0_REG_800 (0x4c^bE) +#define STEST0_800_SLT 0x08 /* Selection response logic test */ +#define STEST0_800_ART 0x04 /* Arbitration priority encoder test */ +#define STEST0_800_SOZ 0x02 /* Synchronous offset zero */ +#define STEST0_800_SOM 0x01 /* Synchronous offset maximum */ + +#define STEST1_REG_800 (0x4d^bE) +#define STEST1_800_SCLK 0x80 /* Disable SCSI clock */ + +#define STEST2_REG_800 (0x4e^bE) +#define STEST2_800_SCE 0x80 /* Enable SOCL/SODL */ +#define STEST2_800_ROF 0x40 /* Reset SCSI sync offset */ +#define STEST2_800_SLB 0x10 /* Enable SCSI loopback mode */ +#define STEST2_800_SZM 0x08 /* SCSI high impedance mode */ +#define STEST2_800_EXT 0x02 /* Extend REQ/ACK filter 30 to 60ns */ +#define STEST2_800_LOW 0x01 /* SCSI low level mode */ + +#define STEST3_REG_800 (0x4f^bE) +#define STEST3_800_TE 0x80 /* Enable active negation */ +#define STEST3_800_STR 0x40 /* SCSI FIFO test read */ +#define STEST3_800_HSC 0x20 /* Halt SCSI clock */ +#define STEST3_800_DSI 0x10 /* Disable single initiator response */ +#define STEST3_800_TTM 0x04 /* Time test mode */ +#define STEST3_800_CSF 0x02 /* Clear SCSI FIFO */ +#define STEST3_800_STW 0x01 /* SCSI FIFO test write */ + +#define ISTAT_REG ISTAT_REG_700 +#define SCRATCH_REG SCRATCHB_REG_10 + +#ifdef MEM_MAPPED +#define NCR_read8(address) \ + (unsigned int)readb((u32)(host->base) + ((u32)(address))) + +#define NCR_read32(address) \ + (unsigned int) readl((u32)(host->base) + (u32)(address)) + +#define NCR_write8(address,value) \ + { DEB(DEB_REGS, printk("NCR: %02x => %08x\n", (u32)(value), ((u32)(host->base) + (u32)(address)))); \ + *(volatile unsigned char *) \ + ((u32)(host->base) + (u32)(address)) = (value); } + +#define NCR_write32(address,value) \ + { DEB(DEB_REGS, printk("NCR: %08x => %08x\n", (u32)(value), ((u32)(host->base) + (u32)(address)))); \ + *(volatile unsigned long *) \ + ((u32)(host->base) + (u32)(address)) = (value); } +#else +#define NCR_read8(address) \ + inb((u32)(host->base) + (address)) + +#define NCR_read32(address) \ + inl((u32)(host->base) + (address)) + +#define NCR_write8(address,value) \ + { DEB(DEB_REGS, printk("NCR: %02x => %08x\n", (u32)(value), ((u32)(host->base) + (u32)(address)))); \ + outb((value), (u32)(host->base) + (u32)(address)); } + +#define NCR_write32(address,value) \ + { DEB(DEB_REGS, printk("NCR: %08x => %08x\n", (u32)(value), ((u32)(host->base) + (u32)(address)))); \ + outl((value), (u32)(host->base) + (u32)(address)); } +#endif + +/* Patch arbitrary 32 bit words in the script */ +#define patch_abs_32(script, offset, symbol, value) \ + for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \ + (u32)); ++i) { \ + (script)[A_##symbol##_used[i] - (offset)] += (value); \ + DEB(DEB_FIXUP, printk("scsi%d: %s reference %d at 0x%x in %s is now 0x%x\n",\ + host->host_no, #symbol, i, A_##symbol##_used[i] - \ + (int)(offset), #script, (script)[A_##symbol##_used[i] - \ + (offset)])); \ + } + +#endif +#endif diff --git a/drivers/scsi/sim710.scr b/drivers/scsi/sim710.scr new file mode 100644 index 000000000..60893a662 --- /dev/null +++ b/drivers/scsi/sim710.scr @@ -0,0 +1,554 @@ +/* + * sim710.scr - Copyright (C) 1999 Richard Hirst + */ + +/* Offsets from DSA, allow 128 elements of scatter/gather */ + +ABSOLUTE dsa_select = 0 +ABSOLUTE dsa_msgout = 8 +ABSOLUTE dsa_cmnd = 16 +ABSOLUTE dsa_status = 24 +ABSOLUTE dsa_msgin = 32 +ABSOLUTE dsa_datain = 40 /* 8 * 128 = 1024 bytes */ +ABSOLUTE dsa_dataout = 1064 /* 8 * 128 = 1024 bytes */ +ABSOLUTE dsa_size = 2088 + +ABSOLUTE reselected_identify = 0 +ABSOLUTE msgin_buf = 0 + +/* Interrupt values passed back to driver */ + +ABSOLUTE int_bad_extmsg1a = 0xab930000 +ABSOLUTE int_bad_extmsg1b = 0xab930001 +ABSOLUTE int_bad_extmsg2a = 0xab930002 +ABSOLUTE int_bad_extmsg2b = 0xab930003 +ABSOLUTE int_bad_extmsg3a = 0xab930004 +ABSOLUTE int_bad_extmsg3b = 0xab930005 +ABSOLUTE int_bad_msg1 = 0xab930006 +ABSOLUTE int_bad_msg2 = 0xab930007 +ABSOLUTE int_bad_msg3 = 0xab930008 +ABSOLUTE int_cmd_bad_phase = 0xab930009 +ABSOLUTE int_cmd_complete = 0xab93000a +ABSOLUTE int_data_bad_phase = 0xab93000b +ABSOLUTE int_msg_sdtr1 = 0xab93000c +ABSOLUTE int_msg_sdtr2 = 0xab93000d +ABSOLUTE int_msg_sdtr3 = 0xab93000e +ABSOLUTE int_no_msgout1 = 0xab93000f +ABSOLUTE int_no_msgout2 = 0xab930010 +ABSOLUTE int_no_msgout3 = 0xab930011 +ABSOLUTE int_not_cmd_complete = 0xab930012 +ABSOLUTE int_sel_no_ident = 0xab930013 +ABSOLUTE int_sel_not_cmd = 0xab930014 +ABSOLUTE int_status_not_msgin = 0xab930015 +ABSOLUTE int_resel_not_msgin = 0xab930016 +ABSOLUTE int_reselected = 0xab930017 +ABSOLUTE int_selected = 0xab930018 +ABSOLUTE int_disc1 = 0xab930019 +ABSOLUTE int_disc2 = 0xab93001a +ABSOLUTE int_disc3 = 0xab93001b +ABSOLUTE int_not_rej = 0xab93001c + + +/* Bit field settings used to record status in SCRATCH */ + +ABSOLUTE had_select = 0x01 +ABSOLUTE had_msgout = 0x02 +ABSOLUTE had_cmdout = 0x04 +ABSOLUTE had_datain = 0x08 +ABSOLUTE had_dataout = 0x10 +ABSOLUTE had_status = 0x20 +ABSOLUTE had_msgin = 0x40 +ABSOLUTE had_extmsg = 0x80 + + +/* These scripts are heavily based on the examples in the NCR 53C710 + * Programmer's Guide (Preliminary). + */ + +ENTRY do_select +do_select: + CLEAR TARGET + MOVE SCRATCH0 & 0 TO SCRATCH0 + ; Enable selection timer + MOVE CTEST7 & 0xef TO CTEST7 + SELECT ATN FROM dsa_select, reselect + JUMP get_status, WHEN STATUS + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + MOVE SCRATCH0 | had_select TO SCRATCH0 + INT int_sel_no_ident, IF NOT MSG_OUT + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + MOVE FROM dsa_msgout, when MSG_OUT +ENTRY done_ident +done_ident: + JUMP get_status, IF STATUS +redo_msgin1: + JUMP get_msgin1, WHEN MSG_IN + INT int_sel_not_cmd, IF NOT CMD +ENTRY resume_cmd +resume_cmd: + MOVE SCRATCH0 | had_cmdout TO SCRATCH0 + MOVE FROM dsa_cmnd, WHEN CMD +ENTRY resume_pmm +resume_pmm: +redo_msgin2: + JUMP get_msgin2, WHEN MSG_IN + JUMP get_status, IF STATUS + JUMP input_data, IF DATA_IN + JUMP output_data, IF DATA_OUT + INT int_cmd_bad_phase + +get_status: + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + MOVE FROM dsa_status, WHEN STATUS + INT int_status_not_msgin, WHEN NOT MSG_IN + MOVE FROM dsa_msgin, WHEN MSG_IN + INT int_not_cmd_complete, IF NOT 0x00 + CLEAR ACK +ENTRY wait_disc_complete +wait_disc_complete: + WAIT DISCONNECT + INT int_cmd_complete + +input_data: + MOVE SCRATCH0 | had_datain TO SCRATCH0 +ENTRY patch_input_data +patch_input_data: + JUMP 0 + MOVE FROM dsa_datain+0x0000, WHEN DATA_IN + MOVE FROM dsa_datain+0x0008, WHEN DATA_IN + MOVE FROM dsa_datain+0x0010, WHEN DATA_IN + MOVE FROM dsa_datain+0x0018, WHEN DATA_IN + MOVE FROM dsa_datain+0x0020, WHEN DATA_IN + MOVE FROM dsa_datain+0x0028, WHEN DATA_IN + MOVE FROM dsa_datain+0x0030, WHEN DATA_IN + MOVE FROM dsa_datain+0x0038, WHEN DATA_IN + MOVE FROM dsa_datain+0x0040, WHEN DATA_IN + MOVE FROM dsa_datain+0x0048, WHEN DATA_IN + MOVE FROM dsa_datain+0x0050, WHEN DATA_IN + MOVE FROM dsa_datain+0x0058, WHEN DATA_IN + MOVE FROM dsa_datain+0x0060, WHEN DATA_IN + MOVE FROM dsa_datain+0x0068, WHEN DATA_IN + MOVE FROM dsa_datain+0x0070, WHEN DATA_IN + MOVE FROM dsa_datain+0x0078, WHEN DATA_IN + MOVE FROM dsa_datain+0x0080, WHEN DATA_IN + MOVE FROM dsa_datain+0x0088, WHEN DATA_IN + MOVE FROM dsa_datain+0x0090, WHEN DATA_IN + MOVE FROM dsa_datain+0x0098, WHEN DATA_IN + MOVE FROM dsa_datain+0x00a0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00a8, WHEN DATA_IN + MOVE FROM dsa_datain+0x00b0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00b8, WHEN DATA_IN + MOVE FROM dsa_datain+0x00c0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00c8, WHEN DATA_IN + MOVE FROM dsa_datain+0x00d0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00d8, WHEN DATA_IN + MOVE FROM dsa_datain+0x00e0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00e8, WHEN DATA_IN + MOVE FROM dsa_datain+0x00f0, WHEN DATA_IN + MOVE FROM dsa_datain+0x00f8, WHEN DATA_IN + MOVE FROM dsa_datain+0x0100, WHEN DATA_IN + MOVE FROM dsa_datain+0x0108, WHEN DATA_IN + MOVE FROM dsa_datain+0x0110, WHEN DATA_IN + MOVE FROM dsa_datain+0x0118, WHEN DATA_IN + MOVE FROM dsa_datain+0x0120, WHEN DATA_IN + MOVE FROM dsa_datain+0x0128, WHEN DATA_IN + MOVE FROM dsa_datain+0x0130, WHEN DATA_IN + MOVE FROM dsa_datain+0x0138, WHEN DATA_IN + MOVE FROM dsa_datain+0x0140, WHEN DATA_IN + MOVE FROM dsa_datain+0x0148, WHEN DATA_IN + MOVE FROM dsa_datain+0x0150, WHEN DATA_IN + MOVE FROM dsa_datain+0x0158, WHEN DATA_IN + MOVE FROM dsa_datain+0x0160, WHEN DATA_IN + MOVE FROM dsa_datain+0x0168, WHEN DATA_IN + MOVE FROM dsa_datain+0x0170, WHEN DATA_IN + MOVE FROM dsa_datain+0x0178, WHEN DATA_IN + MOVE FROM dsa_datain+0x0180, WHEN DATA_IN + MOVE FROM dsa_datain+0x0188, WHEN DATA_IN + MOVE FROM dsa_datain+0x0190, WHEN DATA_IN + MOVE FROM dsa_datain+0x0198, WHEN DATA_IN + MOVE FROM dsa_datain+0x01a0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01a8, WHEN DATA_IN + MOVE FROM dsa_datain+0x01b0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01b8, WHEN DATA_IN + MOVE FROM dsa_datain+0x01c0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01c8, WHEN DATA_IN + MOVE FROM dsa_datain+0x01d0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01d8, WHEN DATA_IN + MOVE FROM dsa_datain+0x01e0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01e8, WHEN DATA_IN + MOVE FROM dsa_datain+0x01f0, WHEN DATA_IN + MOVE FROM dsa_datain+0x01f8, WHEN DATA_IN + MOVE FROM dsa_datain+0x0200, WHEN DATA_IN + MOVE FROM dsa_datain+0x0208, WHEN DATA_IN + MOVE FROM dsa_datain+0x0210, WHEN DATA_IN + MOVE FROM dsa_datain+0x0218, WHEN DATA_IN + MOVE FROM dsa_datain+0x0220, WHEN DATA_IN + MOVE FROM dsa_datain+0x0228, WHEN DATA_IN + MOVE FROM dsa_datain+0x0230, WHEN DATA_IN + MOVE FROM dsa_datain+0x0238, WHEN DATA_IN + MOVE FROM dsa_datain+0x0240, WHEN DATA_IN + MOVE FROM dsa_datain+0x0248, WHEN DATA_IN + MOVE FROM dsa_datain+0x0250, WHEN DATA_IN + MOVE FROM dsa_datain+0x0258, WHEN DATA_IN + MOVE FROM dsa_datain+0x0260, WHEN DATA_IN + MOVE FROM dsa_datain+0x0268, WHEN DATA_IN + MOVE FROM dsa_datain+0x0270, WHEN DATA_IN + MOVE FROM dsa_datain+0x0278, WHEN DATA_IN + MOVE FROM dsa_datain+0x0280, WHEN DATA_IN + MOVE FROM dsa_datain+0x0288, WHEN DATA_IN + MOVE FROM dsa_datain+0x0290, WHEN DATA_IN + MOVE FROM dsa_datain+0x0298, WHEN DATA_IN + MOVE FROM dsa_datain+0x02a0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02a8, WHEN DATA_IN + MOVE FROM dsa_datain+0x02b0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02b8, WHEN DATA_IN + MOVE FROM dsa_datain+0x02c0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02c8, WHEN DATA_IN + MOVE FROM dsa_datain+0x02d0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02d8, WHEN DATA_IN + MOVE FROM dsa_datain+0x02e0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02e8, WHEN DATA_IN + MOVE FROM dsa_datain+0x02f0, WHEN DATA_IN + MOVE FROM dsa_datain+0x02f8, WHEN DATA_IN + MOVE FROM dsa_datain+0x0300, WHEN DATA_IN + MOVE FROM dsa_datain+0x0308, WHEN DATA_IN + MOVE FROM dsa_datain+0x0310, WHEN DATA_IN + MOVE FROM dsa_datain+0x0318, WHEN DATA_IN + MOVE FROM dsa_datain+0x0320, WHEN DATA_IN + MOVE FROM dsa_datain+0x0328, WHEN DATA_IN + MOVE FROM dsa_datain+0x0330, WHEN DATA_IN + MOVE FROM dsa_datain+0x0338, WHEN DATA_IN + MOVE FROM dsa_datain+0x0340, WHEN DATA_IN + MOVE FROM dsa_datain+0x0348, WHEN DATA_IN + MOVE FROM dsa_datain+0x0350, WHEN DATA_IN + MOVE FROM dsa_datain+0x0358, WHEN DATA_IN + MOVE FROM dsa_datain+0x0360, WHEN DATA_IN + MOVE FROM dsa_datain+0x0368, WHEN DATA_IN + MOVE FROM dsa_datain+0x0370, WHEN DATA_IN + MOVE FROM dsa_datain+0x0378, WHEN DATA_IN + MOVE FROM dsa_datain+0x0380, WHEN DATA_IN + MOVE FROM dsa_datain+0x0388, WHEN DATA_IN + MOVE FROM dsa_datain+0x0390, WHEN DATA_IN + MOVE FROM dsa_datain+0x0398, WHEN DATA_IN + MOVE FROM dsa_datain+0x03a0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03a8, WHEN DATA_IN + MOVE FROM dsa_datain+0x03b0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03b8, WHEN DATA_IN + MOVE FROM dsa_datain+0x03c0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03c8, WHEN DATA_IN + MOVE FROM dsa_datain+0x03d0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03d8, WHEN DATA_IN + MOVE FROM dsa_datain+0x03e0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03e8, WHEN DATA_IN + MOVE FROM dsa_datain+0x03f0, WHEN DATA_IN + MOVE FROM dsa_datain+0x03f8, WHEN DATA_IN + JUMP end_data_trans + +output_data: + MOVE SCRATCH0 | had_dataout TO SCRATCH0 +ENTRY patch_output_data +patch_output_data: + JUMP 0 + MOVE FROM dsa_dataout+0x0000, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0008, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0010, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0018, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0020, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0028, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0030, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0038, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0040, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0048, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0050, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0058, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0060, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0068, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0070, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0078, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0080, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0088, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0090, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0098, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00a0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00a8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00b0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00b8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00c0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00c8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00d0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00d8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00e0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00e8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00f0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x00f8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0100, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0108, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0110, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0118, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0120, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0128, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0130, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0138, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0140, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0148, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0150, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0158, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0160, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0168, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0170, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0178, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0180, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0188, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0190, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0198, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01a0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01a8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01b0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01b8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01c0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01c8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01d0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01d8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01e0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01e8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01f0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x01f8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0200, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0208, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0210, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0218, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0220, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0228, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0230, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0238, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0240, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0248, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0250, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0258, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0260, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0268, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0270, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0278, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0280, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0288, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0290, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0298, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02a0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02a8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02b0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02b8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02c0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02c8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02d0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02d8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02e0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02e8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02f0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x02f8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0300, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0308, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0310, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0318, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0320, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0328, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0330, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0338, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0340, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0348, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0350, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0358, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0360, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0368, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0370, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0378, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0380, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0388, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0390, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x0398, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03a0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03a8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03b0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03b8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03c0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03c8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03d0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03d8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03e0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03e8, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03f0, WHEN DATA_OUT + MOVE FROM dsa_dataout+0x03f8, WHEN DATA_OUT +ENTRY end_data_trans +end_data_trans: +redo_msgin3: + JUMP get_status, WHEN STATUS + JUMP get_msgin3, WHEN MSG_IN + INT int_data_bad_phase + +get_msgin1: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + MOVE 1, msgin_buf, WHEN MSG_IN + JUMP ext_msg1, IF 0x01 ; Extended Message + JUMP ignore_msg1, IF 0x02 ; Save Data Pointers + JUMP ignore_msg1, IF 0x03 ; Save Restore Pointers + JUMP disc1, IF 0x04 ; Disconnect + INT int_bad_msg1 +ignore_msg1: + CLEAR ACK + JUMP redo_msgin1 +ext_msg1: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + CLEAR ACK + MOVE 1, msgin_buf + 1, WHEN MSG_IN + JUMP ext_msg1a, IF 0x03 + INT int_bad_extmsg1a +ext_msg1a: + CLEAR ACK + MOVE 1, msgin_buf + 2, WHEN MSG_IN + JUMP ext_msg1b, IF 0x01 ; Must be SDTR + INT int_bad_extmsg1b +ext_msg1b: + CLEAR ACK + MOVE 2, msgin_buf + 3, WHEN MSG_IN + INT int_msg_sdtr1 +disc1: + CLEAR ACK +ENTRY wait_disc1 +wait_disc1: + WAIT DISCONNECT + INT int_disc1 +ENTRY resume_msgin1a +resume_msgin1a: + CLEAR ACK + JUMP redo_msgin1 +ENTRY resume_msgin1b +resume_msgin1b: + SET ATN + CLEAR ACK + INT int_no_msgout1, WHEN NOT MSG_OUT + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + MOVE FROM dsa_msgout, when MSG_OUT + JUMP redo_msgin1 + +get_msgin2: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + MOVE 1, msgin_buf, WHEN MSG_IN + JUMP ext_msg2, IF 0x01 ; Extended Message + JUMP ignore_msg2, IF 0x02 ; Save Data Pointers + JUMP ignore_msg2, IF 0x03 ; Save Restore Pointers + JUMP disc2, IF 0x04 ; Disconnect + INT int_bad_msg2 +ignore_msg2: + CLEAR ACK + JUMP redo_msgin2 +ext_msg2: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + CLEAR ACK + MOVE 1, msgin_buf + 1, WHEN MSG_IN + JUMP ext_msg2a, IF 0x03 + INT int_bad_extmsg2a +ext_msg2a: + CLEAR ACK + MOVE 1, msgin_buf + 2, WHEN MSG_IN + JUMP ext_msg2b, IF 0x01 ; Must be SDTR + INT int_bad_extmsg2b +ext_msg2b: + CLEAR ACK + MOVE 2, msgin_buf + 3, WHEN MSG_IN + INT int_msg_sdtr2 +disc2: + CLEAR ACK +ENTRY wait_disc2 +wait_disc2: + WAIT DISCONNECT + INT int_disc2 +ENTRY resume_msgin2a +resume_msgin2a: + CLEAR ACK + JUMP redo_msgin2 +ENTRY resume_msgin2b +resume_msgin2b: + SET ATN + CLEAR ACK + INT int_no_msgout2, WHEN NOT MSG_OUT + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + MOVE FROM dsa_msgout, when MSG_OUT + JUMP redo_msgin2 + +get_msgin3: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + MOVE 1, msgin_buf, WHEN MSG_IN + JUMP ext_msg3, IF 0x01 ; Extended Message + JUMP ignore_msg3, IF 0x02 ; Save Data Pointers + JUMP ignore_msg3, IF 0x03 ; Save Restore Pointers + JUMP disc3, IF 0x04 ; Disconnect + INT int_bad_msg3 +ignore_msg3: + CLEAR ACK + JUMP redo_msgin3 +ext_msg3: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + CLEAR ACK + MOVE 1, msgin_buf + 1, WHEN MSG_IN + JUMP ext_msg3a, IF 0x03 + INT int_bad_extmsg3a +ext_msg3a: + CLEAR ACK + MOVE 1, msgin_buf + 2, WHEN MSG_IN + JUMP ext_msg3b, IF 0x01 ; Must be SDTR + INT int_bad_extmsg3b +ext_msg3b: + CLEAR ACK + MOVE 2, msgin_buf + 3, WHEN MSG_IN + INT int_msg_sdtr3 +disc3: + CLEAR ACK +ENTRY wait_disc3 +wait_disc3: + WAIT DISCONNECT + INT int_disc3 +ENTRY resume_msgin3a +resume_msgin3a: + CLEAR ACK + JUMP redo_msgin3 +ENTRY resume_msgin3b +resume_msgin3b: + SET ATN + CLEAR ACK + INT int_no_msgout3, WHEN NOT MSG_OUT + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + MOVE FROM dsa_msgout, when MSG_OUT + JUMP redo_msgin3 + +ENTRY resume_rej_ident +resume_rej_ident: + CLEAR ATN + MOVE 1, msgin_buf, WHEN MSG_IN + INT int_not_rej, IF NOT 0x07 ; Reject + CLEAR ACK + JUMP done_ident + +ENTRY reselect +reselect: + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + WAIT RESELECT resel_err + INT int_resel_not_msgin, WHEN NOT MSG_IN + MOVE 1, reselected_identify, WHEN MSG_IN + INT int_reselected +resel_err: + MOVE CTEST2 & 0x40 TO SFBR + JUMP selected, IF 0x00 + MOVE SFBR & 0 TO SFBR +ENTRY patch_new_dsa +patch_new_dsa: + MOVE SFBR | 0x11 TO DSA0 + MOVE SFBR | 0x22 TO DSA1 + MOVE SFBR | 0x33 TO DSA2 + MOVE SFBR | 0x44 TO DSA3 + JUMP do_select + +selected: + INT int_selected + diff --git a/drivers/scsi/sim710_d.h b/drivers/scsi/sim710_d.h new file mode 100644 index 000000000..22e046478 --- /dev/null +++ b/drivers/scsi/sim710_d.h @@ -0,0 +1,2360 @@ +u32 SCRIPT[] = { +/* + + + + + + +ABSOLUTE dsa_select = 0 +ABSOLUTE dsa_msgout = 8 +ABSOLUTE dsa_cmnd = 16 +ABSOLUTE dsa_status = 24 +ABSOLUTE dsa_msgin = 32 +ABSOLUTE dsa_datain = 40 +ABSOLUTE dsa_dataout = 1064 +ABSOLUTE dsa_size = 2088 + +ABSOLUTE reselected_identify = 0 +ABSOLUTE msgin_buf = 0 + + + +ABSOLUTE int_bad_extmsg1a = 0xab930000 +ABSOLUTE int_bad_extmsg1b = 0xab930001 +ABSOLUTE int_bad_extmsg2a = 0xab930002 +ABSOLUTE int_bad_extmsg2b = 0xab930003 +ABSOLUTE int_bad_extmsg3a = 0xab930004 +ABSOLUTE int_bad_extmsg3b = 0xab930005 +ABSOLUTE int_bad_msg1 = 0xab930006 +ABSOLUTE int_bad_msg2 = 0xab930007 +ABSOLUTE int_bad_msg3 = 0xab930008 +ABSOLUTE int_cmd_bad_phase = 0xab930009 +ABSOLUTE int_cmd_complete = 0xab93000a +ABSOLUTE int_data_bad_phase = 0xab93000b +ABSOLUTE int_msg_sdtr1 = 0xab93000c +ABSOLUTE int_msg_sdtr2 = 0xab93000d +ABSOLUTE int_msg_sdtr3 = 0xab93000e +ABSOLUTE int_no_msgout1 = 0xab93000f +ABSOLUTE int_no_msgout2 = 0xab930010 +ABSOLUTE int_no_msgout3 = 0xab930011 +ABSOLUTE int_not_cmd_complete = 0xab930012 +ABSOLUTE int_sel_no_ident = 0xab930013 +ABSOLUTE int_sel_not_cmd = 0xab930014 +ABSOLUTE int_status_not_msgin = 0xab930015 +ABSOLUTE int_resel_not_msgin = 0xab930016 +ABSOLUTE int_reselected = 0xab930017 +ABSOLUTE int_selected = 0xab930018 +ABSOLUTE int_disc1 = 0xab930019 +ABSOLUTE int_disc2 = 0xab93001a +ABSOLUTE int_disc3 = 0xab93001b +ABSOLUTE int_not_rej = 0xab93001c + + + + +ABSOLUTE had_select = 0x01 +ABSOLUTE had_msgout = 0x02 +ABSOLUTE had_cmdout = 0x04 +ABSOLUTE had_datain = 0x08 +ABSOLUTE had_dataout = 0x10 +ABSOLUTE had_status = 0x20 +ABSOLUTE had_msgin = 0x40 +ABSOLUTE had_extmsg = 0x80 + + + + + + +ENTRY do_select +do_select: + CLEAR TARGET + +at 0x00000000 : */ 0x60000200,0x00000000, +/* + MOVE SCRATCH0 & 0 TO SCRATCH0 + +at 0x00000002 : */ 0x7c340000,0x00000000, +/* + ; Enable selection timer + MOVE CTEST7 & 0xef TO CTEST7 + +at 0x00000004 : */ 0x7c1bef00,0x00000000, +/* + SELECT ATN FROM dsa_select, reselect + +at 0x00000006 : */ 0x43000000,0x00000c48, +/* + JUMP get_status, WHEN STATUS + +at 0x00000008 : */ 0x830b0000,0x000000a0, +/* + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + +at 0x0000000a : */ 0x7a1b1000,0x00000000, +/* + MOVE SCRATCH0 | had_select TO SCRATCH0 + +at 0x0000000c : */ 0x7a340100,0x00000000, +/* + INT int_sel_no_ident, IF NOT MSG_OUT + +at 0x0000000e : */ 0x9e020000,0xab930013, +/* + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + +at 0x00000010 : */ 0x7a340200,0x00000000, +/* + MOVE FROM dsa_msgout, when MSG_OUT + +at 0x00000012 : */ 0x1e000000,0x00000008, +/* +ENTRY done_ident +done_ident: + JUMP get_status, IF STATUS + +at 0x00000014 : */ 0x830a0000,0x000000a0, +/* +redo_msgin1: + JUMP get_msgin1, WHEN MSG_IN + +at 0x00000016 : */ 0x870b0000,0x00000920, +/* + INT int_sel_not_cmd, IF NOT CMD + +at 0x00000018 : */ 0x9a020000,0xab930014, +/* +ENTRY resume_cmd +resume_cmd: + MOVE SCRATCH0 | had_cmdout TO SCRATCH0 + +at 0x0000001a : */ 0x7a340400,0x00000000, +/* + MOVE FROM dsa_cmnd, WHEN CMD + +at 0x0000001c : */ 0x1a000000,0x00000010, +/* +ENTRY resume_pmm +resume_pmm: +redo_msgin2: + JUMP get_msgin2, WHEN MSG_IN + +at 0x0000001e : */ 0x870b0000,0x00000a20, +/* + JUMP get_status, IF STATUS + +at 0x00000020 : */ 0x830a0000,0x000000a0, +/* + JUMP input_data, IF DATA_IN + +at 0x00000022 : */ 0x810a0000,0x000000e0, +/* + JUMP output_data, IF DATA_OUT + +at 0x00000024 : */ 0x800a0000,0x000004f8, +/* + INT int_cmd_bad_phase + +at 0x00000026 : */ 0x98080000,0xab930009, +/* + +get_status: + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + +at 0x00000028 : */ 0x7a1b1000,0x00000000, +/* + MOVE FROM dsa_status, WHEN STATUS + +at 0x0000002a : */ 0x1b000000,0x00000018, +/* + INT int_status_not_msgin, WHEN NOT MSG_IN + +at 0x0000002c : */ 0x9f030000,0xab930015, +/* + MOVE FROM dsa_msgin, WHEN MSG_IN + +at 0x0000002e : */ 0x1f000000,0x00000020, +/* + INT int_not_cmd_complete, IF NOT 0x00 + +at 0x00000030 : */ 0x98040000,0xab930012, +/* + CLEAR ACK + +at 0x00000032 : */ 0x60000040,0x00000000, +/* +ENTRY wait_disc_complete +wait_disc_complete: + WAIT DISCONNECT + +at 0x00000034 : */ 0x48000000,0x00000000, +/* + INT int_cmd_complete + +at 0x00000036 : */ 0x98080000,0xab93000a, +/* + +input_data: + MOVE SCRATCH0 | had_datain TO SCRATCH0 + +at 0x00000038 : */ 0x7a340800,0x00000000, +/* +ENTRY patch_input_data +patch_input_data: + JUMP 0 + +at 0x0000003a : */ 0x80080000,0x00000000, +/* + MOVE FROM dsa_datain+0x0000, WHEN DATA_IN + +at 0x0000003c : */ 0x19000000,0x00000028, +/* + MOVE FROM dsa_datain+0x0008, WHEN DATA_IN + +at 0x0000003e : */ 0x19000000,0x00000030, +/* + MOVE FROM dsa_datain+0x0010, WHEN DATA_IN + +at 0x00000040 : */ 0x19000000,0x00000038, +/* + MOVE FROM dsa_datain+0x0018, WHEN DATA_IN + +at 0x00000042 : */ 0x19000000,0x00000040, +/* + MOVE FROM dsa_datain+0x0020, WHEN DATA_IN + +at 0x00000044 : */ 0x19000000,0x00000048, +/* + MOVE FROM dsa_datain+0x0028, WHEN DATA_IN + +at 0x00000046 : */ 0x19000000,0x00000050, +/* + MOVE FROM dsa_datain+0x0030, WHEN DATA_IN + +at 0x00000048 : */ 0x19000000,0x00000058, +/* + MOVE FROM dsa_datain+0x0038, WHEN DATA_IN + +at 0x0000004a : */ 0x19000000,0x00000060, +/* + MOVE FROM dsa_datain+0x0040, WHEN DATA_IN + +at 0x0000004c : */ 0x19000000,0x00000068, +/* + MOVE FROM dsa_datain+0x0048, WHEN DATA_IN + +at 0x0000004e : */ 0x19000000,0x00000070, +/* + MOVE FROM dsa_datain+0x0050, WHEN DATA_IN + +at 0x00000050 : */ 0x19000000,0x00000078, +/* + MOVE FROM dsa_datain+0x0058, WHEN DATA_IN + +at 0x00000052 : */ 0x19000000,0x00000080, +/* + MOVE FROM dsa_datain+0x0060, WHEN DATA_IN + +at 0x00000054 : */ 0x19000000,0x00000088, +/* + MOVE FROM dsa_datain+0x0068, WHEN DATA_IN + +at 0x00000056 : */ 0x19000000,0x00000090, +/* + MOVE FROM dsa_datain+0x0070, WHEN DATA_IN + +at 0x00000058 : */ 0x19000000,0x00000098, +/* + MOVE FROM dsa_datain+0x0078, WHEN DATA_IN + +at 0x0000005a : */ 0x19000000,0x000000a0, +/* + MOVE FROM dsa_datain+0x0080, WHEN DATA_IN + +at 0x0000005c : */ 0x19000000,0x000000a8, +/* + MOVE FROM dsa_datain+0x0088, WHEN DATA_IN + +at 0x0000005e : */ 0x19000000,0x000000b0, +/* + MOVE FROM dsa_datain+0x0090, WHEN DATA_IN + +at 0x00000060 : */ 0x19000000,0x000000b8, +/* + MOVE FROM dsa_datain+0x0098, WHEN DATA_IN + +at 0x00000062 : */ 0x19000000,0x000000c0, +/* + MOVE FROM dsa_datain+0x00a0, WHEN DATA_IN + +at 0x00000064 : */ 0x19000000,0x000000c8, +/* + MOVE FROM dsa_datain+0x00a8, WHEN DATA_IN + +at 0x00000066 : */ 0x19000000,0x000000d0, +/* + MOVE FROM dsa_datain+0x00b0, WHEN DATA_IN + +at 0x00000068 : */ 0x19000000,0x000000d8, +/* + MOVE FROM dsa_datain+0x00b8, WHEN DATA_IN + +at 0x0000006a : */ 0x19000000,0x000000e0, +/* + MOVE FROM dsa_datain+0x00c0, WHEN DATA_IN + +at 0x0000006c : */ 0x19000000,0x000000e8, +/* + MOVE FROM dsa_datain+0x00c8, WHEN DATA_IN + +at 0x0000006e : */ 0x19000000,0x000000f0, +/* + MOVE FROM dsa_datain+0x00d0, WHEN DATA_IN + +at 0x00000070 : */ 0x19000000,0x000000f8, +/* + MOVE FROM dsa_datain+0x00d8, WHEN DATA_IN + +at 0x00000072 : */ 0x19000000,0x00000100, +/* + MOVE FROM dsa_datain+0x00e0, WHEN DATA_IN + +at 0x00000074 : */ 0x19000000,0x00000108, +/* + MOVE FROM dsa_datain+0x00e8, WHEN DATA_IN + +at 0x00000076 : */ 0x19000000,0x00000110, +/* + MOVE FROM dsa_datain+0x00f0, WHEN DATA_IN + +at 0x00000078 : */ 0x19000000,0x00000118, +/* + MOVE FROM dsa_datain+0x00f8, WHEN DATA_IN + +at 0x0000007a : */ 0x19000000,0x00000120, +/* + MOVE FROM dsa_datain+0x0100, WHEN DATA_IN + +at 0x0000007c : */ 0x19000000,0x00000128, +/* + MOVE FROM dsa_datain+0x0108, WHEN DATA_IN + +at 0x0000007e : */ 0x19000000,0x00000130, +/* + MOVE FROM dsa_datain+0x0110, WHEN DATA_IN + +at 0x00000080 : */ 0x19000000,0x00000138, +/* + MOVE FROM dsa_datain+0x0118, WHEN DATA_IN + +at 0x00000082 : */ 0x19000000,0x00000140, +/* + MOVE FROM dsa_datain+0x0120, WHEN DATA_IN + +at 0x00000084 : */ 0x19000000,0x00000148, +/* + MOVE FROM dsa_datain+0x0128, WHEN DATA_IN + +at 0x00000086 : */ 0x19000000,0x00000150, +/* + MOVE FROM dsa_datain+0x0130, WHEN DATA_IN + +at 0x00000088 : */ 0x19000000,0x00000158, +/* + MOVE FROM dsa_datain+0x0138, WHEN DATA_IN + +at 0x0000008a : */ 0x19000000,0x00000160, +/* + MOVE FROM dsa_datain+0x0140, WHEN DATA_IN + +at 0x0000008c : */ 0x19000000,0x00000168, +/* + MOVE FROM dsa_datain+0x0148, WHEN DATA_IN + +at 0x0000008e : */ 0x19000000,0x00000170, +/* + MOVE FROM dsa_datain+0x0150, WHEN DATA_IN + +at 0x00000090 : */ 0x19000000,0x00000178, +/* + MOVE FROM dsa_datain+0x0158, WHEN DATA_IN + +at 0x00000092 : */ 0x19000000,0x00000180, +/* + MOVE FROM dsa_datain+0x0160, WHEN DATA_IN + +at 0x00000094 : */ 0x19000000,0x00000188, +/* + MOVE FROM dsa_datain+0x0168, WHEN DATA_IN + +at 0x00000096 : */ 0x19000000,0x00000190, +/* + MOVE FROM dsa_datain+0x0170, WHEN DATA_IN + +at 0x00000098 : */ 0x19000000,0x00000198, +/* + MOVE FROM dsa_datain+0x0178, WHEN DATA_IN + +at 0x0000009a : */ 0x19000000,0x000001a0, +/* + MOVE FROM dsa_datain+0x0180, WHEN DATA_IN + +at 0x0000009c : */ 0x19000000,0x000001a8, +/* + MOVE FROM dsa_datain+0x0188, WHEN DATA_IN + +at 0x0000009e : */ 0x19000000,0x000001b0, +/* + MOVE FROM dsa_datain+0x0190, WHEN DATA_IN + +at 0x000000a0 : */ 0x19000000,0x000001b8, +/* + MOVE FROM dsa_datain+0x0198, WHEN DATA_IN + +at 0x000000a2 : */ 0x19000000,0x000001c0, +/* + MOVE FROM dsa_datain+0x01a0, WHEN DATA_IN + +at 0x000000a4 : */ 0x19000000,0x000001c8, +/* + MOVE FROM dsa_datain+0x01a8, WHEN DATA_IN + +at 0x000000a6 : */ 0x19000000,0x000001d0, +/* + MOVE FROM dsa_datain+0x01b0, WHEN DATA_IN + +at 0x000000a8 : */ 0x19000000,0x000001d8, +/* + MOVE FROM dsa_datain+0x01b8, WHEN DATA_IN + +at 0x000000aa : */ 0x19000000,0x000001e0, +/* + MOVE FROM dsa_datain+0x01c0, WHEN DATA_IN + +at 0x000000ac : */ 0x19000000,0x000001e8, +/* + MOVE FROM dsa_datain+0x01c8, WHEN DATA_IN + +at 0x000000ae : */ 0x19000000,0x000001f0, +/* + MOVE FROM dsa_datain+0x01d0, WHEN DATA_IN + +at 0x000000b0 : */ 0x19000000,0x000001f8, +/* + MOVE FROM dsa_datain+0x01d8, WHEN DATA_IN + +at 0x000000b2 : */ 0x19000000,0x00000200, +/* + MOVE FROM dsa_datain+0x01e0, WHEN DATA_IN + +at 0x000000b4 : */ 0x19000000,0x00000208, +/* + MOVE FROM dsa_datain+0x01e8, WHEN DATA_IN + +at 0x000000b6 : */ 0x19000000,0x00000210, +/* + MOVE FROM dsa_datain+0x01f0, WHEN DATA_IN + +at 0x000000b8 : */ 0x19000000,0x00000218, +/* + MOVE FROM dsa_datain+0x01f8, WHEN DATA_IN + +at 0x000000ba : */ 0x19000000,0x00000220, +/* + MOVE FROM dsa_datain+0x0200, WHEN DATA_IN + +at 0x000000bc : */ 0x19000000,0x00000228, +/* + MOVE FROM dsa_datain+0x0208, WHEN DATA_IN + +at 0x000000be : */ 0x19000000,0x00000230, +/* + MOVE FROM dsa_datain+0x0210, WHEN DATA_IN + +at 0x000000c0 : */ 0x19000000,0x00000238, +/* + MOVE FROM dsa_datain+0x0218, WHEN DATA_IN + +at 0x000000c2 : */ 0x19000000,0x00000240, +/* + MOVE FROM dsa_datain+0x0220, WHEN DATA_IN + +at 0x000000c4 : */ 0x19000000,0x00000248, +/* + MOVE FROM dsa_datain+0x0228, WHEN DATA_IN + +at 0x000000c6 : */ 0x19000000,0x00000250, +/* + MOVE FROM dsa_datain+0x0230, WHEN DATA_IN + +at 0x000000c8 : */ 0x19000000,0x00000258, +/* + MOVE FROM dsa_datain+0x0238, WHEN DATA_IN + +at 0x000000ca : */ 0x19000000,0x00000260, +/* + MOVE FROM dsa_datain+0x0240, WHEN DATA_IN + +at 0x000000cc : */ 0x19000000,0x00000268, +/* + MOVE FROM dsa_datain+0x0248, WHEN DATA_IN + +at 0x000000ce : */ 0x19000000,0x00000270, +/* + MOVE FROM dsa_datain+0x0250, WHEN DATA_IN + +at 0x000000d0 : */ 0x19000000,0x00000278, +/* + MOVE FROM dsa_datain+0x0258, WHEN DATA_IN + +at 0x000000d2 : */ 0x19000000,0x00000280, +/* + MOVE FROM dsa_datain+0x0260, WHEN DATA_IN + +at 0x000000d4 : */ 0x19000000,0x00000288, +/* + MOVE FROM dsa_datain+0x0268, WHEN DATA_IN + +at 0x000000d6 : */ 0x19000000,0x00000290, +/* + MOVE FROM dsa_datain+0x0270, WHEN DATA_IN + +at 0x000000d8 : */ 0x19000000,0x00000298, +/* + MOVE FROM dsa_datain+0x0278, WHEN DATA_IN + +at 0x000000da : */ 0x19000000,0x000002a0, +/* + MOVE FROM dsa_datain+0x0280, WHEN DATA_IN + +at 0x000000dc : */ 0x19000000,0x000002a8, +/* + MOVE FROM dsa_datain+0x0288, WHEN DATA_IN + +at 0x000000de : */ 0x19000000,0x000002b0, +/* + MOVE FROM dsa_datain+0x0290, WHEN DATA_IN + +at 0x000000e0 : */ 0x19000000,0x000002b8, +/* + MOVE FROM dsa_datain+0x0298, WHEN DATA_IN + +at 0x000000e2 : */ 0x19000000,0x000002c0, +/* + MOVE FROM dsa_datain+0x02a0, WHEN DATA_IN + +at 0x000000e4 : */ 0x19000000,0x000002c8, +/* + MOVE FROM dsa_datain+0x02a8, WHEN DATA_IN + +at 0x000000e6 : */ 0x19000000,0x000002d0, +/* + MOVE FROM dsa_datain+0x02b0, WHEN DATA_IN + +at 0x000000e8 : */ 0x19000000,0x000002d8, +/* + MOVE FROM dsa_datain+0x02b8, WHEN DATA_IN + +at 0x000000ea : */ 0x19000000,0x000002e0, +/* + MOVE FROM dsa_datain+0x02c0, WHEN DATA_IN + +at 0x000000ec : */ 0x19000000,0x000002e8, +/* + MOVE FROM dsa_datain+0x02c8, WHEN DATA_IN + +at 0x000000ee : */ 0x19000000,0x000002f0, +/* + MOVE FROM dsa_datain+0x02d0, WHEN DATA_IN + +at 0x000000f0 : */ 0x19000000,0x000002f8, +/* + MOVE FROM dsa_datain+0x02d8, WHEN DATA_IN + +at 0x000000f2 : */ 0x19000000,0x00000300, +/* + MOVE FROM dsa_datain+0x02e0, WHEN DATA_IN + +at 0x000000f4 : */ 0x19000000,0x00000308, +/* + MOVE FROM dsa_datain+0x02e8, WHEN DATA_IN + +at 0x000000f6 : */ 0x19000000,0x00000310, +/* + MOVE FROM dsa_datain+0x02f0, WHEN DATA_IN + +at 0x000000f8 : */ 0x19000000,0x00000318, +/* + MOVE FROM dsa_datain+0x02f8, WHEN DATA_IN + +at 0x000000fa : */ 0x19000000,0x00000320, +/* + MOVE FROM dsa_datain+0x0300, WHEN DATA_IN + +at 0x000000fc : */ 0x19000000,0x00000328, +/* + MOVE FROM dsa_datain+0x0308, WHEN DATA_IN + +at 0x000000fe : */ 0x19000000,0x00000330, +/* + MOVE FROM dsa_datain+0x0310, WHEN DATA_IN + +at 0x00000100 : */ 0x19000000,0x00000338, +/* + MOVE FROM dsa_datain+0x0318, WHEN DATA_IN + +at 0x00000102 : */ 0x19000000,0x00000340, +/* + MOVE FROM dsa_datain+0x0320, WHEN DATA_IN + +at 0x00000104 : */ 0x19000000,0x00000348, +/* + MOVE FROM dsa_datain+0x0328, WHEN DATA_IN + +at 0x00000106 : */ 0x19000000,0x00000350, +/* + MOVE FROM dsa_datain+0x0330, WHEN DATA_IN + +at 0x00000108 : */ 0x19000000,0x00000358, +/* + MOVE FROM dsa_datain+0x0338, WHEN DATA_IN + +at 0x0000010a : */ 0x19000000,0x00000360, +/* + MOVE FROM dsa_datain+0x0340, WHEN DATA_IN + +at 0x0000010c : */ 0x19000000,0x00000368, +/* + MOVE FROM dsa_datain+0x0348, WHEN DATA_IN + +at 0x0000010e : */ 0x19000000,0x00000370, +/* + MOVE FROM dsa_datain+0x0350, WHEN DATA_IN + +at 0x00000110 : */ 0x19000000,0x00000378, +/* + MOVE FROM dsa_datain+0x0358, WHEN DATA_IN + +at 0x00000112 : */ 0x19000000,0x00000380, +/* + MOVE FROM dsa_datain+0x0360, WHEN DATA_IN + +at 0x00000114 : */ 0x19000000,0x00000388, +/* + MOVE FROM dsa_datain+0x0368, WHEN DATA_IN + +at 0x00000116 : */ 0x19000000,0x00000390, +/* + MOVE FROM dsa_datain+0x0370, WHEN DATA_IN + +at 0x00000118 : */ 0x19000000,0x00000398, +/* + MOVE FROM dsa_datain+0x0378, WHEN DATA_IN + +at 0x0000011a : */ 0x19000000,0x000003a0, +/* + MOVE FROM dsa_datain+0x0380, WHEN DATA_IN + +at 0x0000011c : */ 0x19000000,0x000003a8, +/* + MOVE FROM dsa_datain+0x0388, WHEN DATA_IN + +at 0x0000011e : */ 0x19000000,0x000003b0, +/* + MOVE FROM dsa_datain+0x0390, WHEN DATA_IN + +at 0x00000120 : */ 0x19000000,0x000003b8, +/* + MOVE FROM dsa_datain+0x0398, WHEN DATA_IN + +at 0x00000122 : */ 0x19000000,0x000003c0, +/* + MOVE FROM dsa_datain+0x03a0, WHEN DATA_IN + +at 0x00000124 : */ 0x19000000,0x000003c8, +/* + MOVE FROM dsa_datain+0x03a8, WHEN DATA_IN + +at 0x00000126 : */ 0x19000000,0x000003d0, +/* + MOVE FROM dsa_datain+0x03b0, WHEN DATA_IN + +at 0x00000128 : */ 0x19000000,0x000003d8, +/* + MOVE FROM dsa_datain+0x03b8, WHEN DATA_IN + +at 0x0000012a : */ 0x19000000,0x000003e0, +/* + MOVE FROM dsa_datain+0x03c0, WHEN DATA_IN + +at 0x0000012c : */ 0x19000000,0x000003e8, +/* + MOVE FROM dsa_datain+0x03c8, WHEN DATA_IN + +at 0x0000012e : */ 0x19000000,0x000003f0, +/* + MOVE FROM dsa_datain+0x03d0, WHEN DATA_IN + +at 0x00000130 : */ 0x19000000,0x000003f8, +/* + MOVE FROM dsa_datain+0x03d8, WHEN DATA_IN + +at 0x00000132 : */ 0x19000000,0x00000400, +/* + MOVE FROM dsa_datain+0x03e0, WHEN DATA_IN + +at 0x00000134 : */ 0x19000000,0x00000408, +/* + MOVE FROM dsa_datain+0x03e8, WHEN DATA_IN + +at 0x00000136 : */ 0x19000000,0x00000410, +/* + MOVE FROM dsa_datain+0x03f0, WHEN DATA_IN + +at 0x00000138 : */ 0x19000000,0x00000418, +/* + MOVE FROM dsa_datain+0x03f8, WHEN DATA_IN + +at 0x0000013a : */ 0x19000000,0x00000420, +/* + JUMP end_data_trans + +at 0x0000013c : */ 0x80080000,0x00000908, +/* + +output_data: + MOVE SCRATCH0 | had_dataout TO SCRATCH0 + +at 0x0000013e : */ 0x7a341000,0x00000000, +/* +ENTRY patch_output_data +patch_output_data: + JUMP 0 + +at 0x00000140 : */ 0x80080000,0x00000000, +/* + MOVE FROM dsa_dataout+0x0000, WHEN DATA_OUT + +at 0x00000142 : */ 0x18000000,0x00000428, +/* + MOVE FROM dsa_dataout+0x0008, WHEN DATA_OUT + +at 0x00000144 : */ 0x18000000,0x00000430, +/* + MOVE FROM dsa_dataout+0x0010, WHEN DATA_OUT + +at 0x00000146 : */ 0x18000000,0x00000438, +/* + MOVE FROM dsa_dataout+0x0018, WHEN DATA_OUT + +at 0x00000148 : */ 0x18000000,0x00000440, +/* + MOVE FROM dsa_dataout+0x0020, WHEN DATA_OUT + +at 0x0000014a : */ 0x18000000,0x00000448, +/* + MOVE FROM dsa_dataout+0x0028, WHEN DATA_OUT + +at 0x0000014c : */ 0x18000000,0x00000450, +/* + MOVE FROM dsa_dataout+0x0030, WHEN DATA_OUT + +at 0x0000014e : */ 0x18000000,0x00000458, +/* + MOVE FROM dsa_dataout+0x0038, WHEN DATA_OUT + +at 0x00000150 : */ 0x18000000,0x00000460, +/* + MOVE FROM dsa_dataout+0x0040, WHEN DATA_OUT + +at 0x00000152 : */ 0x18000000,0x00000468, +/* + MOVE FROM dsa_dataout+0x0048, WHEN DATA_OUT + +at 0x00000154 : */ 0x18000000,0x00000470, +/* + MOVE FROM dsa_dataout+0x0050, WHEN DATA_OUT + +at 0x00000156 : */ 0x18000000,0x00000478, +/* + MOVE FROM dsa_dataout+0x0058, WHEN DATA_OUT + +at 0x00000158 : */ 0x18000000,0x00000480, +/* + MOVE FROM dsa_dataout+0x0060, WHEN DATA_OUT + +at 0x0000015a : */ 0x18000000,0x00000488, +/* + MOVE FROM dsa_dataout+0x0068, WHEN DATA_OUT + +at 0x0000015c : */ 0x18000000,0x00000490, +/* + MOVE FROM dsa_dataout+0x0070, WHEN DATA_OUT + +at 0x0000015e : */ 0x18000000,0x00000498, +/* + MOVE FROM dsa_dataout+0x0078, WHEN DATA_OUT + +at 0x00000160 : */ 0x18000000,0x000004a0, +/* + MOVE FROM dsa_dataout+0x0080, WHEN DATA_OUT + +at 0x00000162 : */ 0x18000000,0x000004a8, +/* + MOVE FROM dsa_dataout+0x0088, WHEN DATA_OUT + +at 0x00000164 : */ 0x18000000,0x000004b0, +/* + MOVE FROM dsa_dataout+0x0090, WHEN DATA_OUT + +at 0x00000166 : */ 0x18000000,0x000004b8, +/* + MOVE FROM dsa_dataout+0x0098, WHEN DATA_OUT + +at 0x00000168 : */ 0x18000000,0x000004c0, +/* + MOVE FROM dsa_dataout+0x00a0, WHEN DATA_OUT + +at 0x0000016a : */ 0x18000000,0x000004c8, +/* + MOVE FROM dsa_dataout+0x00a8, WHEN DATA_OUT + +at 0x0000016c : */ 0x18000000,0x000004d0, +/* + MOVE FROM dsa_dataout+0x00b0, WHEN DATA_OUT + +at 0x0000016e : */ 0x18000000,0x000004d8, +/* + MOVE FROM dsa_dataout+0x00b8, WHEN DATA_OUT + +at 0x00000170 : */ 0x18000000,0x000004e0, +/* + MOVE FROM dsa_dataout+0x00c0, WHEN DATA_OUT + +at 0x00000172 : */ 0x18000000,0x000004e8, +/* + MOVE FROM dsa_dataout+0x00c8, WHEN DATA_OUT + +at 0x00000174 : */ 0x18000000,0x000004f0, +/* + MOVE FROM dsa_dataout+0x00d0, WHEN DATA_OUT + +at 0x00000176 : */ 0x18000000,0x000004f8, +/* + MOVE FROM dsa_dataout+0x00d8, WHEN DATA_OUT + +at 0x00000178 : */ 0x18000000,0x00000500, +/* + MOVE FROM dsa_dataout+0x00e0, WHEN DATA_OUT + +at 0x0000017a : */ 0x18000000,0x00000508, +/* + MOVE FROM dsa_dataout+0x00e8, WHEN DATA_OUT + +at 0x0000017c : */ 0x18000000,0x00000510, +/* + MOVE FROM dsa_dataout+0x00f0, WHEN DATA_OUT + +at 0x0000017e : */ 0x18000000,0x00000518, +/* + MOVE FROM dsa_dataout+0x00f8, WHEN DATA_OUT + +at 0x00000180 : */ 0x18000000,0x00000520, +/* + MOVE FROM dsa_dataout+0x0100, WHEN DATA_OUT + +at 0x00000182 : */ 0x18000000,0x00000528, +/* + MOVE FROM dsa_dataout+0x0108, WHEN DATA_OUT + +at 0x00000184 : */ 0x18000000,0x00000530, +/* + MOVE FROM dsa_dataout+0x0110, WHEN DATA_OUT + +at 0x00000186 : */ 0x18000000,0x00000538, +/* + MOVE FROM dsa_dataout+0x0118, WHEN DATA_OUT + +at 0x00000188 : */ 0x18000000,0x00000540, +/* + MOVE FROM dsa_dataout+0x0120, WHEN DATA_OUT + +at 0x0000018a : */ 0x18000000,0x00000548, +/* + MOVE FROM dsa_dataout+0x0128, WHEN DATA_OUT + +at 0x0000018c : */ 0x18000000,0x00000550, +/* + MOVE FROM dsa_dataout+0x0130, WHEN DATA_OUT + +at 0x0000018e : */ 0x18000000,0x00000558, +/* + MOVE FROM dsa_dataout+0x0138, WHEN DATA_OUT + +at 0x00000190 : */ 0x18000000,0x00000560, +/* + MOVE FROM dsa_dataout+0x0140, WHEN DATA_OUT + +at 0x00000192 : */ 0x18000000,0x00000568, +/* + MOVE FROM dsa_dataout+0x0148, WHEN DATA_OUT + +at 0x00000194 : */ 0x18000000,0x00000570, +/* + MOVE FROM dsa_dataout+0x0150, WHEN DATA_OUT + +at 0x00000196 : */ 0x18000000,0x00000578, +/* + MOVE FROM dsa_dataout+0x0158, WHEN DATA_OUT + +at 0x00000198 : */ 0x18000000,0x00000580, +/* + MOVE FROM dsa_dataout+0x0160, WHEN DATA_OUT + +at 0x0000019a : */ 0x18000000,0x00000588, +/* + MOVE FROM dsa_dataout+0x0168, WHEN DATA_OUT + +at 0x0000019c : */ 0x18000000,0x00000590, +/* + MOVE FROM dsa_dataout+0x0170, WHEN DATA_OUT + +at 0x0000019e : */ 0x18000000,0x00000598, +/* + MOVE FROM dsa_dataout+0x0178, WHEN DATA_OUT + +at 0x000001a0 : */ 0x18000000,0x000005a0, +/* + MOVE FROM dsa_dataout+0x0180, WHEN DATA_OUT + +at 0x000001a2 : */ 0x18000000,0x000005a8, +/* + MOVE FROM dsa_dataout+0x0188, WHEN DATA_OUT + +at 0x000001a4 : */ 0x18000000,0x000005b0, +/* + MOVE FROM dsa_dataout+0x0190, WHEN DATA_OUT + +at 0x000001a6 : */ 0x18000000,0x000005b8, +/* + MOVE FROM dsa_dataout+0x0198, WHEN DATA_OUT + +at 0x000001a8 : */ 0x18000000,0x000005c0, +/* + MOVE FROM dsa_dataout+0x01a0, WHEN DATA_OUT + +at 0x000001aa : */ 0x18000000,0x000005c8, +/* + MOVE FROM dsa_dataout+0x01a8, WHEN DATA_OUT + +at 0x000001ac : */ 0x18000000,0x000005d0, +/* + MOVE FROM dsa_dataout+0x01b0, WHEN DATA_OUT + +at 0x000001ae : */ 0x18000000,0x000005d8, +/* + MOVE FROM dsa_dataout+0x01b8, WHEN DATA_OUT + +at 0x000001b0 : */ 0x18000000,0x000005e0, +/* + MOVE FROM dsa_dataout+0x01c0, WHEN DATA_OUT + +at 0x000001b2 : */ 0x18000000,0x000005e8, +/* + MOVE FROM dsa_dataout+0x01c8, WHEN DATA_OUT + +at 0x000001b4 : */ 0x18000000,0x000005f0, +/* + MOVE FROM dsa_dataout+0x01d0, WHEN DATA_OUT + +at 0x000001b6 : */ 0x18000000,0x000005f8, +/* + MOVE FROM dsa_dataout+0x01d8, WHEN DATA_OUT + +at 0x000001b8 : */ 0x18000000,0x00000600, +/* + MOVE FROM dsa_dataout+0x01e0, WHEN DATA_OUT + +at 0x000001ba : */ 0x18000000,0x00000608, +/* + MOVE FROM dsa_dataout+0x01e8, WHEN DATA_OUT + +at 0x000001bc : */ 0x18000000,0x00000610, +/* + MOVE FROM dsa_dataout+0x01f0, WHEN DATA_OUT + +at 0x000001be : */ 0x18000000,0x00000618, +/* + MOVE FROM dsa_dataout+0x01f8, WHEN DATA_OUT + +at 0x000001c0 : */ 0x18000000,0x00000620, +/* + MOVE FROM dsa_dataout+0x0200, WHEN DATA_OUT + +at 0x000001c2 : */ 0x18000000,0x00000628, +/* + MOVE FROM dsa_dataout+0x0208, WHEN DATA_OUT + +at 0x000001c4 : */ 0x18000000,0x00000630, +/* + MOVE FROM dsa_dataout+0x0210, WHEN DATA_OUT + +at 0x000001c6 : */ 0x18000000,0x00000638, +/* + MOVE FROM dsa_dataout+0x0218, WHEN DATA_OUT + +at 0x000001c8 : */ 0x18000000,0x00000640, +/* + MOVE FROM dsa_dataout+0x0220, WHEN DATA_OUT + +at 0x000001ca : */ 0x18000000,0x00000648, +/* + MOVE FROM dsa_dataout+0x0228, WHEN DATA_OUT + +at 0x000001cc : */ 0x18000000,0x00000650, +/* + MOVE FROM dsa_dataout+0x0230, WHEN DATA_OUT + +at 0x000001ce : */ 0x18000000,0x00000658, +/* + MOVE FROM dsa_dataout+0x0238, WHEN DATA_OUT + +at 0x000001d0 : */ 0x18000000,0x00000660, +/* + MOVE FROM dsa_dataout+0x0240, WHEN DATA_OUT + +at 0x000001d2 : */ 0x18000000,0x00000668, +/* + MOVE FROM dsa_dataout+0x0248, WHEN DATA_OUT + +at 0x000001d4 : */ 0x18000000,0x00000670, +/* + MOVE FROM dsa_dataout+0x0250, WHEN DATA_OUT + +at 0x000001d6 : */ 0x18000000,0x00000678, +/* + MOVE FROM dsa_dataout+0x0258, WHEN DATA_OUT + +at 0x000001d8 : */ 0x18000000,0x00000680, +/* + MOVE FROM dsa_dataout+0x0260, WHEN DATA_OUT + +at 0x000001da : */ 0x18000000,0x00000688, +/* + MOVE FROM dsa_dataout+0x0268, WHEN DATA_OUT + +at 0x000001dc : */ 0x18000000,0x00000690, +/* + MOVE FROM dsa_dataout+0x0270, WHEN DATA_OUT + +at 0x000001de : */ 0x18000000,0x00000698, +/* + MOVE FROM dsa_dataout+0x0278, WHEN DATA_OUT + +at 0x000001e0 : */ 0x18000000,0x000006a0, +/* + MOVE FROM dsa_dataout+0x0280, WHEN DATA_OUT + +at 0x000001e2 : */ 0x18000000,0x000006a8, +/* + MOVE FROM dsa_dataout+0x0288, WHEN DATA_OUT + +at 0x000001e4 : */ 0x18000000,0x000006b0, +/* + MOVE FROM dsa_dataout+0x0290, WHEN DATA_OUT + +at 0x000001e6 : */ 0x18000000,0x000006b8, +/* + MOVE FROM dsa_dataout+0x0298, WHEN DATA_OUT + +at 0x000001e8 : */ 0x18000000,0x000006c0, +/* + MOVE FROM dsa_dataout+0x02a0, WHEN DATA_OUT + +at 0x000001ea : */ 0x18000000,0x000006c8, +/* + MOVE FROM dsa_dataout+0x02a8, WHEN DATA_OUT + +at 0x000001ec : */ 0x18000000,0x000006d0, +/* + MOVE FROM dsa_dataout+0x02b0, WHEN DATA_OUT + +at 0x000001ee : */ 0x18000000,0x000006d8, +/* + MOVE FROM dsa_dataout+0x02b8, WHEN DATA_OUT + +at 0x000001f0 : */ 0x18000000,0x000006e0, +/* + MOVE FROM dsa_dataout+0x02c0, WHEN DATA_OUT + +at 0x000001f2 : */ 0x18000000,0x000006e8, +/* + MOVE FROM dsa_dataout+0x02c8, WHEN DATA_OUT + +at 0x000001f4 : */ 0x18000000,0x000006f0, +/* + MOVE FROM dsa_dataout+0x02d0, WHEN DATA_OUT + +at 0x000001f6 : */ 0x18000000,0x000006f8, +/* + MOVE FROM dsa_dataout+0x02d8, WHEN DATA_OUT + +at 0x000001f8 : */ 0x18000000,0x00000700, +/* + MOVE FROM dsa_dataout+0x02e0, WHEN DATA_OUT + +at 0x000001fa : */ 0x18000000,0x00000708, +/* + MOVE FROM dsa_dataout+0x02e8, WHEN DATA_OUT + +at 0x000001fc : */ 0x18000000,0x00000710, +/* + MOVE FROM dsa_dataout+0x02f0, WHEN DATA_OUT + +at 0x000001fe : */ 0x18000000,0x00000718, +/* + MOVE FROM dsa_dataout+0x02f8, WHEN DATA_OUT + +at 0x00000200 : */ 0x18000000,0x00000720, +/* + MOVE FROM dsa_dataout+0x0300, WHEN DATA_OUT + +at 0x00000202 : */ 0x18000000,0x00000728, +/* + MOVE FROM dsa_dataout+0x0308, WHEN DATA_OUT + +at 0x00000204 : */ 0x18000000,0x00000730, +/* + MOVE FROM dsa_dataout+0x0310, WHEN DATA_OUT + +at 0x00000206 : */ 0x18000000,0x00000738, +/* + MOVE FROM dsa_dataout+0x0318, WHEN DATA_OUT + +at 0x00000208 : */ 0x18000000,0x00000740, +/* + MOVE FROM dsa_dataout+0x0320, WHEN DATA_OUT + +at 0x0000020a : */ 0x18000000,0x00000748, +/* + MOVE FROM dsa_dataout+0x0328, WHEN DATA_OUT + +at 0x0000020c : */ 0x18000000,0x00000750, +/* + MOVE FROM dsa_dataout+0x0330, WHEN DATA_OUT + +at 0x0000020e : */ 0x18000000,0x00000758, +/* + MOVE FROM dsa_dataout+0x0338, WHEN DATA_OUT + +at 0x00000210 : */ 0x18000000,0x00000760, +/* + MOVE FROM dsa_dataout+0x0340, WHEN DATA_OUT + +at 0x00000212 : */ 0x18000000,0x00000768, +/* + MOVE FROM dsa_dataout+0x0348, WHEN DATA_OUT + +at 0x00000214 : */ 0x18000000,0x00000770, +/* + MOVE FROM dsa_dataout+0x0350, WHEN DATA_OUT + +at 0x00000216 : */ 0x18000000,0x00000778, +/* + MOVE FROM dsa_dataout+0x0358, WHEN DATA_OUT + +at 0x00000218 : */ 0x18000000,0x00000780, +/* + MOVE FROM dsa_dataout+0x0360, WHEN DATA_OUT + +at 0x0000021a : */ 0x18000000,0x00000788, +/* + MOVE FROM dsa_dataout+0x0368, WHEN DATA_OUT + +at 0x0000021c : */ 0x18000000,0x00000790, +/* + MOVE FROM dsa_dataout+0x0370, WHEN DATA_OUT + +at 0x0000021e : */ 0x18000000,0x00000798, +/* + MOVE FROM dsa_dataout+0x0378, WHEN DATA_OUT + +at 0x00000220 : */ 0x18000000,0x000007a0, +/* + MOVE FROM dsa_dataout+0x0380, WHEN DATA_OUT + +at 0x00000222 : */ 0x18000000,0x000007a8, +/* + MOVE FROM dsa_dataout+0x0388, WHEN DATA_OUT + +at 0x00000224 : */ 0x18000000,0x000007b0, +/* + MOVE FROM dsa_dataout+0x0390, WHEN DATA_OUT + +at 0x00000226 : */ 0x18000000,0x000007b8, +/* + MOVE FROM dsa_dataout+0x0398, WHEN DATA_OUT + +at 0x00000228 : */ 0x18000000,0x000007c0, +/* + MOVE FROM dsa_dataout+0x03a0, WHEN DATA_OUT + +at 0x0000022a : */ 0x18000000,0x000007c8, +/* + MOVE FROM dsa_dataout+0x03a8, WHEN DATA_OUT + +at 0x0000022c : */ 0x18000000,0x000007d0, +/* + MOVE FROM dsa_dataout+0x03b0, WHEN DATA_OUT + +at 0x0000022e : */ 0x18000000,0x000007d8, +/* + MOVE FROM dsa_dataout+0x03b8, WHEN DATA_OUT + +at 0x00000230 : */ 0x18000000,0x000007e0, +/* + MOVE FROM dsa_dataout+0x03c0, WHEN DATA_OUT + +at 0x00000232 : */ 0x18000000,0x000007e8, +/* + MOVE FROM dsa_dataout+0x03c8, WHEN DATA_OUT + +at 0x00000234 : */ 0x18000000,0x000007f0, +/* + MOVE FROM dsa_dataout+0x03d0, WHEN DATA_OUT + +at 0x00000236 : */ 0x18000000,0x000007f8, +/* + MOVE FROM dsa_dataout+0x03d8, WHEN DATA_OUT + +at 0x00000238 : */ 0x18000000,0x00000800, +/* + MOVE FROM dsa_dataout+0x03e0, WHEN DATA_OUT + +at 0x0000023a : */ 0x18000000,0x00000808, +/* + MOVE FROM dsa_dataout+0x03e8, WHEN DATA_OUT + +at 0x0000023c : */ 0x18000000,0x00000810, +/* + MOVE FROM dsa_dataout+0x03f0, WHEN DATA_OUT + +at 0x0000023e : */ 0x18000000,0x00000818, +/* + MOVE FROM dsa_dataout+0x03f8, WHEN DATA_OUT + +at 0x00000240 : */ 0x18000000,0x00000820, +/* +ENTRY end_data_trans +end_data_trans: +redo_msgin3: + JUMP get_status, WHEN STATUS + +at 0x00000242 : */ 0x830b0000,0x000000a0, +/* + JUMP get_msgin3, WHEN MSG_IN + +at 0x00000244 : */ 0x870b0000,0x00000b20, +/* + INT int_data_bad_phase + +at 0x00000246 : */ 0x98080000,0xab93000b, +/* + +get_msgin1: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + +at 0x00000248 : */ 0x7a344000,0x00000000, +/* + MOVE 1, msgin_buf, WHEN MSG_IN + +at 0x0000024a : */ 0x0f000001,0x00000000, +/* + JUMP ext_msg1, IF 0x01 ; Extended Message + +at 0x0000024c : */ 0x800c0001,0x00000968, +/* + JUMP ignore_msg1, IF 0x02 ; Save Data Pointers + +at 0x0000024e : */ 0x800c0002,0x00000958, +/* + JUMP ignore_msg1, IF 0x03 ; Save Restore Pointers + +at 0x00000250 : */ 0x800c0003,0x00000958, +/* + JUMP disc1, IF 0x04 ; Disconnect + +at 0x00000252 : */ 0x800c0004,0x000009c8, +/* + INT int_bad_msg1 + +at 0x00000254 : */ 0x98080000,0xab930006, +/* +ignore_msg1: + CLEAR ACK + +at 0x00000256 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin1 + +at 0x00000258 : */ 0x80080000,0x00000058, +/* +ext_msg1: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + +at 0x0000025a : */ 0x7a348000,0x00000000, +/* + CLEAR ACK + +at 0x0000025c : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 1, WHEN MSG_IN + +at 0x0000025e : */ 0x0f000001,0x00000001, +/* + JUMP ext_msg1a, IF 0x03 + +at 0x00000260 : */ 0x800c0003,0x00000990, +/* + INT int_bad_extmsg1a + +at 0x00000262 : */ 0x98080000,0xab930000, +/* +ext_msg1a: + CLEAR ACK + +at 0x00000264 : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 2, WHEN MSG_IN + +at 0x00000266 : */ 0x0f000001,0x00000002, +/* + JUMP ext_msg1b, IF 0x01 ; Must be SDTR + +at 0x00000268 : */ 0x800c0001,0x000009b0, +/* + INT int_bad_extmsg1b + +at 0x0000026a : */ 0x98080000,0xab930001, +/* +ext_msg1b: + CLEAR ACK + +at 0x0000026c : */ 0x60000040,0x00000000, +/* + MOVE 2, msgin_buf + 3, WHEN MSG_IN + +at 0x0000026e : */ 0x0f000002,0x00000003, +/* + INT int_msg_sdtr1 + +at 0x00000270 : */ 0x98080000,0xab93000c, +/* +disc1: + CLEAR ACK + +at 0x00000272 : */ 0x60000040,0x00000000, +/* +ENTRY wait_disc1 +wait_disc1: + WAIT DISCONNECT + +at 0x00000274 : */ 0x48000000,0x00000000, +/* + INT int_disc1 + +at 0x00000276 : */ 0x98080000,0xab930019, +/* +ENTRY resume_msgin1a +resume_msgin1a: + CLEAR ACK + +at 0x00000278 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin1 + +at 0x0000027a : */ 0x80080000,0x00000058, +/* +ENTRY resume_msgin1b +resume_msgin1b: + SET ATN + +at 0x0000027c : */ 0x58000008,0x00000000, +/* + CLEAR ACK + +at 0x0000027e : */ 0x60000040,0x00000000, +/* + INT int_no_msgout1, WHEN NOT MSG_OUT + +at 0x00000280 : */ 0x9e030000,0xab93000f, +/* + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + +at 0x00000282 : */ 0x7a340200,0x00000000, +/* + MOVE FROM dsa_msgout, when MSG_OUT + +at 0x00000284 : */ 0x1e000000,0x00000008, +/* + JUMP redo_msgin1 + +at 0x00000286 : */ 0x80080000,0x00000058, +/* + +get_msgin2: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + +at 0x00000288 : */ 0x7a344000,0x00000000, +/* + MOVE 1, msgin_buf, WHEN MSG_IN + +at 0x0000028a : */ 0x0f000001,0x00000000, +/* + JUMP ext_msg2, IF 0x01 ; Extended Message + +at 0x0000028c : */ 0x800c0001,0x00000a68, +/* + JUMP ignore_msg2, IF 0x02 ; Save Data Pointers + +at 0x0000028e : */ 0x800c0002,0x00000a58, +/* + JUMP ignore_msg2, IF 0x03 ; Save Restore Pointers + +at 0x00000290 : */ 0x800c0003,0x00000a58, +/* + JUMP disc2, IF 0x04 ; Disconnect + +at 0x00000292 : */ 0x800c0004,0x00000ac8, +/* + INT int_bad_msg2 + +at 0x00000294 : */ 0x98080000,0xab930007, +/* +ignore_msg2: + CLEAR ACK + +at 0x00000296 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin2 + +at 0x00000298 : */ 0x80080000,0x00000078, +/* +ext_msg2: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + +at 0x0000029a : */ 0x7a348000,0x00000000, +/* + CLEAR ACK + +at 0x0000029c : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 1, WHEN MSG_IN + +at 0x0000029e : */ 0x0f000001,0x00000001, +/* + JUMP ext_msg2a, IF 0x03 + +at 0x000002a0 : */ 0x800c0003,0x00000a90, +/* + INT int_bad_extmsg2a + +at 0x000002a2 : */ 0x98080000,0xab930002, +/* +ext_msg2a: + CLEAR ACK + +at 0x000002a4 : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 2, WHEN MSG_IN + +at 0x000002a6 : */ 0x0f000001,0x00000002, +/* + JUMP ext_msg2b, IF 0x01 ; Must be SDTR + +at 0x000002a8 : */ 0x800c0001,0x00000ab0, +/* + INT int_bad_extmsg2b + +at 0x000002aa : */ 0x98080000,0xab930003, +/* +ext_msg2b: + CLEAR ACK + +at 0x000002ac : */ 0x60000040,0x00000000, +/* + MOVE 2, msgin_buf + 3, WHEN MSG_IN + +at 0x000002ae : */ 0x0f000002,0x00000003, +/* + INT int_msg_sdtr2 + +at 0x000002b0 : */ 0x98080000,0xab93000d, +/* +disc2: + CLEAR ACK + +at 0x000002b2 : */ 0x60000040,0x00000000, +/* +ENTRY wait_disc2 +wait_disc2: + WAIT DISCONNECT + +at 0x000002b4 : */ 0x48000000,0x00000000, +/* + INT int_disc2 + +at 0x000002b6 : */ 0x98080000,0xab93001a, +/* +ENTRY resume_msgin2a +resume_msgin2a: + CLEAR ACK + +at 0x000002b8 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin2 + +at 0x000002ba : */ 0x80080000,0x00000078, +/* +ENTRY resume_msgin2b +resume_msgin2b: + SET ATN + +at 0x000002bc : */ 0x58000008,0x00000000, +/* + CLEAR ACK + +at 0x000002be : */ 0x60000040,0x00000000, +/* + INT int_no_msgout2, WHEN NOT MSG_OUT + +at 0x000002c0 : */ 0x9e030000,0xab930010, +/* + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + +at 0x000002c2 : */ 0x7a340200,0x00000000, +/* + MOVE FROM dsa_msgout, when MSG_OUT + +at 0x000002c4 : */ 0x1e000000,0x00000008, +/* + JUMP redo_msgin2 + +at 0x000002c6 : */ 0x80080000,0x00000078, +/* + +get_msgin3: + MOVE SCRATCH0 | had_msgin TO SCRATCH0 + +at 0x000002c8 : */ 0x7a344000,0x00000000, +/* + MOVE 1, msgin_buf, WHEN MSG_IN + +at 0x000002ca : */ 0x0f000001,0x00000000, +/* + JUMP ext_msg3, IF 0x01 ; Extended Message + +at 0x000002cc : */ 0x800c0001,0x00000b68, +/* + JUMP ignore_msg3, IF 0x02 ; Save Data Pointers + +at 0x000002ce : */ 0x800c0002,0x00000b58, +/* + JUMP ignore_msg3, IF 0x03 ; Save Restore Pointers + +at 0x000002d0 : */ 0x800c0003,0x00000b58, +/* + JUMP disc3, IF 0x04 ; Disconnect + +at 0x000002d2 : */ 0x800c0004,0x00000bc8, +/* + INT int_bad_msg3 + +at 0x000002d4 : */ 0x98080000,0xab930008, +/* +ignore_msg3: + CLEAR ACK + +at 0x000002d6 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin3 + +at 0x000002d8 : */ 0x80080000,0x00000908, +/* +ext_msg3: + MOVE SCRATCH0 | had_extmsg TO SCRATCH0 + +at 0x000002da : */ 0x7a348000,0x00000000, +/* + CLEAR ACK + +at 0x000002dc : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 1, WHEN MSG_IN + +at 0x000002de : */ 0x0f000001,0x00000001, +/* + JUMP ext_msg3a, IF 0x03 + +at 0x000002e0 : */ 0x800c0003,0x00000b90, +/* + INT int_bad_extmsg3a + +at 0x000002e2 : */ 0x98080000,0xab930004, +/* +ext_msg3a: + CLEAR ACK + +at 0x000002e4 : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 2, WHEN MSG_IN + +at 0x000002e6 : */ 0x0f000001,0x00000002, +/* + JUMP ext_msg3b, IF 0x01 ; Must be SDTR + +at 0x000002e8 : */ 0x800c0001,0x00000bb0, +/* + INT int_bad_extmsg3b + +at 0x000002ea : */ 0x98080000,0xab930005, +/* +ext_msg3b: + CLEAR ACK + +at 0x000002ec : */ 0x60000040,0x00000000, +/* + MOVE 2, msgin_buf + 3, WHEN MSG_IN + +at 0x000002ee : */ 0x0f000002,0x00000003, +/* + INT int_msg_sdtr3 + +at 0x000002f0 : */ 0x98080000,0xab93000e, +/* +disc3: + CLEAR ACK + +at 0x000002f2 : */ 0x60000040,0x00000000, +/* +ENTRY wait_disc3 +wait_disc3: + WAIT DISCONNECT + +at 0x000002f4 : */ 0x48000000,0x00000000, +/* + INT int_disc3 + +at 0x000002f6 : */ 0x98080000,0xab93001b, +/* +ENTRY resume_msgin3a +resume_msgin3a: + CLEAR ACK + +at 0x000002f8 : */ 0x60000040,0x00000000, +/* + JUMP redo_msgin3 + +at 0x000002fa : */ 0x80080000,0x00000908, +/* +ENTRY resume_msgin3b +resume_msgin3b: + SET ATN + +at 0x000002fc : */ 0x58000008,0x00000000, +/* + CLEAR ACK + +at 0x000002fe : */ 0x60000040,0x00000000, +/* + INT int_no_msgout3, WHEN NOT MSG_OUT + +at 0x00000300 : */ 0x9e030000,0xab930011, +/* + MOVE SCRATCH0 | had_msgout TO SCRATCH0 + +at 0x00000302 : */ 0x7a340200,0x00000000, +/* + MOVE FROM dsa_msgout, when MSG_OUT + +at 0x00000304 : */ 0x1e000000,0x00000008, +/* + JUMP redo_msgin3 + +at 0x00000306 : */ 0x80080000,0x00000908, +/* + +ENTRY resume_rej_ident +resume_rej_ident: + CLEAR ATN + +at 0x00000308 : */ 0x60000008,0x00000000, +/* + MOVE 1, msgin_buf, WHEN MSG_IN + +at 0x0000030a : */ 0x0f000001,0x00000000, +/* + INT int_not_rej, IF NOT 0x07 ; Reject + +at 0x0000030c : */ 0x98040007,0xab93001c, +/* + CLEAR ACK + +at 0x0000030e : */ 0x60000040,0x00000000, +/* + JUMP done_ident + +at 0x00000310 : */ 0x80080000,0x00000050, +/* + +ENTRY reselect +reselect: + ; Disable selection timer + MOVE CTEST7 | 0x10 TO CTEST7 + +at 0x00000312 : */ 0x7a1b1000,0x00000000, +/* + WAIT RESELECT resel_err + +at 0x00000314 : */ 0x50000000,0x00000c70, +/* + INT int_resel_not_msgin, WHEN NOT MSG_IN + +at 0x00000316 : */ 0x9f030000,0xab930016, +/* + MOVE 1, reselected_identify, WHEN MSG_IN + +at 0x00000318 : */ 0x0f000001,0x00000000, +/* + INT int_reselected + +at 0x0000031a : */ 0x98080000,0xab930017, +/* +resel_err: + MOVE CTEST2 & 0x40 TO SFBR + +at 0x0000031c : */ 0x74164000,0x00000000, +/* + JUMP selected, IF 0x00 + +at 0x0000031e : */ 0x800c0000,0x00000cb0, +/* + MOVE SFBR & 0 TO SFBR + +at 0x00000320 : */ 0x7c080000,0x00000000, +/* +ENTRY patch_new_dsa +patch_new_dsa: + MOVE SFBR | 0x11 TO DSA0 + +at 0x00000322 : */ 0x6a101100,0x00000000, +/* + MOVE SFBR | 0x22 TO DSA1 + +at 0x00000324 : */ 0x6a112200,0x00000000, +/* + MOVE SFBR | 0x33 TO DSA2 + +at 0x00000326 : */ 0x6a123300,0x00000000, +/* + MOVE SFBR | 0x44 TO DSA3 + +at 0x00000328 : */ 0x6a134400,0x00000000, +/* + JUMP do_select + +at 0x0000032a : */ 0x80080000,0x00000000, +/* + +selected: + INT int_selected + +at 0x0000032c : */ 0x98080000,0xab930018, +}; + +#define A_dsa_cmnd 0x00000010 +u32 A_dsa_cmnd_used[] = { + 0x0000001d, +}; + +#define A_dsa_datain 0x00000028 +u32 A_dsa_datain_used[] = { + 0x0000003d, + 0x0000003f, + 0x00000041, + 0x00000043, + 0x00000045, + 0x00000047, + 0x00000049, + 0x0000004b, + 0x0000004d, + 0x0000004f, + 0x00000051, + 0x00000053, + 0x00000055, + 0x00000057, + 0x00000059, + 0x0000005b, + 0x0000005d, + 0x0000005f, + 0x00000061, + 0x00000063, + 0x00000065, + 0x00000067, + 0x00000069, + 0x0000006b, + 0x0000006d, + 0x0000006f, + 0x00000071, + 0x00000073, + 0x00000075, + 0x00000077, + 0x00000079, + 0x0000007b, + 0x0000007d, + 0x0000007f, + 0x00000081, + 0x00000083, + 0x00000085, + 0x00000087, + 0x00000089, + 0x0000008b, + 0x0000008d, + 0x0000008f, + 0x00000091, + 0x00000093, + 0x00000095, + 0x00000097, + 0x00000099, + 0x0000009b, + 0x0000009d, + 0x0000009f, + 0x000000a1, + 0x000000a3, + 0x000000a5, + 0x000000a7, + 0x000000a9, + 0x000000ab, + 0x000000ad, + 0x000000af, + 0x000000b1, + 0x000000b3, + 0x000000b5, + 0x000000b7, + 0x000000b9, + 0x000000bb, + 0x000000bd, + 0x000000bf, + 0x000000c1, + 0x000000c3, + 0x000000c5, + 0x000000c7, + 0x000000c9, + 0x000000cb, + 0x000000cd, + 0x000000cf, + 0x000000d1, + 0x000000d3, + 0x000000d5, + 0x000000d7, + 0x000000d9, + 0x000000db, + 0x000000dd, + 0x000000df, + 0x000000e1, + 0x000000e3, + 0x000000e5, + 0x000000e7, + 0x000000e9, + 0x000000eb, + 0x000000ed, + 0x000000ef, + 0x000000f1, + 0x000000f3, + 0x000000f5, + 0x000000f7, + 0x000000f9, + 0x000000fb, + 0x000000fd, + 0x000000ff, + 0x00000101, + 0x00000103, + 0x00000105, + 0x00000107, + 0x00000109, + 0x0000010b, + 0x0000010d, + 0x0000010f, + 0x00000111, + 0x00000113, + 0x00000115, + 0x00000117, + 0x00000119, + 0x0000011b, + 0x0000011d, + 0x0000011f, + 0x00000121, + 0x00000123, + 0x00000125, + 0x00000127, + 0x00000129, + 0x0000012b, + 0x0000012d, + 0x0000012f, + 0x00000131, + 0x00000133, + 0x00000135, + 0x00000137, + 0x00000139, + 0x0000013b, +}; + +#define A_dsa_dataout 0x00000428 +u32 A_dsa_dataout_used[] = { + 0x00000143, + 0x00000145, + 0x00000147, + 0x00000149, + 0x0000014b, + 0x0000014d, + 0x0000014f, + 0x00000151, + 0x00000153, + 0x00000155, + 0x00000157, + 0x00000159, + 0x0000015b, + 0x0000015d, + 0x0000015f, + 0x00000161, + 0x00000163, + 0x00000165, + 0x00000167, + 0x00000169, + 0x0000016b, + 0x0000016d, + 0x0000016f, + 0x00000171, + 0x00000173, + 0x00000175, + 0x00000177, + 0x00000179, + 0x0000017b, + 0x0000017d, + 0x0000017f, + 0x00000181, + 0x00000183, + 0x00000185, + 0x00000187, + 0x00000189, + 0x0000018b, + 0x0000018d, + 0x0000018f, + 0x00000191, + 0x00000193, + 0x00000195, + 0x00000197, + 0x00000199, + 0x0000019b, + 0x0000019d, + 0x0000019f, + 0x000001a1, + 0x000001a3, + 0x000001a5, + 0x000001a7, + 0x000001a9, + 0x000001ab, + 0x000001ad, + 0x000001af, + 0x000001b1, + 0x000001b3, + 0x000001b5, + 0x000001b7, + 0x000001b9, + 0x000001bb, + 0x000001bd, + 0x000001bf, + 0x000001c1, + 0x000001c3, + 0x000001c5, + 0x000001c7, + 0x000001c9, + 0x000001cb, + 0x000001cd, + 0x000001cf, + 0x000001d1, + 0x000001d3, + 0x000001d5, + 0x000001d7, + 0x000001d9, + 0x000001db, + 0x000001dd, + 0x000001df, + 0x000001e1, + 0x000001e3, + 0x000001e5, + 0x000001e7, + 0x000001e9, + 0x000001eb, + 0x000001ed, + 0x000001ef, + 0x000001f1, + 0x000001f3, + 0x000001f5, + 0x000001f7, + 0x000001f9, + 0x000001fb, + 0x000001fd, + 0x000001ff, + 0x00000201, + 0x00000203, + 0x00000205, + 0x00000207, + 0x00000209, + 0x0000020b, + 0x0000020d, + 0x0000020f, + 0x00000211, + 0x00000213, + 0x00000215, + 0x00000217, + 0x00000219, + 0x0000021b, + 0x0000021d, + 0x0000021f, + 0x00000221, + 0x00000223, + 0x00000225, + 0x00000227, + 0x00000229, + 0x0000022b, + 0x0000022d, + 0x0000022f, + 0x00000231, + 0x00000233, + 0x00000235, + 0x00000237, + 0x00000239, + 0x0000023b, + 0x0000023d, + 0x0000023f, + 0x00000241, +}; + +#define A_dsa_msgin 0x00000020 +u32 A_dsa_msgin_used[] = { + 0x0000002f, +}; + +#define A_dsa_msgout 0x00000008 +u32 A_dsa_msgout_used[] = { + 0x00000013, + 0x00000285, + 0x000002c5, + 0x00000305, +}; + +#define A_dsa_select 0x00000000 +u32 A_dsa_select_used[] = { + 0x00000006, +}; + +#define A_dsa_size 0x00000828 +u32 A_dsa_size_used[] = { +}; + +#define A_dsa_status 0x00000018 +u32 A_dsa_status_used[] = { + 0x0000002b, +}; + +#define A_had_cmdout 0x00000004 +u32 A_had_cmdout_used[] = { + 0x0000001a, +}; + +#define A_had_datain 0x00000008 +u32 A_had_datain_used[] = { + 0x00000038, +}; + +#define A_had_dataout 0x00000010 +u32 A_had_dataout_used[] = { + 0x0000013e, +}; + +#define A_had_extmsg 0x00000080 +u32 A_had_extmsg_used[] = { + 0x0000025a, + 0x0000029a, + 0x000002da, +}; + +#define A_had_msgin 0x00000040 +u32 A_had_msgin_used[] = { + 0x00000248, + 0x00000288, + 0x000002c8, +}; + +#define A_had_msgout 0x00000002 +u32 A_had_msgout_used[] = { + 0x00000010, + 0x00000282, + 0x000002c2, + 0x00000302, +}; + +#define A_had_select 0x00000001 +u32 A_had_select_used[] = { + 0x0000000c, +}; + +#define A_had_status 0x00000020 +u32 A_had_status_used[] = { +}; + +#define A_int_bad_extmsg1a 0xab930000 +u32 A_int_bad_extmsg1a_used[] = { + 0x00000263, +}; + +#define A_int_bad_extmsg1b 0xab930001 +u32 A_int_bad_extmsg1b_used[] = { + 0x0000026b, +}; + +#define A_int_bad_extmsg2a 0xab930002 +u32 A_int_bad_extmsg2a_used[] = { + 0x000002a3, +}; + +#define A_int_bad_extmsg2b 0xab930003 +u32 A_int_bad_extmsg2b_used[] = { + 0x000002ab, +}; + +#define A_int_bad_extmsg3a 0xab930004 +u32 A_int_bad_extmsg3a_used[] = { + 0x000002e3, +}; + +#define A_int_bad_extmsg3b 0xab930005 +u32 A_int_bad_extmsg3b_used[] = { + 0x000002eb, +}; + +#define A_int_bad_msg1 0xab930006 +u32 A_int_bad_msg1_used[] = { + 0x00000255, +}; + +#define A_int_bad_msg2 0xab930007 +u32 A_int_bad_msg2_used[] = { + 0x00000295, +}; + +#define A_int_bad_msg3 0xab930008 +u32 A_int_bad_msg3_used[] = { + 0x000002d5, +}; + +#define A_int_cmd_bad_phase 0xab930009 +u32 A_int_cmd_bad_phase_used[] = { + 0x00000027, +}; + +#define A_int_cmd_complete 0xab93000a +u32 A_int_cmd_complete_used[] = { + 0x00000037, +}; + +#define A_int_data_bad_phase 0xab93000b +u32 A_int_data_bad_phase_used[] = { + 0x00000247, +}; + +#define A_int_disc1 0xab930019 +u32 A_int_disc1_used[] = { + 0x00000277, +}; + +#define A_int_disc2 0xab93001a +u32 A_int_disc2_used[] = { + 0x000002b7, +}; + +#define A_int_disc3 0xab93001b +u32 A_int_disc3_used[] = { + 0x000002f7, +}; + +#define A_int_msg_sdtr1 0xab93000c +u32 A_int_msg_sdtr1_used[] = { + 0x00000271, +}; + +#define A_int_msg_sdtr2 0xab93000d +u32 A_int_msg_sdtr2_used[] = { + 0x000002b1, +}; + +#define A_int_msg_sdtr3 0xab93000e +u32 A_int_msg_sdtr3_used[] = { + 0x000002f1, +}; + +#define A_int_no_msgout1 0xab93000f +u32 A_int_no_msgout1_used[] = { + 0x00000281, +}; + +#define A_int_no_msgout2 0xab930010 +u32 A_int_no_msgout2_used[] = { + 0x000002c1, +}; + +#define A_int_no_msgout3 0xab930011 +u32 A_int_no_msgout3_used[] = { + 0x00000301, +}; + +#define A_int_not_cmd_complete 0xab930012 +u32 A_int_not_cmd_complete_used[] = { + 0x00000031, +}; + +#define A_int_not_rej 0xab93001c +u32 A_int_not_rej_used[] = { + 0x0000030d, +}; + +#define A_int_resel_not_msgin 0xab930016 +u32 A_int_resel_not_msgin_used[] = { + 0x00000317, +}; + +#define A_int_reselected 0xab930017 +u32 A_int_reselected_used[] = { + 0x0000031b, +}; + +#define A_int_sel_no_ident 0xab930013 +u32 A_int_sel_no_ident_used[] = { + 0x0000000f, +}; + +#define A_int_sel_not_cmd 0xab930014 +u32 A_int_sel_not_cmd_used[] = { + 0x00000019, +}; + +#define A_int_selected 0xab930018 +u32 A_int_selected_used[] = { + 0x0000032d, +}; + +#define A_int_status_not_msgin 0xab930015 +u32 A_int_status_not_msgin_used[] = { + 0x0000002d, +}; + +#define A_msgin_buf 0x00000000 +u32 A_msgin_buf_used[] = { + 0x0000024b, + 0x0000025f, + 0x00000267, + 0x0000026f, + 0x0000028b, + 0x0000029f, + 0x000002a7, + 0x000002af, + 0x000002cb, + 0x000002df, + 0x000002e7, + 0x000002ef, + 0x0000030b, +}; + +#define A_reselected_identify 0x00000000 +u32 A_reselected_identify_used[] = { + 0x00000319, +}; + +#define Ent_do_select 0x00000000 +#define Ent_done_ident 0x00000050 +#define Ent_end_data_trans 0x00000908 +#define Ent_patch_input_data 0x000000e8 +#define Ent_patch_new_dsa 0x00000c88 +#define Ent_patch_output_data 0x00000500 +#define Ent_reselect 0x00000c48 +#define Ent_resume_cmd 0x00000068 +#define Ent_resume_msgin1a 0x000009e0 +#define Ent_resume_msgin1b 0x000009f0 +#define Ent_resume_msgin2a 0x00000ae0 +#define Ent_resume_msgin2b 0x00000af0 +#define Ent_resume_msgin3a 0x00000be0 +#define Ent_resume_msgin3b 0x00000bf0 +#define Ent_resume_pmm 0x00000078 +#define Ent_resume_rej_ident 0x00000c20 +#define Ent_wait_disc1 0x000009d0 +#define Ent_wait_disc2 0x00000ad0 +#define Ent_wait_disc3 0x00000bd0 +#define Ent_wait_disc_complete 0x000000d0 +u32 LABELPATCHES[] = { + 0x00000007, + 0x00000009, + 0x00000015, + 0x00000017, + 0x0000001f, + 0x00000021, + 0x00000023, + 0x00000025, + 0x0000013d, + 0x00000243, + 0x00000245, + 0x0000024d, + 0x0000024f, + 0x00000251, + 0x00000253, + 0x00000259, + 0x00000261, + 0x00000269, + 0x0000027b, + 0x00000287, + 0x0000028d, + 0x0000028f, + 0x00000291, + 0x00000293, + 0x00000299, + 0x000002a1, + 0x000002a9, + 0x000002bb, + 0x000002c7, + 0x000002cd, + 0x000002cf, + 0x000002d1, + 0x000002d3, + 0x000002d9, + 0x000002e1, + 0x000002e9, + 0x000002fb, + 0x00000307, + 0x00000311, + 0x00000315, + 0x0000031f, + 0x0000032b, +}; + +struct { + u32 offset; + void *address; +} EXTERNAL_PATCHES[] = { +}; + +u32 INSTRUCTIONS = 407; +u32 PATCHES = 42; +u32 EXTERNAL_PATCHES_LEN = 0; diff --git a/drivers/scsi/sim710_u.h b/drivers/scsi/sim710_u.h new file mode 100644 index 000000000..e660a847d --- /dev/null +++ b/drivers/scsi/sim710_u.h @@ -0,0 +1,67 @@ +#undef A_dsa_cmnd +#undef A_dsa_datain +#undef A_dsa_dataout +#undef A_dsa_msgin +#undef A_dsa_msgout +#undef A_dsa_select +#undef A_dsa_size +#undef A_dsa_status +#undef A_had_cmdout +#undef A_had_datain +#undef A_had_dataout +#undef A_had_extmsg +#undef A_had_msgin +#undef A_had_msgout +#undef A_had_select +#undef A_had_status +#undef A_int_bad_extmsg1a +#undef A_int_bad_extmsg1b +#undef A_int_bad_extmsg2a +#undef A_int_bad_extmsg2b +#undef A_int_bad_extmsg3a +#undef A_int_bad_extmsg3b +#undef A_int_bad_msg1 +#undef A_int_bad_msg2 +#undef A_int_bad_msg3 +#undef A_int_cmd_bad_phase +#undef A_int_cmd_complete +#undef A_int_data_bad_phase +#undef A_int_disc1 +#undef A_int_disc2 +#undef A_int_disc3 +#undef A_int_msg_sdtr1 +#undef A_int_msg_sdtr2 +#undef A_int_msg_sdtr3 +#undef A_int_no_msgout1 +#undef A_int_no_msgout2 +#undef A_int_no_msgout3 +#undef A_int_not_cmd_complete +#undef A_int_not_rej +#undef A_int_resel_not_msgin +#undef A_int_reselected +#undef A_int_sel_no_ident +#undef A_int_sel_not_cmd +#undef A_int_selected +#undef A_int_status_not_msgin +#undef A_msgin_buf +#undef A_reselected_identify +#undef Ent_do_select +#undef Ent_done_ident +#undef Ent_end_data_trans +#undef Ent_patch_input_data +#undef Ent_patch_new_dsa +#undef Ent_patch_output_data +#undef Ent_reselect +#undef Ent_resume_cmd +#undef Ent_resume_msgin1a +#undef Ent_resume_msgin1b +#undef Ent_resume_msgin2a +#undef Ent_resume_msgin2b +#undef Ent_resume_msgin3a +#undef Ent_resume_msgin3b +#undef Ent_resume_pmm +#undef Ent_resume_rej_ident +#undef Ent_wait_disc1 +#undef Ent_wait_disc2 +#undef Ent_wait_disc3 +#undef Ent_wait_disc_complete diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c index 80696413e..4479532b6 100644 --- a/drivers/scsi/sym53c8xx.c +++ b/drivers/scsi/sym53c8xx.c @@ -1,7 +1,7 @@ /****************************************************************************** ** High Performance device driver for the Symbios 53C896 controller. ** -** Copyright (C) 1998 Gerard Roudier <groudier@club-internet.fr> +** Copyright (C) 1998-1999 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 @@ -55,7 +55,7 @@ */ /* -** April 2 1999, sym53c8xx version 1.3c +** Sep 10 1999, sym53c8xx 1.5e ** ** Supported SCSI features: ** Synchronous data transfers @@ -64,13 +64,14 @@ ** Tagged command queuing ** SCSI Parity checking ** -** Supported NCR chips: +** Supported NCR/SYMBIOS chips: ** 53C810A (8 bits, Fast 10, no rom BIOS) ** 53C825A (Wide, Fast 10, on-board rom BIOS) ** 53C860 (8 bits, Fast 20, no rom BIOS) ** 53C875 (Wide, Fast 20, on-board rom BIOS) ** 53C876 (Wide, Fast 20 Dual, on-board rom BIOS) ** 53C895 (Wide, Fast 40, on-board rom BIOS) +** 53C895A (Wide, Fast 40, on-board rom BIOS) ** 53C896 (Wide, Fast 40 Dual, on-board rom BIOS) ** ** Other features: @@ -82,7 +83,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.3c" +#define SCSI_NCR_DRIVER_NAME "sym53c8xx - version 1.5e" /* #define DEBUG_896R1 */ #define SCSI_NCR_OPTIMIZE_896 @@ -109,8 +110,10 @@ #include <asm/dma.h> #include <asm/io.h> #include <asm/system.h> -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) #include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +#include <asm/spinlock.h> #endif #include <linux/delay.h> #include <linux/signal.h> @@ -130,13 +133,13 @@ #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35) #include <linux/init.h> -#else -#ifndef __initdata -#define __initdata #endif + #ifndef __init #define __init #endif +#ifndef __initdata +#define __initdata #endif #if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) @@ -314,26 +317,34 @@ static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head) #endif /* -** TAGS are actually limited to 64 tags/lun. -** We need to deal with power of 2, for alignment constraints. +** TAGS are actually unlimited (256 tags/lun). +** But Linux only supports 255. :) */ -#if SCSI_NCR_MAX_TAGS > 64 -#undef SCSI_NCR_MAX_TAGS -#define SCSI_NCR_MAX_TAGS (64) +#if SCSI_NCR_MAX_TAGS > 255 +#define MAX_TAGS 255 +#else +#define MAX_TAGS SCSI_NCR_MAX_TAGS #endif -#define NO_TAG (255) - /* -** Choose appropriate type for tag bitmap. -*/ -#if SCSI_NCR_MAX_TAGS > 32 -typedef u_int64 tagmap_t; +** Since the ncr chips only have a 8 bit ALU, we try to be clever +** about offset calculation in the TASK TABLE per LUN that is an +** array of DWORDS = 4 bytes. +*/ +#if MAX_TAGS > (512/4) +#define MAX_TASKS (1024/4) +#elif MAX_TAGS > (256/4) +#define MAX_TASKS (512/4) #else -typedef u_int32 tagmap_t; +#define MAX_TASKS (256/4) #endif /* +** This one means 'NO TAG for this job' +*/ +#define NO_TAG (256) + +/* ** Number of targets supported by the driver. ** n permits target numbers 0..n-1. ** Default is 16, meaning targets #0..#15. @@ -354,7 +365,7 @@ typedef u_int32 tagmap_t; */ #ifdef SCSI_NCR_MAX_LUN -#define MAX_LUN SCSI_NCR_MAX_LUN +#define MAX_LUN 64 #else #define MAX_LUN (1) #endif @@ -377,51 +388,34 @@ typedef u_int32 tagmap_t; #ifdef SCSI_NCR_CAN_QUEUE #define MAX_START (SCSI_NCR_CAN_QUEUE + 4) #else -#define MAX_START (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS) +#define MAX_START (MAX_TARGET + 7 * MAX_TAGS) +#endif + +/* +** We donnot want to allocate more than 1 PAGE for the +** the start queue and the done queue. We hard-code entry +** size to 8 in order to let cpp do the checking. +** Allows 512-4=508 pending IOs for i386 but Linux seems for +** now not able to provide the driver with this amount of IOs. +*/ +#if MAX_START > PAGE_SIZE/8 +#undef MAX_START +#define MAX_START (PAGE_SIZE/8) #endif /* ** The maximum number of segments a transfer is split into. ** We support up to 127 segments for both read and write. -** Since we try to avoid phase mismatches by testing the PHASE -** before each MOV, the both DATA_IN and DATA_OUT scripts do -** not fit in the 4K on-chip RAM. For this reason, the data -** scripts are broken into 2 sub-scripts. -** 80 (MAX_SCATTERL) segments are moved from a sub-script -** in on-chip RAM. This makes data transfers shorter than -** 80k (assuming 1k fs) as fast as possible. -** The 896 allows to handle phase mismatches from SCRIPTS. -** So, for this chip, we use a simple array of MOV's. -** Perhaps, using a simple array of MOV's and going with -** the phase mismatch interrupt is also the best solution -** for the 895 in Ultra2-mode, since the PHASE test + MOV -** latency may be enough to fill the SCSI offset for very -** fast disks like the Cheatah Wide LVD and so, may waste -** SCSI BUS bandwitch. */ #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER) - -#ifdef SCSI_NCR_OPTIMIZE_896 #define SCR_SG_SIZE (2) -#define MAX_SCATTERL MAX_SCATTER -#define MAX_SCATTERH 0 -#else -#if (MAX_SCATTER > 80) -#define SCR_SG_SIZE (4) -#define MAX_SCATTERL 80 -#define MAX_SCATTERH (MAX_SCATTER - MAX_SCATTERL) -#else -#define MAX_SCATTERL MAX_SCATTER -#define MAX_SCATTERH 0 -#endif -#endif /* SCSI_NCR_OPTIMIZE_896 */ /* ** Io mapped or memory mapped. */ -#if defined(SCSI_NCR_IOMAPPED) +#if defined(SCSI_NCR_IOMAPPED) || defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED) #define NCR_IOMAPPED #endif @@ -464,17 +458,14 @@ typedef u_int32 tagmap_t; #define DEBUG_ALLOC (0x0001) #define DEBUG_PHASE (0x0002) -#define DEBUG_POLL (0x0004) #define DEBUG_QUEUE (0x0008) #define DEBUG_RESULT (0x0010) -#define DEBUG_SCATTER (0x0020) +#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_FREEZE (0x0800) -#define DEBUG_RESTART (0x1000) /* ** Enable/Disable debug messages. @@ -569,12 +560,19 @@ spinlock_t sym53c8xx_lock; #endif #ifdef __sparc__ -#define pcivtobus(p) ((p) & pci_dvma_mask) -#else /* __sparc__ */ -#define pcivtobus(p) (p) +# define ioremap(base, size) ((u_long) __va(base)) +# define iounmap(vaddr) +# define pcivtobus(p) ((p) & pci_dvma_mask) +# define memcpy_to_pci(a, b, c) memcpy_toio((u_long) (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 -#if !defined(NCR_IOMAPPED) || defined(__i386__) +#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; @@ -589,7 +587,8 @@ static void __init unmap_pci_mem(u_long vaddr, u_long size) if (vaddr) iounmap((void *) (vaddr & PAGE_MASK)); } -#endif /* !NCR_IOMAPPED || __i386__ */ + +#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED */ /* ** Insert a delay in micro-seconds and milli-seconds. @@ -760,7 +759,7 @@ static void *m_calloc(int size, char *name, int uflags) NCR_UNLOCK_DRIVER(flags); if (DEBUG_FLAGS & DEBUG_ALLOC) - printk ("new %s[%d] @%p.\n", name, size, p); + printk ("new %-10s[%4d] @%p.\n", name, size, p); if (p) memset(p, 0, size); @@ -775,13 +774,26 @@ static void m_free(void *ptr, int size, char *name) u_long flags; if (DEBUG_FLAGS & DEBUG_ALLOC) - printk ("freeing %s[%d] @%p.\n", name, size, ptr); + printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr); NCR_LOCK_DRIVER(flags); __m_free(ptr, size); NCR_UNLOCK_DRIVER(flags); } +static void ncr_print_hex(u_char *p, int n) +{ + while (n-- > 0) + printk (" %x", *p++); +} + +static void ncr_printl_hex(char *label, u_char *p, int n) +{ + printk("%s", label); + ncr_print_hex(p, n); + printk (".\n"); +} + /* ** Transfer direction ** @@ -831,34 +843,6 @@ static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset, ** This structure is initialized from linux config options. ** It can be overridden at boot-up by the boot command line. */ -#define SCSI_NCR_MAX_EXCLUDES 8 -struct ncr_driver_setup { - u_char master_parity; - u_char scsi_parity; - u_char disconnection; - u_char special_features; - u_char ultra_scsi; - u_char force_sync_nego; - u_char reverse_probe; - u_char pci_fix_up; - u_char use_nvram; - u_char verbose; - u_char default_tags; - u_short default_sync; - u_short debug; - u_char burst_max; - u_char led_pin; - u_char max_wide; - u_char settle_delay; - u_char diff_support; - u_char irqm; - u_char bus_check; - u_char optimize; - u_char recovery; - u_int excludes[SCSI_NCR_MAX_EXCLUDES]; - char tag_ctrl[100]; -}; - static struct ncr_driver_setup driver_setup = SCSI_NCR_DRIVER_SETUP; @@ -888,134 +872,7 @@ static void sym53c8xx_timeout(unsigned long np); #define bootverbose (np->verbose) #ifdef SCSI_NCR_NVRAM_SUPPORT -/* -** Symbios NvRAM data format -*/ -#define SYMBIOS_NVRAM_SIZE 368 -#define SYMBIOS_NVRAM_ADDRESS 0x100 - -struct Symbios_nvram { -/* Header 6 bytes */ - u_short type; /* 0x0000 */ - u_short byte_count; /* excluding header/trailer */ - u_short checksum; - -/* Controller set up 20 bytes */ - u_char v_major; /* 0x00 */ - u_char v_minor; /* 0x30 */ - u_int32 boot_crc; - u_short flags; -#define SYMBIOS_SCAM_ENABLE (1) -#define SYMBIOS_PARITY_ENABLE (1<<1) -#define SYMBIOS_VERBOSE_MSGS (1<<2) -#define SYMBIOS_CHS_MAPPING (1<<3) -#define SYMBIOS_NO_NVRAM (1<<3) /* ??? */ - u_short flags1; -#define SYMBIOS_SCAN_HI_LO (1) - u_short term_state; -#define SYMBIOS_TERM_CANT_PROGRAM (0) -#define SYMBIOS_TERM_ENABLED (1) -#define SYMBIOS_TERM_DISABLED (2) - u_short rmvbl_flags; -#define SYMBIOS_RMVBL_NO_SUPPORT (0) -#define SYMBIOS_RMVBL_BOOT_DEVICE (1) -#define SYMBIOS_RMVBL_MEDIA_INSTALLED (2) - u_char host_id; - u_char num_hba; /* 0x04 */ - u_char num_devices; /* 0x10 */ - u_char max_scam_devices; /* 0x04 */ - u_char num_valid_scam_devives; /* 0x00 */ - u_char rsvd; - -/* Boot order 14 bytes * 4 */ - struct Symbios_host{ - u_short type; /* 4:8xx / 0:nok */ - u_short device_id; /* PCI device id */ - u_short vendor_id; /* PCI vendor id */ - u_char bus_nr; /* PCI bus number */ - u_char device_fn; /* PCI device/function number << 3*/ - u_short word8; - u_short flags; -#define SYMBIOS_INIT_SCAN_AT_BOOT (1) - u_short io_port; /* PCI io_port address */ - } host[4]; - -/* Targets 8 bytes * 16 */ - struct Symbios_target { - u_char flags; -#define SYMBIOS_DISCONNECT_ENABLE (1) -#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1) -#define SYMBIOS_SCAN_LUNS (1<<2) -#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3) - u_char rsvd; - u_char bus_width; /* 0x08/0x10 */ - u_char sync_offset; - u_short sync_period; /* 4*period factor */ - u_short timeout; - } target[16]; -/* Scam table 8 bytes * 4 */ - struct Symbios_scam { - u_short id; - u_short method; -#define SYMBIOS_SCAM_DEFAULT_METHOD (0) -#define SYMBIOS_SCAM_DONT_ASSIGN (1) -#define SYMBIOS_SCAM_SET_SPECIFIC_ID (2) -#define SYMBIOS_SCAM_USE_ORDER_GIVEN (3) - u_short status; -#define SYMBIOS_SCAM_UNKNOWN (0) -#define SYMBIOS_SCAM_DEVICE_NOT_FOUND (1) -#define SYMBIOS_SCAM_ID_NOT_SET (2) -#define SYMBIOS_SCAM_ID_VALID (3) - u_char target_id; - u_char rsvd; - } scam[4]; - - u_char spare_devices[15*8]; - u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */ -}; -typedef struct Symbios_nvram Symbios_nvram; -typedef struct Symbios_host Symbios_host; -typedef struct Symbios_target Symbios_target; -typedef struct Symbios_scam Symbios_scam; - -/* -** Tekram NvRAM data format. -*/ -#define TEKRAM_NVRAM_SIZE 64 -#define TEKRAM_NVRAM_ADDRESS 0 - -struct Tekram_nvram { - struct Tekram_target { - u_char flags; -#define TEKRAM_PARITY_CHECK (1) -#define TEKRAM_SYNC_NEGO (1<<1) -#define TEKRAM_DISCONNECT_ENABLE (1<<2) -#define TEKRAM_START_CMD (1<<3) -#define TEKRAM_TAGGED_COMMANDS (1<<4) -#define TEKRAM_WIDE_NEGO (1<<5) - u_char sync_index; - u_short word2; - } target[16]; - u_char host_id; - u_char flags; -#define TEKRAM_MORE_THAN_2_DRIVES (1) -#define TEKRAM_DRIVES_SUP_1GB (1<<1) -#define TEKRAM_RESET_ON_POWER_ON (1<<2) -#define TEKRAM_ACTIVE_NEGATION (1<<3) -#define TEKRAM_IMMEDIATE_SEEK (1<<4) -#define TEKRAM_SCAN_LUNS (1<<5) -#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */ - u_char boot_delay_index; - u_char max_tags_index; - u_short flags1; -#define TEKRAM_F2_F6_ENABLED (1) - u_short spare[29]; -}; -typedef struct Tekram_nvram Tekram_nvram; -typedef struct Tekram_target Tekram_target; - static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21}; - #endif /* SCSI_NCR_NVRAM_SUPPORT */ /* @@ -1282,36 +1139,38 @@ typedef struct { #define SIR_BAD_STATUS (1) #define SIR_SEL_ATN_NO_MSG_OUT (2) -#define SIR_NEGO_SYNC (3) -#define SIR_NEGO_WIDE (4) +#define SIR_MSG_RECEIVED (3) +#define SIR_MSG_WEIRD (4) #define SIR_NEGO_FAILED (5) #define SIR_NEGO_PROTO (6) -#define SIR_REJECT_RECEIVED (7) +#define SIR_SCRIPT_STOPPED (7) #define SIR_REJECT_TO_SEND (8) -#define SIR_IGN_RESIDUE (9) -#define SIR_MISSING_SAVE (10) +#define SIR_SWIDE_OVERRUN (9) +#define SIR_SODL_UNDERRUN (10) #define SIR_RESEL_NO_MSG_IN (11) #define SIR_RESEL_NO_IDENTIFY (12) #define SIR_RESEL_BAD_LUN (13) -#define SIR_UNUSED_14 (14) +#define SIR_TARGET_SELECTED (14) #define SIR_RESEL_BAD_I_T_L (15) #define SIR_RESEL_BAD_I_T_L_Q (16) -#define SIR_UNUSED_17 (17) +#define SIR_ABORT_SENT (17) #define SIR_RESEL_ABORTED (18) #define SIR_MSG_OUT_DONE (19) -#define SIR_MAX (19) +#define SIR_AUTO_SENSE_DONE (20) +#define SIR_DUMMY_INTERRUPT (21) +#define SIR_MAX (21) /*========================================================== ** -** Extended error codes. +** Extended error bits. ** xerr_status field of struct ccb. ** **========================================================== */ -#define XE_OK (0) -#define XE_EXTRA_DATA (1) /* unexpected data phase */ -#define XE_BAD_PHASE (2) /* illegal phase (4/5) */ +#define XE_EXTRA_DATA (1) /* unexpected data phase */ +#define XE_BAD_PHASE (2) /* illegal phase (4/5) */ +#define XE_PARITY_ERR (4) /* unrecovered SCSI parity error */ /*========================================================== ** @@ -1334,9 +1193,6 @@ typedef struct { */ #define QUIRK_AUTOSAVE (0x01) -#define QUIRK_NOMSG (0x02) -#define QUIRK_NOSYNC (0x10) -#define QUIRK_NOWIDE16 (0x20) /*========================================================== ** @@ -1400,6 +1256,8 @@ struct usrcmd { #define UC_SETFLAG 15 #define UC_CLEARPROF 16 #define UC_SETVERBOSE 17 +#define UC_RESETDEV 18 +#define UC_CLEARDEV 19 #define UF_TRACE (0x01) #define UF_NODISC (0x02) @@ -1443,8 +1301,11 @@ struct tcb { */ u_int32 *luntbl; /* lcbs bus address table */ u_int32 b_luntbl; /* bus address of this table */ - lcb_p lp[MAX_LUN]; /* The lcb's of this tcb */ - + u_int32 b_lun0; /* bus address of lun0 */ + lcb_p l0p; /* lcb of LUN #0 (normal case) */ +#if MAX_LUN > 1 + lcb_p *lmp; /* Other lcb's [1..MAX_LUN] */ +#endif /*---------------------------------------------------------------- ** Target capabilities. **---------------------------------------------------------------- @@ -1453,6 +1314,12 @@ struct tcb { u_char inq_byte7; /* Contains these capabilities */ /*---------------------------------------------------------------- + ** Some flags. + **---------------------------------------------------------------- + */ + u_char to_reset; /* This target is to be reset */ + + /*---------------------------------------------------------------- ** Pointer to the ccb used for negotiation. ** Prevent from starting a negotiation for all queued commands ** when tagged command queuing is enabled. @@ -1488,8 +1355,8 @@ struct tcb { */ u_char usrsync; u_char usrwide; - u_char usrtags; u_char usrflag; + u_short usrtags; }; /*======================================================================== @@ -1526,11 +1393,11 @@ struct lcb { */ XPT_QUEHEAD busy_ccbq; /* Queue of busy CCBs */ XPT_QUEHEAD wait_ccbq; /* Queue of waiting for IO CCBs */ - u_char busyccbs; /* CCBs busy for this lun */ - u_char queuedccbs; /* CCBs queued to the controller*/ - u_char queuedepth; /* Queue depth for this lun */ - u_char scdev_depth; /* SCSI device queue depth */ - u_char maxnxs; /* Max possible nexuses */ + u_short busyccbs; /* CCBs busy for this lun */ + u_short queuedccbs; /* CCBs queued to the controller*/ + u_short queuedepth; /* Queue depth for this lun */ + u_short scdev_depth; /* SCSI device queue depth */ + u_short maxnxs; /* Max possible nexuses */ /*---------------------------------------------------------------- ** Control of tagged command queuing. @@ -1538,22 +1405,23 @@ struct lcb { ** This avoids using a loop for tag allocation. **---------------------------------------------------------------- */ - u_char ia_tag; /* Tag allocation index */ - u_char if_tag; /* Tag release index */ - u_char cb_tags[SCSI_NCR_MAX_TAGS]; /* Circular tags buffer */ + u_short ia_tag; /* Tag allocation index */ + u_short if_tag; /* Tag release index */ + u_char *cb_tags; /* Circular tags buffer */ + u_char inq_byte7; /* Store unit CmdQ capability */ u_char usetags; /* Command queuing is active */ - u_char maxtags; /* Max NR of tags asked by user */ - u_char numtags; /* Current number of tags */ - u_char inq_byte7; /* Store unit CmdQ capabitility */ + u_char to_clear; /* User wants to clear all tasks*/ + u_short maxtags; /* Max NR of tags asked by user */ + u_short numtags; /* Current number of tags */ /*---------------------------------------------------------------- ** QUEUE FULL and ORDERED tag control. **---------------------------------------------------------------- */ u_short num_good; /* Nr of GOOD since QUEUE FULL */ - tagmap_t tags_umap; /* Used tags bitmap */ - tagmap_t tags_smap; /* Tags in use at 'tag_stime' */ - u_long tags_stime; /* Last time we set smap=umap */ + u_short tags_sum[2]; /* Tags sum counters */ + u_char tags_si; /* Current index to tags sum */ + u_long tags_stime; /* Last time we switch tags_sum */ }; /*======================================================================== @@ -1641,6 +1509,18 @@ struct head { }; /* +** LUN control block lookup. +** We use a direct pointer for LUN #0, and a table of pointers +** which is only allocated for devices that support LUN(s) > 0. +*/ +#if MAX_LUN <= 1 +#define ncr_lp(np, tp, lun) (!lun) ? (tp)->l0p : 0 +#else +#define ncr_lp(np, tp, lun) \ + (!lun) ? (tp)->l0p : (tp)->lmp ? (tp)->lmp[(lun)] : 0 +#endif + +/* ** The status bytes are used by the host and the script processor. ** ** The last four bytes (status[4]) are copied to the scratchb register @@ -1683,10 +1563,14 @@ struct head { #define HF_IN_PM1 (1u<<1) #define HF_ACT_PM (1u<<2) #define HF_DP_SAVED (1u<<3) -#define HF_PAR_ERR (1u<<4) +#define HF_AUTO_SENSE (1u<<4) #define HF_DATA_ST (1u<<5) #define HF_PM_TO_C (1u<<6) +#ifdef SCSI_NCR_IARB_SUPPORT +#define HF_HINT_IARB (1u<<7) +#endif + /* ** First four bytes (script) */ @@ -1731,6 +1615,7 @@ struct dsb { struct scr_tblsel select; struct scr_tblmove smsg ; + struct scr_tblmove smsg_ext ; struct scr_tblmove cmd ; struct scr_tblmove sense ; struct scr_tblmove data [MAX_SCATTER]; @@ -1744,6 +1629,12 @@ struct dsb { struct pm_ctx pm0; struct pm_ctx pm1; + /* + ** Extra bytes count transferred + ** in case of data overrun. + */ + u_int32 extra_bytes; + #ifdef SCSI_NCR_PROFILE_SUPPORT /* ** Disconnection counter @@ -1774,8 +1665,8 @@ struct ccb { **---------------------------------------------------------------- */ Scsi_Cmnd *cmd; /* SCSI command */ - u_long tlimit; /* Deadline for this job */ int data_len; /* Total data length */ + int segments; /* Number of SG segments */ /*---------------------------------------------------------------- ** Message areas. @@ -1791,21 +1682,34 @@ struct ccb { u_char scsi_smsg2[8]; /*---------------------------------------------------------------- + ** Saved info for auto-sense + **---------------------------------------------------------------- + */ + u_char sv_scsi_status; + u_char sv_xerr_status; + + /*---------------------------------------------------------------- ** Other fields. **---------------------------------------------------------------- */ u_long p_ccb; /* BUS address of this CCB */ u_char sensecmd[6]; /* Sense command */ - u_char tag; /* Tag for this transfer */ - /* 255 means no tag */ + u_char to_abort; /* This CCB is to be aborted */ + u_short tag; /* Tag for this transfer */ + /* NO_TAG means no tag */ + u_char tags_si; /* Lun tags sum index (0,1) */ + u_char target; u_char lun; - u_char queued; - u_char auto_sense; + u_short queued; ccb_p link_ccb; /* Host adapter CCB chain */ ccb_p link_ccbh; /* Host adapter CCB hash chain */ XPT_QUEHEAD link_ccbq; /* Link to unit CCB queue */ u_int32 startp; /* Initial data pointer */ + u_int32 lastp0; /* Initial 'lastp' */ + int ext_sg; /* Extreme data pointer, used */ + int ext_ofs; /* to calculate the residual. */ + int resid; }; #define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl)) @@ -1899,11 +1803,15 @@ struct ncb { ** Virtual and physical bus addresses of the chip. **---------------------------------------------------------------- */ +#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED u_long base_va; /* MMIO base virtual address */ + u_long base2_va; /* On-chip RAM virtual address */ +#endif u_long base_ba; /* MMIO base bus address */ u_long base_io; /* IO space base address */ u_long base_ws; /* (MM)IO window size */ - u_long base2_ba; /* On-chip RAM bus address. */ + u_long base2_ba; /* On-chip RAM bus address */ + u_long base2_ws; /* On-chip RAM window size */ u_int irq; /* IRQ number */ volatile /* Pointer to volatile for */ struct ncr_reg *reg; /* memory mapped IO. */ @@ -1912,7 +1820,7 @@ struct ncb { ** SCRIPTS virtual and physical bus addresses. ** 'script' is loaded in the on-chip RAM if present. ** 'scripth' stays in main memory for all chips except the - ** 53C896 that provides 8K on-chip RAM. + ** 53C895A and 53C896 that provide 8K on-chip RAM. **---------------------------------------------------------------- */ struct script *script0; /* Copies of script and scripth */ @@ -2004,6 +1912,24 @@ struct ncb { XPT_QUEHEAD free_ccbq; /* Queue of available CCBs */ /*---------------------------------------------------------------- + ** IMMEDIATE ARBITRATION (IARB) control. + ** We keep track in 'last_cp' of the last CCB that has been + ** queued to the SCRIPTS processor and clear 'last_cp' when + ** this CCB completes. If last_cp is not zero at the moment + ** we queue a new CCB, we set a flag in 'last_cp' that is + ** used by the SCRIPTS as a hint for setting IARB. + ** We donnot set more than 'iarb_max' consecutive hints for + ** IARB in order to leave devices a chance to reselect. + ** By the way, any non zero value of 'iarb_max' is unfair. :) + **---------------------------------------------------------------- + */ +#ifdef SCSI_NCR_IARB_SUPPORT + struct ccb *last_cp; /* Last queud CCB used for IARB */ + u_short iarb_max; /* Max. # consecutive IARB hints*/ + u_short iarb_count; /* Actual # of these hints */ +#endif + + /*---------------------------------------------------------------- ** We need the LCB in order to handle disconnections and ** to count active CCBs for task management. So, we use ** a unique CCB for LUNs we donnot have the LCB yet. @@ -2019,6 +1945,17 @@ struct ncb { int (*scatter) (ccb_p, Scsi_Cmnd *); /*---------------------------------------------------------------- + ** Command abort handling. + ** We need to synchronize tightly with the SCRIPTS + ** processor in order to handle things correctly. + **---------------------------------------------------------------- + */ + u_char abrt_msg[4]; /* Message to send buffer */ + struct scr_tblmove abrt_tbl; /* Table for the MOV of it */ + struct scr_tblsel abrt_sel; /* Sync params for selection */ + u_char istat_sem; /* Tells the chip to stop (SEM) */ + + /*---------------------------------------------------------------- ** Fields that should be removed or changed. **---------------------------------------------------------------- */ @@ -2053,34 +1990,53 @@ struct ncb { /* ** Script fragments which are loaded into the on-chip RAM -** of 825A, 875, 876, 895 and 896 chips. +** of 825A, 875, 876, 895, 895A and 896 chips. */ struct script { - ncrcmd start [ 10]; + ncrcmd start [ 18]; ncrcmd getjob_begin [ 4]; ncrcmd getjob_end [ 4]; - ncrcmd select [ 4]; + ncrcmd select [ 8]; ncrcmd wf_sel_done [ 2]; ncrcmd send_ident [ 2]; - ncrcmd select2 [ 6]; +#ifdef SCSI_NCR_IARB_SUPPORT + ncrcmd select2 [ 8]; +#else + ncrcmd select2 [ 2]; +#endif ncrcmd command [ 2]; - ncrcmd dispatch [ 26]; + ncrcmd dispatch [ 28]; ncrcmd sel_no_cmd [ 10]; ncrcmd init [ 6]; ncrcmd clrack [ 4]; - ncrcmd databreak [ 2]; + ncrcmd disp_msg_in [ 2]; + ncrcmd disp_status [ 4]; + ncrcmd datai_done [ 16]; + ncrcmd datao_done [ 10]; + ncrcmd ign_i_w_r_msg [ 4]; #ifdef SCSI_NCR_PROFILE_SUPPORT ncrcmd dataphase [ 4]; #else ncrcmd dataphase [ 2]; #endif - ncrcmd status [ 8]; ncrcmd msg_in [ 2]; - ncrcmd msg_in2 [ 16]; - ncrcmd msg_bad [ 6]; + ncrcmd msg_in2 [ 10]; +#ifdef SCSI_NCR_IARB_SUPPORT + ncrcmd status [ 14]; +#else + ncrcmd status [ 10]; +#endif ncrcmd complete [ 8]; - ncrcmd complete2 [ 6]; +#ifdef SCSI_NCR_PCIQ_MAY_REORDER_WRITES + ncrcmd complete2 [ 12]; +#else + ncrcmd complete2 [ 10]; +#endif +#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR + ncrcmd done [ 18]; +#else ncrcmd done [ 14]; +#endif ncrcmd done_end [ 2]; ncrcmd save_dp [ 8]; ncrcmd restore_dp [ 4]; @@ -2089,57 +2045,64 @@ struct script { #else ncrcmd disconnect [ 20]; #endif +#ifdef SCSI_NCR_IARB_SUPPORT + ncrcmd idle [ 4]; +#else ncrcmd idle [ 2]; +#endif +#ifdef SCSI_NCR_IARB_SUPPORT + ncrcmd ungetjob [ 6]; +#else ncrcmd ungetjob [ 4]; +#endif ncrcmd reselect [ 4]; - ncrcmd reselected [ 44]; + ncrcmd reselected [ 48]; +#if MAX_TASKS*4 > 512 + ncrcmd resel_tag [ 16]; +#elif MAX_TASKS*4 > 256 + ncrcmd resel_tag [ 10]; +#else ncrcmd resel_tag [ 6]; +#endif ncrcmd resel_go [ 6]; ncrcmd resel_notag [ 4]; ncrcmd resel_dsa [ 8]; - ncrcmd data_in [MAX_SCATTERL * SCR_SG_SIZE]; + ncrcmd data_in [MAX_SCATTER * SCR_SG_SIZE]; ncrcmd data_in2 [ 4]; - ncrcmd data_out [MAX_SCATTERL * SCR_SG_SIZE]; + ncrcmd data_out [MAX_SCATTER * SCR_SG_SIZE]; ncrcmd data_out2 [ 4]; ncrcmd pm0_data [ 16]; ncrcmd pm1_data [ 16]; - - /* Data area */ - ncrcmd saved_dsa [ 1]; - ncrcmd done_pos [ 1]; - ncrcmd startpos [ 1]; - ncrcmd targtbl [ 1]; }; /* ** Script fragments which stay in main memory for all chips -** except for the 896 that support 8K on-chip RAM. +** except for the 895A and 896 that support 8K on-chip RAM. */ struct scripth { ncrcmd start64 [ 2]; - ncrcmd select_no_atn [ 4]; + ncrcmd sel_for_abort [ 18]; + ncrcmd sel_for_abort_1 [ 2]; + ncrcmd select_no_atn [ 8]; ncrcmd wf_sel_done_no_atn [ 4]; - ncrcmd cancel [ 4]; - ncrcmd msg_reject [ 8]; - ncrcmd msg_ign_residue [ 24]; - ncrcmd msg_extended [ 10]; - ncrcmd msg_ext_2 [ 10]; - ncrcmd msg_wdtr [ 14]; + + ncrcmd msg_in_etc [ 14]; + ncrcmd msg_received [ 4]; + ncrcmd msg_weird_seen [ 4]; + ncrcmd msg_extended [ 20]; + ncrcmd msg_bad [ 6]; + ncrcmd msg_weird [ 4]; + ncrcmd msg_weird1 [ 8]; + + ncrcmd wdtr_resp [ 6]; ncrcmd send_wdtr [ 4]; - ncrcmd msg_ext_3 [ 10]; - ncrcmd msg_sdtr [ 14]; + ncrcmd sdtr_resp [ 6]; ncrcmd send_sdtr [ 4]; ncrcmd nego_bad_phase [ 4]; ncrcmd msg_out_abort [ 12]; ncrcmd msg_out [ 6]; ncrcmd msg_out_done [ 4]; - ncrcmd no_data [ 16]; -#if MAX_SCATTERH != 0 - ncrcmd hdata_in [MAX_SCATTERH * SCR_SG_SIZE]; - ncrcmd hdata_in2 [ 2]; - ncrcmd hdata_out [MAX_SCATTERH * SCR_SG_SIZE]; - ncrcmd hdata_out2 [ 2]; -#endif + ncrcmd no_data [ 28]; ncrcmd abort_resel [ 16]; ncrcmd resend_ident [ 4]; ncrcmd ident_break [ 4]; @@ -2156,22 +2119,39 @@ struct scripth { ncrcmd pm_handle [ 20]; ncrcmd pm_handle1 [ 4]; ncrcmd pm_save [ 4]; - ncrcmd pm0_save [ 10]; - ncrcmd pm1_save [ 10]; + ncrcmd pm0_save [ 14]; + ncrcmd pm1_save [ 14]; + + /* SWIDE handling */ + ncrcmd swide_ma_32 [ 4]; + ncrcmd swide_ma_64 [ 6]; + ncrcmd swide_scr_64 [ 26]; + ncrcmd swide_scr_64_1 [ 12]; + ncrcmd swide_com_64 [ 6]; + ncrcmd swide_common [ 10]; + ncrcmd swide_fin_32 [ 24]; /* Data area */ + ncrcmd zero [ 1]; + ncrcmd scratch [ 1]; + ncrcmd scratch1 [ 1]; ncrcmd pm0_data_addr [ 1]; ncrcmd pm1_data_addr [ 1]; + ncrcmd saved_dsa [ 1]; + ncrcmd saved_drs [ 1]; + ncrcmd done_pos [ 1]; + ncrcmd startpos [ 1]; + ncrcmd targtbl [ 1]; /* End of data area */ +#ifdef SCSI_NCR_PCI_MEM_NOT_SUPPORTED ncrcmd start_ram [ 1]; ncrcmd script0_ba [ 4]; - ncrcmd start_ram64 [ 3]; ncrcmd script0_ba64 [ 3]; ncrcmd scripth0_ba64 [ 6]; ncrcmd ram_seg64 [ 1]; - +#endif ncrcmd snooptest [ 6]; ncrcmd snoopend [ 2]; }; @@ -2195,6 +2175,7 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln); static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data); static void ncr_getclock (ncb_p np, int mult); +static u_int ncr_getpciclock (ncb_p np); static void ncr_selectclock (ncb_p np, u_char scntl3); static ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln); static void ncr_init (ncb_p np, int reset, char * msg, u_long code); @@ -2204,8 +2185,8 @@ static void ncr_int_ma (ncb_p np); static void ncr_int_sir (ncb_p np); static void ncr_int_sto (ncb_p np); static void ncr_int_udc (ncb_p np); -static u_long ncr_lookup (char* id); -static void ncr_negotiate (struct ncb* np, struct tcb* tp); +static void ncr_negotiate (ncb_p np, tcb_p tp); +static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr); #ifdef SCSI_NCR_PROFILE_SUPPORT static void ncb_profile (ncb_p np, ccb_p cp); #endif @@ -2219,6 +2200,7 @@ 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); static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack); static int ncr_show_msg (u_char * msg); +static void ncr_print_msg (ccb_p cp, char *label, u_char * msg); static int ncr_snooptest (ncb_p np); static void ncr_timeout (ncb_p np); static void ncr_wakeup (ncb_p np, u_long code); @@ -2228,6 +2210,7 @@ static void ncr_put_start_queue(ncb_p np, ccb_p cp); static void ncr_soft_reset (ncb_p np); static void ncr_start_reset (ncb_p np); static int ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay); +static int ncr_compute_residual (ncb_p np, ccb_p cp); #ifdef SCSI_NCR_USER_COMMAND_SUPPORT static void ncr_usercmd (ncb_p np); @@ -2328,6 +2311,25 @@ static struct script script0 __initdata = { */ SCR_FROM_REG (ctest2), 0, + + /* + ** Stop here if the C code wants to perform + ** some error recovery procedure manually. + ** (Indicate this by setting SEM in ISTAT) + */ + SCR_FROM_REG (istat), + 0, + SCR_JUMPR ^ IFFALSE (MASK (SEM, SEM)), + 16, + /* + ** Report to the C code the next position in + ** the start queue the SCRIPTS will schedule. + */ + SCR_LOAD_ABS (scratcha, 4), + PADDRH (startpos), + SCR_INT, + SIR_SCRIPT_STOPPED, + /* ** Start the next job. ** @@ -2343,14 +2345,14 @@ static struct script script0 __initdata = { ** and the the next queue position points to the next JOB. */ SCR_LOAD_ABS (scratcha, 4), - PADDR (startpos), + PADDRH (startpos), SCR_LOAD_ABS (dsa, 4), - PADDR (startpos), + PADDRH (startpos), SCR_LOAD_REL (temp, 4), 4, }/*-------------------------< GETJOB_BEGIN >------------------*/,{ SCR_STORE_ABS (temp, 4), - PADDR (startpos), + PADDRH (startpos), SCR_LOAD_REL (dsa, 4), 0, }/*-------------------------< GETJOB_END >--------------------*/,{ @@ -2407,6 +2409,19 @@ static struct script script0 __initdata = { ** So we have to wait immediately for the next phase ** or the selection to complete or time-out. */ + + /* + ** load the savep (saved pointer) into + ** the actual data pointer. + */ + SCR_LOAD_REL (temp, 4), + offsetof (struct ccb, phys.header.savep), + /* + ** Initialize the status registers + */ + SCR_LOAD_REL (scr0, 4), + offsetof (struct ccb, phys.header.status), + }/*-------------------------< WF_SEL_DONE >----------------------*/,{ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), SIR_SEL_ATN_NO_MSG_OUT, @@ -2419,17 +2434,18 @@ static struct script script0 __initdata = { SCR_MOVE_TBL ^ SCR_MSG_OUT, offsetof (struct dsb, smsg), }/*-------------------------< SELECT2 >----------------------*/,{ +#ifdef SCSI_NCR_IARB_SUPPORT /* - ** load the savep (saved pointer) into - ** the actual data pointer. - */ - SCR_LOAD_REL (temp, 4), - offsetof (struct ccb, phys.header.savep), - /* - ** Initialize the status registers + ** Set IMMEDIATE ARBITRATION if we have been given + ** a hint to do so. (Some job to do after this one). */ - SCR_LOAD_REL (scr0, 4), - offsetof (struct ccb, phys.header.status), + SCR_FROM_REG (HF_REG), + 0, + SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)), + 8, + SCR_REG_REG (scntl1, SCR_OR, IARB), + 0, +#endif /* ** Anticipate the COMMAND phase. ** This is the PHASE we expect at this point. @@ -2465,7 +2481,9 @@ static struct script script0 __initdata = { /* ** Discard one illegal phase byte, if required. */ - SCR_LOAD_REG (scratcha, XE_BAD_PHASE), + SCR_LOAD_REL (scratcha, 1), + offsetof (struct ccb, xerr_status), + SCR_REG_REG (scratcha, SCR_OR, XE_BAD_PHASE), 0, SCR_STORE_REL (scratcha, 1), offsetof (struct ccb, xerr_status), @@ -2517,7 +2535,7 @@ static struct script script0 __initdata = { SCR_FROM_REG (sstat0), 0, SCR_JUMPR ^ IFTRUE (MASK (IRST, IRST)), - -8, + -16, SCR_JUMP, PADDR (start), }/*-------------------------< CLRACK >----------------------*/,{ @@ -2529,37 +2547,115 @@ static struct script script0 __initdata = { SCR_JUMP, PADDR (dispatch), -}/*-------------------------< DATABREAK >-------------------*/,{ +}/*-------------------------< DISP_MSG_IN >----------------------*/,{ /* - ** Jump to dispatcher. + ** Anticipate MSG_IN phase then STATUS phase. + ** + ** May spare 2 SCRIPTS instructions when we have + ** completed the OUTPUT of the data and the device + ** goes directly to STATUS phase. + */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)), + PADDR (msg_in), + +}/*-------------------------< DISP_STATUS >----------------------*/,{ + /* + ** Anticipate STATUS phase. + ** + ** Does spare 3 SCRIPTS instructions when we have + ** completed the INPUT of the data. */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)), + PADDR (status), SCR_JUMP, PADDR (dispatch), -}/*-------------------------< DATAPHASE >------------------*/,{ -#ifdef SCSI_NCR_PROFILE_SUPPORT - SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST), +}/*-------------------------< DATAI_DONE >-------------------*/,{ + /* + ** If the SWIDE is not full, jump to dispatcher. + ** We anticipate a STATUS phase. + ** If we get later an IGNORE WIDE RESIDUE, we + ** will alias it as a MODIFY DP (-1). + */ + SCR_FROM_REG (scntl2), 0, -#endif - SCR_RETURN, - 0, - -}/*-------------------------< STATUS >--------------------*/,{ + SCR_JUMP ^ IFFALSE (MASK (WSR, WSR)), + PADDR (disp_status), /* - ** get the status + ** The SWIDE is full. + ** Clear this condition. */ - SCR_MOVE_ABS (1) ^ SCR_STATUS, - NADDR (scratch), + SCR_REG_REG (scntl2, SCR_OR, WSR), + 0, /* - ** save status to scsi_status. - ** mark as complete. + ** Since the device is required to send any + ** IGNORE WIDE RESIDUE message prior to any + ** other information, we just snoop the SCSI + ** BUS to check for such a message. */ - SCR_TO_REG (SS_REG), + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (disp_status), + SCR_FROM_REG (sbdl), 0, - SCR_LOAD_REG (HS_REG, HS_COMPLETE), + SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)), + PADDR (disp_status), + /* + ** We have been ODD at the end of the transfer, + ** but the device hasn't be so. + ** Signal a DATA OVERRUN condition to the C code. + */ + SCR_INT, + SIR_SWIDE_OVERRUN, + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< DATAO_DONE >-------------------*/,{ + /* + ** If the SODL is not full jump to dispatcher. + ** We anticipate a MSG IN phase or a STATUS phase. + */ + SCR_FROM_REG (scntl2), 0, + SCR_JUMP ^ IFFALSE (MASK (WSS, WSS)), + PADDR (disp_msg_in), + /* + ** The SODL is full, clear this condition. + */ + SCR_REG_REG (scntl2, SCR_OR, WSS), + 0, + /* + ** And signal a DATA UNDERRUN condition + ** to the C code. + */ + SCR_INT, + SIR_SODL_UNDERRUN, SCR_JUMP, PADDR (dispatch), + +}/*-------------------------< IGN_I_W_R_MSG >--------------*/,{ + /* + ** We jump here from the phase mismatch interrupt, + ** When we have a SWIDE and the device has presented + ** a IGNORE WIDE RESIDUE message on the BUS. + ** We just have to throw away this message and then + ** to jump to dispatcher. + */ + SCR_MOVE_ABS (2) ^ SCR_MSG_IN, + NADDR (scratch), + /* + ** Clear ACK and jump to dispatcher. + */ + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< DATAPHASE >------------------*/,{ +#ifdef SCSI_NCR_PROFILE_SUPPORT + SCR_REG_REG (HF_REG, SCR_OR, HF_DATA_ST), + 0, +#endif + SCR_RETURN, + 0, + }/*-------------------------< MSG_IN >--------------------*/,{ /* ** Get the first byte of the message. @@ -2571,7 +2667,8 @@ static struct script script0 __initdata = { NADDR (msgin[0]), }/*-------------------------< MSG_IN2 >--------------------*/,{ /* - ** Handle this message. + ** Check first against 1 byte messages + ** that we handle from SCRIPTS. */ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)), PADDR (complete), @@ -2581,31 +2678,47 @@ static struct script script0 __initdata = { PADDR (save_dp), SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)), PADDR (restore_dp), - SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), - PADDRH (msg_extended), - SCR_JUMP ^ IFTRUE (DATA (M_NOOP)), - PADDR (clrack), - SCR_JUMP ^ IFTRUE (DATA (M_REJECT)), - PADDRH (msg_reject), - SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)), - PADDRH (msg_ign_residue), /* - ** Rest of the messages left as - ** an exercise ... - ** - ** Unimplemented messages: - ** fall through to MSG_BAD. + ** We handle all other messages from the + ** C code, so no need to waste on-chip RAM + ** for those ones. */ -}/*-------------------------< MSG_BAD >------------------*/,{ + SCR_JUMP, + PADDRH (msg_in_etc), + +}/*-------------------------< STATUS >--------------------*/,{ /* - ** unimplemented message - reject it. + ** get the status */ - SCR_INT, - SIR_REJECT_TO_SEND, - SCR_SET (SCR_ATN), + SCR_MOVE_ABS (1) ^ SCR_STATUS, + NADDR (scratch), +#ifdef SCSI_NCR_IARB_SUPPORT + /* + ** If STATUS is not GOOD, clear IMMEDIATE ARBITRATION, + ** since we may have to tamper the start queue from + ** the C code. + */ + SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)), + 8, + SCR_REG_REG (scntl1, SCR_AND, ~IARB), + 0, +#endif + /* + ** save status to scsi_status. + ** mark as complete. + */ + SCR_TO_REG (SS_REG), 0, + SCR_LOAD_REG (HS_REG, HS_COMPLETE), + 0, + /* + ** Anticipate the MESSAGE PHASE for + ** the TASK COMPLETE message. + */ + SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)), + PADDR (msg_in), SCR_JUMP, - PADDR (clrack), + PADDR (dispatch), }/*-------------------------< COMPLETE >-----------------*/,{ /* @@ -2642,6 +2755,18 @@ static struct script script0 __initdata = { SCR_STORE_REL (scr0, 4), offsetof (struct ccb, phys.header.status), +#ifdef SCSI_NCR_PCIQ_MAY_REORDER_WRITES + /* + ** Some bridges may reorder DMA writes to memory. + ** We donnot want the CPU to deal with completions + ** without all the posted write having been flushed + ** to memory. This DUMMY READ should flush posted + ** buffers prior to the CPU having to deal with + ** completions. + */ + SCR_LOAD_REL (scr0, 4), /* DUMMY READ */ + offsetof (struct ccb, phys.header.status), +#endif /* ** If command resulted in not GOOD status, ** call the C code if needed. @@ -2651,7 +2776,35 @@ static struct script script0 __initdata = { SCR_CALL ^ IFFALSE (DATA (S_GOOD)), PADDRH (bad_status), + /* + ** If we performed an auto-sense, call + ** the C code to synchronyze task aborts + ** with UNIT ATTENTION conditions. + */ + SCR_FROM_REG (HF_REG), + 0, + SCR_INT ^ IFTRUE (MASK (HF_AUTO_SENSE, HF_AUTO_SENSE)), + SIR_AUTO_SENSE_DONE, + }/*------------------------< DONE >-----------------*/,{ +#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR + /* + ** It seems that some bridges flush everything + ** when the INTR line is raised. For these ones, + ** we can just ensure that the INTR line will be + ** raised before each completion. So, if it happens + ** that we have been faster that the CPU, we just + ** have to synchronize with it. A dummy programmed + ** interrupt will do the trick. + ** Note that we overlap at most 1 IO with the CPU + ** in this situation and that the IRQ line must not + ** be shared. + */ + SCR_FROM_REG (istat), + 0, + SCR_INT ^ IFTRUE (MASK (INTF, INTF)), + SIR_DUMMY_INTERRUPT, +#endif /* ** Copy the DSA to the DONE QUEUE and ** signal completion to the host. @@ -2660,11 +2813,11 @@ static struct script script0 __initdata = { ** the completed CCB will be lost. */ SCR_STORE_ABS (dsa, 4), - PADDR (saved_dsa), + PADDRH (saved_dsa), SCR_LOAD_ABS (dsa, 4), - PADDR (done_pos), + PADDRH (done_pos), SCR_LOAD_ABS (scratcha, 4), - PADDR(saved_dsa), + PADDRH (saved_dsa), SCR_STORE_REL (scratcha, 4), 0, /* @@ -2679,7 +2832,7 @@ static struct script script0 __initdata = { SCR_INT_FLY, 0, SCR_STORE_ABS (temp, 4), - PADDR (done_pos), + PADDRH (done_pos), }/*------------------------< DONE_END >-----------------*/,{ SCR_JUMP, PADDR (start), @@ -2791,7 +2944,20 @@ static struct script script0 __initdata = { */ SCR_NO_OP, 0, +#ifdef SCSI_NCR_IARB_SUPPORT + SCR_JUMPR, + 8, +#endif }/*-------------------------< UNGETJOB >-----------------*/,{ +#ifdef SCSI_NCR_IARB_SUPPORT + /* + ** Set IMMEDIATE ARBITRATION, for the next time. + ** This will give us better chance to win arbitration + ** for the job we just wanted to do. + */ + SCR_REG_REG (scntl1, SCR_OR, IARB), + 0, +#endif /* ** We are not able to restart the SCRIPTS if we are ** interrupted and these instruction haven't been @@ -2801,7 +2967,7 @@ static struct script script0 __initdata = { SCR_LOAD_REG (dsa, 0xff), 0, SCR_STORE_ABS (scratcha, 4), - PADDR (startpos), + PADDRH (startpos), }/*-------------------------< RESELECT >--------------------*/,{ /* ** make the host status invalid. @@ -2834,7 +3000,7 @@ static struct script script0 __initdata = { ** load the target control block address */ SCR_LOAD_ABS (dsa, 4), - PADDR (targtbl), + PADDRH (targtbl), SCR_SFBR_REG (dsa, SCR_SHL, 0), 0, SCR_REG_REG (dsa, SCR_SHL, 0), @@ -2870,12 +3036,13 @@ static struct script script0 __initdata = { /* ** It is an IDENTIFY message, ** Load the LUN control block address. - ** Avoid nasty address calculation if LUN #0. + ** If LUN 0, avoid a PCI BUS ownership by loading + ** directly 'b_lun0' from the TCB. */ + SCR_JUMPR ^ IFTRUE (MASK (0x0, 0x3f)), + 48, SCR_LOAD_REL (dsa, 4), offsetof(struct tcb, b_luntbl), - SCR_JUMPR ^ IFTRUE (MASK (0x0, 0x3f)), - 24, SCR_SFBR_REG (dsa, SCR_SHL, 0), 0, SCR_REG_REG (dsa, SCR_SHL, 0), @@ -2884,6 +3051,14 @@ static struct script script0 __initdata = { 0, SCR_LOAD_REL (dsa, 4), 0, + SCR_JUMPR, + 8, + /* + ** LUN 0 special case (but usual one :)) + */ + SCR_LOAD_REL (dsa, 4), + offsetof(struct tcb, b_lun0), + /* ** Load the reselect task action for this LUN. ** Load the tasks DSA array for this LUN. @@ -2914,6 +3089,23 @@ static struct script script0 __initdata = { */ SCR_REG_SFBR (sidl, SCR_SHL, 0), 0, +#if MAX_TASKS*4 > 512 + SCR_JUMPR ^ IFFALSE (CARRYSET), + 8, + SCR_REG_REG (dsa1, SCR_OR, 2), + 0, + SCR_REG_REG (sfbr, SCR_SHL, 0), + 0, + SCR_JUMPR ^ IFFALSE (CARRYSET), + 8, + SCR_REG_REG (dsa1, SCR_OR, 1), + 0, +#elif MAX_TASKS*4 > 256 + SCR_JUMPR ^ IFFALSE (CARRYSET), + 8, + SCR_REG_REG (dsa1, SCR_OR, 1), + 0, +#endif /* ** Retrieve the DSA of this task. ** JUMP indirectly to the restart point of the CCB. @@ -2966,13 +3158,11 @@ static struct script script0 __initdata = { }/*-------------------------< DATA_IN >--------------------*/,{ /* ** Because the size depends on the -** #define MAX_SCATTERL parameter, +** #define MAX_SCATTER parameter, ** it is filled in at runtime. ** -** ##===========< i=0; i<MAX_SCATTERL >========= -** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), -** || PADDR (databreak), -** || SCR_MOVE_TBL ^ SCR_DATA_IN, +** ##===========< i=0; i<MAX_SCATTER >========= +** || SCR_CHMOV_TBL ^ SCR_DATA_IN, ** || offsetof (struct dsb, data[ i]), ** ##========================================== ** @@ -2981,19 +3171,17 @@ static struct script script0 __initdata = { 0 }/*-------------------------< DATA_IN2 >-------------------*/,{ SCR_CALL, - PADDR (databreak), + PADDR (datai_done), SCR_JUMP, PADDRH (no_data), }/*-------------------------< DATA_OUT >--------------------*/,{ /* ** Because the size depends on the -** #define MAX_SCATTERL parameter, +** #define MAX_SCATTER parameter, ** it is filled in at runtime. ** -** ##===========< i=0; i<MAX_SCATTERL >========= -** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), -** || PADDR (databreak), -** || SCR_MOVE_TBL ^ SCR_DATA_OUT, +** ##===========< i=0; i<MAX_SCATTER >========= +** || SCR_CHMOV_TBL ^ SCR_DATA_OUT, ** || offsetof (struct dsb, data[ i]), ** ##========================================== ** @@ -3002,7 +3190,7 @@ static struct script script0 __initdata = { 0 }/*-------------------------< DATA_OUT2 >-------------------*/,{ SCR_CALL, - PADDR (databreak), + PADDR (datao_done), SCR_JUMP, PADDRH (no_data), @@ -3019,11 +3207,11 @@ static struct script script0 __initdata = { */ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)), 16, - SCR_MOVE_TBL ^ SCR_DATA_IN, + SCR_CHMOV_TBL ^ SCR_DATA_IN, offsetof (struct ccb, phys.pm0.sg), SCR_JUMPR, 8, - SCR_MOVE_TBL ^ SCR_DATA_OUT, + SCR_CHMOV_TBL ^ SCR_DATA_OUT, offsetof (struct ccb, phys.pm0.sg), /* ** Clear the flag that told we were in @@ -3053,11 +3241,11 @@ static struct script script0 __initdata = { */ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)), 16, - SCR_MOVE_TBL ^ SCR_DATA_IN, + SCR_CHMOV_TBL ^ SCR_DATA_IN, offsetof (struct ccb, phys.pm1.sg), SCR_JUMPR, 8, - SCR_MOVE_TBL ^ SCR_DATA_OUT, + SCR_CHMOV_TBL ^ SCR_DATA_OUT, offsetof (struct ccb, phys.pm1.sg), /* ** Clear the flag that told we were in @@ -3074,27 +3262,76 @@ static struct script script0 __initdata = { offsetof (struct ccb, phys.pm1.ret), SCR_RETURN, 0, - -}/*-------------------------< SAVED_DSA >-------------------*/,{ - SCR_DATA_ZERO, -}/*-------------------------< DONE_POS >--------------------*/,{ - SCR_DATA_ZERO, -}/*-------------------------< STARTPOS >--------------------*/,{ - SCR_DATA_ZERO, -}/*-------------------------< TARGTBL >---------------------*/,{ - SCR_DATA_ZERO, -}/*--------------------------------------------------------*/ +}/*---------------------------------------------------------*/ }; static struct scripth scripth0 __initdata = { /*------------------------< START64 >-----------------------*/{ /* - ** SCRIPT entry point for the 896. + ** SCRIPT entry point for the 895A and the 896. ** For now, there is no specific stuff for that ** chip at this point, but this may come. */ SCR_JUMP, PADDR (init), + +}/*-----------------------< SEL_FOR_ABORT >------------------*/,{ + /* + ** We are jumped here by the C code, if we have + ** some target to reset or some disconnected + ** job to abort. Since error recovery is a serious + ** busyness, we will really reset the SCSI BUS, if + ** case of a SCSI interrupt occuring in this path. + */ + + /* + ** Set initiator mode. + */ + SCR_CLR (SCR_TRG), + 0, + /* + ** And try to select this target. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct ncb, abrt_sel), + PADDR (reselect), + + /* + ** Wait for the selection to complete or + ** the selection to time out. + */ + SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)), + -8, + /* + ** Call the C code. + */ + SCR_INT, + SIR_TARGET_SELECTED, + /* + ** The C code should let us continue here. + ** Send the 'kiss of death' message. + ** We expect an immediate disconnect once + ** the target has eaten the message. + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct ncb, abrt_tbl), + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + SCR_WAIT_DISC, + 0, + /* + ** Tell the C code that we are done. + */ + SCR_INT, + SIR_ABORT_SENT, +}/*-----------------------< SEL_FOR_ABORT_1 >--------------*/,{ + /* + ** Jump at scheduler. + */ + SCR_JUMP, + PADDR (start), + }/*------------------------< SELECT_NO_ATN >-----------------*/,{ /* ** Set Initiator mode. @@ -3105,6 +3342,18 @@ static struct scripth scripth0 __initdata = { 0, SCR_SEL_TBL ^ offsetof (struct dsb, select), PADDR (ungetjob), + /* + ** load the savep (saved pointer) into + ** the actual data pointer. + */ + SCR_LOAD_REL (temp, 4), + offsetof (struct ccb, phys.header.savep), + /* + ** Initialize the status registers + */ + SCR_LOAD_REL (scr0, 4), + offsetof (struct ccb, phys.header.status), + }/*------------------------< WF_SEL_DONE_NO_ATN >-----------------*/,{ /* ** Wait immediately for the next phase or @@ -3115,129 +3364,111 @@ static struct scripth scripth0 __initdata = { SCR_JUMP, PADDR (select2), -}/*-------------------------< CANCEL >------------------------*/,{ +}/*-------------------------< MSG_IN_ETC >--------------------*/,{ /* - ** Load the host status. + ** If it is an EXTENDED (variable size message) + ** Handle it. */ - SCR_LOAD_REG (HS_REG, HS_ABORTED), - 0, - SCR_JUMP, - PADDR (complete2), - -}/*-------------------------< MSG_REJECT >---------------*/,{ + SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), + PADDRH (msg_extended), /* - ** If a negotiation was in progress, - ** negotiation failed. - ** Otherwise just make host log this message + ** Let the C code handle any other + ** 1 byte message. */ - SCR_FROM_REG (HS_REG), + SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)), + PADDRH (msg_received), + SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)), + PADDRH (msg_received), + /* + ** We donnot handle 2 bytes messages from SCRIPTS. + ** So, let the C code deal with these ones too. + */ + SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)), + PADDRH (msg_weird_seen), + SCR_CLR (SCR_ACK), 0, - SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), - SIR_REJECT_RECEIVED, - SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), - SIR_NEGO_FAILED, + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[1]), SCR_JUMP, - PADDR (clrack), + PADDRH (msg_received), + +}/*-------------------------< MSG_RECEIVED >--------------------*/,{ + SCR_LOAD_REL (scratcha, 4), /* DUMMY READ */ + 0, + SCR_INT, + SIR_MSG_RECEIVED, + +}/*-------------------------< MSG_WEIRD_SEEN >------------------*/,{ + SCR_LOAD_REL (scratcha1, 4), /* DUMMY READ */ + 0, + SCR_INT, + SIR_MSG_WEIRD, -}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ +}/*-------------------------< MSG_EXTENDED >--------------------*/,{ /* - ** Terminate cycle + ** Clear ACK and get the next byte + ** assumed to be the message length. */ SCR_CLR (SCR_ACK), 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get residue size. - */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, NADDR (msgin[1]), /* - ** Size is 0 .. ignore message. + ** Try to catch some unlikely situations as 0 length + ** or too large the length. */ SCR_JUMP ^ IFTRUE (DATA (0)), - PADDR (clrack), - /* - ** Size is not 1 .. have to interrupt. - */ - SCR_JUMPR ^ IFFALSE (DATA (1)), - 40, - /* - ** Check for residue byte in swide register - */ - SCR_FROM_REG (scntl2), + PADDRH (msg_weird_seen), + SCR_TO_REG (scratcha), 0, - SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), - 16, + SCR_REG_REG (sfbr, SCR_ADD, (256-8)), + 0, + SCR_JUMP ^ IFTRUE (CARRYSET), + PADDRH (msg_weird_seen), /* - ** There IS data in the swide register. - ** Discard it. + ** We donnot handle extended messages from SCRIPTS. + ** Read the amount of data correponding to the + ** message length and call the C code. */ - SCR_REG_REG (scntl2, SCR_OR, WSR), + SCR_STORE_REL (scratcha, 1), + offsetof (struct dsb, smsg_ext.size), + SCR_CLR (SCR_ACK), 0, + SCR_MOVE_TBL ^ SCR_MSG_IN, + offsetof (struct dsb, smsg_ext), SCR_JUMP, - PADDR (clrack), + PADDRH (msg_received), + +}/*-------------------------< MSG_BAD >------------------*/,{ /* - ** Load again the size to the sfbr register. + ** unimplemented message - reject it. */ - SCR_FROM_REG (scratcha), - 0, SCR_INT, - SIR_IGN_RESIDUE, + SIR_REJECT_TO_SEND, + SCR_SET (SCR_ATN), + 0, SCR_JUMP, PADDR (clrack), -}/*-------------------------< MSG_EXTENDED >-------------*/,{ +}/*-------------------------< MSG_WEIRD >--------------------*/,{ /* - ** Terminate cycle + ** weird message received + ** ignore all MSG IN phases and reject it. */ - SCR_CLR (SCR_ACK), + SCR_INT, + SIR_REJECT_TO_SEND, + SCR_SET (SCR_ATN), 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get length. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[1]), - /* - */ - SCR_JUMP ^ IFTRUE (DATA (3)), - PADDRH (msg_ext_3), - SCR_JUMP ^ IFFALSE (DATA (2)), - PADDR (msg_bad), -}/*-------------------------< MSG_EXT_2 >----------------*/,{ +}/*-------------------------< MSG_WEIRD1 >--------------------*/,{ SCR_CLR (SCR_ACK), 0, SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), PADDR (dispatch), - /* - ** get extended message code. - */ SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[2]), - SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)), - PADDRH (msg_wdtr), - /* - ** unknown extended message - */ + NADDR (scratch), SCR_JUMP, - PADDR (msg_bad) -}/*-------------------------< MSG_WDTR >-----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get data bus width - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[3]), - /* - ** let the host do the real work. - */ - SCR_INT, - SIR_NEGO_WIDE, + PADDRH (msg_weird1), +}/*-------------------------< WDTR_RESP >----------------*/,{ /* ** let the target fetch our answer. */ @@ -3257,39 +3488,7 @@ static struct scripth scripth0 __initdata = { SCR_JUMP, PADDRH (msg_out_done), -}/*-------------------------< MSG_EXT_3 >----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get extended message code. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[2]), - SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)), - PADDRH (msg_sdtr), - /* - ** unknown extended message - */ - SCR_JUMP, - PADDR (msg_bad) - -}/*-------------------------< MSG_SDTR >-----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get period and offset - */ - SCR_MOVE_ABS (2) ^ SCR_MSG_IN, - NADDR (msgin[3]), - /* - ** let the host do the real work. - */ - SCR_INT, - SIR_NEGO_SYNC, +}/*-------------------------< SDTR_RESP >-------------*/,{ /* ** let the target fetch our answer. */ @@ -3372,7 +3571,9 @@ static struct scripth scripth0 __initdata = { ** or in the wrong direction. ** Remember that in extended error. */ - SCR_LOAD_REG (scratcha, XE_EXTRA_DATA), + SCR_LOAD_REL (scratcha, 1), + offsetof (struct ccb, xerr_status), + SCR_REG_REG (scratcha, SCR_OR, XE_EXTRA_DATA), 0, SCR_STORE_REL (scratcha, 1), offsetof (struct ccb, xerr_status), @@ -3388,57 +3589,28 @@ static struct scripth scripth0 __initdata = { SCR_MOVE_ABS (1) ^ SCR_DATA_IN, NADDR (scratch), /* + ** Count this byte. + ** This will allow to return a positive + ** residual to user. + */ + SCR_LOAD_REL (scratcha, 4), + offsetof (struct ccb, phys.extra_bytes), + SCR_REG_REG (scratcha, SCR_ADD, 0x01), + 0, + SCR_REG_REG (scratcha1, SCR_ADDC, 0), + 0, + SCR_REG_REG (scratcha2, SCR_ADDC, 0), + 0, + SCR_STORE_REL (scratcha, 4), + offsetof (struct ccb, phys.extra_bytes), + /* ** .. and repeat as required. */ SCR_CALL, - PADDR (databreak), + PADDR (dispatch), SCR_JUMP, PADDRH (no_data), -#if MAX_SCATTERH != 0 - -}/*-------------------------< HDATA_IN >-------------------*/,{ -/* -** Because the size depends on the -** #define MAX_SCATTERH parameter, -** it is filled in at runtime. -** -** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== -** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), -** || PADDR (databreak), -** || SCR_MOVE_TBL ^ SCR_DATA_IN, -** || offsetof (struct dsb, data[ i]), -** ##=================================================== -** -**--------------------------------------------------------- -*/ -0 -}/*-------------------------< HDATA_IN2 >------------------*/,{ - SCR_JUMP, - PADDR (data_in), - -}/*-------------------------< HDATA_OUT >-------------------*/,{ -/* -** Because the size depends on the -** #define MAX_SCATTERH parameter, -** it is filled in at runtime. -** -** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >== -** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), -** || PADDR (databreak), -** || SCR_MOVE_TBL ^ SCR_DATA_OUT, -** || offsetof (struct dsb, data[ i]), -** ##=================================================== -** -**--------------------------------------------------------- -*/ -0 -}/*-------------------------< HDATA_OUT2 >------------------*/,{ - SCR_JUMP, - PADDR (data_out), - -#endif /* MAX_SCATTERH */ - }/*-------------------------< ABORT_RESEL >----------------*/,{ SCR_SET (SCR_ATN), 0, @@ -3482,10 +3654,10 @@ static struct scripth scripth0 __initdata = { SCR_JUMP, PADDR (select2), }/*-------------------------< SDATA_IN >-------------------*/,{ - SCR_MOVE_TBL ^ SCR_DATA_IN, + SCR_CHMOV_TBL ^ SCR_DATA_IN, offsetof (struct dsb, sense), SCR_CALL, - PADDR (databreak), + PADDR (dispatch), SCR_JUMP, PADDRH (no_data), @@ -3588,7 +3760,7 @@ static struct scripth scripth0 __initdata = { ** call the C code. */ SCR_LOAD_ABS (scratcha, 4), - PADDR (startpos), + PADDRH (startpos), SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)), SIR_BAD_STATUS, SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)), @@ -3690,6 +3862,17 @@ static struct scripth scripth0 __initdata = { SCR_JUMP ^ IFTRUE (MASK (HF_ACT_PM, HF_ACT_PM)), PADDRH (pm1_save), }/*-------------------------< PM0_SAVE >-------------------*/,{ + SCR_STORE_REL (ia, 4), + offsetof(struct ccb, phys.pm0.ret), + /* + ** If WSR bit is set, either UA and RBC may + ** have to be changed whatever the device wants + ** to ignore this residue ot not. + */ + SCR_FROM_REG (scntl2), + 0, + SCR_CALL ^ IFTRUE (MASK (WSR, WSR)), + PADDRH (swide_scr_64), /* ** Save the remaining byte count, the updated ** address and the return address. @@ -3698,16 +3881,25 @@ static struct scripth scripth0 __initdata = { offsetof(struct ccb, phys.pm0.sg.size), SCR_STORE_REL (ua, 4), offsetof(struct ccb, phys.pm0.sg.addr), - SCR_STORE_REL (ia, 4), - offsetof(struct ccb, phys.pm0.ret), /* ** Set the current pointer at the PM0 DATA mini-script. */ SCR_LOAD_ABS (temp, 4), PADDRH (pm0_data_addr), SCR_JUMP, - PADDR (databreak), + PADDR (dispatch), }/*-------------------------< PM1_SAVE >-------------------*/,{ + SCR_STORE_REL (ia, 4), + offsetof(struct ccb, phys.pm1.ret), + /* + ** If WSR bit is set, either UA and RBC may + ** have been changed whatever the device wants + ** to ignore this residue or not. + */ + SCR_FROM_REG (scntl2), + 0, + SCR_CALL ^ IFTRUE (MASK (WSR, WSR)), + PADDRH (swide_scr_64), /* ** Save the remaining byte count, the updated ** address and the return address. @@ -3716,19 +3908,237 @@ static struct scripth scripth0 __initdata = { offsetof(struct ccb, phys.pm1.sg.size), SCR_STORE_REL (ua, 4), offsetof(struct ccb, phys.pm1.sg.addr), - SCR_STORE_REL (ia, 4), - offsetof(struct ccb, phys.pm1.ret), /* ** Set the current pointer at the PM1 DATA mini-script. */ SCR_LOAD_ABS (temp, 4), PADDRH (pm1_data_addr), SCR_JUMP, - PADDR (databreak), + PADDR (dispatch), + +}/*--------------------------< SWIDE_MA_32 >-----------------------*/,{ + /* + ** Handling of the SWIDE for 32 bit chips. + ** + ** We jump here from the C code with SCRATCHA + ** containing the address to write the SWIDE. + ** - Save 32 bit address in <scratch>. + */ + SCR_STORE_ABS (scratcha, 4), + PADDRH (scratch), + SCR_JUMP, + PADDRH (swide_common), +}/*--------------------------< SWIDE_MA_64 >-----------------------*/,{ + /* + ** Handling of the SWIDE for 64 bit chips when the + ** hardware handling of phase mismatch is disabled. + ** + ** We jump here from the C code with SCRATCHA + ** containing the address to write the SWIDE and + ** SBR containing bit 32..39 of this address. + ** - Save 32 bit address in <scratch>. + ** - Move address bit 32..39 to SFBR. + */ + SCR_STORE_ABS (scratcha, 4), + PADDRH (scratch), + SCR_FROM_REG (sbr), + 0, + SCR_JUMP, + PADDRH (swide_com_64), +}/*--------------------------< SWIDE_SCR_64 >-----------------------*/,{ + /* + ** Handling of the SWIDE for 64 bit chips when + ** hardware phase mismatch is enabled. + ** We are entered with a SCR_CALL from PMO_SAVE + ** and PM1_SAVE sub-scripts. + ** + ** Snoop the SCSI BUS in case of the device + ** willing to ignore this residue. + ** If it does, we must only increment the RBC, + ** since this register does reflect all bytes + ** received from the SCSI BUS including the SWIDE. + */ + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDRH (swide_scr_64_1), + SCR_FROM_REG (sbdl), + 0, + SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)), + PADDRH (swide_scr_64_1), + SCR_REG_REG (rbc, SCR_ADD, 1), + 0, + SCR_REG_REG (rbc1, SCR_ADDC, 0), + 0, + SCR_REG_REG (rbc2, SCR_ADDC, 0), + 0, + /* + ** Save UA and RBC, since the PM0/1_SAVE + ** sub-scripts haven't moved them to the + ** context yet and the below MOV may just + ** change their value. + */ + SCR_STORE_ABS (ua, 4), + PADDRH (scratch), + SCR_STORE_ABS (rbc, 4), + PADDRH (scratch1), + /* + ** Throw away the IGNORE WIDE RESIDUE message. + ** since we just did take care of it. + */ + SCR_MOVE_ABS (2) ^ SCR_MSG_IN, + NADDR (scratch), + SCR_CLR (SCR_ACK), + 0, + /* + ** Restore UA and RBC registers and return. + */ + SCR_LOAD_ABS (ua, 4), + PADDRH (scratch), + SCR_LOAD_ABS (rbc, 4), + PADDRH (scratch1), + SCR_RETURN, + 0, +}/*--------------------------< SWIDE_SCR_64_1 >---------------------*/,{ + /* + ** We must grab the SWIDE and move it to + ** memory. + ** + ** - Save UA (32 bit address) in <scratch>. + ** - Move address bit 32..39 to SFBR. + ** - Increment UA (updated address). + */ + SCR_STORE_ABS (ua, 4), + PADDRH (scratch), + SCR_FROM_REG (rbc3), + 0, + SCR_REG_REG (ua, SCR_ADD, 1), + 0, + SCR_REG_REG (ua1, SCR_ADDC, 0), + 0, + SCR_REG_REG (ua2, SCR_ADDC, 0), + 0, + SCR_REG_REG (ua3, SCR_ADDC, 0), + 0, +}/*--------------------------< SWIDE_COM_64 >-----------------------*/,{ + /* + ** - Save DRS. + ** - Load DRS with address bit 32..39 of the + ** location to write the SWIDE. + ** SFBR has been loaded with these bits. + ** (Look above). + */ + SCR_STORE_ABS (drs, 4), + PADDRH (saved_drs), + SCR_LOAD_ABS (drs, 4), + PADDRH (zero), + SCR_TO_REG (drs), + 0, +}/*--------------------------< SWIDE_COMMON >-----------------------*/,{ + /* + ** - Save current DSA + ** - Load DSA with bit 0..31 of the memory + ** location to write the SWIDE. + */ + SCR_STORE_ABS (dsa, 4), + PADDRH (saved_dsa), + SCR_LOAD_ABS (dsa, 4), + PADDRH (scratch), + /* + ** Move the SWIDE to memory. + ** Clear the WSR bit. + */ + SCR_STORE_REL (swide, 1), + 0, + SCR_REG_REG (scntl2, SCR_OR, WSR), + 0, + /* + ** Restore the original DSA. + */ + SCR_LOAD_ABS (dsa, 4), + PADDRH (saved_dsa), +}/*--------------------------< SWIDE_FIN_32 >-----------------------*/,{ + /* + ** For 32 bit chip, the following SCRIPTS + ** instruction is patched with a JUMP to dispatcher. + ** (Look into the C code). + */ + SCR_LOAD_ABS (drs, 4), + PADDRH (saved_drs), + /* + ** 64 bit chip only. + ** If PM handling from SCRIPTS, we are just + ** a helper for the C code, so jump to + ** dispatcher now. + */ + SCR_FROM_REG (ccntl0), + 0, + SCR_JUMP ^ IFFALSE (MASK (ENPMJ, ENPMJ)), + PADDR (dispatch), + /* + ** 64 bit chip with hardware PM handling enabled. + ** + ** Since we are paranoid:), we donnot want + ** a SWIDE followed by a CHMOV(1) to lead to + ** a CHMOV(0) in our PM context. + ** We check against such a condition. + ** Also does the C code. + */ + SCR_FROM_REG (rbc), + 0, + SCR_RETURN ^ IFFALSE (DATA (0)), + 0, + SCR_FROM_REG (rbc1), + 0, + SCR_RETURN ^ IFFALSE (DATA (0)), + 0, + SCR_FROM_REG (rbc2), + 0, + SCR_RETURN ^ IFFALSE (DATA (0)), + 0, + /* + ** If we are there, RBC(0..23) is zero, + ** and we just have to load the current + ** DATA SCRIPTS address (register TEMP) + ** with the IA and go to dispatch. + ** No PM context is needed. + */ + SCR_STORE_ABS (ia, 4), + PADDRH (scratch), + SCR_LOAD_ABS (temp, 4), + PADDRH (scratch), + SCR_JUMP, + PADDR (dispatch), + +}/*-------------------------< ZERO >------------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< SCRATCH >---------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< SCRATCH1 >--------------------*/,{ + SCR_DATA_ZERO, }/*-------------------------< PM0_DATA_ADDR >---------------*/,{ SCR_DATA_ZERO, }/*-------------------------< PM1_DATA_ADDR >---------------*/,{ SCR_DATA_ZERO, +}/*-------------------------< SAVED_DSA >-------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< SAVED_DRS >-------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< DONE_POS >--------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< STARTPOS >--------------------*/,{ + SCR_DATA_ZERO, +}/*-------------------------< TARGTBL >---------------------*/,{ + SCR_DATA_ZERO, + + +/* +** We may use MEMORY MOVE instructions to load the on chip-RAM, +** if it happens that mapping PCI memory is not possible. +** But writing the RAM from the CPU is the preferred method, +** since PCI 2.2 seems to disallow PCI self-mastering. +*/ + +#ifdef SCSI_NCR_PCI_MEM_NOT_SUPPORTED + }/*-------------------------< START_RAM >-------------------*/,{ /* ** Load the script into on-chip RAM, @@ -3743,7 +4153,7 @@ static struct scripth scripth0 __initdata = { }/*-------------------------< START_RAM64 >--------------------*/,{ /* - ** Load the RAM and start for 64 bit PCI (896). + ** Load the RAM and start for 64 bit PCI (895A,896). ** Both scripts (script and scripth) are loaded into ** the RAM which is 8K (4K for 825A/875/895). ** We also need to load some 32-63 bit segments @@ -3768,6 +4178,9 @@ static struct scripth scripth0 __initdata = { PADDRH (start64), }/*-------------------------< RAM_SEG64 >--------------------*/,{ 0, + +#endif /* SCSI_NCR_PCI_MEM_NOT_SUPPORTED */ + }/*-------------------------< SNOOPTEST >-------------------*/,{ /* ** Read the variable. @@ -3801,50 +4214,17 @@ void __init ncr_script_fill (struct script * scr, struct scripth * scrh) int i; ncrcmd *p; -#if MAX_SCATTERH != 0 - p = scrh->hdata_in; - for (i=0; i<MAX_SCATTERH; i++) { -#if SCR_SG_SIZE == 4 - *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); - *p++ =PADDR (databreak); -#endif - *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; - *p++ =offsetof (struct dsb, data[i]); - }; - assert ((u_long)p == (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in)); -#endif - p = scr->data_in; - for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { -#if SCR_SG_SIZE == 4 - *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)); - *p++ =PADDR (databreak); -#endif - *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; + for (i=0; i<MAX_SCATTER; i++) { + *p++ =SCR_CHMOV_TBL ^ SCR_DATA_IN; *p++ =offsetof (struct dsb, data[i]); }; - assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in)); -#if MAX_SCATTERH != 0 - p = scrh->hdata_out; - for (i=0; i<MAX_SCATTERH; i++) { -#if SCR_SG_SIZE == 4 - *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); - *p++ =PADDR (databreak); -#endif - *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; - *p++ =offsetof (struct dsb, data[i]); - }; - assert ((u_long)p==(u_long)&scrh->hdata_out + sizeof (scrh->hdata_out)); -#endif + assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in)); p = scr->data_out; - for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) { -#if SCR_SG_SIZE == 4 - *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)); - *p++ =PADDR (databreak); -#endif - *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; + for (i=0; i<MAX_SCATTER; i++) { + *p++ =SCR_CHMOV_TBL ^ SCR_DATA_OUT; *p++ =offsetof (struct dsb, data[i]); }; @@ -3860,7 +4240,8 @@ void __init ncr_script_fill (struct script * scr, struct scripth * scrh) **========================================================== */ -static void __init ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,int len) +static void __init +ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,int len) { ncrcmd opcode, new, old, tmp1, tmp2; ncrcmd *start, *end; @@ -3950,11 +4331,22 @@ static void __init ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,in case 0x0: /* - ** MOVE (absolute address) + ** MOVE/CHMOV (absolute address) */ + if (!(np->features & FE_WIDE)) + dst[-1] = cpu_to_scr(opcode | OPC_MOVE); relocs = 1; break; + case 0x1: + /* + ** MOVE/CHMOV (table indirect) + */ + if (!(np->features & FE_WIDE)) + dst[-1] = cpu_to_scr(opcode | OPC_MOVE); + relocs = 0; + break; + case 0x8: /* ** JUMP / CALL @@ -4016,6 +4408,7 @@ static void __init ncr_script_copy_and_bind (ncb_p np,ncrcmd *src,ncrcmd *dst,in } /* fall through */ default: + new = 0; /* For 'cc' not to complain */ panic("ncr_script_copy_and_bind: " "weird relocation %x\n", old); break; @@ -4083,9 +4476,16 @@ static u_long div_10M[] = ** Prepare io register values used by ncr_init() according ** to selected and supported features. ** -** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 -** transfers. 32,64,128 are only supported by 825A, 875, 895 -** and 896 chips. +** NCR/SYMBIOS chips allow burst lengths of 2, 4, 8, 16, 32, 64, +** 128 transfers. All chips support at least 16 transfers bursts. +** The 825A, 875 and 895 chips support bursts of up to 128 +** transfers and the 895A and 896 support bursts of up to 64 +** transfers. All other chips support up to 16 transfers bursts. +** +** For PCI 32 bit data transfers each transfer is a DWORD (4 bytes). +** It is a QUADWORD (8 bytes) for PCI 64 bit data transfers. +** Only the 896 is able to perform 64 bit data transfers. +** ** We use log base 2 (burst length) as internal code, with ** value 0 meaning "burst disabled". ** @@ -4128,8 +4528,8 @@ static inline void ncr_init_burst(ncb_p np, u_char bc) ** Get target set-up from Symbios format NVRAM. */ -static void __init - ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) +static void __init +ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) { tcb_p tp = &np->target[target]; Symbios_target *tn = &nvram->target[target]; @@ -4137,7 +4537,7 @@ static void __init tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255; tp->usrwide = tn->bus_width == 0x10 ? 1 : 0; tp->usrtags = - (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0; + (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? MAX_TAGS : 0; if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE)) tp->usrflag |= UF_NODISC; @@ -4150,7 +4550,7 @@ static void __init */ static void __init - ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram) +ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram) { tcb_p tp = &np->target[target]; struct Tekram_target *tn = &nvram->target[target]; @@ -4261,7 +4661,7 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) np->maxsync = period > 2540 ? 254 : period / 10; /* - ** 64 bit (53C896) ? + ** 64 bit (53C895A or 53C896) ? */ if (np->features & FE_64BIT) #if BITS_PER_LONG > 32 @@ -4271,8 +4671,8 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) #endif /* - ** Phase mismatch handled by SCRIPTS (53C896) ? - */ + ** Phase mismatch handled by SCRIPTS (53C895A or 53C896) ? + */ if (np->features & FE_NOPM) np->rv_ccntl0 |= (ENPMJ); @@ -4383,28 +4783,50 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) ncr_init_burst(np, burst_max); /* - ** Set differential mode and LED support. - ** Ignore these features for boards known to use a - ** specific GPIO wiring (Tekram only for now) and - ** for the 896 that drives the LED directly. - ** Probe initial setting of GPREG and GPCNTL for - ** other ones. - */ - if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) { + ** Set SCSI BUS mode. + ** + ** - ULTRA2 chips (895/895A/896) report the current + ** BUS mode through the STEST4 IO register. + ** - For previous generation chips (825/825A/875), + ** user has to tell us how to check against HVD, + ** since a 100% safe algorithm is not possible. + */ + np->scsi_mode = SMODE_SE; + if (np->features & FE_ULTRA2) + np->scsi_mode = (np->sv_stest4 & SMODE); + else if (np->features & FE_DIFF) { switch(driver_setup.diff_support) { - case 3: + case 4: /* Trust previous settings if present, then GPIO3 */ + if (np->sv_scntl3) { + if (np->sv_stest2 & 0x20) + np->scsi_mode = SMODE_HVD; + break; + } + case 3: /* SYMBIOS controllers report HVD through GPIO3 */ + if (nvram && nvram->type != SCSI_NCR_SYMBIOS_NVRAM) + break; if (INB(nc_gpreg) & 0x08) + break; + case 2: /* Set HVD unconditionally */ + np->scsi_mode = SMODE_HVD; + case 1: /* Trust previous settings for HVD */ + if (np->sv_stest2 & 0x20) + np->scsi_mode = SMODE_HVD; break; - case 2: - np->rv_stest2 |= 0x20; - break; - case 1: - np->rv_stest2 |= (np->sv_stest2 & 0x20); - break; - default: + default:/* Don't care about HVD */ break; } } + if (np->scsi_mode == SMODE_HVD) + np->rv_stest2 |= 0x20; + + /* + ** Set LED support from SCRIPTS. + ** Ignore this feature for boards known to use a + ** specific GPIO wiring and for the 895A or 896 + ** that drive the LED directly. + ** Also probe initial setting of GPIO0 as output. + */ if ((driver_setup.led_pin || (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) && !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01)) @@ -4457,7 +4879,7 @@ static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) #endif tp->usrsync = driver_setup.default_sync; tp->usrwide = driver_setup.max_wide; - tp->usrtags = SCSI_NCR_MAX_TAGS; + tp->usrtags = MAX_TAGS; if (!driver_setup.disconnection) np->target[i].usrflag = UF_NODISC; } @@ -4587,7 +5009,8 @@ void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram) ** start the timer daemon. */ -static int __init ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) +static int __init +ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) { struct host_data *host_data; ncb_p np = 0; @@ -4758,6 +5181,19 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n (void) ncr_prepare_setting(np, nvram); /* + ** Check the PCI clock frequency. + ** Must be done after prepare_setting since it + ** destroys STEST1 that is used to probe for + ** the clock doubler. + */ + i = (int) ncr_getpciclock(np); + if (i > 37000) { + printk(KERN_ERR "%s: PCI clock seems too high (%u KHz).\n", + ncr_name(np), i); + goto attach_error; + } + + /* ** Patch script to physical addresses */ ncr_script_fill (&script0, &scripth0); @@ -4769,17 +5205,36 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n if (np->base2_ba) { np->p_script = pcivtobus(np->base2_ba); if (np->features & FE_RAM8K) { + np->base2_ws = 8192; np->p_scripth = np->p_script + 4096; #if BITS_PER_LONG > 32 np->scr_ram_seg = cpu_to_scr(np->base2_ba >> 32); #endif } + else + np->base2_ws = 4096; +#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED + np->base2_va = remap_pci_mem(np->base2_ba, np->base2_ws); + if (!np->base2_va) { + printk(KERN_ERR "%s: can't map PCI MEMORY region\n", + ncr_name(np)); + goto attach_error; + } +#endif } 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)); /* + ** If not 64 bit chip, patch some places in SCRIPTS. + */ + if (!(np->features & FE_64BIT)) { + np->scripth0->swide_fin_32[0] = cpu_to_scr(SCR_JUMP); + np->scripth0->swide_fin_32[1] = + cpu_to_scr(NCB_SCRIPT_PHYS(np, dispatch)); + } + /* ** Patch some variables in SCRIPTS */ np->scripth0->pm0_data_addr[0] = @@ -4787,11 +5242,12 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n np->scripth0->pm1_data_addr[0] = cpu_to_scr(NCB_SCRIPT_PHYS(np, pm1_data)); +#ifdef SCSI_NCR_PCI_MEM_NOT_SUPPORTED np->scripth0->script0_ba[0] = cpu_to_scr(vtobus(np->script0)); np->scripth0->script0_ba64[0] = cpu_to_scr(vtobus(np->script0)); np->scripth0->scripth0_ba64[0] = cpu_to_scr(vtobus(np->scripth0)); np->scripth0->ram_seg64[0] = np->scr_ram_seg; - +#endif /* ** Prepare the idle and invalid task actions. */ @@ -4827,10 +5283,11 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n /* ** Prepare the target bus address array. */ - np->script0->targtbl[0] = cpu_to_scr(vtobus(np->targtbl)); + np->scripth0->targtbl[0] = cpu_to_scr(vtobus(np->targtbl)); for (i = 0 ; i < MAX_TARGET ; i++) { np->targtbl[i] = cpu_to_scr(vtobus(&np->target[i])); np->target[i].b_luntbl = cpu_to_scr(vtobus(np->badluntbl)); + np->target[i].b_lun0 = cpu_to_scr(vtobus(&np->resel_badlun)); } /* @@ -4846,6 +5303,23 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe)); } +#ifdef SCSI_NCR_IARB_SUPPORT + /* + ** If user does not want to use IMMEDIATE ARBITRATION + ** when we are reselected while attempting to arbitrate, + ** patch the SCRIPTS accordingly with a SCRIPT NO_OP. + */ + if (!(driver_setup.iarb & 1)) + np->script0->ungetjob[0] = cpu_to_scr(SCR_NO_OP); + /* + ** If user wants IARB to be set when we win arbitration + ** and have other jobs, compute the max number of consecutive + ** settings of IARB hint before we leave devices a chance to + ** arbitrate for reselection. + */ + np->iarb_max = (driver_setup.iarb >> 4); +#endif + /* ** DEL 472 - 53C896 Rev 1 - Part Number 609-0393055 - ITEM 5. */ @@ -4890,14 +5364,20 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n /* ** Install the interrupt handler. + ** If we synchonize the C code with SCRIPTS on interrupt, + ** we donnot want to share the INTR line at all. */ if (request_irq(device->slot.irq, sym53c8xx_intr, +#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR + ((driver_setup.irqm & 0x20) ? 0 : SA_INTERRUPT), +#else ((driver_setup.irqm & 0x10) ? 0 : SA_SHIRQ) | #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0) ((driver_setup.irqm & 0x20) ? 0 : SA_INTERRUPT), #else 0, #endif +#endif NAME53C8XX, np)) { printk(KERN_ERR "%s: request irq %d failure\n", ncr_name(np), device->slot.irq); @@ -4957,8 +5437,9 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n ** and return success. */ instance->max_channel = 0; + instance->this_id = np->myaddr; instance->max_id = np->maxwide ? 16 : 8; - instance->max_lun = SCSI_NCR_MAX_LUN; + instance->max_lun = MAX_LUN; #ifndef NCR_IOMAPPED instance->base = (char *) np->reg; #endif @@ -4967,6 +5448,9 @@ printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n instance->io_port = np->base_io; instance->n_io_port = np->base_ws; instance->dma_channel = 0; + instance->cmd_per_lun = MAX_TAGS; + instance->can_queue = (MAX_START-4); + instance->select_queue_depths = sym53c8xx_select_queue_depths; NCR_UNLOCK_NCB(np, flags); @@ -5002,9 +5486,11 @@ static void ncr_free_resources(ncb_p np) free_irq(np->irq, np); if (np->base_io) release_region(np->base_io, np->base_ws); -#ifndef NCR_IOMAPPED +#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED if (np->base_va) unmap_pci_mem(np->base_va, np->base_ws); + if (np->base2_va) + unmap_pci_mem(np->base2_va, np->base2_ws); #endif if (np->scripth0) m_free(np->scripth0, sizeof(struct scripth), "SCRIPTH"); @@ -5026,13 +5512,19 @@ static void ncr_free_resources(ncb_p np) for (target = 0; target < MAX_TARGET ; target++) { tp = &np->target[target]; for (lun = 0 ; lun < MAX_LUN ; lun++) { - lp = tp->lp[lun]; + lp = ncr_lp(np, tp, lun); if (!lp) continue; if (lp->tasktbl != &lp->tasktbl_0) - m_free(lp->tasktbl, 256, "TASKTBL"); + m_free(lp->tasktbl, MAX_TASKS*4, "TASKTBL"); + if (lp->cb_tags) + m_free(lp->cb_tags, MAX_TAGS, "CB_TAGS"); m_free(lp, sizeof(*lp), "LCB"); } +#if MAX_LUN > 1 + if (tp->lmp) + m_free(tp->lmp, MAX_LUN * sizeof(lcb_p), "LMP"); +#endif } m_free(np, sizeof(*np), "NCB"); @@ -5074,6 +5566,81 @@ static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd) } } +/*========================================================== +** +** +** Prepare the next negotiation message if needed. +** +** Fill in the part of message buffer that contains the +** negotiation and the nego_status field of the CCB. +** Returns the size of the message in bytes. +** +** +**========================================================== +*/ + +static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr) +{ + tcb_p tp = &np->target[cp->target]; + int msglen = 0; + int nego = 0; + + if (tp->inq_done) { + + /* + ** negotiate wide transfers ? + */ + + if (!tp->widedone) { + if (tp->inq_byte7 & INQ7_WIDE16) { + nego = NS_WIDE; + } else + tp->widedone=1; + }; + + /* + ** negotiate synchronous transfers? + */ + + if (!nego && !tp->period) { + if (tp->inq_byte7 & INQ7_SYNC) { + nego = NS_SYNC; + } else { + tp->period =0xffff; + PRINT_TARGET(np, cp->target); + printk ("target did not report SYNC.\n"); + }; + }; + }; + + switch (nego) { + case NS_SYNC: + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 3; + msgptr[msglen++] = M_X_SYNC_REQ; + msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0; + msgptr[msglen++] = tp->maxoffs; + break; + case NS_WIDE: + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 2; + msgptr[msglen++] = M_X_WIDE_REQ; + msgptr[msglen++] = tp->usrwide; + break; + }; + + cp->nego_status = nego; + + if (nego) { + tp->nego_cp = cp; + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, nego == NS_WIDE ? + "wide msgout":"sync_msgout", msgptr); + }; + }; + + return msglen; +} /*========================================================== ** @@ -5088,12 +5655,12 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) { /* Scsi_Device *device = cmd->device; */ tcb_p tp = &np->target[cmd->target]; - lcb_p lp = tp->lp[cmd->lun]; + lcb_p lp = ncr_lp(np, tp, cmd->lun); ccb_p cp; int segments; - u_char nego, idmsg, *msgptr; - u_int msglen; + u_char idmsg, *msgptr; + u_int msglen; int direction; u_int32 lastp, goalp; @@ -5138,9 +5705,10 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** **---------------------------------------------------- */ - if (np->settle_time && cmd->timeout_per_command >= HZ && - np->settle_time > jiffies + cmd->timeout_per_command - HZ) { - np->settle_time = jiffies + cmd->timeout_per_command - HZ; + if (np->settle_time && cmd->timeout_per_command >= HZ) { + u_long tlimit = ktime_get(cmd->timeout_per_command - HZ); + if (ktime_dif(np->settle_time, tlimit) > 0) + np->settle_time = tlimit; } if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) { @@ -5166,52 +5734,6 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) cp->phys.num_disc = 0; #endif - /*--------------------------------------------------- - ** - ** negotiation required? - ** - **--------------------------------------------------- - */ - - nego = 0; - - if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) { - - /* - ** negotiate wide transfers ? - */ - - if (!tp->widedone) { - if (tp->inq_byte7 & INQ7_WIDE16) { - nego = NS_WIDE; - } else - tp->widedone=1; - }; - - /* - ** negotiate synchronous transfers? - */ - - if (!nego && !tp->period) { - if (tp->inq_byte7 & INQ7_SYNC) { - nego = NS_SYNC; - } else { - tp->period =0xffff; - PRINT_TARGET(np, cp->target); - printk ("target did not report SYNC.\n"); - }; - }; - - /* - ** remember nego is pending for the target. - ** Avoid to start a nego for all queued commands - ** when tagged command queuing is enabled. - */ - - if (nego) - tp->nego_cp = cp; - }; - /*---------------------------------------------------- ** ** Build the identify / tag / sdtr message @@ -5235,16 +5757,16 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** Force ordered tag if necessary to avoid timeouts ** and to preserve interactivity. */ - if (lp && lp->tags_stime + (3*HZ) <= jiffies) { - if (lp->tags_smap) { + if (lp && ktime_exp(lp->tags_stime)) { + lp->tags_si = !(lp->tags_si); + if (lp->tags_sum[lp->tags_si]) { order = M_ORDERED_TAG; - if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ + if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>0){ PRINT_ADDR(cmd); printk("ordered tag forced.\n"); } } - lp->tags_stime = jiffies; - lp->tags_smap = lp->tags_umap; + lp->tags_stime = ktime_get(3*HZ); } if (order == 0) { @@ -5263,41 +5785,19 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) } msgptr[msglen++] = order; /* - ** Actual tags are numbered 1,3,5,..2*MAXTAGS+1, - ** since we may have to deal with devices that have - ** problems with #TAG 0 or too great #TAG numbers. + ** For less than 128 tags, actual tags are numbered + ** 1,3,5,..2*MAXTAGS+1,since we may have to deal + ** with devices that have problems with #TAG 0 or too + ** great #TAG numbers. For more tags (up to 256), + ** we use directly our tag number. */ +#if MAX_TASKS > (512/4) + msgptr[msglen++] = cp->tag; +#else msgptr[msglen++] = (cp->tag << 1) + 1; +#endif } - switch (nego) { - case NS_SYNC: - msgptr[msglen++] = M_EXTENDED; - msgptr[msglen++] = 3; - msgptr[msglen++] = M_X_SYNC_REQ; - msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0; - msgptr[msglen++] = tp->maxoffs; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("sync msgout: "); - ncr_show_msg (&cp->scsi_smsg [msglen-5]); - printk (".\n"); - }; - break; - case NS_WIDE: - msgptr[msglen++] = M_EXTENDED; - msgptr[msglen++] = 2; - msgptr[msglen++] = M_X_WIDE_REQ; - msgptr[msglen++] = tp->usrwide; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("wide msgout: "); - ncr_show_msg (&cp->scsi_smsg [msglen-4]); - printk (".\n"); - }; - break; - }; - cp->host_flags = 0; /*---------------------------------------------------- @@ -5307,7 +5807,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) **---------------------------------------------------- */ - segments = np->scatter (cp, cp->cmd); + cp->segments = segments = np->scatter (cp, cp->cmd); if (segments < 0) { ncr_free_ccb(np, cp); @@ -5357,16 +5857,8 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) */ if (direction & XFER_OUT) { goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8; -#if MAX_SCATTERH != 0 - if (segments <= MAX_SCATTERL) - lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4)); - else { - lastp = NCB_SCRIPTH_PHYS (np, hdata_out2); - lastp -= (segments - MAX_SCATTERL) * (SCR_SG_SIZE*4); - } -#else lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4)); -#endif + /* ** If actual data direction is unknown, save pointers ** in header. The SCRIPTS will swap them to current @@ -5383,16 +5875,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) */ if (direction & XFER_IN) { goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8; -#if MAX_SCATTERH != 0 - if (segments <= MAX_SCATTERL) - lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4)); - else { - lastp = NCB_SCRIPTH_PHYS (np, hdata_in2); - lastp -= (segments - MAX_SCATTERL) * (SCR_SG_SIZE*4); - } -#else lastp = goalp - 8 - (segments * (SCR_SG_SIZE*4)); -#endif } /* @@ -5411,8 +5894,26 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) /* ** Save the initial data pointer in order to be able ** to redo the command. + ** We also have to save the initial lastp, since it + ** will be changed to DATA_IO if we don't know the data + ** direction and the device completes the command with + ** QUEUE FULL status (without entering the data phase). */ cp->startp = cp->phys.header.savep; + cp->lastp0 = cp->phys.header.lastp; + + /*--------------------------------------------------- + ** + ** negotiation required? + ** + ** (nego_status is filled by ncr_prepare_nego()) + ** + **--------------------------------------------------- + */ + + cp->nego_status = 0; + if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) + msglen += ncr_prepare_nego (np, cp, msgptr + msglen); /*---------------------------------------------------- ** @@ -5439,24 +5940,30 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) /* ** message */ - cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg)); - cp->phys.smsg.size = cpu_to_scr(msglen); + cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg)); + cp->phys.smsg.size = cpu_to_scr(msglen); /* ** command */ - cp->phys.cmd.addr = cpu_to_scr(vtobus (&cmd->cmnd[0])); - cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len); + cp->phys.cmd.addr = cpu_to_scr(vtobus (&cmd->cmnd[0])); + cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len); /* ** status */ - cp->actualquirks = tp->quirks; - cp->host_status = nego ? HS_NEGOTIATE : HS_BUSY; - cp->scsi_status = S_ILLEGAL; + cp->actualquirks = tp->quirks; + cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; + cp->scsi_status = S_ILLEGAL; + cp->xerr_status = 0; + cp->phys.extra_bytes = 0; - cp->xerr_status = XE_OK; - cp->nego_status = nego; + /* + ** extreme data pointer. + ** shall be positive, so -1 is lower than lowest.:) + */ + cp->ext_sg = -1; + cp->ext_ofs = 0; /*---------------------------------------------------- ** @@ -5469,17 +5976,10 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** activate this job. */ - /* Compute a time limit greater than the middle-level driver one */ - if (cmd->timeout_per_command > 0) - cp->tlimit = jiffies + cmd->timeout_per_command + HZ; - else - cp->tlimit = jiffies + 86400 * HZ;/* No timeout=24 hours */ - /* ** insert next CCBs into start queue. ** 2 max at a time is enough to flush the CCB wait queue. */ - cp->auto_sense = 0; if (lp) ncr_start_next_ccb(np, lp, 2); else @@ -5525,6 +6025,24 @@ static void ncr_put_start_queue(ncb_p np, ccb_p cp) { u_short qidx; +#ifdef SCSI_NCR_IARB_SUPPORT + /* + ** If the previously queued CCB is not yet done, + ** set the IARB hint. The SCRIPTS will go with IARB + ** for this job when starting the previous one. + ** We leave devices a chance to win arbitration by + ** not using more than 'iarb_max' consecutive + ** immediate arbitrations. + */ + if (np->last_cp && np->iarb_count < np->iarb_max) { + np->last_cp->host_flags |= HF_HINT_IARB; + ++np->iarb_count; + } + else + np->iarb_count = 0; + np->last_cp = cp; +#endif + /* ** insert into start queue. */ @@ -5546,7 +6064,7 @@ static void ncr_put_start_queue(ncb_p np, ccb_p cp) ** Wake it up. */ MEMORY_BARRIER(); - OUTB (nc_istat, SIGP); + OUTB (nc_istat, SIGP|np->istat_sem); } @@ -5593,7 +6111,6 @@ static void ncr_soft_reset(ncb_p np) ** ** ** Start reset process. -** If reset in progress do nothing. ** The interrupt handler will reinitialize the chip. ** The timeout handler will wait for settle_time before ** clearing it and so resuming command processing. @@ -5603,17 +6120,15 @@ static void ncr_soft_reset(ncb_p np) */ static void ncr_start_reset(ncb_p np) { - if (!np->settle_time) { - (void) ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay); - } - } + (void) ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay); +} static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay) { u_int32 term; int retv = 0; - np->settle_time = jiffies + settle_delay * HZ; + np->settle_time = ktime_get(settle_delay * HZ); if (bootverbose > 1) printk("%s: resetting, " @@ -5747,8 +6262,6 @@ static int ncr_abort_command (ncb_p np, Scsi_Cmnd *cmd) { /* Scsi_Device *device = cmd->device; */ ccb_p cp; - int found; - int retv; /* * First, look for the scsi command in the waiting list @@ -5762,58 +6275,41 @@ static int ncr_abort_command (ncb_p np, Scsi_Cmnd *cmd) /* * Then, look in the wakeup list */ - for (found=0, cp=np->ccbc; cp; cp=cp->link_ccb) { + for (cp=np->ccbc; cp; cp=cp->link_ccb) { /* ** look for the ccb of this command. */ if (cp->host_status == HS_IDLE) continue; - if (cp->cmd == cmd) { - found = 1; + if (cp->cmd == cmd) break; - } } - if (!found) { + if (!cp) { return SCSI_ABORT_NOT_RUNNING; } - if (np->settle_time) { - return SCSI_ABORT_SNOOZE; - } - /* - ** If the CCB is active, patch schedule jumps for the - ** script to abort the command. + ** Keep track we have to abort this job. */ + cp->to_abort = 1; - cp->tlimit = 0; - switch(cp->host_status) { - case HS_BUSY: - case HS_NEGOTIATE: - printk ("%s: abort ccb=%p (cancel)\n", ncr_name (np), cp); - cp->phys.header.go.start = - cpu_to_scr(NCB_SCRIPTH_PHYS (np, cancel)); - retv = SCSI_ABORT_PENDING; - break; - case HS_DISCONNECT: - cp->phys.header.go.restart = - cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q)); - retv = SCSI_ABORT_PENDING; - break; - default: - retv = SCSI_ABORT_NOT_RUNNING; - break; - - } + /* + ** Tell the SCRIPTS processor to stop + ** and synchronize with us. + */ + np->istat_sem = SEM; /* ** If there are no requests, the script ** processor will sleep on SEL_WAIT_RESEL. ** Let's wake it up, since it may have to work. */ - OUTB (nc_istat, SIGP); + OUTB (nc_istat, SIGP|SEM); - return retv; + /* + ** Tell user we are working for him. + */ + return SCSI_ABORT_PENDING; } /*========================================================== @@ -5917,7 +6413,7 @@ void ncr_complete (ncb_p np, ccb_p cp) cmd = cp->cmd; cp->cmd = NULL; tp = &np->target[cp->target]; - lp = tp->lp[cp->lun]; + lp = ncr_lp(np, tp, cp->lun); /* ** We donnot queue more than 1 ccb per target @@ -5928,41 +6424,47 @@ void ncr_complete (ncb_p np, ccb_p cp) if (cp == tp->nego_cp) tp->nego_cp = 0; +#ifdef SCSI_NCR_IARB_SUPPORT /* - ** If auto-sense performed, change scsi status. + ** We just complete the last queued CCB. + ** Clear this info that is no more relevant. */ - if (cp->auto_sense) { - cp->scsi_status = cp->auto_sense; - } + if (cp == np->last_cp) + np->last_cp = 0; +#endif /* - ** Check for parity errors. + ** If auto-sense performed, change scsi status, + ** Otherwise, compute the residual. */ - - if (cp->host_flags & HF_PAR_ERR) { - PRINT_ADDR(cmd); - printk ("unrecovered SCSI parity error.\n"); - if (cp->host_status==HS_COMPLETE) - cp->host_status = HS_FAIL; + if (cp->host_flags & HF_AUTO_SENSE) { + cp->scsi_status = cp->sv_scsi_status; + cp->xerr_status = cp->sv_xerr_status; + } + else { + cp->resid = 0; + if (cp->phys.header.lastp != cp->phys.header.goalp) + cp->resid = ncr_compute_residual(np, cp); } /* ** Check for extended errors. */ - if (cp->xerr_status != XE_OK) { - PRINT_ADDR(cmd); - switch (cp->xerr_status) { - case XE_EXTRA_DATA: + if (cp->xerr_status) { + if (cp->xerr_status & XE_PARITY_ERR) { + PRINT_ADDR(cp->cmd); + printk ("unrecovered SCSI parity error.\n"); + } + if (cp->xerr_status & XE_EXTRA_DATA) { + PRINT_ADDR(cp->cmd); printk ("extraneous data discarded.\n"); - break; - case XE_BAD_PHASE: + } + if (cp->xerr_status & XE_BAD_PHASE) { + PRINT_ADDR(cp->cmd); printk ("illegal scsi phase (4/5).\n"); - break; - default: - printk ("extended error %d.\n", cp->xerr_status); - break; } + if (cp->host_status==HS_COMPLETE) cp->host_status = HS_FAIL; } @@ -5971,10 +6473,13 @@ void ncr_complete (ncb_p np, ccb_p cp) ** Print out any error for debugging purpose. */ if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) { - if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD) { + if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD || + cp->resid) { PRINT_ADDR(cmd); - printk ("ERROR: cmd=%x host_status=%x scsi_status=%x\n", - cmd->cmnd[0], cp->host_status, cp->scsi_status); + printk ("ERROR: cmd=%x host_status=%x scsi_status=%x " + "data_len=%d residual=%d\n", + cmd->cmnd[0], cp->host_status, cp->scsi_status, + cp->data_len, -cp->resid); } } @@ -5992,13 +6497,6 @@ void ncr_complete (ncb_p np, ccb_p cp) SetScsiResult(cmd, DID_OK, cp->scsi_status); /* - ** @RESID@ - ** Could dig out the correct value for resid, - ** but it would be quite complicated. - */ - /* if (cp->phys.header.lastp != cp->phys.header.goalp) */ - - /* ** Allocate the lcb if not yet. */ if (!lp) @@ -6038,12 +6536,8 @@ void ncr_complete (ncb_p np, ccb_p cp) SetScsiResult(cmd, DID_OK, S_CHECK_COND); if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) { - u_char * p = (u_char*) & cmd->sense_buffer; - int i; PRINT_ADDR(cmd); - printk ("sense data:"); - for (i=0; i<14; i++) printk (" %x", *p++); - printk (".\n"); + ncr_printl_hex("sense data:", cmd->sense_buffer, 14); } } else if ((cp->host_status == HS_COMPLETE) @@ -6078,6 +6572,7 @@ void ncr_complete (ncb_p np, ccb_p cp) SetScsiResult(cmd, DID_ABORT, cp->scsi_status); } else { + int did_status; /* ** Other protocol messes @@ -6086,7 +6581,11 @@ void ncr_complete (ncb_p np, ccb_p cp) printk ("COMMAND FAILED (%x %x) @%p.\n", cp->host_status, cp->scsi_status, cp); - SetScsiResult(cmd, DID_ERROR, cp->scsi_status); + did_status = DID_ERROR; + if (cp->xerr_status & XE_PARITY_ERR) + did_status = DID_PARITY; + + SetScsiResult(cmd, did_status, cp->scsi_status); } /* @@ -6094,12 +6593,9 @@ void ncr_complete (ncb_p np, ccb_p cp) */ if (tp->usrflag & UF_TRACE) { - u_char * p; - int i; PRINT_ADDR(cmd); printk (" CMD:"); - p = (u_char*) &cmd->cmnd[0]; - for (i=0; i<cmd->cmd_len; i++) printk (" %x", *p++); + ncr_print_hex(cmd->cmnd, cmd->cmd_len); if (cp->host_status==HS_COMPLETE) { switch (cp->scsi_status) { @@ -6108,9 +6604,7 @@ void ncr_complete (ncb_p np, ccb_p cp) break; case S_CHECK_COND: printk (" SENSE:"); - p = (u_char*) &cmd->sense_buffer; - for (i=0; i<14; i++) - printk (" %x", *p++); + ncr_print_hex(cmd->sense_buffer, 14); break; default: printk (" STAT: %x\n", cp->scsi_status); @@ -6250,7 +6744,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) ** Start at first entry. */ np->squeueput = 0; - np->script0->startpos[0] = cpu_to_scr(phys); + np->scripth0->startpos[0] = cpu_to_scr(phys); /* ** Clear Done Queue @@ -6265,7 +6759,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) /* ** Start at first entry. */ - np->script0->done_pos[0] = cpu_to_scr(phys); + np->scripth0->done_pos[0] = cpu_to_scr(phys); np->dqueueget = 0; /* @@ -6313,7 +6807,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) np->rv_ccntl0 |= DPR; /* - ** If 64 bit (53C896) enable 40 bit address table + ** If 64 bit (53C895A or 53C896) enable 40 bit address table ** indirect addressing for MOVE. */ @@ -6322,7 +6816,7 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) } /* - ** If phase mismatch handled by scripts (53C896), + ** If phase mismatch handled by scripts (53C895A or 53C896), ** set PM jump addresses. */ @@ -6370,6 +6864,8 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) for (i=0;i<MAX_TARGET;i++) { tcb_p tp = &np->target[i]; + tp->to_reset = 0; + tp->sval = 0; tp->wval = np->rv_scntl3; @@ -6390,21 +6886,41 @@ void ncr_init (ncb_p np, int reset, char * msg, u_long code) } /* - ** Start script processor. + ** Download SCSI SCRIPTS to on-chip RAM if present, + ** and start script processor. + ** We do the download preferently from the CPU. + ** For platforms that may not support PCI memory mapping, + ** we use a simple SCRIPTS that performs MEMORY MOVEs. */ MEMORY_BARRIER(); if (np->base2_ba) { if (bootverbose) printk ("%s: Downloading SCSI SCRIPTS.\n", ncr_name(np)); - if (np->features & FE_RAM8K) +#ifdef SCSI_NCR_PCI_MEM_NOT_SUPPORTED + if (np->base2_ws == 8192) phys = NCB_SCRIPTH0_PHYS (np, start_ram64); else phys = NCB_SCRIPTH_PHYS (np, start_ram); +#else + if (np->base2_ws == 8192) { + memcpy_to_pci(np->base2_va + 4096, + np->scripth0, sizeof(struct scripth)); + OUTL (nc_mmws, np->scr_ram_seg); + OUTL (nc_mmrs, np->scr_ram_seg); + OUTL (nc_sfs, np->scr_ram_seg); + phys = NCB_SCRIPTH_PHYS (np, start64); + } + else + phys = NCB_SCRIPT_PHYS (np, init); + memcpy_to_pci(np->base2_va, np->script0, sizeof(struct script)); +#endif /* SCSI_NCR_PCI_MEM_NOT_SUPPORTED */ } else phys = NCB_SCRIPT_PHYS (np, init); + np->istat_sem = 0; + OUTL (nc_dsa, vtobus(np)); OUTL (nc_dsp, phys); } @@ -6609,7 +7125,11 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer) /* ** Bells and whistles ;-) + ** Donnot announce negotiations due to auto-sense, + ** unless user really want us to be verbose. :) */ + if (bootverbose < 2 && (cp->host_flags & HF_AUTO_SENSE)) + goto next; PRINT_TARGET(np, target); if (sxfer & 0x01f) { unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0); @@ -6634,7 +7154,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer) mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f); } else printk ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : ""); - +next: /* ** set actual value and sync_status ** patch ALL ccbs of this target. @@ -6705,8 +7225,8 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack) static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln) { tcb_p tp = &np->target[tn]; - lcb_p lp = tp->lp[ln]; - u_char reqtags, maxdepth; + lcb_p lp = ncr_lp(np, tp, ln); + u_short reqtags, maxdepth; /* ** Just in case ... @@ -6800,35 +7320,12 @@ static void ncr_usercmd (ncb_p np) { u_char t; tcb_p tp; + int ln; + u_long size; switch (np->user.cmd) { - case 0: return; - case UC_SETSYNC: - for (t=0; t<MAX_TARGET; t++) { - if (!((np->user.target>>t)&1)) continue; - tp = &np->target[t]; - tp->usrsync = np->user.data; - ncr_negotiate (np, tp); - }; - break; - - case UC_SETTAGS: - for (t=0; t<MAX_TARGET; t++) { - int ln; - if (!((np->user.target>>t)&1)) continue; - np->target[t].usrtags = np->user.data; - for (ln = 0; ln < MAX_LUN; ln++) { - lcb_p lp = np->target[t].lp[ln]; - if (!lp) - continue; - lp->maxtags = lp->numtags = np->user.data; - ncr_setup_tags (np, t, ln); - } - }; - break; - case UC_SETDEBUG: #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT ncr_debug = np->user.data; @@ -6843,31 +7340,73 @@ static void ncr_usercmd (ncb_p np) np->verbose = np->user.data; break; - case UC_SETWIDE: - for (t=0; t<MAX_TARGET; t++) { - u_long size; - if (!((np->user.target>>t)&1)) continue; - tp = &np->target[t]; - size = np->user.data; - if (size > np->maxwide) size=np->maxwide; - tp->usrwide = size; - ncr_negotiate (np, tp); - }; - break; - - case UC_SETFLAG: - for (t=0; t<MAX_TARGET; t++) { - if (!((np->user.target>>t)&1)) continue; - tp = &np->target[t]; - tp->usrflag = np->user.data; - }; - break; - #ifdef SCSI_NCR_PROFILE_SUPPORT case UC_CLEARPROF: bzero(&np->profile, sizeof(np->profile)); break; #endif + default: + /* + ** We assume that other commands apply to targets. + ** This should always be the case and avoid the below + ** 4 lines to be repeated 5 times. + */ + for (t = 0; t < MAX_TARGET; t++) { + if (!((np->user.target >> t) & 1)) + continue; + tp = &np->target[t]; + + switch (np->user.cmd) { + + case UC_SETSYNC: + tp->usrsync = np->user.data; + ncr_negotiate (np, tp); + break; + + case UC_SETWIDE: + size = np->user.data; + if (size > np->maxwide) + size=np->maxwide; + tp->usrwide = size; + ncr_negotiate (np, tp); + break; + + case UC_SETTAGS: + tp->usrtags = np->user.data; + for (ln = 0; ln < MAX_LUN; ln++) { + lcb_p lp; + lp = ncr_lp(np, tp, ln); + if (!lp) + continue; + lp->numtags = np->user.data; + lp->maxtags = lp->numtags; + ncr_setup_tags (np, t, ln); + } + break; + + case UC_RESETDEV: + tp->to_reset = 1; + np->istat_sem = SEM; + OUTB (nc_istat, SIGP|SEM); + break; + + case UC_CLEARDEV: + for (ln = 0; ln < MAX_LUN; ln++) { + lcb_p lp; + lp = ncr_lp(np, tp, ln); + if (lp) + lp->to_clear = 1; + } + np->istat_sem = SEM; + OUTB (nc_istat, SIGP|SEM); + break; + + case UC_SETFLAG: + tp->usrflag = np->user.data; + break; + } + } + break; } np->user.cmd=0; } @@ -6889,7 +7428,7 @@ static void ncr_usercmd (ncb_p np) static void ncr_timeout (ncb_p np) { - u_long thistime = jiffies; + u_long thistime = ktime_get(0); /* ** If release process in progress, let's go @@ -6902,7 +7441,11 @@ static void ncr_timeout (ncb_p np) return; } - np->timer.expires = jiffies + SCSI_NCR_TIMER_INTERVAL; +#ifdef SCSI_NCR_PCIQ_BROKEN_INTR + np->timer.expires = ktime_get((HZ+9)/10); +#else + np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL); +#endif add_timer(&np->timer); /* @@ -6926,7 +7469,19 @@ static void ncr_timeout (ncb_p np) np->lasttime = thistime; } -#ifdef SCSI_NCR_BROKEN_INTR +#ifdef SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS + /* + ** Some way-broken PCI bridges may lead to + ** completions being lost when the clearing + ** of the INTFLY flag by the CPU occurs + ** concurrently with the chip raising this flag. + ** If this ever happen, lost completions will + ** be reaped here. + */ + ncr_wakeup_done(np); +#endif + +#ifdef SCSI_NCR_PCIQ_BROKEN_INTR if (INB(nc_istat) & (INTF|SIP|DIP)) { /* @@ -6936,7 +7491,7 @@ static void ncr_timeout (ncb_p np) ncr_exception (np); if (DEBUG_FLAGS & DEBUG_TINY) printk ("}"); } -#endif /* SCSI_NCR_BROKEN_INTR */ +#endif /* SCSI_NCR_PCIQ_BROKEN_INTR */ } /*========================================================== @@ -6963,7 +7518,7 @@ static void ncr_timeout (ncb_p np) ** dsp: script adress (relative to start of script). ** dbc: first word of script command. ** -** First 16 register of the chip: +** First 24 register of the chip: ** r0..rf ** **========================================================== @@ -7012,7 +7567,7 @@ static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat) } printk ("%s: regdump:", ncr_name(np)); - for (i=0; i<16;i++) + for (i=0; i<24;i++) printk (" %02x", (unsigned)INB_OFF(i)); printk (".\n"); } @@ -7119,7 +7674,7 @@ void ncr_exception (ncb_p np) ** with my 895 that unfortunately suffers of the MA int.). */ if (driver_setup.optimize & 1) { - OUTB(nc_istat, (INTF | SIGP)); + OUTB(nc_istat, (INTF | SIGP | np->istat_sem)); if (ncr_wakeup_done (np)) { #ifdef SCSI_NCR_PROFILE_SUPPORT ++np->profile.num_fly; @@ -7131,10 +7686,17 @@ void ncr_exception (ncb_p np) /* ** interrupt on the fly ? + ** + ** For bridges that donnot flush posted writes + ** in the reverse direction on read, a dummy read + ** may help not to miss completions. */ istat = INB (nc_istat); if (istat & INTF) { - OUTB (nc_istat, (istat & SIGP) | INTF); + OUTB (nc_istat, (istat & SIGP) | INTF | np->istat_sem); +#ifdef SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM + istat = INB (nc_istat); /* DUMMY READ */ +#endif if (DEBUG_FLAGS & DEBUG_TINY) printk ("F "); (void)ncr_wakeup_done (np); #ifdef SCSI_NCR_PROFILE_SUPPORT @@ -7252,8 +7814,8 @@ void ncr_exception (ncb_p np) ** Reset everything. **========================================================= */ - if (jiffies - np->regtime > 10*HZ) { - np->regtime = jiffies; + if (ktime_exp(np->regtime)) { + np->regtime = ktime_get(10*HZ); for (i = 0; i<sizeof(np->regdump); i++) ((char*)&np->regdump)[i] = INB_OFF(i); np->regdump.nc_dstat = dstat; @@ -7315,17 +7877,9 @@ static void ncr_recover_scsi_int (ncb_p np, u_char hsts) { u_int32 dsp = INL (nc_dsp); u_int32 dsa = INL (nc_dsa); - u_char scntl1 = INB (nc_scntl1); ccb_p cp = ncr_ccb_from_dsa(np, dsa); /* - ** If we are connected to the SCSI BUS, we only - ** can reset the BUS. - */ - if (scntl1 & ISCON) - goto reset_all; - - /* ** If we haven't been interrupted inside the SCRIPTS ** critical pathes, we can safely restart the SCRIPTS ** and trust the DSA value if it matches a CCB. @@ -7334,6 +7888,8 @@ static void ncr_recover_scsi_int (ncb_p np, u_char hsts) dsp < NCB_SCRIPT_PHYS (np, getjob_end) + 1)) && (!(dsp > NCB_SCRIPT_PHYS (np, ungetjob) && dsp < NCB_SCRIPT_PHYS (np, reselect) + 1)) && + (!(dsp > NCB_SCRIPTH_PHYS (np, sel_for_abort) && + dsp < NCB_SCRIPTH_PHYS (np, sel_for_abort_1) + 1)) && (!(dsp > NCB_SCRIPT_PHYS (np, done) && dsp < NCB_SCRIPT_PHYS (np, done_end) + 1))) { if (cp) { @@ -7377,7 +7933,6 @@ void ncr_int_sto (ncb_p np) if (DEBUG_FLAGS & DEBUG_TINY) printk ("T"); if (dsp == NCB_SCRIPT_PHYS (np, wf_sel_done) + 8 || - dsp == NCB_SCRIPTH_PHYS (np, wf_sel_done_no_atn) + 8 || !(driver_setup.recovery & 1)) ncr_recover_scsi_int(np, HS_SEL_TIMEOUT); else @@ -7429,7 +7984,7 @@ static void ncr_int_sbmc (ncb_p np) ** Suspend command processing for 1 second and ** reinitialize all except the chip. */ - np->settle_time = jiffies + HZ; + np->settle_time = ktime_get(1*HZ); ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET); } @@ -7455,8 +8010,8 @@ static void ncr_int_sbmc (ncb_p np) ** The chip will then interrupt with both PAR and MA ** conditions set. ** -** - A phase mismatch occurs before the MOV finished -** and phase errors are to be handled by SCRIPTS (896). +** - A phase mismatch occurs before the MOV finished and +** phase errors are to be handled by SCRIPTS (895A or 896). ** The chip will load the DSP with the phase mismatch ** JUMP address and interrupt the host processor. ** @@ -7472,6 +8027,7 @@ static void ncr_int_par (ncb_p np, u_short sist) u_char sbcl = INB (nc_sbcl); u_char cmd = dbc >> 24; int phase = cmd & 7; + ccb_p cp = ncr_ccb_from_dsa(np, dsa); printk("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n", ncr_name(np), hsts, dbc, sbcl); @@ -7491,7 +8047,7 @@ static void ncr_int_par (ncb_p np, u_short sist) ** If the nexus is not clearly identified, reset the bus. ** We will try to do better later. */ - if (!ncr_ccb_from_dsa(np, dsa)) + if (!cp) goto reset_all; /* @@ -7504,7 +8060,7 @@ static void ncr_int_par (ncb_p np, u_short sist) /* ** Keep track of the parity error. */ - OUTONB (HF_PRT, HF_PAR_ERR); + cp->xerr_status |= XE_PARITY_ERR; /* ** Prepare the message to send to the device. @@ -7528,7 +8084,7 @@ static void ncr_int_par (ncb_p np, u_short sist) /* No phase mismatch occurred */ else { OUTL (nc_temp, dsp); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, databreak)); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); } } else @@ -7590,8 +8146,9 @@ static void ncr_int_ma (ncb_p np) /* ** Donnot take into account dma fifo and various buffers in - ** DATA IN phase since the chip flushes everything before + ** INPUT phase since the chip flushes everything before ** raising the MA interrupt for interrupted INPUT phases. + ** For DATA IN phase, we will check for the SWIDE later. */ if ((cmd & 7) != 1) { u_int32 dfifo; @@ -7633,7 +8190,7 @@ static void ncr_int_ma (ncb_p np) */ OUTB (nc_ctest3, np->rv_ctest3 | CLF); /* dma fifo */ OUTB (nc_stest3, TE|CSF); /* scsi fifo */ - }; + } /* ** log the information @@ -7732,8 +8289,8 @@ static void ncr_int_ma (ncb_p np) ** ** Look at the PM_SAVE SCRIPT if you want to understand ** this stuff. The equivalent code is implemented in - ** SCRIPTS for the 896 that is able to handle PM from - ** the SCRIPTS processor. + ** SCRIPTS for the 895A and 896 that are able to handle + ** PM from the SCRIPTS processor. */ hflags0 = INB (HF_PRT); @@ -7770,6 +8327,53 @@ static void ncr_int_ma (ncb_p np) pm->sg.size = cpu_to_scr(rest); pm->ret = cpu_to_scr(nxtdsp); + /* + ** If we have a SWIDE, + ** - prepare the address to write the SWIDE from SCRIPTS, + ** - compute the SCRIPTS address to restart from, + ** - move current data pointer context by one byte. + */ + nxtdsp = NCB_SCRIPT_PHYS (np, dispatch); + if ((cmd & 7) == 1 && cp && (cp->phys.select.sel_scntl3 & EWS) && + (INB (nc_scntl2) & WSR)) { + /* + ** Hmmm... The device may want to also ignore + ** this residue but it must send immediately the + ** appropriate message. We snoop the SCSI BUS + ** and will just throw away this message from + ** SCRIPTS if the SWIDE is to be ignored. + */ + if ((INB (nc_sbcl) & 7) == 7 && + INB (nc_sbdl) == M_IGN_RESIDUE) { + nxtdsp = NCB_SCRIPT_PHYS (np, ign_i_w_r_msg); + } + /* + ** We must grab the SWIDE. + ** We will use some complex SCRIPTS for that. + */ + else { + OUTL (nc_scratcha, pm->sg.addr); + nxtdsp = NCB_SCRIPTH_PHYS (np, swide_ma_32); + if (np->features & FE_64BIT) { + OUTB (nc_sbr, (pm->sg.size >> 24)); + nxtdsp = NCB_SCRIPTH_PHYS (np, swide_ma_64); + } + /* + ** Adjust our data pointer context. + */ + ++pm->sg.addr; + --pm->sg.size; + /* + ** Hmmm... Could it be possible that a SWIDE that + ** is followed by a 1 byte CHMOV would lead to + ** a CHMOV(0). Anyway, we handle it by just + ** skipping context that would attempt a CHMOV(0). + */ + if (!pm->sg.size) + newcmd = pm->ret; + } + } + if (DEBUG_FLAGS & DEBUG_PHASE) { PRINT_ADDR(cp->cmd); printk ("PM %x %x %x / %x %x %x.\n", @@ -7780,12 +8384,11 @@ static void ncr_int_ma (ncb_p np) } /* - ** fake the return address (to the patch). - ** and restart script processor at dispatcher. + ** Restart the SCRIPTS processor. */ OUTL (nc_temp, newcmd); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, databreak)); + OUTL (nc_dsp, nxtdsp); return; /* @@ -7889,6 +8492,10 @@ reset_all: ** requeue the CCB that failed in front of the LUN queue. ** I just hope this not to be performed too often. :) ** +** If we are using IMMEDIATE ARBITRATION, we clear the +** IARB hint for every commands we encounter in order not +** to be stuck with a won arbitration and no job to queue +** to a device. **---------------------------------------------------------- */ @@ -7896,11 +8503,12 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) { Scsi_Cmnd *cmd = cp->cmd; tcb_p tp = &np->target[cp->target]; - lcb_p lp = tp->lp[cp->lun]; + lcb_p lp = ncr_lp(np, tp, cp->lun); ccb_p cp2; int busyccbs = 1; u_int32 startp; u_char s_status = INB (SS_PRT); + int msglen; /* ** Remove all CCBs queued to the chip for that LUN and put @@ -7923,6 +8531,11 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) continue; if (cp2->target != cp->target || cp2->lun != cp->lun) continue; +#ifdef SCSI_NCR_IARB_SUPPORT + cp2->host_flags &= ~HF_HINT_IARB; + if (cp2 == np->last_cp) + np->last_cp = 0; +#endif xpt_remque(&cp2->link_ccbq); xpt_insque_head(&cp2->link_ccbq, &lp->wait_ccbq); --lp->queuedccbs; @@ -7935,11 +8548,21 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) ** Requeue the interrupted CCB in front of ** the LUN CCB wait queue. */ +#ifdef SCSI_NCR_IARB_SUPPORT + cp->host_flags &= ~HF_HINT_IARB; + if (cp == np->last_cp) + np->last_cp = 0; +#endif xpt_remque(&cp->link_ccbq); xpt_insque_head(&cp->link_ccbq, &lp->wait_ccbq); --lp->queuedccbs; cp->queued = 0; +#ifdef SCSI_NCR_IARB_SUPPORT + if (np->last_cp) + np->last_cp->host_flags &= ~HF_HINT_IARB; +#endif + /* ** Repair the startqueue if necessary. */ @@ -7992,10 +8615,13 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) /* ** Repair the offending CCB. */ - cp->phys.header.savep = cp->startp; - cp->host_status = HS_BUSY; - cp->scsi_status = S_ILLEGAL; - cp->host_flags &= HF_PM_TO_C; + cp->phys.header.savep = cp->startp; + cp->phys.header.lastp = cp->lastp0; + cp->host_status = HS_BUSY; + cp->scsi_status = S_ILLEGAL; + cp->xerr_status = 0; + cp->phys.extra_bytes = 0; + cp->host_flags &= HF_PM_TO_C; break; @@ -8004,12 +8630,20 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) /* ** If we were requesting sense, give up. */ - if (cp->auto_sense) { + if (cp->host_flags & HF_AUTO_SENSE) { ncr_complete(np, cp); break; } /* + ** Save SCSI status and extended error. + ** Compute the data residual now. + */ + cp->sv_scsi_status = cp->scsi_status; + cp->sv_xerr_status = cp->xerr_status; + cp->resid = ncr_compute_residual(np, cp); + + /* ** Device returned CHECK CONDITION status. ** Prepare all needed data strutures for getting ** sense data. @@ -8019,8 +8653,29 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) ** identify message */ cp->scsi_smsg2[0] = M_IDENTIFY | cp->lun; + msglen = 1; + + /* + ** If we are currently using anything different from + ** async. 8 bit data transfers with that target, + ** start a negotiation, since the device may want + ** to report us a UNIT ATTENTION condition due to + ** a cause we currently ignore, and we donnot want + ** to be stuck with WIDE and/or SYNC data transfer. + ** + ** cp->nego_status is filled by ncr_prepare_nego(). + */ + ncr_negotiate(np, tp); + cp->nego_status = 0; + if ((tp->wval & EWS) || (tp->sval & 0x1f)) + msglen += + ncr_prepare_nego (np, cp, &cp->scsi_smsg2[msglen]); + + /* + ** Message table indirect structure. + */ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2)); - cp->phys.smsg.size = cpu_to_scr(1); + cp->phys.smsg.size = cpu_to_scr(msglen); /* ** sense command @@ -8038,6 +8693,7 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) /* ** sense data */ + bzero(cmd->sense_buffer, sizeof(cmd->sense_buffer)); cp->phys.sense.addr = cpu_to_scr(vtobus (&cmd->sense_buffer[0])); cp->phys.sense.size = @@ -8046,30 +8702,22 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) /* ** requeue the command. */ - startp = cpu_to_scr(NCB_SCRIPTH_PHYS (np, sdata_in)); + startp = NCB_SCRIPTH_PHYS (np, sdata_in); - cp->phys.header.savep = startp; - cp->phys.header.goalp = startp + 24; - cp->phys.header.lastp = startp; - cp->phys.header.wgoalp = startp + 24; - cp->phys.header.wlastp = startp; + cp->phys.header.savep = cpu_to_scr(startp); + cp->phys.header.goalp = cpu_to_scr(startp + 16); + cp->phys.header.lastp = cpu_to_scr(startp); + cp->phys.header.wgoalp = cpu_to_scr(startp + 16); + cp->phys.header.wlastp = cpu_to_scr(startp); - cp->host_status = HS_BUSY; + cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; cp->scsi_status = S_ILLEGAL; - cp->host_flags = 0; - cp->auto_sense = s_status; + cp->host_flags = HF_AUTO_SENSE; cp->phys.header.go.start = cpu_to_scr(NCB_SCRIPT_PHYS (np, select)); /* - ** Select without ATN for quirky devices. - */ - if (tp->quirks & QUIRK_NOMSG) - cp->phys.header.go.start = - cpu_to_scr(NCB_SCRIPTH_PHYS (np, select_no_atn)); - - /* ** If lp not yet allocated, requeue the command. */ if (!lp) @@ -8086,12 +8734,718 @@ static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp) return; } +/*---------------------------------------------------------- +** +** After a device has accepted some management message +** as BUS DEVICE RESET, ABORT TASK, etc ..., or when +** a device signals a UNIT ATTENTION condition, some +** tasks are thrown away by the device. We are required +** to reflect that on our tasks list since the device +** will never complete these tasks. +** +** This function completes all disconnected CCBs for a +** given target that matches the following criteria: +** - lun=-1 means any logical UNIT otherwise a given one. +** - task=-1 means any task, otherwise a given one. +**---------------------------------------------------------- +*/ +static int ncr_clear_tasks(ncb_p np, u_char hsts, + int target, int lun, int task) +{ + int i = 0; + ccb_p cp; + + for (cp = np->ccbc; cp; cp = cp->link_ccb) { + if (cp->host_status != HS_DISCONNECT) + continue; + if (cp->target != target) + continue; + if (lun != -1 && cp->lun != lun) + continue; + if (task != -1 && cp->tag != NO_TAG && cp->scsi_smsg[2] != task) + continue; + cp->host_status = hsts; + cp->scsi_status = S_ILLEGAL; + ncr_complete(np, cp); + ++i; + } + return i; +} /*========================================================== ** +** ncr chip handler for TASKS recovery. ** -** ncr chip exception handler for programmed interrupts. +**========================================================== +** +** We cannot safely abort a command, while the SCRIPTS +** processor is running, since we just would be in race +** with it. +** +** As long as we have tasks to abort, we keep the SEM +** bit set in the ISTAT. When this bit is set, the +** SCRIPTS processor interrupts (SIR_SCRIPT_STOPPED) +** each time it enters the scheduler. +** +** If we have to reset a target, clear tasks of a unit, +** or to perform the abort of a disconnected job, we +** restart the SCRIPTS for selecting the target. Once +** selected, the SCRIPTS interrupts (SIR_TARGET_SELECTED). +** If it loses arbitration, the SCRIPTS will interrupt again +** the next time it will enter its scheduler, and so on ... +** +** On SIR_TARGET_SELECTED, we scan for the more +** appropriate thing to do: +** +** - If nothing, we just sent a M_ABORT message to the +** target to get rid of the useless SCSI bus ownership. +** According to the specs, no tasks shall be affected. +** - If the target is to be reset, we send it a M_RESET +** message. +** - If a logical UNIT is to be cleared , we send the +** IDENTIFY(lun) + M_ABORT. +** - If an untagged task is to be aborted, we send the +** IDENTIFY(lun) + M_ABORT. +** - If a tagged task is to be aborted, we send the +** IDENTIFY(lun) + task attributes + M_ABORT_TAG. +** +** Once our 'kiss of death' :) message has been accepted +** by the target, the SCRIPTS interrupts again +** (SIR_ABORT_SENT). On this interrupt, we complete +** all the CCBs that should have been aborted by the +** target according to our message. +** +**---------------------------------------------------------- +*/ +static void ncr_sir_task_recovery(ncb_p np, int num) +{ + ccb_p cp; + tcb_p tp; + int target=-1, lun=-1, task; + int i, k; + u_char *p; + + switch(num) { + /* + ** The SCRIPTS processor stopped before starting + ** the next command in order to allow us to perform + ** some task recovery. + */ + case SIR_SCRIPT_STOPPED: + + /* + ** Do we have any target to reset or unit to clear ? + */ + for (i = 0 ; i < MAX_TARGET ; i++) { + tp = &np->target[i]; + if (tp->to_reset || (tp->l0p && tp->l0p->to_clear)) { + target = i; + break; + } + if (!tp->lmp) + continue; + for (k = 1 ; k < MAX_LUN ; k++) { + if (tp->lmp[k] && tp->lmp[k]->to_clear) { + target = i; + break; + } + } + if (target != -1) + break; + } + + /* + ** If not, look at the CCB list for any + ** disconnected CCB to be aborted. + */ + if (target == -1) { + for (cp = np->ccbc; cp; cp = cp->link_ccb) { + if (cp->host_status != HS_DISCONNECT) + continue; + if (cp->to_abort) { + target = cp->target; + break; + } + } + } + + /* + ** If some target is to be selected, + ** prepare and start the selection. + */ + if (target != -1) { + tp = &np->target[target]; + np->abrt_sel.sel_id = target; + np->abrt_sel.sel_scntl3 = tp->wval; + np->abrt_sel.sel_sxfer = tp->sval; + OUTL(nc_dsa, vtobus(np)); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sel_for_abort)); + return; + } + + /* + ** Nothing is to be selected, so we donnot need + ** to synchronize with the SCRIPTS anymore. + ** Remove the SEM flag from the ISTAT. + */ + np->istat_sem = 0; + OUTB (nc_istat, SIGP); + + /* + ** Now look at CCBs to abort that haven't started yet. + ** Remove all those CCBs from the start queue and + ** complete them with appropriate status. + ** Btw, the SCRIPTS processor is still stopped, so + ** we are not in race. + */ + for (cp = np->ccbc; cp; cp = cp->link_ccb) { + if (cp->host_status != HS_BUSY && + cp->host_status != HS_NEGOTIATE) + continue; + if (!cp->to_abort) + continue; +#ifdef SCSI_NCR_IARB_SUPPORT + /* + ** If we are using IMMEDIATE ARBITRATION, we donnot + ** want to cancel the last queued CCB, since the + ** SCRIPTS may have anticipated the selection. + */ + if (cp == np->last_cp) { + cp->to_abort = 0; + continue; + } +#endif + /* + ** Compute index of next position in the start + ** queue the SCRIPTS will schedule. + */ + i = (INL (nc_scratcha) - vtobus(np->squeue)) / 4; + + /* + ** Remove the job from the start queue. + */ + k = -1; + while (1) { + if (i == np->squeueput) + break; + if (k == -1) { /* Not found yet */ + if (cp == ncr_ccb_from_dsa(np, + scr_to_cpu(np->squeue[i]))) + k = i; /* Found */ + } + else { + /* + ** Once found, we have to move + ** back all jobs by 1 position. + */ + np->squeue[k] = np->squeue[i]; + k += 2; + if (k >= MAX_START*2) + k = 0; + } + + i += 2; + if (i >= MAX_START*2) + i = 0; + } + assert(k != -1); + if (k != 1) { + np->squeue[k] = np->squeue[i]; /* Idle task */ + cp->host_status = HS_ABORTED; + cp->scsi_status = S_ILLEGAL; + ncr_complete(np, cp); + } + } + break; + /* + ** The SCRIPTS processor has selected a target + ** we may have some manual recovery to perform for. + */ + case SIR_TARGET_SELECTED: + target = (INB (nc_sdid) & 0xf); + tp = &np->target[target]; + + np->abrt_tbl.addr = vtobus(np->abrt_msg); + + /* + ** If the target is to be reset, prepare a + ** M_RESET message and clear the to_reset flag + ** since we donnot expect this operation to fail. + */ + if (tp->to_reset) { + np->abrt_msg[0] = M_RESET; + np->abrt_tbl.size = 1; + tp->to_reset = 0; + break; + } + + /* + ** Otherwise, look for some logical unit to be cleared. + */ + if (tp->l0p && tp->l0p->to_clear) + lun = 0; + else if (tp->lmp) { + for (k = 1 ; k < MAX_LUN ; k++) { + if (tp->lmp[k] && tp->lmp[k]->to_clear) { + lun = k; + break; + } + } + } + + /* + ** If a logical unit is to be cleared, prepare + ** an IDENTIFY(lun) + ABORT MESSAGE. + */ + if (lun != -1) { + lcb_p lp = ncr_lp(np, tp, lun); + lp->to_clear = 0; /* We donnot expect to fail here */ + np->abrt_msg[0] = M_IDENTIFY | lun; + np->abrt_msg[1] = M_ABORT; + np->abrt_tbl.size = 2; + break; + } + + /* + ** Otherwise, look for some disconnected job to + ** abort for this target. + */ + for (cp = np->ccbc; cp; cp = cp->link_ccb) { + if (cp->host_status != HS_DISCONNECT) + continue; + if (cp->target != target) + continue; + if (cp->to_abort) + break; + } + + /* + ** If we have none, probably since the device has + ** completed the command before we won abitration, + ** send a M_ABORT message without IDENTIFY. + ** According to the specs, the device must just + ** disconnect the BUS and not abort any task. + */ + if (!cp) { + np->abrt_msg[0] = M_ABORT; + np->abrt_tbl.size = 1; + break; + } + + /* + ** We have some task to abort. + ** Set the IDENTIFY(lun) + */ + np->abrt_msg[0] = M_IDENTIFY | cp->lun; + + /* + ** If we want to abort an untagged command, we + ** will send a IDENTIFY + M_ABORT. + ** Otherwise (tagged command), we will send + ** a IDENTITFY + task attributes + ABORT TAG. + */ + if (cp->tag == NO_TAG) { + np->abrt_msg[1] = M_ABORT; + np->abrt_tbl.size = 2; + } + else { + np->abrt_msg[1] = cp->scsi_smsg[1]; + np->abrt_msg[2] = cp->scsi_smsg[2]; + np->abrt_msg[3] = M_ABORT_TAG; + np->abrt_tbl.size = 4; + } + cp->to_abort = 0; /* We donnot expect to fail here */ + break; + + /* + ** The target has accepted our message and switched + ** to BUS FREE phase as we expected. + */ + case SIR_ABORT_SENT: + target = (INB (nc_sdid) & 0xf); + tp = &np->target[target]; + + /* + ** If we didn't abort anything, leave here. + */ + if (np->abrt_msg[0] == M_ABORT) + break; + + /* + ** If we sent a M_RESET, then a hardware reset has + ** been performed by the target. + ** - Reset everything to async 8 bit + ** - Tell ourself to negotiate next time :-) + ** - Prepare to clear all disconnected CCBs for + ** this target from our task list (lun=task=-1) + */ + lun = -1; + task = -1; + if (np->abrt_msg[0] == M_RESET) { + tp->sval = 0; + tp->wval = np->rv_scntl3; + ncr_set_sync_wide_status(np, target); + ncr_negotiate(np, tp); + } + + /* + ** Otherwise, check for the LUN and TASK(s) + ** concerned by the cancelation. + ** If it is not ABORT_TAG then it is CLEAR_QUEUE + ** or an ABORT message :-) + */ + else { + lun = np->abrt_msg[0] & 0x3f; + if (np->abrt_msg[1] == M_ABORT_TAG) + task = np->abrt_msg[2]; + } + + /* + ** Complete all the CCBs the device should have + ** aborted due to our 'kiss of death' message. + */ + (void) ncr_clear_tasks(np, HS_ABORTED, target, lun, task); + break; + + /* + ** We have performed a auto-sense that succeeded. + ** If the device reports a UNIT ATTENTION condition + ** due to a RESET condition, we must complete all + ** disconnect CCBs for this unit since the device + ** shall have thrown them away. + ** Since I haven't time to guess what the specs are + ** expecting for other UNIT ATTENTION conditions, I + ** decided to only care about RESET conditions. :) + */ + case SIR_AUTO_SENSE_DONE: + cp = ncr_ccb_from_dsa(np, INL (nc_dsa)); + p = &cp->cmd->sense_buffer[0]; + + if (p[0] != 0x70 || p[2] != 0x6 || p[12] != 0x29) + break; + (void) ncr_clear_tasks(np, HS_RESET, cp->target, cp->lun, -1); + break; + } + + /* + ** Print to the log the message we intend to send. + */ + if (num == SIR_TARGET_SELECTED) { + PRINT_TARGET(np, target); + ncr_printl_hex("control msgout:", np->abrt_msg, + np->abrt_tbl.size); + np->abrt_tbl.size = cpu_to_scr(np->abrt_tbl.size); + } + + /* + ** Let the SCRIPTS processor continue. + */ + OUTONB (nc_dcntl, (STD|NOCOM)); +} + + +/*========================================================== +** +** Gérard's alchemy:) that deals with with the data +** pointer for both MDP and the residual calculation. +** +**========================================================== +** +** I didn't want to bloat the code by more than 200 +** lignes for the handling of both MDP and the residual. +** This has been achieved by using a data pointer +** representation consisting in an index in the data +** array (dp_sg) and a negative offset (dp_ofs) that +** have the following meaning: +** +** - dp_sg = MAX_SCATTER +** we are at the end of the data script. +** - dp_sg < MAX_SCATTER +** dp_sg points to the next entry of the scatter array +** we want to transfer. +** - dp_ofs < 0 +** dp_ofs represents the residual of bytes of the +** previous entry scatter entry we will send first. +** - dp_ofs = 0 +** no residual to send first. +** +** The function ncr_evaluate_dp() accepts an arbitray +** offset (basically from the MDP message) and returns +** the corresponding values of dp_sg and dp_ofs. +** +**---------------------------------------------------------- +*/ + +static int ncr_evaluate_dp(ncb_p np, ccb_p cp, u_int32 scr, int *ofs) +{ + u_int32 dp_scr; + int dp_ofs, dp_sg, dp_sgmin; + int tmp; + struct pm_ctx *pm; + + /* + ** Compute the resulted data pointer in term of a script + ** address within some DATA script and a signed byte offset. + */ + dp_scr = scr; + dp_ofs = *ofs; + if (dp_scr == NCB_SCRIPT_PHYS (np, pm0_data)) + pm = &cp->phys.pm0; + else if (dp_scr == NCB_SCRIPT_PHYS (np, pm1_data)) + pm = &cp->phys.pm1; + else + pm = 0; + + if (pm) { + dp_scr = pm->ret; + dp_ofs -= pm->sg.size; + } + + /* + ** Deduce the index of the sg entry. + ** Keep track of the index of the first valid entry. + ** If result is dp_sg = MAX_SCATTER, then we are at the + ** end of the data. + */ + tmp = scr_to_cpu(cp->phys.header.goalp); + dp_sg = MAX_SCATTER - (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4); + dp_sgmin = MAX_SCATTER - cp->segments; + + /* + ** Move to the sg entry the data pointer belongs to. + ** + ** If we are inside the data area, we expect result to be: + ** + ** Either, + ** dp_ofs = 0 and dp_sg is the index of the sg entry + ** the data pointer belongs to (or the end of the data) + ** Or, + ** dp_ofs < 0 and dp_sg is the index of the sg entry + ** the data pointer belongs to + 1. + */ + if (dp_ofs < 0) { + int n; + while (dp_sg > dp_sgmin) { + --dp_sg; + tmp = scr_to_cpu(cp->phys.data[dp_sg].size); + n = dp_ofs + (tmp & 0xffffff); + if (n > 0) { + ++dp_sg; + break; + } + dp_ofs = n; + } + } + else if (dp_ofs > 0) { + while (dp_sg < MAX_SCATTER) { + ++dp_sg; + tmp = scr_to_cpu(cp->phys.data[dp_sg].size); + dp_ofs -= (tmp & 0xffffff); + if (dp_ofs <= 0) + break; + } + } + + /* + ** Make sure the data pointer is inside the data area. + ** If not, return some error. + */ + if (dp_sg < dp_sgmin || (dp_sg == dp_sgmin && dp_ofs < 0)) + goto out_err; + else if (dp_sg > MAX_SCATTER || (dp_sg == MAX_SCATTER && dp_ofs > 0)) + goto out_err; + + /* + ** Save the extreme pointer if needed. + */ + if (dp_sg > cp->ext_sg || + (dp_sg == cp->ext_sg && dp_ofs < cp->ext_ofs)) { + cp->ext_sg = dp_sg; + cp->ext_ofs = dp_ofs; + } + + /* + ** Return data. + */ + *ofs = dp_ofs; + return dp_sg; + +out_err: + return -1; +} + +/*========================================================== +** +** ncr chip handler for MODIFY DATA POINTER MESSAGE +** +**========================================================== +** +** We also call this function on IGNORE WIDE RESIDUE +** messages that do not match a SWIDE full condition. +** Btw, we assume in that situation that such a message +** is equivalent to a MODIFY DATA POINTER (offset=-1). +** +**---------------------------------------------------------- +*/ + +static void ncr_modify_dp(ncb_p np, tcb_p tp, ccb_p cp, int ofs) +{ + int dp_ofs = ofs; + u_int32 dp_scr = INL (nc_temp); + u_int32 dp_ret; + u_char hflags; + int dp_sg; + struct pm_ctx *pm; + + /* + ** Not supported for auto_sense; + */ + if (cp->host_flags & HF_AUTO_SENSE) + goto out_reject; + + /* + ** Apply our alchemy:) (see comments in ncr_evaluate_dp()), + ** to the resulted data pointer. + */ + dp_sg = ncr_evaluate_dp(np, cp, dp_scr, &dp_ofs); + if (dp_sg < 0) + goto out_reject; + + /* + ** And our alchemy:) allows to easily calculate the data + ** script address we want to return for the next data phase. + */ + dp_ret = cpu_to_scr(cp->phys.header.goalp); + dp_ret = dp_ret - 8 - (MAX_SCATTER - dp_sg) * (SCR_SG_SIZE*4); + + /* + ** If offset / scatter entry is zero we donnot need + ** a context for the new current data pointer. + */ + if (dp_ofs == 0) { + dp_scr = dp_ret; + goto out_ok; + } + + /* + ** Get a context for the new current data pointer. + */ + hflags = INB (HF_PRT); + + if (hflags & HF_DP_SAVED) + hflags ^= HF_ACT_PM; + + if (!(hflags & HF_ACT_PM)) { + pm = &cp->phys.pm0; + dp_scr = NCB_SCRIPT_PHYS (np, pm0_data); + } + else { + pm = &cp->phys.pm1; + dp_scr = NCB_SCRIPT_PHYS (np, pm1_data); + } + + hflags &= ~(HF_DP_SAVED); + + OUTB (HF_PRT, hflags); + + /* + ** Set up the new current data pointer. + ** ofs < 0 there, and for the next data phase, we + ** want to transfer part of the data of the sg entry + ** corresponding to index dp_sg-1 prior to returning + ** to the main data script. + */ + pm->ret = cpu_to_scr(dp_ret); + pm->sg.addr = cp->phys.data[dp_sg-1].addr + dp_ofs; + pm->sg.size = cp->phys.data[dp_sg-1].size - dp_ofs; + +out_ok: + OUTL (nc_temp, dp_scr); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); + return; + +out_reject: + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_bad)); +} + + +/*========================================================== ** +** ncr chip calculation of the data residual. +** +**========================================================== +** +** As I used to say, the requirement of data residual +** in SCSI is broken, useless and cannot be achieved +** without huge complexity. +** But most OSes and even the official CAM require it. +** When stupidity happens to be so widely spread inside +** a community, it gets hard to convince. +** +** Anyway, I don't care, since I am not going to use +** any software that considers this data residual as +** a relevant information. :) +** +**---------------------------------------------------------- +*/ + +static int ncr_compute_residual(ncb_p np, ccb_p cp) +{ + int dp_sg, dp_sgmin, resid, tmp; + int dp_ofs = 0; + + /* + ** Should have been checked by the caller. + */ + if (cp->phys.header.lastp == cp->phys.header.goalp) + return 0; + + /* + ** If the last data pointer is data_io (direction + ** unknown), then no data transfer should have + ** taken place. + */ + if (cp->phys.header.lastp == NCB_SCRIPTH_PHYS (np, data_io)) + return -cp->data_len; + + /* + ** If the device asked for more data than available, + ** return a positive residual value. + */ + if (cp->phys.extra_bytes) + return scr_to_cpu(cp->phys.extra_bytes); + + /* + ** Evaluate the pointer saved on message COMPLETE. + ** According to our alchemy:), the extreme data + ** pointer will also be updated if needed. + ** On error, assume no data transferred (this may + ** happen if the data direction is unknown). + */ + tmp = cpu_to_scr(cp->phys.header.lastp); + if (ncr_evaluate_dp(np, cp, tmp, &dp_ofs) < 0) + return -cp->data_len; + + /* + ** We are now full comfortable in the computation + ** of the data residual (2's complement). + */ + dp_sgmin = MAX_SCATTER - cp->segments; + resid = cp->ext_ofs; + for (dp_sg = cp->ext_sg; dp_sg < MAX_SCATTER; ++dp_sg) { + tmp = scr_to_cpu(cp->phys.data[dp_sg].size); + resid -= (tmp & 0xffffff); + } + + /* + ** Hopefully, the result is not too wrong. + */ + return resid; +} + +/*========================================================== +** +** Print out the containt of a SCSI message. ** **========================================================== */ @@ -8113,80 +9467,22 @@ static int ncr_show_msg (u_char * msg) return (1); } - -void ncr_int_sir (ncb_p np) +static void ncr_print_msg (ccb_p cp, char *label, u_char *msg) { - u_char scntl3; - u_char chg, ofs, per, fak, wide; - u_char num = INB (nc_dsps); - ccb_p cp=0; - u_long dsa = INL (nc_dsa); - u_char target = INB (nc_sdid) & 0x0f; - tcb_p tp = &np->target[target]; - - if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num); + if (cp) + PRINT_ADDR(cp->cmd); + if (label) + printk ("%s: ", label); - switch (num) { - case SIR_SEL_ATN_NO_MSG_OUT: - /* - ** The device didn't go to MSG OUT phase after having - ** been selected with ATN. We donnot want to handle - ** that. - */ - printk ("%s:%d: No MSG OUT phase after selection with ATN.\n", - ncr_name (np), target); - goto out_stuck; - case SIR_RESEL_NO_MSG_IN: - case SIR_RESEL_NO_IDENTIFY: - /* - ** If devices reselecting without sending an IDENTIFY - ** message still exist, this should help. - ** We just assume lun=0, 1 CCB, no tag. - */ - if (tp->lp[0]) { - OUTL (nc_dsa, scr_to_cpu(tp->lp[0]->tasktbl[0])); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, resel_go)); - return; - } - case SIR_RESEL_BAD_LUN: - np->msgout[0] = M_RESET; - goto out; - case SIR_RESEL_BAD_I_T_L: - np->msgout[0] = M_ABORT; - goto out; - case SIR_RESEL_BAD_I_T_L_Q: - np->msgout[0] = M_ABORT_TAG; - goto out; - case SIR_RESEL_ABORTED: - np->lastmsg = np->msgout[0]; - np->msgout[0] = M_NOOP; - printk ("%s:%d: message %d sent on bad reselection.\n", - ncr_name (np), target, np->lastmsg); - goto out; - case SIR_MSG_OUT_DONE: - np->lastmsg = np->msgout[0]; - np->msgout[0] = M_NOOP; - /* Should we really care of that */ - if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) - OUTOFFB (HF_PRT, HF_PAR_ERR); - goto out; - case SIR_BAD_STATUS: - cp = ncr_ccb_from_dsa(np, dsa); - if (!cp) - goto out; - ncr_sir_to_redo(np, num, cp); - return; - default: - /* - ** lookup the ccb - */ - cp = ncr_ccb_from_dsa(np, dsa); - if (!cp) - goto out; - } + (void) ncr_show_msg (msg); + printk (".\n"); +} - switch (num) { -/*----------------------------------------------------------------------------- +/*=================================================================== +** +** Negotiation for WIDE and SYNCHRONOUS DATA TRANSFER. +** +**=================================================================== ** ** Was Sie schon immer ueber transfermode negotiation wissen wollten ... ** @@ -8199,8 +9495,8 @@ void ncr_int_sir (ncb_p np) ** The host status field is set to HS_NEGOTIATE to mark this ** situation. ** -** If the target doesn't answer this message immidiately -** (as required by the standard), the SIR_NEGO_FAIL interrupt +** If the target doesn't answer this message immediately +** (as required by the standard), the SIR_NEGO_FAILED interrupt ** will be raised eventually. ** The handler removes the HS_NEGOTIATE status, and sets the ** negotiated value to the default (async / nowide). @@ -8218,375 +9514,532 @@ void ncr_int_sir (ncb_p np) ** ** If the target doesn't fetch the answer (no message out phase), ** we assume the negotiation has failed, and fall back to default -** settings. +** settings (SIR_NEGO_PROTO interrupt). ** ** When we set the values, we adjust them in all ccbs belonging ** to this target, in the controller's register, and in the "phys" ** field of the controller's struct ncb. ** -** Possible cases: hs sir msg_in value send goto -** We try to negotiate: -** -> target doesnt't msgin NEG FAIL noop defa. - dispatch -** -> target rejected our msg NEG FAIL reject defa. - dispatch -** -> target answered (ok) NEG SYNC sdtr set - clrack -** -> target answered (!ok) NEG SYNC sdtr defa. REJ--->msg_bad -** -> target answered (ok) NEG WIDE wdtr set - clrack -** -> target answered (!ok) NEG WIDE wdtr defa. REJ--->msg_bad -** -> any other msgin NEG FAIL noop defa. - dispatch -** -** Target tries to negotiate: -** -> incoming message --- SYNC sdtr set SDTR - -** -> incoming message --- WIDE wdtr set WDTR - -** We sent our answer: -** -> target doesn't msgout --- PROTO ? defa. - dispatch -** -**----------------------------------------------------------------------------- +**--------------------------------------------------------------------- */ - case SIR_NEGO_FAILED: - /*------------------------------------------------------- - ** - ** Negotiation failed. - ** Target doesn't send an answer message, - ** or target rejected our message. - ** - ** Remove negotiation request. - ** - **------------------------------------------------------- - */ - OUTB (HS_PRT, HS_BUSY); - - /* fall through */ - - case SIR_NEGO_PROTO: - /*------------------------------------------------------- - ** - ** Negotiation failed. - ** Target doesn't fetch the answer message. - ** - **------------------------------------------------------- - */ - - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("negotiation failed sir=%x status=%x.\n", - num, cp->nego_status); - }; - - /* - ** any error in negotiation: - ** fall back to default mode. - */ - switch (cp->nego_status) { - - case NS_SYNC: - ncr_setsync (np, cp, 0, 0xe0); - break; - - case NS_WIDE: - ncr_setwide (np, cp, 0, 0); - break; - - }; - np->msgin [0] = M_NOOP; - np->msgout[0] = M_NOOP; - cp->nego_status = 0; - break; +/*========================================================== +** +** ncr chip handler for SYNCHRONOUS DATA TRANSFER +** REQUEST (SDTR) message. +** +**========================================================== +** +** Read comments above. +** +**---------------------------------------------------------- +*/ +static void ncr_sync_nego(ncb_p np, tcb_p tp, ccb_p cp) +{ + u_char scntl3; + u_char chg, ofs, per, fak; - case SIR_NEGO_SYNC: - /* - ** Synchronous request message received. - */ + /* + ** Synchronous request message received. + */ - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("sync msgin: "); - (void) ncr_show_msg (np->msgin); - printk (".\n"); - }; + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, "sync msg in", np->msgin); + }; - /* - ** get requested values. - */ + /* + ** get requested values. + */ - chg = 0; - per = np->msgin[3]; - ofs = np->msgin[4]; - if (ofs==0) per=255; + chg = 0; + per = np->msgin[3]; + ofs = np->msgin[4]; + if (ofs==0) per=255; - /* - ** if target sends SDTR message, - ** it CAN transfer synch. - */ + /* + ** if target sends SDTR message, + ** it CAN transfer synch. + */ - if (ofs) - tp->inq_byte7 |= INQ7_SYNC; + if (ofs) + tp->inq_byte7 |= INQ7_SYNC; - /* - ** check values against driver limits. - */ + /* + ** check values against driver limits. + */ - if (per < np->minsync) - {chg = 1; per = np->minsync;} - if (per < tp->minsync) - {chg = 1; per = tp->minsync;} - if (ofs > tp->maxoffs) - {chg = 1; ofs = tp->maxoffs;} + if (per < np->minsync) + {chg = 1; per = np->minsync;} + if (per < tp->minsync) + {chg = 1; per = tp->minsync;} + if (ofs > tp->maxoffs) + {chg = 1; ofs = tp->maxoffs;} - /* - ** Check against controller limits. - */ + /* + ** Check against controller limits. + */ + fak = 7; + scntl3 = 0; + if (ofs != 0) { + ncr_getsync(np, per, &fak, &scntl3); + if (fak > 7) { + chg = 1; + ofs = 0; + } + } + if (ofs == 0) { fak = 7; + per = 0; scntl3 = 0; - if (ofs != 0) { - ncr_getsync(np, per, &fak, &scntl3); - if (fak > 7) { - chg = 1; - ofs = 0; - } - } - if (ofs == 0) { - fak = 7; - per = 0; - scntl3 = 0; - tp->minsync = 0; - } - - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", - per, scntl3, ofs, fak, chg); - } + tp->minsync = 0; + } - if (INB (HS_PRT) == HS_NEGOTIATE) { - OUTB (HS_PRT, HS_BUSY); - switch (cp->nego_status) { + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->cmd); + printk ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", + per, scntl3, ofs, fak, chg); + } - case NS_SYNC: + if (INB (HS_PRT) == HS_NEGOTIATE) { + OUTB (HS_PRT, HS_BUSY); + switch (cp->nego_status) { + case NS_SYNC: + /* + ** This was an answer message + */ + if (chg) { /* - ** This was an answer message + ** Answer wasn't acceptable. */ - if (chg) { - /* - ** Answer wasn't acceptable. - */ - ncr_setsync (np, cp, 0, 0xe0); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); - } else { - /* - ** Answer is ok. - */ - ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); - }; - return; - - case NS_WIDE: - ncr_setwide (np, cp, 0, 0); - break; + ncr_setsync (np, cp, 0, 0xe0); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_bad)); + } else { + /* + ** Answer is ok. + */ + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; + return; + + case NS_WIDE: + ncr_setwide (np, cp, 0, 0); + break; }; + }; - /* - ** It was a request. Set value and - ** prepare an answer message - */ + /* + ** It was a request. Set value and + ** prepare an answer message + */ - ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); - np->msgout[0] = M_EXTENDED; - np->msgout[1] = 3; - np->msgout[2] = M_X_SYNC_REQ; - np->msgout[3] = per; - np->msgout[4] = ofs; + np->msgout[0] = M_EXTENDED; + np->msgout[1] = 3; + np->msgout[2] = M_X_SYNC_REQ; + np->msgout[3] = per; + np->msgout[4] = ofs; - cp->nego_status = NS_SYNC; + cp->nego_status = NS_SYNC; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("sync msgout: "); - (void) ncr_show_msg (np->msgout); - printk (".\n"); - } + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, "sync msgout", np->msgout); + } - if (!ofs) { - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); - return; - } - np->msgin [0] = M_NOOP; + np->msgin [0] = M_NOOP; - break; + if (!ofs) + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_bad)); + else + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sdtr_resp)); +} - case SIR_NEGO_WIDE: - /* - ** Wide request message received. - */ - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("wide msgin: "); - (void) ncr_show_msg (np->msgin); - printk (".\n"); - }; +/*========================================================== +** +** ncr chip handler for WIDE DATA TRANSFER REQUEST +** (WDTR) message. +** +**========================================================== +** +** Read comments above. +** +**---------------------------------------------------------- +*/ +static void ncr_wide_nego(ncb_p np, tcb_p tp, ccb_p cp) +{ + u_char chg, wide; - /* - ** get requested values. - */ + /* + ** Wide request message received. + */ + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, "wide msgin", np->msgin); + }; - chg = 0; - wide = np->msgin[3]; + /* + ** get requested values. + */ - /* - ** if target sends WDTR message, - ** it CAN transfer wide. - */ + chg = 0; + wide = np->msgin[3]; - if (wide) - tp->inq_byte7 |= INQ7_WIDE16; + /* + ** if target sends WDTR message, + ** it CAN transfer wide. + */ - /* - ** check values against driver limits. - */ + if (wide) + tp->inq_byte7 |= INQ7_WIDE16; - if (wide > tp->usrwide) - {chg = 1; wide = tp->usrwide;} + /* + ** check values against driver limits. + */ - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("wide: wide=%d chg=%d.\n", wide, chg); - } + if (wide > tp->usrwide) + {chg = 1; wide = tp->usrwide;} - if (INB (HS_PRT) == HS_NEGOTIATE) { - OUTB (HS_PRT, HS_BUSY); - switch (cp->nego_status) { + if (DEBUG_FLAGS & DEBUG_NEGO) { + PRINT_ADDR(cp->cmd); + printk ("wide: wide=%d chg=%d.\n", wide, chg); + } - case NS_WIDE: + if (INB (HS_PRT) == HS_NEGOTIATE) { + OUTB (HS_PRT, HS_BUSY); + switch (cp->nego_status) { + case NS_WIDE: + /* + ** This was an answer message + */ + if (chg) { /* - ** This was an answer message + ** Answer wasn't acceptable. */ - if (chg) { - /* - ** Answer wasn't acceptable. - */ - ncr_setwide (np, cp, 0, 1); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); - } else { - /* - ** Answer is ok. - */ - ncr_setwide (np, cp, wide, 1); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); - }; - return; - - case NS_SYNC: - ncr_setsync (np, cp, 0, 0xe0); - break; + ncr_setwide (np, cp, 0, 1); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_bad)); + } else { + /* + ** Answer is ok. + */ + ncr_setwide (np, cp, wide, 1); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; + return; + + case NS_SYNC: + ncr_setsync (np, cp, 0, 0xe0); + break; }; + }; - /* - ** It was a request, set value and - ** prepare an answer message - */ + /* + ** It was a request, set value and + ** prepare an answer message + */ - ncr_setwide (np, cp, wide, 1); + ncr_setwide (np, cp, wide, 1); - np->msgout[0] = M_EXTENDED; - np->msgout[1] = 2; - np->msgout[2] = M_X_WIDE_REQ; - np->msgout[3] = wide; + np->msgout[0] = M_EXTENDED; + np->msgout[1] = 2; + np->msgout[2] = M_X_WIDE_REQ; + np->msgout[3] = wide; - np->msgin [0] = M_NOOP; + np->msgin [0] = M_NOOP; - cp->nego_status = NS_WIDE; + cp->nego_status = NS_WIDE; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("wide msgout: "); - (void) ncr_show_msg (np->msgin); - printk (".\n"); - } - break; + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, "wide msgout", np->msgout); + } -/*-------------------------------------------------------------------- -** -** Processing of special messages -** -**-------------------------------------------------------------------- + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, wdtr_resp)); +} + +/* +** Reset SYNC or WIDE to default settings. +** Called when a negotiation does not succeed either +** on rejection or on protocol error. */ +static void ncr_nego_default(ncb_p np, tcb_p tp, ccb_p cp) +{ + /* + ** any error in negotiation: + ** fall back to default mode. + */ + switch (cp->nego_status) { - case SIR_REJECT_RECEIVED: - /*----------------------------------------------- - ** - ** We received a M_REJECT message. - ** - **----------------------------------------------- - */ + case NS_SYNC: + ncr_setsync (np, cp, 0, 0xe0); + break; - PRINT_ADDR(cp->cmd); - printk ("M_REJECT received (%x:%x).\n", - (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]); + case NS_WIDE: + ncr_setwide (np, cp, 0, 0); break; - case SIR_REJECT_TO_SEND: - /*----------------------------------------------- - ** - ** We received an unknown message - ** - **----------------------------------------------- - */ + }; + np->msgin [0] = M_NOOP; + np->msgout[0] = M_NOOP; + cp->nego_status = 0; +} + +/*========================================================== +** +** ncr chip handler for MESSAGE REJECT received for +** a WIDE or SYNCHRONOUS negotiation. +** +**========================================================== +** +** Read comments above. +** +**---------------------------------------------------------- +*/ +static void ncr_nego_rejected(ncb_p np, tcb_p tp, ccb_p cp) +{ + ncr_nego_default(np, tp, cp); + OUTB (HS_PRT, HS_BUSY); +} - PRINT_ADDR(cp->cmd); - printk ("M_REJECT to send for "); - (void) ncr_show_msg (np->msgin); - printk (".\n"); - np->msgout[0] = M_REJECT; - break; -/*-------------------------------------------------------------------- +/*========================================================== +** ** -** Processing of special messages +** ncr chip exception handler for programmed interrupts. ** -**-------------------------------------------------------------------- +** +**========================================================== */ - case SIR_IGN_RESIDUE: - /*----------------------------------------------- - ** - ** We received an IGNORE RESIDUE message, - ** which couldn't be handled by the script. - ** - **----------------------------------------------- - */ +void ncr_int_sir (ncb_p np) +{ + u_char num = INB (nc_dsps); + u_long dsa = INL (nc_dsa); + ccb_p cp = ncr_ccb_from_dsa(np, dsa); + u_char target = INB (nc_sdid) & 0x0f; + tcb_p tp = &np->target[target]; + int tmp; - PRINT_ADDR(cp->cmd); - printk ("M_IGN_RESIDUE received, but not yet implemented.\n"); - break; -#if 0 - case SIR_MISSING_SAVE: - /*----------------------------------------------- - ** - ** We received an DISCONNECT message, - ** but the datapointer wasn't saved before. - ** - **----------------------------------------------- - */ + if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num); - PRINT_ADDR(cp->cmd); - printk ("M_DISCONNECT received, but datapointer not saved: " - "data=%x save=%x goal=%x.\n", - (unsigned) INL (nc_temp), - (unsigned) scr_to_cpu(np->header.savep), - (unsigned) scr_to_cpu(np->header.goalp)); - break; + switch (num) { + /* + ** See comments in the SCRIPTS code. + */ +#ifdef SCSI_NCR_PCIQ_SYNC_ON_INTR + case SIR_DUMMY_INTERRUPT: + goto out; #endif + /* + ** The C code is currently trying to recover from something. + ** Typically, user want to abort some command. + */ + case SIR_SCRIPT_STOPPED: + case SIR_TARGET_SELECTED: + case SIR_ABORT_SENT: + ncr_sir_task_recovery(np, num); + return; + /* + ** The device didn't go to MSG OUT phase after having + ** been selected with ATN. We donnot want to handle + ** that. + */ + case SIR_SEL_ATN_NO_MSG_OUT: + printk ("%s:%d: No MSG OUT phase after selection with ATN.\n", + ncr_name (np), target); + goto out_stuck; + /* + ** The device didn't switch to MSG IN phase after + ** having reseleted the initiator. + */ + case SIR_RESEL_NO_MSG_IN: + /* + ** After reselection, the device sent a message that wasn't + ** an IDENTIFY. + */ + case SIR_RESEL_NO_IDENTIFY: + /* + ** If devices reselecting without sending an IDENTIFY + ** message still exist, this should help. + ** We just assume lun=0, 1 CCB, no tag. + */ + if (tp->l0p) { + OUTL (nc_dsa, scr_to_cpu(tp->l0p->tasktbl[0])); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, resel_go)); + return; + } + /* + ** The device reselected a LUN we donnot know of. + */ + case SIR_RESEL_BAD_LUN: + np->msgout[0] = M_RESET; + goto out; + /* + ** The device reselected for an untagged nexus and we + ** haven't any. + */ + case SIR_RESEL_BAD_I_T_L: + np->msgout[0] = M_ABORT; + goto out; + /* + ** The device reselected for a tagged nexus that we donnot + ** have. + */ + case SIR_RESEL_BAD_I_T_L_Q: + np->msgout[0] = M_ABORT_TAG; + goto out; + /* + ** The SCRIPTS let us know that the device has grabbed + ** our message and will abort the job. + */ + case SIR_RESEL_ABORTED: + np->lastmsg = np->msgout[0]; + np->msgout[0] = M_NOOP; + printk ("%s:%d: message %x sent on bad reselection.\n", + ncr_name (np), target, np->lastmsg); + goto out; + /* + ** The SCRIPTS let us know that a message has been + ** successfully sent to the device. + */ + case SIR_MSG_OUT_DONE: + np->lastmsg = np->msgout[0]; + np->msgout[0] = M_NOOP; + /* Should we really care of that */ + if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) { + if (cp) + cp->xerr_status &= ~XE_PARITY_ERR; + } + goto out; + /* + ** The device didn't send a GOOD SCSI status. + ** We may have some work to do prior to allow + ** the SCRIPTS processor to continue. + */ + case SIR_BAD_STATUS: + if (!cp) + goto out; + ncr_sir_to_redo(np, num, cp); + return; + /* + ** We are asked by the SCRIPTS to prepare a + ** REJECT message. + */ + case SIR_REJECT_TO_SEND: + ncr_print_msg(cp, "M_REJECT to send for ", np->msgin); + np->msgout[0] = M_REJECT; + goto out; + /* + ** We have been ODD at the end of a DATA IN + ** transfer and the device didn't send a + ** IGNORE WIDE RESIDUE message. + ** It is a data overrun condition. + */ + case SIR_SWIDE_OVERRUN: + if (cp) + cp->xerr_status |= XE_EXTRA_DATA; + goto out; + /* + ** We have been ODD at the end of a DATA OUT + ** transfer. + ** It is a data underrun condition. + */ + case SIR_SODL_UNDERRUN: + if (cp) + cp->xerr_status |= XE_EXTRA_DATA; + goto out; + /* + ** We received a message. + */ + case SIR_MSG_RECEIVED: + if (!cp) + goto out_stuck; + switch (np->msgin [0]) { + /* + ** We received an extended message. + ** We handle MODIFY DATA POINTER, SDTR, WDTR + ** and reject all other extended messages. + */ + case M_EXTENDED: + switch (np->msgin [2]) { + case M_X_MODIFY_DP: + if (DEBUG_FLAGS & DEBUG_POINTER) + ncr_print_msg(cp,"modify DP",np->msgin); + tmp = (np->msgin[3]<<24) + (np->msgin[4]<<16) + + (np->msgin[5]<<8) + (np->msgin[6]); + ncr_modify_dp(np, tp, cp, tmp); + return; + case M_X_SYNC_REQ: + ncr_sync_nego(np, tp, cp); + return; + case M_X_WIDE_REQ: + ncr_wide_nego(np, tp, cp); + return; + default: + goto out_reject; + } + break; + /* + ** We received a 1/2 byte message not handled from SCRIPTS. + ** We are only expecting MESSAGE REJECT and IGNORE WIDE + ** RESIDUE messages that haven't been anticipated by + ** SCRIPTS on SWIDE full condition. Unanticipated IGNORE + ** WIDE RESIDUE messages are aliased as MODIFY DP (-1). + */ + case M_IGN_RESIDUE: + if (DEBUG_FLAGS & DEBUG_POINTER) + ncr_print_msg(cp,"ign wide residue", np->msgin); + ncr_modify_dp(np, tp, cp, -1); + return; + case M_REJECT: + if (INB (HS_PRT) == HS_NEGOTIATE) + ncr_nego_rejected(np, tp, cp); + else { + PRINT_ADDR(cp->cmd); + printk ("M_REJECT received (%x:%x).\n", + scr_to_cpu(np->lastmsg), np->msgout[0]); + } + goto out_clrack; + break; + default: + goto out_reject; + } + break; + /* + ** We received an unknown message. + ** Ignore all MSG IN phases and reject it. + */ + case SIR_MSG_WEIRD: + ncr_print_msg(cp, "WEIRD message received", np->msgin); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_weird)); + return; + /* + ** Negotiation failed. + ** Target does not send us the reply. + ** Remove the HS_NEGOTIATE status. + */ + case SIR_NEGO_FAILED: + OUTB (HS_PRT, HS_BUSY); + /* + ** Negotiation failed. + ** Target does not want answer message. + */ + case SIR_NEGO_PROTO: + ncr_nego_default(np, tp, cp); + goto out; }; out: OUTONB (nc_dcntl, (STD|NOCOM)); + return; +out_reject: + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, msg_bad)); + return; +out_clrack: + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); + return; out_stuck: } + /*========================================================== ** ** @@ -8599,8 +10052,8 @@ out_stuck: static ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln) { tcb_p tp = &np->target[tn]; - lcb_p lp = tp->lp[ln]; - u_char tag = NO_TAG; + lcb_p lp = ncr_lp(np, tp, ln); + u_short tag = NO_TAG; XPT_QUEHEAD *qp; ccb_p cp = (ccb_p) 0; @@ -8644,9 +10097,10 @@ static ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln) if (lp->busyccbs < lp->maxnxs) { tag = lp->cb_tags[lp->ia_tag]; ++lp->ia_tag; - if (lp->ia_tag == SCSI_NCR_MAX_TAGS) + if (lp->ia_tag == MAX_TAGS) lp->ia_tag = 0; - lp->tags_umap |= (((tagmap_t) 1) << tag); + cp->tags_si = lp->tags_si; + ++lp->tags_sum[cp->tags_si]; } else goto out_free; @@ -8663,6 +10117,7 @@ static ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln) /* ** Remember all informations needed to free this CCB. */ + cp->to_abort = 0; cp->tag = tag; cp->target = tn; cp->lun = ln; @@ -8691,7 +10146,7 @@ out_free: static void ncr_free_ccb (ncb_p np, ccb_p cp) { tcb_p tp = &np->target[cp->target]; - lcb_p lp = tp->lp[cp->lun]; + lcb_p lp = ncr_lp(np, tp, cp->lun); if (DEBUG_FLAGS & DEBUG_TAGS) { PRINT_LUN(np, cp->target, cp->lun); @@ -8706,10 +10161,9 @@ static void ncr_free_ccb (ncb_p np, ccb_p cp) if (lp) { if (cp->tag != NO_TAG) { lp->cb_tags[lp->if_tag++] = cp->tag; - if (lp->if_tag == SCSI_NCR_MAX_TAGS) + if (lp->if_tag == MAX_TAGS) lp->if_tag = 0; - lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag); - lp->tags_smap &= lp->tags_umap; + --lp->tags_sum[cp->tags_si]; lp->tasktbl[cp->tag] = cpu_to_scr(np->p_bad_i_t_l_q); } else { lp->tasktbl[0] = cpu_to_scr(np->p_bad_i_t_l); @@ -8770,6 +10224,11 @@ static ccb_p ncr_alloc_ccb(ncb_p np) cp->phys.header.go.restart = cpu_to_scr(NCB_SCRIPTH_PHYS(np,bad_i_t_l)); /* + ** Initilialyze some other fields. + */ + cp->phys.smsg_ext.addr = cpu_to_scr(vtobus(&np->msgin[2])); + + /* ** Chain into wakeup list and free ccb queue. */ cp->link_ccb = np->ccbc; @@ -8820,25 +10279,6 @@ static ccb_p ncr_ccb_from_dsa(ncb_p np, u_long dsa) */ static void ncr_init_tcb (ncb_p np, u_char tn) { - tcb_p tp = &np->target[tn]; - - /* - ** Already bone. - */ - if (tp->luntbl) - return; - /* - ** Allocate the lcb bus address array. - */ - tp->luntbl = m_calloc(256, "LUNTBL", MEMO_WARN); - if (!tp->luntbl) - return; - - /* - ** Compute the bus address of this table. - */ - tp->b_luntbl = cpu_to_scr(vtobus(tp->luntbl)); - /* ** Check some alignments required by the chip. */ @@ -8858,7 +10298,7 @@ static void ncr_init_tcb (ncb_p np, u_char tn) static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln) { tcb_p tp = &np->target[tn]; - lcb_p lp = tp->lp[ln]; + lcb_p lp = ncr_lp(np, tp, ln); /* ** Already done, return. @@ -8870,21 +10310,46 @@ static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln) ** Initialize the target control block if not yet. */ ncr_init_tcb(np, tn); - if (!tp->luntbl) - goto fail; /* - ** Allocate the lcb. + ** Allocate the lcb bus address array. + ** Compute the bus address of this table. */ - lp = m_calloc(sizeof(struct lcb), "LCB", MEMO_WARN); - if (!lp) - goto fail; - tp->lp[ln] = lp; + if (ln && !tp->luntbl) { + int i; + + tp->luntbl = m_calloc(256, "LUNTBL", MEMO_WARN); + if (!tp->luntbl) + goto fail; + for (i = 0 ; i < 64 ; i++) + tp->luntbl[i] = cpu_to_scr(vtobus(&np->resel_badlun)); + tp->b_luntbl = cpu_to_scr(vtobus(tp->luntbl)); + } + + /* + ** Allocate the table of pointers for LUN(s) > 0, if needed. + */ + if (ln && !tp->lmp) { + tp->lmp = m_calloc(MAX_LUN * sizeof(lcb_p), "LMP", MEMO_WARN); + if (!tp->lmp) + goto fail; + } /* + ** Allocate the lcb. ** Make it available to the chip. */ - tp->luntbl[ln] = cpu_to_scr(vtobus(lp)); + lp = m_calloc(sizeof(struct lcb), "LCB", MEMO_WARN); + if (!lp) + goto fail; + if (ln) { + tp->lmp[ln] = lp; + tp->luntbl[ln] = cpu_to_scr(vtobus(lp)); + } + else { + tp->l0p = lp; + tp->b_lun0 = cpu_to_scr(vtobus(lp)); + } /* ** Initialize the CCB queue headers. @@ -8924,7 +10389,7 @@ fail: static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data) { tcb_p tp = &np->target[tn]; - lcb_p lp = tp->lp[ln]; + lcb_p lp = ncr_lp(np, tp, ln); u_char inq_byte7; int i; @@ -8934,14 +10399,16 @@ static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data) if (!lp && !(lp = ncr_alloc_lcb(np, tn, ln))) goto fail; +#if 0 /* No more used. Left here as provision */ /* - ** Get device quirks from a speciality table. + ** Get device quirks. */ - tp->quirks = ncr_lookup (inq_data); + tp->quirks = 0; if (tp->quirks && bootverbose) { PRINT_LUN(np, tn, ln); printk ("quirks=%x.\n", tp->quirks); } +#endif /* ** Evaluate trustable target/unit capabilities. @@ -8984,18 +10451,23 @@ static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data) ** initialyze the task table if not yet. */ if ((inq_byte7 & INQ7_QUEUE) && lp->tasktbl == &lp->tasktbl_0) { - lp->tasktbl = m_calloc(256, "TASKTBL", MEMO_WARN); + lp->tasktbl = m_calloc(MAX_TASKS*4, "TASKTBL", MEMO_WARN); if (!lp->tasktbl) { lp->tasktbl = &lp->tasktbl_0; goto fail; } lp->b_tasktbl = cpu_to_scr(vtobus(lp->tasktbl)); - for (i = 0 ; i < 64 ; i++) + for (i = 0 ; i < MAX_TASKS ; i++) lp->tasktbl[i] = cpu_to_scr(np->p_notask); - for (i = 0 ; i < SCSI_NCR_MAX_TAGS ; i++) + + lp->cb_tags = m_calloc(MAX_TAGS, "CB_TAGS", MEMO_WARN); + if (!lp->cb_tags) + goto fail; + for (i = 0 ; i < MAX_TAGS ; i++) lp->cb_tags[i] = i; - lp->maxnxs = SCSI_NCR_MAX_TAGS; - lp->tags_stime = jiffies; + + lp->maxnxs = MAX_TAGS; + lp->tags_stime = ktime_get(3*HZ); } /* @@ -9043,7 +10515,7 @@ fail: /* ** For 64 bit systems, we use the 8 upper bits of the size field ** to provide bus address bits 32-39 to the SCRIPTS processor. -** This allows the 896 to access up to 1 tera-bytes of memory. +** This allows the 895A and 896 to address up to 1 TB of memory. ** For 32 bit chips on 64 bit systems, we must be provided with ** memory addresses that fit into the first 32 bit bus address ** range and so, this does not matter and we expect an error from @@ -9325,62 +10797,6 @@ static void ncb_profile (ncb_p np, ccb_p cp) /*========================================================== ** -** -** Device lookup. -** -** @GENSCSI@ should be integrated to scsiconf.c -** -** -**========================================================== -*/ - -struct table_entry { - char * manufacturer; - char * model; - char * version; - u_long info; -}; - -static struct table_entry device_tab[] = -{ -#if 0 - {"", "", "", QUIRK_NOMSG}, -#endif - {"SONY", "SDT-5000", "3.17", QUIRK_NOMSG}, - {"WangDAT", "Model 2600", "01.7", QUIRK_NOMSG}, - {"WangDAT", "Model 3200", "02.2", QUIRK_NOMSG}, - {"WangDAT", "Model 1300", "02.4", QUIRK_NOMSG}, - {"", "", "", 0} /* catch all: must be last entry. */ -}; - -static u_long ncr_lookup(char * id) -{ - struct table_entry * p = device_tab; - char *d, *r, c; - - for (;;p++) { - - d = id+8; - r = p->manufacturer; - while ((c=*r++)) if (c!=*d++) break; - if (c) continue; - - d = id+16; - r = p->model; - while ((c=*r++)) if (c!=*d++) break; - if (c) continue; - - d = id+32; - r = p->version; - while ((c=*r++)) if (c!=*d++) break; - if (c) continue; - - return (p->info); - } -} - -/*========================================================== -** ** Determine the ncr's clock frequency. ** This is essential for the negotiation ** of the synchronous transfer rate. @@ -9395,7 +10811,7 @@ static u_long ncr_lookup(char * id) ** do not have a clock doubler and so are provided with a ** 80 MHz clock. All other fast20 boards incorporate a doubler ** and so should be delivered with a 40 MHz clock. -** The recent fast40 chips (895/896) use a 40 Mhz base clock +** The recent fast40 chips (895/896/895A) use a 40 Mhz base clock ** and provide a clock quadrupler (160 Mhz). The code below ** tries to deal as cleverly as possible with all this stuff. ** @@ -9436,7 +10852,8 @@ static void ncr_selectclock(ncb_p np, u_char scntl3) */ static unsigned __init ncrgetfreq (ncb_p np, int gen) { - unsigned ms = 0; + unsigned int ms = 0; + unsigned int f; /* * Measure GEN timer delay in order @@ -9453,7 +10870,6 @@ static unsigned __init ncrgetfreq (ncb_p np, int gen) * performed trust the higher delay * (lower frequency returned). */ - OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */ OUTW (nc_sien , 0); /* mask all scsi interrupts */ (void) INW (nc_sist); /* clear pending scsi interrupt */ OUTB (nc_dien , 0); /* mask all dma interrupts */ @@ -9471,12 +10887,28 @@ static unsigned __init ncrgetfreq (ncb_p np, int gen) */ OUTB (nc_scntl3, 0); - if (bootverbose >= 2) - printk ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms); /* * adjust for prescaler, and convert into KHz */ - return ms ? ((1 << gen) * 4340) / ms : 0; + f = ms ? ((1 << gen) * 4340) / ms : 0; + + if (bootverbose >= 2) + printk ("%s: Delay (GEN=%d): %u msec, %u KHz\n", + ncr_name(np), gen, ms, f); + + return f; +} + +static unsigned __init ncr_getfreq (ncb_p np) +{ + u_int f1, f2; + int gen = 11; + + (void) ncrgetfreq (np, gen); /* throw away first result */ + f1 = ncrgetfreq (np, gen); + f2 = ncrgetfreq (np, gen); + if (f1 > f2) f1 = f2; /* trust lower result */ + return f1; } /* @@ -9492,7 +10924,7 @@ static void __init ncr_getclock (ncb_p np, int mult) f1 = 40000; /* - ** True with 875/895/896 with clock multiplier selected + ** True with 875/895/896/895A with clock multiplier selected */ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { if (bootverbose >= 2) @@ -9506,16 +10938,11 @@ static void __init ncr_getclock (ncb_p np, int mult) ** Otherwise trust scntl3 BIOS setting. */ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) { - unsigned f2; - - (void) ncrgetfreq (np, 11); /* throw away first result */ - f1 = ncrgetfreq (np, 11); - f2 = ncrgetfreq (np, 11); + OUTB (nc_stest1, 0); /* make sure doubler is OFF */ + f1 = ncr_getfreq (np); if (bootverbose) - printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2); - - if (f1 > f2) f1 = f2; /* trust lower result */ + printk ("%s: NCR clock is %uKHz\n", ncr_name(np), f1); if (f1 < 45000) f1 = 40000; else if (f1 < 55000) f1 = 50000; @@ -9541,6 +10968,21 @@ static void __init ncr_getclock (ncb_p np, int mult) np->clock_khz = f1; } +/* + * Get/probe PCI clock frequency + */ +static u_int __init ncr_getpciclock (ncb_p np) +{ + static u_int f = 0; + + if (!f) { + OUTB (nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */ + f = ncr_getfreq (np); + OUTB (nc_stest1, 0); + } + return f; +} + /*===================== LINUX ENTRY POINTS SECTION ==========================*/ #ifndef uchar @@ -9592,6 +11034,11 @@ static void __init ncr_getclock (ncb_p np, int mult) #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:" @@ -9606,7 +11053,11 @@ static char setup_token[] __initdata = "buschk:" "optim:" "recovery:" "safe:" "nvram:" - "excl:"; + "excl:" "hostid:" +#ifdef SCSI_NCR_IARB_SUPPORT + "iarb:" +#endif + ; /* DONNOT REMOVE THIS ';' */ #ifdef MODULE #define ARG_SEP ' ' @@ -9631,7 +11082,7 @@ static int __init get_setup_token(char *p) } -void __init sym53c8xx_setup(char *str, int *ints) +int __init sym53c8xx_setup(char *str) { #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT char *cur = str; @@ -9736,6 +11187,14 @@ void __init sym53c8xx_setup(char *str, int *ints) 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; @@ -9745,8 +11204,15 @@ void __init sym53c8xx_setup(char *str, int *ints) ++cur; } #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */ + return 0; } +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) +#ifndef MODULE +__setup("sym53c8xx=", sym53c8xx_setup); +#endif +#endif + static int sym53c8xx_pci_init(Scsi_Host_Template *tpnt, uchar bus, uchar device_fn, ncr_device *device); @@ -9893,7 +11359,7 @@ int __init sym53c8xx_detect(Scsi_Host_Template *tpnt) #if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE) if (sym53c8xx) - sym53c8xx_setup(sym53c8xx, (int *) 0); + sym53c8xx_setup(sym53c8xx); #endif #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT ncr_debug = driver_setup.debug; @@ -9945,8 +11411,16 @@ if (sym53c8xx) continue; } ++index; + /* Some HW as the HP LH4 may report twice PCI devices */ + for (i = 0; i < count ; i++) { + if (devtbl[i].slot.bus == bus && + devtbl[i].slot.device_fn == device_fn) + break; + } + if (i != count) /* Ignore this device if we already have it */ + continue; devp = &devtbl[count]; - devp->host_id = 255; + devp->host_id = driver_setup.host_id; devp->attach_done = 0; if (sym53c8xx_pci_init(tpnt, bus, device_fn, devp)) { continue; @@ -10050,7 +11524,7 @@ next: **=================================================================== */ #if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) -static int __init +static int __init pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) { u_int32 tmp; @@ -10067,18 +11541,27 @@ pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) } return offset; } -#else /* LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) */ -static int __init +#elif LINUX_VERSION_CODE <= LinuxVersionCode(2,3,12) +static int __init pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) { - /* FIXME! This is just unbelieably horrible backwards compatibility code */ - struct resource *res = pdev->resource + index; - - *base = res->start | (res->flags & 0xf); - if ((res->flags & 0x7) == 0x4) { + *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+1; + return index; +} +#else /* 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; } #endif @@ -10094,6 +11577,7 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, 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; u_char revision; #if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) struct pci_dev *pdev; @@ -10173,18 +11657,18 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, if (revision > ncr_chip_table[i].revision_id) continue; if (!(ncr_chip_table[i].features & FE_LDSTR)) - continue; + break; 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. ** 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; @@ -10206,7 +11690,7 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, } } } -#endif +#endif /* i386 and PCI MEMORY accessible */ if (!chip) { printk(NAME53C8XX ": not initializing, device not supported\n"); @@ -10267,6 +11751,9 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, ** coherent with hardware and software resource identifications. ** This is fairly simple, but seems still too complex for Sparc. */ + base = __pa(base); + base_2 = __pa(base_2); + if (!cache_line_size) suggested_cache_line_size = 16; @@ -10290,9 +11777,16 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, /* ** 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 (!(command & PCI_COMMAND_IO) || !(io_port & 1)) { +/* #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; @@ -10307,7 +11801,8 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, base &= PCI_BASE_ADDRESS_MEM_MASK; base_2 &= PCI_BASE_ADDRESS_MEM_MASK; -#ifdef NCR_IOMAPPED +/* #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); @@ -10315,7 +11810,8 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, } if (!io_port) return -1; -#else +#endif +#ifndef NCR_IOMAPPED if (!base) { printk(NAME53C8XX ": MMIO base address disabled.\n"); return -1; @@ -10354,11 +11850,24 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, 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. + */ + pci_fix_up = driver_setup.pci_fix_up; + 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 ((driver_setup.pci_fix_up & 1) && (chip->features & FE_CLSE) && + if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && !cache_line_size && suggested_cache_line_size) { cache_line_size = suggested_cache_line_size; pcibios_write_config_byte(bus, device_fn, @@ -10367,7 +11876,7 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, cache_line_size); } - if ((driver_setup.pci_fix_up & 2) && 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; @@ -10379,7 +11888,7 @@ static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt, ** (latency timer >= burst length + 6, we add 10 to be quite sure) */ - if ((driver_setup.pci_fix_up & 4) && chip->burst_max) { + if ((pci_fix_up & 4) && chip->burst_max) { uchar lt = (1 << chip->burst_max) + 6 + 10; if (latency_timer < lt) { latency_timer = lt; @@ -10532,7 +12041,7 @@ static void sym53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de np = ((struct host_data *) host->hostdata)->ncb; tp = &np->target[device->id]; - lp = tp->lp[device->lun]; + lp = ncr_lp(np, tp, device->lun); /* ** Select queue depth from driver setup. @@ -10548,8 +12057,8 @@ static void sym53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de device->queue_depth = numtags; if (device->queue_depth < 2) device->queue_depth = 2; - if (device->queue_depth > SCSI_NCR_MAX_TAGS) - device->queue_depth = SCSI_NCR_MAX_TAGS; + if (device->queue_depth > MAX_TAGS) + device->queue_depth = MAX_TAGS; /* ** Since the queue depth is not tunable under Linux, @@ -10973,8 +12482,14 @@ static int ncr_user_command(ncb_p np, char *buffer, int length) uc->cmd = UC_SETDEBUG; else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0) uc->cmd = UC_SETFLAG; + else if ((arg_len = is_keyword(ptr, len, "resetdev")) != 0) + uc->cmd = UC_RESETDEV; + else if ((arg_len = is_keyword(ptr, len, "cleardev")) != 0) + uc->cmd = UC_CLEARDEV; +#ifdef SCSI_NCR_PROFILE_SUPPORT else if ((arg_len = is_keyword(ptr, len, "clearprof")) != 0) uc->cmd = UC_CLEARPROF; +#endif else arg_len = 0; @@ -10991,6 +12506,8 @@ printk("ncr_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd); case UC_SETTAGS: case UC_SETWIDE: case UC_SETFLAG: + case UC_RESETDEV: + case UC_CLEARDEV: SKIP_SPACES(1); if ((arg_len = is_keyword(ptr, len, "all")) != 0) { ptr += arg_len; len -= arg_len; @@ -11034,14 +12551,12 @@ printk("ncr_user_command: data=%ld\n", uc->data); uc->data |= DEBUG_ALLOC; else if ((arg_len = is_keyword(ptr, len, "phase"))) uc->data |= DEBUG_PHASE; - else if ((arg_len = is_keyword(ptr, len, "poll"))) - uc->data |= DEBUG_POLL; else if ((arg_len = is_keyword(ptr, len, "queue"))) uc->data |= DEBUG_QUEUE; else if ((arg_len = is_keyword(ptr, len, "result"))) uc->data |= DEBUG_RESULT; - else if ((arg_len = is_keyword(ptr, len, "scatter"))) - uc->data |= DEBUG_SCATTER; + else if ((arg_len = is_keyword(ptr, len, "pointer"))) + uc->data |= DEBUG_POINTER; else if ((arg_len = is_keyword(ptr, len, "script"))) uc->data |= DEBUG_SCRIPT; else if ((arg_len = is_keyword(ptr, len, "tiny"))) @@ -11052,10 +12567,6 @@ printk("ncr_user_command: data=%ld\n", uc->data); uc->data |= DEBUG_NEGO; else if ((arg_len = is_keyword(ptr, len, "tags"))) uc->data |= DEBUG_TAGS; - else if ((arg_len = is_keyword(ptr, len, "freeze"))) - uc->data |= DEBUG_FREEZE; - else if ((arg_len = is_keyword(ptr, len, "restart"))) - uc->data |= DEBUG_RESTART; else return -EINVAL; ptr += arg_len; len -= arg_len; @@ -11167,7 +12678,7 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len) (u_long) np->reg); #endif copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync); - copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS); + copy_info(&info, "max commands per lun %d\n", MAX_TAGS); if (driver_setup.debug || driver_setup.verbose > 1) { copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug); @@ -11350,8 +12861,8 @@ static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) nvram_stop(np, &gpreg); #ifdef SCSI_NCR_DEBUG_NVRAM -printk("sym53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n", - nvram->start_marker, +printk("sym53c8xx: 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, @@ -11375,7 +12886,8 @@ out: /* * 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) +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; @@ -11404,7 +12916,8 @@ static void __init nvram_start(ncr_slot *np, u_char *gpreg) * 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) +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; @@ -11418,7 +12931,8 @@ static void __init nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write * 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) +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; @@ -11436,7 +12950,8 @@ static void __init nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_d * 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) +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); @@ -11447,7 +12962,8 @@ static void __init nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, * 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) +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); @@ -11458,7 +12974,8 @@ static void __init nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, * 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) +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); @@ -11480,7 +12997,8 @@ static void __init nvram_stop(ncr_slot *np, u_char *gpreg) /* * 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) +static void __init +nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) { UDELAY (5); switch (bit_mode){ @@ -11566,7 +13084,8 @@ static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) /* * 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) +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; @@ -11591,7 +13110,8 @@ static u_short __init Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_c /* * 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) +static void __init +Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg) { int x; @@ -11605,7 +13125,8 @@ static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char /* * READ a byte from the NVRAM */ -static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) +static void __init +Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) { int x; u_char read_bit; @@ -11624,7 +13145,8 @@ static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *g /* * Read bit from NVRAM */ -static void __init Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) +static void __init +Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) { UDELAY (2); Tnvram_Clk(np, gpreg); @@ -11634,7 +13156,8 @@ static void __init Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg /* * Write bit to GPIO0 */ -static void __init Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) +static void __init +Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) { if (write_bit & 0x01) *gpreg |= 0x02; diff --git a/drivers/scsi/sym53c8xx.h b/drivers/scsi/sym53c8xx.h index 5bdd608e9..4e39980c4 100644 --- a/drivers/scsi/sym53c8xx.h +++ b/drivers/scsi/sym53c8xx.h @@ -1,7 +1,7 @@ /****************************************************************************** ** High Performance device driver for the Symbios 53C896 controller. ** -** Copyright (C) 1998 Gerard Roudier <groudier@club-internet.fr> +** Copyright (C) 1998-1999 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 diff --git a/drivers/scsi/sym53c8xx_defs.h b/drivers/scsi/sym53c8xx_defs.h index 3b30e1bb5..0ec18fc2d 100644 --- a/drivers/scsi/sym53c8xx_defs.h +++ b/drivers/scsi/sym53c8xx_defs.h @@ -1,7 +1,7 @@ /****************************************************************************** ** High Performance device driver for the Symbios 53C896 controller. ** -** Copyright (C) 1998 Gerard Roudier <groudier@club-internet.fr> +** Copyright (C) 1998-1999 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 @@ -137,13 +137,13 @@ #define SCSI_NCR_MAX_SYNC (40) /* - * Allow tags from 2 to 64, default 8 + * Allow tags from 2 to 256, default 8 */ #ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS #if CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2 #define SCSI_NCR_MAX_TAGS (2) -#elif CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 64 -#define SCSI_NCR_MAX_TAGS (64) +#elif CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 256 +#define SCSI_NCR_MAX_TAGS (256) #else #define SCSI_NCR_MAX_TAGS CONFIG_SCSI_NCR53C8XX_MAX_TAGS #endif @@ -175,6 +175,13 @@ #endif /* + * Immediate arbitration + */ +#if defined(CONFIG_SCSI_NCR53C8XX_IARB) +#define SCSI_NCR_IARB_SUPPORT +#endif + +/* * Sync transfer frequency at startup. * Allow from 5Mhz to 40Mhz default 20 Mhz. */ @@ -238,7 +245,7 @@ */ #ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT #define SCSI_NCR_SETUP_LED_PIN (1) -#define SCSI_NCR_SETUP_DIFF_SUPPORT (3) +#define SCSI_NCR_SETUP_DIFF_SUPPORT (4) #else #define SCSI_NCR_SETUP_LED_PIN (0) #define SCSI_NCR_SETUP_DIFF_SUPPORT (0) @@ -250,6 +257,57 @@ #define SCSI_NCR_SETUP_SETTLE_TIME (2) /* +** Bridge quirks work-around option defaulted to 1. +*/ +#ifndef SCSI_NCR_PCIQ_WORK_AROUND_OPT +#define SCSI_NCR_PCIQ_WORK_AROUND_OPT 1 +#endif + +/* +** Work-around common bridge misbehaviour. +** +** - Do not flush posted writes in the opposite +** direction on read. +** - May reorder DMA writes to memory. +** +** This option should not affect performances +** significantly, so it is the default. +*/ +#if SCSI_NCR_PCIQ_WORK_AROUND_OPT == 1 +#define SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM +#define SCSI_NCR_PCIQ_MAY_REORDER_WRITES +#define SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS + +/* +** Same as option 1, but also deal with +** misconfigured interrupts. +** +** - Edge triggerred instead of level sensitive. +** - No interrupt line connected. +** - IRQ number misconfigured. +** +** If no interrupt is delivered, the driver will +** catch the interrupt conditions 10 times per +** second. No need to say that this option is +** not recommended. +*/ +#elif SCSI_NCR_PCIQ_WORK_AROUND_OPT == 2 +#define SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM +#define SCSI_NCR_PCIQ_MAY_REORDER_WRITES +#define SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS +#define SCSI_NCR_PCIQ_BROKEN_INTR + +/* +** Some bridge designers decided to flush +** everything prior to deliver the interrupt. +** This option tries to deal with such a +** behaviour. +*/ +#elif SCSI_NCR_PCIQ_WORK_AROUND_OPT == 3 +#define SCSI_NCR_PCIQ_SYNC_ON_INTR +#endif + +/* ** Other parameters not configurable with "make config" ** Avoid to change these constants, unless you know what you are doing. */ @@ -258,20 +316,20 @@ #define SCSI_NCR_MAX_SCATTER (127) #define SCSI_NCR_MAX_TARGET (16) -/* No need to use a too large adapter queue */ -#if SCSI_NCR_MAX_TAGS <= 32 -#define SCSI_NCR_CAN_QUEUE (7*SCSI_NCR_MAX_TAGS) -#else -#define SCSI_NCR_CAN_QUEUE (250) -#endif - +/* +** Compute some desirable value for CAN_QUEUE +** and CMD_PER_LUN. +** The driver will use lower values if these +** ones appear to be too large. +*/ +#define SCSI_NCR_CAN_QUEUE (8*SCSI_NCR_MAX_TAGS + 2*SCSI_NCR_MAX_TARGET) #define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS) -#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER) +#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER) #define SCSI_NCR_TIMER_INTERVAL (HZ) #if 1 /* defined CONFIG_SCSI_MULTI_LUN */ -#define SCSI_NCR_MAX_LUN (8) +#define SCSI_NCR_MAX_LUN (16) #else #define SCSI_NCR_MAX_LUN (1) #endif @@ -279,6 +337,18 @@ #ifndef HOSTS_C /* +** These simple macros limit expression involving +** kernel time values (jiffies) to some that have +** chance not to be too much incorrect. :-) +*/ +#define ktime_get(o) (jiffies + (u_long) o) +#define ktime_exp(b) ((long)(jiffies) - (long)(b) >= 0) +#define ktime_dif(a, b) ((long)(a) - (long)(b)) +/* These ones are not used in this driver */ +#define ktime_add(a, o) ((a) + (u_long)(o)) +#define ktime_sub(a, o) ((a) - (u_long)(o)) + +/* ** IO functions definition for big/little endian support. ** For now, the NCR is only supported in little endian addressing mode, ** and big endian byte ordering is only supported for the PPC. @@ -384,6 +454,10 @@ #define PCI_DEVICE_ID_NCR_53C896 0xb #endif +#ifndef PCI_DEVICE_ID_NCR_53C895A +#define PCI_DEVICE_ID_NCR_53C895A 0x12 +#endif + /* ** NCR53C8XX devices features table. */ @@ -391,31 +465,33 @@ typedef struct { unsigned short device_id; unsigned short revision_id; char *name; - unsigned char burst_max; + unsigned char burst_max; /* log-base-2 of max burst */ unsigned char offset_max; unsigned char nr_divisor; unsigned int features; #define FE_LED0 (1<<0) -#define FE_WIDE (1<<1) -#define FE_ULTRA (1<<2) -#define FE_ULTRA2 (1<<3) -#define FE_DBLR (1<<4) -#define FE_QUAD (1<<5) -#define FE_ERL (1<<6) -#define FE_CLSE (1<<7) -#define FE_WRIE (1<<8) -#define FE_ERMP (1<<9) -#define FE_BOF (1<<10) -#define FE_DFS (1<<11) -#define FE_PFEN (1<<12) -#define FE_LDSTR (1<<13) -#define FE_RAM (1<<14) -#define FE_CLK80 (1<<15) -#define FE_RAM8K (1<<16) -#define FE_64BIT (1<<17) -#define FE_IO256 (1<<18) -#define FE_NOPM (1<<19) -#define FE_LEDC (1<<20) +#define FE_WIDE (1<<1) /* Wide data transfers */ +#define FE_ULTRA (1<<2) /* Ultra speed 20Mtrans/sec */ +#define FE_ULTRA2 (1<<3) /* Ultra 2 - 40 Mtrans/sec */ +#define FE_DBLR (1<<4) /* Clock doubler present */ +#define FE_QUAD (1<<5) /* Clock quadrupler present */ +#define FE_ERL (1<<6) /* Enable read line */ +#define FE_CLSE (1<<7) /* Cache line size enable */ +#define FE_WRIE (1<<8) /* Write & Invalidate enable */ +#define FE_ERMP (1<<9) /* Enable read multiple */ +#define FE_BOF (1<<10) /* Burst opcode fetch */ +#define FE_DFS (1<<11) /* DMA fifo size */ +#define FE_PFEN (1<<12) /* Prefetch enable */ +#define FE_LDSTR (1<<13) /* Load/Store supported */ +#define FE_RAM (1<<14) /* On chip RAM present */ +#define FE_CLK80 (1<<15) /* Board clock is 80 MHz */ +#define FE_RAM8K (1<<16) /* On chip RAM sized 8Kb */ +#define FE_64BIT (1<<17) /* Supports 64-bit addressing */ +#define FE_IO256 (1<<18) /* Requires full 256 bytes in PCI space */ +#define FE_NOPM (1<<19) /* Scripts handles phase mismatch */ +#define FE_LEDC (1<<20) /* Hardware control of LED */ +#define FE_DIFF (1<<21) /* Support Differential SCSI */ + #define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP) #define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80) #define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM) @@ -449,35 +525,53 @@ typedef struct { FE_WIDE|FE_ERL} \ , \ {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \ - FE_WIDE|FE_ERL|FE_BOF} \ + FE_WIDE|FE_ERL|FE_BOF|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6, 8, 4, \ - FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \ + FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \ FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN} \ , \ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 6, 16, 5, \ - FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|\ + FE_RAM|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C875, 0x0f, "875", 6, 16, 5, \ - FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_DIFF} \ + , \ + {PCI_DEVICE_ID_NCR_53C875, 0x1f, "876", 6, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_DIFF} \ + , \ + {PCI_DEVICE_ID_NCR_53C875, 0x2f, "875E", 6, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C875, 0xff, "876", 6, 16, 5, \ - FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5, \ - FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM} \ , \ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 6, 16, 5, \ - FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_DIFF} \ , \ {PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 6, 31, 7, \ - FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM} \ + , \ + {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 6, 31, 7, \ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC} \ , \ - {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 7, 31, 7, \ - FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|\ - FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC}\ + {PCI_DEVICE_ID_NCR_53C895A, 0xff, "895a", 6, 31, 7, \ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC} \ } /* @@ -494,10 +588,47 @@ typedef struct { PCI_DEVICE_ID_NCR_53C875J, \ PCI_DEVICE_ID_NCR_53C885, \ PCI_DEVICE_ID_NCR_53C895, \ - PCI_DEVICE_ID_NCR_53C896 \ + PCI_DEVICE_ID_NCR_53C896, \ + PCI_DEVICE_ID_NCR_53C895A \ } /* +** Driver setup structure. +** +** This structure is initialized from linux config options. +** It can be overridden at boot-up by the boot command line. +*/ +#define SCSI_NCR_MAX_EXCLUDES 8 +struct ncr_driver_setup { + u_char master_parity; + u_char scsi_parity; + u_char disconnection; + u_char special_features; + u_char ultra_scsi; + u_char force_sync_nego; + u_char reverse_probe; + u_char pci_fix_up; + u_char use_nvram; + u_char verbose; + u_char default_tags; + u_short default_sync; + u_short debug; + u_char burst_max; + u_char led_pin; + u_char max_wide; + u_char settle_delay; + u_char diff_support; + u_char irqm; + u_char bus_check; + u_char optimize; + u_char recovery; + u_char host_id; + u_short iarb; + u_int excludes[SCSI_NCR_MAX_EXCLUDES]; + char tag_ctrl[100]; +}; + +/* ** Initial setup. ** Can be overriden at startup by a command line. */ @@ -524,7 +655,9 @@ typedef struct { 0, \ 1, \ 0, \ - 0 \ + 0, \ + 255, \ + 0x00 \ } /* @@ -555,9 +688,139 @@ typedef struct { 1, \ 1, \ 0, \ - 0 \ + 0, \ + 255 \ } +#ifdef SCSI_NCR_NVRAM_SUPPORT +/* +** Symbios NvRAM data format +*/ +#define SYMBIOS_NVRAM_SIZE 368 +#define SYMBIOS_NVRAM_ADDRESS 0x100 + +struct Symbios_nvram { +/* Header 6 bytes */ + u_short type; /* 0x0000 */ + u_short byte_count; /* excluding header/trailer */ + u_short checksum; + +/* Controller set up 20 bytes */ + u_char v_major; /* 0x00 */ + u_char v_minor; /* 0x30 */ + u_int32 boot_crc; + u_short flags; +#define SYMBIOS_SCAM_ENABLE (1) +#define SYMBIOS_PARITY_ENABLE (1<<1) +#define SYMBIOS_VERBOSE_MSGS (1<<2) +#define SYMBIOS_CHS_MAPPING (1<<3) +#define SYMBIOS_NO_NVRAM (1<<3) /* ??? */ + u_short flags1; +#define SYMBIOS_SCAN_HI_LO (1) + u_short term_state; +#define SYMBIOS_TERM_CANT_PROGRAM (0) +#define SYMBIOS_TERM_ENABLED (1) +#define SYMBIOS_TERM_DISABLED (2) + u_short rmvbl_flags; +#define SYMBIOS_RMVBL_NO_SUPPORT (0) +#define SYMBIOS_RMVBL_BOOT_DEVICE (1) +#define SYMBIOS_RMVBL_MEDIA_INSTALLED (2) + u_char host_id; + u_char num_hba; /* 0x04 */ + u_char num_devices; /* 0x10 */ + u_char max_scam_devices; /* 0x04 */ + u_char num_valid_scam_devives; /* 0x00 */ + u_char rsvd; + +/* Boot order 14 bytes * 4 */ + struct Symbios_host{ + u_short type; /* 4:8xx / 0:nok */ + u_short device_id; /* PCI device id */ + u_short vendor_id; /* PCI vendor id */ + u_char bus_nr; /* PCI bus number */ + u_char device_fn; /* PCI device/function number << 3*/ + u_short word8; + u_short flags; +#define SYMBIOS_INIT_SCAN_AT_BOOT (1) + u_short io_port; /* PCI io_port address */ + } host[4]; + +/* Targets 8 bytes * 16 */ + struct Symbios_target { + u_char flags; +#define SYMBIOS_DISCONNECT_ENABLE (1) +#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1) +#define SYMBIOS_SCAN_LUNS (1<<2) +#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3) + u_char rsvd; + u_char bus_width; /* 0x08/0x10 */ + u_char sync_offset; + u_short sync_period; /* 4*period factor */ + u_short timeout; + } target[16]; +/* Scam table 8 bytes * 4 */ + struct Symbios_scam { + u_short id; + u_short method; +#define SYMBIOS_SCAM_DEFAULT_METHOD (0) +#define SYMBIOS_SCAM_DONT_ASSIGN (1) +#define SYMBIOS_SCAM_SET_SPECIFIC_ID (2) +#define SYMBIOS_SCAM_USE_ORDER_GIVEN (3) + u_short status; +#define SYMBIOS_SCAM_UNKNOWN (0) +#define SYMBIOS_SCAM_DEVICE_NOT_FOUND (1) +#define SYMBIOS_SCAM_ID_NOT_SET (2) +#define SYMBIOS_SCAM_ID_VALID (3) + u_char target_id; + u_char rsvd; + } scam[4]; + + u_char spare_devices[15*8]; + u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */ +}; +typedef struct Symbios_nvram Symbios_nvram; +typedef struct Symbios_host Symbios_host; +typedef struct Symbios_target Symbios_target; +typedef struct Symbios_scam Symbios_scam; + +/* +** Tekram NvRAM data format. +*/ +#define TEKRAM_NVRAM_SIZE 64 +#define TEKRAM_NVRAM_ADDRESS 0 + +struct Tekram_nvram { + struct Tekram_target { + u_char flags; +#define TEKRAM_PARITY_CHECK (1) +#define TEKRAM_SYNC_NEGO (1<<1) +#define TEKRAM_DISCONNECT_ENABLE (1<<2) +#define TEKRAM_START_CMD (1<<3) +#define TEKRAM_TAGGED_COMMANDS (1<<4) +#define TEKRAM_WIDE_NEGO (1<<5) + u_char sync_index; + u_short word2; + } target[16]; + u_char host_id; + u_char flags; +#define TEKRAM_MORE_THAN_2_DRIVES (1) +#define TEKRAM_DRIVES_SUP_1GB (1<<1) +#define TEKRAM_RESET_ON_POWER_ON (1<<2) +#define TEKRAM_ACTIVE_NEGATION (1<<3) +#define TEKRAM_IMMEDIATE_SEEK (1<<4) +#define TEKRAM_SCAN_LUNS (1<<5) +#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */ + u_char boot_delay_index; + u_char max_tags_index; + u_short flags1; +#define TEKRAM_F2_F6_ENABLED (1) + u_short spare[29]; +}; +typedef struct Tekram_nvram Tekram_nvram; +typedef struct Tekram_target Tekram_target; + +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + /**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/ /*----------------------------------------------------------------- @@ -573,6 +836,7 @@ struct ncr_reg { /*01*/ u_char nc_scntl1; /* no reset */ #define ISCON 0x10 /* connected to scsi */ #define CRST 0x08 /* force reset */ + #define IARB 0x02 /* immediate arbitration */ /*02*/ u_char nc_scntl2; /* no disconnect expected */ #define SDU 0x80 /* cmd: disconnect will raise error */ @@ -639,7 +903,10 @@ struct ncr_reg { #define DM 0x04 /* sta: DIFFSENS mismatch (895/6 only) */ #define LDSC 0x02 /* sta: disconnect & reconnect */ -/*10*/ u_int32 nc_dsa; /* --> Base page */ +/*10*/ u_char nc_dsa; /* --> Base page */ +/*11*/ u_char nc_dsa1; +/*12*/ u_char nc_dsa2; +/*13*/ u_char nc_dsa3; /*14*/ u_char nc_istat; /* --> Main Command and status */ #define CABRT 0x80 /* cmd: abort current operation */ @@ -696,7 +963,7 @@ struct ncr_reg { #define BOF 0x02 /* mod: burst op code fetch */ /*39*/ u_char nc_dien; -/*3a*/ u_char nc_dwt; +/*3a*/ u_char nc_sbr; /*3b*/ u_char nc_dcntl; /* --> Script execution control */ #define CLSE 0x80 /* mod: cache line size enable */ @@ -736,6 +1003,7 @@ struct ncr_reg { /*4c*/ u_char nc_stest0; /*4d*/ u_char nc_stest1; + #define SCLK 0x80 /* Use the PCI clock as SCSI clock */ #define DBLEN 0x08 /* clock doubler running */ #define DBLSEL 0x04 /* clock doubler selected */ @@ -794,8 +1062,15 @@ struct ncr_reg { /*c0*/ u_int32 nc_pmjad1; /* Phase Mismatch Jump Address 1 */ /*c4*/ u_int32 nc_pmjad2; /* Phase Mismatch Jump Address 2 */ -/*c8*/ u_int32 nc_rbc; /* Remaining Byte Count */ -/*cc*/ u_int32 nc_ua; /* Updated Address */ +/*c8*/ u_char nc_rbc; /* Remaining Byte Count */ +/*c9*/ u_char nc_rbc1; /* */ +/*ca*/ u_char nc_rbc2; /* */ +/*cb*/ u_char nc_rbc3; /* */ + +/*cc*/ u_char nc_ua; /* Updated Address */ +/*cd*/ u_char nc_ua1; /* */ +/*ce*/ u_char nc_ua2; /* */ +/*cf*/ u_char nc_ua3; /* */ /*d0*/ u_int32 nc_esa; /* Entry Storage Address */ /*d4*/ u_char nc_ia; /* Instruction Address */ /*d5*/ u_char nc_ia1; @@ -815,10 +1090,6 @@ struct ncr_reg { #define REGJ(p,r) (offsetof(struct ncr_reg, p ## r)) #define REG(r) REGJ (nc_, r) -#ifndef TARGET_MODE -#define TARGET_MODE 0 -#endif - typedef u_int32 ncrcmd; /*----------------------------------------------------------- @@ -855,9 +1126,15 @@ typedef u_int32 ncrcmd; **----------------------------------------------------------- */ -#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l)) -#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l)) -#define SCR_MOVE_TBL (0x18000000 ^ (TARGET_MODE << 1ul)) +#define OPC_MOVE 0x08000000 + +#define SCR_MOVE_ABS(l) ((0x00000000 | OPC_MOVE) | (l)) +#define SCR_MOVE_IND(l) ((0x20000000 | OPC_MOVE) | (l)) +#define SCR_MOVE_TBL (0x10000000 | OPC_MOVE) + +#define SCR_CHMOV_ABS(l) ((0x00000000) | (l)) +#define SCR_CHMOV_IND(l) ((0x20000000) | (l)) +#define SCR_CHMOV_TBL (0x10000000) struct scr_tblmove { u_int32 size; @@ -870,7 +1147,7 @@ struct scr_tblmove { ** **----------------------------------------------------------- ** -** SEL_ABS | SCR_ID (0..7) [ | REL_JMP] +** SEL_ABS | SCR_ID (0..15) [ | REL_JMP] ** <<alternate_address>> ** ** SEL_TBL | << dnad_offset>> [ | REL_JMP] @@ -1104,7 +1381,7 @@ struct scr_tblsel { ** Conditions: ** WHEN (phase) ** IF (phase) -** CARRY +** CARRYSET ** DATA (data, mask) ** **----------------------------------------------------------- diff --git a/drivers/sgi/char/shmiq.c b/drivers/sgi/char/shmiq.c index 641467deb..3551de64b 100644 --- a/drivers/sgi/char/shmiq.c +++ b/drivers/sgi/char/shmiq.c @@ -1,4 +1,4 @@ -/* $Id: shmiq.c,v 1.13 1999/09/28 22:26:59 ralf Exp $ +/* $Id: shmiq.c,v 1.14 1999/10/09 00:01:31 ralf Exp $ * * shmiq.c: shared memory input queue driver * written 1997 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -118,7 +118,7 @@ shmiq_push_event (struct shmqevent *e) s->tail = tail_next; shmiqs [device].tail = tail_next; if (shmiqs [device].fasync) - kill_fasync (shmiqs [device].fasync, SIGIO); + kill_fasync (shmiqs [device].fasync, SIGIO, POLL_IN); wake_up_interruptible (&shmiqs [device].proc_list); } diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index 90ccaf342..ec41d0b6a 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -58,6 +58,8 @@ * replaced current->state = x with set_current_state(x) * 03.09.99 0.8 change read semantics for MIDI to match * OSS more closely; remove possible wakeup race + * 07.10.99 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer * */ @@ -108,6 +110,7 @@ #define MPUBASE_EXTENT 4 #define GPBASE_EXTENT 4 +#define FMSYNTH_EXTENT 4 /* MIDI buffer sizes */ @@ -207,7 +210,7 @@ extern inline void write_seq(struct solo1_state *s, unsigned char data) { int i; unsigned long flags; - + /* the __cli stunt is to send the data within the command window */ for (i = 0; i < 0xffff; i++) { __save_flags(flags); @@ -219,6 +222,8 @@ extern inline void write_seq(struct solo1_state *s, unsigned char data) } __restore_flags(flags); } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); } extern inline int read_seq(struct solo1_state *s, unsigned char *data) @@ -232,6 +237,7 @@ extern inline int read_seq(struct solo1_state *s, unsigned char *data) *data = inb(s->sbbase+0xa); return 1; } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); return 0; } @@ -1993,6 +1999,12 @@ static int solo1_dmfm_open(struct inode *inode, struct file *file) return -ERESTARTSYS; down(&s->open_sem); } + if (check_region(s->sbbase, FMSYNTH_EXTENT)) { + up(&s->open_sem); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1"); /* init the stuff */ outb(1, s->sbbase); outb(0x20, s->sbbase+1); /* enable waveforms */ @@ -2020,6 +2032,7 @@ static int solo1_dmfm_release(struct inode *inode, struct file *file) outb(regb, s->sbbase+2); outb(0, s->sbbase+3); } + release_region(s->sbbase, FMSYNTH_EXTENT); up(&s->open_sem); wake_up(&s->open_wait); MOD_DEC_USE_COUNT; @@ -2113,14 +2126,14 @@ static int __init init_solo1(void) s->gpbase = RSRCADDRESS(pcidev, 4); s->irq = pcidev->irq; if (check_region(s->iobase, IOBASE_EXTENT) || - check_region(s->sbbase+4, SBBASE_EXTENT-4) || + check_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT) || check_region(s->ddmabase, DDMABASE_EXTENT) || check_region(s->mpubase, MPUBASE_EXTENT)) { printk(KERN_ERR "solo1: io ports in use\n"); goto err_region; } request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1"); - request_region(s->sbbase+4, SBBASE_EXTENT-4, "ESS Solo1"); /* allow OPL3 synth module */ + request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1"); request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1"); request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1"); if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) { @@ -2147,6 +2160,10 @@ static int __init init_solo1(void) if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) goto err_dev4; /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + goto err; + } outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ /* initialize mixer regs */ @@ -2181,6 +2198,8 @@ static int __init init_solo1(void) index++; continue; + err: + unregister_sound_dsp(s->dev_dmfm); err_dev4: unregister_sound_dsp(s->dev_midi); err_dev3: @@ -2192,7 +2211,7 @@ static int __init init_solo1(void) free_irq(s->irq, s); err_irq: release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); err_region: @@ -2222,7 +2241,7 @@ static void __exit cleanup_solo1(void) pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */ free_irq(s->irq, s); release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); unregister_sound_dsp(s->dev_audio); diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 04e688614..19a76c0bd 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -10,39 +10,43 @@ comment 'USB drivers - not for the faint of heart' tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB if [ ! "$CONFIG_USB" = "n" ]; then - dep_tristate 'UHCI (intel PIIX4 and others) support' CONFIG_USB_UHCI \ +comment 'USB Controllers' + dep_tristate ' UHCI (Intel PIIX4 and others) support' CONFIG_USB_UHCI \ $CONFIG_USB - dep_tristate 'OHCI (compaq and some others) support' CONFIG_USB_OHCI \ + dep_tristate ' OHCI (Compaq and some others) support' CONFIG_USB_OHCI \ $CONFIG_USB - if [ "$CONFIG_USB_OHCI" != "n" ]; then - bool ' Enable tons of OHCI debugging output' CONFIG_USB_OHCI_DEBUG - fi - dep_tristate 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support' \ + if [ "$CONFIG_USB_OHCI" != "n" ]; then + bool ' Enable tons of OHCI debugging output' CONFIG_USB_OHCI_DEBUG + fi + dep_tristate ' OHCI-HCD (other OHCI opt. Virt. Root Hub) support' \ CONFIG_USB_OHCI_HCD $CONFIG_USB - if [ "$CONFIG_USB_OHCI_HCD" != "n" ]; then - bool ' OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB - fi + if [ "$CONFIG_USB_OHCI_HCD" != "n" ]; then + bool ' OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB + fi - bool 'Enable lots of ISOC debugging output' CONFIG_USB_DEBUG_ISOC +comment 'Miscellaneous USB options' + bool ' Enable lots of ISOC debugging output' CONFIG_USB_DEBUG_ISOC + if [ "$CONFIG_PROC_FS" != "n" ]; then + bool ' Preliminary /proc/bus/usb support' CONFIG_USB_PROC + fi + dep_tristate ' EZUSB Firmware downloader' CONFIG_USB_EZUSB $CONFIG_USB - dep_tristate 'USB hub support' CONFIG_USB_HUB $CONFIG_USB - dep_tristate 'USB mouse support' CONFIG_USB_MOUSE $CONFIG_USB - dep_tristate 'USB HP scanner support' CONFIG_USB_HP_SCANNER $CONFIG_USB - dep_tristate 'USB keyboard support' CONFIG_USB_KBD $CONFIG_USB - dep_tristate 'USB audio parsing support' CONFIG_USB_AUDIO $CONFIG_USB - dep_tristate 'USB Communications Device Class (ACM) support' CONFIG_USB_ACM $CONFIG_USB - dep_tristate 'USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB - dep_tristate 'USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB - dep_tristate 'USB SCSI Support' CONFIG_USB_SCSI $CONFIG_USB - if [ "$CONFIG_USB_SCSI" != "n" ]; then - dep_tristate ' USB SCSI verbose debug' CONFIG_USB_SCSI_DEBUG $CONFIG_USB_SCSI - fi - dep_tristate 'EZUSB Firmware downloader' CONFIG_USB_EZUSB $CONFIG_USB - dep_tristate 'USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT - if [ "$CONFIG_PROC_FS" != "n" ]; then - bool 'Preliminary /proc/bus/usb support' CONFIG_USB_PROC - fi +comment 'USB Devices' + dep_tristate ' USB hub support (Required!)' CONFIG_USB_HUB $CONFIG_USB + dep_tristate ' USB mouse support' CONFIG_USB_MOUSE $CONFIG_USB + dep_tristate ' USB HP scanner support' CONFIG_USB_HP_SCANNER $CONFIG_USB + dep_tristate ' USB keyboard support' CONFIG_USB_KBD $CONFIG_USB + dep_tristate ' USB audio parsing support' CONFIG_USB_AUDIO $CONFIG_USB + dep_tristate ' USB Communications Device Class (ACM) support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + dep_tristate ' USB Belkin and Peracom serial support' CONFIG_USB_SERIAL $CONFIG_USB + dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB + dep_tristate ' USB SCSI Support' CONFIG_USB_SCSI $CONFIG_USB + if [ "$CONFIG_USB_SCSI" != "n" ]; then + bool ' USB SCSI verbose debug' CONFIG_USB_SCSI_DEBUG + fi + dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT fi endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 0531c15bd..8d6006493 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -97,6 +97,15 @@ ifeq ($(CONFIG_USB_PRINTER),m) MIX_OBJS += printer.o endif +ifeq ($(CONFIG_USB_SERIAL),y) + L_OBJS += serial.o +endif + +ifeq ($(CONFIG_USB_SERIAL),m) + M_OBJS += serial.o + MIX_OBJS += serial.o +endif + ifeq ($(CONFIG_USB_KBD),y) L_OBJS += keyboard.o keymap.o endif diff --git a/drivers/usb/README.ohci b/drivers/usb/README.ohci index 921d9a9a3..22420779f 100644 --- a/drivers/usb/README.ohci +++ b/drivers/usb/README.ohci @@ -1,5 +1,17 @@ [This is the readme for ohci.c, ohci-debug.c and ohci.h] +September 05, 1999 17:03:21 + +I haven't updated this file in a while. I have started merging good +ideas from the ohci-hcd driver into this code as well as simplifying +the code where I can. Isochronous transfers still need to be ported +over from ohci-hcd. Large control and bulk transfers should be +working much better now. + +A big thanks goes to 3Com and APC for donating me a modem and UPS to +test with! They aren't working with OHCI quite yet but we hope to +change that soon. + June 23, 1999 00:31:20 PST I now have bulk support in a reasonably working state. The only diff --git a/drivers/usb/README.serial b/drivers/usb/README.serial new file mode 100644 index 000000000..e67fb748a --- /dev/null +++ b/drivers/usb/README.serial @@ -0,0 +1,35 @@ +This serial driver currently only works with the Belkin and Peracom USB +Serial devices. It should also work for the Etek converter, but I do +not know the vendor id, and device id of that device (if anyone does, +please let me know.) + +The driver can handle enumerating the device, and sending and receiving +data from the converter. However, since I do not have a spec for this +device, and the raw dumps from the Win98 driver are confusing, no control +signals are handled, and the data will most likely come through on a baud +rate that you are not expecting. + +But I am working on figuring the control settings out. This release is to +let others try it out, and give some feedback. + +The major number that the driver uses is 240 (in the local/experimental +range.) It will stay there until some agreements are reached on how to +handle the configuration problem that USB provides. + +To use the driver, create the following nodes: +mknod /dev/ttyUSB0 c 240 0 +mknod /dev/ttyUSB1 c 240 1 +mknod /dev/ttyUSB2 c 240 2 +mknod /dev/ttyUSB3 c 240 3 + +then plug in a device and use your friendly terminal program to see what +happens. + +If anyone has any problems getting the device to enumerate, or data to +flow through it, please contact me. + + + +greg k-h +greg@kroah.com + diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 6560331fc..61830f720 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -1,7 +1,11 @@ /* * USB Abstract Control Model based on Brad Keryan's USB busmouse driver * - * Armin Fuerst 5/8/1999 + * Armin Fuerst 5/8/1999 <armin.please@put.your.email.here.!!!!> + * + * version 0.8: Fixed endianity bug, some cleanups. I really hate to have + * half of driver in form if (...) { info("x"); return y; } + * Pavel Machek <pavel@suse.de> * * version 0.7: Added usb flow control. Fixed bug in uhci.c (what idiot * wrote this code? ...Oops that was me). Fixed module cleanup. Did some @@ -63,8 +67,8 @@ #define NR_PORTS 3 #define ACM_MAJOR 166 -#define info(message); printk(message); -//#define info(message); +//#define info(message...); printk(message); +#define info(message...); #define CTRL_STAT_DTR 1 #define CTRL_STAT_RTS 2 @@ -94,6 +98,7 @@ struct acm_state { unsigned ctrlinterval; //interval to poll from device }; +#define ACM_READY (acm->present && acm->active) //functions for various ACM requests @@ -108,7 +113,7 @@ void Set_Control_Line_Status (unsigned int status,struct acm_state *acm) dr.value = status; dr.index = 0; dr.length = 0; - acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev,0), &dr, NULL, 0); + acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev,0), &dr, NULL, 0, HZ); acm->ctrlstate=status; } @@ -124,7 +129,7 @@ void Set_Line_Coding (unsigned int coding,struct acm_state *acm) dr.value = coding; dr.index = 0; dr.length = 0; - acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev,0), &dr, NULL, 0); + acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev,0), &dr, NULL, 0, HZ); acm->linecoding=coding; } @@ -139,20 +144,16 @@ static int acm_irq(int state, void *__buffer, int count, void *dev_id) info("ACM_USB_IRQ\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); + if (!acm->present) return 0; - } - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); + if (!acm->active) return 1; - } dr=__buffer; data=__buffer; data+=sizeof(dr); -#if 1 +#if 0 printk("reqtype: %02X\n",dr->requesttype); printk("request: %02X\n",dr->request); printk("wValue: %02X\n",dr->value); @@ -161,31 +162,26 @@ static int acm_irq(int state, void *__buffer, int count, void *dev_id) #endif switch(dr->request) { - //Network connection - case 0x00: - printk("Network connection: "); - if (dr->request==0) info("disconnected\n"); - if (dr->request==1) info("connected\n"); - break; + case 0x00: /* Network connection */ + printk("Network connection: "); + if (dr->request==0) printk("disconnected\n"); + if (dr->request==1) printk("connected\n"); + break; - //Response available - case 0x01: - printk("Response available\n"); - break; + case 0x01: /* Response available */ + printk("Response available\n"); + break; - //Set serial line state - case 0x20: - printk("Set serial control line state\n"); - if ((dr->index==1)&&(dr->length==2)) { - acm->ctrlstate=* ((unsigned short int *)data); - printk("Serstate: %02X\n",acm->ctrlstate); - } - break; + case 0x20: /* Set serial line state */ + printk("acm.c: Set serial control line state\n"); + if ((dr->index==1)&&(dr->length==2)) { + acm->ctrlstate= data[0] || (data[1] << 16); + printk("Serstate: %02X\n",acm->ctrlstate); + } + break; } - //info("Done\n"); - //Continue transfer - return 1; + return 1; /* Continue transfer */ } static int acm_read_irq(int state, void *__buffer, int count, void *dev_id) @@ -195,29 +191,20 @@ static int acm_read_irq(int state, void *__buffer, int count, void *dev_id) unsigned char* data=__buffer; int i; - info("ACM_READ_IRQ\n"); - - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - //Stop transfer - return 0; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); - //Stop transfer - return 0; + info("ACM_READ_IRQ: state %d, %d bytes\n", state, count); + if (state) { + printk( "acm_read_irq: strange state received: %x\n", state ); + return 1; } + + if (!ACM_READY) + return 0; /* stop transfer */ -// printk("%d %s\n",count,data); - for (i=0;i<count;i++) { - tty_insert_flip_char(tty,data[i],0); - } + for (i=0;i<count;i++) + tty_insert_flip_char(tty,data[i],0); tty_flip_buffer_push(tty); - - //info("Done\n"); - //Continue transfer - return 1; + + return 1; /* continue transfer */ } static int acm_write_irq(int state, void *__buffer, int count, void *dev_id) @@ -227,16 +214,8 @@ static int acm_write_irq(int state, void *__buffer, int count, void *dev_id) info("ACM_WRITE_IRQ\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - //Stop transfer - return 0; - } - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); - //Stop transfer - return 0; - } + if (!ACM_READY) + return 0; /* stop transfer */ usb_terminate_bulk(acm->dev, acm->writetransfer); acm->writing=0; @@ -244,9 +223,7 @@ static int acm_write_irq(int state, void *__buffer, int count, void *dev_id) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); - //info("Done\n"); - //Stop transfer - return 0; + return 0; /* stop tranfer */ } /*TTY STUFF*/ @@ -260,22 +237,16 @@ static int rs_open(struct tty_struct *tty, struct file * filp) tty->driver_data=acm=&acm_state_table[MINOR(tty->device)-tty->driver.minor_start]; acm->tty=tty; - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); + if (!acm->present) return -EINVAL; - } - if (acm->active) { - info ("ACM DEVICE ALREADY OPEN\n"); - return -EINVAL; - } - acm->active=1; + if (acm->active++) + return 0; - /*Start reading from the device*/ + /* Start reading from the device */ ret = usb_request_irq(acm->dev,acm->ctrlpipe, acm_irq, acm->ctrlinterval, acm, &acm->ctrltransfer); - if (ret) { + if (ret) printk (KERN_WARNING "usb-acm: usb_request_irq failed (0x%x)\n", ret); - } acm->reading=1; acm->readtransfer=usb_request_bulk(acm->dev,acm->readpipe, acm_read_irq, acm->readbuffer, acm->readsize, acm); @@ -289,15 +260,11 @@ static void rs_close(struct tty_struct *tty, struct file * filp) struct acm_state *acm = (struct acm_state *) tty->driver_data; info("rs_close\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); + if (!acm->present) return; - } - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); + if (--acm->active) return; - } Set_Control_Line_Status (0, acm); @@ -309,9 +276,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) usb_terminate_bulk(acm->dev, acm->readtransfer); acm->reading=0; } -// usb_release_irq(acm->dev,acm->ctrltransfer, acm->ctrlpipe); - - acm->active=0; +// usb_release_irq(acm->dev,acm->ctrltransfer, acm->ctrlpipe); } static int rs_write(struct tty_struct * tty, int from_user, @@ -322,15 +287,8 @@ static int rs_write(struct tty_struct * tty, int from_user, info("rs_write\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - return -EINVAL; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); + if (!ACM_READY) return -EINVAL; - } if (acm->writing) { info ("already writing\n"); @@ -339,14 +297,8 @@ static int rs_write(struct tty_struct * tty, int from_user, written=(count>acm->writesize) ? acm->writesize : count; - if (from_user) { - //info("fromuser\n"); - copy_from_user(acm->writebuffer,buf,written); - } - else { - //info("notfromuser\n"); - memcpy(acm->writebuffer,buf,written); - } + if (from_user) copy_from_user(acm->writebuffer,buf,written); + else memcpy(acm->writebuffer,buf,written); //start the transfer acm->writing=1; @@ -359,18 +311,8 @@ static void rs_put_char(struct tty_struct *tty, unsigned char ch) { struct acm_state *acm = (struct acm_state *) tty->driver_data; - info("rs_put_char\n"); - - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - return; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); - return; - } -// printk("%c\n",ch); + printk( "acm: rs_put_char: Who called this unsupported routine?\n" ); + BUG(); } static int rs_write_room(struct tty_struct *tty) @@ -379,42 +321,22 @@ static int rs_write_room(struct tty_struct *tty) info("rs_write_room\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); + if (!ACM_READY) return -EINVAL; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); - return -EINVAL; - } - if (acm->writing) { - return 0; - } - return acm->writesize; + return acm->writing ? 0 : acm->writesize; } static int rs_chars_in_buffer(struct tty_struct *tty) { struct acm_state *acm = (struct acm_state *) tty->driver_data; - info("rs_chars_in_buffer\n"); +// info("rs_chars_in_buffer\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - return -EINVAL; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); + if (!ACM_READY) return -EINVAL; - } - if (acm->writing) { - return acm->writesize; - } - return 0; + return acm->writing ? acm->writesize : 0; } static void rs_throttle(struct tty_struct * tty) @@ -423,21 +345,12 @@ static void rs_throttle(struct tty_struct * tty) info("rs_throttle\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); - return; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); + if (!ACM_READY) return; - } - /* if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); */ - if (tty->termios->c_cflag & CRTSCTS) Set_Control_Line_Status (acm->ctrlstate & ~CTRL_STAT_RTS, acm); } @@ -448,21 +361,12 @@ static void rs_unthrottle(struct tty_struct * tty) info("rs_unthrottle\n"); - if (!acm->present) { - info("NO ACM DEVICE REGISTERED\n"); + if (!ACM_READY) return; - } - - if (!acm->active) { - info ("ACM DEVICE NOT OPEN\n"); - return; - } - /* if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); */ - if (tty->termios->c_cflag & CRTSCTS) Set_Control_Line_Status (acm->ctrlstate | CTRL_STAT_RTS, acm); } @@ -521,7 +425,8 @@ static int acm_probe(struct usb_device *dev) interface->bNumEndpoints != 2) continue; - if ((endpoint->bEndpointAddress & 0x80) == 0x80) + endpoint = &interface->endpoint[0]; + if ((endpoint->bEndpointAddress & 0x80) != 0x80) swapped = 1; /*With a bulk input */ @@ -578,10 +483,8 @@ static void acm_disconnect(struct usb_device *dev) info("acm_disconnect\n"); - if (!acm->present) { - printk("device not present\n"); + if (!acm->present) return; - } printk("disconnecting\n"); @@ -593,7 +496,7 @@ static void acm_disconnect(struct usb_device *dev) usb_terminate_bulk(acm->dev, acm->readtransfer); acm->reading=0; } -// usb_release_irq(acm->dev,acm->ctrltransfer, acm->ctrlpipe); + usb_release_irq(acm->dev,acm->ctrltransfer, acm->ctrlpipe); //BUG: What to do if a device is open?? Notify process or not allow cleanup? acm->active=0; acm->present=0; @@ -643,9 +546,9 @@ int usb_acm_init(void) acm_tty_driver.open = rs_open; acm_tty_driver.close = rs_close; acm_tty_driver.write = rs_write; - acm_tty_driver.put_char = rs_put_char; //FUCKIN BUG IN DOKU!!! + acm_tty_driver.put_char = rs_put_char; acm_tty_driver.flush_chars = NULL; //rs_flush_chars; - acm_tty_driver.write_room = rs_write_room; //ANBOTHER FUCKIN BUG!! + acm_tty_driver.write_room = rs_write_room; acm_tty_driver.ioctl = NULL; //rs_ioctl; acm_tty_driver.set_termios = NULL; //rs_set_termios; acm_tty_driver.set_ldisc = NULL; diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 8e25ae40b..291f424dc 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -1,25 +1,2540 @@ +/*****************************************************************************/ + +/* + * audio.c -- USB Audio Class driver + * + * Copyright (C) 1999 + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * + * 1999-09-07: Alan Cox + * Parsing Audio descriptor patch + * 1999-09-08: Thomas Sailer + * Added OSS compatible data io functions; both parts of the + * driver remain to be glued together + * 1999-09-10: Thomas Sailer + * Beautified the driver. Added sample format conversions. + * Still not properly glued with the parsing code. + * The parsing code seems to have its problems btw, + * Since it parses all available configs but doesn't + * store which iface/altsetting belongs to which config. + * 1999-09-20: Thomas Sailer + * Threw out Alan's parsing code and implemented my own one. + * You cannot reasonnably linearly parse audio descriptors, + * especially the AudioClass descriptors have to be considered + * pointer lists. Mixer parsing untested, due to lack of device. + * First stab at synch pipe implementation, the Dallas USB DAC + * wants to use an Asynch out pipe. usb_audio_state now basically + * only contains lists of mixer and wave devices. We can therefore + * now have multiple mixer/wave devices per USB device. + * + */ + +/* + * Strategy: + * + * Alan Cox and Thomas Sailer are starting to dig at opposite ends and + * are hoping to meet in the middle, just like tunnel diggers :) + * Alan tackles the descriptor parsing, Thomas the actual data IO and the + * OSS compatible interface. + * + * Data IO implementation issues + * + * A mmap'able ring buffer per direction is implemented, because + * almost every OSS app expects it. It is however impractical to + * transmit/receive USB data directly into and out of the ring buffer, + * due to alignment and synchronisation issues. Instead, the ring buffer + * feeds a constant time delay line that handles the USB issues. + * + * Now we first try to find an alternate setting that exactly matches + * the sample format requested by the user. If we find one, we do not + * need to perform any sample rate conversions. If there is no matching + * altsetting, we choose the closest one and perform sample format + * conversions. We never do sample rate conversion; these are too + * expensive to be performed in the kernel. + * + * Current status: + * - The IO code seems to work a couple of frames, but then gets + * UHCI into a "complaining" mode, i.e. uhci won't work again until + * removed and reloaded, it will not even notice disconnect/reconnect + * events. + * It seems to work more stably on OHCI-HCD. + * + * Generally: Due to the brokenness of the Audio Class spec + * it seems generally impossible to write a generic Audio Class driver, + * so a reasonable driver should implement the features that are actually + * used. + * + * Parsing implementation issues + * + * One cannot reasonably parse the AudioClass descriptors linearly. + * Therefore the current implementation features routines to look + * for a specific descriptor in the descriptor list. + * + * How does the parsing work? First, all interfaces are searched + * for an AudioControl class interface. If found, the config descriptor + * that belongs to the current configuration is fetched from the device. + * Then the HEADER descriptor is fetched. It contains a list of + * all AudioStreaming and MIDIStreaming devices. This list is then walked, + * and all AudioStreaming interfaces are classified into input and output + * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming + * is currently not supported). The input & output list is then used + * to group inputs and outputs together and issued pairwise to the + * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors + * are walked and issued to the mixer construction routine. + * + * The AudioStreaming parser simply enumerates all altsettings belonging + * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE + * class specific descriptors to extract the sample format/sample rate + * data. Only sample format types PCM and PCM8 are supported right now, and + * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to + * be the first endpoint of the interface, and the optional synchronisation + * isochronous endpoint the second one. + * + * Mixer construction works as follows: The various TERMINAL and UNIT + * descriptors span a tree from the root (OUTPUT_TERMINAL) through the + * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk + * that tree in a depth first manner. FEATURE_UNITs may contribute volume, + * bass and treble sliders to the mixer, MIXER_UNITs volume sliders. + * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic + * to determine "meaningful" OSS slider numbers, however we will see + * how well this works in practice. Other features are not used at the + * moment, they seem less often used. Also, it seems difficult at least + * to construct recording source switches from SELECTOR_UNITs, but + * since there are not many USB ADC's available, we leave that for later. + */ + +/*****************************************************************************/ + #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/module.h> +#include <linux/sound.h> +#include <linux/soundcard.h> +#include <linux/list.h> +#include <linux/vmalloc.h> +#include <linux/wrapper.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/bitops.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <linux/spinlock.h> #include "usb.h" +#include "audio.h" + #define AUDIO_DEBUG 1 -static int usb_audio_probe(struct usb_device *dev); -static void usb_audio_disconnect(struct usb_device *dev); -static LIST_HEAD(usb_audio_list); +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +/* + * Linked list of all audio devices... + */ +static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs); +static DECLARE_MUTEX(open_sem); + +/* + * wait queue for processes wanting to open an USB audio device + */ +static DECLARE_WAIT_QUEUE_HEAD(open_wait); + + +#define MAXFORMATS MAX_ALT +#define DMABUFSHIFT 17 /* 128k worth of DMA buffer */ +#define NRSGBUF (1U<<(DMABUFSHIFT-PAGE_SHIFT)) + +/* + * This influences: + * - Latency + * - Interrupt rate + * - Synchronisation behaviour + * Don't touch this if you don't understand all of the above. + */ +#define DESCFRAMES 4 + +#define MIXFLG_STEREOIN 1 +#define MIXFLG_STEREOOUT 2 + +struct mixerchannel { + __u16 value; + __u16 osschannel; /* number of the OSS channel */ + __s16 minval, maxval; + __u8 unitid; + __u8 selector; + __u8 chnum; + __u8 flags; +}; + +struct audioformat { + unsigned int format; + unsigned int sratelo; + unsigned int sratehi; + unsigned char altsetting; +}; + +struct dmabuf { + /* buffer data format */ + unsigned int format; + unsigned int srate; + /* physical buffer */ + unsigned char *sgbuf[NRSGBUF]; + unsigned bufsize; + unsigned numfrag; + unsigned fragshift; + unsigned wrptr, rdptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct usb_audio_state; + +#define FLG_NEXTID 1 +#define FLG_ID0RUNNING 2 +#define FLG_ID1RUNNING 4 +#define FLG_SYNCNEXTID 8 +#define FLG_SYNC0RUNNING 16 +#define FLG_SYNC1RUNNING 32 +#define FLG_RUNNING 64 -struct usb_audio { - struct usb_device *dev; +struct usb_audiodev { struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_audio; + + /* wave stuff */ + mode_t open_mode; + spinlock_t lock; /* DMA buffer access spinlock */ + + struct usbin { + unsigned int interface; /* Interface number */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */ + unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct usb_isoc_desc *dataiso[2]; /* ISO descriptors for the data endpoint */ + unsigned char *data[2]; /* data pages associated with the ISO descriptors */ + + struct usb_isoc_desc *synciso[2]; /* ISO sync pipe descriptor if needed */ + unsigned char *syncdata[2]; /* data page for sync data */ + + struct dmabuf dma; + } usbin; + + struct usbout { + unsigned int interface; /* Interface number */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */ + unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct usb_isoc_desc *dataiso[2]; /* ISO descriptors for the data endpoint */ + unsigned char *data[2]; /* data pages associated with the ISO descriptors */ + + struct usb_isoc_desc *synciso[2]; /* ISO sync pipe descriptor if needed */ + unsigned char *syncdata[2]; /* data page for sync data */ + + struct dmabuf dma; + } usbout; - void *irq_handle; - unsigned int irqpipe; + + unsigned int numfmtin, numfmtout; + struct audioformat fmtin[MAXFORMATS]; + struct audioformat fmtout[MAXFORMATS]; +}; + +struct usb_mixerdev { + struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_mixer; + + unsigned char iface; /* interface number of the AudioControl interface */ + + /* USB format descriptions */ + unsigned int numch, modcnt; + + /* mixch is last and gets allocated dynamically */ + struct mixerchannel ch[0]; }; +struct usb_audio_state { + struct list_head audiodev; + + /* USB device */ + struct usb_device *usbdev; + + struct list_head audiolist; + struct list_head mixerlist; + + unsigned count; /* usage counter; NOTE: the usb stack is also considered a user */ +}; + +/* private audio format extensions */ +#define AFMT_STEREO 0x80000000 +#define AFMT_ISSTEREO(x) ((x) & AFMT_STEREO) +#define AFMT_IS16BIT(x) ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0)) +#define AFMT_BYTES(x) (1<<AFMT_BYTESSHFIT(x)) + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +/* + * OSS compatible ring buffer management. The ring buffer may be mmap'ed into + * an application address space. + * + * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so + * we now use an array of pointers to a single page each. This saves us the + * kernel page table manipulations, but we have to do a page table alike mechanism + * (though only one indirection) in software. + */ + +static void dmabuf_release(struct dmabuf *db) +{ + unsigned int nr; + void *p; + + for(nr = 0; nr < NRSGBUF; nr++) { + if (!(p = db->sgbuf[nr])) + continue; + mem_map_unreserve(MAP_NR(p)); + free_page((unsigned long)p); + db->sgbuf[nr] = NULL; + } + db->mapped = db->ready = 0; +} + +static int dmabuf_init(struct dmabuf *db) +{ + unsigned int nr, bytepersec, bufs; + void *p; + + /* initialize some fields */ + db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; + /* calculate required buffer size */ + bytepersec = db->srate << AFMT_BYTESSHIFT(db->format); + bufs = 1U << DMABUFSHIFT; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->dmasize = db->numfrag << db->fragshift; + for(nr = 0; nr < NRSGBUF; nr++) { + if (!db->sgbuf[nr]) { + p = (void *)get_free_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + db->sgbuf[nr] = p; + mem_map_reserve(MAP_NR(p)); + } + memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE); + if ((nr << PAGE_SHIFT) >= db->dmasize) + break; + } + db->bufsize = nr << PAGE_SHIFT; + db->ready = 1; + printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " + "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d\n", + bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, + db->numfrag, db->dmasize, db->bufsize); + return 0; +} + +static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) +{ + unsigned int nr; + + if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) + return -EINVAL; + size >>= PAGE_SHIFT; + for(nr = 0; nr < size; nr++) + if (!db->sgbuf[nr]) + return -EINVAL; + db->mapped = 1; + for(nr = 0; nr < size; nr++) { + if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), PAGE_SIZE, prot)) + return -EAGAIN; + start += PAGE_SIZE; + } + return 0; +} + +static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->wrptr; + if (pgrem > rem) + pgrem = rem; + memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->wrptr += pgrem; + if (db->wrptr >= db->dmasize) + db->wrptr = 0; + } +} + +static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->rdptr; + if (pgrem > rem) + pgrem = rem; + memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->rdptr += pgrem; + if (db->rdptr >= db->dmasize) + db->rdptr = 0; + } +} + +static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_from_user_ret((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} + +static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_to_user_ret(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} + +/* --------------------------------------------------------------------- */ +/* + * USB I/O code. We do sample format conversion if necessary + */ + +static void usbin_stop(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + while (i & (FLG_ID0RUNNING|FLG_ID1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + } + if (u->dataiso[0]) + usb_free_isoc(u->dataiso[0]); + if (u->dataiso[1]) + usb_free_isoc(u->dataiso[1]); + if (u->synciso[0]) + usb_free_isoc(u->synciso[0]); + if (u->synciso[1]) + usb_free_isoc(u->synciso[1]); + u->dataiso[0] = u->dataiso[1] = u->synciso[0] = u->synciso[1] = NULL; +} + +static void usbin_release(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + + usbin_stop(as); + if (u->data[0]) + free_page((unsigned long)u->data[0]); + if (u->data[1]) + free_page((unsigned long)u->data[1]); + if (u->syncdata[0]) + free_page((unsigned long)u->syncdata[0]); + if (u->syncdata[1]) + free_page((unsigned long)u->syncdata[1]); + u->data[0] = u->data[1] = u->syncdata[0] = u->syncdata[1] = NULL; +} + +static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh, cnt, i; + __s16 *sp, *sp2, s; + unsigned char *bp; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + cnt = scnt; + if (AFMT_ISSTEREO(u->format)) + cnt <<= 1; + sp = tmp.s + cnt; + switch (u->format & ~AFMT_STEREO) { + case AFMT_U8: + for (bp = buffer+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = (*bp ^ 0x80) << 8; + } + break; + + case AFMT_S8: + for (bp = buffer+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = *bp << 8; + } + break; + + case AFMT_U16_LE: + for (bp = buffer+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; + } + break; + + case AFMT_U16_BE: + for (bp = buffer+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; + } + break; + + case AFMT_S16_LE: + for (bp = buffer+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[0] | (bp[1] << 8); + } + break; + + case AFMT_S16_BE: + for (bp = buffer+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[1] | (bp[0] << 8); + } + break; + } + if (!AFMT_ISSTEREO(u->format) && AFMT_ISSTEREO(u->dma.format)) { + /* expand from mono to stereo */ + for (sp = tmp.s+scnt, sp2 = tmp.s+2*scnt, i = 0; i < scnt; i++) { + sp--; + sp2 -= 2; + sp2[0] = sp2[1] = sp[0]; + } + } + if (AFMT_ISSTEREO(u->format) && !AFMT_ISSTEREO(u->dma.format)) { + /* contract from stereo to mono */ + for (sp = sp2 = tmp.s, i = 0; i < scnt; i++, sp++, sp2 += 2) + sp[0] = (sp2[0] + sp2[1]) >> 1; + } + cnt = scnt; + if (AFMT_ISSTEREO(u->dma.format)) + cnt <<= 1; + sp = tmp.s; + bp = tmp.b; + switch (u->dma.format & ~AFMT_STEREO) { + case AFMT_U8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = (*sp >> 8) ^ 0x80; + break; + + case AFMT_S8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = *sp >> 8; + break; + + case AFMT_U16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_U16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_S16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = s >> 8; + } + break; + + case AFMT_S16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = s >> 8; + } + break; + } + dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh); + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbin_prepare_desc(struct usbin *u, struct usb_isoc_desc *id) +{ + unsigned int i, maxsize; + + maxsize = (u->freqn + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); + for (i = 0; i < DESCFRAMES; i++) + id->frames[i].frame_length = maxsize; + return 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + * convert sample format on the fly if necessary + */ +static int usbin_retire_desc(struct usbin *u, struct usb_isoc_desc *id) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree, maxsize; + unsigned char *cp = id->data; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + maxsize = (u->freqn + 0x3fff) >> (14 - ufmtsh); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = 0; i < DESCFRAMES; i++, cp += maxsize) { + if (id->frames[i].frame_status) { + printk(KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + continue; + } + scnt = id->frames[i].frame_length >> ufmtsh; + if (!scnt) + continue; + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + dmafree = u->dma.dmasize - u->dma.count; + if (cnt > dmafree) { + scnt = dmafree >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } + } + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyin(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbin_convert(u, cp, scnt); + } + } + if (err) + u->dma.error++; + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + return err ? -1 : 0; +} + +static int usbin_completed(int status, void *__buffer, int rval, void *dev_id) +{ +#if 1 + struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; + struct usb_audiodev *as = (struct usb_audiodev *)id->context; +#else + struct usb_audiodev *as = (struct usb_audiodev *)dev_id; + struct usb_isoc_desc *id; +#endif + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int next, idmask; + +#if 0 + printk(KERN_DEBUG "usbin_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); +#endif + spin_lock_irqsave(&as->lock, flags); + next = !(u->flags & FLG_NEXTID); + idmask = FLG_ID1RUNNING >> next; + u->flags = (u->flags & ~(FLG_NEXTID | idmask)) | next; + id = u->dataiso[!next]; + if (!usbin_retire_desc(u, id) && + u->flags & FLG_RUNNING && + !usbin_prepare_desc(u, id) && + !usb_run_isoc(id, u->dataiso[next])) { + u->flags |= idmask; + } else { + u->flags &= ~FLG_RUNNING; + printk(KERN_DEBUG "usbin_completed: descriptor not restarted\n"); + } + if (!(u->flags & idmask)) { + printk(KERN_DEBUG "usbin_completed: killing id\n"); + usb_kill_isoc(id); + printk(KERN_DEBUG "usbin_completed: id killed\n"); + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +/* + * we output sync data + */ +static int usbin_sync_prepare_desc(struct usbin *u, struct usb_isoc_desc *id) +{ + unsigned char *cp = id->data; + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++, cp += 3) { + id->frames[i].frame_length = 3; + cp[0] = u->freqn; + cp[1] = u->freqn >> 8; + cp[2] = u->freqn >> 16; + } + return 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbin_sync_retire_desc(struct usbin *u, struct usb_isoc_desc *id) +{ + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++) { + if (id->frames[i].frame_status) { + printk(KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + continue; + } + } + return 0; +} + +static int usbin_sync_completed(int status, void *__buffer, int rval, void *dev_id) +{ +#if 1 + struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; + struct usb_audiodev *as = (struct usb_audiodev *)id->context; +#else + struct usb_audiodev *as = (struct usb_audiodev *)dev_id; + struct usb_isoc_desc *id; +#endif + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int next, idmask; + +#if 0 + printk(KERN_DEBUG "usbin_sync_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); +#endif + spin_lock_irqsave(&as->lock, flags); + next = !(u->flags & FLG_SYNCNEXTID); + idmask = FLG_SYNC1RUNNING >> next; + u->flags = (u->flags & ~(FLG_SYNCNEXTID | idmask)) | ((-next) & FLG_SYNCNEXTID); + id = u->synciso[!next]; + if (!usbin_sync_retire_desc(u, id) && + u->flags & FLG_RUNNING && + !usbin_sync_prepare_desc(u, id) && + !usb_run_isoc(id, u->synciso[next])) { + u->flags |= idmask; + } else { + u->flags &= ~FLG_RUNNING; + printk(KERN_DEBUG "usbin_sync_completed: descriptor not restarted\n"); + } + if (!(u->flags & idmask)) { + printk(KERN_DEBUG "usbin_sync_completed: killing id\n"); + usb_kill_isoc(id); + printk(KERN_DEBUG "usbin_sync_completed: id killed\n"); + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +static void usbin_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbin *u = &as->usbin; + struct usb_isoc_desc *id; + unsigned long flags; + unsigned int which, i; + +#if 0 + printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + /* UHCI wants the data to be page aligned - this is silly */ + if (!u->data[0]) + u->data[0] = (void *)get_free_page(GFP_KERNEL); + if (!u->data[1]) + u->data[1] = (void *)get_free_page(GFP_KERNEL); + if (!u->dataiso[0] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+0)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->datapipe); + u->dataiso[0] = NULL; + } + if (!u->dataiso[1] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+1)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->datapipe); + u->dataiso[1] = NULL; + } + if (u->syncpipe) { + if (!u->syncdata[0]) + u->syncdata[0] = (void *)get_free_page(GFP_KERNEL); + if (!u->syncdata[1]) + u->syncdata[1] = (void *)get_free_page(GFP_KERNEL); + if (!u->synciso[0] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+0)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->syncpipe); + u->synciso[0] = NULL; + } + if (!u->synciso[1] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+1)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->syncpipe); + u->synciso[1] = NULL; + } + } + if (!u->data[0] || !u->data[1] || !u->dataiso[0] || !u->dataiso[1] || + (u->syncpipe && (!u->syncdata[0] || !u->syncdata[1] || !u->synciso[0] || !u->synciso[1]))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return; + } + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_RUNNING)) { + u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->phase = 0; + } + u->flags |= FLG_RUNNING; + if (!(u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING))) { + id = u->dataiso[0]; + id->start_type = START_ASAP; + id->start_frame = 0; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbin_completed; + id->data = u->data[0]; + id->buf_size = PAGE_SIZE; + u->flags &= ~FLG_NEXTID; + if (!usbin_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + u->flags |= FLG_ID0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + i = u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING); + if (u->flags & FLG_RUNNING && (i == FLG_ID0RUNNING || i == FLG_ID1RUNNING)) { + which = !(u->flags & FLG_ID1RUNNING); + id = u->dataiso[which]; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbin_completed; + id->data = u->data[which]; + id->buf_size = PAGE_SIZE; + if (!usbin_prepare_desc(u, id) && !usb_run_isoc(id, u->dataiso[!which])) + u->flags |= FLG_ID0RUNNING << which; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (!(u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING))) { + id = u->synciso[0]; + id->start_type = START_ASAP; + id->start_frame = 0; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbin_sync_completed; + id->data = u->syncdata[0]; + id->buf_size = PAGE_SIZE; + u->flags &= ~FLG_SYNCNEXTID; + if (!usbin_sync_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + i = u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING); + if (u->flags & FLG_RUNNING && (i == FLG_SYNC0RUNNING || i == FLG_SYNC1RUNNING)) { + which = !(u->flags & FLG_SYNC1RUNNING); + id = u->synciso[which]; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbin_sync_completed; + id->data = u->syncdata[which]; + id->buf_size = PAGE_SIZE; + if (!usbin_sync_prepare_desc(u, id) && !usb_run_isoc(id, u->synciso[!which])) + u->flags |= FLG_SYNC0RUNNING << which; + else + u->flags &= ~FLG_RUNNING; + } + } + spin_unlock_irqrestore(&as->lock, flags); +} + +static void usbout_stop(struct usb_audiodev *as) +{ + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int i; + +printk(KERN_DEBUG "usb_audio: usbout_stop (1) flags 0x%04x\n", u->flags); + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); +printk(KERN_DEBUG "usb_audio: usbout_stop (2) flags 0x%04x\n", i); + while (i & (FLG_ID0RUNNING|FLG_ID1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); +printk(KERN_DEBUG "usb_audio: usbout_stop (3) flags 0x%04x\n", i); + } + if (u->dataiso[0]) + usb_free_isoc(u->dataiso[0]); + if (u->dataiso[1]) + usb_free_isoc(u->dataiso[1]); + if (u->synciso[0]) + usb_free_isoc(u->synciso[0]); + if (u->synciso[1]) + usb_free_isoc(u->synciso[1]); + u->dataiso[0] = u->dataiso[1] = u->synciso[0] = u->synciso[1] = NULL; +} + +static void usbout_release(struct usb_audiodev *as) +{ + struct usbout *u = &as->usbout; + + usbout_stop(as); + if (u->data[0]) + free_page((unsigned long)u->data[0]); + if (u->data[1]) + free_page((unsigned long)u->data[1]); + if (u->syncdata[0]) + free_page((unsigned long)u->syncdata[0]); + if (u->syncdata[1]) + free_page((unsigned long)u->syncdata[1]); + u->data[0] = u->data[1] = u->syncdata[0] = u->syncdata[1] = NULL; +} + +static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh, cnt, i; + __s16 *sp, *sp2, s; + unsigned char *bp; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + cnt = scnt; + if (AFMT_ISSTEREO(u->dma.format)) + cnt <<= 1; + dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh); + sp = tmp.s + cnt; + switch (u->dma.format & ~AFMT_STEREO) { + case AFMT_U8: + for (bp = tmp.b+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = (*bp ^ 0x80) << 8; + } + break; + + case AFMT_S8: + for (bp = tmp.b+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = *bp << 8; + } + break; + + case AFMT_U16_LE: + for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; + } + break; + + case AFMT_U16_BE: + for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; + } + break; + + case AFMT_S16_LE: + for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[0] | (bp[1] << 8); + } + break; + + case AFMT_S16_BE: + for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[1] | (bp[0] << 8); + } + break; + } + if (!AFMT_ISSTEREO(u->dma.format) && AFMT_ISSTEREO(u->format)) { + /* expand from mono to stereo */ + for (sp = tmp.s+scnt, sp2 = tmp.s+2*scnt, i = 0; i < scnt; i++) { + sp--; + sp2 -= 2; + sp2[0] = sp2[1] = sp[0]; + } + } + if (AFMT_ISSTEREO(u->dma.format) && !AFMT_ISSTEREO(u->format)) { + /* contract from stereo to mono */ + for (sp = sp2 = tmp.s, i = 0; i < scnt; i++, sp++, sp2 += 2) + sp[0] = (sp2[0] + sp2[1]) >> 1; + } + cnt = scnt; + if (AFMT_ISSTEREO(u->format)) + cnt <<= 1; + sp = tmp.s; + bp = buffer; + switch (u->format & ~AFMT_STEREO) { + case AFMT_U8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = (*sp >> 8) ^ 0x80; + break; + + case AFMT_S8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = *sp >> 8; + break; + + case AFMT_U16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_U16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_S16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = s >> 8; + } + break; + + case AFMT_S16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = s >> 8; + } + break; + } + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbout_prepare_desc(struct usbout *u, struct usb_isoc_desc *id) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt; + unsigned char *cp = id->data; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = 0; i < DESCFRAMES; i++) { + u->phase = (u->phase & 0x3fff) + u->freqm; + scnt = u->phase >> 14; + if (!scnt) { + id->frames[i].frame_length = 0; + continue; + } + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + if (cnt > u->dma.count) { + scnt = u->dma.count >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } + u->dma.count -= cnt; + } else + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyout(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbout_convert(u, cp, scnt); + } + cnt = scnt << ufmtsh; + id->frames[i].frame_length = cnt; + cp += cnt; + } + if (err) + u->dma.error++; + if (u->dma.mapped) { + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } else { + if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } + return err ? -1 : 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbout_retire_desc(struct usbout *u, struct usb_isoc_desc *id) +{ + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++) { + if (id->frames[i].frame_status) { + printk(KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + continue; + } + } + return 0; +} + +static int usbout_completed(int status, void *__buffer, int rval, void *dev_id) +{ +#if 1 + struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; + struct usb_audiodev *as = (struct usb_audiodev *)id->context; +#else + struct usb_audiodev *as = (struct usb_audiodev *)dev_id; + struct usb_isoc_desc *id; +#endif + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int next, idmask; + +#if 0 + printk(KERN_DEBUG "usbout_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); +#endif + spin_lock_irqsave(&as->lock, flags); + next = !(u->flags & FLG_NEXTID); + idmask = FLG_ID1RUNNING >> next; + u->flags = (u->flags & ~(FLG_NEXTID | idmask)) | next; + id = u->dataiso[!next]; + if (!usbout_retire_desc(u, id) && + u->flags & FLG_RUNNING && + !usbout_prepare_desc(u, id) && + !usb_run_isoc(id, u->dataiso[next])) { + u->flags |= idmask; + } else { + u->flags &= ~FLG_RUNNING; + printk(KERN_DEBUG "usbout_completed: descriptor not restarted\n"); + } + if (!(u->flags & idmask)) { + printk(KERN_DEBUG "usbout_completed: killing id\n"); + usb_kill_isoc(id); + printk(KERN_DEBUG "usbout_completed: id killed\n"); + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +static int usbout_sync_prepare_desc(struct usbout *u, struct usb_isoc_desc *id) +{ + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++) + id->frames[i].frame_length = 3; + return 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbout_sync_retire_desc(struct usbout *u, struct usb_isoc_desc *id) +{ + unsigned char *cp = id->data; + unsigned int i, f; + + for (i = 0; i < DESCFRAMES; i++, cp += 3) { + if (id->frames[i].frame_status) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, id->frames[i].frame_status); + continue; + } + if (id->frames[i].frame_length < 3) { + printk(KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, id->frames[i].frame_length); + continue; + } + f = cp[0] | (cp[1] << 8) | (cp[2] << 16); + if (abs(f - u->freqn) > (u->freqn >> 3)) { + printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn); + continue; + } + u->freqm = f; + } + return 0; +} + +static int usbout_sync_completed(int status, void *__buffer, int rval, void *dev_id) +{ +#if 1 + struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; + struct usb_audiodev *as = (struct usb_audiodev *)id->context; +#else + struct usb_audiodev *as = (struct usb_audiodev *)dev_id; + struct usb_isoc_desc *id; +#endif + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int next, idmask; + +#if 0 + printk(KERN_DEBUG "usbout_sync_completed: status %d rval %d flags 0x%x\n", status, rval, u->flags); +#endif + spin_lock_irqsave(&as->lock, flags); + next = !(u->flags & FLG_SYNCNEXTID); + idmask = FLG_SYNC1RUNNING >> next; + u->flags = (u->flags & ~(FLG_SYNCNEXTID | idmask)) | ((-next) & FLG_SYNCNEXTID); + id = u->synciso[!next]; + if (!usbout_sync_retire_desc(u, id) && + u->flags & FLG_RUNNING && + !usbout_sync_prepare_desc(u, id) && + !usb_run_isoc(id, u->synciso[next])) { + u->flags |= idmask; + } else { + u->flags &= ~FLG_RUNNING; + printk(KERN_DEBUG "usbout_sync_completed: descriptor not restarted\n"); + } + if (!(u->flags & idmask)) { + printk(KERN_DEBUG "usbout_sync_completed: killing id\n"); + usb_kill_isoc(id); + printk(KERN_DEBUG "usbout_sync_completed: id killed\n"); + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +static void usbout_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbout *u = &as->usbout; + struct usb_isoc_desc *id; + unsigned long flags; + unsigned int which, i; + +#if 0 + printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + /* UHCI wants the data to be page aligned - this is silly */ + if (!u->data[0]) + u->data[0] = (void *)get_free_page(GFP_KERNEL); + if (!u->data[1]) + u->data[1] = (void *)get_free_page(GFP_KERNEL); + if (!u->dataiso[0] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+0)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->datapipe); + u->dataiso[0] = NULL; + } + if (!u->dataiso[1] && usb_init_isoc(dev, u->datapipe, DESCFRAMES, as, u->dataiso+1)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->datapipe); + u->dataiso[1] = NULL; + } + if (u->syncpipe) { + if (!u->syncdata[0]) + u->syncdata[0] = (void *)get_free_page(GFP_KERNEL); + if (!u->syncdata[1]) + u->syncdata[1] = (void *)get_free_page(GFP_KERNEL); + if (!u->synciso[0] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+0)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->syncpipe); + u->synciso[0] = NULL; + } + if (!u->synciso[1] && usb_init_isoc(dev, u->syncpipe, DESCFRAMES, as, u->synciso+1)) { + printk(KERN_ERR "usbaudio: cannot init isoc descriptor device %d pipe 0x%08x\n", + dev->devnum, u->syncpipe); + u->synciso[1] = NULL; + } + } + if (!u->data[0] || !u->data[1] || !u->dataiso[0] || !u->dataiso[1] || + (u->syncpipe && (!u->syncdata[0] || !u->syncdata[1] || !u->synciso[0] || !u->synciso[1]))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return; + } + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_RUNNING)) { + u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->phase = 0; + } + u->flags |= FLG_RUNNING; + if (!(u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING))) { + id = u->dataiso[0]; + id->start_type = START_ASAP; + id->start_frame = 0; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbout_completed; + id->data = u->data[0]; + id->buf_size = PAGE_SIZE; + u->flags &= ~FLG_NEXTID; + if (!usbout_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + u->flags |= FLG_ID0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + i = u->flags & (FLG_ID0RUNNING|FLG_ID1RUNNING); + if (u->flags & FLG_RUNNING && (i == FLG_ID0RUNNING || i == FLG_ID1RUNNING)) { + which = !(u->flags & FLG_ID1RUNNING); + id = u->dataiso[which]; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbout_completed; + id->data = u->data[which]; + id->buf_size = PAGE_SIZE; + if (!usbout_prepare_desc(u, id) && !usb_run_isoc(id, u->dataiso[!which])) + u->flags |= FLG_ID0RUNNING << which; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (!(u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING))) { + id = u->synciso[0]; + id->start_type = START_ASAP; + id->start_frame = 0; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbout_sync_completed; + id->data = u->syncdata[0]; + id->buf_size = PAGE_SIZE; + u->flags &= ~FLG_NEXTID; + if (!usbout_sync_prepare_desc(u, id) && !usb_run_isoc(id, NULL)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + i = u->flags & (FLG_SYNC0RUNNING|FLG_SYNC1RUNNING); + if (u->flags & FLG_RUNNING && (i == FLG_SYNC0RUNNING || i == FLG_SYNC1RUNNING)) { + which = !(u->flags & FLG_SYNC1RUNNING); + id = u->synciso[which]; + id->callback_frames = /*0*/DESCFRAMES; + id->callback_fn = usbout_sync_completed; + id->data = u->syncdata[which]; + id->buf_size = PAGE_SIZE; + if (!usbout_sync_prepare_desc(u, id) && !usb_run_isoc(id, u->synciso[!which])) + u->flags |= FLG_SYNC0RUNNING << which; + else + u->flags &= ~FLG_RUNNING; + } + } + spin_unlock_irqrestore(&as->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static unsigned int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt) +{ + unsigned int i; + + /* first find an exact match */ + for (i = 0; i < nr; i++) + if (afp[i].format == fmt) + return i; + /* second find a match with the same stereo/mono and 8bit/16bit property */ + for (i = 0; i < nr; i++) + if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) && + !AFMT_IS16BIT(afp[i].format) == !AFMT_IS16BIT(fmt)) + return i; + /* third find a match with the same number of channels */ + for (i = 0; i < nr; i++) + if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt)) + return i; + /* return anything */ + return 0; +} + +static int set_format_in(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbin *u = &as->usbin; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int fmtnr, ep; + unsigned char data[3]; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + fmtnr = find_format(as->fmtin, as->numfmtin, d->format); + fmt = as->fmtin + fmtnr; + alts = &iface->altsetting[fmt->altsetting]; + u->format = fmt->format; + u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x08) { + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress & 0x7f)) { + printk(KERN_ERR "usb_audio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ) < 0) { + printk(KERN_ERR "usbaudio: failure to set input sampling frequency device %d endpoint 0x%x to %u\n", + dev->devnum, ep, d->srate); + return -1; + } + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ) < 0) { + printk(KERN_ERR "usbaudio: failure to get input sampling frequency device %d endpoint 0x%x\n", + dev->devnum, ep); + return -1; + } + printk(KERN_DEBUG "usb_audio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + return 0; +} + +static int set_format_out(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbout *u = &as->usbout; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int fmtnr, ep; + unsigned char data[3]; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + fmtnr = find_format(as->fmtout, as->numfmtout, d->format); + fmt = as->fmtout + fmtnr; + u->format = fmt->format; + alts = &iface->altsetting[fmt->altsetting]; + u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x04) { + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress | 0x80)) { + printk(KERN_ERR "usb_audio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ) < 0) { + printk(KERN_ERR "usbaudio: failure to set output sampling frequency device %d endpoint 0x%x to %u\n", + dev->devnum, ep, d->srate); + return -1; + } + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ) < 0) { + printk(KERN_ERR "usbaudio: failure to get output sampling frequency device %d endpoint 0x%x\n", + dev->devnum, ep); + return -1; + } + printk(KERN_DEBUG "usb_audio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16)); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + return 0; +} + +static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate) +{ + int ret1 = 0, ret2 = 0; + + if (!(fmode & (FMODE_READ|FMODE_WRITE))) + return -EINVAL; + if (fmode & FMODE_READ) { + usbin_stop(s); + s->usbin.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbin.dma.format; + else + s->usbin.dma.format = fmt; + if (!srate) + srate = s->usbin.dma.srate; + else + s->usbin.dma.srate = srate; + } + if (fmode & FMODE_WRITE) { + usbout_stop(s); + s->usbout.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbout.dma.format; + else + s->usbout.dma.format = fmt; + if (!srate) + srate = s->usbout.dma.srate; + else + s->usbout.dma.srate = srate; + } + if (fmode & FMODE_READ) + ret1 = set_format_in(s); + if (fmode & FMODE_WRITE) + ret2 = set_format_out(s); + return ret1 ? ret1 : ret2; +} + +/* --------------------------------------------------------------------- */ + +static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned char data[2]; + struct mixerchannel *ch; + int v1, v2, v3; + + if (mixch >= ms->numch) + return -1; + ch = &ms->ch[mixch]; + v3 = ch->maxval - ch->minval; + v1 = value & 0xff; + v2 = (value >> 8) & 0xff; + if (v1 > 100) + v1 = 100; + if (v2 > 100) + v2 = 100; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + v2 = v1; + ch->value = v1 | (v2 << 8); + v1 = (v1 * v3) / 100 + ch->minval; + v2 = (v2 * v3) / 100 + ch->minval; + switch (ch->selector) { + case 0: /* mixer unit request */ + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + /* various feature unit controls */ + case VOLUME_CONTROL: + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (ch->chnum == 0) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + data[0] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + if (ch->chnum == 0) + return 0; + data[0] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + return 0; + + default: + return -1; + } + return 0; + + err: + printk(KERN_ERR "usb_audio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); + return -1; +} + +/* --------------------------------------------------------------------- */ + +/* + * should be called with open_sem hold, so that no new processes + * look at the audio device to be destroyed + */ + +static void release(struct usb_audio_state *s) +{ + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + s->count--; + if (s->count) { + up(&open_sem); + return; + } + up(&open_sem); + wake_up(&open_wait); + while (!list_empty(&s->audiolist)) { + as = list_entry(s->audiolist.next, struct usb_audiodev, list); + list_del(&as->list); + usbin_release(as); + usbout_release(as); + dmabuf_release(&as->usbin.dma); + dmabuf_release(&as->usbout.dma); + kfree(as); + } + while (!list_empty(&s->mixerlist)) { + ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); + list_del(&ms->list); + kfree(ms); + } + kfree(s); + MOD_DEC_USE_COUNT; +} + +extern inline int prog_dmabuf_in(struct usb_audiodev *as) +{ + usbin_stop(as); + return dmabuf_init(&as->usbin.dma); +} + +extern inline int prog_dmabuf_out(struct usb_audiodev *as) +{ + usbout_stop(as); + return dmabuf_init(&as->usbout.dma); +} + +/* --------------------------------------------------------------------- */ + +static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int usb_audio_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *devs, *mdevs; + struct usb_mixerdev *ms; + struct usb_audio_state *s; + + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) { + ms = list_entry(mdevs, struct usb_mixerdev, list); + if (ms->dev_mixer == minor) + goto mixer_found; + } + } + up(&open_sem); + return -ENODEV; + + mixer_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + file->private_data = ms; + s->count++; + up(&open_sem); + return 0; +} + +static int usb_audio_release_mixdev(struct inode *inode, struct file *file) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + struct usb_audio_state *s = ms->state; + + down(&open_sem); + release(s); + return 0; +} + +static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + int i, j, val; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + info.modify_counter = ms->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + /* don't know how to handle this yet */ + return put_user(0, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < ms->numch; i++) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + /* don't know how to handle this yet */ + return put_user(0, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch; j++) { + if (ms->ch[j].osschannel == i) { + return put_user(ms->ch[j].value, (int *)arg); + } + } + return -EINVAL; + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + ms->modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + /* set recording source: val */ + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++); + if (j >= ms->numch) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (wrmixer(ms, j, val)) + return -EIO; + return put_user(ms->ch[j].value, (int *)arg); + } +} + +static /*const*/ struct file_operations usb_mixer_fops = { + &usb_audio_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &usb_audio_ioctl_mixdev, + NULL, /* mmap */ + &usb_audio_open_mixdev, + NULL, /* flush */ + &usb_audio_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_out(struct usb_audiodev *as, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (as->usbout.dma.mapped || !as->usbout.dma.ready) + return 0; + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&as->usbout.dma.wait, &wait); + for (;;) { + spin_lock_irqsave(&as->lock, flags); + count = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * count / as->usbout.dma.srate; + tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "usbaudio: dma timed out??\n"); + } + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t usb_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbin.dma.mapped) + return -ENXIO; + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&as->usbin.dma.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&as->lock, flags); + ptr = as->usbin.dma.rdptr; + cnt = as->usbin.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + usbin_start(as); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbin.dma.dmasize) + ptr -= as->usbin.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbin.dma.rdptr = ptr; + as->usbin.dma.count -= cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbin.dma.wait, &wait); + return ret; +} + +static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbout.dma.mapped) + return -ENXIO; + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&as->usbout.dma.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&as->lock, flags); + if (as->usbout.dma.count < 0) { + as->usbout.dma.count = 0; + as->usbout.dma.rdptr = as->usbout.dma.wrptr; + } + ptr = as->usbout.dma.wrptr; + cnt = as->usbout.dma.dmasize - as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + usbout_start(as); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbout.dma.dmasize) + ptr -= as->usbout.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbout.dma.wrptr = ptr; + as->usbout.dma.count += cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + usbout_start(as); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbout.dma.wait, &wait); + return ret; +} + +static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!as->usbout.dma.ready) + prog_dmabuf_out(as); + poll_wait(file, &as->usbout.dma.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!as->usbin.dma.ready) + prog_dmabuf_in(as); + poll_wait(file, &as->usbin.dma.wait, wait); + } + spin_lock_irqsave(&as->lock, flags); + if (file->f_mode & FMODE_READ) { + if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (as->usbout.dma.mapped) { + if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return mask; +} + +static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct dmabuf *db; + int ret; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_out(as)) != 0) + return ret; + db = &as->usbout.dma; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_in(as)) != 0) + return ret; + db = &as->usbin.dma; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + return dmabuf_mmap(db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, val2, mapped, ret; + + if (!s->usbdev) + return -EIO; + mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) || + ((file->f_mode & FMODE_READ) && as->usbin.dma.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BATCH, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (val < 4000) + val = 4000; + if (val > 100000) + val = 100000; + if (set_format(as, file->f_mode, AFMT_QUERY, val)) + return -EIO; + } + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SNDCTL_DSP_STEREO: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (set_format(as, file->f_mode, val2 | AFMT_STEREO, 0)) + return -EIO; + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (val == 1) + val2 &= ~AFMT_STEREO; + else + val2 |= AFMT_STEREO; + if (set_format(as, file->f_mode, val2, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (hweight32(val) != 1) + return -EINVAL; + if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE))) + return -EINVAL; + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + val |= val2 & AFMT_STEREO; + if (set_format(as, file->f_mode, val, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(val2 & ~AFMT_STEREO, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + usbin_start(as); + } else + usbin_stop(as); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + usbout_start(as); + } else + usbout_stop(as); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbout.dma.fragsize; + abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count; + abinfo.fragstotal = as->usbout.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbin.dma.fragsize; + abinfo.bytes = as->usbin.dma.count; + abinfo.fragstotal = as->usbin.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + val = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbin.dma.total_bytes; + cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift; + cinfo.ptr = as->usbin.dma.wrptr; + if (as->usbin.dma.mapped) + as->usbin.dma.count &= as->usbin.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbout.dma.total_bytes; + cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift; + cinfo.ptr = as->usbout.dma.rdptr; + if (as->usbout.dma.mapped) + as->usbout.dma.count &= as->usbout.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_out(as))) + return val; + return put_user(as->usbout.dma.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_in(as))) + return val; + return put_user(as->usbin.dma.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + as->usbin.dma.ossfragshift = val & 0xffff; + as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbin.dma.ossfragshift < 4) + as->usbin.dma.ossfragshift = 4; + if (as->usbin.dma.ossfragshift > 15) + as->usbin.dma.ossfragshift = 15; + if (as->usbin.dma.ossmaxfrags < 4) + as->usbin.dma.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + as->usbout.dma.ossfragshift = val & 0xffff; + as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbout.dma.ossfragshift < 4) + as->usbout.dma.ossfragshift = 4; + if (as->usbout.dma.ossfragshift > 15) + as->usbout.dma.ossfragshift = 15; + if (as->usbout.dma.ossmaxfrags < 4) + as->usbout.dma.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) || + (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + as->usbin.dma.subdivision = val; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_IS16BIT(val2) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -ENOIOCTLCMD; +} + +static int usb_audio_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct list_head *devs, *adevs; + struct usb_audiodev *as; + struct usb_audio_state *s; + + for (;;) { + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (adevs = s->audiolist.next; adevs != &s->audiolist; adevs = adevs->next) { + as = list_entry(adevs, struct usb_audiodev, list); + if (!((as->dev_audio ^ minor) & ~0xf)) + goto device_found; + } + } + up(&open_sem); + return -ENODEV; + + device_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + /* wait for device to become free */ + if (!(as->open_mode & file->f_mode)) + break; + if (file->f_flags & O_NONBLOCK) { + up(&open_sem); + return -EBUSY; + } + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&open_wait, &wait); + up(&open_sem); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&open_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (file->f_mode & FMODE_READ) + as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0; + if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) { + up(&open_sem); + return -EIO; + } + file->private_data = as; + as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + s->count++; + up(&open_sem); + return 0; +} + +static int usb_audio_release(struct inode *inode, struct file *file) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + + if (file->f_mode & FMODE_WRITE) + drain_out(as, file->f_flags & O_NONBLOCK); + down(&open_sem); + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + if (s->usbdev) + usb_set_interface(s->usbdev, as->usbout.interface, 0); + dmabuf_release(&as->usbout.dma); + usbout_release(as); + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + if (s->usbdev) + usb_set_interface(s->usbdev, as->usbin.interface, 0); + dmabuf_release(&as->usbin.dma); + usbin_release(as); + } + as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + release(s); + wake_up(&open_wait); + return 0; +} + +static /*const*/ struct file_operations usb_audio_fops = { + &usb_audio_llseek, + &usb_audio_read, + &usb_audio_write, + NULL, /* readdir */ + &usb_audio_poll, + &usb_audio_ioctl, + &usb_audio_mmap, + &usb_audio_open, + NULL, /* flush */ + &usb_audio_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* + * TO DO in order to get to the point of building an OSS interface + * structure, let alone playing music.. + * + * Use kmalloc/kfree for the descriptors we build + * Write the descriptor->OSS convertor code + * Figure how we deal with mixers + * Check alternate configurations. For now assume we will find one + * zero bandwidth (idle) config and one or more live one pers interface. + */ + +static int usb_audio_probe(struct usb_device *dev); +static void usb_audio_disconnect(struct usb_device *dev); + static struct usb_driver usb_audio_driver = { "audio", usb_audio_probe, @@ -32,7 +2547,7 @@ static struct usb_driver usb_audio_driver = { static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) { #if 0 - struct usb_audio *aud = (struct usb_audio *)dev_id; + struct usb_audio_device *aud = (struct usb_audio_device *)dev_id; printk("irq on %p\n", aud); #endif @@ -41,107 +2556,953 @@ static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) } #endif -static int usb_audio_probe(struct usb_device *dev) +static void *find_descriptor(void *descstart, unsigned int desclen, void *after, + u8 dtype, int iface, int altsetting) { - struct usb_interface_descriptor *interface; - struct usb_audio *aud; - int i; - int na=0; - - for (i=0; i<dev->config[0].bNumInterfaces; i++) { - interface = &dev->config->interface[i].altsetting[0]; - - if (interface->bInterfaceClass != 1) - continue; - - printk(KERN_INFO "USB audio device detected.\n"); - - switch(interface->bInterfaceSubClass) { - case 0x01: - printk(KERN_INFO "audio: control device\n"); - break; - case 0x02: - printk(KERN_INFO "audio: streaming\n"); - break; - case 0x03: - printk(KERN_INFO "audio: nonstreaming\n"); - break; + u8 *p, *end, *next; + int ifc = -1, as = -1; + + p = descstart; + end = p + desclen; + for (; p < end;) { + if (p[0] < 2) + return NULL; + next = p + p[0]; + if (next > end) + return NULL; + if (p[1] == USB_DT_INTERFACE) { + /* minimum length of interface descriptor */ + if (p[0] < 9) + return NULL; + ifc = p[2]; + as = p[3]; + } + if (p[1] == dtype && (!after || (void *)p > after) && + (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) { + return p; } - na++; + p = next; } - - if (!na) - return -1; + return NULL; +} - aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL); - if (!aud) - return -1; +static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting) +{ + unsigned char *p; - memset(aud, 0, sizeof(*aud)); - aud->dev = dev; - dev->private = aud; + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting); + while (p) { + if (p[0] >= 3 && p[2] == dsubtype) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting); + } + return NULL; +} -/* - if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { - printk (KERN_INFO "Failed usb_set_configuration: Audio\n"); - break; +static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface) +{ + unsigned char *p; + + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1); + while (p) { + if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1); } - usb_set_protocol(dev, 0); - usb_set_idle(dev, 0, 0); -*/ - -/* - aud->irqpipe = usb_rcvctrlpipe(dev, endpoint->bEndpointAddress); - na = usb_request_irq(dev, aud->irqpipe, - usb_audio_irq, endpoint->bInterval, - aud, &aud->irq_handle); - if (na) { - printk (KERN_WARNING "usb-audio: usb_request_irq failed (0x%x)\n", na); + return NULL; +} + +static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout) +{ + struct usb_device *dev = s->usbdev; + struct usb_audiodev *as; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct audioformat *fp; + unsigned char *fmt; + unsigned int i, j, k, format; + + if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL))) + return; + memset(as, 0, sizeof(struct usb_audiodev)); + init_waitqueue_head(&as->usbin.dma.wait); + init_waitqueue_head(&as->usbout.dma.wait); + spin_lock_init(&as->lock); + as->state = s; + as->usbin.interface = asifin; + as->usbout.interface = asifout; + /* search for input formats */ + if (asifin >= 0) { + iface = &config->interface[asifin]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifin, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + !(alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u first endpoint not isochronous in\n", + dev->devnum, asifin, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i); + if (!fmt) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifin, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i); + if (!fmt) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifin, i, fmt[4], fmt[5]); + continue; + } + if (as->numfmtin >= MAXFORMATS) + continue; + fp = &as->fmtin[as->numfmtin++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + printk(KERN_INFO "usb_audio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u\n", + dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi); + } } -*/ + /* search for output formats */ + if (asifout >= 0) { + iface = &config->interface[asifout]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifout, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + (alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u first endpoint not isochronous out\n", + dev->devnum, asifout, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i); + if (!fmt) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifout, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i); + if (!fmt) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usb_audio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifout, i, fmt[4], fmt[5]); + continue; + } + if (as->numfmtout >= MAXFORMATS) + continue; + fp = &as->fmtout[as->numfmtout++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + printk(KERN_INFO "usb_audio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u\n", + dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi); + } + } + if (as->numfmtin == 0 && as->numfmtout == 0) { + kfree(as); + return; + } + if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) { + printk(KERN_ERR "usb_audio: cannot register dsp\n"); + kfree(as); + return; + } + /* everything successful */ + list_add_tail(&as->list, &s->audiolist); +} + +struct consmixstate { + struct usb_audio_state *s; + unsigned char *buffer; + unsigned int buflen; + unsigned int ctrlif; + struct mixerchannel mixch[SOUND_MIXER_NRDEVICES]; + unsigned int nrmixch; + unsigned int mixchmask; + unsigned long unitbitmap[32/sizeof(unsigned long)]; + /* return values */ + unsigned int nrchannels; + unsigned int termtype; + unsigned int chconfig; +}; + +static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr) +{ + struct mixerchannel *c; + + if (nr >= SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usb_audio: invalid OSS mixer channel %u\n", nr); + return NULL; + } + if (!(state->mixchmask & (1 << nr))) { + printk(KERN_WARNING "usb_audio: OSS mixer channel %u already in use\n", nr); + return NULL; + } + c = &state->mixch[state->nrmixch++]; + c->osschannel = nr; + state->mixchmask &= ~(1 << nr); + return c; +} + +static unsigned int getvolchannel(struct consmixstate *state) +{ + unsigned int u; + + if ((state->termtype & 0xff00) == 0x0000 && !(state->mixchmask & SOUND_MASK_VOLUME)) + return SOUND_MIXER_VOLUME; + if ((state->termtype & 0xff00) == 0x0100) { + if (state->mixchmask & SOUND_MASK_PCM) + return SOUND_MIXER_PCM; + if (state->mixchmask & SOUND_MASK_ALTPCM) + return SOUND_MIXER_ALTPCM; + } + if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC)) + return SOUND_MIXER_MIC; + if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) + return SOUND_MIXER_SPEAKER; + if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) + return SOUND_MIXER_SPEAKER; + if ((state->termtype & 0xff00) == 0x0500) { + if (state->mixchmask & SOUND_MASK_PHONEIN) + return SOUND_MIXER_PHONEIN; + if (state->mixchmask & SOUND_MASK_PHONEOUT) + return SOUND_MIXER_PHONEOUT; + } + if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO)) + return SOUND_MIXER_RADIO; + if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO)) + return SOUND_MIXER_VIDEO; + u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 | + SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3)); + return u-1; +} - list_add(&aud->list, &usb_audio_list); +static void prepmixch(struct consmixstate *state) +{ + struct usb_device *dev = state->s->usbdev; + struct mixerchannel *ch; + unsigned char buf[2]; + __s16 v1; + unsigned int v2, v3; + + if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES) + return; + ch = &state->mixch[state->nrmixch-1]; + switch (ch->selector) { + case 0: /* mixer unit request */ + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + if (!v2) + v2 = 1; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + /* various feature unit controls */ + case VOLUME_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->chnum != 0) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; - return 0; + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->minval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->maxval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->chnum != 0) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + default: + goto err; + } + return; + + err: + printk(KERN_ERR "usb_audio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector); + if (state->nrmixch) + state->nrmixch--; } -static void usb_audio_disconnect(struct usb_device *dev) + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid); + +extern inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch) +{ + unsigned int idx; + + idx = inidx*numoch; + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 1; + idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT); + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + return 1; +} + +static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer) { - struct usb_audio *aud = (struct usb_audio*) dev->private; + unsigned int nroutch = mixer[5+mixer[4]]; + unsigned int chidx[SOUND_MIXER_NRDEVICES+1]; + unsigned int termt[SOUND_MIXER_NRDEVICES]; + unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0; + unsigned char *bmap = &mixer[9+mixer[4]]; + unsigned int bmapsize; + struct mixerchannel *ch; + unsigned int i; - if (!aud) + if (!mixer[4]) { + printk(KERN_ERR "usb_audio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]); + return; + } + if (mixer[4] > SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usb_audio: mixer unit %u: too many input pins\n", mixer[3]); return; + } + chidx[0] = 0; + for (i = 0; i < mixer[4]; i++) { + usb_audio_recurseunit(state, mixer[5+i]); + chidx[i+1] = chidx[i] + state->nrchannels; + termt[i] = state->termtype; + } + state->termtype = 0; + state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8); + bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3; + bmap += bmapsize - 1; + if (mixer[0] < 10+mixer[4]+bmapsize) { + printk(KERN_ERR "usb_audio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]); + return; + } + for (i = 0; i < mixer[4]; i++) { + state->termtype = termt[i]; + if (chidx[i+1]-chidx[i] >= 2) { + flg |= MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + continue; + } + } + flg &= ~MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + } + } + state->termtype = 0; +} - list_del(&aud->list); +static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) +{ + unsigned int chnum, i; + + if (!selector[4]) { + printk(KERN_ERR "usb_audio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]); + return; + } + usb_audio_recurseunit(state, selector[5]); + chnum = state->nrchannels; + for (i = 1; i < selector[4]; i++) { + usb_audio_recurseunit(state, selector[5+i]); + if (chnum != state->nrchannels) { + printk(KERN_ERR "usb_audio: selector unit %u: input pins with varying channel numbers\n", selector[3]); + state->termtype = 0; + state->chconfig = 0; + state->nrchannels = 0; + return; + } + } + state->termtype = 0; + state->chconfig = 0; +} - usb_release_irq(aud->dev, aud->irq_handle, aud->irqpipe); +/* in the future we might try to handle 3D etc. effect units */ - kfree(aud); - dev->private = NULL; +static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc) +{ + unsigned int i; + + for (i = 0; i < proc[6]; i++) + usb_audio_recurseunit(state, proc[7+i]); + state->nrchannels = proc[7+proc[6]]; + state->termtype = 0; + state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8); } -int usb_audio_init(void) +static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr) { - usb_register(&usb_audio_driver); + struct mixerchannel *ch; + unsigned short chftr, mchftr; + + usb_audio_recurseunit(state, ftr[4]); + if (state->nrchannels == 0) { + printk(KERN_ERR "usb_audio: feature unit %u source has no channels\n", ftr[3]); + return; + } + if (state->nrchannels > 2) + printk(KERN_WARNING "usb_audio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]); + if (ftr[0] < 7+ftr[5]*(1+state->nrchannels)) { + printk(KERN_ERR "usb_audio: unit %u: invalid FEATURE_UNIT descriptor\n", ftr[3]); + return; + } + mchftr = ftr[6]; + chftr = ftr[6+ftr[5]]; + if (state->nrchannels > 1) + chftr &= ftr[6+2*ftr[5]]; + /* volume control */ + if (chftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 1; + ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + prepmixch(state); + } + } else if (mchftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* bass control */ + if (chftr & 4) { + ch = getmixchannel(state, SOUND_MIXER_BASS); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 1; + ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + prepmixch(state); + } + } else if (mchftr & 4) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* treble control */ + if (chftr & 16) { + ch = getmixchannel(state, SOUND_MIXER_TREBLE); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 1; + ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + prepmixch(state); + } + } else if (mchftr & 16) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } +} + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid) +{ + unsigned char *p1; + unsigned int i, j; + + if (test_and_set_bit(unitid, &state->unitbitmap)) { + printk(KERN_ERR "usb_audio: mixer path recursion detected, unit %d!\n", unitid); + return; + } + p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif); + if (!p1) { + printk(KERN_ERR "usb_audio: unit %d not found!\n", unitid); + return; + } + state->nrchannels = 0; + state->termtype = 0; + state->chconfig = 0; + switch (p1[2]) { + case INPUT_TERMINAL: + if (p1[0] < 12) { + printk(KERN_ERR "usb_audio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid); + return; + } + state->nrchannels = p1[7]; + state->termtype = p1[4] | (p1[5] << 8); + state->chconfig = p1[8] | (p1[9] << 8); + return; + + case MIXER_UNIT: + if (p1[0] < 10 || p1[0] < 10+p1[4]) { + printk(KERN_ERR "usb_audio: unit %u: invalid MIXER_UNIT descriptor\n", unitid); + return; + } + usb_audio_mixerunit(state, p1); + return; + + case SELECTOR_UNIT: + if (p1[0] < 6 || p1[0] < 6+p1[4]) { + printk(KERN_ERR "usb_audio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid); + return; + } + usb_audio_selectorunit(state, p1); + return; + + case FEATURE_UNIT: + if (p1[0] < 7 || p1[0] < 7+p1[5]) { + printk(KERN_ERR "usb_audio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); + return; + } + usb_audio_featureunit(state, p1); + return; + + case PROCESSING_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) { + printk(KERN_ERR "usb_audio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid); + return; + } + usb_audio_processingunit(state, p1); + return; + + case EXTENSION_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { + printk(KERN_ERR "usb_audio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid); + return; + } + for (j = i = 0; i < p1[6]; i++) { + usb_audio_recurseunit(state, p1[7+i]); + if (!i) + j = state->termtype; + else if (j != state->termtype) + j = 0; + } + state->nrchannels = p1[7+p1[6]]; + state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8); + state->termtype = j; + return; + + default: + printk(KERN_ERR "usb_audio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return; + } +} + +static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm) +{ + struct usb_mixerdev *ms; + struct consmixstate state; + + memset(&state, 0, sizeof(state)); + state.s = s; + state.nrmixch = 0; + state.mixchmask = ~0; + state.buffer = buffer; + state.buflen = buflen; + state.ctrlif = ctrlif; + set_bit(oterm[3], &state.unitbitmap); /* mark terminal ID as visited */ + printk(KERN_INFO "usb_audio: constructing mixer for Terminal %u type 0x%04x\n", + oterm[3], oterm[4] | (oterm[5] << 8)); + usb_audio_recurseunit(&state, oterm[7]); + if (!state.nrmixch) { + printk(KERN_INFO "usb_audio: no mixer controls found for Terminal %u\n", oterm[3]); + return; + } + if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL))) + return; + memset(ms, 0, sizeof(struct usb_mixerdev)); + memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel)); + ms->state = s; + ms->iface = ctrlif; + ms->numch = state.nrmixch; + if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) { + printk(KERN_ERR "usb_audio: cannot register mixer\n"); + kfree(ms); + return; + } + list_add_tail(&ms->list, &s->mixerlist); +} + +static int usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif) +{ + struct usb_audio_state *s; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface *iface; + unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES]; + unsigned char *p1; + unsigned int i, j, numifin = 0, numifout = 0; + + if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL))) + return -1; + memset(s, 0, sizeof(struct usb_audio_state)); + INIT_LIST_HEAD(&s->audiolist); + INIT_LIST_HEAD(&s->mixerlist); + s->usbdev = dev; + s->count = 1; + /* find audiocontrol interface */ + if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u no HEADER found\n", + dev->devnum, ctrlif); + goto ret; + } + if (p1[0] < 8 + p1[7]) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u HEADER error\n", + dev->devnum, ctrlif); + goto ret; + } + if (!p1[7]) + printk(KERN_INFO "usb_audio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n", + dev->devnum, ctrlif); + for (i = 0; i < p1[7]; i++) { + j = p1[8+i]; + if (j >= config->bNumInterfaces) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u interface %u does not exist\n", + dev->devnum, ctrlif, j); + continue; + } + iface = &config->interface[j]; + if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass == 3) { + printk(KERN_INFO "usb_audio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass != 2) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->num_altsetting < 2 || + iface->altsetting[0].bNumEndpoints > 0) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u altsetting 0 not zero bandwidth\n", + dev->devnum, ctrlif); + continue; + } + if (iface->altsetting[1].bNumEndpoints < 1) { + printk(KERN_ERR "usb_audio: device %d audiocontrol interface %u interface %u has no endpoint\n", + dev->devnum, ctrlif, j); + continue; + } + /* note: this requires the data endpoint to be ep0 and the optional sync + ep to be ep1, which seems to be the case */ + if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) { + if (numifin < USB_MAXINTERFACES) + ifin[numifin++] = j; + } else { + if (numifout < USB_MAXINTERFACES) + ifout[numifout++] = j; + } + } + printk(KERN_INFO "usb_audio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n", + dev->devnum, ctrlif, numifin, numifout); + for (i = 0; i < numifin && i < numifout; i++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]); + for (j = i; j < numifin; j++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1); + for (j = i; j < numifout; j++) + usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]); + /* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */ + p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1); + while (p1) { + if (p1[0] >= 9) + usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1); + p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1); + } + + ret: + if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) { + kfree(s); + return -1; + } + /* everything successful */ + dev->private = s; + down(&open_sem); + list_add_tail(&s->audiodev, &audiodevs); + up(&open_sem); + MOD_INC_USE_COUNT; return 0; } -/* - * Support functions for parsing - */ - -void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) +/* we only care for the currently active configuration */ + +static int usb_audio_probe(struct usb_device *dev) { -#ifdef AUDIO_DEBUG - printk(KERN_DEBUG "usb_audio_interface.\n"); -#endif + struct usb_config_descriptor *config = dev->actconfig; + unsigned char *buffer; + unsigned char buf[8]; + unsigned int i, buflen; + int ret; + + for (i = 0; i < config->bNumInterfaces; i++) + if (config->interface[i].altsetting[0].bInterfaceClass == USB_CLASS_AUDIO && + config->interface[i].altsetting[0].bInterfaceSubClass == 1) /* audiocontrol interface found */ + goto audioctrlfound; + printk(KERN_DEBUG "usb_audio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + return -1; + + audioctrlfound: + /* find which configuration number is active */ + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + if (dev->config+i == config) + goto configfound; + printk(KERN_ERR "usb_audio: cannot find active configuration number of device %d\n", dev->devnum); + return -1; + + configfound: + if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { + printk(KERN_ERR "usb_audio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue); + return -1; + } + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); + if (ret) { + printk(KERN_ERR "usb_audio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum); + return -1; + } + if (buf[1] != USB_DT_CONFIG || buf[0] < 9) { + printk(KERN_ERR "usb_audio: invalid config descriptor %d of device %d\n", i, dev->devnum); + return -1; + } + buflen = buf[2] | (buf[3] << 8); + if (!(buffer = kmalloc(buflen, GFP_KERNEL))) + return -1; + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen); + if (ret) { + kfree(buffer); + printk(KERN_ERR "usb_audio: cannot get config descriptor %d of device %d\n", i, dev->devnum); + return -1; + } + /* find first audio control interface; we currently cannot handle more than one */ + for (i = 0; i < config->bNumInterfaces; i++) { + if (config->interface[i].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || + config->interface[i].altsetting[0].bInterfaceSubClass != 1) + continue; + /* audiocontrol interface found */ + if (!usb_audio_parsecontrol(dev, buffer, buflen, i)) + return 0; + } + return -1; } -void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) + +/* a revoke facility would make things simpler */ + +static void usb_audio_disconnect(struct usb_device *dev) { -#ifdef AUDIO_DEBUG - printk(KERN_DEBUG "usb_audio_interface.\n"); + struct usb_audio_state *s = (struct usb_audio_state *)dev->private; + struct list_head *list; + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + down(&open_sem); + list_del(&s->audiodev); + INIT_LIST_HEAD(&s->audiodev); + s->usbdev = NULL; + /* deregister all audio and mixer devices, so no new processes can open this device */ + for(list = s->audiolist.next; list != &s->audiolist; list = list->next) { + as = list_entry(list, struct usb_audiodev, list); + if (as->dev_audio >= 0) + unregister_sound_dsp(as->dev_audio); + as->dev_audio = -1; + } + for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) { + ms = list_entry(list, struct usb_mixerdev, list); + if (ms->dev_mixer >= 0) + unregister_sound_mixer(ms->dev_mixer); + } +#if 0 + if(aud->irq_handle) + usb_release_irq(dev, aud->irq_handle, aud->irqpipe); + aud->irq_handle = NULL; #endif + release(s); + wake_up(&open_wait); + dev->private = NULL; +} + +int usb_audio_init(void) +{ + usb_register(&usb_audio_driver); + return 0; } #ifdef MODULE @@ -156,4 +3517,3 @@ void cleanup_module(void) } #endif - diff --git a/drivers/usb/audio.h b/drivers/usb/audio.h new file mode 100644 index 000000000..a0ecb51ee --- /dev/null +++ b/drivers/usb/audio.h @@ -0,0 +1,116 @@ +#define USB_DT_CS_DEVICE 0x21 +#define USB_DT_CS_CONFIG 0x22 +#define USB_DT_CS_STRING 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 + +#define CS_AUDIO_UNDEFINED 0x20 +#define CS_AUDIO_DEVICE 0x21 +#define CS_AUDIO_CONFIGURATION 0x22 +#define CS_AUDIO_STRING 0x23 +#define CS_AUDIO_INTERFACE 0x24 +#define CS_AUDIO_ENDPOINT 0x25 + +#define HEADER 0x01 +#define INPUT_TERMINAL 0x02 +#define OUTPUT_TERMINAL 0x03 +#define MIXER_UNIT 0x04 +#define SELECTOR_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define PROCESSING_UNIT 0x07 +#define EXTENSION_UNIT 0x08 + +#define AS_GENERAL 0x01 +#define FORMAT_TYPE 0x02 +#define FORMAT_SPECIFIC 0x03 + +#define EP_GENERAL 0x01 + +#define MAX_CHAN 9 +#define MAX_FREQ 16 +#define MAX_IFACE 8 +#define MAX_FORMAT 8 +#define MAX_ALT 8 + +struct usb_audio_terminal +{ + u8 flags; + u8 assoc; + u16 type; /* Mic etc */ + u8 channels; + u8 source; + u16 chancfg; +}; + +struct usb_audio_format +{ + u8 type; + u8 channels; + u8 num_freq; + u8 sfz; + u8 bits; + u16 freq[MAX_FREQ]; +}; + +struct usb_audio_interface +{ + u8 terminal; + u8 delay; + u16 num_formats; + u16 format_type; + u8 flags; + u8 idleconf; /* Idle config */ +#define AU_IFACE_FOUND 1 + struct usb_audio_format format[MAX_FORMAT]; +}; + +struct usb_audio_device +{ + struct list_head list; + u8 mixer; + u8 selector; + void *irq_handle; + u8 num_channels; + u8 num_dsp_iface; + u8 channel_map[MAX_CHAN]; + struct usb_audio_terminal terminal[MAX_CHAN]; + struct usb_audio_interface interface[MAX_IFACE][MAX_ALT]; +}; + + + +/* Audio Class specific Request Codes */ + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +/* Terminal Control Selectors */ + +#define COPY_PROTECT_CONTROL 0x01 + +/* Feature Unit Control Selectors */ + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AUTOMATIC_GAIN_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +/* Endpoint Control Selectors */ + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c index 6a466c5a7..02df61455 100644 --- a/drivers/usb/cpia.c +++ b/drivers/usb/cpia.c @@ -28,452 +28,509 @@ #define CPIA_DEBUG /* Gobs of debugging info */ +/* Video Size 384 x 288 x 3 bytes for RGB */ #define MAX_FRAME_SIZE (384 * 288 * 3) /*******************************/ /* Memory management functions */ /*******************************/ -/* convert virtual user memory address to physical address */ -/* (virt_to_phys only works for kmalloced kernel memory) */ +#define MDEBUG(x) do { } while(0) /* Debug memory management */ -static inline unsigned long uvirt_to_phys(unsigned long adr) +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { - pgd_t *pgd; - pmd_t *pmd; - pte_t *ptep, pte; - - pgd = pgd_offset(current->mm, adr); - if (pgd_none(*pgd)) - return 0; - pmd = pmd_offset(pgd, adr); - if (pmd_none(*pmd)) - return 0; - ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); - pte = *ptep; - if(pte_present(pte)) - return - virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); - return 0; + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = (pte_page(pte) | (adr & (PAGE_SIZE-1))); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; } -static inline unsigned long uvirt_to_bus(unsigned long adr) +static inline unsigned long uvirt_to_bus(unsigned long adr) { - return virt_to_bus(phys_to_virt(uvirt_to_phys(adr))); -} + unsigned long kva, ret; -/* convert virtual kernel memory address to physical address */ -/* (virt_to_phys only works for kmalloced kernel memory) */ + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} -static inline unsigned long kvirt_to_phys(unsigned long adr) +static inline unsigned long kvirt_to_bus(unsigned long adr) { - return uvirt_to_phys(VMALLOC_VMADDR(adr)); + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; } -static inline unsigned long kvirt_to_bus(unsigned long adr) +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) { - return uvirt_to_bus(VMALLOC_VMADDR(adr)); -} + unsigned long va, kva, ret; + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} -static void * rvmalloc(unsigned long size) +static void *rvmalloc(unsigned long size) { - void * mem; + void *mem; unsigned long adr, page; - + + /* Round it off to PAGE_SIZE */ size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - mem=vmalloc(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_phys(adr); - mem_map_reserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) - size-=PAGE_SIZE; - else - size=0; - } + mem = vmalloc(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } + return mem; } -static void rvfree(void * mem, unsigned long size) +static void rvfree(void *mem, unsigned long size) { unsigned long adr, page; - + + if (!mem) + return; + size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - if (mem) - { - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_phys(adr); - mem_map_unreserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) - size-=PAGE_SIZE; - else - size=0; - } - vfree(mem); + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } + vfree(mem); } -int usb_cpia_get_version(struct usb_device *dev, void *buf) +static int usb_cpia_get_version(struct usb_device *dev, void *buf) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; - dr.request = USB_REQ_CPIA_GET_VERSION; - dr.value = 0; - dr.index = 0; - dr.length = 4; - - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 4); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_VERSION, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 4, HZ); } -int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf) +#ifdef NOTUSED +static int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; - dr.request = USB_REQ_CPIA_GET_PNP_ID; - dr.value = 0; - dr.index = 0; - dr.length = 6; - - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 6); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_PNP_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 6, HZ); } +#endif -int usb_cpia_get_camera_status(struct usb_device *dev, void *buf) +#ifdef NOTUSED +static int usb_cpia_get_camera_status(struct usb_device *dev, void *buf) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80; - dr.request = USB_REQ_CPIA_GET_CAMERA_STATUS; - dr.value = 0; - dr.index = 0; - dr.length = 8; - - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 8); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_CAMERA_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 8, HZ); } +#endif -int usb_cpia_goto_hi_power(struct usb_device *dev) +static int usb_cpia_goto_hi_power(struct usb_device *dev) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_GOTO_HI_POWER; - dr.value = 0; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_GOTO_HI_POWER, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, HZ); } -int usb_cpia_get_vp_version(struct usb_device *dev, void *buf) +static int usb_cpia_get_vp_version(struct usb_device *dev, void *buf) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_GET_VP_VERSION; - dr.value = 0; - dr.index = 0; - dr.length = 4; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, buf, 4); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CPIA_GET_VP_VERSION, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 4, HZ); } -int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor) +static int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_SET_SENSOR_FPS; - dr.value = (sensorclkdivisor << 8) + sensorbaserate; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_SENSOR_FPS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (sensorbaserate << 8) + sensorclkdivisor, 0, NULL, 0, HZ); } -int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline) +static int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_GRAB_FRAME; - dr.value = streamstartline << 8; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_GRAB_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + streamstartline << 8, 0, NULL, 0, HZ); } -int usb_cpia_upload_frame(struct usb_device *dev, int forceupload) +static int usb_cpia_upload_frame(struct usb_device *dev, int forceupload) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_UPLOAD_FRAME; - dr.value = forceupload; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_UPLOAD_FRAME, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, forceupload, 0, NULL, 0, HZ); } -int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab) +static int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_SET_GRAB_MODE; - dr.value = continuousgrab; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_GRAB_MODE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, continuousgrab, + 0, NULL, 0, HZ); } -int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order) +static int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_SET_FORMAT; - dr.value = (subsample << 8) + size; - dr.index = order; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_FORMAT, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (subsample << 8) + size, order, NULL, 0, HZ); } -int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation) +static int usb_cpia_set_roi(struct usb_device *dev, int colstart, int colend, int rowstart, int rowend) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_SET_COMPRESSION; - dr.value = (decimation << 8) + compmode; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_ROI, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (colend << 8) + colstart, (rowend << 8) + rowstart, + NULL, 0, HZ); } -int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline) +static int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_INIT_STREAM_CAP; - dr.value = (streamstartline << 8) + skipframes; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_SET_COMPRESSION, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (decimation << 8) + compmode, 0, NULL, 0, HZ); } -int usb_cpia_finistreamcap(struct usb_device *dev) +#ifdef NOTUSED +static int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_FINI_STREAM_CAP; - dr.value = 0; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_INIT_STREAM_CAP, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (streamstartline << 8) + skipframes, 0, NULL, 0, HZ); } -int usb_cpia_startstreamcap(struct usb_device *dev) +static int usb_cpia_finistreamcap(struct usb_device *dev) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_START_STREAM_CAP; - dr.value = 0; - dr.index = 0; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_FINI_STREAM_CAP, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); } -int usb_cpia_endstreamcap(struct usb_device *dev) +static int usb_cpia_startstreamcap(struct usb_device *dev) { - devrequest dr; - - dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE); - dr.request = USB_REQ_CPIA_END_STREAM_CAP; - dr.value = 0; - dr.index = 0; - dr.length = 0; + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_START_STREAM_CAP, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); +} - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); +static int usb_cpia_endstreamcap(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CPIA_END_STREAM_CAP, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); } +#endif /* How much data is left in the scratch buf? */ #define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch)) static void cpia_parse_data(struct usb_cpia *cpia) { + struct cpia_frame *frame, *pframe; unsigned char *data = cpia->scratch; unsigned long l; - int done; - done = 0; - while (!done && scratch_left(data)) { - switch (cpia->state) { + frame = &cpia->frame[cpia->curframe]; + pframe = &cpia->frame[(cpia->curframe - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES]; + + while (1) { + if (!scratch_left(data)) + goto out; + + switch (frame->scanstate) { case STATE_SCANNING: { - unsigned char *begin = data; + struct cpia_frame_header *header; /* We need atleast 2 bytes for the magic value */ - if (scratch_left(data) < 2) { - done = 1; - break; - } + if (scratch_left(data) < 2) + goto out; + + header = (struct cpia_frame_header *)data; - /* 0x1968 is magic */ - printk("header: %X\n", (*data << 8) + *(data + 1)); - if ((*data == 0x19) && (*(data + 1) == 0x68)) { - cpia->state = STATE_HEADER; - printk("moving to header\n"); + if (be16_to_cpup(&header->magic) == CPIA_MAGIC) { + frame->scanstate = STATE_HEADER; break; } /* Woops, lost the header, find the end of the frame */ - if (scratch_left(data) < 4) { - done = 1; - break; - } + if (scratch_left(data) < 4) + goto out; - printk("Scanning for end of frame\n"); + /* See if we found the end of the frame */ while (scratch_left(data) >= 4) { - if ((*data == 0xFF) && - (*(data + 1) == 0xFF) && - (*(data + 2) == 0xFF) && - (*(data + 3) == 0xFF)) { + if (*((__u32 *)data) == 0xFFFFFFFF) { +printk("found end of frame\n"); data += 4; - break; +goto error; } data++; } -#ifdef CPIA_DEBUG - printk("scan: scanned %d bytes\n", data-begin); -#endif break; } case STATE_HEADER: /* We need atleast 64 bytes for the header */ - if (scratch_left(data) < 64) { - done = 1; - break; - } + if (scratch_left(data) < + sizeof(struct cpia_frame_header)) + goto out; + + memcpy(&frame->header, data, + sizeof(struct cpia_frame_header)); + + /* Skip over the header */ + data += sizeof(struct cpia_frame_header); + frame->hdrwidth = (frame->header.col_end - + frame->header.col_start) * 8; + frame->hdrheight = (frame->header.row_end - + frame->header.row_start) * 4; #ifdef CPIA_DEBUG - printk("header: framerate %d\n", data[41]); + printk("cpia: frame size %dx%d\n", + frame->hdrwidth, frame->hdrheight); + printk("cpia: frame %scompressed\n", + frame->header.comp_enable ? "" : "not "); #endif - data += 64; + frame->scanstate = STATE_LINES; + frame->curline = 0; - cpia->state = STATE_LINES; - break; case STATE_LINES: { - unsigned char *begin = data; - int found = 0; - - while (scratch_left(data)) { - if (*data == 0xFD) { - data++; - found = 1; - break; - } else if ((*data == 0xFF) && - (scratch_left(data) >= 3) && - (*(data + 1) == 0xFF) && - (*(data + 2) == 0xFF) && - (*(data + 3) == 0xFF)) { - data += 4; - cpia->curline = 144; - found = 1; - break; - } - - data++; + unsigned char *f, *end; + unsigned int len; + int i; + int y, u, y1, v, r, g, b; + + /* We want atleast 2 bytes for the length */ + if (scratch_left(data) < 2) + goto out; + + /* Grab the length */ + len = data[0] + (data[1] << 8); + +printk("line %d, %d bytes long\n", frame->curline, len); + /* Check to make sure it's nothing outrageous */ + if (len > (frame->hdrwidth * 2) + 1) { + printk(KERN_INFO "cpia: bad length, resynching\n"); + goto error; } - if (data-begin == 355 && cpia->frame[cpia->curframe].width != 64) { - int i; - char *f = cpia->frame[cpia->curframe].data, *b = begin; - - b += 2; - f += (cpia->frame[cpia->curframe].width * 3) * cpia->curline; - - for (i = 0; i < 176; i++) - f[(i * 3) + 0] = - f[(i * 3) + 1] = - f[(i * 3) + 2] = - b[(i * 2)]; + /* Make sure there's enough data for the entire line */ + if (scratch_left(data + 2) < len) + goto out; + + /* Skip over the length */ + data += 2; + + /* Is the end of the line there */ + if (data[len - 1] != 0xFD) { + printk(KERN_INFO "cpia: lost synch\n"); +end = data + len - 1 - 4; +printk("%02X %02X %02X %02X %02X %02X %02X %02X\n", +end[0], end[1], +end[2], end[3], +end[4], end[5], +end[6], end[7]); + goto error; } - if (found) { - cpia->curline++; - if (cpia->curline >= 144) { - wake_up(&cpia->wq); - cpia->state = STATE_SCANNING; - cpia->curline = 0; - cpia->curframe = -1; - done = 1; + /* Start at the beginning */ + end = data + len - 1; + + f = frame->data + (frame->width * 3 * frame->curline); + + if (frame->header.comp_enable) { + unsigned char *fp; + + /* We use the previous frame as a reference */ + fp = pframe->data + + (frame->width * 3 * frame->curline); + + while (data < end) { + if (*data & 1) { + /* Compress RLE data */ + i = *data >> 1; + memcpy(f, fp, i * 3); + f += (i * 3); + fp += (i * 3); + data++; + } else { + /* Raw data */ + +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +y = *data++ - 16; +u = *data++ - 128; +y1 = *data++ - 16; +v = *data++ - 128; +r = 104635 * v; +g = -25690 * u + -53294 * v; +b = 132278 * u; +y *= 76310; +y1 *= 76310; +*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y); +*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1); + fp += 6; +/* + f[0] = f[1] = f[2] = *data; + f += 3; + data += 2; + fp += 3; +*/ + } } } else { - data = begin; - done = 1; + /* Raw data */ + while (data < end) { +y = *data++ - 16; +u = *data++ - 128; +y1 = *data++ - 16; +v = *data++ - 128; +r = 104635 * v; +g = -25690 * u + -53294 * v; +b = 132278 * u; +y *= 76310; +y1 *= 76310; +*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y); +*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1); + } } - + +#ifdef CPIA_DEBUG + /* Make sure we found the end correctly */ + if (*data != 0xFD) + printk("cpia: missed end!\n"); +#endif + + /* Skip the last byte */ + data++; + + if (++frame->curline >= frame->hdrheight) + goto nextframe; + break; } } } +nextframe: + if (scratch_left(data) >= 4 && *((__u32 *)data) == 0xFFFFFFFF) { + data += 4; +printk("end of frame found normally\n"); +} + + frame->grabstate = FRAME_DONE; + cpia->curframe = -1; + + /* This will cause the process to request another frame */ + if (waitqueue_active(&frame->wq)) + wake_up_interruptible(&frame->wq); + + goto out; + +error: + frame->grabstate = FRAME_ERROR; + cpia->curframe = -1; + cpia->compress = 0; + + /* This will cause the process to request another frame */ + if (waitqueue_active(&frame->wq)) + wake_up_interruptible(&frame->wq); + +out: +printk("scanned %d bytes, %d left\n", data - cpia->scratch, scratch_left(data)); /* Grab the remaining */ l = scratch_left(data); memmove(cpia->scratch, data, l); - cpia->scratchlen = l; } /* - * For the moment there is no actual data compression (making blocks - * of data contiguous). This just checks the "frames" array for errors. + * Make all of the blocks of data contiguous */ -static int cpia_compress_isochronous(struct usb_isoc_desc *isodesc) +static int cpia_compress_isochronous(struct usb_cpia *cpia, struct usb_isoc_desc *isodesc) { - char *data = isodesc->data; + unsigned char *cdata, *data; int i, totlen = 0; + cdata = isodesc->data; + data = cpia->scratch + cpia->scratchlen; for (i = 0; i < isodesc->frame_count; i++) { - int n = isodesc->frames [i].frame_length; - int st = isodesc->frames [i].frame_status; - + int n = isodesc->frames[i].frame_length; #ifdef CPIA_DEBUG - /* Debugging */ + int st = isodesc->frames[i].frame_status; + if (st) printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); #endif + if ((cpia->scratchlen + n) > SCRATCH_BUF_SIZE) { + printk(KERN_ERR "cpia: scratch buf overflow!\n"); + return 0; + } + + if (n) + memmove(data, cdata, n); + data += n; + totlen += n; + cpia->scratchlen += n; + cdata += isodesc->frame_size; } return totlen; @@ -481,157 +538,114 @@ static int cpia_compress_isochronous(struct usb_isoc_desc *isodesc) static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id) { - struct usb_cpia *cpia = dev_id; - struct usb_device *dev = cpia->dev; + struct usb_cpia *cpia = (struct usb_cpia *)dev_id; struct cpia_sbuf *sbuf; int i; - char *p; if (!cpia->streaming) { printk("oops, not streaming, but interrupt\n"); return 0; } - if (cpia->curframe < 0) { - if (cpia->frame[0].state == FRAME_READY) { - cpia->curframe = 0; - cpia->frame[0].state = FRAME_GRABBING; -#ifdef CPIA_DEBUG - printk("capturing to frame 0\n"); -#endif - } else if (cpia->frame[1].state == FRAME_READY) { - cpia->curframe = 1; - cpia->frame[1].state = FRAME_GRABBING; -#ifdef CPIA_DEBUG - printk("capturing to frame 1\n"); -#endif -#ifdef CPIA_DEBUG - } else - printk("no frame available\n"); -#else - } -#endif - } - - sbuf = &cpia->sbuf[cpia->receivesbuf]; - + sbuf = &cpia->sbuf[cpia->cursbuf]; usb_kill_isoc(sbuf->isodesc); - /* Do something to it now */ - sbuf->len = cpia_compress_isochronous(sbuf->isodesc); + /* Copy the data received into our scratch buffer */ + len = cpia_compress_isochronous(cpia, sbuf->isodesc); -#ifdef CPIA_DEBUG - if (sbuf->len) - printk("%d bytes received\n", sbuf->len); -#endif +printk("%d bytes received\n", len); + /* If we don't have a frame we're current working on, complain */ + if (len) { + if (cpia->curframe < 0) + printk("cpia: received data, but no frame available\n"); + else + cpia_parse_data(cpia); + } - if (sbuf->len && cpia->curframe >= 0) { - if (sbuf->len > (SCRATCH_BUF_SIZE - cpia->scratchlen)) { - printk("overflow!\n"); - return 0; - } - memcpy(cpia->scratch + cpia->scratchlen, sbuf->data, sbuf->len); - cpia->scratchlen += sbuf->len; + for (i = 0; i < FRAMES_PER_DESC; i++) + sbuf->isodesc->frames[i].frame_length = FRAME_SIZE_PER_DESC; - cpia_parse_data(cpia); - } + /* Move to the next sbuf */ + cpia->cursbuf = (cpia->cursbuf + 1) % CPIA_NUMSBUF; /* Reschedule this block of Isochronous desc */ - /* - usb_run_isoc(sbuf->isodesc, NULL); - */ -/* - usb_schedule_isochronous(dev, sbuf->isodesc, cpia->sbuf[(cpia->receivesbuf + 2) % 3].isodesc); -*/ - - /* Move to the next one */ - cpia->receivesbuf = (cpia->receivesbuf + 1) % 3; + usb_run_isoc(sbuf->isodesc, cpia->sbuf[cpia->cursbuf].isodesc); - return 1; + return -1; } -int cpia_init_isoc(struct usb_cpia *cpia) +static int cpia_init_isoc(struct usb_cpia *cpia) { struct usb_device *dev = cpia->dev; struct usb_isoc_desc *id; int fx, err; - cpia->receivesbuf = 0; - + cpia->curframe = -1; + cpia->cursbuf = 0; cpia->scratchlen = 0; - cpia->curline = 0; - cpia->state = STATE_SCANNING; - /* ALT_ISOC is only doing double-buffering, not triple. */ - err = usb_init_isoc (dev, usb_rcvisocpipe (dev, 1), FRAMES_PER_DESC, cpia, - &cpia->sbuf[0].isodesc); - if (err) - printk ("cpia_init_isoc: usb_init_isoc() ret. %d\n", err); - err = usb_init_isoc (dev, usb_rcvisocpipe (dev, 1), FRAMES_PER_DESC, cpia, - &cpia->sbuf[1].isodesc); - if (err) - printk ("cpia_init_isoc: usb_init_isoc() ret. %d\n", err); + /* We double buffer the Iso lists */ + err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, + cpia, &cpia->sbuf[0].isodesc); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_init_isoc() ret %d\n", + err); + return -ENOMEM; + } - if (!cpia->sbuf[0].isodesc || !cpia->sbuf[1].isodesc) { - if (cpia->sbuf[0].isodesc) - usb_free_isoc (cpia->sbuf[0].isodesc); - if (cpia->sbuf[1].isodesc) - usb_free_isoc (cpia->sbuf[1].isodesc); + err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, + cpia, &cpia->sbuf[1].isodesc); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_init_isoc() ret %d\n", + err); + usb_free_isoc (cpia->sbuf[0].isodesc); return -ENOMEM; } -#if 0 - cpia->sbuf[0].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[0].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); - cpia->sbuf[1].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[1].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); - cpia->sbuf[2].isodesc = usb_allocate_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[2].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia); -#endif #ifdef CPIA_DEBUG printk("isodesc[0] @ %p\n", cpia->sbuf[0].isodesc); printk("isodesc[1] @ %p\n", cpia->sbuf[1].isodesc); -#if 0 - printk("isodesc[2] @ %p\n", cpia->sbuf[2].isodesc); -#endif #endif /* Set the Isoc. desc. parameters. */ /* First for desc. [0] */ - id = cpia->sbuf [0].isodesc; + id = cpia->sbuf[0].isodesc; id->start_type = START_ASAP; id->callback_frames = 10; /* on every 10th frame */ id->callback_fn = cpia_isoc_irq; id->data = cpia->sbuf[0].data; id->buf_size = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) - id->frames [fx].frame_length = FRAME_SIZE_PER_DESC; + id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; /* and the desc. [1] */ - id = cpia->sbuf [1].isodesc; + id = cpia->sbuf[1].isodesc; id->start_type = 0; /* will follow the first desc. */ id->callback_frames = 10; /* on every 10th frame */ id->callback_fn = cpia_isoc_irq; id->data = cpia->sbuf[1].data; id->buf_size = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; for (fx = 0; fx < FRAMES_PER_DESC; fx++) - id->frames [fx].frame_length = FRAME_SIZE_PER_DESC; + id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; - usb_run_isoc (cpia->sbuf[0].isodesc, NULL); - usb_run_isoc (cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); - -#if 0 - usb_schedule_isochronous(dev, cpia->sbuf[0].isodesc, NULL); - usb_schedule_isochronous(dev, cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); - usb_schedule_isochronous(dev, cpia->sbuf[2].isodesc, cpia->sbuf[1].isodesc); -#endif + usb_run_isoc(cpia->sbuf[0].isodesc, NULL); + usb_run_isoc(cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); #ifdef CPIA_DEBUG printk("done scheduling\n"); #endif - if (usb_set_interface(cpia->dev, 1, 3)) { - printk("cpia_set_interface error\n"); - return -EINVAL; + /* Alternate interface 3 is is the biggest frame size */ + if (usb_set_interface(cpia->dev, 1, 3) < 0) { + printk("usb_set_interface error\n"); + return -EBUSY; } - usb_cpia_startstreamcap(cpia->dev); +#if 0 + if (usb_cpia_grab_frame(dev, 120) < 0) { + printk(KERN_INFO "cpia_grab_frame error\n"); + return -EBUSY; + } +#endif cpia->streaming = 1; #ifdef CPIA_DEBUG @@ -641,41 +655,102 @@ int cpia_init_isoc(struct usb_cpia *cpia) return 0; } -void cpia_stop_isoc(struct usb_cpia *cpia) +static void cpia_stop_isoc(struct usb_cpia *cpia) { - struct usb_device *dev = cpia->dev; - if (!cpia->streaming) return; cpia->streaming = 0; - /* Stop the streaming */ - usb_cpia_endstreamcap(cpia->dev); + /* Turn off continuous grab */ + if (usb_cpia_set_grab_mode(cpia->dev, 0) < 0) { + printk(KERN_INFO "cpia_set_grab_mode error\n"); + return /* -EBUSY */; + } + +#if 0 + if (usb_cpia_grab_frame(cpia->dev, 0) < 0) { + printk(KERN_INFO "cpia_grab_frame error\n"); + return /* -EBUSY */; + } +#endif /* Set packet size to 0 */ - if (usb_set_interface(cpia->dev, 1, 0)) { - printk("cpia_set_interface error\n"); + if (usb_set_interface(cpia->dev, 1, 0) < 0) { + printk(KERN_INFO "usb_set_interface error\n"); return /* -EINVAL */; } /* Unschedule all of the iso td's */ - usb_kill_isoc (cpia->sbuf[1].isodesc); - usb_kill_isoc (cpia->sbuf[0].isodesc); -#if 0 - usb_unschedule_isochronous(dev, cpia->sbuf[2].isodesc); - usb_unschedule_isochronous(dev, cpia->sbuf[1].isodesc); - usb_unschedule_isochronous(dev, cpia->sbuf[0].isodesc); -#endif + usb_kill_isoc(cpia->sbuf[1].isodesc); + usb_kill_isoc(cpia->sbuf[0].isodesc); /* Delete them all */ - usb_free_isoc (cpia->sbuf[1].isodesc); - usb_free_isoc (cpia->sbuf[0].isodesc); -#if 0 - usb_delete_isochronous(dev, cpia->sbuf[2].isodesc); - usb_delete_isochronous(dev, cpia->sbuf[1].isodesc); - usb_delete_isochronous(dev, cpia->sbuf[0].isodesc); -#endif + usb_free_isoc(cpia->sbuf[1].isodesc); + usb_free_isoc(cpia->sbuf[0].isodesc); +} + +static int cpia_new_frame(struct usb_cpia *cpia, int framenum) +{ + struct cpia_frame *frame; + int width, height; + +printk("new frame %d\n", framenum); + if (framenum == -1) { + int i; + for (i = 0; i < CPIA_NUMFRAMES; i++) + if (cpia->frame[i].grabstate == FRAME_READY) + break; + + if (i >= CPIA_NUMFRAMES) { + printk("no frame ready\n"); + return 0; + } + + framenum = i; +printk("using frame %d\n", framenum); + } + + if (cpia->curframe != -1 && cpia->curframe != framenum) + return 0; + + frame = &cpia->frame[framenum]; + width = frame->width; + height = frame->height; + + /* Make sure it's not too big */ + if (width > 352) + width = 352; + width = (width / 8) * 8; /* Multiple of 8 */ + + if (height > 288) + height = 288; + height = (height / 4) * 4; /* Multiple of 4 */ + + /* Set the ROI they want */ + if (usb_cpia_set_roi(cpia->dev, 0, width / 8, 0, height / 4) < 0) + return -EBUSY; + + if (usb_cpia_set_compression(cpia->dev, cpia->compress ? 1 : 0, 0) < 0) { + printk(KERN_INFO "cpia_set_compression error\n"); + return -EBUSY; + } + + /* We want a fresh frame every 30 we get */ + cpia->compress = (cpia->compress + 1) % 30; + + /* Grab the frame */ + if (usb_cpia_upload_frame(cpia->dev, 1) < 0) { + printk(KERN_INFO "cpia_upload_frame error\n"); + return -EBUSY; + } + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + + cpia->curframe = framenum; + + return 0; } /* Video 4 Linux API */ @@ -688,13 +763,14 @@ static int cpia_open(struct video_device *dev, int flags) printk("cpia_open\n"); #endif + cpia->frame[0].grabstate = FRAME_UNUSED; + cpia->frame[1].grabstate = FRAME_UNUSED; + + /* Allocate memory for the frame buffers */ cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE); if (!cpia->fbuf) goto open_err_ret; - cpia->frame[0].state = FRAME_DONE; - cpia->frame[1].state = FRAME_DONE; - cpia->frame[0].data = cpia->fbuf; cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE; #ifdef CPIA_DEBUG @@ -710,37 +786,17 @@ static int cpia_open(struct video_device *dev, int flags) if (!cpia->sbuf[1].data) goto open_err_on1; -#if 0 - cpia->sbuf[0].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); - if (!cpia->sbuf[0].data) - goto open_err_on0; - - cpia->sbuf[1].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); - if (!cpia->sbuf[1].data) - goto open_err_on1; - - cpia->sbuf[2].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL); - if (!cpia->sbuf[2].data) - goto open_err_on2; -#endif - #ifdef CPIA_DEBUG printk("sbuf[0] @ %p\n", cpia->sbuf[0].data); printk("sbuf[1] @ %p\n", cpia->sbuf[1].data); -#if 0 - printk("sbuf[2] @ %p\n", cpia->sbuf[2].data); -#endif #endif - cpia->curframe = -1; - cpia->receivesbuf = 0; - - usb_cpia_initstreamcap(cpia->dev, 0, 60); - err = cpia_init_isoc(cpia); if (err) goto open_err_on2; + MOD_INC_USE_COUNT; + return 0; open_err_on2: @@ -761,13 +817,12 @@ static void cpia_close(struct video_device *dev) printk("cpia_close\n"); #endif - cpia_stop_isoc(cpia); + MOD_DEC_USE_COUNT; - usb_cpia_finistreamcap(cpia->dev); + cpia_stop_isoc(cpia); rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); - kfree(cpia->sbuf[2].data); kfree(cpia->sbuf[1].data); kfree(cpia->sbuf[0].data); } @@ -791,23 +846,32 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_capability b; +#ifdef CPIA_DEBUG + printk("GCAP\n"); +#endif + strcpy(b.name, "CPiA USB Camera"); - b.type = VID_TYPE_CAPTURE /* | VID_TYPE_SUBCAPTURE */; + b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; b.channels = 1; b.audios = 0; - b.maxwidth = 176 /* 352 */; - b.maxheight = 144 /* 240 */; - b.minwidth = 176 /* (Something small?) */; - b.minheight = 144 /* " " */; + b.maxwidth = 352; /* CIF */ + b.maxheight = 288; /* " */ + b.minwidth = 176; /* QCIF */ + b.minheight = 144; /* " */ if (copy_to_user(arg, &b, sizeof(b))) return -EFAULT; + return 0; } case VIDIOCGCHAN: { struct video_channel v; +#ifdef CPIA_DEBUG + printk("GCHAN\n"); +#endif + if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if (v.channel != 0) @@ -826,47 +890,14 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { int v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if (v != 0) - return -EINVAL; - - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner v; - - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if (v.tuner) - return -EINVAL; - - strcpy(v.name, "Format"); - - v.rangelow = 0; - v.rangehigh = 0; - v.flags = 0; - v.mode = VIDEO_MODE_AUTO; - - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner v; +#ifdef CPIA_DEBUG + printk("SCHAN\n"); +#endif if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; - if (v.tuner) - return -EINVAL; - - if (v.mode != VIDEO_MODE_AUTO) + if (v != 0) return -EINVAL; return 0; @@ -875,6 +906,10 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_picture p; +#ifdef CPIA_DEBUG + printk("GPICT\n"); +#endif + p.colour = 0x8000; /* Damn British people :) */ p.hue = 0x8000; p.brightness = 180 << 8; /* XXX */ @@ -895,6 +930,10 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_picture p; +#ifdef CPIA_DEBUG + printk("SPICT\n"); +#endif + if (copy_from_user(&p, arg, sizeof(p))) return -EFAULT; @@ -910,7 +949,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) struct video_window vw; #ifdef CPIA_DEBUG - printk("VIDIOCSWIN\n"); + printk("SWIN\n"); #endif if (copy_from_user(&vw, arg, sizeof(vw))) @@ -924,6 +963,8 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if (vw.width != 144) return -EINVAL; + cpia->compress = 0; + return 0; } case VIDIOCGWIN: @@ -931,7 +972,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) struct video_window vw; #ifdef CPIA_DEBUG - printk("VIDIOCGWIN\n"); + printk("GWIN\n"); #endif vw.x = 0; @@ -949,6 +990,9 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCGMBUF: { struct video_mbuf vm; +#ifdef CPIA_DEBUG + printk("MBUF\n"); +#endif memset(&vm, 0, sizeof(vm)); vm.size = MAX_FRAME_SIZE * 2; @@ -965,6 +1009,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_mmap vm; + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; @@ -980,53 +1025,85 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if ((vm.frame != 0) && (vm.frame != 1)) return -EINVAL; + if (cpia->frame[vm.frame].grabstate == FRAME_GRABBING) + return -EBUSY; + + /* Don't compress if the size changed */ + if ((cpia->frame[vm.frame].width != vm.width) || + (cpia->frame[vm.frame].height != vm.height)) + cpia->compress = 0; + cpia->frame[vm.frame].width = vm.width; cpia->frame[vm.frame].height = vm.height; - /* Mark it as free */ - cpia->frame[vm.frame].state = FRAME_READY; + /* Mark it as ready */ + cpia->frame[vm.frame].grabstate = FRAME_READY; - return 0; + return cpia_new_frame(cpia, vm.frame); } case VIDIOCSYNC: { int frame; +#ifdef CPIA_DEBUG + printk("SYNC\n"); +#endif + if (copy_from_user((void *)&frame, arg, sizeof(int))) return -EFAULT; #ifdef CPIA_DEBUG - printk("syncing to frame %d\n", frame); + printk("cpia: syncing to frame %d\n", frame); #endif - switch (cpia->frame[frame].state) { - case FRAME_UNUSED: - return -EINVAL; - case FRAME_READY: - case FRAME_GRABBING: - interruptible_sleep_on(&cpia->wq); - case FRAME_DONE: - cpia->frame[frame].state = FRAME_UNUSED; - break; + + switch (cpia->frame[frame].grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: +redo: + do { +printk("enter sleeping\n"); + interruptible_sleep_on(&cpia->frame[frame].wq); +printk("back from sleeping\n"); + if (signal_pending(current)) + return -EINTR; + } while (cpia->frame[frame].grabstate == + FRAME_GRABBING); + + if (cpia->frame[frame].grabstate == + FRAME_ERROR) { + int ret; + + if ((ret = cpia_new_frame(cpia, frame)) < 0) + return ret; + goto redo; + } + case FRAME_DONE: + cpia->frame[frame].grabstate = FRAME_UNUSED; + break; } + #ifdef CPIA_DEBUG - printk("synced to frame %d\n", frame); + printk("cpia: finished, synced to frame %d\n", frame); #endif - return 0; + + return cpia_new_frame(cpia, -1); } + case VIDIOCKEY: + return 0; case VIDIOCCAPTURE: return -EINVAL; case VIDIOCGFBUF: - return -EINVAL; case VIDIOCSFBUF: return -EINVAL; - case VIDIOCKEY: - return 0; - case VIDIOCGFREQ: + case VIDIOCGTUNER: + case VIDIOCSTUNER: return -EINVAL; + case VIDIOCGFREQ: case VIDIOCSFREQ: return -EINVAL; case VIDIOCGAUDIO: - return -EINVAL; case VIDIOCSAUDIO: return -EINVAL; default: @@ -1037,17 +1114,14 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock) { +#if 0 struct usb_cpia *cpia = (struct usb_cpia *)dev; int len; +#endif #ifdef CPIA_DEBUG printk("cpia_read: %ld bytes\n", count); #endif -#if 0 - len = cpia_capture(cpia, buf, count); - - return len; -#endif return 0; } @@ -1064,17 +1138,17 @@ static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long si return -EINVAL; pos = (unsigned long)cpia->fbuf; - while (size > 0) - { - page = kvirt_to_phys(pos); + while (size > 0) { + page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; + + start += PAGE_SIZE; + pos += PAGE_SIZE; if (size > PAGE_SIZE) - size-=PAGE_SIZE; + size -= PAGE_SIZE; else - size=0; + size = 0; } return 0; @@ -1097,87 +1171,92 @@ static struct video_device cpia_template = { 0 }; -static void usb_cpia_configure(struct usb_cpia *cpia) +static int usb_cpia_configure(struct usb_cpia *cpia) { struct usb_device *dev = cpia->dev; unsigned char version[4]; - unsigned char pnpid[6]; - unsigned char camerastat[8]; - unsigned char *buf; - if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { - printk (KERN_INFO " Failed usb_set_configuration: CPIA\n"); - return; + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) < 0) { + printk(KERN_INFO "cpia: usb_set_configuration failed\n"); + return -EBUSY; } - if (usb_cpia_get_version(dev, version)) { - printk("cpia_get_version error\n"); - return; + /* Set packet size to 0 */ + if (usb_set_interface(dev, 1, 0) < 0) { + printk(KERN_INFO "usb_set_interface error\n"); + return -EBUSY; } - printk("cpia: Firmware v%d.%d, VC Hardware v%d.%d\n", - version[0], version[1], version[2], version[3]); - - if (usb_cpia_get_pnp_id(dev, pnpid)) { - printk("cpia_get_pnp_id error\n"); - return; + if (usb_cpia_get_version(dev, version) < 0) { + printk(KERN_INFO "cpia_get_version error\n"); + return -EBUSY; } - printk("cpia: PnP Id: Vendor: %X, Product: %X, Revision: %X\n", - (pnpid[1] << 8) + pnpid[0], (pnpid[3] << 8) + pnpid[2], - (pnpid[5] << 8) + pnpid[4]); + printk("cpia: Firmware v%d.%d, VC Hardware v%d.%d\n", + version[0], version[1], version[2], version[3]); memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template)); - init_waitqueue_head(&cpia->wq); + init_waitqueue_head(&cpia->frame[0].wq); + init_waitqueue_head(&cpia->frame[1].wq); if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) { - printk("video_register_device failed\n"); - return; + printk(KERN_INFO "video_register_device failed\n"); + return -EBUSY; } - if (usb_cpia_goto_hi_power(dev)) { - printk("cpia_goto_hi_power error\n"); - return; + if (usb_cpia_goto_hi_power(dev) < 0) { + printk(KERN_INFO "cpia_goto_hi_power error\n"); + goto error; } - if (usb_cpia_get_vp_version(dev, version)) { - printk("cpia_get_vp_version error\n"); - return; + if (usb_cpia_get_vp_version(dev, version) < 0) { + printk(KERN_INFO "cpia_get_vp_version error\n"); + goto error; } printk("cpia: VP v%d rev %d\n", version[0], version[1]); printk("cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]); - /* Turn off continuous grab */ - if (usb_cpia_set_grab_mode(dev, 1)) { - printk("cpia_set_grab_mode error\n"); - return; + /* Turn on continuous grab */ + if (usb_cpia_set_grab_mode(dev, 1) < 0) { + printk(KERN_INFO "cpia_set_grab_mode error\n"); + goto error; } /* Set up the sensor to be 30fps */ - if (usb_cpia_set_sensor_fps(dev, 1, 0)) { - printk("cpia_set_sensor_fps error\n"); - return; + if (usb_cpia_set_sensor_fps(dev, 1, 0) < 0) { + printk(KERN_INFO "cpia_set_sensor_fps error\n"); + goto error; } - /* Set video into QCIF mode, and order into YUYV mode */ - if (usb_cpia_set_format(dev, CPIA_QCIF, 1, CPIA_YUYV)) { - printk("cpia_set_format error\n"); - return; + /* Set video into CIF mode, and order into YUYV mode */ + if (usb_cpia_set_format(dev, CPIA_CIF, 1, CPIA_YUYV) < 0) { + printk(KERN_INFO "cpia_set_format error\n"); + goto error; } /* Turn off compression */ - if (usb_cpia_set_compression(dev, 0, 0)) { - printk("cpia_set_compression error\n"); - return; + if (usb_cpia_set_compression(dev, 0, 0) < 0) { + printk(KERN_INFO "cpia_set_compression error\n"); + goto error; } + + cpia->compress = 0; + + return 0; + +error: + video_unregister_device(&cpia->vdev); + + kfree(cpia); + + return -EBUSY; } static int cpia_probe(struct usb_device *dev) { struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; struct usb_cpia *cpia; /* We don't handle multi-config cameras */ @@ -1191,6 +1270,8 @@ static int cpia_probe(struct usb_device *dev) return -1; if (dev->descriptor.idProduct != 0x0002) return -1; + + /* Checking vendor/product should be enough, but what the hell */ if (interface->bInterfaceClass != 0xFF) return -1; if (interface->bInterfaceSubClass != 0x00) @@ -1209,9 +1290,7 @@ static int cpia_probe(struct usb_device *dev) dev->private = cpia; cpia->dev = dev; - usb_cpia_configure(cpia); - - return 0; + return usb_cpia_configure(cpia); } static void cpia_disconnect(struct usb_device *dev) @@ -1231,9 +1310,6 @@ static struct usb_driver cpia_driver = { { NULL, NULL } }; -/* - * This should be a separate module. - */ int usb_cpia_init(void) { usb_register(&cpia_driver); @@ -1241,6 +1317,11 @@ int usb_cpia_init(void) return 0; } +void usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + #ifdef MODULE int init_module(void) { @@ -1249,6 +1330,7 @@ int init_module(void) void cleanup_module(void) { + usb_cpia_cleanup(); } #endif diff --git a/drivers/usb/cpia.h b/drivers/usb/cpia.h index 4596ec159..f1d9ed7cd 100644 --- a/drivers/usb/cpia.h +++ b/drivers/usb/cpia.h @@ -78,9 +78,10 @@ #define CPIA_YUYV 0 #define CPIA_UYVY 1 -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +/* #define STREAM_BUF_SIZE (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC) */ -#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) #define FRAMES_PER_DESC 10 #define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ @@ -91,54 +92,97 @@ enum { STATE_LINES, /* Parsing lines */ }; +#define CPIA_MAGIC 0x1968 +struct cpia_frame_header { + __u16 magic; /* 0 - 1 */ + __u16 timestamp; /* 2 - 3 */ + __u16 unused; /* 4 - 5 */ + __u16 timestamp1; /* 6 - 7 */ + __u8 unused1[8]; /* 8 - 15 */ + __u8 video_size; /* 16 0 = QCIF, 1 = CIF */ + __u8 sub_sample; /* 17 0 = 4:2:0, 1 = 4:2:2 */ + __u8 yuv_order; /* 18 0 = YUYV, 1 = UYVY */ + __u8 unused2[5]; /* 19 - 23 */ + __u8 col_start; /* 24 */ + __u8 col_end; /* 25 */ + __u8 row_start; /* 26 */ + __u8 row_end; /* 27 */ + __u8 comp_enable; /* 28 0 = non compressed, 1 = compressed */ + __u8 decimation; /* 29 0 = no decimation, 1 = decimation */ + __u8 y_thresh; /* 30 */ + __u8 uv_thresh; /* 31 */ + __u8 system_state; /* 32 */ + __u8 grab_state; /* 33 */ + __u8 stream_state; /* 34 */ + __u8 fatal_error; /* 35 */ + __u8 cmd_error; /* 36 */ + __u8 debug_flags; /* 37 */ + __u8 camera_state_7; /* 38 */ + __u8 camera_state_8; /* 39 */ + __u8 cr_achieved; /* 40 */ + __u8 fr_achieved; /* 41 */ + __u8 unused3[22]; /* 42 - 63 */ +}; + struct usb_device; struct cpia_sbuf { char *data; - int len; struct usb_isoc_desc *isodesc; -#if 0 - void *isodesc; -#endif }; enum { - FRAME_READY, /* Ready to grab into */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ FRAME_GRABBING, /* In the process of being grabbed into */ FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_ERROR, /* Something bad happened while processing */ }; struct cpia_frame { - char *data; - int width; - int height; - int state; + char *data; /* Frame buffer */ + + struct cpia_frame_header header; /* Header from stream */ + + int width; /* Width application is expecting */ + int height; /* Height */ + + int hdrwidth; /* Width the frame actually is */ + int hdrheight; /* Height */ + + int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int curline; /* Line of frame we're working on */ + + wait_queue_head_t wq; /* Processes waiting */ }; +#define CPIA_NUMFRAMES 2 +#define CPIA_NUMSBUF 2 + struct usb_cpia { struct video_device vdev; /* Device structure */ struct usb_device *dev; - int streaming; + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ - char *fbuf; /* Videodev buffer area */ + int compress; /* Should the next frame be compressed? */ - int curframe; - struct cpia_frame frame[2]; /* Double buffering */ + char *fbuf; /* Videodev buffer area */ - int receivesbuf; /* Current receiving sbuf */ - struct cpia_sbuf sbuf[3]; /* Triple buffering */ + int curframe; + struct cpia_frame frame[CPIA_NUMFRAMES]; /* Double buffering */ - int state; /* Current scanning state */ - int curline; + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ - char scratch[SCRATCH_BUF_SIZE]; + /* Scratch space from the Isochronous pipe */ + unsigned char scratch[SCRATCH_BUF_SIZE]; int scratchlen; - - wait_queue_head_t wq; }; #endif diff --git a/drivers/usb/ezusb.c b/drivers/usb/ezusb.c index 4c00ef143..59e7b4a67 100644 --- a/drivers/usb/ezusb.c +++ b/drivers/usb/ezusb.c @@ -27,7 +27,8 @@ * Implemented EZUSB_SETINTERFACE, more sanity checks for EZUSB_BULK. * Preliminary ISO support * 0.3 01.09.99 Async Bulk and ISO support - * 0.4 01.09.99 + * 0.4 01.09.99 Set callback_frames to the total number of frames to make + * it work with OHCI-HCD * */ @@ -40,9 +41,10 @@ #include <linux/list.h> #include <linux/vmalloc.h> #include <linux/slab.h> -#include <linux/spinlock.h> #include <asm/uaccess.h> +#include <linux/spinlock.h> + #include "usb.h" #include "ezusb.h" @@ -76,9 +78,9 @@ struct async { /* --------------------------------------------------------------------- */ -extern inline unsigned ld2(unsigned int x) +extern inline unsigned int ld2(unsigned int x) { - unsigned r = 0; + unsigned int r = 0; if (x >= 0x10000) { x >>= 16; @@ -101,6 +103,17 @@ extern inline unsigned ld2(unsigned int x) return r; } +#if 0 +/* why doesn't this work properly on i386? */ +extern inline unsigned int ld2(unsigned int x) +{ + unsigned int r; + + __asm__("bsrl %1,%0" : "=r" (r) : "g" (x)); + return r; +} +#endif + /* --------------------------------------------------------------------- */ extern __inline__ void async_removelist(struct async *as) @@ -173,22 +186,41 @@ extern __inline__ struct async *async_getpending(struct ezusb *ez, void *context /* --------------------------------------------------------------------- */ -static int async_completed(int status, void *__buffer, int rval, void *dev_id) +static int bulk_completed(int status, void *__buffer, int rval, void *dev_id) { struct async *as = (struct async *)dev_id; struct ezusb *ez = as->ez; unsigned cnt; -printk(KERN_DEBUG "ezusb: async_completed: status %d rval %d\n", status, rval); +printk(KERN_DEBUG "ezusb: bulk_completed: status %d rval %d\n", status, rval); as->completed.length = rval; - if (as->numframes > 0) { - as->completed.status = USB_ST_NOERROR; - for (cnt = 0; cnt < as->numframes; cnt++) { - as->completed.isostat[cnt].status = as->desc.iso->frames[cnt].frame_status; - as->completed.isostat[cnt].length = as->desc.iso->frames[cnt].frame_length; - } - } else - as->completed.status = status; + as->completed.status = status; + spin_lock(&ez->lock); + list_del(&as->asynclist); + list_add_tail(&as->asynclist, &ez->async_completed); + spin_unlock(&ez->lock); + wake_up(&ez->wait); + return 0; +} + +static int iso_completed(int status, void *__buffer, int rval, void *dev_id) +{ +#if 1 + struct async *as = (struct async *)dev_id; +#else + struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; + struct async *as = (struct async *)id->context; +#endif + struct ezusb *ez = as->ez; + unsigned cnt; + +printk(KERN_DEBUG "ezusb: iso_completed: status %d rval %d\n", status, rval); + as->completed.length = rval; + as->completed.status = USB_ST_NOERROR; + for (cnt = 0; cnt < as->numframes; cnt++) { + as->completed.isostat[cnt].status = as->desc.iso->frames[cnt].frame_status; + as->completed.isostat[cnt].length = as->desc.iso->frames[cnt].frame_length; + } spin_lock(&ez->lock); list_del(&as->asynclist); list_add_tail(&as->asynclist, &ez->async_completed); @@ -264,7 +296,6 @@ static ssize_t ezusb_read(struct file *file, char *buf, size_t sz, loff_t *ppos) unsigned ret = 0; unsigned len; unsigned char b[64]; - devrequest dr; int i; if (*ppos < 0 || *ppos >= 0x10000) @@ -280,15 +311,10 @@ static ssize_t ezusb_read(struct file *file, char *buf, size_t sz, loff_t *ppos) len = sizeof(b); if (pos + len > 0x10000) len = 0x10000 - pos; - dr.requesttype = 0xc0; - dr.request = 0xa0; - dr.value = pos; - dr.index = 0; - dr.length = len; - i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), &dr, b, len); + i = usb_control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), 0xa0, 0xc0, pos, 0, b, len, HZ); if (i) { up(&ez->mutex); - printk(KERN_WARNING "ezusb: upload failed pos %u len %u ret %d\n", dr.value, dr.length, i); + printk(KERN_WARNING "ezusb: upload failed pos %u len %u ret %d\n", pos, len, i); *ppos = pos; if (ret) return ret; @@ -318,7 +344,6 @@ static ssize_t ezusb_write(struct file *file, const char *buf, size_t sz, loff_t unsigned ret = 0; unsigned len; unsigned char b[64]; - devrequest dr; int i; if (*ppos < 0 || *ppos >= 0x10000) @@ -341,15 +366,10 @@ static ssize_t ezusb_write(struct file *file, const char *buf, size_t sz, loff_t return ret; return -EFAULT; } - dr.requesttype = 0x40; - dr.request = 0xa0; - dr.value = pos; - dr.index = 0; - dr.length = len; - i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), &dr, b, len); + i = usb_control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), 0xa0, 0x40, pos, 0, b, len, HZ); if (i) { up(&ez->mutex); - printk(KERN_WARNING "ezusb: download failed pos %u len %u ret %d\n", dr.value, dr.length, i); + printk(KERN_WARNING "ezusb: download failed pos %u len %u ret %d\n", pos, len, i); *ppos = pos; if (ret) return ret; @@ -401,7 +421,7 @@ static int ezusb_release(struct inode *inode, struct file *file) static int ezusb_control(struct usb_device *usbdev, unsigned char requesttype, unsigned char request, unsigned short value, unsigned short index, unsigned short length, - void *data) + unsigned int timeout, void *data) { unsigned char *tbuf = NULL; unsigned int pipe; @@ -429,7 +449,7 @@ static int ezusb_control(struct usb_device *usbdev, unsigned char requesttype, } } } - i = usb_control_msg(usbdev, pipe, request, requesttype, value, index, tbuf, length); + i = usb_control_msg(usbdev, pipe, request, requesttype, value, index, tbuf, length, timeout); if (i < 0) { if (length > 0) free_page((unsigned long)tbuf); @@ -444,7 +464,7 @@ static int ezusb_control(struct usb_device *usbdev, unsigned char requesttype, return i; } -static int ezusb_bulk(struct usb_device *usbdev, unsigned int ep, unsigned int length, void *data) +static int ezusb_bulk(struct usb_device *usbdev, unsigned int ep, unsigned int length, unsigned int timeout, void *data) { unsigned char *tbuf = NULL; unsigned int pipe; @@ -473,7 +493,7 @@ static int ezusb_bulk(struct usb_device *usbdev, unsigned int ep, unsigned int l } } } - ret = usbdev->bus->op->bulk_msg(usbdev, pipe, tbuf, length, &len2); + ret = usbdev->bus->op->bulk_msg(usbdev, pipe, tbuf, length, &len2, timeout); if (ret < 0) { if (length > 0) free_page((unsigned long)tbuf); @@ -553,7 +573,7 @@ static int ezusb_requestbulk(struct ezusb *ez, struct ezusb_asyncbulk *ab) } } async_newpending(as); - if (!(as->desc.bulk = usb_request_bulk(ez->usbdev, pipe, async_completed, as->data, ab->len, as))) { + if (!(as->desc.bulk = usb_request_bulk(ez->usbdev, pipe, bulk_completed, as->data, ab->len, as))) { async_removelist(as); goto err_inval; } @@ -592,6 +612,7 @@ static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigne if (!(maxpkt = usb_maxpacket(ez->usbdev, pipe, !(ai->ep & 0x80)))) return -EINVAL; dsize = maxpkt * ai->framecnt; +printk(KERN_DEBUG "ezusb: iso: dsize %d\n", dsize); if (dsize > 65536) return -EINVAL; order = ld2(dsize >> PAGE_SHIFT); @@ -603,6 +624,7 @@ static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigne assize = sizeof(struct async) + ai->framecnt * sizeof(struct ezusb_isoframestat); if (!(as = kmalloc(assize, GFP_KERNEL))) return -ENOMEM; +printk(KERN_DEBUG "ezusb: iso: assize %d\n", assize); memset(as, 0, assize); INIT_LIST_HEAD(&as->asynclist); as->ez = ez; @@ -630,8 +652,8 @@ static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigne } as->desc.iso->start_type = START_ABSOLUTE; as->desc.iso->start_frame = ai->startframe; - as->desc.iso->callback_frames = 0; - as->desc.iso->callback_fn = async_completed; + as->desc.iso->callback_frames = /*0*/ai->framecnt; + as->desc.iso->callback_fn = iso_completed; as->desc.iso->data = as->data; as->desc.iso->buf_size = dsize; for (j = 0; j < ai->framecnt; j++) { @@ -709,9 +731,13 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, DECLARE_WAITQUEUE(wait, current); struct usb_proc_ctrltransfer pctrl; struct usb_proc_bulktransfer pbulk; + struct usb_proc_old_ctrltransfer opctrl; + struct usb_proc_old_bulktransfer opbulk; struct usb_proc_setinterface psetintf; struct ezusb_ctrltransfer ctrl; struct ezusb_bulktransfer bulk; + struct ezusb_old_ctrltransfer octrl; + struct ezusb_old_bulktransfer obulk; struct ezusb_setinterface setintf; struct ezusb_asyncbulk abulk; struct ezusb_asynciso aiso; @@ -732,7 +758,8 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, break; } ret = ezusb_control(ez->usbdev, pctrl.requesttype, pctrl.request, - pctrl.value, pctrl.index, pctrl.length, pctrl.data); + pctrl.value, pctrl.index, pctrl.length, + (pctrl.timeout * HZ + 500) / 1000, pctrl.data); break; case USB_PROC_BULK: @@ -740,7 +767,25 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ret = -EFAULT; break; } - ret = ezusb_bulk(ez->usbdev, pbulk.ep, pbulk.len, pbulk.data); + ret = ezusb_bulk(ez->usbdev, pbulk.ep, pbulk.len, + (pbulk.timeout * HZ + 500) / 1000, pbulk.data); + break; + + case USB_PROC_OLD_CONTROL: + if (copy_from_user(&opctrl, (void *)arg, sizeof(opctrl))) { + ret = -EFAULT; + break; + } + ret = ezusb_control(ez->usbdev, opctrl.requesttype, opctrl.request, + opctrl.value, opctrl.index, opctrl.length, HZ, opctrl.data); + break; + + case USB_PROC_OLD_BULK: + if (copy_from_user(&opbulk, (void *)arg, sizeof(opbulk))) { + ret = -EFAULT; + break; + } + ret = ezusb_bulk(ez->usbdev, opbulk.ep, opbulk.len, 5*HZ, opbulk.data); break; case USB_PROC_RESETEP: @@ -772,12 +817,9 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ret = -EFAULT; break; } - if (ctrl.dlen != ctrl.length) { - ret = -EINVAL; - break; - } ret = ezusb_control(ez->usbdev, ctrl.requesttype, ctrl.request, - ctrl.value, ctrl.index, ctrl.length, ctrl.data); + ctrl.value, ctrl.index, ctrl.length, + (ctrl.timeout * HZ + 500) / 1000, ctrl.data); break; case EZUSB_BULK: @@ -785,7 +827,29 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ret = -EFAULT; break; } - ret = ezusb_bulk(ez->usbdev, bulk.ep, bulk.len, bulk.data); + ret = ezusb_bulk(ez->usbdev, bulk.ep, bulk.len, + (bulk.timeout * HZ + 500) / 1000, bulk.data); + break; + + case EZUSB_OLD_CONTROL: + if (copy_from_user(&octrl, (void *)arg, sizeof(octrl))) { + ret = -EFAULT; + break; + } + if (octrl.dlen != octrl.length) { + ret = -EINVAL; + break; + } + ret = ezusb_control(ez->usbdev, octrl.requesttype, octrl.request, + octrl.value, octrl.index, octrl.length, HZ, octrl.data); + break; + + case EZUSB_OLD_BULK: + if (copy_from_user(&obulk, (void *)arg, sizeof(obulk))) { + ret = -EFAULT; + break; + } + ret = ezusb_bulk(ez->usbdev, obulk.ep, obulk.len, 5*HZ, obulk.data); break; case EZUSB_RESETEP: @@ -813,6 +877,7 @@ static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, break; case EZUSB_ASYNCCOMPLETED: + as = NULL; current->state = TASK_INTERRUPTIBLE; add_wait_queue(&ez->wait, &wait); for (;;) { @@ -928,9 +993,11 @@ static int ezusb_probe(struct usb_device *usbdev) if (usbdev->descriptor.bNumConfigurations != 1) return -1; +#if 0 /* We don't handle multiple interfaces */ if (usbdev->config[0].bNumInterfaces != 1) return -1; +#endif down(&ez->mutex); if (ez->usbdev) { diff --git a/drivers/usb/ezusb.h b/drivers/usb/ezusb.h index 630169d11..1d2597129 100644 --- a/drivers/usb/ezusb.h +++ b/drivers/usb/ezusb.h @@ -31,6 +31,23 @@ /* --------------------------------------------------------------------- */ +struct ezusb_old_ctrltransfer { + /* keep in sync with usb.h:devrequest */ + unsigned char requesttype; + unsigned char request; + unsigned short value; + unsigned short index; + unsigned short length; + unsigned int dlen; + void *data; +}; + +struct ezusb_old_bulktransfer { + unsigned int ep; + unsigned int len; + void *data; +}; + struct ezusb_ctrltransfer { /* keep in sync with usb.h:devrequest */ unsigned char requesttype; @@ -38,14 +55,14 @@ struct ezusb_ctrltransfer { unsigned short value; unsigned short index; unsigned short length; - /* pointer to data */ - unsigned dlen; + unsigned int timeout; /* in milliseconds */ void *data; }; struct ezusb_bulktransfer { unsigned int ep; unsigned int len; + unsigned int timeout; /* in milliseconds */ void *data; }; @@ -84,8 +101,10 @@ struct ezusb_asynciso { struct ezusb_isoframestat isostat[0]; }; -#define EZUSB_CONTROL _IOWR('E', 0, struct ezusb_ctrltransfer) +#define EZUSB_CONTROL _IOWR('E', 1, struct ezusb_ctrltransfer) #define EZUSB_BULK _IOWR('E', 2, struct ezusb_bulktransfer) +#define EZUSB_OLD_CONTROL _IOWR('E', 0, struct ezusb_old_ctrltransfer) +#define EZUSB_OLD_BULK _IOWR('E', 2, struct ezusb_old_bulktransfer) #define EZUSB_RESETEP _IOR('E', 3, unsigned int) #define EZUSB_SETINTERFACE _IOR('E', 4, struct ezusb_setinterface) #define EZUSB_SETCONFIGURATION _IOR('E', 5, unsigned int) diff --git a/drivers/usb/hp_scanner.c b/drivers/usb/hp_scanner.c index c7a5af555..bdf3c4181 100644 --- a/drivers/usb/hp_scanner.c +++ b/drivers/usb/hp_scanner.c @@ -130,7 +130,7 @@ write_scanner(struct file * file, const char * buffer, return bytes_written ? bytes_written : -EINTR; } - result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, 2), obuf, thistime, &partial); + result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, 2), obuf, thistime, &partial, HZ*10); //printk(KERN_DEBUG "write stats: result:%d thistime:%lu partial:%lu\n", result, thistime, partial); @@ -188,7 +188,7 @@ read_scanner(struct file * file, char * buffer, return -ENODEV; this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count; - result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, 1), ibuf, this_read, &partial); + result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, 1), ibuf, this_read, &partial, HZ*10); printk(KERN_DEBUG "read stats: result:%d this_read:%u partial:%lu\n", result, this_read, partial); diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 691cb9a58..59a539867 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -34,33 +34,33 @@ static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - USB_DT_HUB << 8, 0, data, size); + USB_DT_HUB << 8, 0, data, size, HZ); } static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0); + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); } static int usb_set_port_feature(struct usb_device *dev, int port, int feature) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0); + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); } static int usb_get_hub_status(struct usb_device *dev, void *data) { /* FIXME: Don't hardcode 4 */ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, data, 4); + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, data, 4, HZ); } static int usb_get_port_status(struct usb_device *dev, int port, void *data) { /* FIXME: Don't hardcode 4 */ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, data, 4); + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, data, 4, HZ); } /* diff --git a/drivers/usb/inits.h b/drivers/usb/inits.h index cc49fbbd5..1d9acb1a4 100644 --- a/drivers/usb/inits.h +++ b/drivers/usb/inits.h @@ -6,5 +6,8 @@ int usb_printer_init(void); void usb_hub_cleanup(void); void usb_mouse_cleanup(void); int usb_scsi_init(void); +int usb_hp_scanner_init(void); +void usb_hp_scanner_cleanup(void); int proc_usb_init (void); void proc_usb_cleanup (void); +int usb_serial_init (void); diff --git a/drivers/usb/keyboard.c b/drivers/usb/keyboard.c index 13f5eccaa..f35f1b091 100644 --- a/drivers/usb/keyboard.c +++ b/drivers/usb/keyboard.c @@ -21,7 +21,7 @@ #define PCKBD_RELEASED 0x80 #define PCKBD_NEEDS_E0 0x80 -#define USBKBD_MODIFIER_BASE 120 +#define USBKBD_MODIFIER_BASE 224 #define USBKBD_KEYCODE_OFFSET 2 #define USBKBD_KEYCODE_COUNT 6 diff --git a/drivers/usb/keymap.c b/drivers/usb/keymap.c index 478de504b..82e2f1454 100644 --- a/drivers/usb/keymap.c +++ b/drivers/usb/keymap.c @@ -22,7 +22,7 @@ unsigned char usb_kbd_map[256] = 0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -42,7 +42,7 @@ unsigned char usb_kbd_map[256] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/drivers/usb/maps/usb.map b/drivers/usb/maps/usb.map index e05c71cd8..f4f40ffbf 100644 --- a/drivers/usb/maps/usb.map +++ b/drivers/usb/maps/usb.map @@ -223,11 +223,11 @@ keycode 104 = F13 keycode 105 = F14 # modifiers -keycode 120 = Control -keycode 121 = Shift -keycode 122 = Alt -keycode 123 = Window -keycode 124 = Control_R -keycode 125 = Shift_R -keycode 126 = Alt_R -keycode 127 = Window_R +keycode 224 = Control +keycode 225 = Shift +keycode 226 = Alt +keycode 227 = Window +keycode 228 = Control_R +keycode 229 = Shift_R +keycode 230 = Alt_R +keycode 231 = Window_R diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c index 4384f07d3..da5ee515e 100644 --- a/drivers/usb/mouse.c +++ b/drivers/usb/mouse.c @@ -120,7 +120,7 @@ static int mouse_irq(int state, void *__buffer, int len, void *dev_id) wake_up_interruptible(&mouse->wait); if (mouse->fasync) - kill_fasync(mouse->fasync, SIGIO); + kill_fasync(mouse->fasync, SIGIO, POLL_IN); return 1; } diff --git a/drivers/usb/ohci-debug.c b/drivers/usb/ohci-debug.c index 339652d21..628b93428 100644 --- a/drivers/usb/ohci-debug.c +++ b/drivers/usb/ohci-debug.c @@ -80,7 +80,7 @@ void show_ohci_ed(struct ohci_ed *ed) printk(KERN_DEBUG " ohci ED:\n"); printk(KERN_DEBUG " status = 0x%x\n", stat); - printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d%s\n", + printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d [HCD_%d%s]\n", skip ? "Skip " : "", mps, isoc ? " Isoc." : "", @@ -91,7 +91,8 @@ void show_ohci_ed(struct ohci_ed *ed) toggle, endnum, funcaddr, - (stat & ED_ALLOCATED) ? " Allocated" : ""); + ohci_ed_hcdtype(ed) >> 27, + (stat & ED_ALLOCATED) ? ", Allocated" : ""); printk(KERN_DEBUG " tail_td = 0x%x\n", ed_tail_td(ed)); printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed)); printk(KERN_DEBUG " next_ed = 0x%x\n", le32_to_cpup(&ed->next_ed)); diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c index 34f2613e6..eeace6d61 100644 --- a/drivers/usb/ohci-hcd.c +++ b/drivers/usb/ohci-hcd.c @@ -14,7 +14,7 @@ * [ Open Host Controller Interface driver for USB. ] * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ] - * [ $Log: ohci.c,v $ ] + * [ _Log: ohci-hcd.c,v _ * [ Revision 1.1 1999/04/05 08:32:30 greg ] * * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes @@ -224,7 +224,7 @@ static int sohci_release_irq(struct usb_device *usb_dev, void * ed) return 0; } -static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len) +static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout) { DECLARE_WAITQUEUE(wait, current); struct ohci_state state = {0, TD_NOTACCESSED}; @@ -254,7 +254,7 @@ static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devr OHCI_DEBUG(printk("USB HC trans req ed %x: %x :", ed->hwINFO, (unsigned int ) ed); ) OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; ) if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); - schedule_timeout(HZ*5); + schedule_timeout(timeout); if(state.status == TD_NOTACCESSED) { current->state = TASK_UNINTERRUPTIBLE; @@ -266,7 +266,7 @@ static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devr return state.status; } -static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval) +static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout) { DECLARE_WAITQUEUE(wait, current); struct ohci_state state = {0, TD_NOTACCESSED}; @@ -286,7 +286,7 @@ static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *d ohci_trans_req(ohci, ed, 0, NULL, data, len, (__OHCI_BAG) &state, (__OHCI_BAG) &wait,(usb_pipeout(pipe))?BULK_OUT:BULK_IN, sohci_blocking_handler); if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed); - schedule_timeout(HZ*5); + schedule_timeout(timeout); if(state.status == TD_NOTACCESSED) { current->state = TASK_UNINTERRUPTIBLE; diff --git a/drivers/usb/ohci-hcd.h b/drivers/usb/ohci-hcd.h index 35cb4148c..3ff3ae91d 100644 --- a/drivers/usb/ohci-hcd.h +++ b/drivers/usb/ohci-hcd.h @@ -12,7 +12,7 @@ * [ Open Host Controller Interface driver for USB. ] * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ] - * [ $Log: ohci.c,v $ ] + * [ _Log: ohci-hcd.h,v _ * [ Revision 1.1 1999/04/05 08:32:30 greg ] * * v4.0 1999/08/18 diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c index a7da1361b..763b8199e 100644 --- a/drivers/usb/ohci.c +++ b/drivers/usb/ohci.c @@ -15,7 +15,7 @@ * Architecture" book by Mindshare, Inc. It was a reasonable introduction * and overview of USB and the two dominant host controller interfaces * however you're better off just reading the real specs available - * from www.usb.org as you'll need them to get enough detailt to + * from www.usb.org as you'll need them to get enough details to * actually implement a HCD. The book has many typos and omissions * Beware, the specs are the victim of a committee. * @@ -24,7 +24,7 @@ * * No filesystems were harmed in the development of this code. * - * $Id: ohci.c,v 1.43 1999/05/16 22:35:24 greg Exp $ + * $Id: ohci.c,v 1.77 1999/09/16 04:30:19 greg Exp $ */ #include <linux/config.h> @@ -201,7 +201,6 @@ static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p) spin_unlock_irqrestore(&ohci_edtd_lock, flags); } /* ohci_add_ed_to_hw() */ - /* * Put a control ED on the controller's list */ @@ -275,24 +274,19 @@ struct ohci_ed *ohci_get_periodic_ed(struct ohci_device *dev, int period, for (;;) { int_ed = bus_to_virt(le32_to_cpup(&int_ed->next_ed)); /* stop if we get to the end or to another dummy ED. */ - if (int_ed == 0) - break; + if (!int_ed) + break; /* return NULL */ status = le32_to_cpup(&int_ed->status); - if ((status & OHCI_ED_FA) == 0) - break; + if ((status & OHCI_ED_FA) == 0) { + int_ed = NULL; + break; /* return NULL */ + } /* check whether all the appropriate fields match */ if ((status & 0x7ffa7ff) == req_status) - return int_ed; + break; /* return int_ed */ } - return 0; -} - -/* - * Put an isochronous ED on the controller's list - */ -inline void ohci_add_isoc_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_add_periodic_ed(ohci, ed, 1); + spin_unlock_irqrestore(&ohci_edtd_lock, flags); + return int_ed; } @@ -326,14 +320,14 @@ static void ohci_wait_sof(struct ohci_regs *regs) * adds it to a list of EDs to destroy during the SOF interrupt * processing would be useful (and be callable from an interrupt). */ -void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_type) +void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed) { __u32 *hw_listcurrent; /* tell the controller to skip this ED */ ed->status |= cpu_to_le32(OHCI_ED_SKIP); - switch (ed_type) { + switch (ohci_ed_hcdtype(ed)) { case HCD_ED_CONTROL: hw_listcurrent = ®s->ed_controlcurrent; break; @@ -370,7 +364,7 @@ void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_ty * * Note that the SKIP bit is left on in the removed ED. */ -void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_type) +void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed) { unsigned long flags; struct ohci_regs *regs = ohci->regs; @@ -378,6 +372,7 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t __u32 bus_ed = virt_to_bus(ed); __u32 bus_cur; __u32 *hw_listhead_p; + __u32 ed_type = ohci_ed_hcdtype(ed); if (ed == NULL || !bus_ed) return; @@ -391,7 +386,9 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t hw_listhead_p = ®s->ed_bulkhead; break; default: - printk(KERN_ERR "Unknown HCD ED type %d.\n", ed_type); +#ifdef OHCI_DEBUG + printk(KERN_ERR "Wrong HCD ED type: %d.\n", ed_type); +#endif return; } @@ -426,7 +423,7 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t /* * Make sure this ED is not being accessed by the HC as we speak. */ - ohci_wait_for_ed_safe(regs, ed, ed_type); + ohci_wait_for_ed_safe(regs, ed); /* clear any links from the ED for safety */ ed->next_ed = 0; @@ -434,24 +431,6 @@ void ohci_remove_norm_ed_from_hw(struct ohci *ohci, struct ohci_ed *ed, int ed_t spin_unlock_irqrestore(&ohci_edtd_lock, flags); } /* ohci_remove_norm_ed_from_hw() */ -/* - * Remove an ED from the controller's control list. Note that the SKIP bit - * is left on in the removed ED. - */ -inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_CONTROL); -} - -/* - * Remove an ED from the controller's bulk list. Note that the SKIP bit - * is left on in the removed ED. - */ -inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK); -} - /* * Remove a periodic ED from the host controller @@ -476,7 +455,7 @@ void ohci_remove_periodic_ed(struct ohci *ohci, struct ohci_ed *ed) while (cur_ed) { if (ed == cur_ed) { /* remove the ED */ /* set its SKIP bit and be sure its not in use */ - ohci_wait_for_ed_safe(ohci->regs, ed, HCD_ED_INT); + ohci_wait_for_ed_safe(ohci->regs, ed); /* unlink it */ spin_lock_irqsave(&ohci_edtd_lock, flags); @@ -506,6 +485,20 @@ void ohci_remove_periodic_ed(struct ohci *ohci, struct ohci_ed *ed) /* + * Remove an ED from the host controller, choosing + */ +void ohci_remove_ed(struct ohci *ohci, struct ohci_ed *ed) +{ + /* Remove the ED from the HC */ + if (ohci_ed_hcdtype(ed) & HCD_ED_NORMAL) { + ohci_remove_norm_ed_from_hw(ohci, ed); + } else { + ohci_remove_periodic_ed(ohci, ed); + } +} /* ohci_remove_ed() */ + + +/* * Remove all the EDs which have a given device address from a list. * Used when the device is unplugged. * Returns 1 if anything was changed. @@ -573,7 +566,7 @@ void ohci_remove_device(struct ohci *ohci, int devnum) * Remove a TD from the given EDs TD list. The TD is freed as well. * (so far this function hasn't been needed) */ -static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) +void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) { unsigned long flags; struct ohci_td *head_td; @@ -670,10 +663,9 @@ static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) * pool. Return NULL if none are left. * * NOTE: This function does not allocate and attach the dummy_td. - * That is done in ohci_fill_ed(). FIXME: it should probably be moved - * into here. + * That is done in ohci_fill_ed(). [should we really do that here?] */ -static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) +static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev, __u32 ed_type) { int idx; @@ -687,6 +679,7 @@ static struct ohci_ed *ohci_get_free_ed(struct ohci_device *dev) new_ed->status |= cpu_to_le32(OHCI_ED_SKIP); /* mark it as allocated */ allocate_ed(new_ed); + ohci_ed_set_hcdtype(new_ed, ed_type); new_ed->ohci_dev = dev; return new_ed; } @@ -764,8 +757,7 @@ struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 * force the Allocated bit on. */ struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, - int maxpacketsize, int lowspeed, int endp_id, - int isoc_tds) + int maxpacketsize, int lowspeed, int endp_id) { struct ohci_td *dummy_td; @@ -789,11 +781,12 @@ struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, /* set the head TD to the dummy and clear the Carry & Halted bits */ ed->_head_td = ed->tail_td; - ed->status = cpu_to_le32( + ed->status &= cpu_to_le32(OHCI_ED_HCD_MASK); + ed->status |= cpu_to_le32( ed_set_maxpacket(maxpacketsize) | ed_set_speed(lowspeed) | (endp_id & 0x7ff) | - ((isoc_tds == 0) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC)); + ((ohci_ed_hcdtype(ed) != HCD_ED_ISOC) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC)); allocate_ed(ed); ed->next_ed = 0; @@ -959,7 +952,7 @@ static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, /* Get an ED and TD */ interrupt_ed = ohci_get_periodic_ed(dev, period, pipe, 0); if (interrupt_ed == 0) { - interrupt_ed = ohci_get_free_ed(dev); + interrupt_ed = ohci_get_free_ed(dev, HCD_ED_INT); if (!interrupt_ed) { printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev); return (-ENOMEM); @@ -970,7 +963,7 @@ static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, * device number (function address), and type of TD. */ ohci_fill_ed(dev, interrupt_ed, maxps, usb_pipeslow(pipe), - usb_pipe_endpdev(pipe), 0 /* normal TDs */); + usb_pipe_endpdev(pipe) ); interrupt_ed->status &= cpu_to_le32(~OHCI_ED_SKIP); /* Assimilate the new ED into the collective */ @@ -1077,7 +1070,7 @@ int ohci_release_irq(struct usb_device *usb, void* handle) static void * ohci_generic_trans(struct usb_device *usb_dev, int pipe, int data_td_toggle, int round, int autofree, void *dev_id, usb_device_irq handler, void *data, int len, - int ed_type, struct ohci_td *setup_td, struct ohci_td *status_td) + __u32 ed_type, struct ohci_td *setup_td, struct ohci_td *status_td) { struct ohci_device *dev = usb_to_ohci(usb_dev); struct ohci_ed *trans_ed; @@ -1089,7 +1082,7 @@ static void * ohci_generic_trans(struct usb_device *usb_dev, int pipe, printk(KERN_DEBUG "ohci_request_trans()\n"); #endif - trans_ed = ohci_get_free_ed(dev); + trans_ed = ohci_get_free_ed(dev, ed_type); if (!trans_ed) { printk("usb-ohci: couldn't get ED for dev %p\n", dev); return NULL; @@ -1142,8 +1135,7 @@ static void * ohci_generic_trans(struct usb_device *usb_dev, int pipe, ohci_fill_ed(dev, trans_ed, usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)), usb_pipeslow(pipe), - usb_pipe_endpdev(pipe), - (ed_type == HCD_ED_ISOC) ); + usb_pipe_endpdev(pipe) ); /* initialize the toggle carry flag in the ED */ if (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) @@ -1202,7 +1194,7 @@ gt_free_and_exit: * * This function is NOT safe to call from an interrupt. */ -static int ohci_terminate_trans(struct usb_device *usb_dev, void *handle, int ed_type) +static int ohci_terminate_trans(struct usb_device *usb_dev, void *handle) { struct ohci_ed *req_ed = (struct ohci_ed *) handle; struct ohci_device *dev = usb_to_ohci(usb_dev); @@ -1213,20 +1205,10 @@ static int ohci_terminate_trans(struct usb_device *usb_dev, void *handle, int ed /* stop the transfer & collect the number of bytes */ /* (this is the non-interrupt safe function call) */ - ohci_wait_for_ed_safe(regs, req_ed, ed_type); + ohci_wait_for_ed_safe(regs, req_ed); - /* Remove the ED from the appropriate HC list */ - switch (ed_type) { - case HCD_ED_ISOC: - ohci_remove_periodic_ed(dev->ohci, req_ed); - break; - case HCD_ED_CONTROL: - ohci_remove_control_ed(dev->ohci, req_ed); - break; - case HCD_ED_BULK: - ohci_remove_bulk_ed(dev->ohci, req_ed); - break; - } + /* Remove the ED from the HC */ + ohci_remove_ed(dev->ohci, req_ed); ohci_free_ed(req_ed); /* return it to the pool */ @@ -1252,7 +1234,15 @@ static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id /* pass the TDs completion status back to control_msg */ if (dev_id) { int *completion_status = (int *)dev_id; - *completion_status = stats; + + switch (stats) { + case USB_ST_NOERROR: + case USB_ST_DATAOVERRUN: + *completion_status = len; + break; + default: + *completion_status = -stats; + } } wake_up(&control_wakeup); @@ -1274,13 +1264,13 @@ static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id * This function can NOT be called from an interrupt. */ static int ohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, - devrequest *cmd, void *data, int len) + devrequest *cmd, void *data, int len, int timeout) { struct ohci_device *dev = usb_to_ohci(usb_dev); void *trans_handle; struct ohci_td *setup_td, *status_td; DECLARE_WAITQUEUE(wait, current); - int completion_status = -1; + int completion_status = USB_ST_TIMEOUT; devrequest our_cmd; /* byte-swap fields of cmd if necessary */ @@ -1356,8 +1346,8 @@ static int ohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, return USB_ST_INTERNALERROR; } - /* wait a "reasonable" amount of time for it to complete */ - schedule_timeout(HZ); + /* wait a user defined amount of time for it to complete */ + schedule_timeout(timeout); remove_wait_queue(&control_wakeup, &wait); @@ -1369,9 +1359,12 @@ static int ohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, * Also, since the TDs were autofreed, there is no guarantee that * they haven't been reclaimed by another transfer by now... */ - if (completion_status != 0) { - const char *what = (completion_status < 0)? "timed out": - cc_names[completion_status & 0xf]; + if (completion_status < 0) { + const char *what; + if (completion_status == USB_ST_TIMEOUT) + what = "timed out"; + else + what = cc_names[(-completion_status) & 0xf]; printk(KERN_ERR "ohci_control_msg: %s on pipe %x cmd %x %x %x %x %x", what, pipe, cmd->requesttype, cmd->request, cmd->value, cmd->index, cmd->length); @@ -1422,10 +1415,8 @@ static int ohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, /* no TD cleanup, the TDs were auto-freed as they finished */ /* remove the generic_trans ED from the HC and free it */ - ohci_terminate_trans(usb_dev, trans_handle, HCD_ED_CONTROL); + ohci_terminate_trans(usb_dev, trans_handle); - if (completion_status < 0) - completion_status = USB_ST_TIMEOUT; return completion_status; } /* ohci_control_msg() */ @@ -1471,7 +1462,7 @@ static void * ohci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, u */ static int ohci_terminate_bulk(struct usb_device *usb_dev, void * handle) { - return ohci_terminate_trans(usb_dev, handle, HCD_ED_CONTROL); + return ohci_terminate_trans(usb_dev, handle); } /* ohci_terminate_bulk() */ @@ -1536,24 +1527,6 @@ static int ohci_bulk_msg_td_handler(int stats, void *buffer, int len, void *dev_ } /* ohci_bulk_msg_td_handler() */ -/* - * Request to send or receive bulk data for a blocking bulk_msg call. - * - * bulk_request->bytes_transferred_p is a pointer to an integer that - * will be set to the number of bytes that have been successfully - * transferred upon completion. The interrupt handler will update it - * after each internal TD completes successfully. - */ -static struct ohci_ed* ohci_request_bulk_msg(struct ohci_bulk_msg_request_state *bulk_request) -{ - /* initialize the internal counter */ - bulk_request->_bytes_done = 0; - - return ohci_request_bulk(bulk_request->usb_dev, bulk_request->pipe, - ohci_bulk_msg_td_handler, - bulk_request->data, bulk_request->length, - bulk_request); -} /* ohci_request_bulk_msg() */ static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); @@ -1574,7 +1547,7 @@ static int ohci_bulk_msg_completed(int stats, void *buffer, int len, void *dev_i } /* ohci_bulk_msg_completed() */ -static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *bytes_transferred_p) +static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *bytes_transferred_p, int timeout) { DECLARE_WAITQUEUE(wait, current); int completion_status = USB_ST_INTERNALERROR; @@ -1598,6 +1571,7 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da req.bytes_transferred_p = bytes_transferred_p; req.dev_id = &completion_status; req.completion = ohci_bulk_msg_completed; + req._bytes_done = 0; if (bytes_transferred_p) *bytes_transferred_p = 0; @@ -1611,10 +1585,20 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(&bulk_wakeup, &wait); - req_ed = ohci_request_bulk_msg(&req); + /* + * Request to send or receive bulk data for a blocking bulk_msg call. + * + * req.bytes_transferred_p is a pointer to an integer that + * will be set to the number of bytes that have been successfully + * transferred upon completion. The interrupt handler will update it + * after each internal TD completes successfully. + */ + req_ed = ohci_request_bulk(usb_dev, pipe, + ohci_bulk_msg_td_handler, + data, len, &req); - /* FIXME this should to wait for a caller specified time... */ - schedule_timeout(HZ*5); + /* wait for a caller specified time... */ + schedule_timeout(timeout); /* completion_status will only stay in this state of the * request never finished */ @@ -1629,7 +1613,7 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da * a previously requested bulk transfer. -greg */ /* stop the transfer & collect the number of bytes */ - ohci_wait_for_ed_safe(regs, req_ed, HCD_ED_BULK); + ohci_wait_for_ed_safe(regs, req_ed); /* Get the number of bytes transferred out of the head TD * on the ED if it didn't finish while we were waiting. */ @@ -1659,7 +1643,7 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da remove_wait_queue(&bulk_wakeup, &wait); /* remove the ED from the HC */ - ohci_remove_bulk_ed(usb_to_ohci(usb_dev)->ohci, req_ed); + ohci_remove_norm_ed_from_hw(usb_to_ohci(usb_dev)->ohci, req_ed); /* save the toggle value back into the usb_dev */ usb_settoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), @@ -1681,31 +1665,19 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da /* - * Allocate a new USB device to be attached to an OHCI controller + * Allocate a new USB device attached to this OHCI controller */ -static struct usb_device *ohci_usb_allocate(struct usb_device *parent) +static int ohci_dev_allocate(struct usb_device *usb_dev) { - struct usb_device *usb_dev; struct ohci_device *dev; int idx; /* - * Allocate the generic USB device - */ - usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL); - if (!usb_dev) - return NULL; - - memset(usb_dev, 0, sizeof(*usb_dev)); - - /* * Allocate an OHCI device (EDs and TDs for this device) */ dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(usb_dev); - return NULL; - } + if (!dev) + return -1; memset(dev, 0, sizeof(*dev)); @@ -1721,80 +1693,71 @@ static struct usb_device *ohci_usb_allocate(struct usb_device *parent) usb_dev->hcpriv = dev; dev->usb = usb_dev; - /* - * Link the device to its parent (hub, etc..) if any. - */ - usb_dev->parent = parent; + if (usb_dev->parent) + dev->ohci = usb_to_ohci(usb_dev->parent)->ohci; - if (parent) { - usb_dev->bus = parent->bus; - dev->ohci = usb_to_ohci(parent)->ohci; - } - - return usb_dev; -} /* ohci_usb_allocate() */ + return 0; +} /* ohci_dev_allocate() */ /* - * Free a usb device. - * - * TODO This function needs to take better care of the EDs and TDs, etc. + * Free a usb device on this ohci controller. */ -static int ohci_usb_deallocate(struct usb_device *usb_dev) +static int ohci_dev_deallocate(struct usb_device *usb_dev) { struct ohci_device *dev = usb_to_ohci(usb_dev); ohci_remove_device(dev->ohci, usb_dev->devnum); - /* kfree(usb_to_ohci(usb_dev)); */ - /* kfree(usb_dev); */ + kfree(usb_to_ohci(usb_dev)); return 0; } - -static void *ohci_alloc_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id) +static int ohci_get_current_frame_number(struct usb_device *usb_dev) { - return NULL; + return USB_ST_NOTSUPPORTED; } -static void ohci_delete_isochronous (struct usb_device *dev, void *_isodesc) +static int ohci_alloc_isochronous (struct usb_device *usb_dev, +unsigned int pipe, int frame_count, void *context, +struct usb_isoc_desc **isocdesc) { - return; + return USB_ST_NOTSUPPORTED; } -static int ohci_sched_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc) +static void ohci_delete_isochronous (struct usb_isoc_desc *isocdesc) { - return USB_ST_NOTSUPPORTED; + return; } -static int ohci_unsched_isochronous (struct usb_device *usb_dev, void *_isodesc) +static int ohci_sched_isochronous (struct usb_isoc_desc *isocdesc, +struct usb_isoc_desc *pr_isocdesc) { return USB_ST_NOTSUPPORTED; } -static int ohci_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) +static int ohci_unsched_isochronous (struct usb_isoc_desc *isocdesc) { return USB_ST_NOTSUPPORTED; } - /* * functions for the generic USB driver */ struct usb_operations ohci_device_operations = { - ohci_usb_allocate, - ohci_usb_deallocate, + ohci_dev_allocate, + ohci_dev_deallocate, ohci_control_msg, ohci_bulk_msg, ohci_request_irq, ohci_release_irq, ohci_request_bulk, ohci_terminate_bulk, + ohci_get_current_frame_number, ohci_alloc_isochronous, ohci_delete_isochronous, ohci_sched_isochronous, - ohci_unsched_isochronous, - ohci_compress_isochronous + ohci_unsched_isochronous }; @@ -1887,8 +1850,10 @@ static int start_hc(struct ohci *ohci) /* Enter the USB Operational state & start the frames a flowing.. */ writel_set(OHCI_USB_OPER, &ohci->regs->control); - /* Enable control lists */ - writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control); + /* Enable control lists, claim the host controller */ + writel_set( + OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, + &ohci->regs->control); /* Force global power enable -gal@cs.uni-magdeburg.de */ /* @@ -1988,10 +1953,11 @@ static void ohci_connect_change(struct ohci * ohci, int port) /* * Allocate a device for the new thingy that's been attached */ - usb_dev = ohci_usb_allocate(root_hub->usb); - dev = usb_dev->hcpriv; + usb_dev = usb_alloc_dev(root_hub->usb, root_hub->usb->bus); + if (!usb_dev) + return; - dev->ohci = ohci; + dev = usb_dev->hcpriv; usb_connect(dev->usb); @@ -2360,6 +2326,8 @@ static struct ohci *alloc_ohci(void* mem_base) struct usb_bus *bus; struct ohci_device *dev; struct usb_device *usb; + struct ohci_hcca *hcca; + u32 ctrl; #if 0 printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base); @@ -2368,22 +2336,28 @@ static struct ohci *alloc_ohci(void* mem_base) ohci = kmalloc(sizeof(*ohci), GFP_KERNEL); if (!ohci) return NULL; - memset(ohci, 0, sizeof(*ohci)); ohci->irq = -1; ohci->regs = mem_base; INIT_LIST_HEAD(&ohci->interrupt_list); - bus = kmalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - return NULL; + /* + * Allocate the Host Controller Communications Area on a 256 + * byte boundary. XXX take the easy way out and just grab a + * page as that's guaranteed to have a nice boundary. + */ + hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL); + if (hcca == NULL) + goto au_free_ohci; + memset(hcca, 0, sizeof(struct ohci_hcca)); - memset(bus, 0, sizeof(*bus)); + bus = usb_alloc_bus(&ohci_device_operations); + if (!bus) + goto au_free_hcca; ohci->bus = bus; bus->hcpriv = ohci; - bus->op = &ohci_device_operations; /* * Allocate the USB device structure and root hub. @@ -2393,25 +2367,21 @@ static struct ohci *alloc_ohci(void* mem_base) * a nice pool of memory with pointers to endpoint descriptors * for the different interrupts. */ - usb = ohci_usb_allocate(NULL); + usb = usb_alloc_dev(NULL, bus); if (!usb) - return NULL; + goto au_free_bus; - dev = usb_to_ohci(usb); - ohci->bus->root_hub= ohci_to_usb(dev); usb->bus = bus; + + dev = usb_to_ohci(usb); + dev->ohci = ohci; + dev->hcca = hcca; + + ohci->bus->root_hub = ohci_to_usb(dev); /* Initialize the root hub */ dev->ohci = ohci; /* link back to the controller */ - /* - * Allocate the Host Controller Communications Area on a 256 - * byte boundary. XXX take the easy way out and just grab a - * page as that's guaranteed to have a nice boundary. - */ - dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL); - memset(dev->hcca, 0, sizeof(struct ohci_hcca)); - /* Tell the controller where the HCCA is */ writel(virt_to_bus(dev->hcca), &ohci->regs->hcca); @@ -2431,6 +2401,25 @@ static struct ohci *alloc_ohci(void* mem_base) } printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild); + + /* + * Fix up legacy mode + */ + + ctrl = readl(&ohci->regs->control); + if(ctrl&OHCI_USB_IR) + { + int ct = 0; + /* Ask SMM for the controls */ + writel(8, &ohci->regs->cmdstatus); + printk(KERN_INFO "usb-ohci: switching interface from legacy mode.\n"); + while((readl(&ohci->regs->control)&OHCI_USB_IR) && ct < 250) + { + udelay(100); + ct++; + } + } + /* * Initialize the ED polling "tree" (for simplicity's sake in * this driver many nodes in the tree will be identical) @@ -2485,6 +2474,15 @@ static struct ohci *alloc_ohci(void* mem_base) #endif return ohci; + +au_free_bus: + usb_free_bus(bus); +au_free_hcca: + free_page((unsigned long)hcca); +au_free_ohci: + kfree(ohci); + return NULL; + } /* alloc_ohci() */ @@ -2753,6 +2751,8 @@ static int init_ohci(struct pci_dev *dev) if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) return -ENODEV; + pci_set_master(dev); + /* Get the memory address and map it for IO */ mem_base = dev->resource[0].start; diff --git a/drivers/usb/ohci.h b/drivers/usb/ohci.h index 4bb71c55c..b06e5ca65 100644 --- a/drivers/usb/ohci.h +++ b/drivers/usb/ohci.h @@ -6,7 +6,7 @@ * * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> * - * $Id: ohci.h,v 1.24 1999/05/16 10:18:26 greg Exp $ + * $Id: ohci.h,v 1.40 1999/09/05 07:26:46 greg Exp $ */ #include <linux/list.h> @@ -148,19 +148,45 @@ struct ohci_ed { #define OHCI_ED_EN (0xf << 7) #define OHCI_ED_FA (0x7f) + #define ed_get_en(ed) ((le32_to_cpup(&(ed)->status) & OHCI_ED_EN) >> 7) #define ed_get_fa(ed) (le32_to_cpup(&(ed)->status) & OHCI_ED_FA) /* NOTE: bits 27-31 of the status dword are reserved for the HCD */ +#define OHCI_ED_HCD_MASK (0x1f << 27) /* * We'll use this status flag for to mark if an ED is in use by the - * driver or not. If the bit is set, it is being used. + * driver or not. If the bit is set, it is being used. (bit 31) */ #define ED_ALLOCATED (1 << 31) #define ed_allocated(ed) (le32_to_cpup(&(ed).status) & ED_ALLOCATED) #define allocate_ed(ed) ((ed)->status |= cpu_to_le32(ED_ALLOCATED)) /* + * These store the endpoint transfer type for this ED in the status + * field. (bits 27 and 28) + * Bit 28: + * 0 = Periodic ED + * Bit 27: + * 0 = Isochronous + * 1 = Interrupt + * 1 = Normal ED + * Bit 27: + * 0 = Control + * 1 = Bulk + */ +#define HCD_ED_NORMAL (1 << 28) /* (2 << 27) */ +#define HCD_ED_ISOC (0) +#define HCD_ED_INT (1 << 27) +#define HCD_ED_CONTROL (2 << 27) +#define HCD_ED_BULK (3 << 27) +#define HCD_ED_MASK (3 << 27) + +#define ohci_ed_hcdtype(ed) (le32_to_cpup(&(ed)->status) & HCD_ED_MASK) +#define ohci_ed_set_hcdtype(ed, t) ( (ed)->status = ((ed)->status & ~cpu_to_le32(HCD_ED_MASK)) | cpu_to_le32((t) & HCD_ED_MASK) ) + + +/* * The HCCA (Host Controller Communications Area) is a 256 byte * structure defined in the OHCI spec. that the host controller is * told the base address of. It must be 256-byte aligned. @@ -299,15 +325,6 @@ struct ohci_regs { } roothub; } __attribute((aligned(32))); -/* - * These are used by internal ED managing functions as a - * parameter to state the type of ED to deal with (when it matters). - */ -#define HCD_ED_ISOC (0) -#define HCD_ED_INT (1) -#define HCD_ED_CONTROL (2) -#define HCD_ED_BULK (3) - /* * Read a MMIO register and re-write it after ANDing with (m) */ @@ -368,12 +385,16 @@ struct ohci_regs { /* * Control register masks */ +#define OHCI_USB_RESUME (1 << 6) #define OHCI_USB_OPER (2 << 6) #define OHCI_USB_SUSPEND (3 << 6) #define OHCI_USB_PLE (1 << 2) /* Periodic (interrupt) list enable */ #define OHCI_USB_IE (1 << 3) /* Isochronous list enable */ #define OHCI_USB_CLE (1 << 4) /* Control list enable */ #define OHCI_USB_BLE (1 << 5) /* Bulk list enable */ +#define OHCI_USB_IR (1 << 8) /* Int. Routing, HCD ownership */ +#define OHCI_USB_RWC (1 << 9) /* Remote Wakeup Connected */ +#define OHCI_USB_RWE (1 << 10) /* Remote Wakeup Enable */ /* * Command status register masks diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 826df63a8..17dac365a 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -58,7 +58,7 @@ unsigned char printer_read_status(struct pp_usb_data *p) dr.value = 0; dr.index = 0; dr.length = 1; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) { + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1, HZ)) { return 0; } return status; @@ -104,7 +104,7 @@ void printer_reset(struct pp_usb_data *p) dr.value = 0; dr.index = 0; dr.length = 0; - dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0, HZ); } static int open_printer(struct inode * inode, struct file * file) @@ -129,10 +129,11 @@ static int open_printer(struct inode * inode, struct file * file) printer_check_status(p); - file->private_data = p; // printer_reset(p); init_waitqueue_head(&p->wait_q); + + MOD_INC_USE_COUNT; return 0; } @@ -143,13 +144,12 @@ static int close_printer(struct inode * inode, struct file * file) free_page((unsigned long)p->obuf); p->isopen = 0; file->private_data = NULL; + /* free the resources if the printer is no longer around */ if(!p->pusb_dev) { minor_data[p->minor] = NULL; kfree(p); - - MOD_DEC_USE_COUNT; - } + MOD_DEC_USE_COUNT; return 0; } @@ -184,7 +184,7 @@ static ssize_t write_printer(struct file * file, } result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, usb_sndbulkpipe(p->pusb_dev, endpoint_num), - obuf, thistime, &partial); + obuf, thistime, &partial, HZ*20); if (partial) { obuf += partial; thistime -= partial; @@ -243,7 +243,7 @@ static ssize_t read_printer(struct file * file, result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, usb_rcvbulkpipe(p->pusb_dev, endpoint_num), - buf, this_read, &partial); + buf, this_read, &partial, HZ*20); /* unlike writes, we don't retry a NAK, just stop now */ if (!result & partial) @@ -344,7 +344,7 @@ static int printer_probe(struct usb_device *dev) dr.value = 0; dr.index = 0; dr.length = sizeof(ieee_id) - 1; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) { + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1, HZ) == 0) { if (ieee_id[1] < sizeof(ieee_id) - 1) ieee_id[ieee_id[1]+2] = '\0'; else @@ -373,7 +373,6 @@ static void printer_disconnect(struct usb_device *dev) minor_data[pp->minor] = NULL; kfree(pp); dev->private = NULL; /* just in case */ - MOD_DEC_USE_COUNT; } static struct usb_driver printer_driver = { @@ -402,8 +401,6 @@ int usb_printer_init(void) { int result; - MOD_INC_USE_COUNT; - if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) { printk(KERN_WARNING "usbprinter: Cannot register device\n"); return result; diff --git a/drivers/usb/proc_usb.c b/drivers/usb/proc_usb.c index ae97e749c..53c5e87e0 100644 --- a/drivers/usb/proc_usb.c +++ b/drivers/usb/proc_usb.c @@ -286,9 +286,12 @@ static int usb_dump_desc (const struct usb_device *dev, char *buf, int *len) static int usb_hcd_bandwidth (const struct usb_device *dev, char *buf, int *len) { *len += sprintf (buf + *len, format_bandwidth, - dev->bus->bandwidth_allocated, FRAME_TIME_MAX_USECS_ALLOC, - 100 * dev->bus->bandwidth_allocated / FRAME_TIME_MAX_USECS_ALLOC, - dev->bus->bandwidth_int_reqs, dev->bus->bandwidth_isoc_reqs + dev->bus->bandwidth_allocated, + FRAME_TIME_MAX_USECS_ALLOC, + (100 * dev->bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / + FRAME_TIME_MAX_USECS_ALLOC, + dev->bus->bandwidth_int_reqs, + dev->bus->bandwidth_isoc_reqs ); return (*len >= DUMP_LIMIT) ? -1 : 0; @@ -360,12 +363,12 @@ static int usb_device_dump (char *buf, int *len, if (*len >= DUMP_LIMIT) return -1; - if (usbdev->devnum > 0) { /* for any except root hub */ - if (usb_dump_desc (usbdev, buf, len) < 0) + if ((level == 0) && (usbdev->devnum < 0)) { /* for root hub */ + if (usb_hcd_bandwidth (usbdev, buf, len) < 0) return -1; } - else { /* for a host controller */ - if (usb_hcd_bandwidth (usbdev, buf, len) < 0) + else { /* for anything but a root hub */ + if (usb_dump_desc (usbdev, buf, len) < 0) return -1; } @@ -509,8 +512,10 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi struct usb_device *dev = (struct usb_device *)dp->data; struct ezusb_ctrltransfer ctrl; struct ezusb_bulktransfer bulk; + struct ezusb_old_ctrltransfer octrl; + struct ezusb_old_bulktransfer obulk; struct ezusb_setinterface setintf; - unsigned int len1, ep, pipe; + unsigned int len1, ep, pipe, cfg; unsigned long len2; unsigned char *tbuf; int i; @@ -525,24 +530,28 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi if (!capable(CAP_SYS_RAWIO)) return -EPERM; copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT); - if (ctrl.dlen > PAGE_SIZE) + if (ctrl.length > PAGE_SIZE) return -EINVAL; if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; if (ctrl.requesttype & 0x80) { - if (ctrl.dlen && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.dlen)) { + if (ctrl.length && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.length)) { free_page((unsigned long)tbuf); return -EINVAL; } - i = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), (devrequest *)&ctrl, tbuf, ctrl.dlen); - if (!i && ctrl.dlen) { - copy_to_user_ret(ctrl.data, tbuf, ctrl.dlen, -EFAULT); + i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, + (ctrl.timeout * HZ + 500) / 1000); + if (!i && ctrl.length) { + copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); } } else { - if (ctrl.dlen) { - copy_from_user_ret(tbuf, ctrl.data, ctrl.dlen, -EFAULT); + if (ctrl.length) { + copy_from_user_ret(tbuf, ctrl.data, ctrl.length, -EFAULT); } - i = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), (devrequest *)&ctrl, tbuf, ctrl.dlen); + i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, + (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); if (i) { @@ -577,7 +586,7 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi free_page((unsigned long)tbuf); return -EINVAL; } - i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2); + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, (ctrl.timeout * HZ + 500) / 1000); if (!i && len2) { copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); } @@ -585,7 +594,7 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi if (len1) { copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT); } - i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2); + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); if (i) { @@ -595,6 +604,85 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi } return len2; + case EZUSB_OLD_CONTROL: + if (obsolete_warn < 20) { + printk(KERN_WARNING "/proc/bus/usb: process %d (%s) used obsolete EZUSB_OLD_CONTROL ioctl\n", + current->pid, current->comm); + obsolete_warn++; + } + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + copy_from_user_ret(&octrl, (void *)arg, sizeof(octrl), -EFAULT); + if (octrl.dlen > PAGE_SIZE) + return -EINVAL; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + if (octrl.requesttype & 0x80) { + if (octrl.dlen && !access_ok(VERIFY_WRITE, octrl.data, octrl.dlen)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); + if (!i && octrl.dlen) { + copy_to_user_ret(octrl.data, tbuf, octrl.dlen, -EFAULT); + } + } else { + if (octrl.dlen) { + copy_from_user_ret(tbuf, octrl.data, octrl.dlen, -EFAULT); + } + i = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), (devrequest *)&octrl, tbuf, octrl.dlen, HZ); + } + free_page((unsigned long)tbuf); + if (i) { + printk(KERN_WARNING "procusb: EZUSB_OLD_CONTROL failed rqt %u rq %u len %u ret %d\n", + octrl.requesttype, octrl.request, octrl.length, i); + return -ENXIO; + } + return 0; + + case EZUSB_OLD_BULK: + if (obsolete_warn < 20) { + printk(KERN_WARNING "/proc/bus/usb: process %d (%s) used obsolete EZUSB_OLD_BULK ioctl\n", + current->pid, current->comm); + obsolete_warn++; + } + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + copy_from_user_ret(&obulk, (void *)arg, sizeof(obulk), -EFAULT); + if (obulk.ep & 0x80) + pipe = usb_rcvbulkpipe(dev, obulk.ep & 0x7f); + else + pipe = usb_sndbulkpipe(dev, obulk.ep & 0x7f); + if (!usb_maxpacket(dev, pipe, !(obulk.ep & 0x80))) + return -EINVAL; + len1 = obulk.len; + if (len1 > PAGE_SIZE) + len1 = PAGE_SIZE; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + if (obulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, obulk.data, len1)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); + if (!i && len2) { + copy_to_user_ret(obulk.data, tbuf, len2, -EFAULT); + } + } else { + if (len1) { + copy_from_user_ret(tbuf, obulk.data, len1, -EFAULT); + } + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); + } + free_page((unsigned long)tbuf); + if (i) { + printk(KERN_WARNING "procusb: EZUSB_OLD_BULK failed ep 0x%x len %u ret %d\n", + obulk.ep, obulk.len, i); + return -ENXIO; + } + return len2; + case EZUSB_RESETEP: if (obsolete_warn < 20) { printk(KERN_WARNING "/proc/bus/usb: process %d (%s) used obsolete EZUSB_RESETEP ioctl\n", @@ -621,6 +709,20 @@ static int usbdev_ioctl_ezusbcompat(struct inode *inode, struct file *file, unsi if (usb_set_interface(dev, setintf.interface, setintf.altsetting)) return -EINVAL; return 0; + + case EZUSB_SETCONFIGURATION: + if (obsolete_warn < 20) { + printk(KERN_WARNING "/proc/bus/usb: process %d (%s) used obsolete EZUSB_SETCONFIGURATION ioctl\n", + current->pid, current->comm); + obsolete_warn++; + } + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + get_user_ret(cfg, (unsigned int *)arg, -EFAULT); + if (usb_set_configuration(dev, cfg) < 0) + return -EINVAL; + return 0; + } return -ENOIOCTLCMD; } @@ -633,8 +735,10 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd struct usb_device *dev = (struct usb_device *)dp->data; struct usb_proc_ctrltransfer ctrl; struct usb_proc_bulktransfer bulk; + struct usb_proc_old_ctrltransfer octrl; + struct usb_proc_old_bulktransfer obulk; struct usb_proc_setinterface setintf; - unsigned int len1, ep, pipe; + unsigned int len1, ep, pipe, cfg; unsigned long len2; unsigned char *tbuf; int i; @@ -655,7 +759,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, - ctrl.length); + ctrl.length, (ctrl.timeout * HZ + 500) / 1000); if (!i && ctrl.length) { copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); } @@ -665,7 +769,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, - ctrl.length); + ctrl.length, (ctrl.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); if (i) { @@ -695,7 +799,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd free_page((unsigned long)tbuf); return -EINVAL; } - i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2); + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, (bulk.timeout * HZ + 500) / 1000); if (!i && len2) { copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); } @@ -703,7 +807,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (len1) { copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT); } - i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2); + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, (bulk.timeout * HZ + 500) / 1000); } free_page((unsigned long)tbuf); if (i) { @@ -713,6 +817,79 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } return len2; + case USB_PROC_OLD_CONTROL: + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + copy_from_user_ret(&octrl, (void *)arg, sizeof(octrl), -EFAULT); + if (octrl.length > PAGE_SIZE) + return -EINVAL; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + if (octrl.requesttype & 0x80) { + if (octrl.length && !access_ok(VERIFY_WRITE, octrl.data, octrl.length)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), octrl.request, + octrl.requesttype, octrl.value, octrl.index, tbuf, + octrl.length, HZ); + if (!i && octrl.length) { + copy_to_user_ret(octrl.data, tbuf, octrl.length, -EFAULT); + } + } else { + if (octrl.length) { + copy_from_user_ret(tbuf, octrl.data, octrl.length, -EFAULT); + } + i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), octrl.request, + octrl.requesttype, octrl.value, octrl.index, tbuf, + octrl.length, HZ); + } + free_page((unsigned long)tbuf); + if (i) { + printk(KERN_WARNING "/proc/bus/usb: USB_PROC_OLD_CONTROL failed rqt %u rq %u len %u ret %d\n", + octrl.requesttype, octrl.request, octrl.length, i); + return -ENXIO; + } + return 0; + + case USB_PROC_OLD_BULK: + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + copy_from_user_ret(&obulk, (void *)arg, sizeof(obulk), -EFAULT); + if (obulk.ep & 0x80) + pipe = usb_rcvbulkpipe(dev, obulk.ep & 0x7f); + else + pipe = usb_sndbulkpipe(dev, obulk.ep & 0x7f); + if (!usb_maxpacket(dev, pipe, !(obulk.ep & 0x80))) + return -EINVAL; + len1 = obulk.len; + if (len1 > PAGE_SIZE) + len1 = PAGE_SIZE; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + if (obulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, obulk.data, len1)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); + if (!i && len2) { + copy_to_user_ret(obulk.data, tbuf, len2, -EFAULT); + } + } else { + if (len1) { + copy_from_user_ret(tbuf, obulk.data, len1, -EFAULT); + } + i = dev->bus->op->bulk_msg(dev, pipe, tbuf, len1, &len2, HZ*5); + } + free_page((unsigned long)tbuf); + if (i) { + printk(KERN_WARNING "/proc/bus/usb: USB_PROC_OLD_BULK failed ep 0x%x len %u ret %d\n", + obulk.ep, obulk.len, i); + return -ENXIO; + } + return len2; + case USB_PROC_RESETEP: if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -730,10 +907,21 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd return -EINVAL; return 0; + case USB_PROC_SETCONFIGURATION: + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + get_user_ret(cfg, (unsigned int *)arg, -EFAULT); + if (usb_set_configuration(dev, cfg) < 0) + return -EINVAL; + return 0; + case EZUSB_CONTROL: case EZUSB_BULK: + case EZUSB_OLD_CONTROL: + case EZUSB_OLD_BULK: case EZUSB_RESETEP: case EZUSB_SETINTERFACE: + case EZUSB_SETCONFIGURATION: return usbdev_ioctl_ezusbcompat(inode, file, cmd, arg); } return -ENOIOCTLCMD; diff --git a/drivers/usb/serial.c b/drivers/usb/serial.c new file mode 100644 index 000000000..b85f2b5c6 --- /dev/null +++ b/drivers/usb/serial.c @@ -0,0 +1,691 @@ +/* + * USB Serial Converter driver + * + * Greg Kroah-Hartman (greg@kroah.com) + * + * This was based on the ACM driver by Armin Fuerst (which was based + * on a driver by Brad Keryan) + * + * Currently only works for the Belkin and Peracom Serial converters. + * Should also work on the Etek serial converter, if anyone knows the + * vendor and device ids for that device. + * + * + * version 0.1.1 (10/05/99) gkh + * Changed the major number to not conflict with anything else. + * + * version 0.1 (09/28/99) gkh + * Can recognize the two different devices and start up a read from + * device when asked to. Writes also work. No control signals yet, this + * all is vendor specific data (i.e. no spec), also no control for + * different baud rates or other bit settings. + * Currently we are using the same devid as the acm driver. This needs + * to change. + * + * (C) Copyright 1999 Greg Kroah-Hartman (greg@kroah.com) + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/fcntl.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/spinlock.h> + + +#include "usb.h" +/*#define SERIAL_DEBUG 1*/ + +#ifdef SERIAL_DEBUG + #define debug_info(message); printk(message); +#else + #define debug_info(message); +#endif + + +/* USB Serial devices vendor ids and device ids that this driver supports */ +#define BELKIN_VENDOR_ID 0x056c +#define BELKIN_SERIAL_CONVERTER 0x8007 +#define PERACOM_VENDOR_ID 0x0565 +#define PERACOM_SERIAL_CONVERTER 0x0001 + + +#define SERIAL_MAJOR 240 + +#define NUM_PORTS 4 /* Have to pick a number for now. Need to look */ + /* into dynamically creating them at insertion time. */ + + +static int usb_serial_probe(struct usb_device *dev); +static void usb_serial_disconnect(struct usb_device *dev); + +typedef enum { + unknown = 0, + Belkin = 1, + Peracom = 2 + } SERIAL_TYPE; + +struct usb_serial_state { + struct usb_device * dev; + SERIAL_TYPE type; /* what manufacturer's type of converter */ + void * irq_handle; + unsigned int irqpipe; + struct tty_struct *tty; /* the coresponding tty for this device */ + char present; + char active; + + char interrupt_in_inuse; + __u8 interrupt_in_endpoint; + __u8 interrupt_in_interval; + __u16 interrupt_in_size; + unsigned int interrupt_in_pipe; + unsigned char * interrupt_in_buffer; + void * interrupt_in_transfer; + + char bulk_in_inuse; + __u8 bulk_in_endpoint; + __u8 bulk_in_interval; + __u16 bulk_in_size; + unsigned int bulk_in_pipe; + unsigned char * bulk_in_buffer; + void * bulk_in_transfer; + + char bulk_out_inuse; + __u8 bulk_out_endpoint; + __u8 bulk_out_interval; + __u16 bulk_out_size; + unsigned int bulk_out_pipe; + unsigned char * bulk_out_buffer; + void * bulk_out_transfer; +}; + +static struct usb_driver usb_serial_driver = { + "serial", + usb_serial_probe, + usb_serial_disconnect, + { NULL, NULL } +}; + +static int serial_refcount; +static struct tty_driver serial_tty_driver; +static struct tty_struct * serial_tty[NUM_PORTS]; +static struct termios * serial_termios[NUM_PORTS]; +static struct termios * serial_termios_locked[NUM_PORTS]; +static struct usb_serial_state serial_state_table[NUM_PORTS]; + + + +static int serial_read_irq (int state, void *buffer, int count, void *dev_id) +{ + struct usb_serial_state *serial = (struct usb_serial_state *)dev_id; + struct tty_struct *tty = serial->tty; + unsigned char* data = buffer; + int i; + + debug_info("USB: serial_read_irq\n"); + +#ifdef SERIAL_DEBUG + if (count) { + printk("%d %s\n", count, data); + } +#endif + + if (count) { + for (i=0;i<count;i++) { + tty_insert_flip_char(tty,data[i],0); + } + tty_flip_buffer_push(tty); + } + + /* Continue transfer */ + /* return (1); */ + + /* No more transfer, let the irq schedule us again */ + serial->bulk_in_inuse = 0; + return (0); +} + + +static int serial_write_irq (int state, void *buffer, int count, void *dev_id) +{ + struct usb_serial_state *serial = (struct usb_serial_state *) dev_id; + struct tty_struct *tty = serial->tty; + + debug_info("USB Serial: serial_write_irq\n"); + + if (!serial->bulk_out_inuse) { + debug_info("USB Serial: write irq for a finished pipe?\n"); + return (0); + } + + usb_terminate_bulk (serial->dev, serial->bulk_out_transfer); + serial->bulk_out_inuse = 0; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + return 0; +} + + +static int usb_serial_irq (int state, void *buffer, int len, void *dev_id) +{ +// struct usb_serial_state *serial = (struct usb_serial_state *) dev_id; + + debug_info("USB Serial: usb_serial_irq\n"); + + /* ask for a bulk read */ +// serial->bulk_in_inuse = 1; +// serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial); + + return (1); +} + + + + + +/* tty interface functions */ +static int serial_open (struct tty_struct *tty, struct file * filp) +{ + struct usb_serial_state *serial; + + debug_info("USB: serial_open\n"); + + serial = &serial_state_table [MINOR(tty->device)-tty->driver.minor_start]; + tty->driver_data = serial; + serial->tty = tty; + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return -EINVAL; + } + + if (serial->active) { + debug_info ("USB Serial: device already open\n"); + return -EINVAL; + } + serial->active = 1; + + /*Start reading from the device*/ + serial->bulk_in_inuse = 1; + serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial); + + /* Need to do device specific setup here (control lines, baud rate, etc.) */ + /* FIXME!!! */ + + return (0); +} + + +static void serial_close(struct tty_struct *tty, struct file * filp) +{ + struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; + debug_info("USB: serial_close\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return; + } + + if (!serial->active) { + debug_info ("USB Serial: device already open\n"); + return; + } + + /* Need to change the control lines here */ + /* FIXME */ + + if (serial->bulk_out_inuse){ + usb_terminate_bulk (serial->dev, serial->bulk_out_transfer); + serial->bulk_out_inuse = 0; + } + if (serial->bulk_in_inuse){ + usb_terminate_bulk (serial->dev, serial->bulk_in_transfer); + serial->bulk_in_inuse = 0; + } + + /* release the irq? */ + + serial->active = 0; +} + + +static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; + int written; + + debug_info("USB Serial: serial_write\n"); + + if (!serial->present) { + debug_info("USB Serial: device not registered\n"); + return (-EINVAL); + } + + if (!serial->active) { + debug_info ("USB Serial: device not opened\n"); + return (-EINVAL); + } + + if (serial->bulk_out_inuse) { + debug_info ("USB Serial: already writing\n"); + return (0); + } + + written = (count > serial->bulk_out_size) ? serial->bulk_out_size : count; + + if (from_user) { + copy_from_user(serial->bulk_out_buffer, buf, written); + } + else { + memcpy (serial->bulk_out_buffer, buf, written); + } + + /* send the data out the bulk port */ + serial->bulk_out_inuse = 1; + serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, written, serial); + + return (written); +} + + +static void serial_put_char (struct tty_struct *tty, unsigned char ch) +{ + struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; + + debug_info("USB Serial: serial_put_char\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return; + } + + if (!serial->active) { + debug_info ("USB Serial: device not open\n"); + return; + } + + if (serial->bulk_out_inuse) { + debug_info ("USB Serial: already writing\n"); + return; + } + + /* send the single character out the bulk port */ + serial->bulk_out_buffer[0] = ch; + serial->bulk_out_inuse = 1; + serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, 1, serial); + + return; +} + + +static int serial_write_room (struct tty_struct *tty) +{ + struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; + + debug_info("USB Serial: serial_write_room\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return (-EINVAL); + } + + if (!serial->active) { + debug_info ("USB Serial: device not open\n"); + return (-EINVAL); + } + + if (serial->bulk_out_inuse) { + return (0); + } + + return serial->bulk_out_size; +} + + +static int serial_chars_in_buffer (struct tty_struct *tty) +{ + struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; + + debug_info("USB Serial: serial_chars_in_buffer\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return (-EINVAL); + } + + if (!serial->active) { + debug_info ("USB Serial: device not open\n"); + return (-EINVAL); + } + + if (serial->bulk_out_inuse) { + return (serial->bulk_out_size); + } + + return (0); +} + + +static void serial_throttle (struct tty_struct * tty) +{ + struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; + + debug_info("USB Serial: serial_throttle\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return; + } + + if (!serial->active) { + debug_info ("USB Serial: device not open\n"); + return; + } + + + /* Change the control signals */ + /* FIXME!!! */ + + return; +} + + +static void serial_unthrottle (struct tty_struct * tty) +{ + struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; + + debug_info("USB Serial: serial_unthrottle\n"); + + if (!serial->present) { + debug_info("USB Serial: no device registered\n"); + return; + } + + if (!serial->active) { + debug_info ("USB Serial: device not open\n"); + return; + } + + + /* Change the control signals */ + /* FIXME!!! */ + + return; +} + + +static int Get_Free_Serial (void) +{ + int i; + + for (i=0; i < NUM_PORTS; ++i) { + if (!serial_state_table[i].present) + return (i); + } + return (-1); +} + + +static int usb_serial_probe(struct usb_device *dev) +{ + struct usb_serial_state *serial; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + SERIAL_TYPE type; + int serial_num; +// int ret; + int i; + + /* look at the device descriptor to see if it is a type that we recognize */ + type = unknown; + if ((dev->descriptor.idVendor == BELKIN_VENDOR_ID) && + (dev->descriptor.idProduct == BELKIN_SERIAL_CONVERTER)) { + /* This is the Belkin serial convertor */ + type = Belkin; + } + + if ((dev->descriptor.idVendor == PERACOM_VENDOR_ID) && + (dev->descriptor.idProduct == PERACOM_SERIAL_CONVERTER)) { + /* This is the Peracom serial convertor */ + type = Peracom; + } + + if (type == unknown) + return (-1); + + printk (KERN_INFO "USB serial converter detected.\n"); + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + printk (KERN_INFO " Failed usb_set_configuration: serial\n"); + return (-1); + } + + if (0>(serial_num = Get_Free_Serial())) { + debug_info("USB Serial: Too many devices connected\n"); + return (-1); + } + + serial = &serial_state_table[serial_num]; + + memset(serial, 0, sizeof(serial)); + serial->dev = dev; + serial->type = type; + dev->private = serial; + + /* we should have 1 bulk in, 1 bulk out, and 1 interrupt in endpoints */ + interface = &dev->config[0].interface[0].altsetting[0]; + for (i = 0; i < interface->bNumEndpoints; ++i) { + endpoint = &interface->endpoint[i]; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found the bulk in endpoint */ + serial->bulk_in_inuse = 0; + serial->bulk_in_endpoint = endpoint->bEndpointAddress; + serial->bulk_in_size = endpoint->wMaxPacketSize; + serial->bulk_in_interval = endpoint->bInterval; + serial->bulk_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint); + serial->bulk_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL); + if (!serial->bulk_in_buffer) { + printk("USB Serial: Couldn't allocate bulk_in_buffer\n"); + goto probe_error; + } + } + + if (((endpoint->bEndpointAddress & 0x80) == 0x00) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found the bulk out endpoint */ + serial->bulk_out_inuse = 0; + serial->bulk_out_endpoint = endpoint->bEndpointAddress; + serial->bulk_out_size = endpoint->wMaxPacketSize; + serial->bulk_out_interval = endpoint->bInterval; + serial->bulk_out_pipe = usb_rcvbulkpipe (dev, serial->bulk_out_endpoint); + serial->bulk_out_buffer = kmalloc (serial->bulk_out_size, GFP_KERNEL); + if (!serial->bulk_out_buffer) { + printk("USB Serial: Couldn't allocate bulk_out_buffer\n"); + goto probe_error; + } + } + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x03)) { + /* we found the interrupt in endpoint */ + serial->interrupt_in_inuse = 0; + serial->interrupt_in_endpoint = endpoint->bEndpointAddress; + serial->interrupt_in_size = endpoint->wMaxPacketSize; + serial->interrupt_in_interval = endpoint->bInterval; + /* serial->interrupt_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint); */ + serial->interrupt_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL); + if (!serial->bulk_in_buffer) { + printk("USB Serial: Couldn't allocate interrupt_in_buffer\n"); + goto probe_error; + } + } + + } + + + /* verify that we found all of the endpoints that we need */ + if ((!serial->bulk_in_buffer) || + (!serial->bulk_out_buffer) || + (!serial->interrupt_in_buffer)) { + printk("USB Serial: did not find all of the required endpoints\n"); + goto probe_error; + } + + + /* set up an interrupt for out bulk in pipe */ + /* ask for a bulk read */ +// serial->bulk_in_inuse = 1; +// serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial); + + /* set up our interrupt to be the time for the bulk in read */ +// ret = usb_request_irq (dev, serial->bulk_in_pipe, usb_serial_irq, serial->bulk_in_interval, serial, &serial->irq_handle); +// if (ret) { +// printk(KERN_INFO "USB Serial failed usb_request_irq (0x%x)\n", ret); +// goto probe_error; +// } + + serial->present = 1; + MOD_INC_USE_COUNT; + + return (0); + +probe_error: + if (serial) { + if (serial->bulk_in_buffer) + kfree (serial->bulk_in_buffer); + if (serial->bulk_out_buffer) + kfree (serial->bulk_out_buffer); + if (serial->interrupt_in_buffer) + kfree (serial->interrupt_in_buffer); + } + return (-1); +} + + +static void usb_serial_disconnect(struct usb_device *dev) +{ + struct usb_serial_state *serial = (struct usb_serial_state *)dev->private; + + if (serial) { + if (!serial->present) { + /* something strange is going on */ + debug_info("USB Serial: disconnect but not present?\n") + return; + } + + /* need to stop any transfers...*/ + if (serial->bulk_in_inuse) { + usb_terminate_bulk (serial->dev, serial->bulk_in_transfer); + serial->bulk_in_inuse = 0; + } + if (serial->bulk_out_inuse) { + usb_terminate_bulk (serial->dev, serial->bulk_out_transfer); + serial->bulk_out_inuse = 0; + } + // usb_release_irq (serial->dev, serial->irq_handle, serial->bulk_in_pipe); + if (serial->bulk_in_buffer) + kfree (serial->bulk_in_buffer); + if (serial->bulk_out_buffer) + kfree (serial->bulk_out_buffer); + if (serial->interrupt_in_buffer) + kfree (serial->interrupt_in_buffer); + + serial->present = 0; + serial->active = 0; + } + dev->private = NULL; + + MOD_DEC_USE_COUNT; + + printk (KERN_INFO "USB Serial device disconnected.\n"); +} + + + +int usb_serial_init(void) +{ + int i; + + /* Initalize our global data */ + for (i = 0; i < NUM_PORTS; ++i) { + memset(&serial_state_table[i], 0x00, sizeof(struct usb_serial_state)); + } + + /* register the tty driver */ + memset (&serial_tty_driver, 0, sizeof(struct tty_driver)); + serial_tty_driver.magic = TTY_DRIVER_MAGIC; + serial_tty_driver.driver_name = "usb"; + serial_tty_driver.name = "ttyUSB"; + serial_tty_driver.major = SERIAL_MAJOR; + serial_tty_driver.minor_start = 0; + serial_tty_driver.num = NUM_PORTS; + serial_tty_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_tty_driver.subtype = SERIAL_TYPE_NORMAL; + serial_tty_driver.init_termios = tty_std_termios; + serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_tty_driver.flags = TTY_DRIVER_REAL_RAW; + serial_tty_driver.refcount = &serial_refcount; + serial_tty_driver.table = serial_tty; + serial_tty_driver.termios = serial_termios; + serial_tty_driver.termios_locked = serial_termios_locked; + + serial_tty_driver.open = serial_open; + serial_tty_driver.close = serial_close; + serial_tty_driver.write = serial_write; + serial_tty_driver.put_char = serial_put_char; + serial_tty_driver.flush_chars = NULL; //serial_flush_chars; + serial_tty_driver.write_room = serial_write_room; + serial_tty_driver.ioctl = NULL; //serial_ioctl; + serial_tty_driver.set_termios = NULL; //serial_set_termios; + serial_tty_driver.set_ldisc = NULL; + serial_tty_driver.throttle = serial_throttle; + serial_tty_driver.unthrottle = serial_unthrottle; + serial_tty_driver.stop = NULL; //serial_stop; + serial_tty_driver.start = NULL; //serial_start; + serial_tty_driver.hangup = NULL; //serial_hangup; + serial_tty_driver.break_ctl = NULL; //serial_break; + serial_tty_driver.wait_until_sent = NULL; //serial_wait_until_sent; + serial_tty_driver.send_xchar = NULL; //serial_send_xchar; + serial_tty_driver.read_proc = NULL; //serial_read_proc; + serial_tty_driver.chars_in_buffer = serial_chars_in_buffer; + serial_tty_driver.flush_buffer = NULL; //serial_flush_buffer; + if (tty_register_driver (&serial_tty_driver)) { + printk( "USB Serial: failed to register tty driver\n" ); + return -EPERM; + } + + /* register the USB driver */ + usb_register(&usb_serial_driver); + printk(KERN_INFO "USB Serial support registered.\n"); + return 0; +} + + +#ifdef MODULE +int init_module(void) +{ + return usb_serial_init(); +} + +void cleanup_module(void) +{ + tty_unregister_driver(&serial_tty_driver); + usb_deregister(&usb_serial_driver); +} + +#endif + diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 5bfab7da9..e359d7721 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -126,7 +126,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned { unsigned int status; struct uhci_td *tmp; - int count = 1000; + int count = 1000, bytesreceived = 0; if (rval) *rval = 0; @@ -144,8 +144,10 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned do { status = uhci_status_bits(tmp->status); - if (status) { - if (debug) { + if (status) + break; +#if 0 + if (debug) { /* Must reset the toggle on first error */ if (uhci_debug) { printk(KERN_DEBUG "Set toggle from %x rval %ld\n", @@ -161,6 +163,13 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (rval && ((tmp->info & 0xFF) == USB_PID_IN)) *rval += uhci_actual_length(tmp->status); } +#endif + /* The length field is only valid if the TD was completed */ + if (!(tmp->status & TD_CTRL_ACTIVE) && uhci_packetin(tmp->info)) { + bytesreceived += uhci_actual_length(tmp->status); + if (rval) + *rval += uhci_actual_length(tmp->status); + } if ((tmp->link & UHCI_PTR_TERM) || (tmp->link & UHCI_PTR_QH)) @@ -173,8 +182,35 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned printk(KERN_ERR "runaway td's in uhci_td_result!\n"); /* Force debugging on */ debug = 1; - } else if (!status) - return USB_ST_NOERROR; + } else { + /* If we got to the last TD */ + + /* No error */ + if (!status) + return USB_ST_NOERROR; + + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of */ + /* the amount we requested */ + if (tmp->status & TD_CTRL_IOC && + tmp->status & TD_CTRL_ACTIVE && + tmp->status & TD_CTRL_NAK) + return USB_ST_NOERROR; + +#if 0 + /* We got to an error, but the controller hasn't finished */ + /* with it, yet */ + if (tmp->status & TD_CTRL_ACTIVE) + return USB_ST_NOCHANGE; +#endif + + /* If this wasn't the last TD and SPD is set, ACTIVE */ + /* is not and NAK isn't then we received a short */ + /* packet */ + if (tmp->status & TD_CTRL_SPD && + !(tmp->status & TD_CTRL_NAK)) + return USB_ST_NOERROR; + } /* Some debugging code */ if (debug && uhci_debug) { @@ -1035,7 +1071,7 @@ static int uhci_generic_completed(int status, void *buffer, int len, void *dev_i } /* td points to the last td in the list, which interrupts on completion */ -static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last) +static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, int timeout) { DECLARE_WAITQUEUE(wait, current); struct uhci_qh *qh = uhci_qh_alloc(dev); @@ -1053,7 +1089,8 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru /* Add it into the skeleton */ uhci_insert_qh(&dev->uhci->skel_control_qh, qh); - schedule_timeout(HZ * 5); /* 5 seconds */ + /* wait a user specified reasonable amount of time */ + schedule_timeout(timeout); remove_wait_queue(&qh->wakeup, &wait); @@ -1094,7 +1131,7 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru * there is no restriction on length of transfers * anymore */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len) +static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; @@ -1184,7 +1221,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre td->link = UHCI_PTR_TERM; /* Terminate */ /* Start it up.. */ - ret = uhci_run_control(dev, first, td); + ret = uhci_run_control(dev, first, td, timeout); count = 1000; td = first; @@ -1231,7 +1268,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre */ /* td points to the last td in the list, which interrupts on completion */ -static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval) +static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval, int timeout) { DECLARE_WAITQUEUE(wait, current); struct uhci_qh *qh = uhci_qh_alloc(dev); @@ -1249,7 +1286,8 @@ static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct /* Add it into the skeleton */ uhci_insert_qh(&dev->uhci->skel_bulk_qh, qh); - schedule_timeout(HZ * 5); /* 5 seconds */ + /* wait a user specified reasonable amount of time */ + schedule_timeout(timeout); remove_wait_queue(&qh->wakeup, &wait); @@ -1272,7 +1310,7 @@ static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct * A bulk message is only built up from * the data phase */ -static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval) +static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; @@ -1334,7 +1372,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da /* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */ /* Start it up.. */ - ret = uhci_run_bulk(dev, first, td, rval); + ret = uhci_run_bulk(dev, first, td, rval, timeout); { int count = 100; @@ -1570,6 +1608,7 @@ static void uhci_check_configuration(struct uhci *uhci) } while (nr < maxchild); } +#if 0 static int fixup_isoc_desc (struct uhci_td *td) { struct usb_isoc_desc *isocdesc = td->dev_id; @@ -1621,6 +1660,7 @@ static int fixup_isoc_desc (struct uhci_td *td) return 0; } +#endif /* 0 */ static void uhci_interrupt_notify(struct uhci *uhci) { @@ -2097,7 +2137,7 @@ static int start_uhci(struct pci_dev *dev) for (i = 0; i < 6; i++) { unsigned int io_addr = dev->resource[i].start; unsigned int io_size = - dev->resource[i].end - dev->resource[i].start; + dev->resource[i].end - dev->resource[i].start + 1; /* IO address? */ if (!(dev->resource[i].flags & 1)) @@ -2107,6 +2147,9 @@ static int start_uhci(struct pci_dev *dev) if (check_region(io_addr, io_size)) break; + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + return found_uhci(dev->irq, io_addr, io_size); } return -1; diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 83b96b0ef..1885c661c 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -53,6 +53,10 @@ #define USBPORTSC_PR 0x0200 /* Port Reset */ #define USBPORTSC_SUSP 0x1000 /* Suspend */ +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + #define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ #define UHCI_PTR_BITS 0x000F diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 6692dd796..2dee323a3 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -2,16 +2,16 @@ * driver/usb/usb-core.c * * (C) Copyright David Waite 1999 - * based on code from usb.c, by Linus Torvolds + * based on code from usb.c, by Linus Torvalds * * The purpose of this file is to pull any and all generic modular code from * usb.c and put it in a separate file. This way usb.c is kept as a generic * library, while this file handles starting drivers, etc. * */ + #include <linux/kernel.h> #include <linux/config.h> -#include <linux/module.h> #include "inits.h" #include "usb.h" @@ -28,9 +28,11 @@ # endif #endif - int usb_init(void) { +#ifdef CONFIG_USB_PROC + proc_usb_init (); +#endif #ifndef CONFIG_USB_MODULE # ifdef CONFIG_USB_UHCI uhci_init(); @@ -59,6 +61,9 @@ int usb_init(void) # ifdef CONFIG_USB_PRINTER usb_printer_init(); # endif +# ifdef CONFIG_USB_SERIAL + usb_serial_init(); +# endif # ifdef CONFIG_USB_CPIA usb_cpia_init(); # endif @@ -69,11 +74,9 @@ int usb_init(void) usb_scsi_init(); # endif #endif -#ifdef CONFIG_USB_PROC - proc_usb_init (); -#endif return 0; } + /* * Clean up when unloading the module */ @@ -100,10 +103,9 @@ int init_module(void) { return usb_init(); } + void cleanup_module(void) { cleanup_drivers(); } #endif - - diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 5895bb6df..f6d901aa3 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -13,15 +13,25 @@ * are evil. */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + #define USB_DEBUG 1 #include <linux/config.h> +#include <linux/module.h> #include <linux/string.h> #include <linux/bitops.h> #include <linux/malloc.h> #include "usb.h" +static int usb_find_driver(struct usb_device *); +static void usb_check_support(struct usb_device *); +static void usb_driver_purge(struct usb_driver *, struct usb_device *); + + /* * We have a per-interface "registered driver" list. */ @@ -172,14 +182,15 @@ static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount * However, this first cut at USB bandwidth allocation does not * contain any frame allocation tracking. */ -int check_bandwidth_alloc (unsigned int old_alloc, long bustime) +static int check_bandwidth_alloc (unsigned int old_alloc, long bustime) { unsigned int new_alloc; new_alloc = old_alloc + bustime; /* what new total allocated bus time would be */ - PRINTD ("usb-bandwidth-alloc: was: %ld, new: %ld, bustime = %ld us, Pipe allowed: %s", + PRINTD ("usb-bandwidth-alloc: was: %u, new: %u, " + "bustime = %ld us, Pipe allowed: %s", old_alloc, new_alloc, bustime, (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? "yes" : "no"); @@ -263,6 +274,7 @@ static void usb_check_support(struct usb_device *dev) if (!dev->driver && dev->devnum > 0) usb_find_driver(dev); } + /* * This entrypoint gets called for each new device. * @@ -318,7 +330,6 @@ void usb_free_dev(struct usb_device *dev) { if (atomic_dec_and_test(&dev->refcnt)) { usb_destroy_configuration(dev); - dev->bus->op->deallocate(dev); kfree(dev); } @@ -681,7 +692,7 @@ int usb_set_address(struct usb_device *dev) dr.index = 0; dr.length = 0; - return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0); + return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0, HZ); } int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) @@ -697,7 +708,7 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char dr.length = size; while (i--) { - if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)) + if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size, HZ)) || result == USB_ST_STALL) break; } @@ -714,7 +725,7 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char dr.index = langid; dr.length = size; - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size); + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size, HZ); } int usb_get_device_descriptor(struct usb_device *dev) @@ -740,7 +751,7 @@ int usb_get_status (struct usb_device *dev, int type, int target, void *data) dr.index = target; dr.length = 2; - return dev->bus->op->control_msg (dev, usb_rcvctrlpipe (dev,0), &dr, data, 2); + return dev->bus->op->control_msg (dev, usb_rcvctrlpipe (dev,0), &dr, data, 2, HZ); } int usb_get_protocol(struct usb_device *dev) @@ -754,7 +765,7 @@ int usb_get_protocol(struct usb_device *dev) dr.index = 1; dr.length = 1; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1)) + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1, HZ)) return -1; return buf[0]; @@ -770,7 +781,7 @@ int usb_set_protocol(struct usb_device *dev, int protocol) dr.index = 1; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) + if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) return -1; return 0; @@ -788,7 +799,7 @@ int usb_set_idle(struct usb_device *dev, int duration, int report_id) dr.index = 1; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) + if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) return -1; return 0; @@ -838,7 +849,7 @@ int usb_clear_halt(struct usb_device *dev, int endp) dr.index = endp; dr.length = 0; - result = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + result = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0, HZ); /* don't clear if failed */ if (result) @@ -850,7 +861,7 @@ int usb_clear_halt(struct usb_device *dev, int endp) dr.length = 2; status = 0xffff; - result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 2); + result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 2, HZ); if (result) return result; @@ -876,7 +887,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) dr.index = interface; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) + if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) return -1; dev->ifnum = interface; @@ -907,7 +918,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) printk(KERN_INFO "usb: selecting invalid configuration %d\n", configuration); return -1; } - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) + if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) return -1; dev->actconfig = cp; @@ -927,7 +938,7 @@ int usb_get_report(struct usb_device *dev, unsigned char type, unsigned char id, dr.index = index; dr.length = size; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, size)) + if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, size, HZ)) return -1; return 0; @@ -962,9 +973,9 @@ int usb_get_configuration(struct usb_device *dev) /* We grab the first 8 bytes so we know how long the whole */ /* configuration is */ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); - if (result) + if (result < 0) return -1; - + /* Get the full buffer */ le16_to_cpus(&desc->wTotalLength); @@ -991,7 +1002,6 @@ int usb_get_configuration(struct usb_device *dev) return 0; } - char *usb_string(struct usb_device *dev, int index) { int len, i; @@ -1115,7 +1125,7 @@ int usb_new_device(struct usb_device *dev) return 0; } -int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size) +int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) { devrequest dr; @@ -1125,7 +1135,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u dr.index = cpu_to_le16p(&index); dr.length = cpu_to_le16p(&size); - return dev->bus->op->control_msg(dev, pipe, &dr, data, size); + return dev->bus->op->control_msg(dev, pipe, &dr, data, size, timeout); } int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle) @@ -1148,9 +1158,10 @@ int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq ha if (!ret) { dev->bus->bandwidth_allocated += bustime; dev->bus->bandwidth_int_reqs++; - PRINTD ("bw_alloc bumped to %d for %d requesters\n", + PRINTD ("bw_alloc bumped to %d for %d requesters", dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs); + dev->bus->bandwidth_int_reqs + + dev->bus->bandwidth_isoc_reqs); } return ret; @@ -1180,9 +1191,10 @@ int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe) bustime = NS_TO_US(bustime); dev->bus->bandwidth_allocated -= bustime; dev->bus->bandwidth_int_reqs--; - PRINTD ("bw_alloc reduced to %d for %d requesters\n", + PRINTD ("bw_alloc reduced to %d for %d requesters", dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs); + dev->bus->bandwidth_int_reqs + + dev->bus->bandwidth_isoc_reqs); } return err; @@ -1208,11 +1220,14 @@ int usb_init_isoc (struct usb_device *usb_dev, long bustime; int err; + if (frame_count <= 0) + return -EINVAL; + /* Check host controller's bandwidth for this Isoc. request. */ /* TBD: some way to factor in frame_spacing ??? */ bustime = calc_bus_time (0, usb_pipein(pipe), 1, usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe))); - bustime = NS_TO_US(bustime); /* work in microseconds */ + bustime = NS_TO_US(bustime) / frame_count; /* work in microseconds */ if (check_bandwidth_alloc (usb_dev->bus->bandwidth_allocated, bustime)) return USB_ST_BANDWIDTH_ERROR; @@ -1222,8 +1237,9 @@ int usb_init_isoc (struct usb_device *usb_dev, if (!err) { usb_dev->bus->bandwidth_allocated += bustime; usb_dev->bus->bandwidth_isoc_reqs++; - PRINTD ("bw_alloc bumped to %d for %d requesters\n", + PRINTD ("bw_alloc bumped to %d for %d requesters", usb_dev->bus->bandwidth_allocated, + usb_dev->bus->bandwidth_int_reqs + usb_dev->bus->bandwidth_isoc_reqs); } @@ -1238,11 +1254,12 @@ void usb_free_isoc (struct usb_isoc_desc *isocdesc) bustime = calc_bus_time (0, usb_pipein(isocdesc->pipe), 1, usb_maxpacket(isocdesc->usb_dev, isocdesc->pipe, usb_pipeout(isocdesc->pipe))); - bustime = NS_TO_US(bustime); + bustime = NS_TO_US(bustime) / isocdesc->frame_count; isocdesc->usb_dev->bus->bandwidth_allocated -= bustime; isocdesc->usb_dev->bus->bandwidth_isoc_reqs--; - PRINTD ("bw_alloc reduced to %d for %d requesters\n", + PRINTD ("bw_alloc reduced to %d for %d requesters", isocdesc->usb_dev->bus->bandwidth_allocated, + isocdesc->usb_dev->bus->bandwidth_int_reqs + isocdesc->usb_dev->bus->bandwidth_isoc_reqs); isocdesc->usb_dev->bus->op->free_isoc (isocdesc); @@ -1271,3 +1288,49 @@ struct list_head *usb_bus_get_list(void) } #endif +/* + * USB may be built into the kernel or be built as modules. + * If the USB core [and maybe a host controller driver] is built + * into the kernel, and other device drivers are built as modules, + * then these symbols need to be exported for the modules to use. + */ +EXPORT_SYMBOL(usb_register); +EXPORT_SYMBOL(usb_deregister); +EXPORT_SYMBOL(usb_alloc_bus); +EXPORT_SYMBOL(usb_free_bus); +EXPORT_SYMBOL(usb_register_bus); +EXPORT_SYMBOL(usb_deregister_bus); +EXPORT_SYMBOL(usb_alloc_dev); +EXPORT_SYMBOL(usb_free_dev); +EXPORT_SYMBOL(usb_inc_dev_use); + +EXPORT_SYMBOL(usb_init_root_hub); +EXPORT_SYMBOL(usb_new_device); +EXPORT_SYMBOL(usb_connect); +EXPORT_SYMBOL(usb_disconnect); + +EXPORT_SYMBOL(usb_set_address); +EXPORT_SYMBOL(usb_get_descriptor); +EXPORT_SYMBOL(usb_get_string); +EXPORT_SYMBOL(usb_string); +EXPORT_SYMBOL(usb_get_protocol); +EXPORT_SYMBOL(usb_set_protocol); +EXPORT_SYMBOL(usb_get_report); +EXPORT_SYMBOL(usb_set_idle); +EXPORT_SYMBOL(usb_clear_halt); +EXPORT_SYMBOL(usb_set_interface); +EXPORT_SYMBOL(usb_get_configuration); +EXPORT_SYMBOL(usb_set_configuration); + +EXPORT_SYMBOL(usb_control_msg); +EXPORT_SYMBOL(usb_request_irq); +EXPORT_SYMBOL(usb_release_irq); +/* EXPORT_SYMBOL(usb_bulk_msg); */ +EXPORT_SYMBOL(usb_request_bulk); +EXPORT_SYMBOL(usb_terminate_bulk); + +EXPORT_SYMBOL(usb_get_current_frame_number); +EXPORT_SYMBOL(usb_init_isoc); +EXPORT_SYMBOL(usb_free_isoc); +EXPORT_SYMBOL(usb_run_isoc); +EXPORT_SYMBOL(usb_kill_isoc); diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index 4ead45e93..4057536bb 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -49,6 +49,15 @@ #define USB_DIR_OUT 0 #define USB_DIR_IN 0x80 +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + /* * USB Packet IDs (PIDs) */ @@ -117,29 +126,44 @@ struct usb_proc_ctrltransfer { __u16 value; __u16 index; __u16 length; - /* pointer to data */ + __u32 timeout; /* in milliseconds */ void *data; }; -#define USB_PROC_CONTROL _IOWR('U', 0, struct usb_proc_ctrltransfer) - struct usb_proc_bulktransfer { unsigned int ep; unsigned int len; + unsigned int timeout; /* in milliseconds */ void *data; }; -#define USB_PROC_BULK _IOWR('U', 2, struct usb_proc_bulktransfer) +struct usb_proc_old_ctrltransfer { + __u8 requesttype; + __u8 request; + __u16 value; + __u16 index; + __u16 length; + /* pointer to data */ + void *data; +}; -#define USB_PROC_RESETEP _IOR('U', 3, unsigned int) +struct usb_proc_old_bulktransfer { + unsigned int ep; + unsigned int len; + void *data; +}; struct usb_proc_setinterface { unsigned int interface; unsigned int altsetting; }; +#define USB_PROC_CONTROL _IOWR('U', 0, struct usb_proc_ctrltransfer) +#define USB_PROC_BULK _IOWR('U', 2, struct usb_proc_bulktransfer) +#define USB_PROC_OLD_CONTROL _IOWR('U', 0, struct usb_proc_old_ctrltransfer) +#define USB_PROC_OLD_BULK _IOWR('U', 2, struct usb_proc_old_bulktransfer) +#define USB_PROC_RESETEP _IOR('U', 3, unsigned int) #define USB_PROC_SETINTERFACE _IOR('U', 4, struct usb_proc_setinterface) - #define USB_PROC_SETCONFIGURATION _IOR('U', 5, unsigned int) @@ -429,8 +453,8 @@ struct usb_isoc_desc { struct usb_operations { int (*allocate)(struct usb_device *); int (*deallocate)(struct usb_device *); - int (*control_msg)(struct usb_device *, unsigned int, devrequest *, void *, int); - int (*bulk_msg)(struct usb_device *, unsigned int, void *, int,unsigned long *); + int (*control_msg)(struct usb_device *, unsigned int, devrequest *, void *, int, int); + int (*bulk_msg)(struct usb_device *, unsigned int, void *, int, unsigned long *, int); int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *, void **); int (*release_irq)(struct usb_device *, void *); void *(*request_bulk)(struct usb_device *, unsigned int, usb_device_irq, @@ -496,7 +520,7 @@ struct usb_device { void *hcpriv; /* Host Controller private data */ void *private; /* Upper layer private data */ - + void *audiopriv; /* May be both audio and HID */ /* procfs entry */ struct proc_dir_entry *proc_entry; @@ -515,10 +539,6 @@ struct usb_device { extern int usb_register(struct usb_driver *); extern void usb_deregister(struct usb_driver *); -int usb_find_driver(struct usb_device *); -void usb_check_support(struct usb_device *); -void usb_driver_purge(struct usb_driver *, struct usb_device *); - extern struct usb_bus *usb_alloc_bus(struct usb_operations *); extern void usb_free_bus(struct usb_bus *); extern void usb_register_bus(struct usb_bus *); @@ -529,7 +549,7 @@ extern void usb_free_dev(struct usb_device *); extern void usb_inc_dev_use(struct usb_device *); #define usb_dec_dev_use usb_free_dev -extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); +extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout); extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *, void **); extern int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe); @@ -695,7 +715,7 @@ void usb_show_string(struct usb_device *dev, char *id, int index); #ifdef USB_DEBUG #define PRINTD(format, args...) printk("usb: " format "\n" , ## args); #else /* NOT DEBUGGING */ -#define PRINTD(fmt, arg...) do {} while (0) /**/ +#define PRINTD(fmt, arg...) do {} while (0) #endif /* USB_DEBUG */ /* A simple way to change one line from DEBUG to NOT DEBUG: */ #define XPRINTD(fmt, arg...) do {} while (0) diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c index 777edd76a..de8a1b356 100644 --- a/drivers/usb/usb_scsi.c +++ b/drivers/usb/usb_scsi.c @@ -142,7 +142,7 @@ static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) do { /*US_DEBUGP("Bulk xfer %x(%d)\n", (unsigned int)buf, this_xfer);*/ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, pipe, buf, - this_xfer, &partial); + this_xfer, &partial, HZ*5); if (result != 0 || partial != this_xfer) US_DEBUGP("bulk_msg returned %d xferred %lu/%d\n", @@ -263,7 +263,6 @@ static int pop_CB_reset(struct us_data *us) { unsigned char cmd[12]; devrequest dr; - int result; US_DEBUGP("pop_CB_reset\n"); dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; @@ -276,7 +275,7 @@ static int pop_CB_reset(struct us_data *us) cmd[1] = 4; us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - &dr, cmd, 12); + &dr, cmd, 12, HZ); /* long wait for reset */ @@ -338,7 +337,7 @@ static int pop_CB_command(Scsi_Cmnd *srb) } result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - &dr, cmd, us->fixedlength); + &dr, cmd, us->fixedlength, HZ); if (!done_start && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/) && cmd[0] == TEST_UNIT_READY && result) { /* as per spec try a start command, wait and retry */ @@ -349,7 +348,7 @@ static int pop_CB_command(Scsi_Cmnd *srb) cmd[4] = 1; /* start */ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - &dr, cmd, us->fixedlength); + &dr, cmd, us->fixedlength, HZ); wait_ms(100); retry++; continue; @@ -357,7 +356,7 @@ static int pop_CB_command(Scsi_Cmnd *srb) } else result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - &dr, srb->cmnd, srb->cmd_len); + &dr, srb->cmnd, srb->cmd_len, HZ); if (/*result != USB_ST_STALL &&*/ result != USB_ST_TIMEOUT) return result; } @@ -389,7 +388,7 @@ static int pop_CB_status(Scsi_Cmnd *srb) dr.length = 2; result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0), - &dr, status, sizeof(status)); + &dr, status, sizeof(status), HZ); if (result != USB_ST_TIMEOUT) break; } @@ -452,7 +451,6 @@ static int pop_CB_status(Scsi_Cmnd *srb) static int pop_CBI(Scsi_Cmnd *srb) { - struct us_data *us = (struct us_data *)srb->host_scribble; int result; /* run the command */ @@ -500,7 +498,7 @@ static int pop_Bulk_reset(struct us_data *us) dr.index = 0; dr.length = 0; - result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0, HZ); if (result) US_DEBUGP("Bulk hard reset failed %d\n", result); usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); @@ -546,7 +544,7 @@ static int pop_Bulk(Scsi_Cmnd *srb) bcb.Tag, bcb.DataTransferLength, bcb.Flags, bcb.Length); result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out), &bcb, - US_BULK_CB_WRAP_LEN, &partial); + US_BULK_CB_WRAP_LEN, &partial, HZ*5); if (result) { US_DEBUGP("Bulk command result %x\n", result); return DID_ABORT << 16; @@ -570,7 +568,7 @@ static int pop_Bulk(Scsi_Cmnd *srb) do { result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, - US_BULK_CS_WRAP_LEN, &partial); + US_BULK_CS_WRAP_LEN, &partial, HZ*5); if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) stall++; else @@ -1296,7 +1294,7 @@ static int scsi_probe(struct usb_device *dev) dr.index = 0; dr.value = 0; dr.length = 0; - ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2); + ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2, HZ); US_DEBUGP("C0 status %x %x\n", qstat[0], qstat[1]); init_waitqueue_head(&ss->ip_waitq); ss->irqpipe = usb_rcvctrlpipe(ss->pusb_dev, ss->ep_int); @@ -1413,8 +1411,6 @@ int init_module(void) void cleanup_module(void) { - unsigned int offset; - usb_deregister(&scsi_driver); } #endif diff --git a/drivers/usb/usb_scsi_debug.c b/drivers/usb/usb_scsi_debug.c index 04ff50174..d29622cad 100644 --- a/drivers/usb/usb_scsi_debug.c +++ b/drivers/usb/usb_scsi_debug.c @@ -5,7 +5,6 @@ * */ -#include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c index 7bf2b0634..9fb30b1a3 100644 --- a/drivers/usb/uss720.c +++ b/drivers/usb/uss720.c @@ -31,6 +31,7 @@ * ECP currently untested * 0.3 10.08.99 fixing merge errors * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable + * 0.5 20.09.99 usb_control_msg wrapper used * */ @@ -61,17 +62,11 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha static const unsigned char regindex[9] = { 4, 0, 1, 5, 5, 0, 2, 3, 6 }; - devrequest dr; int ret; if (!usbdev) return -1; - dr.requesttype = 0xc0; - dr.request = 3; - dr.value = ((unsigned int)reg) << 8; - dr.index = 0; - dr.length = 7; - ret = usbdev->bus->op->control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), &dr, priv->reg, 7); + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, HZ); if (ret) { printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x\n", (unsigned int)reg, ret); @@ -95,17 +90,11 @@ static int set_1284_register(struct parport *pp, unsigned char reg, unsigned cha { struct parport_uss720_private *priv = pp->private_data; struct usb_device *usbdev = priv->usbdev; - devrequest dr; int ret; if (!usbdev) return -1; - dr.requesttype = 0x40; - dr.request = 4; - dr.value = (((unsigned int)reg) << 8) | val; - dr.index = 0; - dr.length = 0; - ret = usbdev->bus->op->control_msg(usbdev, usb_sndctrlpipe(usbdev,0), &dr, NULL, 0); + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, HZ); if (ret) { printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", (unsigned int)reg, (unsigned int)val, ret); @@ -374,7 +363,7 @@ static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, return 0; if (change_mode(pp, ECR_EPP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buf, length, &rlen); + i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buf, length, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buf, length, rlen); change_mode(pp, ECR_PS2); @@ -435,7 +424,7 @@ static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buff return 0; if (change_mode(pp, ECR_ECP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen); + i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); @@ -453,7 +442,7 @@ static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, siz return 0; if (change_mode(pp, ECR_ECP)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen); + i = usbdev->bus->op->bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); @@ -486,7 +475,7 @@ static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer return 0; if (change_mode(pp, ECR_PPF)) return 0; - i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen); + i = usbdev->bus->op->bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), buffer, len, &rlen, HZ*20); if (i) printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %lu\n", buffer, len, rlen); change_mode(pp, ECR_PS2); diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 9fb8d3905..a8e223f6b 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -2,350 +2,362 @@ # Video configuration # +mainmenu_option next_comment +comment 'Frame-buffer support' + +bool 'Support for frame buffer devices (EXPERIMENTAL)' CONFIG_FB + if [ "$CONFIG_FB" = "y" ]; then - define_bool CONFIG_DUMMY_CONSOLE y - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_PCI" = "y" ]; then - tristate 'Cirrus Logic suport (experimental)' CONFIG_FB_CLGEN - tristate 'Permedia2 support (experimental)' CONFIG_FB_PM2 - if [ "$CONFIG_FB_PM2" = "y" ]; then - if [ "$CONFIG_PCI" = "y" ]; then - bool ' enable FIFO disconnect feature' CONFIG_FB_PM2_FIFO_DISCONNECT - bool ' generic Permedia2 PCI board support' CONFIG_FB_PM2_PCI - fi - if [ "$CONFIG_AMIGA" = "y" ]; then - bool ' Phase5 CVisionPPC/BVisionPPC support' CONFIG_FB_PM2_CVPPC - fi + define_bool CONFIG_DUMMY_CONSOLE y + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_PCI" = "y" ]; then + tristate ' Cirrus Logic suport (EXPERIMENTAL)' CONFIG_FB_CLGEN + tristate ' Permedia2 support (EXPERIMENTAL)' CONFIG_FB_PM2 + if [ "$CONFIG_FB_PM2" = "y" ]; then + if [ "$CONFIG_PCI" = "y" ]; then + bool ' enable FIFO disconnect feature' CONFIG_FB_PM2_FIFO_DISCONNECT + bool ' generic Permedia2 PCI board support' CONFIG_FB_PM2_PCI + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + bool ' Phase5 CVisionPPC/BVisionPPC support' CONFIG_FB_PM2_CVPPC + fi + fi fi - fi - fi - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - bool 'Acorn VIDC support' CONFIG_FB_ACORN - fi - if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then - tristate 'Cyber2000 support' CONFIG_FB_CYBER2000 - fi - if [ "$CONFIG_APOLLO" = "y" ]; then - define_bool CONFIG_FB_APOLLO y - fi - if [ "$CONFIG_Q40" = "y" ]; then - define_bool CONFIG_FB_Q40 y - fi - if [ "$CONFIG_AMIGA" = "y" ]; then - bool 'Amiga native chipset support' CONFIG_FB_AMIGA - if [ "$CONFIG_FB_AMIGA" != "n" ]; then - bool 'Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS - bool 'Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS - bool 'Amiga AGA chipset support' CONFIG_FB_AMIGA_AGA - fi - fi - if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'Amiga CyberVision support' CONFIG_FB_CYBER - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Amiga CyberVision3D support (experimental)' CONFIG_FB_VIRGE - tristate 'Amiga RetinaZ3 support' CONFIG_FB_RETINAZ3 - bool 'Amiga FrameMaster II/Rainbow II support (experimental)' CONFIG_FB_FM2 - fi - fi - if [ "$CONFIG_ATARI" = "y" ]; then - bool 'Atari native chipset support' CONFIG_FB_ATARI - tristate 'ATI Mach64 display support' CONFIG_FB_ATY - fi - if [ "$CONFIG_PPC" = "y" ]; then - bool 'Open Firmware frame buffer device support' CONFIG_FB_OF - if [ "$CONFIG_FB_OF" = "y" ]; then - bool 'Apple "control" display support' CONFIG_FB_CONTROL - bool 'Apple "platinum" display support' CONFIG_FB_PLATINUM - bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE - tristate 'ATI Mach64 display support' CONFIG_FB_ATY - bool 'IMS Twin Turbo display support' CONFIG_FB_IMSTT - bool 'Chips 65550 display support' CONFIG_FB_CT65550 - bool 'S3 Trio display support' CONFIG_FB_S3TRIO - fi - tristate 'VGA 16-color graphics console' CONFIG_FB_VGA16 - fi - if [ "$CONFIG_MAC" = "y" ]; then - define_bool CONFIG_FB_MAC y - fi - if [ "$CONFIG_HP300" = "y" ]; then - define_bool CONFIG_FB_HP300 y - fi - if [ "$ARCH" = "alpha" ]; then - tristate 'TGA framebuffer support' CONFIG_FB_TGA - fi - if [ "$ARCH" = "i386" ]; then - bool 'VESA VGA graphics console' CONFIG_FB_VESA - tristate 'VGA 16-color graphics console' CONFIG_FB_VGA16 - define_bool CONFIG_VIDEO_SELECT y - fi - if [ "$CONFIG_VISWS" = "y" ]; then - tristate 'SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW - fi - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_PCI" != "n" ]; then - tristate 'Matrox acceleration' CONFIG_FB_MATROX - if [ "$CONFIG_FB_MATROX" != "n" ]; then - bool ' Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM - bool ' Mystique support' CONFIG_FB_MATROX_MYSTIQUE - bool ' G100/G200/G400 support' CONFIG_FB_MATROX_G100 - bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + bool ' Acorn VIDC support' CONFIG_FB_ACORN + fi + if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then + tristate ' Cyber2000 support' CONFIG_FB_CYBER2000 + fi + if [ "$CONFIG_APOLLO" = "y" ]; then + define_bool CONFIG_FB_APOLLO y + fi + if [ "$CONFIG_Q40" = "y" ]; then + define_bool CONFIG_FB_Q40 y + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + bool ' Amiga native chipset support' CONFIG_FB_AMIGA + if [ "$CONFIG_FB_AMIGA" != "n" ]; then + bool ' Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS + bool ' Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS + bool ' Amiga AGA chipset support' CONFIG_FB_AMIGA_AGA fi - tristate 'ATI Mach64 display support' CONFIG_FB_ATY - fi - fi - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'SBUS and UPA framebuffers' CONFIG_FB_SBUS - if [ "$CONFIG_FB_SBUS" != "n" ]; then - if [ "$ARCH" = "sparc64" ]; then - bool ' Creator/Creator3D support' CONFIG_FB_CREATOR + fi + if [ "$CONFIG_ZORRO" = "y" ]; then + tristate ' Amiga CyberVision support' CONFIG_FB_CYBER + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Amiga CyberVision3D support (EXPERIMENTAL)' CONFIG_FB_VIRGE + tristate ' Amiga RetinaZ3 support (EXPERIMENTAL)' CONFIG_FB_RETINAZ3 + bool ' Amiga FrameMaster II/Rainbow II support (EXPERIMENTAL)' CONFIG_FB_FM2 fi - bool ' CGsix (GX,TurboGX) support' CONFIG_FB_CGSIX - bool ' BWtwo support' CONFIG_FB_BWTWO - bool ' CGthree support' CONFIG_FB_CGTHREE - if [ "$ARCH" = "sparc" ]; then - bool ' TCX (SS4/SS5 only) support' CONFIG_FB_TCX - bool ' CGfourteen (SX) support' CONFIG_FB_CGFOURTEEN - bool ' P9100 (Sparcbook 3 only) support' CONFIG_FB_P9100 + fi + if [ "$CONFIG_ATARI" = "y" ]; then + bool ' Atari native chipset support' CONFIG_FB_ATARI + tristate ' ATI Mach64 display support' CONFIG_FB_ATY + fi + if [ "$CONFIG_PPC" = "y" ]; then + bool ' Open Firmware frame buffer device support' CONFIG_FB_OF + if [ "$CONFIG_FB_OF" = "y" ]; then + bool ' Apple "control" display support' CONFIG_FB_CONTROL + bool ' Apple "platinum" display support' CONFIG_FB_PLATINUM + bool ' Apple "valkyrie" display support' CONFIG_FB_VALKYRIE + bool ' IMS Twin Turbo display support' CONFIG_FB_IMSTT + bool ' Chips 65550 display support' CONFIG_FB_CT65550 + bool ' S3 Trio display support' CONFIG_FB_S3TRIO fi - bool ' Leo (ZX) support' CONFIG_FB_LEO - fi - fi - if [ "$ARCH" = "sparc" ]; then - if [ "$CONFIG_PCI" != "n" ]; then - bool 'PCI framebuffers' CONFIG_FB_PCI - if [ "$CONFIG_FB_PCI" != "n" ]; then - bool ' IGA 168x display support' CONFIG_FB_IGA + tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 + fi + if [ "$CONFIG_MAC" = "y" ]; then + define_bool CONFIG_FB_MAC y + fi + if [ "$CONFIG_HP300" = "y" ]; then + define_bool CONFIG_FB_HP300 y + fi + if [ "$ARCH" = "alpha" ]; then + tristate ' TGA framebuffer support' CONFIG_FB_TGA + fi + if [ "$ARCH" = "i386" ]; then + bool ' VESA VGA graphics console' CONFIG_FB_VESA + tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 + define_bool CONFIG_VIDEO_SELECT y + fi + if [ "$CONFIG_VISWS" = "y" ]; then + tristate ' SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW + define_bool CONFIG_BUS_I2C y + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_PCI" != "n" ]; then + tristate ' Matrox acceleration (EXPERIMENTAL)' CONFIG_FB_MATROX + if [ "$CONFIG_FB_MATROX" != "n" ]; then + bool ' Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM + bool ' Mystique support' CONFIG_FB_MATROX_MYSTIQUE + bool ' G100/G200/G400 support' CONFIG_FB_MATROX_G100 + bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD + fi + tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY + bool ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX fi - fi - fi - if [ "$ARCH" = "sparc64" ]; then - if [ "$CONFIG_PCI" != "n" ]; then - bool 'PCI framebuffers' CONFIG_FB_PCI - if [ "$CONFIG_FB_PCI" != "n" ]; then - tristate ' ATI Mach64 display support' CONFIG_FB_ATY + fi + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + bool ' SBUS and UPA framebuffers' CONFIG_FB_SBUS + if [ "$CONFIG_FB_SBUS" != "n" ]; then + if [ "$ARCH" = "sparc64" ]; then + bool ' Creator/Creator3D support' CONFIG_FB_CREATOR + fi + bool ' CGsix (GX,TurboGX) support' CONFIG_FB_CGSIX + bool ' BWtwo support' CONFIG_FB_BWTWO + bool ' CGthree support' CONFIG_FB_CGTHREE + if [ "$ARCH" = "sparc" ]; then + bool ' TCX (SS4/SS5 only) support' CONFIG_FB_TCX + bool ' CGfourteen (SX) support' CONFIG_FB_CGFOURTEEN + bool ' P9100 (Sparcbook 3 only) support' CONFIG_FB_P9100 + fi + bool ' Leo (ZX) support' CONFIG_FB_LEO fi - fi - fi - tristate 'Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL - - bool 'Advanced low level driver options' CONFIG_FBCON_ADVANCED - if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then - tristate 'Monochrome support' CONFIG_FBCON_MFB - tristate '2 bpp packed pixels support' CONFIG_FBCON_CFB2 - tristate '4 bpp packed pixels support' CONFIG_FBCON_CFB4 - tristate '8 bpp packed pixels support' CONFIG_FBCON_CFB8 - tristate '16 bpp packed pixels support' CONFIG_FBCON_CFB16 - tristate '24 bpp packed pixels support' CONFIG_FBCON_CFB24 - tristate '32 bpp packed pixels support' CONFIG_FBCON_CFB32 - tristate 'Amiga bitplanes support' CONFIG_FBCON_AFB - tristate 'Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM - tristate 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2 - tristate 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4 - tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8 -# tristate 'Atari interleaved bitplanes (16 planes) support' CONFIG_FBCON_IPLAN2P16 - tristate 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC - tristate 'VGA 16-color planar support' CONFIG_FBCON_VGA_PLANES - tristate 'VGA characters/attributes support' CONFIG_FBCON_VGA - else - # Guess what we need - if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_AMIGA" = "y" -o \ - "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ - "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \ - "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ - "$CONFIG_FB_BWTWO" = "y" -o "$CONFIG_FB_CLGEN" = "y" ]; then - define_bool CONFIG_FBCON_MFB y - else - if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \ - "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ - "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \ - "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ - "$CONFIG_FB_BWTWO" = "m" -o "$CONFIG_FB_CLGEN" = "m" ]; then - define_bool CONFIG_FBCON_MFB m + fi + if [ "$ARCH" = "sparc" ]; then + if [ "$CONFIG_PCI" != "n" ]; then + bool ' PCI framebuffers' CONFIG_FB_PCI + if [ "$CONFIG_FB_PCI" != "n" ]; then + bool ' IGA 168x display support' CONFIG_FB_IGA + fi + fi + fi + if [ "$ARCH" = "sparc64" ]; then + if [ "$CONFIG_PCI" != "n" ]; then + bool ' PCI framebuffers' CONFIG_FB_PCI + if [ "$CONFIG_FB_PCI" != "n" ]; then + tristate ' ATI Mach64 display support' CONFIG_FB_ATY + fi fi - fi - if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ - "$CONFIG_FB_VIRTUAL" = "y" ]; then - define_bool CONFIG_FBCON_CFB2 y - define_bool CONFIG_FBCON_CFB4 y - else - if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ - "$CONFIG_FB_VIRTUAL" = "m" ]; then - define_bool CONFIG_FBCON_CFB2 m - define_bool CONFIG_FBCON_CFB4 m + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate ' Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL + fi + + bool ' Advanced low level driver options' CONFIG_FBCON_ADVANCED + if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then + tristate ' Monochrome support' CONFIG_FBCON_MFB + tristate ' 2 bpp packed pixels support' CONFIG_FBCON_CFB2 + tristate ' 4 bpp packed pixels support' CONFIG_FBCON_CFB4 + tristate ' 8 bpp packed pixels support' CONFIG_FBCON_CFB8 + tristate ' 16 bpp packed pixels support' CONFIG_FBCON_CFB16 + tristate ' 24 bpp packed pixels support' CONFIG_FBCON_CFB24 + tristate ' 32 bpp packed pixels support' CONFIG_FBCON_CFB32 + tristate ' Amiga bitplanes support' CONFIG_FBCON_AFB + tristate ' Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM + tristate ' Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2 + tristate ' Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4 + tristate ' Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8 +# tristate ' Atari interleaved bitplanes (16 planes) support' CONFIG_FBCON_IPLAN2P16 + tristate ' Mac variable bpp packed pixels support' CONFIG_FBCON_MAC + tristate ' VGA 16-color planar support' CONFIG_FBCON_VGA_PLANES + tristate ' VGA characters/attributes support' CONFIG_FBCON_VGA + else + # Guess what we need + if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_AMIGA" = "y" -o \ + "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \ + "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ + "$CONFIG_FB_BWTWO" = "y" -o "$CONFIG_FB_CLGEN" = "y" ]; then + define_bool CONFIG_FBCON_MFB y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \ + "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ + "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \ + "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ + "$CONFIG_FB_BWTWO" = "m" -o "$CONFIG_FB_CLGEN" = "m" ]; then + define_bool CONFIG_FBCON_MFB m + fi fi - fi - if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_ATARI" = "y" -o \ - "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ - "$CONFIG_FB_OF" = "y" -o "$CONFIG_FB_TGA" = "y" -o \ - "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ - "$CONFIG_FB_TCX" = "y" -o "$CONFIG_FB_CGTHREE" = "y" -o \ - "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ - "$CONFIG_FB_CGFOURTEEN" = "y" -o "$CONFIG_FB_G364" = "y" -o \ - "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ - "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ - "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ - "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_P9100" = "y" -o \ - "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" ]; then - define_bool CONFIG_FBCON_CFB8 y - else - if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ - "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ - "$CONFIG_FB_OF" = "m" -o "$CONFIG_FB_TGA" = "m" -o \ - "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ - "$CONFIG_FB_TCX" = "m" -o "$CONFIG_FB_CGTHREE" = "m" -o \ - "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ - "$CONFIG_FB_CGFOURTEEN" = "m" -o "$CONFIG_FB_G364" = "m" -o \ - "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ - "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ - "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ - "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_P9100" = "m" -o \ - "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" ]; then - define_bool CONFIG_FBCON_CFB8 m + if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ + "$CONFIG_FB_VIRTUAL" = "y" ]; then + define_bool CONFIG_FBCON_CFB2 y + define_bool CONFIG_FBCON_CFB4 y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_VIRTUAL" = "m" ]; then + define_bool CONFIG_FBCON_CFB2 m + define_bool CONFIG_FBCON_CFB4 m + fi fi - fi - if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \ - "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ - "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" -o \ - "$CONFIG_FB_Q40" = "y" -o \ - "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ - "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ - "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ - "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ - "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ - "$CONFIG_FB_CYBER2000" = "y" ]; then - define_bool CONFIG_FBCON_CFB16 y - else - if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ - "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ - "$CONFIG_FB_VIRTUAL" = "m" -o "$CONFIG_FB_TBOX" = "m" -o \ - "$CONFIG_FB_Q40" = "m" -o \ - "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ - "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ - "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ - "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ - "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "m" -o \ - "$CONFIG_FB_CYBER2000" = "m" ]; then - define_bool CONFIG_FBCON_CFB16 m + if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_ATARI" = "y" -o \ + "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ + "$CONFIG_FB_OF" = "y" -o "$CONFIG_FB_TGA" = "y" -o \ + "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ + "$CONFIG_FB_TCX" = "y" -o "$CONFIG_FB_CGTHREE" = "y" -o \ + "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ + "$CONFIG_FB_CGFOURTEEN" = "y" -o "$CONFIG_FB_G364" = "y" -o \ + "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ + "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ + "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ + "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ + "$CONFIG_FB_P9100" = "y" -o \ + "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ + "$CONFIG_FB_3DFX" = "y" ]; then + define_bool CONFIG_FBCON_CFB8 y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ + "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_OF" = "m" -o "$CONFIG_FB_TGA" = "m" -o \ + "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ + "$CONFIG_FB_TCX" = "m" -o "$CONFIG_FB_CGTHREE" = "m" -o \ + "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ + "$CONFIG_FB_CGFOURTEEN" = "m" -o "$CONFIG_FB_G364" = "m" -o \ + "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ + "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ + "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ + "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ + "$CONFIG_FB_P9100" = "m" -o \ + "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" ]; then + define_bool CONFIG_FBCON_CFB8 m + fi fi - fi - if [ "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ - "$CONFIG_FB_CLGEN" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ - "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_CYBER2000" = "y" ]; then - define_bool CONFIG_FBCON_CFB24 y - else - if [ "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ - "$CONFIG_FB_CLGEN" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ - "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_CYBER2000" = "m" ]; then - define_bool CONFIG_FBCON_CFB24 m + if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ + "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" -o \ + "$CONFIG_FB_Q40" = "y" -o \ + "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ + "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ + "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ + "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ + "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ + "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" ]; then + define_bool CONFIG_FBCON_CFB16 y + else + if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ + "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ + "$CONFIG_FB_VIRTUAL" = "m" -o "$CONFIG_FB_TBOX" = "m" -o \ + "$CONFIG_FB_Q40" = "m" -o \ + "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ + "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ + "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ + "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ + "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "m" -o \ + "$CONFIG_FB_CYBER2000" = "m" ]; then + define_bool CONFIG_FBCON_CFB16 m + fi fi - fi - if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \ - "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ - "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ - "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ - "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then - define_bool CONFIG_FBCON_CFB32 y - else - if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ - "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ - "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ - "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ - "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_SGIVW" = "m" ]; then - define_bool CONFIG_FBCON_CFB32 m + if [ "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ + "$CONFIG_FB_CLGEN" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ + "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ + "$CONFIG_FB_CYBER2000" = "y" ]; then + define_bool CONFIG_FBCON_CFB24 y + else + if [ "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ + "$CONFIG_FB_CLGEN" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ + "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ + "$CONFIG_FB_CYBER2000" = "m" ]; then + define_bool CONFIG_FBCON_CFB24 m + fi fi - fi - if [ "$CONFIG_FB_AMIGA" = "y" ]; then - define_bool CONFIG_FBCON_AFB y - define_bool CONFIG_FBCON_ILBM y - else - if [ "$CONFIG_FB_AMIGA" = "m" ]; then - define_bool CONFIG_FBCON_AFB m - define_bool CONFIG_FBCON_ILBM m + if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \ + "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ + "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ + "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ + "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ + "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ + "$CONFIG_FB_3DFX" = "y" ]; then + define_bool CONFIG_FBCON_CFB32 y + else + if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ + "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ + "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ + "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ + "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ + "$CONFIG_FB_SGIVW" = "m" ]; then + define_bool CONFIG_FBCON_CFB32 m + fi fi - fi - if [ "$CONFIG_FB_ATARI" = "y" ]; then - define_bool CONFIG_FBCON_IPLAN2P2 y - define_bool CONFIG_FBCON_IPLAN2P4 y - define_bool CONFIG_FBCON_IPLAN2P8 y -# define_bool CONFIG_FBCON_IPLAN2P16 y - else - if [ "$CONFIG_FB_ATARI" = "m" ]; then - define_bool CONFIG_FBCON_IPLAN2P2 m - define_bool CONFIG_FBCON_IPLAN2P4 m - define_bool CONFIG_FBCON_IPLAN2P8 m -# define_bool CONFIG_FBCON_IPLAN2P16 m + if [ "$CONFIG_FB_AMIGA" = "y" ]; then + define_bool CONFIG_FBCON_AFB y + define_bool CONFIG_FBCON_ILBM y + else + if [ "$CONFIG_FB_AMIGA" = "m" ]; then + define_bool CONFIG_FBCON_AFB m + define_bool CONFIG_FBCON_ILBM m + fi fi - fi - if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then - define_bool CONFIG_FBCON_MAC y - else - if [ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then - define_bool CONFIG_FBCON_MAC m + if [ "$CONFIG_FB_ATARI" = "y" ]; then + define_bool CONFIG_FBCON_IPLAN2P2 y + define_bool CONFIG_FBCON_IPLAN2P4 y + define_bool CONFIG_FBCON_IPLAN2P8 y +# define_bool CONFIG_FBCON_IPLAN2P16 y + else + if [ "$CONFIG_FB_ATARI" = "m" ]; then + define_bool CONFIG_FBCON_IPLAN2P2 m + define_bool CONFIG_FBCON_IPLAN2P4 m + define_bool CONFIG_FBCON_IPLAN2P8 m +# define_bool CONFIG_FBCON_IPLAN2P16 m + fi fi - fi - if [ "$CONFIG_FB_VGA16" = "y" ]; then - define_bool CONFIG_FBCON_VGA_PLANES y - else - if [ "$CONFIG_FB_VGA16" = "m" ]; then - define_bool CONFIG_FBCON_VGA_PLANES m + if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then + define_bool CONFIG_FBCON_MAC y + else + if [ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then + define_bool CONFIG_FBCON_MAC m + fi fi - fi - if [ "$CONFIG_FB_MDA" = "y" -o "$CONFIG_FB_VGA" = "y" ]; then - define_bool CONFIG_FBCON_VGA y - else - if [ "$CONFIG_FB_MDA" = "m" -o "$CONFIG_FB_VGA" = "m" ]; then - define_bool CONFIG_FBCON_VGA m + if [ "$CONFIG_FB_VGA16" = "y" ]; then + define_bool CONFIG_FBCON_VGA_PLANES y + else + if [ "$CONFIG_FB_VGA16" = "m" ]; then + define_bool CONFIG_FBCON_VGA_PLANES m + fi fi - fi - fi - bool 'Support only 8 pixels wide fonts' CONFIG_FBCON_FONTWIDTH8_ONLY - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'Sparc console 8x16 font' CONFIG_FONT_SUN8x16 - if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then - bool 'Sparc console 12x22 font (not supported by all drivers)' CONFIG_FONT_SUN12x22 - fi - bool 'Select other fonts' CONFIG_FBCON_FONTS - if [ "$CONFIG_FBCON_FONTS" = "y" ]; then - bool ' VGA 8x8 font' CONFIG_FONT_8x8 - bool ' VGA 8x16 font' CONFIG_FONT_8x16 - if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then - bool ' Mac console 6x11 font (not supported by all drivers)' CONFIG_FONT_6x11 + if [ "$CONFIG_FB_MDA" = "y" -o "$CONFIG_FB_VGA" = "y" ]; then + define_bool CONFIG_FBCON_VGA y + else + if [ "$CONFIG_FB_MDA" = "m" -o "$CONFIG_FB_VGA" = "m" ]; then + define_bool CONFIG_FBCON_VGA m + fi fi - bool ' Pearl (old m68k) console 8x8 font' CONFIG_FONT_PEARL_8x8 - bool ' Acorn console 8x8 font' CONFIG_FONT_ACORN_8x8 - fi - else - bool 'Select compiled-in fonts' CONFIG_FBCON_FONTS - if [ "$CONFIG_FBCON_FONTS" = "y" ]; then - bool ' VGA 8x8 font' CONFIG_FONT_8x8 - bool ' VGA 8x16 font' CONFIG_FONT_8x16 + fi + bool ' Support only 8 pixels wide fonts' CONFIG_FBCON_FONTWIDTH8_ONLY + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then bool ' Sparc console 8x16 font' CONFIG_FONT_SUN8x16 if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then - bool ' Sparc console 12x22 font (not supported by all drivers)' CONFIG_FONT_SUN12x22 - bool ' Mac console 6x11 font (not supported by all drivers)' CONFIG_FONT_6x11 + bool ' Sparc console 12x22 font (not supported by all drivers)' CONFIG_FONT_SUN12x22 fi - bool ' Pearl (old m68k) console 8x8 font' CONFIG_FONT_PEARL_8x8 - bool ' Acorn console 8x8 font' CONFIG_FONT_ACORN_8x8 - else - define_bool CONFIG_FONT_8x8 y - define_bool CONFIG_FONT_8x16 y - if [ "$CONFIG_MAC" = "y" ]; then - if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then - define_bool CONFIG_FONT_6x11 y - fi + bool ' Select other fonts' CONFIG_FBCON_FONTS + if [ "$CONFIG_FBCON_FONTS" = "y" ]; then + bool ' VGA 8x8 font' CONFIG_FONT_8x8 + bool ' VGA 8x16 font' CONFIG_FONT_8x16 + if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then + bool ' Mac console 6x11 font (not supported by all drivers)' CONFIG_FONT_6x11 + fi + bool ' Pearl (old m68k) console 8x8 font' CONFIG_FONT_PEARL_8x8 + bool ' Acorn console 8x8 font' CONFIG_FONT_ACORN_8x8 fi - if [ "$CONFIG_AMIGA" = "y" ]; then - define_bool CONFIG_FONT_PEARL_8x8 y + else + bool ' Select compiled-in fonts' CONFIG_FBCON_FONTS + if [ "$CONFIG_FBCON_FONTS" = "y" ]; then + bool ' VGA 8x8 font' CONFIG_FONT_8x8 + bool ' VGA 8x16 font' CONFIG_FONT_8x16 + bool ' Sparc console 8x16 font' CONFIG_FONT_SUN8x16 + if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then + bool ' Sparc console 12x22 font (not supported by all drivers)' CONFIG_FONT_SUN12x22 + bool ' Mac console 6x11 font (not supported by all drivers)' CONFIG_FONT_6x11 + fi + bool ' Pearl (old m68k) console 8x8 font' CONFIG_FONT_PEARL_8x8 + bool ' Acorn console 8x8 font' CONFIG_FONT_ACORN_8x8 + else + define_bool CONFIG_FONT_8x8 y + define_bool CONFIG_FONT_8x16 y + if [ "$CONFIG_MAC" = "y" ]; then + if [ "$CONFIG_FBCON_FONTWIDTH8_ONLY" = "n" ]; then + define_bool CONFIG_FONT_6x11 y + fi + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + define_bool CONFIG_FONT_PEARL_8x8 y + fi + if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_ACORN" = "y" ]; then + define_bool CONFIG_FONT_ACORN_8x8 y + fi fi - if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_ACORN" = "y" ]; then - define_bool CONFIG_FONT_ACORN_8x8 y - fi - fi - fi + fi fi + +endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8e328ed1e..e4466cfcb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -150,13 +150,17 @@ else endif ifeq ($(CONFIG_FB_SGIVW),y) -L_OBJS += sgivwfb.o +LX_OBJS += sgivwfb.o else ifeq ($(CONFIG_FB_SGIVW),m) - M_OBJS += sgivwfb.o + MX_OBJS += sgivwfb.o endif endif +ifeq ($(CONFIG_FB_3DFX),y) +L_OBJS += tdfxfb.o +endif + ifeq ($(CONFIG_FB_MAC),y) L_OBJS += macfb.o endif diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 47531e1bb..fb578ffdb 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -1736,6 +1736,7 @@ default_chipset: fb_info.updatevar = &amifbcon_updatevar; fb_info.blank = &amifbcon_blank; fb_info.flags = FBINFO_FLAG_DEFAULT; + memset(&var, 0, sizeof(var)); if (!fb_find_mode(&var, &fb_info, mode_option, ami_modedb, NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) diff --git a/drivers/video/aty.h b/drivers/video/aty.h index e7b6620ad..2e5900f34 100644 --- a/drivers/video/aty.h +++ b/drivers/video/aty.h @@ -103,6 +103,8 @@ #define CUSTOM_MACRO_CNTL 0x00D4 /* Dword offset 0_35 */ +#define POWER_MANAGEMENT 0x00D8 /* Dword offset 0_36 (LG) */ + #define CONFIG_CNTL 0x00DC /* Dword offset 0_37 (CT, ET, VT) */ #define CONFIG_CHIP_ID 0x00E0 /* Dword offset 0_38 */ #define CONFIG_STAT0 0x00E4 /* Dword offset 0_39 */ @@ -951,4 +953,15 @@ #define MACH64_NUM_CLOCKS 16 #define MACH64_NUM_FREQS 50 +/* Power Management register constants (LTG and LT Pro) */ +#define PWR_MGT_ON 0x00000001 +#define PWR_MGT_MODE_MASK 0x00000006 +#define AUTO_PWR_UP 0x00000008 +#define SELF_REFRESH 0x00000080 +#define PWR_BLON 0x02000000 +#define STANDBY_NOW 0x10000000 +#define SUSPEND_NOW 0x20000000 +#define PWR_MGT_STATUS_MASK 0xC0000000 +#define PWR_MGT_STATUS_SUSPEND 0x80000000 + #endif /* REGMACH64_H */ diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c index f12fec6e1..edba704a3 100644 --- a/drivers/video/atyfb.c +++ b/drivers/video/atyfb.c @@ -1,4 +1,4 @@ -/* $Id: atyfb.c,v 1.122 1999/09/06 20:44:08 geert Exp $ +/* $Id: atyfb.c,v 1.126 1999/09/16 18:46:23 geert Exp $ * linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64 * * Copyright (C) 1997-1998 Geert Uytterhoeven @@ -52,7 +52,6 @@ #include <linux/console.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/nvram.h> #include <linux/kd.h> #include <linux/vt_kern.h> @@ -62,12 +61,15 @@ #include <asm/io.h> -#if defined(CONFIG_PPC) +#ifdef __powerpc__ +#include <linux/adb.h> +#include <linux/pmu.h> #include <asm/prom.h> #include <asm/pci-bridge.h> #include <video/macmodes.h> -#include <asm/adb.h> -#include <asm/pmu.h> +#endif +#ifdef CONFIG_NVRAM +#include <linux/nvram.h> #endif #ifdef __sparc__ #include <asm/pbm.h> @@ -272,8 +274,20 @@ struct fb_info_aty { int vtconsole; int consolecnt; #endif +#ifdef CONFIG_PMAC_PBOOK + unsigned char *save_framebuffer; + unsigned long save_pll[64]; +#endif }; +#ifdef CONFIG_PMAC_PBOOK + int aty_sleep_notify(struct pmu_sleep_notifier *self, int when); + static struct pmu_sleep_notifier aty_sleep_notifier = { + aty_sleep_notify, SLEEP_LEVEL_VIDEO, + }; + static struct fb_info_aty* first_display = NULL; +#endif + /* * Frame buffer device API @@ -446,7 +460,7 @@ static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *fb); static void do_install_cmap(int con, struct fb_info *info); -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC static int read_aty_sense(const struct fb_info_aty *info); #endif @@ -488,9 +502,14 @@ static int default_mclk __initdata = 0; static const char *mode_option __initdata = NULL; #endif -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC +#ifdef CONFIG_NVRAM 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_CHOOSE; +#endif #endif #ifdef CONFIG_ATARI @@ -550,58 +569,46 @@ static const char *aty_ct_ram[8] __initdata = { static inline u32 aty_ld_le32(unsigned int regindex, const struct fb_info_aty *info) { +#if defined(__powerpc__) unsigned long temp; u32 val; -#if defined(__powerpc__) temp = info->ati_regbase; asm volatile("lwbrx %0,%1,%2" : "=r"(val) : "b" (regindex), "r" (temp)); -#elif defined(__sparc_v9__) - temp = info->ati_regbase + regindex; - val = readl(temp); + return val; +#elif defined(__mc68000__) + return le32_to_cpu(*((volatile u32 *)(info->ati_regbase+regindex))); #else - temp = info->ati_regbase+regindex; - val = le32_to_cpu(*((volatile u32 *)(temp))); + return readl (info->ati_regbase + regindex); #endif - return val; } static inline void aty_st_le32(unsigned int regindex, u32 val, const struct fb_info_aty *info) { +#if defined(__powerpc__) unsigned long temp; -#if defined(__powerpc__) temp = info->ati_regbase; asm volatile("stwbrx %0,%1,%2" : : "r" (val), "b" (regindex), "r" (temp) : "memory"); -#elif defined(__sparc_v9__) - temp = info->ati_regbase + regindex; - writel(val, temp); +#elif defined(__mc68000__) + *((volatile u32 *)(info->ati_regbase+regindex)) = cpu_to_le32(val); #else - temp = info->ati_regbase+regindex; - *((volatile u32 *)(temp)) = cpu_to_le32(val); + writel (val, info->ati_regbase + regindex); #endif } static inline u8 aty_ld_8(unsigned int regindex, const struct fb_info_aty *info) { -#ifdef __sparc_v9__ - return readb(info->ati_regbase + regindex); -#else - return *(volatile u8 *)(info->ati_regbase+regindex); -#endif + return readb (info->ati_regbase + regindex); } static inline void aty_st_8(unsigned int regindex, u8 val, const struct fb_info_aty *info) { -#ifdef __sparc_v9__ - writeb(val, info->ati_regbase + regindex); -#else - *(volatile u8 *)(info->ati_regbase+regindex) = val; -#endif + writeb (val, info->ati_regbase + regindex); } @@ -791,7 +798,7 @@ static u8 aty_ld_pll(int offset, const struct fb_info_aty *info) return res; } -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC /* * Apple monitor sense @@ -831,7 +838,7 @@ static int read_aty_sense(const struct fb_info_aty *info) return sense; } -#endif /* defined(CONFIG_PPC) */ +#endif /* CONFIG_PMAC */ /* ------------------------------------------------------------------------- */ @@ -903,17 +910,19 @@ aty_set_cursor_shape(struct fb_info_aty *fb) for (x = 0; x < c->size.x >> 2; x++) { m = c->mask[x][y]; b = c->bits[x][y]; - *ram++ = cursor_mask_lookup[m >> 4] | - cursor_bits_lookup[(b & m) >> 4]; - *ram++ = cursor_mask_lookup[m & 0x0f] | - cursor_bits_lookup[(b & m) & 0x0f]; + fb_writeb (cursor_mask_lookup[m >> 4] | + cursor_bits_lookup[(b & m) >> 4], + ram++); + fb_writeb (cursor_mask_lookup[m & 0x0f] | + cursor_bits_lookup[(b & m) & 0x0f], + ram++); } for ( ; x < 8; x++) { - *ram++ = 0xaa; - *ram++ = 0xaa; + fb_writeb (0xaa, ram++); + fb_writeb (0xaa, ram++); } } - memset(ram, 0xaa, (64 - c->size.y) * 16); + fb_memset (ram, 0xaa, (64 - c->size.y) * 16); } static void @@ -3239,7 +3248,7 @@ static int __init aty_init(struct fb_info_aty *info, const char *name) struct display *disp; const char *chipname = NULL, *ramname = NULL, *xtal; int pll, mclk, gtb_memsize; -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC int sense; #endif u8 pll_ref_div; @@ -3501,16 +3510,22 @@ static int __init aty_init(struct fb_info_aty *info, const char *name) var = default_var; #else /* !MODULE */ memset(&var, 0, sizeof(var)); -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC + /* + * FIXME: The NVRAM stuff should be put in a Mac-specific file, as it + * applies to all Mac video cards + */ 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 if (default_vmode == VMODE_CHOOSE) { if (Gx == LG_CHIP_ID) /* G3 PowerBook with 1024x768 LCD */ @@ -3522,14 +3537,16 @@ static int __init aty_init(struct fb_info_aty *info, const char *name) } if (default_vmode <= 0 || default_vmode > VMODE_MAX) default_vmode = VMODE_640_480_60; +#ifdef CONFIG_NVRAM if (default_cmode == CMODE_NVRAM) default_cmode = nvram_read_byte(NV_CMODE); +#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 /* !CONFIG_PPC */ +#else /* !CONFIG_PMAC */ #ifdef __sparc__ if (mode_option) { if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0, NULL, 8)) @@ -3540,7 +3557,7 @@ static int __init aty_init(struct fb_info_aty *info, const char *name) if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0, NULL, 8)) var = default_var; #endif /* !__sparc__ */ -#endif /* !CONFIG_PPC */ +#endif /* !CONFIG_PMAC */ #endif /* !MODULE */ if (noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; @@ -3964,6 +3981,13 @@ void __init atyfb_of_init(struct device_node *dp) struct fb_info_aty *info; int i; + if (device_is_compatible(dp, "ATY,264LTPro")) { + /* XXX kludge for now */ + if (dp->name == 0 || strcmp(dp->name, "ATY,264LTProA") != 0 + || dp->parent == 0) + return; + dp = dp->parent; + } switch (dp->n_addrs) { case 1: case 2: @@ -4032,6 +4056,14 @@ void __init atyfb_of_init(struct device_node *dp) return; } +#ifdef CONFIG_PMAC_PBOOK + if (first_display == NULL) + pmu_register_sleep_notifier(&aty_sleep_notifier); + info->next = first_display; + first_display = info; +#endif + + #ifdef CONFIG_FB_COMPAT_XPMAC if (!console_fb_info) console_fb_info = &info->fb_info; @@ -4070,7 +4102,7 @@ int __init atyfb_setup(char *options) default_pll = simple_strtoul(this_opt+4, NULL, 0); else if (!strncmp(this_opt, "mclk:", 5)) default_mclk = simple_strtoul(this_opt+5, NULL, 0); -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC else if (!strncmp(this_opt, "vmode:", 6)) { unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0); if (vmode > 0 && vmode <= VMODE_MAX) @@ -4207,7 +4239,7 @@ static void atyfbcon_blank(int blank, struct fb_info *fb) struct fb_info_aty *info = (struct fb_info_aty *)fb; u8 gen_cntl; -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC if ((_machine == _MACH_Pmac) && blank) pmu_enable_backlight(0); #endif @@ -4232,7 +4264,7 @@ static void atyfbcon_blank(int blank, struct fb_info *fb) gen_cntl &= ~(0x4c); aty_st_8(CRTC_GEN_CNTL, gen_cntl, info); -#if defined(CONFIG_PPC) +#ifdef CONFIG_PMAC if ((_machine == _MACH_Pmac) && !blank) pmu_enable_backlight(1); #endif @@ -4736,6 +4768,98 @@ static struct display_switch fbcon_aty32 = { }; #endif +#ifdef CONFIG_PMAC_PBOOK +/* + * Save the contents of the frame buffer when we go to sleep, + * and restore it when we wake up again. + */ +int +aty_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + struct fb_info_aty *info; + unsigned int pm; + + for (info = first_display; info != NULL; info = info->next) { + struct fb_fix_screeninfo fix; + int nb; + + atyfb_get_fix(&fix, fg_console, (struct fb_info *)info); + nb = fb_display[fg_console].var.yres * fix.line_length; + + switch (when) { + case PBOOK_SLEEP_NOW: + /* Stop accel engine (stop bus mastering) */ + if (info->current_par.accel_flags & FB_ACCELF_TEXT) + reset_engine(info); +#if 1 + /* Backup fb content */ + info->save_framebuffer = vmalloc(nb); + if (info->save_framebuffer) + memcpy(info->save_framebuffer, + (void *)info->frame_buffer, nb); +#endif + /* Blank display and LCD */ + atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info); + + /* Set chip to "suspend" mode. Note: There's an HW bug in the + chip which prevents proper resync on wakeup with automatic + power management, we handle suspend manually using the + following (weird) sequence described by ATI. Note2: + We could enable this for all Rage LT Pro chip ids */ + if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) { + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm &= ~PWR_MGT_ON; + aty_st_le32(POWER_MANAGEMENT, pm, info); + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm &= ~(PWR_BLON | AUTO_PWR_UP); + pm |= SUSPEND_NOW; + aty_st_le32(POWER_MANAGEMENT, pm, info); + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm |= PWR_MGT_ON; + aty_st_le32(POWER_MANAGEMENT, pm, info); + do { + pm = aty_ld_le32(POWER_MANAGEMENT, info); + } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND); + mdelay(500); + } + break; + case PBOOK_WAKE: + /* Wakeup chip */ + if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) { + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm &= ~PWR_MGT_ON; + aty_st_le32(POWER_MANAGEMENT, pm, info); + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm |= (PWR_BLON | AUTO_PWR_UP); + pm &= ~SUSPEND_NOW; + aty_st_le32(POWER_MANAGEMENT, pm, info); + pm = aty_ld_le32(POWER_MANAGEMENT, info); + pm |= PWR_MGT_ON; + aty_st_le32(POWER_MANAGEMENT, pm, info); + do { + pm = aty_ld_le32(POWER_MANAGEMENT, info); + } while ((pm & PWR_MGT_STATUS_MASK) != 0); + mdelay(500); + } +#if 1 + /* Restore fb content */ + if (info->save_framebuffer) { + memcpy((void *)info->frame_buffer, + info->save_framebuffer, nb); + vfree(info->save_framebuffer); + info->save_framebuffer = 0; + } +#endif + /* Restore display */ + atyfb_set_par(&info->current_par, info); + atyfbcon_blank(0, (struct fb_info *)info); + break; + } + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + #ifdef MODULE int __init init_module(void) { diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 79df3aa98..dd20b767e 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -35,8 +35,8 @@ #include <asm/io.h> #include <asm/prom.h> #include <asm/pci-bridge.h> -#include <asm/adb.h> -#include <asm/pmu.h> +#include <linux/adb.h> +#include <linux/pmu.h> #include <video/fbcon.h> #include <video/fbcon-cfb8.h> @@ -103,9 +103,9 @@ struct fb_info_chips { static struct fb_info_chips *all_chips; #ifdef CONFIG_PMAC_PBOOK -int chips_sleep_notify(struct notifier_block *, unsigned long, void *); -static struct notifier_block chips_sleep_notifier = { - chips_sleep_notify, NULL, 0 +int chips_sleep_notify(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier chips_sleep_notifier = { + chips_sleep_notify, SLEEP_LEVEL_VIDEO, }; #endif @@ -329,8 +329,9 @@ static int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, out_8(p->io_base + 0x3c9, blue); #ifdef FBCON_HAS_CFB16 - if (regno < 16) p->fbcon_cfb16_cmap[regno] = - ((red & 0xf8) << 7) | ((green & 0xf8) << 2) | ((blue & 0xf8) >> 3); + if (regno < 16) + p->fbcon_cfb16_cmap[regno] = ((red & 0xf8) << 7) + | ((green & 0xf8) << 2) | ((blue & 0xf8) >> 3); #endif return 0; @@ -638,8 +639,7 @@ static void __init init_chips(struct fb_info_chips *p) #ifdef CONFIG_PMAC_PBOOK if (all_chips == NULL) - notifier_chain_register(&sleep_notifier_list, - &chips_sleep_notifier); + pmu_register_sleep_notifier(&chips_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ p->next = all_chips; all_chips = p; @@ -705,15 +705,26 @@ void __init chips_of_init(struct device_node *dp) * and restore it when we wake up again. */ int -chips_sleep_notify(struct notifier_block *this, unsigned long code, void *x) +chips_sleep_notify(struct pmu_sleep_notifier *self, int when) { struct fb_info_chips *p; for (p = all_chips; p != NULL; p = p->next) { int nb = p->var.yres * p->fix.line_length; - - switch (code) { - case PBOOK_SLEEP: + int i; + + switch (when) { + case PBOOK_SLEEP_NOW: + chipsfb_blank(1, (struct fb_info *)p); + /* get the palette from the chip, Xpmac seems + to set it directly in the chip */ + for (i = 0; i < 256; ++i) { + out_8(p->io_base + 0x3c8, i); + udelay(1); + p->palette[i].red = in_8(p->io_base + 0x3c9); + p->palette[i].green = in_8(p->io_base + 0x3c9); + p->palette[i].blue = in_8(p->io_base + 0x3c9); + } p->save_framebuffer = vmalloc(nb); if (p->save_framebuffer) memcpy(p->save_framebuffer, @@ -726,9 +737,10 @@ chips_sleep_notify(struct notifier_block *this, unsigned long code, void *x) vfree(p->save_framebuffer); p->save_framebuffer = 0; } + chipsfb_blank(0, (struct fb_info *)p); break; } } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 6893afbef..cd269e864 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -43,11 +43,11 @@ #ifdef CONFIG_FB_COMPAT_XPMAC #include <asm/vc_ioctl.h> #endif +#include <linux/adb.h> +#include <linux/cuda.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/pgtable.h> -#include <asm/adb.h> -#include <asm/cuda.h> #include <video/fbcon.h> #include <video/fbcon-cfb8.h> @@ -702,26 +702,48 @@ void __init control_of_init(struct device_node *dp) p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000); /* Work out which banks of VRAM we have installed. */ - /* danj: I guess the card just ignores writes to nonexistant VRAM... */ + /* According to Andrew Fyfe <bandr@best.com>, the VRAM behaves like so: */ + /* afyfe: observations from an 8500: + * - with 2M vram in bank 1, it appears at offsets 0, 2M and 4M + * - with 2M vram in bank 2, it appears only at offset 6M + * - with 4M vram, it appears only as a 4M block at offset 0. + */ + + /* We know there is something at 2M if there is something at 0M. */ + out_8(&p->frame_buffer[0x200000], 0xa5); + out_8(&p->frame_buffer[0x200001], 0x38); + asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0x200000]) : "memory" ); + out_8(&p->frame_buffer[0], 0x5a); out_8(&p->frame_buffer[1], 0xc7); asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0]) : "memory" ); - bank1 = (in_8(&p->frame_buffer[0]) == 0x5a) && (in_8(&p->frame_buffer[1]) == 0xc7); - out_8(&p->frame_buffer[0x600000], 0xa5); - out_8(&p->frame_buffer[0x600001], 0x38); - asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0x600000]) : "memory" ); - bank2 = (in_8(&p->frame_buffer[0x600000]) == 0xa5) - && (in_8(&p->frame_buffer[0x600001]) == 0x38); - - p->total_vram = (bank1 + bank2) * 0x200000; - /* If we don't have bank 1 installed, we hope we have bank 2 :-) */ - p->control_use_bank2 = !bank1; - if (p->control_use_bank2) { + bank1 = (in_8(&p->frame_buffer[0x000000]) == 0x5a) + && (in_8(&p->frame_buffer[0x000001]) == 0xc7); + bank2 = (in_8(&p->frame_buffer[0x200000]) == 0xa5) + && (in_8(&p->frame_buffer[0x200001]) == 0x38); + + if(bank2 && !bank1) + printk(KERN_INFO "controlfb: Found memory at 2MB but not at 0! Please contact dan@debian.org\n"); + + if(!bank1) { + out_8(&p->frame_buffer[0x600000], 0xa5); + out_8(&p->frame_buffer[0x600001], 0x38); + asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0x600000]) : "memory" ); + bank2 = (in_8(&p->frame_buffer[0x600000]) == 0xa5) + && (in_8(&p->frame_buffer[0x600001]) == 0x38); + /* If we don't have bank 1 installed, we hope we have bank 2 :-) */ + p->control_use_bank2 = 1; p->frame_buffer += 0x600000; p->frame_buffer_phys += 0x600000; } + p->total_vram = (bank1 + bank2) * 0x200000; + + printk(KERN_INFO "controlfb: Memory bank 1 %s, bank 2 %s, total VRAM %dMB\n", + bank1 ? "present" : "absent", bank2 ? "present" : "absent", + 2 * (bank1 + bank2)); + init_control(p); } diff --git a/drivers/video/fbcon-afb.c b/drivers/video/fbcon-afb.c index 3b9d4bb93..5bc95651b 100644 --- a/drivers/video/fbcon-afb.c +++ b/drivers/video/fbcon-afb.c @@ -178,7 +178,7 @@ void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx, dest = p->screen_base+dy*fontheight(p)*width; i = p->var.bits_per_pixel; do { - mymemmove(dest, src, height*fontheight(p)*width); + fb_memmove(dest, src, height*fontheight(p)*width); src += p->next_plane; dest += p->next_plane; } while (--i); @@ -191,7 +191,7 @@ void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx, dest = dest0; j = height*fontheight(p); do { - mymemmove(dest, src, width); + fb_memmove(dest, src, width); src += p->next_line; dest += p->next_line; } while (--j); @@ -209,7 +209,7 @@ void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx, do { src -= p->next_line; dest -= p->next_line; - mymemmove(dest, src, width); + fb_memmove(dest, src, width); } while (--j); src0 += p->next_plane; dest0 += p->next_plane; @@ -233,9 +233,9 @@ void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy, int sx, j = height*fontheight(p); do { if (bg & 1) - mymemset(dest, width); + fb_memset255(dest, width); else - mymemclear(dest, width); + fb_memclear(dest, width); dest += p->next_line; } while (--j); bg >>= 1; diff --git a/drivers/video/fbcon-cfb16.c b/drivers/video/fbcon-cfb16.c index 9fef8171e..0c3c79ab7 100644 --- a/drivers/video/fbcon-cfb16.c +++ b/drivers/video/fbcon-cfb16.c @@ -14,6 +14,7 @@ #include <linux/console.h> #include <linux/string.h> #include <linux/fb.h> +#include <asm/io.h> #include <video/fbcon.h> #include <video/fbcon-cfb16.h> @@ -46,7 +47,7 @@ void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src, *dst; if (sx == 0 && dx == 0 && width * fontwidth(p) * 2 == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); return; @@ -64,7 +65,7 @@ void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx; dst = p->screen_base + dy * linesize + dx; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src += bytes; dst += bytes; } @@ -72,7 +73,7 @@ void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + (sy+height) * linesize + sx - bytes; dst = p->screen_base + (dy+height) * linesize + dx - bytes; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src -= bytes; dst -= bytes; } @@ -89,13 +90,13 @@ static inline void rectfill(u8 *dest, int width, int height, u32 data, while (height-- > 0) { u32 *p = (u32 *)dest; for (i = 0; i < width/4; i++) { - *p++ = data; - *p++ = data; + fb_writel(data, p++); + fb_writel(data, p++); } if (width & 2) - *p++ = data; + fb_writel(data, p++); if (width & 1) - *(u16 *)p = data; + fb_writew(data, (u16*)p); dest += linesize; } } @@ -139,11 +140,11 @@ void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c, int yy, cdat = p->fontdata + (c & p->charmask) * fontheight(p); for (rows = fontheight(p); rows--; dest += bytes) { bits = *cdat++; - ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+4); if (fontwidth(p) == 8) { - ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+12); } } break; @@ -152,16 +153,16 @@ void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c, int yy, cdat = p->fontdata + ((c & p->charmask) * fontheight(p) << 1); for (rows = fontheight(p); rows--; dest += bytes) { bits = *cdat++; - ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; - ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+4); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+12); bits = *cdat++; - ((u32 *)dest)[4] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[5] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest+16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+20); if (fontwidth(p) == 16) { - ((u32 *)dest)[6] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[7] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+24); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+28); } } break; @@ -191,11 +192,11 @@ void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + c * fontheight(p); for (rows = fontheight(p), dest = dest0; rows--; dest += bytes) { u8 bits = *cdat++; - ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+4); if (fontwidth(p) == 8) { - ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+12); } } dest0 += fontwidth(p)*2;; @@ -208,16 +209,16 @@ void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + (c * fontheight(p) << 1); for (rows = fontheight(p), dest = dest0; rows--; dest += bytes) { u8 bits = *cdat++; - ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; - ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+4); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+12); bits = *cdat++; - ((u32 *)dest)[4] = (tab_cfb16[bits >> 6] & eorx) ^ bgx; - ((u32 *)dest)[5] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest+16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest+20); if (fontwidth(p) == 16) { - ((u32 *)dest)[6] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx; - ((u32 *)dest)[7] = (tab_cfb16[bits & 3] & eorx) ^ bgx; + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest+24); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest+28); } } dest0 += fontwidth(p)*2; @@ -235,16 +236,20 @@ void fbcon_cfb16_revc(struct display *p, int xx, int yy) for (rows = fontheight(p); rows--; dest += bytes) { switch (fontwidth(p)) { case 16: - ((u32 *)dest)[6] ^= 0xffffffff; ((u32 *)dest)[7] ^= 0xffffffff; + fb_writel(fb_readl(dest+24) ^ 0xffffffff, dest+24); + fb_writel(fb_readl(dest+28) ^ 0xffffffff, dest+28); /* FALL THROUGH */ case 12: - ((u32 *)dest)[4] ^= 0xffffffff; ((u32 *)dest)[5] ^= 0xffffffff; + fb_writel(fb_readl(dest+16) ^ 0xffffffff, dest+16); + fb_writel(fb_readl(dest+20) ^ 0xffffffff, dest+20); /* FALL THROUGH */ case 8: - ((u32 *)dest)[2] ^= 0xffffffff; ((u32 *)dest)[3] ^= 0xffffffff; + fb_writel(fb_readl(dest+8) ^ 0xffffffff, dest+8); + fb_writel(fb_readl(dest+12) ^ 0xffffffff, dest+12); /* FALL THROUGH */ case 4: - ((u32 *)dest)[0] ^= 0xffffffff; ((u32 *)dest)[1] ^= 0xffffffff; + fb_writel(fb_readl(dest+0) ^ 0xffffffff, dest+0); + fb_writel(fb_readl(dest+4) ^ 0xffffffff, dest+4); } } } diff --git a/drivers/video/fbcon-cfb2.c b/drivers/video/fbcon-cfb2.c index 5b339a4fa..4326b87f8 100644 --- a/drivers/video/fbcon-cfb2.c +++ b/drivers/video/fbcon-cfb2.c @@ -61,7 +61,7 @@ void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src,*dst; if (sx == 0 && dx == 0 && width * 2 == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); } @@ -70,7 +70,7 @@ void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx * 2; dst = p->screen_base + dy * linesize + dx * 2; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width * 2); + fb_memmove(dst, src, width * 2); src += bytes; dst += bytes; } @@ -81,7 +81,7 @@ void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx, dst = p->screen_base + (dy+height) * linesize + dx * 2 - bytes; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width * 2); + fb_memmove(dst, src, width * 2); src -= bytes; dst -= bytes; } @@ -105,7 +105,7 @@ void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy, int sx, if (sx == 0 && width * 2 == bytes) { for (i = 0 ; i < lines * width ; i++) { - ((u16 *)dest)[0]=bgx; + fb_writew (bgx, dest); dest+=2; } } else { @@ -114,7 +114,7 @@ void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy, int sx, dest=dest0; for (i = 0 ; i < width ; i++) { /* memset ?? */ - ((u16 *)dest)[0]=bgx; + fb_writew (bgx, dest); dest+=2; } } @@ -140,10 +140,8 @@ void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c, int yy, eorx = fgx ^ bgx; for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u8 *)dest)[0]= - (nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx; - ((u8 *)dest)[1]= - (nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writeb((nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx, dest+0); + fb_writeb((nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx, dest+1); } } @@ -168,10 +166,8 @@ void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, const unsigned sh cdat = p->fontdata + c * fontheight(p); for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) { - ((u8 *)dest)[0]= - (nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx; - ((u8 *)dest)[1]= - (nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writeb((nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx, dest+0); + fb_writeb((nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx, dest+1); } dest0+=2; } @@ -184,7 +180,7 @@ void fbcon_cfb2_revc(struct display *p, int xx, int yy) dest = p->screen_base + yy * fontheight(p) * bytes + xx * 2; for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u16 *)dest)[0] ^= 0xffff; + fb_writew(fb_readw(dest) ^ 0xffff, dest); } } diff --git a/drivers/video/fbcon-cfb24.c b/drivers/video/fbcon-cfb24.c index 17e8ee08a..74ace660f 100644 --- a/drivers/video/fbcon-cfb24.c +++ b/drivers/video/fbcon-cfb24.c @@ -36,7 +36,7 @@ void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src, *dst; if (sx == 0 && dx == 0 && width * fontwidth(p) * 3 == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); return; @@ -55,7 +55,7 @@ void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx; dst = p->screen_base + dy * linesize + dx; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src += bytes; dst += bytes; } @@ -63,7 +63,7 @@ void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + (sy+height) * linesize + sx - bytes; dst = p->screen_base + (dy+height) * linesize + dx - bytes; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src -= bytes; dst -= bytes; } @@ -90,7 +90,11 @@ void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx, static inline void store4pixels(u32 d1, u32 d2, u32 d3, u32 d4, u32 *dest) { - convert4to3(d1, d2, d3, d4, *dest++, *dest++, *dest++); + u32 o1, o2, o3; + convert4to3(d1, d2, d3, d4, o1, o2, o3); + fb_writel (o1, dest++); + fb_writel (o2, dest++); + fb_writel (o3, dest); } static inline void rectfill(u8 *dest, int width, int height, u32 data, @@ -103,9 +107,9 @@ static inline void rectfill(u8 *dest, int width, int height, u32 data, while (height-- > 0) { u32 *p = (u32 *)dest; for (i = 0; i < width/4; i++) { - *p++ = d1; - *p++ = d2; - *p++ = d3; + fb_writel(d1, p++); + fb_writel(d2, p++); + fb_writel(d3, p++); } dest += linesize; } @@ -174,7 +178,7 @@ void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c, int yy, d2 = (-(bits >> 2 & 1) & eorx) ^ bgx; d3 = (-(bits >> 1 & 1) & eorx) ^ bgx; d4 = (-(bits & 1) & eorx) ^ bgx; - store4pixels(d1, d2, d3, d4, (u32 *)(dest+32)); + store4pixels(d1, d2, d3, d4, (u32 *)(dest+36)); } } @@ -225,7 +229,7 @@ void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, d2 = (-(bits >> 2 & 1) & eorx) ^ bgx; d3 = (-(bits >> 1 & 1) & eorx) ^ bgx; d4 = (-(bits & 1) & eorx) ^ bgx; - store4pixels(d1, d2, d3, d4, (u32 *)(dest+32)); + store4pixels(d1, d2, d3, d4, (u32 *)(dest+36)); } dest0 += fontwidth(p)*3; } @@ -240,17 +244,24 @@ void fbcon_cfb24_revc(struct display *p, int xx, int yy) for (rows = fontheight(p); rows--; dest += bytes) { switch (fontwidth(p)) { case 16: - ((u32 *)dest)[9] ^= 0xffffffff; ((u32 *)dest)[10] ^= 0xffffffff; - ((u32 *)dest)[11] ^= 0xffffffff; /* FALL THROUGH */ + fb_writel(fb_readl(dest+36) ^ 0xffffffff, dest+36); + fb_writel(fb_readl(dest+40) ^ 0xffffffff, dest+40); + fb_writel(fb_readl(dest+44) ^ 0xffffffff, dest+44); + /* FALL THROUGH */ case 12: - ((u32 *)dest)[6] ^= 0xffffffff; ((u32 *)dest)[7] ^= 0xffffffff; - ((u32 *)dest)[8] ^= 0xffffffff; /* FALL THROUGH */ + fb_writel(fb_readl(dest+24) ^ 0xffffffff, dest+24); + fb_writel(fb_readl(dest+28) ^ 0xffffffff, dest+28); + fb_writel(fb_readl(dest+32) ^ 0xffffffff, dest+32); + /* FALL THROUGH */ case 8: - ((u32 *)dest)[3] ^= 0xffffffff; ((u32 *)dest)[4] ^= 0xffffffff; - ((u32 *)dest)[5] ^= 0xffffffff; /* FALL THROUGH */ + fb_writel(fb_readl(dest+12) ^ 0xffffffff, dest+12); + fb_writel(fb_readl(dest+16) ^ 0xffffffff, dest+16); + fb_writel(fb_readl(dest+20) ^ 0xffffffff, dest+20); + /* FALL THROUGH */ case 4: - ((u32 *)dest)[0] ^= 0xffffffff; ((u32 *)dest)[1] ^= 0xffffffff; - ((u32 *)dest)[2] ^= 0xffffffff; + fb_writel(fb_readl(dest+0) ^ 0xffffffff, dest+0); + fb_writel(fb_readl(dest+4) ^ 0xffffffff, dest+4); + fb_writel(fb_readl(dest+8) ^ 0xffffffff, dest+8); } } } diff --git a/drivers/video/fbcon-cfb32.c b/drivers/video/fbcon-cfb32.c index b66ec2bad..bedd81c96 100644 --- a/drivers/video/fbcon-cfb32.c +++ b/drivers/video/fbcon-cfb32.c @@ -36,7 +36,7 @@ void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src, *dst; if (sx == 0 && dx == 0 && width * fontwidth(p) * 4 == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); return; @@ -54,7 +54,7 @@ void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx; dst = p->screen_base + dy * linesize + dx; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src += bytes; dst += bytes; } @@ -62,7 +62,7 @@ void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + (sy+height) * linesize + sx - bytes; dst = p->screen_base + (dy+height) * linesize + dx - bytes; for (rows = height * fontheight(p); rows--;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src -= bytes; dst -= bytes; } @@ -77,17 +77,17 @@ static inline void rectfill(u8 *dest, int width, int height, u32 data, while (height-- > 0) { u32 *p = (u32 *)dest; for (i = 0; i < width/4; i++) { - *p++ = data; - *p++ = data; - *p++ = data; - *p++ = data; + fb_writel(data, p++); + fb_writel(data, p++); + fb_writel(data, p++); + fb_writel(data, p++); } if (width & 2) { - *p++ = data; - *p++ = data; + fb_writel(data, p++); + fb_writel(data, p++); } if (width & 1) - *p++ = data; + fb_writel(data, p++); dest += linesize; } } @@ -115,7 +115,7 @@ void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c, int yy, { u8 *dest, *cdat, bits; int bytes = p->next_line, rows; - u32 eorx, fgx, bgx; + u32 eorx, fgx, bgx, *pt; dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4; if (fontwidth(p) <= 8) @@ -128,29 +128,30 @@ void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c, int yy, for (rows = fontheight(p); rows--; dest += bytes) { bits = *cdat++; - ((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx; - ((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx; - ((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx; - ((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx; + pt = (u32 *) dest; + fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 8) continue; - ((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx; - ((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx; - ((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx; - ((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 12) continue; bits = *cdat++; - ((u32 *)dest)[8] = (-(bits >> 7) & eorx) ^ bgx; - ((u32 *)dest)[9] = (-(bits >> 6 & 1) & eorx) ^ bgx; - ((u32 *)dest)[10] = (-(bits >> 5 & 1) & eorx) ^ bgx; - ((u32 *)dest)[11] = (-(bits >> 4 & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 16) continue; - ((u32 *)dest)[12] = (-(bits >> 3 & 1) & eorx) ^ bgx; - ((u32 *)dest)[13] = (-(bits >> 2 & 1) & eorx) ^ bgx; - ((u32 *)dest)[14] = (-(bits >> 1 & 1) & eorx) ^ bgx; - ((u32 *)dest)[15] = (-(bits & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits & 1) & eorx) ^ bgx, pt++); } } @@ -160,7 +161,7 @@ void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, u8 *cdat, *dest, *dest0, bits; u16 c; int rows, bytes = p->next_line; - u32 eorx, fgx, bgx; + u32 eorx, fgx, bgx, *pt; dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4; fgx = ((u32 *)p->dispsw_data)[attr_fgcol(p, scr_readw(s))]; @@ -174,29 +175,30 @@ void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + (c * fontheight(p) << 1); for (rows = fontheight(p), dest = dest0; rows--; dest += bytes) { bits = *cdat++; - ((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx; - ((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx; - ((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx; - ((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx; + pt = (u32 *) dest; + fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 8) continue; - ((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx; - ((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx; - ((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx; - ((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 12) continue; bits = *cdat++; - ((u32 *)dest)[8] = (-(bits >> 7) & eorx) ^ bgx; - ((u32 *)dest)[9] = (-(bits >> 6 & 1) & eorx) ^ bgx; - ((u32 *)dest)[10] = (-(bits >> 5 & 1) & eorx) ^ bgx; - ((u32 *)dest)[11] = (-(bits >> 4 & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++); if (fontwidth(p) < 16) continue; - ((u32 *)dest)[12] = (-(bits >> 3 & 1) & eorx) ^ bgx; - ((u32 *)dest)[13] = (-(bits >> 2 & 1) & eorx) ^ bgx; - ((u32 *)dest)[14] = (-(bits >> 1 & 1) & eorx) ^ bgx; - ((u32 *)dest)[15] = (-(bits & 1) & eorx) ^ bgx; + fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++); + fb_writel((-(bits & 1) & eorx) ^ bgx, pt++); } dest0 += fontwidth(p)*4; } @@ -211,20 +213,28 @@ void fbcon_cfb32_revc(struct display *p, int xx, int yy) for (rows = fontheight(p); rows--; dest += bytes) { switch (fontwidth(p)) { case 16: - ((u32 *)dest)[12] ^= 0xffffffff; ((u32 *)dest)[13] ^= 0xffffffff; - ((u32 *)dest)[14] ^= 0xffffffff; ((u32 *)dest)[15] ^= 0xffffffff; + fb_writel(fb_readl(dest+(4*12)) ^ 0xffffffff, dest+(4*12)); + fb_writel(fb_readl(dest+(4*13)) ^ 0xffffffff, dest+(4*13)); + fb_writel(fb_readl(dest+(4*14)) ^ 0xffffffff, dest+(4*14)); + fb_writel(fb_readl(dest+(4*15)) ^ 0xffffffff, dest+(4*15)); /* FALL THROUGH */ case 12: - ((u32 *)dest)[8] ^= 0xffffffff; ((u32 *)dest)[9] ^= 0xffffffff; - ((u32 *)dest)[10] ^= 0xffffffff; ((u32 *)dest)[11] ^= 0xffffffff; + fb_writel(fb_readl(dest+(4*8)) ^ 0xffffffff, dest+(4*8)); + fb_writel(fb_readl(dest+(4*9)) ^ 0xffffffff, dest+(4*9)); + fb_writel(fb_readl(dest+(4*10)) ^ 0xffffffff, dest+(4*10)); + fb_writel(fb_readl(dest+(4*11)) ^ 0xffffffff, dest+(4*11)); /* FALL THROUGH */ case 8: - ((u32 *)dest)[4] ^= 0xffffffff; ((u32 *)dest)[5] ^= 0xffffffff; - ((u32 *)dest)[6] ^= 0xffffffff; ((u32 *)dest)[7] ^= 0xffffffff; + fb_writel(fb_readl(dest+(4*4)) ^ 0xffffffff, dest+(4*4)); + fb_writel(fb_readl(dest+(4*5)) ^ 0xffffffff, dest+(4*5)); + fb_writel(fb_readl(dest+(4*6)) ^ 0xffffffff, dest+(4*6)); + fb_writel(fb_readl(dest+(4*7)) ^ 0xffffffff, dest+(4*7)); /* FALL THROUGH */ case 4: - ((u32 *)dest)[0] ^= 0xffffffff; ((u32 *)dest)[1] ^= 0xffffffff; - ((u32 *)dest)[2] ^= 0xffffffff; ((u32 *)dest)[3] ^= 0xffffffff; + fb_writel(fb_readl(dest+(4*0)) ^ 0xffffffff, dest+(4*0)); + fb_writel(fb_readl(dest+(4*1)) ^ 0xffffffff, dest+(4*1)); + fb_writel(fb_readl(dest+(4*2)) ^ 0xffffffff, dest+(4*2)); + fb_writel(fb_readl(dest+(4*3)) ^ 0xffffffff, dest+(4*3)); /* FALL THROUGH */ } } diff --git a/drivers/video/fbcon-cfb4.c b/drivers/video/fbcon-cfb4.c index 6248c28ee..a885fddfa 100644 --- a/drivers/video/fbcon-cfb4.c +++ b/drivers/video/fbcon-cfb4.c @@ -61,7 +61,7 @@ void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src,*dst; if (sx == 0 && dx == 0 && width * 4 == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); } @@ -70,7 +70,7 @@ void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx * 4; dst = p->screen_base + dy * linesize + dx * 4; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width * 4); + fb_memmove(dst, src, width * 4); src += bytes; dst += bytes; } @@ -81,7 +81,7 @@ void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx, dst = p->screen_base + (dy+height) * linesize + dx * 4 - bytes; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width * 4); + fb_memmove(dst, src, width * 4); src -= bytes; dst -= bytes; } @@ -107,7 +107,7 @@ void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy, int sx, if (sx == 0 && width * 4 == bytes) { for (i = 0 ; i < lines * width ; i++) { - ((u32 *)dest)[0]=bgx; + fb_writel (bgx, dest); dest+=4; } } else { @@ -116,7 +116,7 @@ void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy, int sx, dest=dest0; for (i = 0 ; i < width ; i++) { /* memset ?? */ - ((u32 *)dest)[0]=bgx; + fb_writel (bgx, dest); dest+=4; } } @@ -142,10 +142,8 @@ void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c, int yy, eorx = fgx ^ bgx; for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u16 *)dest)[0]= - (nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx; - ((u16 *)dest)[1]= - (nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writew((nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx, dest+0); + fb_writew((nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx, dest+2); } } @@ -172,10 +170,8 @@ void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + c * fontheight(p); for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) { - ((u16 *)dest)[0]= - (nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx; - ((u16 *)dest)[1]= - (nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writew((nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx, dest+0); + fb_writew((nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx, dest+2); } dest0+=4; } @@ -188,7 +184,7 @@ void fbcon_cfb4_revc(struct display *p, int xx, int yy) dest = p->screen_base + yy * fontheight(p) * bytes + xx * 4; for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u32 *)dest)[0] ^= 0xffffffff; + fb_writel(fb_readl(dest+0) ^ 0xffffffff, dest+0); } } diff --git a/drivers/video/fbcon-cfb8.c b/drivers/video/fbcon-cfb8.c index 0acf7c989..78e3d462f 100644 --- a/drivers/video/fbcon-cfb8.c +++ b/drivers/video/fbcon-cfb8.c @@ -52,7 +52,7 @@ void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx, u8 *src,*dst; if (sx == 0 && dx == 0 && width * fontwidth(p) == bytes) { - mymemmove(p->screen_base + dy * linesize, + fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, height * linesize); return; @@ -66,7 +66,7 @@ void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + sy * linesize + sx; dst = p->screen_base + dy * linesize + dx; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src += bytes; dst += bytes; } @@ -74,7 +74,7 @@ void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base + (sy+height) * linesize + sx - bytes; dst = p->screen_base + (dy+height) * linesize + dx - bytes; for (rows = height * fontheight(p) ; rows-- ;) { - mymemmove(dst, src, width); + fb_memmove(dst, src, width); src -= bytes; dst -= bytes; } @@ -85,7 +85,7 @@ static inline void rectfill(u8 *dest, int width, int height, u8 data, int linesize) { while (height-- > 0) { - memset(dest, data, width); + fb_memset(dest, data, width); dest += linesize; } } @@ -132,22 +132,22 @@ void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c, int yy, switch (fontwidth(p)) { case 4: for (rows = fontheight(p) ; rows-- ; dest += bytes) - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat++ >> 4] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat++ >> 4] & eorx) ^ bgx, dest); break; case 8: for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx, dest); + fb_writel((nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx, dest+4); } break; case 12: case 16: for (rows = fontheight(p) ; rows-- ; dest += bytes) { - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; - ((u32 *)dest)[2]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx, dest); + fb_writel((nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx, dest+4); + fb_writel((nibbletab_cfb8[(*cdat >> 4) & 0xf] & eorx) ^ bgx, dest+8); if (fontwidth(p) == 16) - ((u32 *)dest)[3]= (nibbletab_cfb8[*cdat & 0xf] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat & 0xf] & eorx) ^ bgx, dest+12); cdat++; } break; @@ -177,7 +177,7 @@ void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + c * fontheight(p); for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat++ >> 4] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat++ >> 4] & eorx) ^ bgx, dest); dest0+=4; } break; @@ -187,8 +187,8 @@ void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + c * fontheight(p); for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) { - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx, dest); + fb_writel((nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx, dest+4); } dest0+=8; } @@ -200,11 +200,11 @@ void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, cdat = p->fontdata + (c * fontheight(p) << 1); for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) { - ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx; - ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx; - ((u32 *)dest)[2]= (nibbletab_cfb8[(*cdat >> 4) & 0xf] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx, dest); + fb_writel((nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx, dest+4); + fb_writel((nibbletab_cfb8[(*cdat >> 4) & 0xf] & eorx) ^ bgx, dest+8); if (fontwidth(p) == 16) - ((u32 *)dest)[3]= (nibbletab_cfb8[*cdat & 0xf] & eorx) ^ bgx; + fb_writel((nibbletab_cfb8[*cdat & 0xf] & eorx) ^ bgx, dest+12); cdat++; } dest0+=fontwidth(p); @@ -221,10 +221,10 @@ void fbcon_cfb8_revc(struct display *p, int xx, int yy) dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); for (rows = fontheight(p) ; rows-- ; dest += bytes) { switch (fontwidth(p)) { - case 16: ((u32 *)dest)[3] ^= 0x0f0f0f0f; /* FALL THROUGH */ - case 12: ((u32 *)dest)[2] ^= 0x0f0f0f0f; /* FALL THROUGH */ - case 8: ((u32 *)dest)[1] ^= 0x0f0f0f0f; /* FALL THROUGH */ - case 4: ((u32 *)dest)[0] ^= 0x0f0f0f0f; /* FALL THROUGH */ + case 16: fb_writel(fb_readl(dest+12) ^ 0x0f0f0f0f, dest+12); /* fall thru */ + case 12: fb_writel(fb_readl(dest+8) ^ 0x0f0f0f0f, dest+8); /* fall thru */ + case 8: fb_writel(fb_readl(dest+4) ^ 0x0f0f0f0f, dest+4); /* fall thru */ + case 4: fb_writel(fb_readl(dest) ^ 0x0f0f0f0f, dest); /* fall thru */ default: break; } } diff --git a/drivers/video/fbcon-ilbm.c b/drivers/video/fbcon-ilbm.c index fbba519d9..d23c7e4b1 100644 --- a/drivers/video/fbcon-ilbm.c +++ b/drivers/video/fbcon-ilbm.c @@ -45,7 +45,7 @@ void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx, int height, int width) { if (sx == 0 && dx == 0 && width == p->next_plane) - mymemmove(p->screen_base+dy*fontheight(p)*p->next_line, + fb_memmove(p->screen_base+dy*fontheight(p)*p->next_line, p->screen_base+sy*fontheight(p)*p->next_line, height*fontheight(p)*p->next_line); else { @@ -56,7 +56,7 @@ void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base+sy*fontheight(p)*p->next_line+sx; dest = p->screen_base+dy*fontheight(p)*p->next_line+dx; for (i = p->var.bits_per_pixel*height*fontheight(p); i--;) { - mymemmove(dest, src, width); + fb_memmove(dest, src, width); src += p->next_plane; dest += p->next_plane; } @@ -66,7 +66,7 @@ void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx, for (i = p->var.bits_per_pixel*height*fontheight(p); i--;) { src -= p->next_plane; dest -= p->next_plane; - mymemmove(dest, src, width); + fb_memmove(dest, src, width); } } } @@ -86,9 +86,9 @@ void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy, int sx, bg = bg0; for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { if (bg & 1) - mymemset(dest, width); + fb_memset255(dest, width); else - mymemclear(dest, width); + fb_memclear(dest, width); bg >>= 1; } } diff --git a/drivers/video/fbcon-iplan2p2.c b/drivers/video/fbcon-iplan2p2.c index 0dfdc6107..8be581507 100644 --- a/drivers/video/fbcon-iplan2p2.c +++ b/drivers/video/fbcon-iplan2p2.c @@ -169,7 +169,7 @@ void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx, /* Special (but often used) case: Moving whole lines can be * done with memmove() */ - mymemmove(p->screen_base + dy * p->next_line * fontheight(p), + fb_memmove(p->screen_base + dy * p->next_line * fontheight(p), p->screen_base + sy * p->next_line * fontheight(p), p->next_line * height * fontheight(p)); } else { @@ -201,7 +201,7 @@ void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx, } if (width > 1) { for (rows = colsize; rows > 0; --rows) { - mymemmove(dst, src, (width>>1)*4); + fb_memmove(dst, src, (width>>1)*4); src += bytes; dst += bytes; } @@ -227,7 +227,7 @@ void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx, for(rows = colsize; rows > 0; --rows) { src -= bytes; dst -= bytes; - mymemmove(dst, src, (width>>1)*4); + fb_memmove(dst, src, (width>>1)*4); } } if (width & 1) @@ -295,7 +295,7 @@ void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p, int sy, /* Clears are split if the region starts at an odd column or * end at an even column. These extra columns are spread * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of + * cleared by normal fb_memclear_small(), because both bytes of * the single plane words are affected. */ diff --git a/drivers/video/fbcon-iplan2p4.c b/drivers/video/fbcon-iplan2p4.c index 805c9170b..8591783d9 100644 --- a/drivers/video/fbcon-iplan2p4.c +++ b/drivers/video/fbcon-iplan2p4.c @@ -177,7 +177,7 @@ void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, int dx, /* Special (but often used) case: Moving whole lines can be *done with memmove() */ - mymemmove(p->screen_base + dy * p->next_line * fontheight(p), + fb_memmove(p->screen_base + dy * p->next_line * fontheight(p), p->screen_base + sy * p->next_line * fontheight(p), p->next_line * height * fontheight(p)); } else { @@ -210,7 +210,7 @@ void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, int dx, } if (width > 1) { for(rows = colsize; rows > 0; --rows) { - mymemmove(dst, src, (width>>1)*8); + fb_memmove(dst, src, (width>>1)*8); src += bytes; dst += bytes; } @@ -236,7 +236,7 @@ void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, int dx, for(rows = colsize; rows > 0; --rows) { src -= bytes; dst -= bytes; - mymemmove(dst, src, (width>>1)*8); + fb_memmove(dst, src, (width>>1)*8); } } if (width & 1) { @@ -305,7 +305,7 @@ void fbcon_iplan2p4_clear(struct vc_data *conp, struct display *p, int sy, /* Clears are split if the region starts at an odd column or * end at an even column. These extra columns are spread * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of + * cleared by normal fb_memclear_small(), because both bytes of * the single plane words are affected. */ diff --git a/drivers/video/fbcon-iplan2p8.c b/drivers/video/fbcon-iplan2p8.c index 411dc5ae9..4ee366019 100644 --- a/drivers/video/fbcon-iplan2p8.c +++ b/drivers/video/fbcon-iplan2p8.c @@ -337,7 +337,7 @@ void fbcon_iplan2p8_clear(struct vc_data *conp, struct display *p, int sy, /* Clears are split if the region starts at an odd column or * end at an even column. These extra columns are spread * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of + * cleared by normal fb_memclear_small(), because both bytes of * the single plane words are affected. */ diff --git a/drivers/video/fbcon-mac.c b/drivers/video/fbcon-mac.c index 2c6486c41..58deaff54 100644 --- a/drivers/video/fbcon-mac.c +++ b/drivers/video/fbcon-mac.c @@ -62,7 +62,7 @@ void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx, if( sx == 0 && width == p->conp->vc_cols) { s = height * fontheight(p) * p->next_line; - mymemmove(dest, src, s); + fb_memmove(dest, src, s); return; } @@ -158,7 +158,7 @@ void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx, plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i); if (j < r) { - mymemmove(dest, src, s); + fb_memmove(dest, src, s); if (move_up) { dest += p->next_line; src += p->next_line; @@ -202,9 +202,9 @@ void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, int sx, if( sx == 0 && width == p->conp->vc_cols) { s = height * fontheight(p) * p->next_line; if (inverse) - mymemclear(dest, s); + fb_memclear(dest, s); else - mymemset(dest, s); + fb_memset255(dest, s); } l = sx * fontwidth(p); @@ -251,9 +251,9 @@ void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, int sx, if (j < r) { if (PIXEL_WHITE_MAC == pixel) - mymemclear(dest, s); + fb_memclear(dest, s); else - mymemset(dest, s); + fb_memset255(dest, s); dest += p->next_line; j += w; } diff --git a/drivers/video/fbcon-mfb.c b/drivers/video/fbcon-mfb.c index 76caf5cc9..9aa3528dd 100644 --- a/drivers/video/fbcon-mfb.c +++ b/drivers/video/fbcon-mfb.c @@ -41,12 +41,12 @@ void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx, if (sx == 0 && dx == 0 && width == p->next_line) { src = p->screen_base+sy*fontheight(p)*width; dest = p->screen_base+dy*fontheight(p)*width; - mymemmove(dest, src, height*fontheight(p)*width); + fb_memmove(dest, src, height*fontheight(p)*width); } else if (dy <= sy) { src = p->screen_base+sy*fontheight(p)*p->next_line+sx; dest = p->screen_base+dy*fontheight(p)*p->next_line+dx; for (rows = height*fontheight(p); rows--;) { - mymemmove(dest, src, width); + fb_memmove(dest, src, width); src += p->next_line; dest += p->next_line; } @@ -54,7 +54,7 @@ void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx, src = p->screen_base+((sy+height)*fontheight(p)-1)*p->next_line+sx; dest = p->screen_base+((dy+height)*fontheight(p)-1)*p->next_line+dx; for (rows = height*fontheight(p); rows--;) { - mymemmove(dest, src, width); + fb_memmove(dest, src, width); src -= p->next_line; dest -= p->next_line; } @@ -72,15 +72,15 @@ void fbcon_mfb_clear(struct vc_data *conp, struct display *p, int sy, int sx, if (sx == 0 && width == p->next_line) { if (inverse) - mymemset(dest, height*fontheight(p)*width); + fb_memset255(dest, height*fontheight(p)*width); else - mymemclear(dest, height*fontheight(p)*width); + fb_memclear(dest, height*fontheight(p)*width); } else for (rows = height*fontheight(p); rows--; dest += p->next_line) if (inverse) - mymemset(dest, width); + fb_memset255(dest, width); else - mymemclear_small(dest, width); + fb_memclear_small(dest, width); } void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c, int yy, @@ -104,7 +104,7 @@ void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c, int yy, d |= d>>1; if (revs) d = ~d; - *dest = d; + fb_writeb (d, dest); } } @@ -133,19 +133,21 @@ void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, d |= d>>1; if (revs) d = ~d; - *dest = d; + fb_writeb (d, dest); } } } void fbcon_mfb_revc(struct display *p, int xx, int yy) { - u8 *dest; + u8 *dest, d; u_int rows; dest = p->screen_base+yy*fontheight(p)*p->next_line+xx; - for (rows = fontheight(p); rows--; dest += p->next_line) - *dest = ~*dest; + for (rows = fontheight(p); rows--; dest += p->next_line) { + d = fb_readb(dest); + fb_writeb (~d, dest); + } } void fbcon_mfb_clear_margins(struct vc_data *conp, struct display *p, @@ -165,9 +167,9 @@ void fbcon_mfb_clear_margins(struct vc_data *conp, struct display *p, bottom -= p->vrows; dest = p->screen_base + bottom * fontheight(p) * p->next_line; if (inverse) - mymemset(dest, height * p->next_line); + fb_memset255(dest, height * p->next_line); else - mymemclear(dest, height * p->next_line); + fb_memclear(dest, height * p->next_line); } diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c index b54d0b5a4..77336e223 100644 --- a/drivers/video/fbcon.c +++ b/drivers/video/fbcon.c @@ -526,7 +526,7 @@ static void fbcon_setup(int con, int init, int logo) q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows); step = logo_lines * old_cols; for (r = q - logo_lines * old_cols; r < q; r++) - if (*r != conp->vc_video_erase_char) + if (scr_readw(r) != conp->vc_video_erase_char) break; if (r != q && nr_rows >= old_rows + logo_lines) { save = kmalloc(logo_lines * nr_cols * 2, GFP_KERNEL); @@ -535,7 +535,7 @@ static void fbcon_setup(int con, int init, int logo) scr_memsetw(save, conp->vc_video_erase_char, logo_lines * nr_cols * 2); r = q - step; for (cnt = 0; cnt < logo_lines; cnt++, r += i) - scr_memcpyw_to(save + cnt * nr_cols, r, 2 * i); + scr_memcpyw_from(save + cnt * nr_cols, r, 2 * i); r = q; } } @@ -551,7 +551,8 @@ static void fbcon_setup(int con, int init, int logo) conp->vc_pos += logo_lines * conp->vc_size_row; } } - scr_memsetw((unsigned short *)conp->vc_origin, conp->vc_video_erase_char, + scr_memsetw((unsigned short *)conp->vc_origin, + conp->vc_video_erase_char, conp->vc_size_row * logo_lines); } @@ -603,7 +604,7 @@ static void fbcon_setup(int con, int init, int logo) } if (save) { q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows); - scr_memcpyw_from(q, save, logo_lines * nr_cols * 2); + memcpy(q, save, logo_lines * nr_cols * 2); conp->vc_y += logo_lines; conp->vc_pos += logo_lines * conp->vc_size_row; kfree(save); @@ -1386,19 +1387,11 @@ static int fbcon_blank(struct vc_data *conp, int blank) if (!p->can_soft_blank) { if (blank) { -#ifdef CONFIG_MAC - if (MACH_IS_MAC) { - if (p->screen_base) - mymemset(p->screen_base, - p->var.xres_virtual*p->var.yres_virtual* - p->var.bits_per_pixel>>3); - } else -#endif if (p->visual == FB_VISUAL_MONO01) { if (p->screen_base) - mymemset(p->screen_base, - p->var.xres_virtual*p->var.yres_virtual* - p->var.bits_per_pixel>>3); + fb_memset255(p->screen_base, + p->var.xres_virtual*p->var.yres_virtual* + p->var.bits_per_pixel>>3); } else { unsigned short oldc; u_int height; @@ -2048,7 +2041,7 @@ static int __init fbcon_show_logo( void ) (*src << blueshift); if (bdepth == 4 && !((long)dst & 3)) { /* Some cards require 32bit access */ - *(u32 *)dst = val; + fb_writel (val, dst); dst += 4; } else { #ifdef __LITTLE_ENDIAN @@ -2056,7 +2049,7 @@ static int __init fbcon_show_logo( void ) #else for( i = bdepth-1; i >= 0; --i ) #endif - *dst++ = val >> (i*8); + fb_writeb (val >> (i*8), dst++); } } } @@ -2078,7 +2071,7 @@ static int __init fbcon_show_logo( void ) #else for( i = bdepth-1; i >= 0; --i ) #endif - *dst++ = val >> (i*8); + fb_writeb (val >> (i*8), dst++); pix = (*src & 0x0f) | 0x10; /* lower nibble */ val = (pix << redshift) | (pix << greenshift) | @@ -2088,7 +2081,7 @@ static int __init fbcon_show_logo( void ) #else for( i = bdepth-1; i >= 0; --i ) #endif - *dst++ = val >> (i*8); + fb_writeb (val >> (i*8), dst++); } } } @@ -2122,7 +2115,7 @@ static int __init fbcon_show_logo( void ) safe_shift((linux_logo_blue[*src-32] & bluemask), blueshift); if (bdepth == 4 && !((long)dst & 3)) { /* Some cards require 32bit access */ - *(u32 *)dst = val; + fb_writel (val, dst); dst += 4; } else { #ifdef __LITTLE_ENDIAN @@ -2130,7 +2123,7 @@ static int __init fbcon_show_logo( void ) #else for( i = bdepth-1; i >= 0; --i ) #endif - *dst++ = val >> (i*8); + fb_writeb (val >> (i*8), dst++); } } } @@ -2145,7 +2138,7 @@ static int __init fbcon_show_logo( void ) for( x1 = 0; x1 < LOGO_W/2; x1++) { u8 q = *src++; q = (q << 4) | (q >> 4); - *dst++ = q; + fb_writeb (q, dst++); } } done = 1; @@ -2159,7 +2152,7 @@ static int __init fbcon_show_logo( void ) for( y1 = 0; y1 < LOGO_H; y1++ ) { dst = fb + y1*line + x; for( x1 = 0; x1 < LOGO_W; x1++ ) - *dst++ = *src++; + fb_writeb (*src++, dst++); } done = 1; } @@ -2228,14 +2221,15 @@ static int __init fbcon_show_logo( void ) p->type == FB_TYPE_INTERLEAVED_PLANES)) { /* monochrome */ - unsigned char inverse = p->inverse ? 0x00 : 0xff; + unsigned char inverse = p->inverse || p->visual == FB_VISUAL_MONO01 + ? 0x00 : 0xff; /* can't use simply memcpy because need to apply inverse */ for( y1 = 0; y1 < LOGO_H; y1++ ) { - src = logo + y1*LOGO_LINE + x/8; - dst = fb + y1*line; + src = logo + y1*LOGO_LINE; + dst = fb + y1*line + x/8; for( x1 = 0; x1 < LOGO_LINE; ++x1 ) - *dst++ = *src++ ^ inverse; + fb_writeb(fb_readb(src++) ^ inverse, dst++); } done = 1; } @@ -2255,13 +2249,15 @@ static int __init fbcon_show_logo( void ) outb_p(*src >> 4,0x3cf); outb_p(8,0x3ce); outb_p(1 << (7 - x1 % 4 * 2),0x3cf); - *(volatile char *) dst |= 1; + fb_readb (dst); + fb_writeb (0, dst); outb_p(0,0x3ce); outb_p(*src & 0xf,0x3cf); outb_p(8,0x3ce); outb_p(1 << (7 - (1 + x1 % 4 * 2)),0x3cf); - *(volatile char *) dst |= 1; + fb_readb (dst); + fb_writeb (0, dst); src++; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a02920395..24504b17a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -100,12 +100,17 @@ extern int fm2fb_setup(char*); extern int q40fb_init(void); extern int sgivwfb_init(void); extern int sgivwfb_setup(char*); +extern int tdfxfb_init(void); +extern int tdfxfb_setup(char*); static struct { const char *name; int (*init)(void); int (*setup)(char*); } fb_drivers[] __initdata = { +#ifdef CONFIG_FB_3DFX + { "tdfx", tdfxfb_init, tdfxfb_setup }, +#endif #ifdef CONFIG_FB_SGIVW { "sgivw", sgivwfb_init, sgivwfb_setup }, #endif @@ -454,6 +459,11 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, for (i = 0; i < MAX_NR_CONSOLES; i++) set_con2fb_map(i, con2fb.framebuffer); return 0; + case FBIOBLANK: + if (info->blank == 0) + return -EINVAL; + (*info->blank)(arg, info); + return 0; default: return fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(info), info); diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index 398e8e500..d020ed752 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -354,9 +354,12 @@ struct fb_info_imstt { } palette[256]; struct imstt_regvals init; struct imstt_cursor cursor; - __u8 *frame_buffer_phys, *frame_buffer; - __u32 *dc_regs_phys, *dc_regs; - __u8 *cmap_regs_phys, *cmap_regs; + unsigned long frame_buffer_phys; + __u8 *frame_buffer; + unsigned long dc_regs_phys; + __u32 *dc_regs; + unsigned long cmap_regs_phys; + __u8 *cmap_regs; __u32 total_vram; __u32 ramdac; }; @@ -1077,7 +1080,7 @@ imsttfbcon_clear (struct vc_data *conp, struct display *disp, out_le32(&p->dc_regs[BI], 0xffffffff); out_le32(&p->dc_regs[MBC], 0xffffffff); out_le32(&p->dc_regs[CLR], bgc); - out_le32(&p->dc_regs[BLTCTL], 0x200000); + out_le32(&p->dc_regs[BLTCTL], 0x840); /* 0x200000 */ while(in_le32(&p->dc_regs[SSTATUS]) & 0x80); while(in_le32(&p->dc_regs[SSTATUS]) & 0x40); } @@ -1854,10 +1857,10 @@ init_imstt(struct fb_info_imstt *p) fb_info_imstt_p[i] = p; #ifdef CONFIG_FB_COMPAT_XPMAC strncpy(display_info.name, "IMS,tt128mb", sizeof(display_info.name)); - display_info.fb_address = (__u32)p->frame_buffer_phys; - display_info.cmap_adr_address = (__u32)&p->cmap_regs_phys[PADDRW]; - display_info.cmap_data_address = (__u32)&p->cmap_regs_phys[PDATA]; - display_info.disp_reg_address = (__u32)p->dc_regs_phys; + display_info.fb_address = p->frame_buffer_phys; + display_info.cmap_adr_address = p->cmap_regs_phys + PADDRW; + display_info.cmap_data_address = p->cmap_regs_phys + PDATA; + display_info.disp_reg_address = p->dc_regs_phys; if (!console_fb_info) console_fb_info = &p->info; #endif /* CONFIG_FB_COMPAT_XPMAC */ @@ -1897,11 +1900,11 @@ imsttfb_of_init(struct device_node *dp) else p->ramdac = IBM; - p->frame_buffer_phys = (__u8 *)addr; + p->frame_buffer_phys = addr; p->frame_buffer = (__u8 *)ioremap(addr, p->ramdac == IBM ? 0x400000 : 0x800000); - p->dc_regs_phys = (__u32 *)(addr + 0x800000); + p->dc_regs_phys = addr + 0x800000; p->dc_regs = (__u32 *)ioremap(addr + 0x800000, 0x1000); - p->cmap_regs_phys = (__u8 *)(addr + 0x840000); + p->cmap_regs_phys = addr + 0x840000; p->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000); init_imstt(p); @@ -1953,11 +1956,11 @@ imsttfb_init(void) break; } - p->frame_buffer_phys = (__u8 *)addr; + p->frame_buffer_phys = addr; p->frame_buffer = (__u8 *)ioremap(addr, p->ramdac == IBM ? 0x400000 : 0x800000); - p->dc_regs_phys = (__u32 *)(addr + 0x800000); + p->dc_regs_phys = addr + 0x800000; p->dc_regs = (__u32 *)ioremap(addr + 0x800000, 0x1000); - p->cmap_regs_phys = (__u8 *)(addr + 0x840000); + p->cmap_regs_phys = addr + 0x840000; p->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000); init_imstt(p); diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index af58b457a..404c646f5 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -14,7 +14,7 @@ #include <linux/sched.h> -#define DEBUG +#undef DEBUG #define name_matches(v, s, l) \ ((v).name && !strncmp((s), (v).name, (l)) && strlen((v).name) == (l)) diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index cf7fe3418..12d067b79 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -10,7 +10,7 @@ * * Hardware information from: * platinum.c: Console support for PowerMac "platinum" display adaptor. - * Copyright (C) 1996 Paul Mackerras + * Copyright (C) 1996 Paul Mackerras and Mark Abene * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -39,7 +39,6 @@ #include <asm/io.h> #include <asm/prom.h> #include <asm/pgtable.h> -#include <asm/adb.h> #include <video/fbcon.h> #include <video/fbcon-cfb8.h> @@ -66,6 +65,7 @@ struct fb_par_platinum { struct fb_info_platinum { struct fb_info fb_info; struct display disp; + struct display_switch dispsw; struct fb_par_platinum default_par; struct fb_par_platinum current_par; @@ -145,6 +145,8 @@ static int platinum_var_to_par(const struct fb_var_screeninfo *var, static int platinum_encode_fix(struct fb_fix_screeninfo *fix, const struct fb_par_platinum *par, const struct fb_info_platinum *info); +static void platinum_set_disp(struct display *disp, struct fb_info_platinum *info, + int cmode, int accel); static int platinum_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *transp, struct fb_info *fb); static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue, @@ -175,10 +177,6 @@ static struct fb_ops platinumfb_ops = { platinum_ioctl }; - -__openfirmware - - static int platinum_open(struct fb_info *info, int user) { MOD_INC_USE_COUNT; @@ -219,6 +217,36 @@ static int platinum_get_var(struct fb_var_screeninfo *var, int con, return 0; } +static void platinum_set_disp(struct display *disp, struct fb_info_platinum *info, + int cmode, int accel) +{ + switch(cmode) { +#ifdef FBCON_HAS_CFB8 + case CMODE_8: + info->dispsw = fbcon_cfb8; + disp->dispsw = &info->dispsw; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case CMODE_16: + info->dispsw = fbcon_cfb16; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case CMODE_32: + info->dispsw = fbcon_cfb32; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb32; + break; +#endif + default: + disp->dispsw = &fbcon_dummy; + break; + } +} + static int platinum_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb) { @@ -227,6 +255,7 @@ static int platinum_set_var(struct fb_var_screeninfo *var, int con, struct display *display; int oldxres, oldyres, oldvxres, oldvyres, oldbpp, err; int activate = var->activate; + struct platinum_regvals *init; display = (con >= 0) ? &fb_display[con] : fb->disp; @@ -242,6 +271,8 @@ static int platinum_set_var(struct fb_var_screeninfo *var, int con, return 0; } + init = platinum_reg_init[par.vmode-1]; + oldxres = display->var.xres; oldyres = display->var.yres; oldvxres = display->var.xres_virtual; @@ -255,7 +286,7 @@ static int platinum_set_var(struct fb_var_screeninfo *var, int con, struct fb_fix_screeninfo fix; platinum_encode_fix(&fix, &par, info); - display->screen_base = (char *) info->frame_buffer + 0x1000; + display->screen_base = (char *) info->frame_buffer + init->fb_offset + 0x20; display->visual = fix.visual; display->type = fix.type; display->type_aux = fix.type_aux; @@ -264,36 +295,14 @@ static int platinum_set_var(struct fb_var_screeninfo *var, int con, display->line_length = fix.line_length; display->can_soft_blank = 1; display->inverse = 0; - - switch(par.cmode) { -#ifdef FBCON_HAS_CFB8 - case CMODE_8: - display->dispsw = &fbcon_cfb8; - break; -#endif -#ifdef FBCON_HAS_CFB16 - case CMODE_16: - display->dispsw = &fbcon_cfb16; - display->dispsw_data = info->fbcon_cmap.cfb16; - break; -#endif -#ifdef FBCON_HAS_CFB32 - case CMODE_32: - display->dispsw = &fbcon_cfb32; - display->dispsw_data = info->fbcon_cmap.cfb32; - break; -#endif - default: - display->dispsw = &fbcon_dummy; - break; - } - + platinum_set_disp(display, info, par.cmode, 0); display->scrollmode = SCROLL_YREDRAW; if (info->fb_info.changevar) (*info->fb_info.changevar)(con); } - if (con == currcon) + if (!info->fb_info.display_fg || + info->fb_info.display_fg->vc_num == con) platinum_set_par(&par, info); if (oldbpp != var->bits_per_pixel) { @@ -322,7 +331,8 @@ static int platinum_pan_display(struct fb_var_screeninfo *var, int con, static int platinum_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { - if (con == currcon) /* current console? */ + if (!info->display_fg || + info->display_fg->vc_num == con) /* current console? */ return fb_get_cmap(cmap, kspc, platinum_getcolreg, info); if (fb_display[con].cmap.len) /* non default colormap? */ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); @@ -337,17 +347,23 @@ static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info) { int err; + struct display *disp; - if (!fb_display[con].cmap.len) { - int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256; - err = fb_alloc_cmap(&fb_display[con].cmap, size, 0); - if (err) + if (con >= 0) + disp = &fb_display[con]; + else + disp = info->disp; + if (!disp->cmap.len) { /* no colormap allocated? */ + int size = disp->var.bits_per_pixel == 16 ? 32 : 256; + if ((err = fb_alloc_cmap(&disp->cmap, size, 0))) return err; } - if (con == currcon) + if (!info->display_fg || + info->display_fg->vc_num == con) /* current console? */ return fb_set_cmap(cmap, kspc, platinum_setcolreg, info); - fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + else + fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1); return 0; } @@ -368,8 +384,9 @@ static int platinum_switch(int con, struct fb_info *fb) fb); currcon = con; - platinum_var_to_par(&fb_display[currcon].var, &par, info); + platinum_var_to_par(&fb_display[con].var, &par, info); platinum_set_par(&par, info); + platinum_set_disp(&fb_display[con], info, par.cmode, 0); do_install_cmap(con, fb); return 1; @@ -431,7 +448,6 @@ static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue, { struct fb_info_platinum *info = (struct fb_info_platinum *) fb; volatile struct cmap_regs *cmap_regs = info->cmap_regs; - int scale; if (regno > 255) return 1; @@ -444,12 +460,10 @@ static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue, info->palette[regno].green = green; info->palette[regno].blue = blue; - scale = (info->current_par.cmode == CMODE_16) ? 3 : 0; - out_8(&cmap_regs->addr, regno); /* tell clut what addr to fill */ - out_8(&cmap_regs->lut, red<<scale); /* send one color channel at */ - out_8(&cmap_regs->lut, green<<scale); /* a time... */ - out_8(&cmap_regs->lut, blue<<scale); + out_8(&cmap_regs->lut, red); /* send one color channel at */ + out_8(&cmap_regs->lut, green); /* a time... */ + out_8(&cmap_regs->lut, blue); if(regno < 16) { #ifdef FBCON_HAS_CFB16 @@ -538,7 +552,7 @@ static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_p out_be32(&platinum_regs->reg[26+32].r, (info->total_vram == 0x100000 ? init->offset[cmode] + 4 - cmode : init->offset[cmode])); - out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys + 0x1000 - 0x10); + out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys+init->fb_offset+0x10); out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]); out_be32(&platinum_regs->reg[19].r, (info->total_vram == 0x100000 ? init->mode[cmode+1] : @@ -571,7 +585,7 @@ static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_p display_info.mode = vmode; strncpy(display_info.name, "platinum", sizeof(display_info.name)); - display_info.fb_address = info->frame_buffer_phys + 0x1000; + display_info.fb_address = info->frame_buffer_phys + init->fb_offset + 0x20; display_info.cmap_adr_address = info->cmap_regs_phys; display_info.cmap_data_address = info->cmap_regs_phys + 0x30; display_info.disp_reg_address = info->platinum_regs_phys; @@ -665,7 +679,7 @@ int __init platinum_init(void) #ifdef __powerpc__ #define invalidate_cache(addr) \ - asm volatile("eieio; dcbi 0,%1" \ + asm volatile("eieio; dcbf 0,%1" \ : "=m" (*(addr)) : "r" (addr) : "memory"); #else #define invalidate_cache(addr) @@ -675,6 +689,7 @@ void __init platinum_of_init(struct device_node *dp) { struct fb_info_platinum *info; unsigned long addr, size; + volatile __u8 *fbuffer; int i, bank0, bank1, bank2, bank3; if(dp->n_addrs != 2) { @@ -711,19 +726,20 @@ void __init platinum_of_init(struct device_node *dp) out_be32(&info->platinum_regs->reg[20].r, 0x1011); /* select max vram */ out_be32(&info->platinum_regs->reg[24].r, 0); /* switch in vram */ - info->base_frame_buffer[0x100000] = 0x34; - info->base_frame_buffer[0x100008] = 0x0; - invalidate_cache(&info->base_frame_buffer[0x100000]); - info->base_frame_buffer[0x200000] = 0x56; - info->base_frame_buffer[0x200008] = 0x0; - invalidate_cache(&info->base_frame_buffer[0x200000]); - info->base_frame_buffer[0x300000] = 0x78; - info->base_frame_buffer[0x300008] = 0x0; - invalidate_cache(&info->base_frame_buffer[0x300000]); + fbuffer = info->base_frame_buffer; + fbuffer[0x100000] = 0x34; + fbuffer[0x100008] = 0x0; + invalidate_cache(&fbuffer[0x100000]); + fbuffer[0x200000] = 0x56; + fbuffer[0x200008] = 0x0; + invalidate_cache(&fbuffer[0x200000]); + fbuffer[0x300000] = 0x78; + fbuffer[0x300008] = 0x0; + invalidate_cache(&fbuffer[0x300000]); bank0 = 1; /* builtin 1MB vram, always there */ - bank1 = info->base_frame_buffer[0x100000] == 0x34; - bank2 = info->base_frame_buffer[0x200000] == 0x56; - bank3 = info->base_frame_buffer[0x300000] == 0x78; + bank1 = fbuffer[0x100000] == 0x34; + bank2 = fbuffer[0x200000] == 0x56; + bank3 = fbuffer[0x300000] == 0x78; info->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000; printk(KERN_INFO "Total VRAM = %dMB %d%d%d%d\n", (int) (info->total_vram / 1024 / 1024), bank3, bank2, bank1, bank0); @@ -835,10 +851,14 @@ static int platinum_encode_fix(struct fb_fix_screeninfo *fix, const struct fb_par_platinum *par, const struct fb_info_platinum *info) { + struct platinum_regvals *init; + + init = platinum_reg_init[par->vmode-1]; + memset(fix, 0, sizeof(*fix)); strcpy(fix->id, "platinum"); - fix->smem_start = (info->frame_buffer_phys + 0x1000); - fix->smem_len = (u32) info->total_vram - 0x1000; + fix->smem_start = (info->frame_buffer_phys) + init->fb_offset + 0x20; + fix->smem_len = (u32) info->total_vram; fix->mmio_start = (info->platinum_regs_phys); fix->mmio_len = 0x1000; fix->type = FB_TYPE_PACKED_PIXELS; @@ -884,6 +904,7 @@ int __init platinum_setup(char *options) } else if (!strncmp(this_opt, "cmode:", 6)) { int depth = simple_strtoul(this_opt+6, NULL, 0); switch (depth) { + case 0: case 8: default_cmode = CMODE_8; break; diff --git a/drivers/video/sbusfb.c b/drivers/video/sbusfb.c index aafa04542..1a3b02e64 100644 --- a/drivers/video/sbusfb.c +++ b/drivers/video/sbusfb.c @@ -304,13 +304,13 @@ static void sbusfb_clear_margin(struct display *p, int s) fb_base -= (skip_bytes + fb->x_margin) / 8; skip_bytes /= 8; scr_size /= 8; - mymemset (fb_base, skip_bytes - fb->x_margin / 8); - mymemset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8); + fb_memset255 (fb_base, skip_bytes - fb->x_margin / 8); + fb_memset255 (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8); incr = fb->var.xres_virtual / 8; size = fb->x_margin / 8 * 2; for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0; h <= he; q += incr, h++) - mymemset (q, size); + fb_memset255 (q, size); } else { fb_base -= (skip_bytes + fb->x_margin); memset (fb_base, attr_bgcol(p,s), skip_bytes - fb->x_margin); diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c new file mode 100644 index 000000000..30f9111ed --- /dev/null +++ b/drivers/video/tdfxfb.c @@ -0,0 +1,1891 @@ +/* + * + * tdfxfb.c + * + * Author: Hannu Mallat <hmallat@cc.hut.fi> + * + * Copyright © 1999 Hannu Mallat + * All rights reserved + * + * Created : Thu Sep 23 18:17:43 1999, hmallat + * Last modified: Thu Oct 7 18:39:04 1999, hmallat + * + * Lots of the information here comes from the Daryll Strauss' Banshee + * patches to the XF86 server, and the rest comes from the 3dfx + * Banshee specification. I'm very much indebted to Daryll for his + * work on the X server. + * + * Voodoo3 support was contributed Harold Oga. Thanks! + * + * While I _am_ grateful to 3Dfx for releasing the specs for Banshee, + * I do wish the next version is a bit more complete. Without the XF86 + * patches I couldn't have gotten even this far... for instance, the + * extensions to the VGA register set go completely unmentioned in the + * spec! Also, lots of references are made to the 'SST core', but no + * spec is publicly available, AFAIK. + * + * The structure of this driver comes pretty much from the Permedia + * driver by Ilario Nardinocchi, which in turn is based on skeletonfb. + * + * TODO: + * - support for 16/32 bpp needs fixing (funky bootup penguin) + * - multihead support (it's all hosed now with pokes to VGA standard + * register locations, but shouldn't be that hard to change, some + * other code needs to be changed too where the fb_info (which should + * be an array of head-specific information) is referred to directly. + * are referred to ) + * - hw cursor + * - better acceleration support (e.g., font blitting from fb memory?) + * - banshee and voodoo3 now supported -- any others? afaik, the original + * voodoo was a 3d-only card, so we won't consider that. what about + * voodoo2? + * - 24bpp + * - panning (doesn't seem to work properly yet) + * + * Version history: + * + * 0.1.1 (released 1999-10-07) added Voodoo3 support by Harold Oga. + * 0.1.0 (released 1999-10-06) initial version + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/selection.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/nvram.h> +#include <linux/kd.h> +#include <linux/vt_kern.h> +#include <asm/io.h> + +#include <video/fbcon.h> +#include <video/fbcon-cfb8.h> +#include <video/fbcon-cfb16.h> +#include <video/fbcon-cfb32.h> + +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 +#endif + +/* membase0 register offsets */ +#define STATUS 0x00 +#define PCIINIT0 0x04 +#define SIPMONITOR 0x08 +#define LFBMEMORYCONFIG 0x0c +#define MISCINIT0 0x10 +#define MISCINIT1 0x14 +#define DRAMINIT0 0x18 +#define DRAMINIT1 0x1c +#define AGPINIT 0x20 +#define TMUGBEINIT 0x24 +#define VGAINIT0 0x28 +#define VGAINIT1 0x2c +#define DRAMCOMMAND 0x30 +#define DRAMDATA 0x34 +/* reserved 0x38 */ +/* reserved 0x3c */ +#define PLLCTRL0 0x40 +#define PLLCTRL1 0x44 +#define PLLCTRL2 0x48 +#define DACMODE 0x4c +#define DACADDR 0x50 +#define DACDATA 0x54 +#define RGBMAXDELTA 0x58 +#define VIDPROCCFG 0x5c +#define HWCURPATADDR 0x60 +#define HWCURLOC 0x64 +#define HWCURC0 0x68 +#define HWCURC1 0x6c +#define VIDINFORMAT 0x70 +#define VIDINSTATUS 0x74 +#define VIDSERPARPORT 0x78 +#define VIDINXDELTA 0x7c +#define VIDININITERR 0x80 +#define VIDINYDELTA 0x84 +#define VIDPIXBUFTHOLD 0x88 +#define VIDCHRMIN 0x8c +#define VIDCHRMAX 0x90 +#define VIDCURLIN 0x94 +#define VIDSCREENSIZE 0x98 +#define VIDOVRSTARTCRD 0x9c +#define VIDOVRENDCRD 0xa0 +#define VIDOVRDUDX 0xa4 +#define VIDOVRDUDXOFF 0xa8 +#define VIDOVRDVDY 0xac +/* ... */ +#define VIDOVRDVDYOFF 0xe0 +#define VIDDESKSTART 0xe4 +#define VIDDESKSTRIDE 0xe8 +#define VIDINADDR0 0xec +#define VIDINADDR1 0xf0 +#define VIDINADDR2 0xf4 +#define VIDINSTRIDE 0xf8 +#define VIDCUROVRSTART 0xfc + +#define INTCTRL (0x00100000 + 0x04) +#define CLIP0MIN (0x00100000 + 0x08) +#define CLIP0MAX (0x00100000 + 0x0c) +#define DSTBASE (0x00100000 + 0x10) +#define DSTFORMAT (0x00100000 + 0x14) +#define SRCBASE (0x00100000 + 0x34) +#define COMMANDEXTRA_2D (0x00100000 + 0x38) +#define CLIP1MIN (0x00100000 + 0x4c) +#define CLIP1MAX (0x00100000 + 0x50) +#define SRCFORMAT (0x00100000 + 0x54) +#define SRCSIZE (0x00100000 + 0x58) +#define SRCXY (0x00100000 + 0x5c) +#define COLORBACK (0x00100000 + 0x60) +#define COLORFORE (0x00100000 + 0x64) +#define DSTSIZE (0x00100000 + 0x68) +#define DSTXY (0x00100000 + 0x6c) +#define COMMAND_2D (0x00100000 + 0x70) +#define LAUNCH_2D (0x00100000 + 0x80) + +#define COMMAND_3D (0x00200000 + 0x120) + +/* register bitfields (not all, only as needed) */ + +#define BIT(x) (1UL << (x)) + +#define ROP_COPY 0xcc + +#define COMMAND_2D_FILLRECT 0x05 +#define COMMAND_2D_BITBLT 0x01 + +#define COMMAND_3D_NOP 0x00 + +#define STATUS_RETRACE BIT(6) +#define STATUS_BUSY BIT(9) + +#define MISCINIT1_CLUT_INV BIT(0) +#define MISCINIT1_2DBLOCK_DIS BIT(15) + +#define DRAMINIT0_SGRAM_NUM BIT(26) +#define DRAMINIT0_SGRAM_TYPE BIT(27) + +#define DRAMINIT1_MEM_SDRAM BIT(30) + +#define VGAINIT0_VGA_DISABLE BIT(0) +#define VGAINIT0_EXT_TIMING BIT(1) +#define VGAINIT0_8BIT_DAC BIT(2) +#define VGAINIT0_EXT_ENABLE BIT(6) +#define VGAINIT0_WAKEUP_3C3 BIT(8) +#define VGAINIT0_LEGACY_DISABLE BIT(9) +#define VGAINIT0_ALT_READBACK BIT(10) +#define VGAINIT0_FAST_BLINK BIT(11) +#define VGAINIT0_EXTSHIFTOUT BIT(12) +#define VGAINIT0_DECODE_3C6 BIT(13) +#define VGAINIT0_SGRAM_HBLANK_DISABLE BIT(22) + +#define VGAINIT1_MASK 0x1fffff + +#define VIDCFG_VIDPROC_ENABLE BIT(0) +#define VIDCFG_CURS_X11 BIT(1) +#define VIDCFG_HALF_MODE BIT(4) +#define VIDCFG_DESK_ENABLE BIT(7) +#define VIDCFG_CLUT_BYPASS BIT(10) +#define VIDCFG_2X BIT(26) +#define VIDCFG_PIXFMT_SHIFT 18 + +#define DACMODE_2X BIT(0) + +/* VGA rubbish, need to change this for multihead support */ +#define MISC_W 0x3c2 +#define MISC_R 0x3cc +#define SEQ_I 0x3c4 +#define SEQ_D 0x3c5 +#define CRT_I 0x3d4 +#define CRT_D 0x3d5 +#define ATT_IW 0x3c0 +#define IS1_R 0x3da +#define GRA_I 0x3ce +#define GRA_D 0x3cf +#define DAC_IR 0x3c7 +#define DAC_IW 0x3c8 +#define DAC_D 0x3c9 + +#ifndef FB_ACCEL_3DFX_BANSHEE +#define FB_ACCEL_3DFX_BANSHEE 31 +#endif + +#define TDFXF_HSYNC_ACT_HIGH 0x01 +#define TDFXF_HSYNC_ACT_LOW 0x02 +#define TDFXF_VSYNC_ACT_HIGH 0x04 +#define TDFXF_VSYNC_ACT_LOW 0x08 +#define TDFXF_LINE_DOUBLE 0x10 +#define TDFXF_VIDEO_ENABLE 0x20 + +#define TDFXF_HSYNC_MASK 0x03 +#define TDFXF_VSYNC_MASK 0x0c + +/* #define TDFXFB_DEBUG */ +#ifdef TDFXFB_DEBUG +#define DPRINTK(a,b...) printk("fb: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) +#endif + +#define PICOS2KHZ(a) (1000000000UL/(a)) +#define KHZ2PICOS(a) (1000000000UL/(a)) + +#define BANSHEE_MAX_PIXCLOCK 270000.0 +#define VOODOO3_MAX_PIXCLOCK 300000.0 + +struct banshee_reg { + /* VGA rubbish */ + unsigned char att[21]; + unsigned char crt[25]; + unsigned char gra[ 9]; + unsigned char misc[1]; + unsigned char seq[ 5]; + + /* Banshee extensions */ + unsigned char ext[2]; + unsigned long vidcfg; + unsigned long vidpll; + unsigned long mempll; + unsigned long gfxpll; + unsigned long dacmode; + unsigned long vgainit0; + unsigned long vgainit1; + unsigned long screensize; + unsigned long stride; + unsigned long cursloc; + unsigned long startaddr; + unsigned long clip0min; + unsigned long clip0max; + unsigned long clip1min; + unsigned long clip1max; + unsigned long srcbase; + unsigned long dstbase; +}; + +struct tdfxfb_par { + u32 pixclock; + + u32 baseline; + + u32 width; + u32 height; + u32 width_virt; + u32 height_virt; + u32 lpitch; /* line pitch, in bytes */ + u32 ppitch; /* pixel pitch, in bits */ + u32 bpp; + + u32 hdispend; + u32 hsyncsta; + u32 hsyncend; + u32 htotal; + + u32 vdispend; + u32 vsyncsta; + u32 vsyncend; + u32 vtotal; + + u32 video; + u32 accel_flags; +}; + +struct fb_info_tdfx { + struct fb_info fb_info; + + u16 dev; + u32 max_pixclock; + + unsigned long regbase_phys; + unsigned long regbase_virt; + unsigned long regbase_size; + unsigned long bufbase_phys; + unsigned long bufbase_virt; + unsigned long bufbase_size; + + struct { u8 red, green, blue, pad; } palette[256]; + struct tdfxfb_par default_par; + struct tdfxfb_par current_par; + struct display disp; + struct display_switch dispsw; + + union { +#ifdef FBCON_HAS_CFB16 + u16 cfb16[16]; +#endif +#ifdef FBCON_HAS_CFB32 + u32 cfb32[16]; +#endif + } fbcon_cmap; +}; + +/* + * Frame buffer device API + */ +static int tdfxfb_open(struct fb_info* info, + int user); +static int tdfxfb_release(struct fb_info* info, + int user); +static int tdfxfb_get_fix(struct fb_fix_screeninfo* fix, + int con, + struct fb_info* fb); +static int tdfxfb_get_var(struct fb_var_screeninfo* var, + int con, + struct fb_info* fb); +static int tdfxfb_set_var(struct fb_var_screeninfo* var, + int con, + struct fb_info* fb); +static int tdfxfb_pan_display(struct fb_var_screeninfo* var, + int con, + struct fb_info* fb); +static int tdfxfb_get_cmap(struct fb_cmap *cmap, + int kspc, + int con, + struct fb_info* info); +static int tdfxfb_set_cmap(struct fb_cmap* cmap, + int kspc, + int con, + struct fb_info* info); +static int tdfxfb_ioctl(struct inode* inode, + struct file* file, + u_int cmd, + u_long arg, + int con, + struct fb_info* info); + +/* + * Interface to the low level console driver + */ +static int tdfxfb_switch_con(int con, + struct fb_info* fb); +static int tdfxfb_updatevar(int con, + struct fb_info* fb); +static void tdfxfb_blank(int blank, + struct fb_info* fb); + +/* + * Internal routines + */ +static void tdfxfb_set_par(const struct tdfxfb_par* par, + struct fb_info_tdfx* + info); +static int tdfxfb_decode_var(const struct fb_var_screeninfo *var, + struct tdfxfb_par *par, + const struct fb_info_tdfx *info); +static int tdfxfb_encode_var(struct fb_var_screeninfo* var, + const struct tdfxfb_par* par, + const struct fb_info_tdfx* info); +static int tdfxfb_encode_fix(struct fb_fix_screeninfo* fix, + const struct tdfxfb_par* par, + const struct fb_info_tdfx* info); +static void tdfxfb_set_disp(struct display* disp, + struct fb_info_tdfx* info, + int bpp, + int accel); +static int tdfxfb_getcolreg(u_int regno, + u_int* red, + u_int* green, + u_int* blue, + u_int* transp, + struct fb_info* fb); +static int tdfxfb_setcolreg(u_int regno, + u_int red, + u_int green, + u_int blue, + u_int transp, + struct fb_info* fb); +static void tdfxfb_install_cmap(int con, + struct fb_info *info); + +/* + * Interface used by the world + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +void tdfxfb_init(void); +#else +int tdfxfb_init(void); +#endif +void tdfxfb_setup(char *options, + int *ints); + +static int currcon = 0; + +static struct fb_ops tdfxfb_ops = { + tdfxfb_open, + tdfxfb_release, + tdfxfb_get_fix, + tdfxfb_get_var, + tdfxfb_set_var, + tdfxfb_get_cmap, + tdfxfb_set_cmap, + tdfxfb_pan_display, + tdfxfb_ioctl, + NULL +}; + +struct mode { + char* name; + struct fb_var_screeninfo var; +} mode; + +/* 2.3.x kernels have a fb mode database, so supply only one backup default */ +struct mode default_mode[] = { + { "640x480-8@60", /* @ 60 Hz */ + { + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 39722, 40, 24, 32, 11, 96, 2, + 0, FB_VMODE_NONINTERLACED + } + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + , + { "800x600-8@56", /* @ 56 Hz */ + { + 800, 600, 800, 600, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 27778, 128, 24, 22, 1, 72, 2, + 0, FB_VMODE_NONINTERLACED + } + }, + { "1024x768-8@60", /* @ 60 Hz */ + { + 1024, 768, 1024, 768, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 15385, 168, 8, 29, 3, 144, 6, + 0, FB_VMODE_NONINTERLACED + } + }, + { "1280x1024-8@61", /* @ 61 Hz */ + { + 1280, 1024, 1280, 1024, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 9091, 200, 48, 26, 1, 184, 3, + 0, FB_VMODE_NONINTERLACED + } + }, + { "1024x768-16@60", /* @ 60 Hz */ /* basically for testing */ + { + 1024, 768, 1024, 768, 0, 0, 16, 0, + {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 15385, 168, 8, 29, 3, 144, 6, + 0, FB_VMODE_NONINTERLACED + } + } +#endif +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +static int modes = sizeof(default_mode)/sizeof(struct mode); +static int default_mode_index = 0; +#endif + +static struct fb_info_tdfx fb_info; + +static int __initdata noaccel = 0; +static int __initdata nopan = 0; +static int __initdata nowrap = 0; +static int __initdata inverse = 0; +static char __initdata fontname[40] = { 0 }; +static const char *mode_option __initdata = NULL; + +/* ------------------------------------------------------------------------- */ + +static inline __u8 vga_inb(__u32 reg) { return inb(reg); } +static inline __u16 vga_inw(__u32 reg) { return inw(reg); } +static inline __u16 vga_inl(__u32 reg) { return inl(reg); } + +static inline void vga_outb(__u32 reg, __u8 val) { outb(val, reg); } +static inline void vga_outw(__u32 reg, __u16 val) { outw(val, reg); } +static inline void vga_outl(__u32 reg, __u32 val) { outl(val, reg); } + +static inline void gra_outb(__u32 idx, __u8 val) { + vga_outb(GRA_I, idx); vga_outb(GRA_D, val); +} + +static inline __u8 gra_inb(__u32 idx) { + vga_outb(GRA_I, idx); return vga_inb(GRA_D); +} + +static inline void seq_outb(__u32 idx, __u8 val) { + vga_outb(SEQ_I, idx); vga_outb(SEQ_D, val); +} + +static inline __u8 seq_inb(__u32 idx) { + vga_outb(SEQ_I, idx); return vga_inb(SEQ_D); +} + +static inline void crt_outb(__u32 idx, __u8 val) { + vga_outb(CRT_I, idx); vga_outb(CRT_D, val); +} + +static inline __u8 crt_inb(__u32 idx) { + vga_outb(CRT_I, idx); return vga_inb(CRT_D); +} + +static inline void att_outb(__u32 idx, __u8 val) { + unsigned char tmp; + tmp = vga_inb(IS1_R); + vga_outb(ATT_IW, idx); + vga_outb(ATT_IW, val); +} + +static inline __u8 att_inb(__u32 idx) { + unsigned char tmp; + tmp = vga_inb(IS1_R); + vga_outb(ATT_IW, idx); + return vga_inb(ATT_IW); +} + +static inline void vga_disable_video(void) { + unsigned char s; + s = seq_inb(0x01) | 0x20; + seq_outb(0x00, 0x01); + seq_outb(0x01, s); + seq_outb(0x00, 0x03); +} + +static inline void vga_enable_video(void) { + unsigned char s; + s = seq_inb(0x01) & 0xdf; + seq_outb(0x00, 0x01); + seq_outb(0x01, s); + seq_outb(0x00, 0x03); +} + +static inline void vga_disable_palette(void) { + vga_inb(IS1_R); + vga_outb(ATT_IW, 0x00); +} + +static inline void vga_enable_palette(void) { + vga_inb(IS1_R); + vga_outb(ATT_IW, 0x20); +} + +static inline __u32 tdfx_inl(unsigned int reg) { + return readl(fb_info.regbase_virt + reg); +} + +static inline void tdfx_outl(unsigned int reg, __u32 val) { + writel(val, fb_info.regbase_virt + reg); +} + +static inline void banshee_make_room(int size) { + while((tdfx_inl(STATUS) & 0x1f) < size); +} + +static inline void banshee_wait_idle(void) { + int i = 0; + + banshee_make_room(1); + tdfx_outl(COMMAND_3D, COMMAND_3D_NOP); + + while(1) { + i = (tdfx_inl(STATUS) & STATUS_BUSY) ? 0 : i + 1; + if(i == 3) break; + } +} + +static void banshee_fillrect(__u32 x, + __u32 y, + __u32 w, + __u32 h, + __u32 color, + __u32 stride, + __u32 bpp) { + banshee_make_room(2); + tdfx_outl(DSTFORMAT, + (stride & 0x3fff) | + (bpp == 8 ? 0x10000 : + bpp == 16 ? 0x30000 : 0x50000)); + tdfx_outl(COLORFORE, color); + + banshee_make_room(3); + tdfx_outl(COMMAND_2D, COMMAND_2D_FILLRECT | (ROP_COPY << 24)); + tdfx_outl(DSTSIZE, (w & 0x1fff) | ((h & 0x1fff) << 16)); + tdfx_outl(LAUNCH_2D, (x & 0x1fff) | ((y & 0x1fff) << 16)); +} + +static void banshee_bitblt(__u32 curx, + __u32 cury, + __u32 dstx, + __u32 dsty, + __u32 width, + __u32 height, + __u32 stride, + __u32 bpp) { + int xdir, ydir; + + xdir = dstx < curx ? 1 : -1; + ydir = dsty < cury ? 1 : -1; + + banshee_make_room(4); + tdfx_outl(SRCFORMAT, + (stride & 0x3fff) | + (bpp == 8 ? 0x10000 : + bpp == 16 ? 0x30000 : 0x50000)); + tdfx_outl(DSTFORMAT, + (stride & 0x3fff) | + (bpp == 8 ? 0x10000 : + bpp == 16 ? 0x30000 : 0x50000)); + tdfx_outl(COMMAND_2D, + COMMAND_2D_BITBLT | + (xdir == -1 ? BIT(14) : 0) | + (ydir == -1 ? BIT(15) : 0)); + tdfx_outl(COMMANDEXTRA_2D, 0); /* no color keying */ + + if(xdir == -1) { + curx += width - 1; + dstx += width - 1; + } + if(ydir == -1) { + cury += height - 1; + dsty += height - 1; + } + + /* Consecutive overlapping regions can hang the board -- + since we allow mmap'ing of control registers, we cannot + __safely__ assume anything, like XF86 does... */ + banshee_make_room(1); + tdfx_outl(COMMAND_3D, COMMAND_3D_NOP); + + banshee_make_room(3); + tdfx_outl(DSTSIZE, (width & 0x1fff) | ((height & 0x1fff) << 16)); + tdfx_outl(DSTXY, (dstx & 0x1fff) | ((dsty & 0x1fff) << 16)); + tdfx_outl(LAUNCH_2D, (curx & 0x1fff) | ((cury & 0x1fff) << 16)); +} + +static __u32 banshee_calc_pll(int freq, int* freq_out) { + int m, n, k, best_m, best_n, best_k, f_cur, best_error; + int fref = 14318; + + /* this really could be done with more intelligence */ + best_error = freq; + best_n = best_m = best_k = 0; + for(n = 1; n < 256; n++) { + for(m = 1; m < 64; m++) { + for(k = 0; k < 4; k++) { + f_cur = fref*(n + 2)/(m + 2)/(1 << k); + if(abs(f_cur - freq) < best_error) { + best_error = abs(f_cur-freq); + best_n = n; + best_m = m; + best_k = k; + } + } + } + } + n = best_n; + m = best_m; + k = best_k; + *freq_out = fref*(n + 2)/(m + 2)/(1 << k); + + DPRINTK("freq = %d kHz, freq_out = %d kHz\n", freq, *freq_out); + DPRINTK("N = %d, M = %d, K = %d\n", n, m, k); + + return (n << 8) | (m << 2) | k; +} + +static void banshee_write_regs(struct banshee_reg* reg) { + int i; + + banshee_wait_idle(); + + tdfx_outl(MISCINIT1, tdfx_inl(MISCINIT1) | 0x01); + + crt_outb(0x11, crt_inb(0x11) & 0x7f); /* CRT unprotect */ + + banshee_make_room(3); + tdfx_outl(VGAINIT1, reg->vgainit1 & 0x001FFFFF); + tdfx_outl(VIDPROCCFG, reg->vidcfg & ~0x00000001); +#if 0 + tdfx_outl(PLLCTRL1, reg->mempll); + tdfx_outl(PLLCTRL2, reg->gfxpll); +#endif + tdfx_outl(PLLCTRL0, reg->vidpll); + + vga_outb(MISC_W, reg->misc[0x00] | 0x01); + + for(i = 0; i < 5; i++) + seq_outb(i, reg->seq[i]); + + for(i = 0; i < 25; i++) + crt_outb(i, reg->crt[i]); + + for(i = 0; i < 9; i++) + gra_outb(i, reg->gra[i]); + + for(i = 0; i < 21; i++) + att_outb(i, reg->att[i]); + + crt_outb(0x1a, reg->ext[0]); + crt_outb(0x1b, reg->ext[1]); + + vga_enable_palette(); + vga_enable_video(); + + banshee_make_room(9); + tdfx_outl(VGAINIT0, reg->vgainit0); + tdfx_outl(DACMODE, reg->dacmode); + tdfx_outl(VIDDESKSTRIDE, reg->stride); + tdfx_outl(HWCURPATADDR, reg->cursloc); + tdfx_outl(VIDSCREENSIZE, reg->screensize); + tdfx_outl(VIDDESKSTART, reg->startaddr); + tdfx_outl(VIDPROCCFG, reg->vidcfg); + tdfx_outl(VGAINIT1, reg->vgainit1); + + banshee_make_room(7); + tdfx_outl(SRCBASE, reg->srcbase); + tdfx_outl(DSTBASE, reg->dstbase); + tdfx_outl(COMMANDEXTRA_2D, 0); + tdfx_outl(CLIP0MIN, 0); + tdfx_outl(CLIP0MAX, 0x0fff0fff); + tdfx_outl(CLIP1MIN, 0); + tdfx_outl(CLIP1MAX, 0x0fff0fff); + + banshee_wait_idle(); +} + +static unsigned long tdfx_lfb_size(void) { + __u32 draminit0 = 0; + __u32 draminit1 = 0; + __u32 miscinit1 = 0; + __u32 lfbsize = 0; + int sgram_p = 0; + + if(!((fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) || + (fb_info.dev == PCI_DEVICE_ID_3DFX_VOODOO3))) + return 0; + + draminit0 = tdfx_inl(DRAMINIT0); + draminit1 = tdfx_inl(DRAMINIT1); + + sgram_p = (draminit1 & DRAMINIT1_MEM_SDRAM) ? 0 : 1; + + lfbsize = sgram_p ? + (((draminit0 & DRAMINIT0_SGRAM_NUM) ? 2 : 1) * + ((draminit0 & DRAMINIT0_SGRAM_TYPE) ? 8 : 4) * 1024 * 1024) : + 16 * 1024 * 1024; + + /* disable block writes for SDRAM (why?) */ + miscinit1 = tdfx_inl(MISCINIT1); + miscinit1 |= sgram_p ? 0 : MISCINIT1_2DBLOCK_DIS; + miscinit1 |= MISCINIT1_CLUT_INV; + tdfx_outl(MISCINIT1, miscinit1); + + return lfbsize; +} + +static void fbcon_banshee_bmove(struct display* p, + int sy, + int sx, + int dy, + int dx, + int height, + int width) { + banshee_bitblt(fontwidth(p)*sx, + fontheight(p)*sy, + fontwidth(p)*dx, + fontheight(p)*dy, + fontwidth(p)*width, + fontheight(p)*height, + fb_info.current_par.lpitch, + fb_info.current_par.bpp); +} + +static void fbcon_banshee_clear(struct vc_data* conp, + struct display* p, + int sy, + int sx, + int height, + int width) { + unsigned int bg; + + bg = attr_bgcol_ec(p,conp); + banshee_fillrect(fontwidth(p)*sx, + fontheight(p)*sy, + fontwidth(p)*width, + fontheight(p)*height, + bg, + fb_info.current_par.lpitch, + fb_info.current_par.bpp); +} + +#ifdef FBCON_HAS_CFB8 +static struct display_switch fbcon_banshee8 = { + fbcon_cfb8_setup, + fbcon_banshee_bmove, + fbcon_banshee_clear, + fbcon_cfb8_putc, + fbcon_cfb8_putcs, + fbcon_cfb8_revc, + NULL, + NULL, + fbcon_cfb8_clear_margins, + FONTWIDTH(8) +}; +#endif +#ifdef FBCON_HAS_CFB16 +static struct display_switch fbcon_banshee16 = { + fbcon_cfb16_setup, + fbcon_banshee_bmove, + fbcon_banshee_clear, + fbcon_cfb16_putc, + fbcon_cfb16_putcs, + fbcon_cfb16_revc, + NULL, + NULL, + fbcon_cfb16_clear_margins, + FONTWIDTH(8) +}; +#endif +#ifdef FBCON_HAS_CFB32 +static struct display_switch fbcon_banshee32 = { + fbcon_cfb32_setup, + fbcon_banshee_bmove, + fbcon_banshee_clear, + fbcon_cfb32_putc, + fbcon_cfb32_putcs, + fbcon_cfb32_revc, + NULL, + NULL, + fbcon_cfb32_clear_margins, + FONTWIDTH(8) +}; +#endif + +/* ------------------------------------------------------------------------- */ + +static void tdfxfb_set_par(const struct tdfxfb_par* par, + struct fb_info_tdfx* info) { + struct fb_info_tdfx* i = (struct fb_info_tdfx*)info; + struct banshee_reg reg; + __u32 cpp; + __u32 hd, hs, he, ht, hbs, hbe; + __u32 vd, vs, ve, vt, vbs, vbe; + __u32 wd; + int fout; + int freq; + + memset(®, 0, sizeof(reg)); + + cpp = (par->bpp + 7)/8; + + wd = (par->hdispend >> 3) - 1; + + hd = (par->hdispend >> 3) - 1; + hs = (par->hsyncsta >> 3) - 1; + he = (par->hsyncend >> 3) - 1; + ht = (par->htotal >> 3) - 1; + hbs = hd; + hbe = ht; + + vd = par->vdispend - 1; + vs = par->vsyncsta - 1; + ve = par->vsyncend - 1; + vt = par->vtotal - 2; + vbs = vd; + vbe = vt; + + /* this is all pretty standard VGA register stuffing */ + reg.misc[0x00] = + 0x0f | + (par->hdispend < 400 ? 0xa0 : + par->hdispend < 480 ? 0x60 : + par->hdispend < 768 ? 0xe0 : 0x20); + + reg.gra[0x00] = 0x00; + reg.gra[0x01] = 0x00; + reg.gra[0x02] = 0x00; + reg.gra[0x03] = 0x00; + reg.gra[0x04] = 0x00; + reg.gra[0x05] = 0x40; + reg.gra[0x06] = 0x05; + reg.gra[0x07] = 0x0f; + reg.gra[0x08] = 0xff; + + reg.att[0x00] = 0x00; + reg.att[0x01] = 0x01; + reg.att[0x02] = 0x02; + reg.att[0x03] = 0x03; + reg.att[0x04] = 0x04; + reg.att[0x05] = 0x05; + reg.att[0x06] = 0x06; + reg.att[0x07] = 0x07; + reg.att[0x08] = 0x08; + reg.att[0x09] = 0x09; + reg.att[0x0a] = 0x0a; + reg.att[0x0b] = 0x0b; + reg.att[0x0c] = 0x0c; + reg.att[0x0d] = 0x0d; + reg.att[0x0e] = 0x0e; + reg.att[0x0f] = 0x0f; + reg.att[0x10] = 0x41; + reg.att[0x11] = 0x00; + reg.att[0x12] = 0x0f; + reg.att[0x13] = 0x00; + reg.att[0x14] = 0x00; + + reg.seq[0x00] = 0x03; + reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */ + reg.seq[0x02] = 0x0f; + reg.seq[0x03] = 0x00; + reg.seq[0x04] = 0x0e; + + reg.crt[0x00] = ht - 4; + reg.crt[0x01] = hd; + reg.crt[0x02] = hbs; + reg.crt[0x03] = 0x80 | (hbe & 0x1f); + reg.crt[0x04] = hs; + reg.crt[0x05] = + ((hbe & 0x20) << 2) | + (he & 0x1f); + reg.crt[0x06] = vt; + reg.crt[0x07] = + ((vs & 0x200) >> 2) | + ((vd & 0x200) >> 3) | + ((vt & 0x200) >> 4) | + 0x10 | + ((vbs & 0x100) >> 5) | + ((vs & 0x100) >> 6) | + ((vd & 0x100) >> 7) | + ((vt & 0x100) >> 8); + reg.crt[0x08] = 0x00; + reg.crt[0x09] = + 0x40 | + ((vbs & 0x200) >> 4); + reg.crt[0x0a] = 0x00; + reg.crt[0x0b] = 0x00; + reg.crt[0x0c] = 0x00; + reg.crt[0x0d] = 0x00; + reg.crt[0x0e] = 0x00; + reg.crt[0x0f] = 0x00; + reg.crt[0x10] = vs; + reg.crt[0x11] = + (ve & 0x0f) | + 0x20; + reg.crt[0x12] = vd; + reg.crt[0x13] = wd; + reg.crt[0x14] = 0x00; + reg.crt[0x15] = vbs; + reg.crt[0x16] = vbe + 1; + reg.crt[0x17] = 0xc3; + reg.crt[0x18] = 0xff; + + /* Banshee's nonvga stuff */ + reg.ext[0x00] = (((ht & 0x100) >> 8) | + ((hd & 0x100) >> 6) | + ((hbs & 0x100) >> 4) | + ((hbe & 0x40) >> 1) | + ((hs & 0x100) >> 2) | + ((he & 0x20) << 2)); + reg.ext[0x01] = (((vt & 0x400) >> 10) | + ((vd & 0x400) >> 8) | + ((vbs & 0x400) >> 6) | + ((vbe & 0x400) >> 4)); + + reg.vgainit0 = + VGAINIT0_8BIT_DAC | + VGAINIT0_EXT_ENABLE | + VGAINIT0_WAKEUP_3C3 | + VGAINIT0_ALT_READBACK | + VGAINIT0_EXTSHIFTOUT; + reg.vgainit1 = tdfx_inl(VGAINIT1) & 0x1fffff; + + reg.vidcfg = + VIDCFG_VIDPROC_ENABLE | + VIDCFG_DESK_ENABLE | + ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) | + (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0); + reg.stride = par->width*cpp; + reg.cursloc = 0; + + reg.startaddr = par->baseline*reg.stride; + reg.srcbase = reg.startaddr; + reg.dstbase = reg.startaddr; + + /* PLL settings */ + freq = par->pixclock; + + reg.dacmode &= ~DACMODE_2X; + reg.vidcfg &= ~VIDCFG_2X; + if(freq > i->max_pixclock/2) { + freq = freq > i->max_pixclock ? i->max_pixclock : freq; + reg.dacmode |= DACMODE_2X; + reg.vidcfg |= VIDCFG_2X; + } + reg.vidpll = banshee_calc_pll(freq, &fout); +#if 0 + reg.mempll = banshee_calc_pll(..., &fout); + reg.gfxpll = banshee_calc_pll(..., &fout); +#endif + + reg.screensize = par->width | (par->height << 12); + reg.vidcfg &= ~VIDCFG_HALF_MODE; + + banshee_write_regs(®); + + i->current_par = *par; +} + +static int tdfxfb_decode_var(const struct fb_var_screeninfo* var, + struct tdfxfb_par* par, + const struct fb_info_tdfx* info) { + struct fb_info_tdfx* i = (struct fb_info_tdfx*)info; + + if(var->bits_per_pixel != 8 && + var->bits_per_pixel != 16 && + var->bits_per_pixel != 32) { + DPRINTK("depth not supported: %u\n", var->bits_per_pixel); + return -EINVAL; + } + + if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; + } + + if(var->xoffset) { + DPRINTK("xoffset not supported\n"); + return -EINVAL; + } + + if(var->xres != var->xres_virtual) { + DPRINTK("virtual x resolution != physical x resolution not supported\n"); + return -EINVAL; + } + + if(nopan && nowrap) { + if(var->yres != var->yres_virtual) { + DPRINTK("virtual y resolution != physical y resolution not supported\n"); + return -EINVAL; + } + } else { + if(var->yres > var->yres_virtual) { + DPRINTK("virtual y resolution < physical y resolution not possible\n"); + return -EINVAL; + } + } + + if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; + } + + memset(par, 0, sizeof(struct tdfxfb_par)); + + switch(i->dev) { + case PCI_DEVICE_ID_3DFX_BANSHEE: + case PCI_DEVICE_ID_3DFX_VOODOO3: + par->width = (var->xres + 15) & ~15; /* could sometimes be 8 */ + par->width_virt = par->width; + par->height = var->yres; + par->height_virt = var->yres_virtual; + par->bpp = var->bits_per_pixel; + par->ppitch = var->bits_per_pixel; + par->lpitch = par->width*par->ppitch/8; + + par->baseline = 0; + + if(par->width < 320 || par->width > 2048) { + DPRINTK("width not supported: %u\n", par->width); + return -EINVAL; + } + if(par->height < 200 || par->height > 2048) { + DPRINTK("height not supported: %u\n", par->height); + return -EINVAL; + } + if(par->lpitch*par->height_virt > i->bufbase_size) { + DPRINTK("no memory for screen (%ux%ux%u)\n", + par->width, par->height_virt, par->bpp); + return -EINVAL; + } + par->pixclock = PICOS2KHZ(var->pixclock); + if(par->pixclock > i->max_pixclock) { + DPRINTK("pixclock too high (%uKHz)\n", par->pixclock); + return -EINVAL; + } + + par->hdispend = var->xres; + par->hsyncsta = par->hdispend + var->right_margin; + par->hsyncend = par->hsyncsta + var->hsync_len; + par->htotal = par->hsyncend + var->left_margin; + + par->vdispend = var->yres; + par->vsyncsta = par->vdispend + var->lower_margin; + par->vsyncend = par->vsyncsta + var->vsync_len; + par->vtotal = par->vsyncend + var->upper_margin; + + if(var->sync & FB_SYNC_HOR_HIGH_ACT) + par->video |= TDFXF_HSYNC_ACT_HIGH; + else + par->video |= TDFXF_HSYNC_ACT_LOW; + if(var->sync & FB_SYNC_VERT_HIGH_ACT) + par->video |= TDFXF_VSYNC_ACT_HIGH; + else + par->video |= TDFXF_VSYNC_ACT_LOW; + if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) + par->video |= TDFXF_LINE_DOUBLE; + if(var->activate == FB_ACTIVATE_NOW) + par->video |= TDFXF_VIDEO_ENABLE; + } + + if(var->accel_flags & FB_ACCELF_TEXT) + par->accel_flags = FB_ACCELF_TEXT; + else + par->accel_flags = 0; + + return 0; +} + +static int tdfxfb_encode_var(struct fb_var_screeninfo* var, + const struct tdfxfb_par* par, + const struct fb_info_tdfx* info) { + struct fb_var_screeninfo v; + + memset(&v, 0, sizeof(struct fb_var_screeninfo)); + v.xres_virtual = par->width_virt; + v.yres_virtual = par->height_virt; + v.xres = par->width; + v.yres = par->height; + v.right_margin = par->hsyncsta - par->hdispend; + v.hsync_len = par->hsyncend - par->hsyncsta; + v.left_margin = par->htotal - par->hsyncend; + v.lower_margin = par->vsyncsta - par->vdispend; + v.vsync_len = par->vsyncend - par->vsyncsta; + v.upper_margin = par->vtotal - par->vsyncend; + v.bits_per_pixel = par->bpp; + switch(par->bpp) { + case 8: + v.red.length = v.green.length = v.blue.length = 8; + break; + case 16: + v.red.offset = 11; + v.red.length = 5; + v.green.offset = 5; + v.green.length = 6; + v.blue.offset = 0; + v.blue.length = 5; + break; + case 32: + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + break; + } + v.height = v.width = -1; + v.pixclock = KHZ2PICOS(par->pixclock); + if((par->video & TDFXF_HSYNC_MASK) == TDFXF_HSYNC_ACT_HIGH) + v.sync |= FB_SYNC_HOR_HIGH_ACT; + if((par->video & TDFXF_VSYNC_MASK) == TDFXF_VSYNC_ACT_HIGH) + v.sync |= FB_SYNC_VERT_HIGH_ACT; + if(par->video & TDFXF_LINE_DOUBLE) + v.vmode = FB_VMODE_DOUBLE; + *var = v; + return 0; +} + +static int tdfxfb_open(struct fb_info* info, + int user) { + MOD_INC_USE_COUNT; + return(0); +} + +static int tdfxfb_release(struct fb_info* info, + int user) { + MOD_DEC_USE_COUNT; + return(0); +} + + +static int tdfxfb_encode_fix(struct fb_fix_screeninfo* fix, + const struct tdfxfb_par* par, + const struct fb_info_tdfx* info) { + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + + switch(info->dev) { + case PCI_DEVICE_ID_3DFX_BANSHEE: + case PCI_DEVICE_ID_3DFX_VOODOO3: + if (info->dev == PCI_DEVICE_ID_3DFX_BANSHEE) + strcpy(fix->id, "3Dfx Banshee"); + else + strcpy(fix->id, "3Dfx Voodoo3"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + fix->smem_start = (char*)info->bufbase_phys; + fix->smem_len = info->bufbase_size; + fix->mmio_start = (char*)info->regbase_phys; + fix->mmio_len = info->regbase_size; +#else + fix->smem_start = info->bufbase_phys; + fix->smem_len = info->bufbase_size; + fix->mmio_start = info->regbase_phys; + fix->mmio_len = info->regbase_size; +#endif + fix->accel = FB_ACCEL_3DFX_BANSHEE; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + fix->line_length = par->lpitch; + fix->visual = par->bpp == 8 + ? FB_VISUAL_PSEUDOCOLOR + : FB_VISUAL_DIRECTCOLOR; + + fix->xpanstep = 0; + fix->ypanstep = (nowrap && nopan) ? 0 : 1; + fix->ywrapstep = nowrap ? 0 : 1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tdfxfb_get_fix(struct fb_fix_screeninfo *fix, + int con, + struct fb_info *fb) { + const struct fb_info_tdfx *info = (struct fb_info_tdfx*)fb; + struct tdfxfb_par par; + + if(con == -1) + par = info->default_par; + else + tdfxfb_decode_var(&fb_display[con].var, &par, info); + tdfxfb_encode_fix(fix, &par, info); + return 0; +} + +static int tdfxfb_get_var(struct fb_var_screeninfo *var, + int con, + struct fb_info *fb) { + const struct fb_info_tdfx *info = (struct fb_info_tdfx*)fb; + + if(con == -1) + tdfxfb_encode_var(var, &info->default_par, info); + else + *var = fb_display[con].var; + return 0; +} + +static void tdfxfb_set_disp(struct display *disp, + struct fb_info_tdfx *info, + int bpp, + int accel) { + DPRINTK("actually, %s using acceleration!\n", + noaccel ? "NOT" : ""); + + switch(bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + info->dispsw = noaccel ? fbcon_cfb8 : fbcon_banshee8; + disp->dispsw = &info->dispsw; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + info->dispsw = noaccel ? fbcon_cfb16 : fbcon_banshee16; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + info->dispsw = noaccel ? fbcon_cfb32 : fbcon_banshee32; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb32; + break; +#endif + default: + info->dispsw = fbcon_dummy; + disp->dispsw = &info->dispsw; + } +} + +static int tdfxfb_set_var(struct fb_var_screeninfo *var, + int con, + struct fb_info *fb) { + struct fb_info_tdfx *info = (struct fb_info_tdfx*)fb; + struct tdfxfb_par par; + struct display *display; + int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel, accel, err; + int activate = var->activate; + + if(con >= 0) + display = &fb_display[con]; + else + display = fb->disp; /* used during initialization */ + + if((err = tdfxfb_decode_var(var, &par, info))) + return err; + + tdfxfb_encode_var(var, &par, info); + + if((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + oldxres = display->var.xres; + oldyres = display->var.yres; + oldvxres = display->var.xres_virtual; + oldvyres = display->var.yres_virtual; + oldbpp = display->var.bits_per_pixel; + oldaccel = display->var.accel_flags; + display->var = *var; + if(con < 0 || + oldxres != var->xres || + oldyres != var->yres || + oldvxres != var->xres_virtual || + oldvyres != var->yres_virtual || + oldbpp != var->bits_per_pixel || + oldaccel != var->accel_flags) { + struct fb_fix_screeninfo fix; + + tdfxfb_encode_fix(&fix, &par, info); + display->screen_base = (char *)info->bufbase_virt; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->next_line = fix.line_length; + display->can_soft_blank = 1; + display->inverse = inverse; + accel = var->accel_flags & FB_ACCELF_TEXT; + tdfxfb_set_disp(display, info, par.bpp, accel); + + if(nopan && nowrap) { + display->scrollmode = SCROLL_YREDRAW; +#ifdef FBCON_HAS_CFB8 + fbcon_banshee8.bmove = fbcon_redraw_bmove; +#endif +#ifdef FBCON_HAS_CFB16 + fbcon_banshee16.bmove = fbcon_redraw_bmove; +#endif +#ifdef FBCON_HAS_CFB32 + fbcon_banshee32.bmove = fbcon_redraw_bmove; +#endif + } + if (info->fb_info.changevar) + (*info->fb_info.changevar)(con); + } + if(!info->fb_info.display_fg || + info->fb_info.display_fg->vc_num == con || + con < 0) + tdfxfb_set_par(&par, info); + if(oldbpp != var->bits_per_pixel || con < 0) { + if((err = fb_alloc_cmap(&display->cmap, 0, 0))) + return err; + tdfxfb_install_cmap(con, &info->fb_info); + } + } + + return 0; +} + +static int tdfxfb_pan_display(struct fb_var_screeninfo* var, + int con, + struct fb_info* fb) { + struct fb_info_tdfx* i = (struct fb_info_tdfx*)fb; + __u32 addr; + + if(nowrap && nopan) { + return -EINVAL; + } else { + if(var->xoffset) + return -EINVAL; + if(var->yoffset < 0) + return -EINVAL; + if(nopan && var->yoffset > var->yres_virtual) + return -EINVAL; + if(nowrap && var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + + i->current_par.baseline = var->yoffset; + + addr = var->yoffset*i->current_par.lpitch; + tdfx_outl(VIDDESKSTART, addr); + tdfx_outl(SRCBASE, addr); + tdfx_outl(DSTBASE, addr); + return 0; + } +} + +static int tdfxfb_get_cmap(struct fb_cmap *cmap, + int kspc, + int con, + struct fb_info *fb) { + if(!fb->display_fg || con == fb->display_fg->vc_num) { + /* current console? */ + return fb_get_cmap(cmap, kspc, tdfxfb_getcolreg, fb); + } else if(fb_display[con].cmap.len) { + /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + } else { + int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256; + fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2); + } + return 0; +} + +static int tdfxfb_set_cmap(struct fb_cmap *cmap, + int kspc, + int con, + struct fb_info *fb) { + int err; + struct display *disp; + + if(con >= 0) + disp = &fb_display[con]; + else + disp = fb->disp; + if(!disp->cmap.len) { /* no colormap allocated? */ + int size = disp->var.bits_per_pixel == 16 ? 32 : 256; + if((err = fb_alloc_cmap(&disp->cmap, size, 0))) + return err; + } + if(!fb->display_fg || con == fb->display_fg->vc_num) { + /* current console? */ + return fb_set_cmap(cmap, kspc, tdfxfb_setcolreg, fb); + } else { + fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1); + } + return 0; +} + +static int tdfxfb_ioctl(struct inode *inode, + struct file *file, + u_int cmd, + u_long arg, + int con, + struct fb_info *fb) { + return -EINVAL; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +__initfunc(void tdfxfb_init(void)) { +#else +int __init tdfxfb_init(void) { +#endif + struct pci_dev *pdev = NULL; + struct fb_var_screeninfo var; + int j, k; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + if(!pcibios_present()) return; +#else + if(!pcibios_present()) return -ENXIO; +#endif + + for(pdev = pci_devices; pdev; pdev = pdev->next) { + if(((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && + (pdev->vendor == PCI_VENDOR_ID_3DFX) && + ((pdev->device == PCI_DEVICE_ID_3DFX_BANSHEE) || + (pdev->device == PCI_DEVICE_ID_3DFX_VOODOO3))) { + + fb_info.dev = pdev->device; + fb_info.max_pixclock = + pdev->device == PCI_DEVICE_ID_3DFX_BANSHEE + ? BANSHEE_MAX_PIXCLOCK + : VOODOO3_MAX_PIXCLOCK; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + fb_info.regbase_phys = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; + fb_info.regbase_size = 1 << 25; + fb_info.regbase_virt = + (__u32)ioremap_nocache(fb_info.regbase_phys, 1 << 25); + if(!fb_info.regbase_virt) { + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't remap Banshee register area.\n"); + else + printk("fb: Can't remap Voodoo3 register area.\n"); + return; + } + + fb_info.bufbase_phys = pdev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK; + if(!(fb_info.bufbase_size = tdfx_lfb_size())) { + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't count Banshee memory.\n"); + else + printk("fb: Can't count Voodoo3 memory.\n"); + iounmap((void*)fb_info.regbase_virt); + return; + } + fb_info.bufbase_virt = + (__u32)ioremap_nocache(fb_info.bufbase_phys, 1 << 25); + if(!fb_info.regbase_virt) { + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't remap Banshee framebuffer.\n"); + else + printk("fb: Can't remap Voodoo3 framebuffer.\n"); + iounmap((void*)fb_info.regbase_virt); + return; + } +#else + fb_info.regbase_phys = pdev->resource[0].start; + fb_info.regbase_size = 1 << 25; + fb_info.regbase_virt = + (__u32)ioremap_nocache(fb_info.regbase_phys, 1 << 25); + if(!fb_info.regbase_virt) { + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't remap Banshee register area.\n"); + else + printk("fb: Can't remap Voodoo3 register area.\n"); + return -ENXIO; + } + + fb_info.bufbase_phys = pdev->resource[1].start; + if(!(fb_info.bufbase_size = tdfx_lfb_size())) { + iounmap((void*)fb_info.regbase_virt); + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't count Banshee memory.\n"); + else + printk("fb: Can't count Voodoo3 memory.\n"); + return -ENXIO; + } + fb_info.bufbase_virt = + (__u32)ioremap_nocache(fb_info.bufbase_phys, 1 << 25); + if(!fb_info.regbase_virt) { + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Can't remap Banshee framebuffer.\n"); + else + printk("fb: Can't remap Voodoo3 framebuffer.\n"); + iounmap((void*)fb_info.regbase_virt); + return -ENXIO; + } +#endif + + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + printk("fb: Banshee memory = %ldK\n", fb_info.bufbase_size >> 10); + else + printk("fb: Voodoo3 memory = %ldK\n", fb_info.bufbase_size >> 10); + + /* clear framebuffer memory */ + memset_io(fb_info.bufbase_virt, 0, fb_info.bufbase_size); + + if (fb_info.dev == PCI_DEVICE_ID_3DFX_BANSHEE) + strcpy(fb_info.fb_info.modename, "3Dfx Banshee"); + else + strcpy(fb_info.fb_info.modename, "3Dfx Voodoo3"); + fb_info.fb_info.changevar = NULL; + fb_info.fb_info.node = -1; + fb_info.fb_info.fbops = &tdfxfb_ops; + fb_info.fb_info.disp = &fb_info.disp; + strcpy(fb_info.fb_info.fontname, fontname); + fb_info.fb_info.switch_con = &tdfxfb_switch_con; + fb_info.fb_info.updatevar = &tdfxfb_updatevar; + fb_info.fb_info.blank = &tdfxfb_blank; + fb_info.fb_info.flags = FBINFO_FLAG_DEFAULT; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + var = default_mode[default_mode_index < modes + ? default_mode_index + : 0].var; +#else + memset(&var, 0, sizeof(var)); + if(!mode_option || + !fb_find_mode(&var, &fb_info.fb_info, mode_option, NULL, 0, NULL, 8)) + var = default_mode[0].var; +#endif + + if(noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; + else var.accel_flags |= FB_ACCELF_TEXT; + + if(tdfxfb_decode_var(&var, &fb_info.default_par, &fb_info)) { + /* ugh -- can't use the mode from the mode db. (or command line), + so try the default */ + + printk("tdfxfb: " + "can't decode the supplied video mode, using default\n"); + + var = default_mode[0].var; + if(noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; + else var.accel_flags |= FB_ACCELF_TEXT; + + if(tdfxfb_decode_var(&var, &fb_info.default_par, &fb_info)) { + printk("tdfxfb: can't decode default video mode\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + return; +#else + return -ENXIO; +#endif + } + } + + fb_info.disp.screen_base = (void*)fb_info.bufbase_virt; + fb_info.disp.visual = + var.bits_per_pixel == 8 + ? FB_VISUAL_PSEUDOCOLOR + : FB_VISUAL_DIRECTCOLOR; + fb_info.disp.type = FB_TYPE_PACKED_PIXELS; + fb_info.disp.type_aux = 0; + + fb_info.disp.ypanstep = (nowrap && nopan) ? 0 : 1; + fb_info.disp.ywrapstep = nowrap ? 0 : 1; + + fb_info.disp.line_length = + fb_info.disp.next_line = + var.xres*(var.bits_per_pixel + 7)/8; + fb_info.disp.can_soft_blank = 1; + fb_info.disp.inverse = inverse; + fb_info.disp.scrollmode = SCROLL_YREDRAW; + fb_info.disp.var = var; + tdfxfb_set_disp(&fb_info.disp, &fb_info, + var.bits_per_pixel, + 0); + + for(j = 0; j < 16; j++) { + k = color_table[j]; + fb_info.palette[j].red = default_red[k]; + fb_info.palette[j].green = default_grn[k]; + fb_info.palette[j].blue = default_blu[k]; + } + + if(tdfxfb_set_var(&var, -1, &fb_info.fb_info)) { + printk("tdfxfb: can't set default video mode\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + return; +#else + return -ENXIO; +#endif + } + + if(register_framebuffer(&fb_info.fb_info) < 0) { + printk("tdfxfb: can't register framebuffer\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + return; +#else + return -ENXIO; +#endif + } + + printk("fb%d: %s frame buffer device\n", + GET_FB_IDX(fb_info.fb_info.node), + fb_info.fb_info.modename); + + MOD_INC_USE_COUNT; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + return; +#else + return 0; +#endif + } + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + return; +#else + return -ENXIO; +#endif +} + +void tdfxfb_setup(char *options, + int *ints) { + char* this_opt; + + if(!options || !*options) + return; + + for(this_opt = strtok(options, ","); + this_opt; + this_opt = strtok(NULL, ",")) { + if(!strcmp(this_opt, "inverse")) { + inverse = 1; + fb_invert_cmaps(); + } else if(!strcmp(this_opt, "noaccel")) { + noaccel = 1; + } else if(!strcmp(this_opt, "nopan")) { + nopan = 1; + } else if(!strcmp(this_opt, "nowrap")) { + nowrap = 1; + } else if (!strncmp(this_opt, "font:", 5)) { + strncpy(fontname, this_opt + 5, 40); + } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) + int i; + for(i = 0; i < modes; i++) { + if(!strcmp(this_opt, default_mode[i].name)) { + default_mode_index = i; + } + } +#else + mode_option = this_opt; +#endif + } + } +} + +static int tdfxfb_switch_con(int con, + struct fb_info *fb) { + struct fb_info_tdfx *info = (struct fb_info_tdfx*)fb; + struct tdfxfb_par par; + + /* Do we have to save the colormap? */ + if(fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, 1, tdfxfb_getcolreg, fb); + + currcon = con; + + tdfxfb_decode_var(&fb_display[con].var, &par, info); + tdfxfb_set_par(&par, info); + tdfxfb_set_disp(&fb_display[con], + info, + par.bpp, + par.accel_flags & FB_ACCELF_TEXT); + + tdfxfb_install_cmap(con, fb); + tdfxfb_updatevar(con, fb); + + return 1; +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ +static void tdfxfb_blank(int blank, + struct fb_info *fb) { + u32 dacmode, state = 0, vgablank = 0; + + dacmode = tdfx_inl(DACMODE); + + switch(blank) { + case 0: /* Screen: On; HSync: On, VSync: On */ + state = 0; + vgablank = 0; + break; + case 1: /* Screen: Off; HSync: On, VSync: On */ + state = 0; + vgablank = 1; + break; + case 2: /* Screen: Off; HSync: On, VSync: Off */ + state = BIT(3); + vgablank = 1; + break; + case 3: /* Screen: Off; HSync: Off, VSync: On */ + state = BIT(1); + vgablank = 1; + break; + case 4: /* Screen: Off; HSync: Off, VSync: Off */ + state = BIT(1) | BIT(3); + vgablank = 1; + break; + } + + dacmode &= ~(BIT(1) | BIT(3)); + dacmode |= state; + tdfx_outl(DACMODE, dacmode); + if(vgablank) + vga_disable_video(); + else + vga_enable_video(); + + return; +} + +static int tdfxfb_updatevar(int con, + struct fb_info* fb) { + if(con != currcon || (nowrap && nopan)) { + return 0; + } else { + struct fb_var_screeninfo* var = &fb_display[currcon].var; + return tdfxfb_pan_display(var, con, fb); + } +} + +static int tdfxfb_getcolreg(unsigned regno, + unsigned* red, + unsigned* green, + unsigned* blue, + unsigned* transp, + struct fb_info* fb) { + struct fb_info_tdfx* i = (struct fb_info_tdfx*)fb; + + if(regno < 256) { + *red = i->palette[regno].red << 8 | i->palette[regno].red; + *green = i->palette[regno].green << 8 | i->palette[regno].green; + *blue = i->palette[regno].blue << 8 | i->palette[regno].blue; + *transp = 0; + } + return regno > 255; +} + +static int tdfxfb_setcolreg(unsigned regno, + unsigned red, + unsigned green, + unsigned blue, + unsigned transp, + struct fb_info* info) { + struct fb_info_tdfx* i = (struct fb_info_tdfx*)info; + + if(regno < 16) { + switch(i->current_par.bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + i->fbcon_cmap.cfb16[regno] = + (((u32)red & 0xf800) >> 0) | + (((u32)green & 0xfc00) >> 5) | + (((u32)blue & 0xf800) >> 11); + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + i->fbcon_cmap.cfb32[regno] = + (((u32)red & 0xff00) << 8) | + (((u32)green & 0xff00) << 0) | + (((u32)blue & 0xff00) >> 8); + break; +#endif + default: + DPRINTK("bad depth %u\n", i->current_par.bpp); + break; + } + } + if(regno < 256) { + i->palette[regno].red = red >> 8; + i->palette[regno].green = green >> 8; + i->palette[regno].blue = blue >> 8; + if(i->current_par.bpp == 8) { + vga_outb(DAC_IW, (unsigned char)regno); + vga_outb(DAC_D, (unsigned char)(red >> 8)); + vga_outb(DAC_D, (unsigned char)(green >> 8)); + vga_outb(DAC_D, (unsigned char)(blue >> 8)); + } + } + return regno > 255; +} + +static void tdfxfb_install_cmap(int con, + struct fb_info* info) { + if(con != currcon) return; + if(fb_display[con].cmap.len) { + fb_set_cmap(&fb_display[con].cmap, 1, tdfxfb_setcolreg, info); + } else { + int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256; + fb_set_cmap(fb_default_cmap(size), 1, tdfxfb_setcolreg, info); + } +} + diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index 2541d14a4..a60774a61 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -55,11 +55,11 @@ #ifdef CONFIG_FB_COMPAT_XPMAC #include <asm/vc_ioctl.h> #endif +#include <linux/adb.h> +#include <linux/cuda.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/pgtable.h> -#include <asm/adb.h> -#include <asm/cuda.h> #include <video/fbcon.h> #include <video/fbcon-cfb8.h> @@ -168,10 +168,6 @@ static int valkyriefb_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); - -__openfirmware - - static int valkyrie_open(struct fb_info *info, int user) { MOD_INC_USE_COUNT; diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index bb98bd8c7..cf4f9baa9 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -532,6 +532,8 @@ int __init vesafb_init(void) video_base = screen_info.lfb_base; video_bpp = screen_info.lfb_depth; + if (15 == video_bpp) + video_bpp = 16; video_width = screen_info.lfb_width; video_height = screen_info.lfb_height; video_linelength = screen_info.lfb_linelength; diff --git a/drivers/video/vga_font.c b/drivers/video/vga_font.c index d6683ee4b..bed9a5e95 100644 --- a/drivers/video/vga_font.c +++ b/drivers/video/vga_font.c @@ -1,6 +1,5 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/modversions.h> #define cmapsz 8192 diff --git a/drivers/video/virgefb.c b/drivers/video/virgefb.c index 1e9abfe3d..050d34543 100644 --- a/drivers/video/virgefb.c +++ b/drivers/video/virgefb.c @@ -1163,7 +1163,7 @@ int __init virgefb_setup(char *options) * Initialization */ -void __init virgefb_init(void) +int __init virgefb_init(void) { struct virgefb_par par; unsigned long board_addr; |