diff options
-rw-r--r-- | drivers/scsi/Kconfig | 13 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 3 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTS.h | 19 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSchip.h | 238 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTScontrol.c | 2231 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSi2c.c | 493 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSinit.c | 2096 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSioctl.h | 94 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSstructs.h | 1530 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTStrigger.c | 33 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTStrigger.h | 8 | ||||
-rw-r--r-- | drivers/scsi/cpqfcTSworker.c | 6516 |
12 files changed, 0 insertions, 13274 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 78c33180ebed..afeca325b4dc 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig | |||
@@ -620,19 +620,6 @@ config SCSI_OMIT_FLASHPOINT | |||
620 | substantial, so users of MultiMaster Host Adapters may wish to omit | 620 | substantial, so users of MultiMaster Host Adapters may wish to omit |
621 | it. | 621 | it. |
622 | 622 | ||
623 | # | ||
624 | # This is marked broken because it uses over 4kB of stack in | ||
625 | # just two routines: | ||
626 | # 2076 CpqTsProcessIMQEntry | ||
627 | # 2052 PeekIMQEntry | ||
628 | # | ||
629 | config SCSI_CPQFCTS | ||
630 | tristate "Compaq Fibre Channel 64-bit/66Mhz HBA support" | ||
631 | depends on PCI && SCSI && BROKEN | ||
632 | help | ||
633 | Say Y here to compile in support for the Compaq StorageWorks Fibre | ||
634 | Channel 64-bit/66Mhz Host Bus Adapter. | ||
635 | |||
636 | config SCSI_DMX3191D | 623 | config SCSI_DMX3191D |
637 | tristate "DMX3191D SCSI support" | 624 | tristate "DMX3191D SCSI support" |
638 | depends on PCI && SCSI | 625 | depends on PCI && SCSI |
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 8dfb9884afe0..b88b8c455598 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile | |||
@@ -120,7 +120,6 @@ obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o | |||
120 | obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o | 120 | obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o |
121 | obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o | 121 | obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o |
122 | obj-$(CONFIG_SCSI_FCAL) += fcal.o | 122 | obj-$(CONFIG_SCSI_FCAL) += fcal.o |
123 | obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o | ||
124 | obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o | 123 | obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o |
125 | obj-$(CONFIG_SCSI_NSP32) += nsp32.o | 124 | obj-$(CONFIG_SCSI_NSP32) += nsp32.o |
126 | obj-$(CONFIG_SCSI_IPR) += ipr.o | 125 | obj-$(CONFIG_SCSI_IPR) += ipr.o |
@@ -165,8 +164,6 @@ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ | |||
165 | CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m) | 164 | CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m) |
166 | zalon7xx-objs := zalon.o ncr53c8xx.o | 165 | zalon7xx-objs := zalon.o ncr53c8xx.o |
167 | NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o | 166 | NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o |
168 | cpqfc-objs := cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o \ | ||
169 | cpqfcTSworker.o cpqfcTStrigger.o | ||
170 | libata-objs := libata-core.o libata-scsi.o | 167 | libata-objs := libata-core.o libata-scsi.o |
171 | 168 | ||
172 | # Files generated that shall be removed upon make clean | 169 | # Files generated that shall be removed upon make clean |
diff --git a/drivers/scsi/cpqfcTS.h b/drivers/scsi/cpqfcTS.h deleted file mode 100644 index 7ce53d88cb96..000000000000 --- a/drivers/scsi/cpqfcTS.h +++ /dev/null | |||
@@ -1,19 +0,0 @@ | |||
1 | #ifndef CPQFCTS_H | ||
2 | #define CPQFCTS_H | ||
3 | #include "cpqfcTSstructs.h" | ||
4 | |||
5 | // These functions are required by the Linux SCSI layers | ||
6 | extern int cpqfcTS_detect(Scsi_Host_Template *); | ||
7 | extern int cpqfcTS_release(struct Scsi_Host *); | ||
8 | extern const char * cpqfcTS_info(struct Scsi_Host *); | ||
9 | extern int cpqfcTS_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); | ||
10 | extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); | ||
11 | extern int cpqfcTS_abort(Scsi_Cmnd *); | ||
12 | extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int); | ||
13 | extern int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd); | ||
14 | extern int cpqfcTS_eh_device_reset(Scsi_Cmnd *); | ||
15 | extern int cpqfcTS_biosparam(struct scsi_device *, struct block_device *, | ||
16 | sector_t, int[]); | ||
17 | extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg); | ||
18 | |||
19 | #endif /* CPQFCTS_H */ | ||
diff --git a/drivers/scsi/cpqfcTSchip.h b/drivers/scsi/cpqfcTSchip.h deleted file mode 100644 index 14b83373861f..000000000000 --- a/drivers/scsi/cpqfcTSchip.h +++ /dev/null | |||
@@ -1,238 +0,0 @@ | |||
1 | /* Copyright(c) 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter | ||
3 | * 64-bit, 66MHz PCI | ||
4 | * Originally developed and tested on: | ||
5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
6 | * SP# P225CXCBFIEL6T, Rev XC | ||
7 | * SP# 161290-001, Rev XD | ||
8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2, or (at your option) any | ||
13 | * later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * Written by Don Zimmerman | ||
20 | */ | ||
21 | #ifndef CPQFCTSCHIP_H | ||
22 | #define CPQFCTSCHIP_H | ||
23 | #ifndef TACHYON_CHIP_INC | ||
24 | |||
25 | // FC-PH (Physical) specification levels for Login payloads | ||
26 | // NOTE: These are NOT strictly complied with by any FC vendors | ||
27 | |||
28 | #define FC_PH42 0x08 | ||
29 | #define FC_PH43 0x09 | ||
30 | #define FC_PH3 0x20 | ||
31 | |||
32 | #define TACHLITE_TS_RX_SIZE 1024 // max inbound frame size | ||
33 | // "I" prefix is for Include | ||
34 | |||
35 | #define IVENDID 0x00 // word | ||
36 | #define IDEVID 0x02 | ||
37 | #define ITLCFGCMD 0x04 | ||
38 | #define IMEMBASE 0x18 // Tachyon | ||
39 | #define ITLMEMBASE 0x1C // Tachlite | ||
40 | #define IIOBASEL 0x10 // Tachyon I/O base address, lower 256 bytes | ||
41 | #define IIOBASEU 0x14 // Tachyon I/O base address, upper 256 bytes | ||
42 | #define ITLIOBASEL 0x14 // TachLite I/O base address, lower 256 bytes | ||
43 | #define ITLIOBASEU 0x18 // TachLite I/O base address, upper 256 bytes | ||
44 | #define ITLRAMBASE 0x20 // TL on-board RAM start | ||
45 | #define ISROMBASE 0x24 | ||
46 | #define IROMBASE 0x30 | ||
47 | |||
48 | #define ICFGCMD 0x04 // PCI config - PCI config access (word) | ||
49 | #define ICFGSTAT 0x06 // PCI status (R - word) | ||
50 | #define IRCTR_WCTR 0x1F2 // ROM control / pre-fetch wait counter | ||
51 | #define IPCIMCTR 0x1F3 // PCI master control register | ||
52 | #define IINTPEND 0x1FD // Interrupt pending (I/O Upper - Tachyon & TL) | ||
53 | #define IINTEN 0x1FE // Interrupt enable (I/O Upper - Tachyon & TL) | ||
54 | #define IINTSTAT 0x1FF // Interrupt status (I/O Upper - Tachyon & TL) | ||
55 | |||
56 | #define IMQ_BASE 0x80 | ||
57 | #define IMQ_LENGTH 0x84 | ||
58 | #define IMQ_CONSUMER_INDEX 0x88 | ||
59 | #define IMQ_PRODUCER_INDEX 0x8C // Tach copies its INDX to bits 0-7 of value | ||
60 | |||
61 | /* | ||
62 | // IOBASE UPPER | ||
63 | #define SFSBQ_BASE 0x00 // single-frame sequences | ||
64 | #define SFSBQ_LENGTH 0x04 | ||
65 | #define SFSBQ_PRODUCER_INDEX 0x08 | ||
66 | #define SFSBQ_CONSUMER_INDEX 0x0C // (R) | ||
67 | #define SFS_BUFFER_LENGTH 0X10 | ||
68 | // SCSI-FCP hardware assists | ||
69 | #define SEST_BASE 0x40 // SSCI Exchange State Table | ||
70 | #define SEST_LENGTH 0x44 | ||
71 | #define SCSI_BUFFER_LENGTH 0x48 | ||
72 | #define SEST_LINKED_LIST 0x4C | ||
73 | |||
74 | #define TACHYON_My_ID 0x6C | ||
75 | #define TACHYON_CONFIGURATION 0x84 // (R/W) reset val 2 | ||
76 | #define TACHYON_CONTROL 0x88 | ||
77 | #define TACHYON_STATUS 0x8C // (R) | ||
78 | #define TACHYON_FLUSH_SEST 0x90 // (R/W) | ||
79 | #define TACHYON_EE_CREDIT_TMR 0x94 // (R) | ||
80 | #define TACHYON_BB_CREDIT_TMR 0x98 // (R) | ||
81 | #define TACHYON_RCV_FRAME_ERR 0x9C // (R) | ||
82 | #define FRAME_MANAGER_CONFIG 0xC0 // (R/W) | ||
83 | #define FRAME_MANAGER_CONTROL 0xC4 | ||
84 | #define FRAME_MANAGER_STATUS 0xC8 // (R) | ||
85 | #define FRAME_MANAGER_ED_TOV 0xCC | ||
86 | #define FRAME_MANAGER_LINK_ERR1 0xD0 // (R) | ||
87 | #define FRAME_MANAGER_LINK_ERR2 0xD4 // (R) | ||
88 | #define FRAME_MANAGER_TIMEOUT2 0xD8 // (W) | ||
89 | #define FRAME_MANAGER_BB_CREDIT 0xDC // (R) | ||
90 | #define FRAME_MANAGER_WWN_HI 0xE0 // (R/W) | ||
91 | #define FRAME_MANAGER_WWN_LO 0xE4 // (R/W) | ||
92 | #define FRAME_MANAGER_RCV_AL_PA 0xE8 // (R) | ||
93 | #define FRAME_MANAGER_PRIMITIVE 0xEC // {K28.5} byte1 byte2 byte3 | ||
94 | */ | ||
95 | |||
96 | #define TL_MEM_ERQ_BASE 0x0 //ERQ Base | ||
97 | #define TL_IO_ERQ_BASE 0x0 //ERQ base | ||
98 | |||
99 | #define TL_MEM_ERQ_LENGTH 0x4 //ERQ Length | ||
100 | #define TL_IO_ERQ_LENGTH 0x4 //ERQ Length | ||
101 | |||
102 | #define TL_MEM_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register | ||
103 | #define TL_IO_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register | ||
104 | |||
105 | #define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register | ||
106 | #define TL_IO_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register | ||
107 | |||
108 | #define TL_MEM_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index | ||
109 | #define TL_IO_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index | ||
110 | |||
111 | #define TL_MEM_SFQ_BASE 0x50 //SFQ Base | ||
112 | #define TL_IO_SFQ_BASE 0x50 //SFQ base | ||
113 | |||
114 | #define TL_MEM_SFQ_LENGTH 0x54 //SFQ Length | ||
115 | #define TL_IO_SFQ_LENGTH 0x54 //SFQ Length | ||
116 | |||
117 | #define TL_MEM_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index | ||
118 | #define TL_IO_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index | ||
119 | |||
120 | #define TL_MEM_IMQ_BASE 0x80 //IMQ Base | ||
121 | #define TL_IO_IMQ_BASE 0x80 //IMQ base | ||
122 | |||
123 | #define TL_MEM_IMQ_LENGTH 0x84 //IMQ Length | ||
124 | #define TL_IO_IMQ_LENGTH 0x84 //IMQ Length | ||
125 | |||
126 | #define TL_MEM_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index | ||
127 | #define TL_IO_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index | ||
128 | |||
129 | #define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register | ||
130 | #define TL_IO_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register | ||
131 | |||
132 | #define TL_MEM_SEST_BASE 0x140 //SFQ Base | ||
133 | #define TL_IO_SEST_BASE 0x40 //SFQ base | ||
134 | |||
135 | #define TL_MEM_SEST_LENGTH 0x144 //SFQ Length | ||
136 | #define TL_IO_SEST_LENGTH 0x44 //SFQ Length | ||
137 | |||
138 | #define TL_MEM_SEST_LINKED_LIST 0x14C | ||
139 | |||
140 | #define TL_MEM_SEST_SG_PAGE 0x168 // Extended Scatter/Gather page size | ||
141 | |||
142 | #define TL_MEM_TACH_My_ID 0x16C | ||
143 | #define TL_IO_TACH_My_ID 0x6C //My AL_PA ID | ||
144 | |||
145 | #define TL_MEM_TACH_CONFIG 0x184 //Tachlite Configuration register | ||
146 | #define TL_IO_CONFIG 0x84 //Tachlite Configuration register | ||
147 | |||
148 | #define TL_MEM_TACH_CONTROL 0x188 //Tachlite Control register | ||
149 | #define TL_IO_CTR 0x88 //Tachlite Control register | ||
150 | |||
151 | #define TL_MEM_TACH_STATUS 0x18C //Tachlite Status register | ||
152 | #define TL_IO_STAT 0x8C //Tachlite Status register | ||
153 | |||
154 | #define TL_MEM_FM_CONFIG 0x1C0 //Frame Manager Configuration register | ||
155 | #define TL_IO_FM_CONFIG 0xC0 //Frame Manager Configuration register | ||
156 | |||
157 | #define TL_MEM_FM_CONTROL 0x1C4 //Frame Manager Control | ||
158 | #define TL_IO_FM_CTL 0xC4 //Frame Manager Control | ||
159 | |||
160 | #define TL_MEM_FM_STATUS 0x1C8 //Frame Manager Status | ||
161 | #define TL_IO_FM_STAT 0xC8 //Frame Manager Status | ||
162 | |||
163 | #define TL_MEM_FM_LINK_STAT1 0x1D0 //Frame Manager Link Status 1 | ||
164 | #define TL_IO_FM_LINK_STAT1 0xD0 //Frame Manager Link Status 1 | ||
165 | |||
166 | #define TL_MEM_FM_LINK_STAT2 0x1D4 //Frame Manager Link Status 2 | ||
167 | #define TL_IO_FM_LINK_STAT2 0xD4 //Frame Manager Link Status 2 | ||
168 | |||
169 | #define TL_MEM_FM_TIMEOUT2 0x1D8 // (W) | ||
170 | |||
171 | #define TL_MEM_FM_BB_CREDIT0 0x1DC | ||
172 | |||
173 | #define TL_MEM_FM_WWN_HI 0x1E0 //Frame Manager World Wide Name High | ||
174 | #define TL_IO_FM_WWN_HI 0xE0 //Frame Manager World Wide Name High | ||
175 | |||
176 | #define TL_MEM_FM_WWN_LO 0x1E4 //Frame Manager World Wide Name LOW | ||
177 | #define TL_IO_FM_WWN_LO 0xE4 //Frame Manager World Wide Name Low | ||
178 | |||
179 | #define TL_MEM_FM_RCV_AL_PA 0x1E8 //Frame Manager AL_PA Received register | ||
180 | #define TL_IO_FM_ALPA 0xE8 //Frame Manager AL_PA Received register | ||
181 | |||
182 | #define TL_MEM_FM_ED_TOV 0x1CC | ||
183 | |||
184 | #define TL_IO_ROMCTR 0xFA //TL PCI ROM Control Register | ||
185 | #define TL_IO_PCIMCTR 0xFB //TL PCI Master Control Register | ||
186 | #define TL_IO_SOFTRST 0xFC //Tachlite Configuration register | ||
187 | #define TL_MEM_SOFTRST 0x1FC //Tachlite Configuration register | ||
188 | |||
189 | // completion message types (bit 8 set means Interrupt generated) | ||
190 | // CM_Type | ||
191 | #define OUTBOUND_COMPLETION 0 | ||
192 | #define ERROR_IDLE_COMPLETION 0x01 | ||
193 | #define OUT_HI_PRI_COMPLETION 0x01 | ||
194 | #define INBOUND_MFS_COMPLETION 0x02 | ||
195 | #define INBOUND_000_COMPLETION 0x03 | ||
196 | #define INBOUND_SFS_COMPLETION 0x04 // Tachyon & TachLite | ||
197 | #define ERQ_FROZEN_COMPLETION 0x06 // TachLite | ||
198 | #define INBOUND_C1_TIMEOUT 0x05 | ||
199 | #define INBOUND_BUSIED_FRAME 0x06 | ||
200 | #define SFS_BUF_WARN 0x07 | ||
201 | #define FCP_FROZEN_COMPLETION 0x07 // TachLite | ||
202 | #define MFS_BUF_WARN 0x08 | ||
203 | #define IMQ_BUF_WARN 0x09 | ||
204 | #define FRAME_MGR_INTERRUPT 0x0A | ||
205 | #define READ_STATUS 0x0B | ||
206 | #define INBOUND_SCSI_DATA_COMPLETION 0x0C | ||
207 | #define INBOUND_FCP_XCHG_COMPLETION 0x0C // TachLite | ||
208 | #define INBOUND_SCSI_DATA_COMMAND 0x0D | ||
209 | #define BAD_SCSI_FRAME 0x0E | ||
210 | #define INB_SCSI_STATUS_COMPLETION 0x0F | ||
211 | #define BUFFER_PROCESSED_COMPLETION 0x11 | ||
212 | |||
213 | // FC-AL (Tachyon) Loop Port State Machine defs | ||
214 | // (loop "Up" states) | ||
215 | #define MONITORING 0x0 | ||
216 | #define ARBITRATING 0x1 | ||
217 | #define ARBITRAT_WON 0x2 | ||
218 | #define OPEN 0x3 | ||
219 | #define OPENED 0x4 | ||
220 | #define XMITTD_CLOSE 0x5 | ||
221 | #define RCVD_CLOSE 0x6 | ||
222 | #define TRANSFER 0x7 | ||
223 | |||
224 | // (loop "Down" states) | ||
225 | #define INITIALIZING 0x8 | ||
226 | #define O_I_INIT 0x9 | ||
227 | #define O_I_PROTOCOL 0xa | ||
228 | #define O_I_LIP_RCVD 0xb | ||
229 | #define HOST_CONTROL 0xc | ||
230 | #define LOOP_FAIL 0xd | ||
231 | // (no 0xe) | ||
232 | #define OLD_PORT 0xf | ||
233 | |||
234 | |||
235 | |||
236 | #define TACHYON_CHIP_INC | ||
237 | #endif | ||
238 | #endif /* CPQFCTSCHIP_H */ | ||
diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c deleted file mode 100644 index bd94c70f473d..000000000000 --- a/drivers/scsi/cpqfcTScontrol.c +++ /dev/null | |||
@@ -1,2231 +0,0 @@ | |||
1 | /* Copyright 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter | ||
3 | * 64-bit, 66MHz PCI | ||
4 | * Originally developed and tested on: | ||
5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
6 | * SP# P225CXCBFIEL6T, Rev XC | ||
7 | * SP# 161290-001, Rev XD | ||
8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2, or (at your option) any | ||
13 | * later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * Written by Don Zimmerman | ||
20 | */ | ||
21 | /* These functions control the host bus adapter (HBA) hardware. The main chip | ||
22 | control takes place in the interrupt handler where we process the IMQ | ||
23 | (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link | ||
24 | events and state information to the driver. The Single Frame Queue (SFQ) | ||
25 | buffers incoming FC frames for processing by the driver. References to | ||
26 | "TL/TS UG" are for: | ||
27 | "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed. | ||
28 | Hewlitt Packard Manual Part Number 5968-1083E. | ||
29 | */ | ||
30 | |||
31 | #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) | ||
32 | |||
33 | #include <linux/blkdev.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/string.h> | ||
36 | #include <linux/ioport.h> // request_region() prototype | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/slab.h> // need "kfree" for ext. S/G pages | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/unistd.h> | ||
43 | #include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O | ||
44 | #include <asm/irq.h> | ||
45 | #include <linux/spinlock.h> | ||
46 | |||
47 | #include "scsi.h" | ||
48 | #include <scsi/scsi_host.h> // Scsi_Host definition for INT handler | ||
49 | #include "cpqfcTSchip.h" | ||
50 | #include "cpqfcTSstructs.h" | ||
51 | |||
52 | //#define IMQ_DEBUG 1 | ||
53 | |||
54 | static void fcParseLinkStatusCounters(TACHYON * fcChip); | ||
55 | static void CpqTsGetSFQEntry(TACHYON * fcChip, | ||
56 | USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); | ||
57 | |||
58 | static void | ||
59 | cpqfc_free_dma_consistent(CPQFCHBA *cpqfcHBAdata) | ||
60 | { | ||
61 | // free up the primary EXCHANGES struct and Link Q | ||
62 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
63 | |||
64 | if (fcChip->Exchanges != NULL) | ||
65 | pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_EXCHANGES), | ||
66 | fcChip->Exchanges, fcChip->exch_dma_handle); | ||
67 | fcChip->Exchanges = NULL; | ||
68 | if (cpqfcHBAdata->fcLQ != NULL) | ||
69 | pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_LINK_QUE), | ||
70 | cpqfcHBAdata->fcLQ, cpqfcHBAdata->fcLQ_dma_handle); | ||
71 | cpqfcHBAdata->fcLQ = NULL; | ||
72 | } | ||
73 | |||
74 | // Note special requirements for Q alignment! (TL/TS UG pg. 190) | ||
75 | // We place critical index pointers at end of QUE elements to assist | ||
76 | // in non-symbolic (i.e. memory dump) debugging | ||
77 | // opcode defines placement of Queues (e.g. local/external RAM) | ||
78 | |||
79 | int CpqTsCreateTachLiteQues( void* pHBA, int opcode) | ||
80 | { | ||
81 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | ||
82 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
83 | |||
84 | int iStatus=0; | ||
85 | unsigned long ulAddr; | ||
86 | dma_addr_t ERQdma, IMQdma, SPQdma, SESTdma; | ||
87 | int i; | ||
88 | |||
89 | // NOTE! fcMemManager() will return system virtual addresses. | ||
90 | // System (kernel) virtual addresses, though non-paged, still | ||
91 | // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's | ||
92 | // DMA use. | ||
93 | ENTER("CreateTachLiteQues"); | ||
94 | |||
95 | |||
96 | // Allocate primary EXCHANGES array... | ||
97 | fcChip->Exchanges = NULL; | ||
98 | cpqfcHBAdata->fcLQ = NULL; | ||
99 | |||
100 | /* printk("Allocating %u for %u Exchanges ", | ||
101 | (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); */ | ||
102 | fcChip->Exchanges = pci_alloc_consistent(cpqfcHBAdata->PciDev, | ||
103 | sizeof(FC_EXCHANGES), &fcChip->exch_dma_handle); | ||
104 | /* printk("@ %p\n", fcChip->Exchanges); */ | ||
105 | |||
106 | if( fcChip->Exchanges == NULL ) // fatal error!! | ||
107 | { | ||
108 | printk("pci_alloc_consistent failure on Exchanges: fatal error\n"); | ||
109 | return -1; | ||
110 | } | ||
111 | // zero out the entire EXCHANGE space | ||
112 | memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES)); | ||
113 | |||
114 | |||
115 | /* printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); */ | ||
116 | cpqfcHBAdata->fcLQ = pci_alloc_consistent(cpqfcHBAdata->PciDev, | ||
117 | sizeof( FC_LINK_QUE), &cpqfcHBAdata->fcLQ_dma_handle); | ||
118 | /* printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); */ | ||
119 | |||
120 | if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!! | ||
121 | { | ||
122 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
123 | printk("pci_alloc_consistent() failure on fc Link Que: fatal error\n"); | ||
124 | return -1; | ||
125 | } | ||
126 | // zero out the entire EXCHANGE space | ||
127 | memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); | ||
128 | |||
129 | // Verify that basic Tach I/O registers are not NULL | ||
130 | if( !fcChip->Registers.ReMapMemBase ) | ||
131 | { | ||
132 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
133 | printk("HBA base address NULL: fatal error\n"); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | |||
138 | // Initialize the fcMemManager memory pairs (stores allocated/aligned | ||
139 | // pairs for future freeing) | ||
140 | memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem)); | ||
141 | |||
142 | |||
143 | // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes) | ||
144 | |||
145 | fcChip->ERQ = fcMemManager( cpqfcHBAdata->PciDev, | ||
146 | &cpqfcHBAdata->dynamic_mem[0], | ||
147 | sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L, &ERQdma); | ||
148 | if( !fcChip->ERQ ) | ||
149 | { | ||
150 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
151 | printk("pci_alloc_consistent/alignment failure on ERQ: fatal error\n"); | ||
152 | return -1; | ||
153 | } | ||
154 | fcChip->ERQ->length = ERQ_LEN-1; | ||
155 | ulAddr = (ULONG) ERQdma; | ||
156 | #if BITS_PER_LONG > 32 | ||
157 | if( (ulAddr >> 32) ) | ||
158 | { | ||
159 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
160 | printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n", | ||
161 | (void*)ulAddr); | ||
162 | return -1; // failed | ||
163 | } | ||
164 | #endif | ||
165 | fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference | ||
166 | |||
167 | |||
168 | // Allocate Tach's Inbound Message Queue (32 bytes per entry) | ||
169 | |||
170 | fcChip->IMQ = fcMemManager( cpqfcHBAdata->PciDev, | ||
171 | &cpqfcHBAdata->dynamic_mem[0], | ||
172 | sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L, &IMQdma ); | ||
173 | if( !fcChip->IMQ ) | ||
174 | { | ||
175 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
176 | printk("pci_alloc_consistent/alignment failure on IMQ: fatal error\n"); | ||
177 | return -1; | ||
178 | } | ||
179 | fcChip->IMQ->length = IMQ_LEN-1; | ||
180 | |||
181 | ulAddr = IMQdma; | ||
182 | #if BITS_PER_LONG > 32 | ||
183 | if( (ulAddr >> 32) ) | ||
184 | { | ||
185 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
186 | printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", | ||
187 | (void*)ulAddr); | ||
188 | return -1; // failed | ||
189 | } | ||
190 | #endif | ||
191 | fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference | ||
192 | |||
193 | |||
194 | // Allocate Tach's Single Frame Queue (64 bytes per entry) | ||
195 | fcChip->SFQ = fcMemManager( cpqfcHBAdata->PciDev, | ||
196 | &cpqfcHBAdata->dynamic_mem[0], | ||
197 | sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L, &SPQdma ); | ||
198 | if( !fcChip->SFQ ) | ||
199 | { | ||
200 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
201 | printk("pci_alloc_consistent/alignment failure on SFQ: fatal error\n"); | ||
202 | return -1; | ||
203 | } | ||
204 | fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries - | ||
205 | // min. 32; max. 4096 (0xffff)] | ||
206 | |||
207 | ulAddr = SPQdma; | ||
208 | #if BITS_PER_LONG > 32 | ||
209 | if( (ulAddr >> 32) ) | ||
210 | { | ||
211 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
212 | printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", | ||
213 | (void*)ulAddr); | ||
214 | return -1; // failed | ||
215 | } | ||
216 | #endif | ||
217 | fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference | ||
218 | |||
219 | |||
220 | // Allocate SCSI Exchange State Table; aligned nearest @sizeof | ||
221 | // power-of-2 boundary | ||
222 | // LIVE DANGEROUSLY! Assume the boundary for SEST mem will | ||
223 | // be on physical page (e.g. 4k) boundary. | ||
224 | /* printk("Allocating %u for TachSEST for %u Exchanges\n", | ||
225 | (ULONG)sizeof(TachSEST), TACH_SEST_LEN); */ | ||
226 | fcChip->SEST = fcMemManager( cpqfcHBAdata->PciDev, | ||
227 | &cpqfcHBAdata->dynamic_mem[0], | ||
228 | sizeof(TachSEST), 4, 0L, &SESTdma ); | ||
229 | // sizeof(TachSEST), 64*TACH_SEST_LEN, 0L ); | ||
230 | if( !fcChip->SEST ) | ||
231 | { | ||
232 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
233 | printk("pci_alloc_consistent/alignment failure on SEST: fatal error\n"); | ||
234 | return -1; | ||
235 | } | ||
236 | |||
237 | for( i=0; i < TACH_SEST_LEN; i++) // for each exchange | ||
238 | fcChip->SEST->sgPages[i] = NULL; | ||
239 | |||
240 | fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one | ||
241 | // (TL/TS UG, pg 153) | ||
242 | |||
243 | ulAddr = SESTdma; | ||
244 | #if BITS_PER_LONG > 32 | ||
245 | if( (ulAddr >> 32) ) | ||
246 | { | ||
247 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
248 | printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", | ||
249 | (void*)ulAddr); | ||
250 | return -1; // failed | ||
251 | } | ||
252 | #endif | ||
253 | fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference | ||
254 | |||
255 | |||
256 | // Now that structures are defined, | ||
257 | // fill in Tachyon chip registers... | ||
258 | |||
259 | // EEEEEEEE EXCHANGE REQUEST QUEUE | ||
260 | |||
261 | writel( fcChip->ERQ->base, | ||
262 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); | ||
263 | |||
264 | writel( fcChip->ERQ->length, | ||
265 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH)); | ||
266 | |||
267 | |||
268 | fcChip->ERQ->producerIndex = 0L; | ||
269 | writel( fcChip->ERQ->producerIndex, | ||
270 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX)); | ||
271 | |||
272 | |||
273 | // NOTE! write consumer index last, since the write | ||
274 | // causes Tachyon to process the other registers | ||
275 | |||
276 | ulAddr = ((unsigned long)&fcChip->ERQ->consumerIndex - | ||
277 | (unsigned long)fcChip->ERQ) + (unsigned long) ERQdma; | ||
278 | |||
279 | // NOTE! Tachyon DMAs to the ERQ consumer Index host | ||
280 | // address; must be correctly aligned | ||
281 | writel( (ULONG)ulAddr, | ||
282 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR)); | ||
283 | |||
284 | |||
285 | |||
286 | // IIIIIIIIIIIII INBOUND MESSAGE QUEUE | ||
287 | // Tell Tachyon where the Que starts | ||
288 | |||
289 | // set the Host's pointer for Tachyon to access | ||
290 | |||
291 | /* printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base ); */ | ||
292 | writel( fcChip->IMQ->base, | ||
293 | (fcChip->Registers.ReMapMemBase + IMQ_BASE)); | ||
294 | |||
295 | writel( fcChip->IMQ->length, | ||
296 | (fcChip->Registers.ReMapMemBase + IMQ_LENGTH)); | ||
297 | |||
298 | writel( fcChip->IMQ->consumerIndex, | ||
299 | (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); | ||
300 | |||
301 | |||
302 | // NOTE: TachLite DMAs to the producerIndex host address | ||
303 | // must be correctly aligned with address bits 1-0 cleared | ||
304 | // Writing the BASE register clears the PI register, so write it last | ||
305 | ulAddr = ((unsigned long)&fcChip->IMQ->producerIndex - | ||
306 | (unsigned long)fcChip->IMQ) + (unsigned long) IMQdma; | ||
307 | |||
308 | #if BITS_PER_LONG > 32 | ||
309 | if( (ulAddr >> 32) ) | ||
310 | { | ||
311 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
312 | printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", | ||
313 | (void*)ulAddr); | ||
314 | return -1; // failed | ||
315 | } | ||
316 | #endif | ||
317 | #if DBG | ||
318 | printk(" PI %Xh\n", (ULONG)ulAddr ); | ||
319 | #endif | ||
320 | writel( (ULONG)ulAddr, | ||
321 | (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX)); | ||
322 | |||
323 | |||
324 | |||
325 | // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE | ||
326 | // Tell TachLite where the Que starts | ||
327 | |||
328 | writel( fcChip->SFQ->base, | ||
329 | (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE)); | ||
330 | |||
331 | writel( fcChip->SFQ->length, | ||
332 | (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH)); | ||
333 | |||
334 | |||
335 | // tell TachLite where SEST table is & how long | ||
336 | writel( fcChip->SEST->base, | ||
337 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE)); | ||
338 | |||
339 | /* printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n", | ||
340 | fcChip->SEST, fcChip->SEST->base, | ||
341 | fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); */ | ||
342 | |||
343 | writel( fcChip->SEST->length, | ||
344 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH)); | ||
345 | |||
346 | writel( (TL_EXT_SG_PAGE_COUNT-1), | ||
347 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE)); | ||
348 | |||
349 | |||
350 | LEAVE("CreateTachLiteQues"); | ||
351 | |||
352 | return iStatus; | ||
353 | } | ||
354 | |||
355 | |||
356 | |||
357 | // function to return TachLite to Power On state | ||
358 | // 1st - reset tachyon ('SOFT' reset) | ||
359 | // others - future | ||
360 | |||
361 | int CpqTsResetTachLite(void *pHBA, int type) | ||
362 | { | ||
363 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | ||
364 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
365 | ULONG ulBuff, i; | ||
366 | int ret_status=0; // def. success | ||
367 | |||
368 | ENTER("ResetTach"); | ||
369 | |||
370 | switch(type) | ||
371 | { | ||
372 | |||
373 | case CLEAR_FCPORTS: | ||
374 | |||
375 | // in case he was running previously, mask Tach's interrupt | ||
376 | writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); | ||
377 | |||
378 | // de-allocate mem for any Logged in ports | ||
379 | // (e.g., our module is unloading) | ||
380 | // search the forward linked list, de-allocating | ||
381 | // the memory we allocated when the port was initially logged in | ||
382 | { | ||
383 | PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort; | ||
384 | PFC_LOGGEDIN_PORT ptr; | ||
385 | // printk("checking for allocated LoggedInPorts...\n"); | ||
386 | |||
387 | while( pLoggedInPort ) | ||
388 | { | ||
389 | ptr = pLoggedInPort; | ||
390 | pLoggedInPort = ptr->pNextPort; | ||
391 | // printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n", | ||
392 | // ptr, ptr->port_id); | ||
393 | kfree( ptr ); | ||
394 | } | ||
395 | } | ||
396 | // (continue resetting hardware...) | ||
397 | |||
398 | case 1: // RESTART Tachyon (power-up state) | ||
399 | |||
400 | // in case he was running previously, mask Tach's interrupt | ||
401 | writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); | ||
402 | // turn OFF laser (NOTE: laser is turned | ||
403 | // off during reset, because GPIO4 is cleared | ||
404 | // to 0 by reset action - see TLUM, sec 7.22) | ||
405 | // However, CPQ 64-bit HBAs have a "health | ||
406 | // circuit" which keeps laser ON for a brief | ||
407 | // period after it is turned off ( < 1s) | ||
408 | |||
409 | fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0); | ||
410 | |||
411 | |||
412 | |||
413 | // soft reset timing constraints require: | ||
414 | // 1. set RST to 1 | ||
415 | // 2. read SOFTRST register | ||
416 | // (128 times per R. Callison code) | ||
417 | // 3. clear PCI ints | ||
418 | // 4. clear RST to 0 | ||
419 | writel( 0xff000001L, | ||
420 | (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); | ||
421 | |||
422 | for( i=0; i<128; i++) | ||
423 | ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST); | ||
424 | |||
425 | // clear the soft reset | ||
426 | for( i=0; i<8; i++) | ||
427 | writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); | ||
428 | |||
429 | |||
430 | |||
431 | // clear out our copy of Tach regs, | ||
432 | // because they must be invalid now, | ||
433 | // since TachLite reset all his regs. | ||
434 | CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs | ||
435 | cpqfcTSClearLinkStatusCounters(fcChip); // clear our s/w accumulators | ||
436 | // lower bits give GBIC info | ||
437 | fcChip->Registers.TYstatus.value = | ||
438 | readl( fcChip->Registers.TYstatus.address ); | ||
439 | break; | ||
440 | |||
441 | /* | ||
442 | case 2: // freeze SCSI | ||
443 | case 3: // reset Outbound command que (ERQ) | ||
444 | case 4: // unfreeze OSM (Outbound Seq. Man.) 'er' | ||
445 | case 5: // report status | ||
446 | |||
447 | break; | ||
448 | */ | ||
449 | default: | ||
450 | ret_status = -1; // invalid option passed to RESET function | ||
451 | break; | ||
452 | } | ||
453 | LEAVE("ResetTach"); | ||
454 | return ret_status; | ||
455 | } | ||
456 | |||
457 | |||
458 | |||
459 | |||
460 | |||
461 | |||
462 | // 'addrBase' is IOBaseU for both TachLite and (older) Tachyon | ||
463 | int CpqTsLaserControl( void* addrBase, int opcode ) | ||
464 | { | ||
465 | ULONG dwBuff; | ||
466 | |||
467 | dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg | ||
468 | // (change only bit 4) | ||
469 | if( opcode == 1) | ||
470 | dwBuff |= ~0xffffffefL; // set - ON | ||
471 | else | ||
472 | dwBuff &= 0xffffffefL; // clear - OFF | ||
473 | writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | |||
478 | |||
479 | |||
480 | |||
481 | // Use controller's "Options" field to determine loopback mode (if any) | ||
482 | // internal loopback (silicon - no GBIC) | ||
483 | // external loopback (GBIC - no FC loop) | ||
484 | // no loopback: L_PORT, external cable from GBIC required | ||
485 | |||
486 | int CpqTsInitializeFrameManager( void *pChip, int opcode) | ||
487 | { | ||
488 | PTACHYON fcChip; | ||
489 | int iStatus; | ||
490 | ULONG wwnLo, wwnHi; // for readback verification | ||
491 | |||
492 | ENTER("InitializeFrameManager"); | ||
493 | fcChip = (PTACHYON)pChip; | ||
494 | if( !fcChip->Registers.ReMapMemBase ) // undefined controller? | ||
495 | return -1; | ||
496 | |||
497 | // TL/TS UG, pg. 184 | ||
498 | // 0x0065 = 100ms for RT_TOV | ||
499 | // 0x01f5 = 500ms for ED_TOV | ||
500 | // 0x07D1 = 2000ms | ||
501 | fcChip->Registers.ed_tov.value = 0x006507D1; | ||
502 | writel( fcChip->Registers.ed_tov.value, | ||
503 | (fcChip->Registers.ed_tov.address)); | ||
504 | |||
505 | |||
506 | // Set LP_TOV to the FC-AL2 specified 2 secs. | ||
507 | // TL/TS UG, pg. 185 | ||
508 | writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); | ||
509 | |||
510 | |||
511 | // Now try to read the WWN from the adapter's NVRAM | ||
512 | iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ | ||
513 | |||
514 | if( iStatus ) // NVRAM read failed? | ||
515 | { | ||
516 | printk(" WARNING! HBA NVRAM WWN read failed - make alias\n"); | ||
517 | // make up a WWN. If NULL or duplicated on loop, FC loop may hang! | ||
518 | |||
519 | |||
520 | fcChip->Registers.wwn_hi = (__u32)jiffies; | ||
521 | fcChip->Registers.wwn_hi |= 0x50000000L; | ||
522 | fcChip->Registers.wwn_lo = 0x44556677L; | ||
523 | } | ||
524 | |||
525 | |||
526 | writel( fcChip->Registers.wwn_hi, | ||
527 | fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI); | ||
528 | |||
529 | writel( fcChip->Registers.wwn_lo, | ||
530 | fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); | ||
531 | |||
532 | |||
533 | // readback for verification: | ||
534 | wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); | ||
535 | |||
536 | wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); | ||
537 | // test for correct chip register WRITE/READ | ||
538 | DEBUG_PCI( printk(" WWN %08X%08X\n", | ||
539 | fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) ); | ||
540 | |||
541 | if( wwnHi != fcChip->Registers.wwn_hi || | ||
542 | wwnLo != fcChip->Registers.wwn_lo ) | ||
543 | { | ||
544 | printk( "cpqfcTS: WorldWideName register load failed\n"); | ||
545 | return -1; // FAILED! | ||
546 | } | ||
547 | |||
548 | |||
549 | |||
550 | // set Frame Manager Initialize command | ||
551 | fcChip->Registers.FMcontrol.value = 0x06; | ||
552 | |||
553 | // Note: for test/debug purposes, we may use "Hard" address, | ||
554 | // but we completely support "soft" addressing, including | ||
555 | // dynamically changing our address. | ||
556 | if( fcChip->Options.intLoopback == 1 ) // internal loopback | ||
557 | fcChip->Registers.FMconfig.value = 0x0f002080L; | ||
558 | else if( fcChip->Options.extLoopback == 1 ) // internal loopback | ||
559 | fcChip->Registers.FMconfig.value = 0x0f004080L; | ||
560 | else // L_Port | ||
561 | fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) | ||
562 | // fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick) | ||
563 | // fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) | ||
564 | |||
565 | // write config to FM | ||
566 | |||
567 | if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback ) | ||
568 | // (also need LASER for real LOOP) | ||
569 | fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER | ||
570 | |||
571 | writel( fcChip->Registers.FMconfig.value, | ||
572 | fcChip->Registers.FMconfig.address); | ||
573 | |||
574 | |||
575 | // issue INITIALIZE command to FM - ACTION! | ||
576 | writel( fcChip->Registers.FMcontrol.value, | ||
577 | fcChip->Registers.FMcontrol.address); | ||
578 | |||
579 | LEAVE("InitializeFrameManager"); | ||
580 | |||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | |||
585 | |||
586 | |||
587 | |||
588 | // This "look ahead" function examines the IMQ for occurrence of | ||
589 | // "type". Returns 1 if found, 0 if not. | ||
590 | static int PeekIMQEntry( PTACHYON fcChip, ULONG type) | ||
591 | { | ||
592 | ULONG CI = fcChip->IMQ->consumerIndex; | ||
593 | ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes | ||
594 | |||
595 | while( CI != PI ) | ||
596 | { // proceed with search | ||
597 | if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check | ||
598 | |||
599 | switch( type ) | ||
600 | { | ||
601 | case ELS_LILP_FRAME: | ||
602 | { | ||
603 | // first, we need to find an Inbound Completion message, | ||
604 | // If we find it, check the incoming frame payload (1st word) | ||
605 | // for LILP frame | ||
606 | if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 ) | ||
607 | { | ||
608 | TachFCHDR_GCMND* fchs; | ||
609 | #error This is too much stack | ||
610 | ULONG ulFibreFrame[2048/4]; // max DWORDS in incoming FC Frame | ||
611 | USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL); | ||
612 | |||
613 | CpqTsGetSFQEntry( fcChip, | ||
614 | SFQpi, // SFQ producer ndx | ||
615 | ulFibreFrame, // contiguous dest. buffer | ||
616 | FALSE); // DON'T update chip--this is a "lookahead" | ||
617 | |||
618 | fchs = (TachFCHDR_GCMND*)&ulFibreFrame; | ||
619 | if( fchs->pl[0] == ELS_LILP_FRAME) | ||
620 | { | ||
621 | return 1; // found the LILP frame! | ||
622 | } | ||
623 | else | ||
624 | { | ||
625 | // keep looking... | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | break; | ||
630 | |||
631 | case OUTBOUND_COMPLETION: | ||
632 | if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 ) | ||
633 | { | ||
634 | |||
635 | // any OCM errors? | ||
636 | if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L ) | ||
637 | return 1; // found OCM error | ||
638 | } | ||
639 | break; | ||
640 | |||
641 | |||
642 | |||
643 | default: | ||
644 | break; | ||
645 | } | ||
646 | } | ||
647 | return 0; // failed to find "type" | ||
648 | } | ||
649 | |||
650 | |||
651 | static void SetTachTOV( CPQFCHBA* cpqfcHBAdata) | ||
652 | { | ||
653 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
654 | |||
655 | // TL/TS UG, pg. 184 | ||
656 | // 0x0065 = 100ms for RT_TOV | ||
657 | // 0x01f5 = 500ms for ED_TOV | ||
658 | // 0x07d1 = 2000ms for ED_TOV | ||
659 | |||
660 | // SANMark Level 1 requires an "initialization backoff" | ||
661 | // (See "SANMark Test Suite Level 1": | ||
662 | // initialization_timeout.fcal.SANMark-1.fc) | ||
663 | // We have to use 2sec, 24sec, then 128sec when login/ | ||
664 | // port discovery processes fail to complete. | ||
665 | |||
666 | // when port discovery completes (logins done), we set | ||
667 | // ED_TOV to 500ms -- this is the normal operational case | ||
668 | // On the first Link Down, we'll move to 2 secs (7D1 ms) | ||
669 | if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5) | ||
670 | fcChip->Registers.ed_tov.value = 0x006507D1; | ||
671 | |||
672 | // If we get another LST after we moved TOV to 2 sec, | ||
673 | // increase to 24 seconds (5DC1 ms) per SANMark! | ||
674 | else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1) | ||
675 | fcChip->Registers.ed_tov.value = 0x00655DC1; | ||
676 | |||
677 | // If we get still another LST, set the max TOV (Tachyon | ||
678 | // has only 16 bits for ms timer, so the max is 65.5 sec) | ||
679 | else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1) | ||
680 | fcChip->Registers.ed_tov.value = 0x0065FFFF; | ||
681 | |||
682 | writel( fcChip->Registers.ed_tov.value, | ||
683 | (fcChip->Registers.ed_tov.address)); | ||
684 | // keep the same 2sec LP_TOV | ||
685 | writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); | ||
686 | } | ||
687 | |||
688 | |||
689 | // The IMQ is an array with IMQ_LEN length, each element (QEntry) | ||
690 | // with eight 32-bit words. Tachyon PRODUCES a QEntry with each | ||
691 | // message it wants to send to the host. The host CONSUMES IMQ entries | ||
692 | |||
693 | // This function copies the current | ||
694 | // (or oldest not-yet-processed) QEntry to | ||
695 | // the caller, clears/ re-enables the interrupt, and updates the | ||
696 | // (Host) Consumer Index. | ||
697 | // Return value: | ||
698 | // 0 message processed, none remain (producer and consumer | ||
699 | // indexes match) | ||
700 | // 1 message processed, more messages remain | ||
701 | // -1 no message processed - none were available to process | ||
702 | // Remarks: | ||
703 | // TL/TS UG specifices that the following actions for | ||
704 | // INTA_L handling: | ||
705 | // 1. read PCI Interrupt Status register (0xff) | ||
706 | // 2. all IMQ messages should be processed before writing the | ||
707 | // IMQ consumer index. | ||
708 | |||
709 | |||
710 | int CpqTsProcessIMQEntry(void *host) | ||
711 | { | ||
712 | struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host; | ||
713 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
714 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
715 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
716 | int iStatus; | ||
717 | USHORT i, RPCset, DPCset; | ||
718 | ULONG x_ID; | ||
719 | ULONG ulBuff, dwStatus; | ||
720 | TachFCHDR_GCMND* fchs; | ||
721 | #error This is too much stack | ||
722 | ULONG ulFibreFrame[2048/4]; // max number of DWORDS in incoming Fibre Frame | ||
723 | UCHAR ucInboundMessageType; // Inbound CM, dword 3 "type" field | ||
724 | |||
725 | ENTER("ProcessIMQEntry"); | ||
726 | |||
727 | |||
728 | // check TachLite's IMQ producer index - | ||
729 | // is a new message waiting for us? | ||
730 | // equal indexes means empty que | ||
731 | |||
732 | if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex ) | ||
733 | { // need to process message | ||
734 | |||
735 | |||
736 | #ifdef IMQ_DEBUG | ||
737 | printk("PI %X, CI %X type: %X\n", | ||
738 | fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex, | ||
739 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type); | ||
740 | #endif | ||
741 | // Examine Completion Messages in IMQ | ||
742 | // what CM_Type? | ||
743 | switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type | ||
744 | & 0xffL) ) | ||
745 | { | ||
746 | case OUTBOUND_COMPLETION: | ||
747 | |||
748 | // Remarks: | ||
749 | // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries | ||
750 | // (starting at 0), and SFS entries (starting at | ||
751 | // SEST_LEN -- outside the SEST space). | ||
752 | // Psuedo code: | ||
753 | // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index | ||
754 | // range check - x_ID | ||
755 | // if x_ID outside 'Transactions' length, error - exit | ||
756 | // if any OCM error, copy error status to Exchange slot | ||
757 | // if FCP ASSIST transaction (x_ID within SEST), | ||
758 | // call fcComplete (to App) | ||
759 | // ... | ||
760 | |||
761 | |||
762 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]; | ||
763 | x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID | ||
764 | // Range check CM OX/RX_ID value... | ||
765 | if( x_ID < TACH_MAX_XID ) // don't go beyond array space | ||
766 | { | ||
767 | |||
768 | |||
769 | if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete? | ||
770 | RPCset = 1; // (SEST transactions only) | ||
771 | else | ||
772 | RPCset = 0; | ||
773 | |||
774 | if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete? | ||
775 | DPCset = 1; // (SEST transactions only) | ||
776 | else | ||
777 | DPCset = 0; | ||
778 | // set the status for this Outbound transaction's ID | ||
779 | dwStatus = 0L; | ||
780 | if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error) | ||
781 | dwStatus |= SESTPROG_ERR; | ||
782 | |||
783 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]; | ||
784 | if( ulBuff & 0x7a000000L ) // any other errs? | ||
785 | { | ||
786 | if( ulBuff & 0x40000000L ) | ||
787 | dwStatus |= INV_ENTRY; | ||
788 | if( ulBuff & 0x20000000L ) | ||
789 | dwStatus |= FRAME_TO; // FTO | ||
790 | if( ulBuff & 0x10000000L ) | ||
791 | dwStatus |= HOSTPROG_ERR; | ||
792 | if( ulBuff & 0x08000000L ) | ||
793 | dwStatus |= LINKFAIL_TX; | ||
794 | if( ulBuff & 0x02000000L ) | ||
795 | dwStatus |= ABORTSEQ_NOTIFY; // ASN | ||
796 | } | ||
797 | |||
798 | |||
799 | if( dwStatus ) // any errors? | ||
800 | { | ||
801 | // set the Outbound Completion status | ||
802 | Exchanges->fcExchange[ x_ID ].status |= dwStatus; | ||
803 | |||
804 | // if this Outbound frame was for a SEST entry, automatically | ||
805 | // reque it in the case of LINKFAIL (it will restart on PDISC) | ||
806 | if( x_ID < TACH_SEST_LEN ) | ||
807 | { | ||
808 | |||
809 | printk(" #OCM error %Xh x_ID %X# ", | ||
810 | dwStatus, x_ID); | ||
811 | |||
812 | Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default | ||
813 | |||
814 | |||
815 | // We Q ABTS for each exchange. | ||
816 | // NOTE: We can get FRAME_TO on bad alpa (device gone). Since | ||
817 | // bad alpa is reported before FRAME_TO, examine the status | ||
818 | // flags to see if the device is removed. If so, DON'T | ||
819 | // post an ABTS, since it will be terminated by the bad alpa | ||
820 | // message. | ||
821 | if( dwStatus & FRAME_TO ) // check for device removed... | ||
822 | { | ||
823 | if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) ) | ||
824 | { | ||
825 | // presumes device is still there: send ABTS. | ||
826 | |||
827 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | ||
828 | } | ||
829 | } | ||
830 | else // Abort all other errors | ||
831 | { | ||
832 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | ||
833 | } | ||
834 | |||
835 | // if the HPE bit is set, we have to CLose the LOOP | ||
836 | // (see TL/TS UG, pg. 239) | ||
837 | |||
838 | if( dwStatus &= HOSTPROG_ERR ) | ||
839 | // set CL bit (see TL/TS UG, pg. 172) | ||
840 | writel( 4, fcChip->Registers.FMcontrol.address); | ||
841 | } | ||
842 | } | ||
843 | // NOTE: we don't necessarily care about ALL completion messages... | ||
844 | // SCSI resp. complete OR | ||
845 | if( ((x_ID < TACH_SEST_LEN) && RPCset)|| | ||
846 | (x_ID >= TACH_SEST_LEN) ) // non-SCSI command | ||
847 | { | ||
848 | // exchange done; complete to upper levels with status | ||
849 | // (if necessary) and free the exchange slot | ||
850 | |||
851 | |||
852 | if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame? | ||
853 | // A Request or Reply has been sent | ||
854 | { // signal waiting WorkerThread | ||
855 | |||
856 | up( cpqfcHBAdata->TYOBcomplete); // frame is OUT of Tach | ||
857 | |||
858 | // WorkerThread will complete Xchng | ||
859 | } | ||
860 | else // X_ID is for FCP assist (SEST) | ||
861 | { | ||
862 | // TBD (target mode) | ||
863 | // fcCompleteExchange( fcChip, x_ID); // TRE completed | ||
864 | } | ||
865 | } | ||
866 | } | ||
867 | else // ERROR CONDITION! bogus x_ID in completion message | ||
868 | { | ||
869 | |||
870 | printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID); | ||
871 | |||
872 | } | ||
873 | |||
874 | |||
875 | |||
876 | // Load the Frame Manager's error counters. We check them here | ||
877 | // because presumably the link is up and healthy enough for the | ||
878 | // counters to be meaningful (i.e., don't check them while loop | ||
879 | // is initializing). | ||
880 | fcChip->Registers.FMLinkStatus1.value = // get TL's counter | ||
881 | readl(fcChip->Registers.FMLinkStatus1.address); | ||
882 | |||
883 | fcChip->Registers.FMLinkStatus2.value = // get TL's counter | ||
884 | readl(fcChip->Registers.FMLinkStatus2.address); | ||
885 | |||
886 | |||
887 | fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators | ||
888 | break; | ||
889 | |||
890 | |||
891 | |||
892 | case ERROR_IDLE_COMPLETION: // TachLite Error Idle... | ||
893 | |||
894 | // We usually get this when the link goes down during heavy traffic. | ||
895 | // For now, presume that if SEST Exchanges are open, we will | ||
896 | // get this as our cue to INVALIDATE all SEST entries | ||
897 | // (and we OWN all the SEST entries). | ||
898 | // See TL/TS UG, pg. 53 | ||
899 | |||
900 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | ||
901 | { | ||
902 | |||
903 | // Does this VALid SEST entry need to be invalidated for Abort? | ||
904 | fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; | ||
905 | } | ||
906 | |||
907 | CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK | ||
908 | |||
909 | break; | ||
910 | |||
911 | |||
912 | case INBOUND_SFS_COMPLETION: //0x04 | ||
913 | // NOTE! we must process this SFQ message to avoid SFQ filling | ||
914 | // up and stopping TachLite. Incoming commands are placed here, | ||
915 | // as well as 'unknown' frames (e.g. LIP loop position data) | ||
916 | // write this CM's producer index to global... | ||
917 | // TL/TS UG, pg 234: | ||
918 | // Type: 0 - reserved | ||
919 | // 1 - Unassisted FCP | ||
920 | // 2 - BAD FCP | ||
921 | // 3 - Unkown Frame | ||
922 | // 4-F reserved | ||
923 | |||
924 | |||
925 | fcChip->SFQ->producerIndex = (USHORT) | ||
926 | (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL); | ||
927 | |||
928 | |||
929 | ucInboundMessageType = 0; // default to useless frame | ||
930 | |||
931 | // we can only process two Types: 1, Unassisted FCP, and 3, Unknown | ||
932 | // Also, we aren't interested in processing frame fragments | ||
933 | // so don't Que anything with 'LKF' bit set | ||
934 | if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] | ||
935 | & 0x40000000) ) // 'LKF' link failure bit clear? | ||
936 | { | ||
937 | ucInboundMessageType = (UCHAR) // ICM DWord3, "Type" | ||
938 | (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL); | ||
939 | } | ||
940 | else | ||
941 | { | ||
942 | fcChip->fcStats.linkFailRX++; | ||
943 | // printk("LKF (link failure) bit set on inbound message\n"); | ||
944 | } | ||
945 | |||
946 | // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff | ||
947 | CpqTsGetSFQEntry( | ||
948 | fcChip, // i.e. this Device Object | ||
949 | (USHORT)fcChip->SFQ->producerIndex, // SFQ producer ndx | ||
950 | ulFibreFrame, TRUE); // contiguous destination buffer, update chip | ||
951 | |||
952 | // analyze the incoming frame outside the INT handler... | ||
953 | // (i.e., Worker) | ||
954 | |||
955 | if( ucInboundMessageType == 1 ) | ||
956 | { | ||
957 | fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame | ||
958 | // don't fill up our Q with garbage - only accept FCP-CMND | ||
959 | // or XRDY frames | ||
960 | if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND | ||
961 | { | ||
962 | // someone sent us a SCSI command | ||
963 | |||
964 | // fcPutScsiQue( cpqfcHBAdata, | ||
965 | // SFQ_UNASSISTED_FCP, ulFibreFrame); | ||
966 | } | ||
967 | else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status) | ||
968 | (fchs->d_id & 0xFF000000) == 0x05000000 ) // XRDY | ||
969 | { | ||
970 | ULONG x_ID; | ||
971 | // Unfortunately, ABTS requires a Freeze on the chip so | ||
972 | // we can modify the shared memory SEST. When frozen, | ||
973 | // any received Exchange frames cannot be processed by | ||
974 | // Tachyon, so they will be dumped in here. It is too | ||
975 | // complex to attempt the reconstruct these frames in | ||
976 | // the correct Exchange context, so we simply seek to | ||
977 | // find status or transfer ready frames, and cause the | ||
978 | // exchange to complete with errors before the timeout | ||
979 | // expires. We use a Linux Scsi Cmnd result code that | ||
980 | // causes immediate retry. | ||
981 | |||
982 | |||
983 | // Do we have an open exchange that matches this s_id | ||
984 | // and ox_id? | ||
985 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | ||
986 | { | ||
987 | if( (fchs->s_id & 0xFFFFFF) == | ||
988 | (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) | ||
989 | && | ||
990 | (fchs->ox_rx_id & 0xFFFF0000) == | ||
991 | (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) ) | ||
992 | { | ||
993 | // printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id ); | ||
994 | // simulate the anticipated error - since the | ||
995 | // SEST was frozen, frames were lost... | ||
996 | Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME; | ||
997 | |||
998 | // presumes device is still there: send ABTS. | ||
999 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | ||
1000 | break; // done | ||
1001 | } | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | } | ||
1006 | |||
1007 | else if( ucInboundMessageType == 3) | ||
1008 | { | ||
1009 | // FC Link Service frames (e.g. PLOGI, ACC) come in here. | ||
1010 | cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); | ||
1011 | |||
1012 | } | ||
1013 | |||
1014 | else if( ucInboundMessageType == 2 ) // "bad FCP"? | ||
1015 | { | ||
1016 | #ifdef IMQ_DEBUG | ||
1017 | printk("Bad FCP incoming frame discarded\n"); | ||
1018 | #endif | ||
1019 | } | ||
1020 | |||
1021 | else // don't know this type | ||
1022 | { | ||
1023 | #ifdef IMQ_DEBUG | ||
1024 | printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType); | ||
1025 | #endif | ||
1026 | } | ||
1027 | |||
1028 | // Check the Frame Manager's error counters. We check them here | ||
1029 | // because presumably the link is up and healthy enough for the | ||
1030 | // counters to be meaningful (i.e., don't check them while loop | ||
1031 | // is initializing). | ||
1032 | fcChip->Registers.FMLinkStatus1.value = // get TL's counter | ||
1033 | readl(fcChip->Registers.FMLinkStatus1.address); | ||
1034 | |||
1035 | |||
1036 | fcChip->Registers.FMLinkStatus2.value = // get TL's counter | ||
1037 | readl(fcChip->Registers.FMLinkStatus2.address); | ||
1038 | |||
1039 | |||
1040 | break; | ||
1041 | |||
1042 | |||
1043 | |||
1044 | |||
1045 | // We get this CM because we issued a freeze | ||
1046 | // command to stop outbound frames. We issue the | ||
1047 | // freeze command at Link Up time; when this message | ||
1048 | // is received, the ERQ base can be switched and PDISC | ||
1049 | // frames can be sent. | ||
1050 | |||
1051 | |||
1052 | case ERQ_FROZEN_COMPLETION: // note: expect ERQ followed immediately | ||
1053 | // by FCP when freezing TL | ||
1054 | fcChip->Registers.TYstatus.value = // read what's frozen | ||
1055 | readl(fcChip->Registers.TYstatus.address); | ||
1056 | // (do nothing; wait for FCP frozen message) | ||
1057 | break; | ||
1058 | case FCP_FROZEN_COMPLETION: | ||
1059 | |||
1060 | fcChip->Registers.TYstatus.value = // read what's frozen | ||
1061 | readl(fcChip->Registers.TYstatus.address); | ||
1062 | |||
1063 | // Signal the kernel thread to proceed with SEST modification | ||
1064 | up( cpqfcHBAdata->TachFrozen); | ||
1065 | |||
1066 | break; | ||
1067 | |||
1068 | |||
1069 | |||
1070 | case INBOUND_C1_TIMEOUT: | ||
1071 | case MFS_BUF_WARN: | ||
1072 | case IMQ_BUF_WARN: | ||
1073 | break; | ||
1074 | |||
1075 | |||
1076 | |||
1077 | |||
1078 | |||
1079 | // In older Tachyons, we 'clear' the internal 'core' interrupt state | ||
1080 | // by reading the FMstatus register. In newer TachLite (Tachyon), | ||
1081 | // we must WRITE the register | ||
1082 | // to clear the condition (TL/TS UG, pg 179) | ||
1083 | case FRAME_MGR_INTERRUPT: | ||
1084 | { | ||
1085 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
1086 | |||
1087 | fcChip->Registers.FMstatus.value = | ||
1088 | readl( fcChip->Registers.FMstatus.address ); | ||
1089 | |||
1090 | // PROBLEM: It is possible, especially with "dumb" hubs that | ||
1091 | // don't automatically LIP on by-pass of ports that are going | ||
1092 | // away, for the hub by-pass process to destroy critical | ||
1093 | // ordered sets of a frame. The result of this is a hung LPSM | ||
1094 | // (Loop Port State Machine), which on Tachyon results in a | ||
1095 | // (default 2 sec) Loop State Timeout (LST) FM message. We | ||
1096 | // want to avoid this relatively huge timeout by detecting | ||
1097 | // likely scenarios which will result in LST. | ||
1098 | // To do this, we could examine FMstatus for Loss of Synchronization | ||
1099 | // and/or Elastic Store (ES) errors. Of these, Elastic Store is better | ||
1100 | // because we get this indication more quickly than the LOS. | ||
1101 | // Not all ES errors are harmfull, so we don't want to LIP on every | ||
1102 | // ES. Instead, on every ES, detect whether our LPSM in in one | ||
1103 | // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE, | ||
1104 | // or RECEIVED CLOSE. (See TL/TS UG, pg. 181) | ||
1105 | // If any of these LPSM states are detected | ||
1106 | // in combination with the LIP while LDn is not set, | ||
1107 | // send an FM init (LIP F7,F7 for loops)! | ||
1108 | // It is critical to the physical link stability NOT to reset (LIP) | ||
1109 | // more than absolutely necessary; this is a basic premise of the | ||
1110 | // SANMark level 1 spec. | ||
1111 | { | ||
1112 | ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4; | ||
1113 | |||
1114 | if( (fcChip->Registers.FMstatus.value & 0x400) // ElasticStore? | ||
1115 | && | ||
1116 | !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn | ||
1117 | && | ||
1118 | !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF | ||
1119 | { | ||
1120 | if( (Lpsm != 0) || // not MONITORING? or | ||
1121 | !(Lpsm & 0x8) )// not already offline? | ||
1122 | { | ||
1123 | // now check the particular LST states... | ||
1124 | if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) || | ||
1125 | (Lpsm == OPENED) || (Lpsm == XMITTD_CLOSE) || | ||
1126 | (Lpsm == RCVD_CLOSE) ) | ||
1127 | { | ||
1128 | // re-init the loop before it hangs itself! | ||
1129 | printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm); | ||
1130 | |||
1131 | |||
1132 | fcChip->fcStats.FMinits++; | ||
1133 | writel( 6, fcChip->Registers.FMcontrol.address); // LIP | ||
1134 | } | ||
1135 | } | ||
1136 | } | ||
1137 | else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST? | ||
1138 | { | ||
1139 | printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm); | ||
1140 | |||
1141 | fcChip->fcStats.FMinits++; | ||
1142 | writel( 6, fcChip->Registers.FMcontrol.address); // LIP | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | |||
1147 | // clear only the 'interrupting' type bits for this REG read | ||
1148 | writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L), | ||
1149 | fcChip->Registers.FMstatus.address); | ||
1150 | |||
1151 | |||
1152 | // copy frame manager status to unused ULONG slot | ||
1153 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] = | ||
1154 | fcChip->Registers.FMstatus.value; // (for debugging) | ||
1155 | |||
1156 | |||
1157 | // Load the Frame Manager's error counters. We check them here | ||
1158 | // because presumably the link is up and healthy enough for the | ||
1159 | // counters to be meaningful (i.e., don't check them while loop | ||
1160 | // is initializing). | ||
1161 | fcChip->Registers.FMLinkStatus1.value = // get TL's counter | ||
1162 | readl(fcChip->Registers.FMLinkStatus1.address); | ||
1163 | |||
1164 | fcChip->Registers.FMLinkStatus2.value = // get TL's counter | ||
1165 | readl(fcChip->Registers.FMLinkStatus2.address); | ||
1166 | |||
1167 | // Get FM BB_Credit Zero Reg - does not clear on READ | ||
1168 | fcChip->Registers.FMBB_CreditZero.value = // get TL's counter | ||
1169 | readl(fcChip->Registers.FMBB_CreditZero.address); | ||
1170 | |||
1171 | |||
1172 | |||
1173 | fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators | ||
1174 | |||
1175 | |||
1176 | // LINK DOWN | ||
1177 | |||
1178 | if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit | ||
1179 | { | ||
1180 | |||
1181 | #ifdef IMQ_DEBUG | ||
1182 | printk("LinkDn\n"); | ||
1183 | #endif | ||
1184 | printk(" #LDn# "); | ||
1185 | |||
1186 | fcChip->fcStats.linkDown++; | ||
1187 | |||
1188 | SetTachTOV( cpqfcHBAdata); // must set according to SANMark | ||
1189 | |||
1190 | // Check the ERQ - force it to be "empty" to prevent Tach | ||
1191 | // from sending out frames before we do logins. | ||
1192 | |||
1193 | |||
1194 | if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex) | ||
1195 | { | ||
1196 | // printk("#ERQ PI != CI#"); | ||
1197 | CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only | ||
1198 | fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0; | ||
1199 | writel( fcChip->ERQ->base, | ||
1200 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); | ||
1201 | // re-writing base forces ERQ PI to equal CI | ||
1202 | |||
1203 | } | ||
1204 | |||
1205 | // link down transition occurred -- port_ids can change | ||
1206 | // on next LinkUp, so we must invalidate current logins | ||
1207 | // (and any I/O in progress) until PDISC or PLOGI/PRLI | ||
1208 | // completes | ||
1209 | { | ||
1210 | pLoggedInPort = &fcChip->fcPorts; | ||
1211 | while( pLoggedInPort ) // for all ports which are expecting | ||
1212 | // PDISC after the next LIP, set the | ||
1213 | // logoutTimer | ||
1214 | { | ||
1215 | |||
1216 | if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? | ||
1217 | { | ||
1218 | pLoggedInPort->LOGO_timer = 3; // we want 2 seconds | ||
1219 | // but Timer granularity | ||
1220 | // is 1 second | ||
1221 | } | ||
1222 | // suspend any I/O in progress until | ||
1223 | // PDISC received... | ||
1224 | pLoggedInPort->prli = FALSE; // block FCP-SCSI commands | ||
1225 | |||
1226 | pLoggedInPort = pLoggedInPort->pNextPort; | ||
1227 | } // ... all Previously known ports checked | ||
1228 | } | ||
1229 | |||
1230 | // since any hot plugging device may NOT support LILP frames | ||
1231 | // (such as early Tachyon chips), clear this flag indicating | ||
1232 | // we shouldn't use (our copy of) a LILP map. | ||
1233 | // If we receive an LILP frame, we'll set it again. | ||
1234 | fcChip->Options.LILPin = 0; // our LILPmap is invalid | ||
1235 | cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports! | ||
1236 | |||
1237 | // also, we want to invalidate (i.e. INITIATOR_ABORT) any | ||
1238 | // open Login exchanges, in case the LinkDown happened in the | ||
1239 | // middle of logins. It's possible that some ports already | ||
1240 | // ACCepted login commands which we have not processed before | ||
1241 | // another LinkDown occurred. Any accepted Login exhanges are | ||
1242 | // invalidated by LinkDown, even before they are acknowledged. | ||
1243 | // It's also possible for a port to have a Queued Reply or Request | ||
1244 | // for login which was interrupted by LinkDown; it may come later, | ||
1245 | // but it will be unacceptable to us. | ||
1246 | |||
1247 | // we must scan the entire exchange space, find every Login type | ||
1248 | // originated by us, and abort it. This is NOT an abort due to | ||
1249 | // timeout, so we don't actually send abort to the other port - | ||
1250 | // we just complete it to free up the fcExchange slot. | ||
1251 | |||
1252 | for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++) | ||
1253 | { // looking for Extended Link Serv.Exchanges | ||
1254 | if( Exchanges->fcExchange[i].type == ELS_PDISC || | ||
1255 | Exchanges->fcExchange[i].type == ELS_PLOGI || | ||
1256 | Exchanges->fcExchange[i].type == ELS_PRLI ) | ||
1257 | { | ||
1258 | // ABORT the exchange! | ||
1259 | #ifdef IMQ_DEBUG | ||
1260 | printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n", | ||
1261 | i, Exchanges->fcExchange[i].type, | ||
1262 | Exchanges->fcExchange[i].fchs.d_id); | ||
1263 | #endif | ||
1264 | |||
1265 | Exchanges->fcExchange[i].status |= INITIATOR_ABORT; | ||
1266 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, i); // abort on LDn | ||
1267 | } | ||
1268 | } | ||
1269 | |||
1270 | } | ||
1271 | |||
1272 | // ################ LINK UP ################## | ||
1273 | if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit | ||
1274 | { // AL_PA could have changed | ||
1275 | |||
1276 | // We need the following code, duplicated from LinkDn condition, | ||
1277 | // because it's possible for the Tachyon to re-initialize (hard | ||
1278 | // reset) without ever getting a LinkDn indication. | ||
1279 | pLoggedInPort = &fcChip->fcPorts; | ||
1280 | while( pLoggedInPort ) // for all ports which are expecting | ||
1281 | // PDISC after the next LIP, set the | ||
1282 | // logoutTimer | ||
1283 | { | ||
1284 | if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? | ||
1285 | { | ||
1286 | pLoggedInPort->LOGO_timer = 3; // we want 2 seconds | ||
1287 | // but Timer granularity | ||
1288 | // is 1 second | ||
1289 | |||
1290 | // suspend any I/O in progress until | ||
1291 | // PDISC received... | ||
1292 | |||
1293 | } | ||
1294 | pLoggedInPort = pLoggedInPort->pNextPort; | ||
1295 | } // ... all Previously known ports checked | ||
1296 | |||
1297 | // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA) | ||
1298 | fcChip->Registers.rcv_al_pa.value = | ||
1299 | readl(fcChip->Registers.rcv_al_pa.address); | ||
1300 | |||
1301 | // Now, if our acquired address is DIFFERENT from our | ||
1302 | // previous one, we are not allow to do PDISC - we | ||
1303 | // must go back to PLOGI, which will terminate I/O in | ||
1304 | // progress for ALL logged in FC devices... | ||
1305 | // (This is highly unlikely). | ||
1306 | |||
1307 | if( (fcChip->Registers.my_al_pa & 0xFF) != | ||
1308 | ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) ) | ||
1309 | { | ||
1310 | |||
1311 | // printk(" #our HBA port_id changed!# "); // FC port_id changed!! | ||
1312 | |||
1313 | pLoggedInPort = &fcChip->fcPorts; | ||
1314 | while( pLoggedInPort ) // for all ports which are expecting | ||
1315 | // PDISC after the next LIP, set the | ||
1316 | // logoutTimer | ||
1317 | { | ||
1318 | pLoggedInPort->pdisc = FALSE; | ||
1319 | pLoggedInPort->prli = FALSE; | ||
1320 | pLoggedInPort = pLoggedInPort->pNextPort; | ||
1321 | } // ... all Previously known ports checked | ||
1322 | |||
1323 | // when the port_id changes, we must terminate | ||
1324 | // all open exchanges. | ||
1325 | cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED); | ||
1326 | |||
1327 | } | ||
1328 | |||
1329 | // Replace the entire 24-bit port_id. We only know the | ||
1330 | // lower 8 bits (alpa) from Tachyon; if a FLOGI is done, | ||
1331 | // we'll get the upper 16-bits from the FLOGI ACC frame. | ||
1332 | // If someone plugs into Fabric switch, we'll do FLOGI and | ||
1333 | // get full 24-bit port_id; someone could then remove and | ||
1334 | // hot-plug us into a dumb hub. If we send a 24-bit PLOGI | ||
1335 | // to a "private" loop device, it might blow up. | ||
1336 | // Consequently, we force the upper 16-bits of port_id to | ||
1337 | // be re-set on every LinkUp transition | ||
1338 | fcChip->Registers.my_al_pa = | ||
1339 | (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF; | ||
1340 | |||
1341 | |||
1342 | // copy frame manager status to unused ULONG slot | ||
1343 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = | ||
1344 | fcChip->Registers.my_al_pa; // (for debugging) | ||
1345 | |||
1346 | // for TachLite, we need to write the acquired al_pa | ||
1347 | // back into the FMconfig register, because after | ||
1348 | // first initialization, the AQ (prev. acq.) bit gets | ||
1349 | // set, causing TL FM to use the AL_PA field in FMconfig. | ||
1350 | // (In Tachyon, FM writes the acquired AL_PA for us.) | ||
1351 | ulBuff = readl( fcChip->Registers.FMconfig.address); | ||
1352 | ulBuff &= 0x00ffffffL; // mask out current al_pa | ||
1353 | ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa | ||
1354 | fcChip->Registers.FMconfig.value = ulBuff; // copy it back | ||
1355 | writel( fcChip->Registers.FMconfig.value, // put in TachLite | ||
1356 | fcChip->Registers.FMconfig.address); | ||
1357 | |||
1358 | |||
1359 | #ifdef IMQ_DEBUG | ||
1360 | printk("#LUp %Xh, FMstat 0x%08X#", | ||
1361 | fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value); | ||
1362 | #endif | ||
1363 | |||
1364 | // also set the WRITE-ONLY My_ID Register (for Fabric | ||
1365 | // initialization) | ||
1366 | writel( fcChip->Registers.my_al_pa, | ||
1367 | fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID); | ||
1368 | |||
1369 | |||
1370 | fcChip->fcStats.linkUp++; | ||
1371 | |||
1372 | // reset TL statistics counters | ||
1373 | // (we ignore these error counters | ||
1374 | // while link is down) | ||
1375 | ulBuff = // just reset TL's counter | ||
1376 | readl( fcChip->Registers.FMLinkStatus1.address); | ||
1377 | |||
1378 | ulBuff = // just reset TL's counter | ||
1379 | readl( fcChip->Registers.FMLinkStatus2.address); | ||
1380 | |||
1381 | // for initiator, need to start verifying ports (e.g. PDISC) | ||
1382 | |||
1383 | |||
1384 | |||
1385 | |||
1386 | |||
1387 | |||
1388 | CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK | ||
1389 | |||
1390 | // Tachyon creates an interesting problem for us on LILP frames. | ||
1391 | // Instead of writing the incoming LILP frame into the SFQ before | ||
1392 | // indicating LINK UP (the actual order of events), Tachyon tells | ||
1393 | // us LINK UP, and later us the LILP. So we delay, then examine the | ||
1394 | // IMQ for an Inbound CM (x04); if found, we can set | ||
1395 | // LINKACTIVE after processing the LILP. Otherwise, just proceed. | ||
1396 | // Since Tachyon imposes this time delay (and doesn't tell us | ||
1397 | // what it is), we have to impose a delay before "Peeking" the IMQ | ||
1398 | // for Tach hardware (DMA) delivery. | ||
1399 | // Processing LILP is required by SANMark | ||
1400 | udelay( 1000); // microsec delay waiting for LILP (if it comes) | ||
1401 | if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) ) | ||
1402 | { // found SFQ LILP, which will post LINKACTIVE | ||
1403 | // printk("skipping LINKACTIVE post\n"); | ||
1404 | |||
1405 | } | ||
1406 | else | ||
1407 | cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame); | ||
1408 | } | ||
1409 | |||
1410 | |||
1411 | |||
1412 | // ******* Set Fabric Login indication ******** | ||
1413 | if( fcChip->Registers.FMstatus.value & 0x2000 ) | ||
1414 | { | ||
1415 | printk(" #Fabric# "); | ||
1416 | fcChip->Options.fabric = 1; | ||
1417 | } | ||
1418 | else | ||
1419 | fcChip->Options.fabric = 0; | ||
1420 | |||
1421 | |||
1422 | |||
1423 | // ******* LIP(F8,x) or BAD AL_PA? ******** | ||
1424 | if( fcChip->Registers.FMstatus.value & 0x30000L ) | ||
1425 | { | ||
1426 | // copy the error AL_PAs | ||
1427 | fcChip->Registers.rcv_al_pa.value = | ||
1428 | readl(fcChip->Registers.rcv_al_pa.address); | ||
1429 | |||
1430 | // Bad AL_PA? | ||
1431 | if( fcChip->Registers.FMstatus.value & 0x10000L ) | ||
1432 | { | ||
1433 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
1434 | |||
1435 | // copy "BAD" al_pa field | ||
1436 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = | ||
1437 | (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8; | ||
1438 | |||
1439 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
1440 | NULL, // DON'T search Scsi Nexus | ||
1441 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id | ||
1442 | NULL, // DON'T search linked list for FC WWN | ||
1443 | NULL); // DON'T care about end of list | ||
1444 | |||
1445 | if( pLoggedInPort ) | ||
1446 | { | ||
1447 | // Just in case we got this BAD_ALPA because a device | ||
1448 | // quietly disappeared (can happen on non-managed hubs such | ||
1449 | // as the Vixel Rapport 1000), | ||
1450 | // do an Implicit Logout. We never expect this on a Logged | ||
1451 | // in port (but do expect it on port discovery). | ||
1452 | // (As a reasonable alternative, this could be changed to | ||
1453 | // simply start the implicit logout timer, giving the device | ||
1454 | // several seconds to "come back".) | ||
1455 | // | ||
1456 | printk(" #BAD alpa %Xh# ", | ||
1457 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]); | ||
1458 | cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); | ||
1459 | } | ||
1460 | } | ||
1461 | // LIP(f8,x)? | ||
1462 | if( fcChip->Registers.FMstatus.value & 0x20000L ) | ||
1463 | { | ||
1464 | // for debugging, copy al_pa field | ||
1465 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] = | ||
1466 | (fcChip->Registers.rcv_al_pa.value & 0xffL); | ||
1467 | // get the other port's al_pa | ||
1468 | // (one that sent LIP(F8,?) ) | ||
1469 | } | ||
1470 | } | ||
1471 | |||
1472 | // Elastic store err | ||
1473 | if( fcChip->Registers.FMstatus.value & 0x400L ) | ||
1474 | { | ||
1475 | // don't count e-s if loop is down! | ||
1476 | if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) ) | ||
1477 | fcChip->fcStats.e_stores++; | ||
1478 | |||
1479 | } | ||
1480 | } | ||
1481 | break; | ||
1482 | |||
1483 | |||
1484 | case INBOUND_FCP_XCHG_COMPLETION: // 0x0C | ||
1485 | |||
1486 | // Remarks: | ||
1487 | // On Tachlite TL/TS, we get this message when the data phase | ||
1488 | // of a SEST inbound transfer is complete. For example, if a WRITE command | ||
1489 | // was received with OX_ID 0, we might respond with XFER_RDY with | ||
1490 | // RX_ID 8001. This would start the SEST controlled data phases. When | ||
1491 | // all data frames are received, we get this inbound completion. This means | ||
1492 | // we should send a status frame to complete the status phase of the | ||
1493 | // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data | ||
1494 | // frames. | ||
1495 | // See Outbound CM discussion of x_IDs | ||
1496 | // Psuedo Code | ||
1497 | // Get SEST index (x_ID) | ||
1498 | // x_ID out of range, return (err condition) | ||
1499 | // set status bits from 2nd dword | ||
1500 | // free transactionID & SEST entry | ||
1501 | // call fcComplete with transactionID & status | ||
1502 | |||
1503 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0]; | ||
1504 | x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID | ||
1505 | // (mask out MSB "direction" bit) | ||
1506 | // Range check CM OX/RX_ID value... | ||
1507 | if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space | ||
1508 | { | ||
1509 | |||
1510 | //#define FCP_COMPLETION_DBG 1 | ||
1511 | #ifdef FCP_COMPLETION_DBG | ||
1512 | printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", | ||
1513 | x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd); | ||
1514 | #endif | ||
1515 | if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or - | ||
1516 | // time to send response frame? | ||
1517 | RPCset = 1; // (SEST transaction) | ||
1518 | else | ||
1519 | RPCset = 0; | ||
1520 | // set the status for this Inbound SCSI transaction's ID | ||
1521 | dwStatus = 0L; | ||
1522 | if( ulBuff & 0x70000000L ) // any errs? | ||
1523 | { | ||
1524 | |||
1525 | if( ulBuff & 0x40000000L ) | ||
1526 | dwStatus |= LINKFAIL_RX; | ||
1527 | |||
1528 | if( ulBuff & 0x20000000L ) | ||
1529 | dwStatus |= COUNT_ERROR; | ||
1530 | |||
1531 | if( ulBuff & 0x10000000L ) | ||
1532 | dwStatus |= OVERFLOW; | ||
1533 | } | ||
1534 | |||
1535 | |||
1536 | // FCP transaction done - copy status | ||
1537 | Exchanges->fcExchange[ x_ID ].status = dwStatus; | ||
1538 | |||
1539 | |||
1540 | // Did the exchange get an FCP-RSP response frame? | ||
1541 | // (Note the little endian/big endian FC payload difference) | ||
1542 | |||
1543 | if( RPCset ) // SEST transaction Response frame rec'd | ||
1544 | { | ||
1545 | // complete the command in our driver... | ||
1546 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev,fcChip, x_ID); | ||
1547 | |||
1548 | } // end "RPCset" | ||
1549 | |||
1550 | else // ("target" logic) | ||
1551 | { | ||
1552 | // Tachlite says all data frames have been received - now it's time | ||
1553 | // to analyze data transfer (successful?), then send a response | ||
1554 | // frame for this exchange | ||
1555 | |||
1556 | ulFibreFrame[0] = x_ID; // copy for later reference | ||
1557 | |||
1558 | // if this was a TWE, we have to send satus response | ||
1559 | if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE ) | ||
1560 | { | ||
1561 | // fcPutScsiQue( cpqfcHBAdata, | ||
1562 | // NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here) | ||
1563 | } | ||
1564 | } | ||
1565 | } | ||
1566 | else // ERROR CONDITION! bogus x_ID in completion message | ||
1567 | { | ||
1568 | printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID); | ||
1569 | } | ||
1570 | |||
1571 | break; | ||
1572 | |||
1573 | |||
1574 | |||
1575 | |||
1576 | case INBOUND_SCSI_DATA_COMMAND: | ||
1577 | case BAD_SCSI_FRAME: | ||
1578 | case INB_SCSI_STATUS_COMPLETION: | ||
1579 | case BUFFER_PROCESSED_COMPLETION: | ||
1580 | break; | ||
1581 | } | ||
1582 | |||
1583 | // Tachyon is producing; | ||
1584 | // we are consuming | ||
1585 | fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex | ||
1586 | if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover | ||
1587 | fcChip->IMQ->consumerIndex = 0L; // reset it | ||
1588 | |||
1589 | |||
1590 | if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex ) | ||
1591 | { // all Messages are processed - | ||
1592 | iStatus = 0; // no more messages to process | ||
1593 | |||
1594 | } | ||
1595 | else | ||
1596 | iStatus = 1; // more messages to process | ||
1597 | |||
1598 | // update TachLite's ConsumerIndex... (clears INTA_L) | ||
1599 | // NOTE: according to TL/TS UG, the | ||
1600 | // "host must return completion messages in sequential order". | ||
1601 | // Does this mean one at a time, in the order received? We | ||
1602 | // presume so. | ||
1603 | |||
1604 | writel( fcChip->IMQ->consumerIndex, | ||
1605 | (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); | ||
1606 | |||
1607 | #if IMQ_DEBUG | ||
1608 | printk("Process IMQ: writing consumer ndx %d\n ", | ||
1609 | fcChip->IMQ->consumerIndex); | ||
1610 | printk("PI %X, CI %X\n", | ||
1611 | fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex ); | ||
1612 | #endif | ||
1613 | |||
1614 | |||
1615 | |||
1616 | } | ||
1617 | else | ||
1618 | { | ||
1619 | // hmmm... why did we get interrupted/called with no message? | ||
1620 | iStatus = -1; // nothing to process | ||
1621 | #if IMQ_DEBUG | ||
1622 | printk("Process IMQ: no message PI %Xh CI %Xh", | ||
1623 | fcChip->IMQ->producerIndex, | ||
1624 | fcChip->IMQ->consumerIndex); | ||
1625 | #endif | ||
1626 | } | ||
1627 | |||
1628 | LEAVE("ProcessIMQEntry"); | ||
1629 | |||
1630 | return iStatus; | ||
1631 | } | ||
1632 | |||
1633 | |||
1634 | |||
1635 | |||
1636 | |||
1637 | // This routine initializes Tachyon according to the following | ||
1638 | // options (opcode1): | ||
1639 | // 1 - RESTART Tachyon, simulate power on condition by shutting | ||
1640 | // down laser, resetting the hardware, de-allocating all buffers; | ||
1641 | // continue | ||
1642 | // 2 - Config Tachyon / PCI registers; | ||
1643 | // continue | ||
1644 | // 3 - Allocating memory and setting Tachyon queues (write Tachyon regs); | ||
1645 | // continue | ||
1646 | // 4 - Config frame manager registers, initialize, turn on laser | ||
1647 | // | ||
1648 | // Returns: | ||
1649 | // -1 on fatal error | ||
1650 | // 0 on success | ||
1651 | |||
1652 | int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2) | ||
1653 | { | ||
1654 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | ||
1655 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1656 | ULONG ulBuff; | ||
1657 | UCHAR bBuff; | ||
1658 | int iStatus=-1; // assume failure | ||
1659 | |||
1660 | ENTER("InitializeTachLite"); | ||
1661 | |||
1662 | // verify board's base address (sanity check) | ||
1663 | |||
1664 | if( !fcChip->Registers.ReMapMemBase) // NULL address for card? | ||
1665 | return -1; // FATAL error! | ||
1666 | |||
1667 | |||
1668 | |||
1669 | switch( opcode1 ) | ||
1670 | { | ||
1671 | case 1: // restore hardware to power-on (hard) restart | ||
1672 | |||
1673 | |||
1674 | iStatus = fcChip->ResetTachyon( | ||
1675 | cpqfcHBAdata, opcode2); // laser off, reset hardware | ||
1676 | // de-allocate aligned buffers | ||
1677 | |||
1678 | |||
1679 | /* TBD // reset FC link Q (producer and consumer = 0) | ||
1680 | fcLinkQReset(cpqfcHBAdata); | ||
1681 | |||
1682 | */ | ||
1683 | |||
1684 | if( iStatus ) | ||
1685 | break; | ||
1686 | |||
1687 | case 2: // Config PCI/Tachyon registers | ||
1688 | // NOTE: For Tach TL/TS, bit 31 must be set to 1. For TS chips, a read | ||
1689 | // of bit 31 indicates state of M66EN signal; if 1, chip may run at | ||
1690 | // 33-66MHz (see TL/TS UG, pg 159) | ||
1691 | |||
1692 | ulBuff = 0x80000000; // TachLite Configuration Register | ||
1693 | |||
1694 | writel( ulBuff, fcChip->Registers.TYconfig.address); | ||
1695 | // ulBuff = 0x0147L; // CpqTs PCI CFGCMD register | ||
1696 | // WritePCIConfiguration( fcChip->Backplane.bus, | ||
1697 | // fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4); | ||
1698 | // ulBuff = 0x0L; // test! | ||
1699 | // ReadPCIConfiguration( fcChip->Backplane.bus, | ||
1700 | // fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4); | ||
1701 | |||
1702 | // read back for reference... | ||
1703 | fcChip->Registers.TYconfig.value = | ||
1704 | readl( fcChip->Registers.TYconfig.address ); | ||
1705 | |||
1706 | // what is the PCI bus width? | ||
1707 | pci_read_config_byte( cpqfcHBAdata->PciDev, | ||
1708 | 0x43, // PCIMCTR offset | ||
1709 | &bBuff); | ||
1710 | |||
1711 | fcChip->Registers.PCIMCTR = bBuff; | ||
1712 | |||
1713 | // set string identifying the chip on the circuit board | ||
1714 | |||
1715 | fcChip->Registers.TYstatus.value = | ||
1716 | readl( fcChip->Registers.TYstatus.address); | ||
1717 | |||
1718 | { | ||
1719 | // Now that we are supporting multiple boards, we need to change | ||
1720 | // this logic to check for PCI vendor/device IDs... | ||
1721 | // for now, quick & dirty is simply checking Chip rev | ||
1722 | |||
1723 | ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5; | ||
1724 | UCHAR Minor = (UCHAR)(RevId & 0x3); | ||
1725 | UCHAR Major = (UCHAR)((RevId & 0x1C) >>2); | ||
1726 | |||
1727 | /* printk(" HBA Tachyon RevId %d.%d\n", Major, Minor); */ | ||
1728 | if( (Major == 1) && (Minor == 2) ) | ||
1729 | { | ||
1730 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12); | ||
1731 | |||
1732 | } | ||
1733 | else if( (Major == 1) && (Minor == 3) ) | ||
1734 | { | ||
1735 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13); | ||
1736 | } | ||
1737 | else if( (Major == 2) && (Minor == 1) ) | ||
1738 | { | ||
1739 | sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21); | ||
1740 | } | ||
1741 | else | ||
1742 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN); | ||
1743 | } | ||
1744 | |||
1745 | |||
1746 | |||
1747 | case 3: // allocate mem, set Tachyon Que registers | ||
1748 | iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2); | ||
1749 | |||
1750 | if( iStatus ) | ||
1751 | break; | ||
1752 | |||
1753 | // now that the Queues exist, Tach can DMA to them, so | ||
1754 | // we can begin processing INTs | ||
1755 | // INTEN register - enable INT (TachLite interrupt) | ||
1756 | writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN); | ||
1757 | |||
1758 | // Fall through | ||
1759 | case 4: // Config Fame Manager, Init Loop Command, laser on | ||
1760 | |||
1761 | // L_PORT or loopback | ||
1762 | // depending on Options | ||
1763 | iStatus = CpqTsInitializeFrameManager( fcChip,0 ); | ||
1764 | if( iStatus ) | ||
1765 | { | ||
1766 | // failed to initialize Frame Manager | ||
1767 | break; | ||
1768 | } | ||
1769 | |||
1770 | default: | ||
1771 | break; | ||
1772 | } | ||
1773 | LEAVE("InitializeTachLite"); | ||
1774 | |||
1775 | return iStatus; | ||
1776 | } | ||
1777 | |||
1778 | |||
1779 | |||
1780 | |||
1781 | // Depending on the type of platform memory allocation (e.g. dynamic), | ||
1782 | // it's probably best to free memory in opposite order as it was allocated. | ||
1783 | // Order of allocation: see other function | ||
1784 | |||
1785 | |||
1786 | int CpqTsDestroyTachLiteQues( void *pHBA, int opcode) | ||
1787 | { | ||
1788 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | ||
1789 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1790 | USHORT i, iStatus=0; | ||
1791 | void* vPtr; // mem Align manager sets this to the freed address on success | ||
1792 | unsigned long ulPtr; // for 64-bit pointer cast (e.g. Alpa machine) | ||
1793 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
1794 | PSGPAGES j, next; | ||
1795 | |||
1796 | ENTER("DestroyTachLiteQues"); | ||
1797 | |||
1798 | if( fcChip->SEST ) | ||
1799 | { | ||
1800 | // search out and free Pool for Extended S/G list pages | ||
1801 | |||
1802 | for( i=0; i < TACH_SEST_LEN; i++) // for each exchange | ||
1803 | { | ||
1804 | // It's possible that extended S/G pages were allocated, mapped, and | ||
1805 | // not cleared due to error conditions or O/S driver termination. | ||
1806 | // Make sure they're all gone. | ||
1807 | if (Exchanges->fcExchange[i].Cmnd != NULL) | ||
1808 | cpqfc_pci_unmap(cpqfcHBAdata->PciDev, Exchanges->fcExchange[i].Cmnd, | ||
1809 | fcChip, i); // undo DMA mappings. | ||
1810 | |||
1811 | for (j=fcChip->SEST->sgPages[i] ; j != NULL ; j = next) { | ||
1812 | next = j->next; | ||
1813 | kfree(j); | ||
1814 | } | ||
1815 | fcChip->SEST->sgPages[i] = NULL; | ||
1816 | } | ||
1817 | ulPtr = (unsigned long)fcChip->SEST; | ||
1818 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, | ||
1819 | &cpqfcHBAdata->dynamic_mem[0], | ||
1820 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | ||
1821 | fcChip->SEST = 0L; // null invalid ptr | ||
1822 | if( !vPtr ) | ||
1823 | { | ||
1824 | printk("SEST mem not freed\n"); | ||
1825 | iStatus = -1; | ||
1826 | } | ||
1827 | } | ||
1828 | |||
1829 | if( fcChip->SFQ ) | ||
1830 | { | ||
1831 | |||
1832 | ulPtr = (unsigned long)fcChip->SFQ; | ||
1833 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, | ||
1834 | &cpqfcHBAdata->dynamic_mem[0], | ||
1835 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | ||
1836 | fcChip->SFQ = 0L; // null invalid ptr | ||
1837 | if( !vPtr ) | ||
1838 | { | ||
1839 | printk("SFQ mem not freed\n"); | ||
1840 | iStatus = -2; | ||
1841 | } | ||
1842 | } | ||
1843 | |||
1844 | |||
1845 | if( fcChip->IMQ ) | ||
1846 | { | ||
1847 | // clear Indexes to show empty Queue | ||
1848 | fcChip->IMQ->producerIndex = 0; | ||
1849 | fcChip->IMQ->consumerIndex = 0; | ||
1850 | |||
1851 | ulPtr = (unsigned long)fcChip->IMQ; | ||
1852 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0], | ||
1853 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | ||
1854 | fcChip->IMQ = 0L; // null invalid ptr | ||
1855 | if( !vPtr ) | ||
1856 | { | ||
1857 | printk("IMQ mem not freed\n"); | ||
1858 | iStatus = -3; | ||
1859 | } | ||
1860 | } | ||
1861 | |||
1862 | if( fcChip->ERQ ) // release memory blocks used by the queues | ||
1863 | { | ||
1864 | ulPtr = (unsigned long)fcChip->ERQ; | ||
1865 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0], | ||
1866 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | ||
1867 | fcChip->ERQ = 0L; // null invalid ptr | ||
1868 | if( !vPtr ) | ||
1869 | { | ||
1870 | printk("ERQ mem not freed\n"); | ||
1871 | iStatus = -4; | ||
1872 | } | ||
1873 | } | ||
1874 | |||
1875 | // free up the primary EXCHANGES struct and Link Q | ||
1876 | cpqfc_free_dma_consistent(cpqfcHBAdata); | ||
1877 | |||
1878 | LEAVE("DestroyTachLiteQues"); | ||
1879 | |||
1880 | return iStatus; // non-zero (failed) if any memory not freed | ||
1881 | } | ||
1882 | |||
1883 | |||
1884 | |||
1885 | |||
1886 | |||
1887 | // The SFQ is an array with SFQ_LEN length, each element (QEntry) | ||
1888 | // with eight 32-bit words. TachLite places incoming FC frames (i.e. | ||
1889 | // a valid FC frame with our AL_PA ) in contiguous SFQ entries | ||
1890 | // and sends a completion message telling the host where the frame is | ||
1891 | // in the que. | ||
1892 | // This function copies the current (or oldest not-yet-processed) QEntry to | ||
1893 | // a caller's contiguous buffer and updates the Tachyon chip's consumer index | ||
1894 | // | ||
1895 | // NOTE: | ||
1896 | // An FC frame may consume one or many SFQ entries. We know the total | ||
1897 | // length from the completion message. The caller passes a buffer large | ||
1898 | // enough for the complete message (max 2k). | ||
1899 | |||
1900 | static void CpqTsGetSFQEntry( | ||
1901 | PTACHYON fcChip, | ||
1902 | USHORT producerNdx, | ||
1903 | ULONG *ulDestPtr, // contiguous destination buffer | ||
1904 | BOOLEAN UpdateChip) | ||
1905 | { | ||
1906 | ULONG total_bytes=0; | ||
1907 | ULONG consumerIndex = fcChip->SFQ->consumerIndex; | ||
1908 | |||
1909 | // check passed copy of SFQ producer index - | ||
1910 | // is a new message waiting for us? | ||
1911 | // equal indexes means SFS is copied | ||
1912 | |||
1913 | while( producerNdx != consumerIndex ) | ||
1914 | { // need to process message | ||
1915 | total_bytes += 64; // maintain count to prevent writing past buffer | ||
1916 | // don't allow copies over Fibre Channel defined length! | ||
1917 | if( total_bytes <= 2048 ) | ||
1918 | { | ||
1919 | memcpy( ulDestPtr, | ||
1920 | &fcChip->SFQ->QEntry[consumerIndex], | ||
1921 | 64 ); // each SFQ entry is 64 bytes | ||
1922 | ulDestPtr += 16; // advance pointer to next 64 byte block | ||
1923 | } | ||
1924 | // Tachyon is producing, | ||
1925 | // and we are consuming | ||
1926 | |||
1927 | if( ++consumerIndex >= SFQ_LEN)// check for rollover | ||
1928 | consumerIndex = 0L; // reset it | ||
1929 | } | ||
1930 | |||
1931 | // if specified, update the Tachlite chip ConsumerIndex... | ||
1932 | if( UpdateChip ) | ||
1933 | { | ||
1934 | fcChip->SFQ->consumerIndex = consumerIndex; | ||
1935 | writel( fcChip->SFQ->consumerIndex, | ||
1936 | fcChip->Registers.SFQconsumerIndex.address); | ||
1937 | } | ||
1938 | } | ||
1939 | |||
1940 | |||
1941 | |||
1942 | // TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO, | ||
1943 | // and Exchange Request Queue (ERQ) on error recover - | ||
1944 | // (e.g. whenever a LIP occurs). Here | ||
1945 | // we routinely RESUME by clearing these bits, but only if the loop is up | ||
1946 | // to avoid ERROR IDLE messages forever. | ||
1947 | |||
1948 | void CpqTsUnFreezeTachlite( void *pChip, int type ) | ||
1949 | { | ||
1950 | PTACHYON fcChip = (PTACHYON)pChip; | ||
1951 | fcChip->Registers.TYcontrol.value = | ||
1952 | readl(fcChip->Registers.TYcontrol.address); | ||
1953 | |||
1954 | // (bit 4 of value is GBIC LASER) | ||
1955 | // if we 'unfreeze' the core machines before the loop is healthy | ||
1956 | // (i.e. FLT, OS, LS failure bits set in FMstatus) | ||
1957 | // we can get 'error idle' messages forever. Verify that | ||
1958 | // FMstatus (Link Status) is OK before unfreezing. | ||
1959 | |||
1960 | if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear? | ||
1961 | !(fcChip->Registers.FMstatus.value & 0x80 )) // Active LPSM? | ||
1962 | { | ||
1963 | fcChip->Registers.TYcontrol.value &= ~0x300L; // clear FEQ, FFA | ||
1964 | if( type == 1 ) // unfreeze ERQ only | ||
1965 | { | ||
1966 | // printk("Unfreezing ERQ\n"); | ||
1967 | fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ | ||
1968 | } | ||
1969 | else // unfreeze both ERQ and FCP-ASSIST (SEST) | ||
1970 | { | ||
1971 | // printk("Unfreezing ERQ & FCP-ASSIST\n"); | ||
1972 | |||
1973 | // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ | ||
1974 | fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ | ||
1975 | } | ||
1976 | |||
1977 | writel( fcChip->Registers.TYcontrol.value, | ||
1978 | fcChip->Registers.TYcontrol.address); | ||
1979 | |||
1980 | } | ||
1981 | // readback for verify (TachLite still frozen?) | ||
1982 | fcChip->Registers.TYstatus.value = | ||
1983 | readl(fcChip->Registers.TYstatus.address); | ||
1984 | } | ||
1985 | |||
1986 | |||
1987 | // Whenever an FC Exchange Abort is required, we must manipulate the | ||
1988 | // Host/Tachyon shared memory SEST table. Before doing this, we | ||
1989 | // must freeze Tachyon, which flushes certain buffers and ensure we | ||
1990 | // can manipulate the SEST without contention. | ||
1991 | // This freeze function will result in FCP & ERQ FROZEN completion | ||
1992 | // messages (per argument "type"). | ||
1993 | |||
1994 | void CpqTsFreezeTachlite( void *pChip, int type ) | ||
1995 | { | ||
1996 | PTACHYON fcChip = (PTACHYON)pChip; | ||
1997 | fcChip->Registers.TYcontrol.value = | ||
1998 | readl(fcChip->Registers.TYcontrol.address); | ||
1999 | |||
2000 | //set FFA, FEQ - freezes SCSI assist and ERQ | ||
2001 | if( type == 1) // freeze ERQ only | ||
2002 | fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser) | ||
2003 | else // freeze both FCP assists (SEST) and ERQ | ||
2004 | fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser) | ||
2005 | |||
2006 | writel( fcChip->Registers.TYcontrol.value, | ||
2007 | fcChip->Registers.TYcontrol.address); | ||
2008 | |||
2009 | } | ||
2010 | |||
2011 | |||
2012 | |||
2013 | |||
2014 | // TL has two Frame Manager Link Status Registers, with three 8-bit | ||
2015 | // fields each. These eight bit counters are cleared after each read, | ||
2016 | // so we define six 32-bit accumulators for these TL counters. This | ||
2017 | // function breaks out each 8-bit field and adds the value to the existing | ||
2018 | // sum. (s/w counters cleared independently) | ||
2019 | |||
2020 | void fcParseLinkStatusCounters(PTACHYON fcChip) | ||
2021 | { | ||
2022 | UCHAR bBuff; | ||
2023 | ULONG ulBuff; | ||
2024 | |||
2025 | |||
2026 | // The BB0 timer usually increments when TL is initialized, resulting | ||
2027 | // in an initially bogus count. If our own counter is ZERO, it means we | ||
2028 | // are reading this thing for the first time, so we ignore the first count. | ||
2029 | // Also, reading the register does not clear it, so we have to keep an | ||
2030 | // additional static counter to detect rollover (yuk). | ||
2031 | |||
2032 | if( fcChip->fcStats.lastBB0timer == 0L) // TL was reset? (ignore 1st values) | ||
2033 | { | ||
2034 | // get TL's register counter - the "last" count | ||
2035 | fcChip->fcStats.lastBB0timer = | ||
2036 | fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; | ||
2037 | } | ||
2038 | else // subsequent pass - check for rollover | ||
2039 | { | ||
2040 | // "this" count | ||
2041 | ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; | ||
2042 | if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened | ||
2043 | { | ||
2044 | // counter advanced to max... | ||
2045 | fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer); | ||
2046 | fcChip->fcStats.BB0_Timer += ulBuff; // plus some more | ||
2047 | |||
2048 | |||
2049 | } | ||
2050 | else // no rollover -- more counts or no change | ||
2051 | { | ||
2052 | fcChip->fcStats.BB0_Timer += (ulBuff - fcChip->fcStats.lastBB0timer); | ||
2053 | |||
2054 | } | ||
2055 | |||
2056 | fcChip->fcStats.lastBB0timer = ulBuff; | ||
2057 | } | ||
2058 | |||
2059 | |||
2060 | |||
2061 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24); | ||
2062 | fcChip->fcStats.LossofSignal += bBuff; | ||
2063 | |||
2064 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16); | ||
2065 | fcChip->fcStats.BadRXChar += bBuff; | ||
2066 | |||
2067 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8); | ||
2068 | fcChip->fcStats.LossofSync += bBuff; | ||
2069 | |||
2070 | |||
2071 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24); | ||
2072 | fcChip->fcStats.Rx_EOFa += bBuff; | ||
2073 | |||
2074 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16); | ||
2075 | fcChip->fcStats.Dis_Frm += bBuff; | ||
2076 | |||
2077 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8); | ||
2078 | fcChip->fcStats.Bad_CRC += bBuff; | ||
2079 | } | ||
2080 | |||
2081 | |||
2082 | void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip) | ||
2083 | { | ||
2084 | ENTER("ClearLinkStatusCounters"); | ||
2085 | memset( &fcChip->fcStats, 0, sizeof( FCSTATS)); | ||
2086 | LEAVE("ClearLinkStatusCounters"); | ||
2087 | |||
2088 | } | ||
2089 | |||
2090 | |||
2091 | |||
2092 | |||
2093 | // The following function reads the I2C hardware to get the adapter's | ||
2094 | // World Wide Name (WWN). | ||
2095 | // If the WWN is "500805f1fadb43e8" (as printed on the card), the | ||
2096 | // Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register | ||
2097 | // is fadb43e8. | ||
2098 | // In the NVRAM, the bytes appear as: | ||
2099 | // [2d] .. | ||
2100 | // [2e] .. | ||
2101 | // [2f] 50 | ||
2102 | // [30] 08 | ||
2103 | // [31] 05 | ||
2104 | // [32] f1 | ||
2105 | // [33] fa | ||
2106 | // [34] db | ||
2107 | // [35] 43 | ||
2108 | // [36] e8 | ||
2109 | // | ||
2110 | // In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will | ||
2111 | // be correctly loaded by Tachyon silicon. In the login payload, bytes | ||
2112 | // must be correctly swapped for Big Endian format. | ||
2113 | |||
2114 | int CpqTsReadWriteWWN( PVOID pChip, int Read) | ||
2115 | { | ||
2116 | PTACHYON fcChip = (PTACHYON)pChip; | ||
2117 | #define NVRAM_SIZE 512 | ||
2118 | unsigned short i, count = NVRAM_SIZE; | ||
2119 | UCHAR nvRam[NVRAM_SIZE], WWNbuf[8]; | ||
2120 | ULONG ulBuff; | ||
2121 | int iStatus=-1; // assume failure | ||
2122 | int WWNoffset; | ||
2123 | |||
2124 | ENTER("ReadWriteWWN"); | ||
2125 | // Now try to read the WWN from the adapter's NVRAM | ||
2126 | |||
2127 | if( Read ) // READing NVRAM WWN? | ||
2128 | { | ||
2129 | ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address, | ||
2130 | fcChip->Registers.TYcontrol.address, | ||
2131 | count, &nvRam[0] ); | ||
2132 | |||
2133 | if( ulBuff ) // NVRAM read successful? | ||
2134 | { | ||
2135 | iStatus = 0; // success! | ||
2136 | |||
2137 | // for engineering/ prototype boards, the data may be | ||
2138 | // invalid (GIGO, usually all "FF"); this prevents the | ||
2139 | // parse routine from working correctly, which means | ||
2140 | // nothing will be written to our passed buffer. | ||
2141 | |||
2142 | WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam ); | ||
2143 | |||
2144 | if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly | ||
2145 | { | ||
2146 | printk( "CAUTION: Copying NVRAM data on fcChip\n"); | ||
2147 | for( i= 0; i < 8; i++) | ||
2148 | WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work | ||
2149 | } | ||
2150 | |||
2151 | fcChip->Registers.wwn_hi = 0L; | ||
2152 | fcChip->Registers.wwn_lo = 0L; | ||
2153 | for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM | ||
2154 | { | ||
2155 | ulBuff = 0L; | ||
2156 | ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i)); | ||
2157 | fcChip->Registers.wwn_hi |= ulBuff; | ||
2158 | } | ||
2159 | for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM | ||
2160 | { | ||
2161 | ulBuff = 0L; | ||
2162 | ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i)); | ||
2163 | fcChip->Registers.wwn_lo |= ulBuff; | ||
2164 | } | ||
2165 | } // done reading | ||
2166 | else | ||
2167 | { | ||
2168 | |||
2169 | printk( "cpqfcTS: NVRAM read failed\n"); | ||
2170 | |||
2171 | } | ||
2172 | } | ||
2173 | |||
2174 | else // WRITE | ||
2175 | { | ||
2176 | |||
2177 | // NOTE: WRITE not supported & not used in released driver. | ||
2178 | |||
2179 | |||
2180 | printk("ReadWriteNRAM: can't write NVRAM; aborting write\n"); | ||
2181 | } | ||
2182 | |||
2183 | LEAVE("ReadWriteWWN"); | ||
2184 | return iStatus; | ||
2185 | } | ||
2186 | |||
2187 | |||
2188 | |||
2189 | |||
2190 | |||
2191 | // The following function reads or writes the entire "NVRAM" contents of | ||
2192 | // the I2C hardware (i.e. the NM24C03). Note that HP's 5121A (TS 66Mhz) | ||
2193 | // adapter does not use the NM24C03 chip, so this function only works on | ||
2194 | // Compaq's adapters. | ||
2195 | |||
2196 | int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read) | ||
2197 | { | ||
2198 | PTACHYON fcChip = (PTACHYON)pChip; | ||
2199 | #define NVRAM_SIZE 512 | ||
2200 | ULONG ulBuff; | ||
2201 | UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array | ||
2202 | int iStatus=-1; // assume failure | ||
2203 | |||
2204 | |||
2205 | if( Read ) // READing NVRAM? | ||
2206 | { | ||
2207 | ulBuff = cpqfcTS_ReadNVRAM( // TRUE on success | ||
2208 | fcChip->Registers.TYstatus.address, | ||
2209 | fcChip->Registers.TYcontrol.address, | ||
2210 | 256, // bytes to write | ||
2211 | ucPtr ); // source ptr | ||
2212 | |||
2213 | |||
2214 | if( ulBuff ) | ||
2215 | iStatus = 0; // success | ||
2216 | else | ||
2217 | { | ||
2218 | #ifdef DBG | ||
2219 | printk( "CAUTION: NVRAM read failed\n"); | ||
2220 | #endif | ||
2221 | } | ||
2222 | } // done reading | ||
2223 | |||
2224 | else // WRITING NVRAM | ||
2225 | { | ||
2226 | |||
2227 | printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n"); | ||
2228 | } | ||
2229 | |||
2230 | return iStatus; | ||
2231 | } | ||
diff --git a/drivers/scsi/cpqfcTSi2c.c b/drivers/scsi/cpqfcTSi2c.c deleted file mode 100644 index b38a6a9a55a3..000000000000 --- a/drivers/scsi/cpqfcTSi2c.c +++ /dev/null | |||
@@ -1,493 +0,0 @@ | |||
1 | /* Copyright(c) 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter | ||
3 | * 64-bit, 66MHz PCI | ||
4 | * Originally developed and tested on: | ||
5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
6 | * SP# P225CXCBFIEL6T, Rev XC | ||
7 | * SP# 161290-001, Rev XD | ||
8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2, or (at your option) any | ||
13 | * later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * Written by Don Zimmerman | ||
20 | */ | ||
21 | // These functions control the NVRAM I2C hardware on | ||
22 | // non-intelligent Fibre Host Adapters. | ||
23 | // The primary purpose is to read the HBA's NVRAM to get adapter's | ||
24 | // manufactured WWN to copy into Tachyon chip registers | ||
25 | // Orignal source author unknown | ||
26 | |||
27 | #include <linux/types.h> | ||
28 | enum boolean { FALSE, TRUE } ; | ||
29 | |||
30 | |||
31 | #ifndef UCHAR | ||
32 | typedef __u8 UCHAR; | ||
33 | #endif | ||
34 | #ifndef BOOLEAN | ||
35 | typedef __u8 BOOLEAN; | ||
36 | #endif | ||
37 | #ifndef USHORT | ||
38 | typedef __u16 USHORT; | ||
39 | #endif | ||
40 | #ifndef ULONG | ||
41 | typedef __u32 ULONG; | ||
42 | #endif | ||
43 | |||
44 | |||
45 | #include <linux/string.h> | ||
46 | #include <linux/pci.h> | ||
47 | #include <linux/delay.h> | ||
48 | #include <linux/sched.h> | ||
49 | #include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O | ||
50 | |||
51 | #include "cpqfcTSchip.h" | ||
52 | |||
53 | static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ); | ||
54 | /*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout, | ||
55 | USHORT startOffset, // e.g. 0x2f for WWN start | ||
56 | USHORT count, | ||
57 | UCHAR *buf ); | ||
58 | */ | ||
59 | |||
60 | // | ||
61 | // Tachlite GPIO2, GPIO3 (I2C) DEFINES | ||
62 | // The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data) | ||
63 | // GPIO2 drives SDA, and GPIO3 drives SCL | ||
64 | // | ||
65 | // Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0 | ||
66 | // and clear writes 1. The input lines (read in TL status) is NOT inverted | ||
67 | // This really helps confuse the code and debugging. | ||
68 | |||
69 | #define SET_DATA_HI 0x0 | ||
70 | #define SET_DATA_LO 0x8 | ||
71 | #define SET_CLOCK_HI 0x0 | ||
72 | #define SET_CLOCK_LO 0x4 | ||
73 | |||
74 | #define SENSE_DATA_HI 0x8 | ||
75 | #define SENSE_DATA_LO 0x0 | ||
76 | #define SENSE_CLOCK_HI 0x4 | ||
77 | #define SENSE_CLOCK_LO 0x0 | ||
78 | |||
79 | #define SLAVE_READ_ADDRESS 0xA1 | ||
80 | #define SLAVE_WRITE_ADDRESS 0xA0 | ||
81 | |||
82 | |||
83 | static void i2c_delay(ULONG mstime); | ||
84 | static void tl_i2c_clock_pulse( UCHAR , void* GPIOout); | ||
85 | static UCHAR tl_read_i2c_data( void* ); | ||
86 | |||
87 | |||
88 | //----------------------------------------------------------------------------- | ||
89 | // | ||
90 | // Name: I2C_RX_ACK | ||
91 | // | ||
92 | // This routine receives an acknowledge over the I2C bus. | ||
93 | // | ||
94 | //----------------------------------------------------------------------------- | ||
95 | static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout ) | ||
96 | { | ||
97 | unsigned long value; | ||
98 | |||
99 | // do clock pulse, let data line float high | ||
100 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | ||
101 | |||
102 | // slave must drive data low for acknowledge | ||
103 | value = tl_read_i2c_data( GPIOin); | ||
104 | if (value & SENSE_DATA_HI ) | ||
105 | return( FALSE ); | ||
106 | |||
107 | return( TRUE ); | ||
108 | } | ||
109 | //----------------------------------------------------------------------------- | ||
110 | // | ||
111 | // Name: READ_I2C_REG | ||
112 | // | ||
113 | // This routine reads the I2C control register using the global | ||
114 | // IO address stored in gpioreg. | ||
115 | // | ||
116 | //----------------------------------------------------------------------------- | ||
117 | static UCHAR tl_read_i2c_data( void* gpioreg ) | ||
118 | { | ||
119 | return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3 | ||
120 | } | ||
121 | //----------------------------------------------------------------------------- | ||
122 | // | ||
123 | // Name: WRITE_I2C_REG | ||
124 | // | ||
125 | // This routine writes the I2C control register using the global | ||
126 | // IO address stored in gpioreg. | ||
127 | // In Tachlite, we don't want to modify other bits in TL Control reg. | ||
128 | // | ||
129 | //----------------------------------------------------------------------------- | ||
130 | static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value ) | ||
131 | { | ||
132 | ULONG temp; | ||
133 | |||
134 | // First read the register and clear out the old bits | ||
135 | temp = readl( gpioregOUT ) & 0xfffffff3L; | ||
136 | |||
137 | // Now or in the new data and send it back out | ||
138 | writel( temp | value, gpioregOUT); | ||
139 | } | ||
140 | //----------------------------------------------------------------------------- | ||
141 | // | ||
142 | // Name: I2C_TX_START | ||
143 | // | ||
144 | // This routine transmits a start condition over the I2C bus. | ||
145 | // 1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH, | ||
146 | // wait 5us to stabilize. | ||
147 | // 2. With SCL still HIGH, drive SDA low. The low transition marks | ||
148 | // the start condition to NM24Cxx (the chip) | ||
149 | // NOTE! In TL control reg., output 1 means chip sees LOW | ||
150 | // | ||
151 | //----------------------------------------------------------------------------- | ||
152 | static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout ) | ||
153 | { | ||
154 | unsigned short i; | ||
155 | ULONG value; | ||
156 | |||
157 | if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)) | ||
158 | { | ||
159 | // start with clock high, let data float high | ||
160 | tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); | ||
161 | |||
162 | // keep sending clock pulses if slave is driving data line | ||
163 | for (i = 0; i < 10; i++) | ||
164 | { | ||
165 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | ||
166 | |||
167 | if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) | ||
168 | break; | ||
169 | } | ||
170 | |||
171 | // if he's still driving data low after 10 clocks, abort | ||
172 | value = tl_read_i2c_data( GPIOin ); // read status | ||
173 | if (!(value & 0x08) ) | ||
174 | return( FALSE ); | ||
175 | } | ||
176 | |||
177 | |||
178 | // To START, bring data low while clock high | ||
179 | tl_write_i2c_reg( GPIOout, SET_CLOCK_HI | SET_DATA_LO ); | ||
180 | |||
181 | i2c_delay(0); | ||
182 | |||
183 | return( TRUE ); // TX start successful | ||
184 | } | ||
185 | //----------------------------------------------------------------------------- | ||
186 | // | ||
187 | // Name: I2C_TX_STOP | ||
188 | // | ||
189 | // This routine transmits a stop condition over the I2C bus. | ||
190 | // | ||
191 | //----------------------------------------------------------------------------- | ||
192 | |||
193 | static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout ) | ||
194 | { | ||
195 | int i; | ||
196 | |||
197 | for (i = 0; i < 10; i++) | ||
198 | { | ||
199 | // Send clock pulse, drive data line low | ||
200 | tl_i2c_clock_pulse( SET_DATA_LO, GPIOout ); | ||
201 | |||
202 | // To STOP, bring data high while clock high | ||
203 | tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); | ||
204 | |||
205 | // Give the data line time to float high | ||
206 | i2c_delay(0); | ||
207 | |||
208 | // If slave is driving data line low, there's a problem; retry | ||
209 | if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) | ||
210 | return( TRUE ); // TX STOP successful! | ||
211 | } | ||
212 | |||
213 | return( FALSE ); // error | ||
214 | } | ||
215 | //----------------------------------------------------------------------------- | ||
216 | // | ||
217 | // Name: I2C_TX_uchar | ||
218 | // | ||
219 | // This routine transmits a byte across the I2C bus. | ||
220 | // | ||
221 | //----------------------------------------------------------------------------- | ||
222 | static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ) | ||
223 | { | ||
224 | UCHAR bit; | ||
225 | |||
226 | for (bit = 0x80; bit; bit >>= 1) | ||
227 | { | ||
228 | if( data & bit ) | ||
229 | tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout); | ||
230 | else | ||
231 | tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout); | ||
232 | } | ||
233 | } | ||
234 | //----------------------------------------------------------------------------- | ||
235 | // | ||
236 | // Name: I2C_RX_uchar | ||
237 | // | ||
238 | // This routine receives a byte across the I2C bus. | ||
239 | // | ||
240 | //----------------------------------------------------------------------------- | ||
241 | static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout ) | ||
242 | { | ||
243 | UCHAR bit; | ||
244 | UCHAR data = 0; | ||
245 | |||
246 | |||
247 | for (bit = 0x80; bit; bit >>= 1) { | ||
248 | // do clock pulse, let data line float high | ||
249 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | ||
250 | |||
251 | // read data line | ||
252 | if ( tl_read_i2c_data( GPIOin) & 0x08 ) | ||
253 | data |= bit; | ||
254 | } | ||
255 | |||
256 | return (data); | ||
257 | } | ||
258 | //***************************************************************************** | ||
259 | //***************************************************************************** | ||
260 | // Function: read_i2c_nvram | ||
261 | // Arguments: UCHAR count number of bytes to read | ||
262 | // UCHAR *buf area to store the bytes read | ||
263 | // Returns: 0 - failed | ||
264 | // 1 - success | ||
265 | //***************************************************************************** | ||
266 | //***************************************************************************** | ||
267 | unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count, | ||
268 | UCHAR *buf ) | ||
269 | { | ||
270 | unsigned short i; | ||
271 | |||
272 | if( !( tl_i2c_tx_start(GPIOin, GPIOout) )) | ||
273 | return FALSE; | ||
274 | |||
275 | // Select the NVRAM for "dummy" write, to set the address | ||
276 | tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS ); | ||
277 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) | ||
278 | return( FALSE ); | ||
279 | |||
280 | // Now send the address where we want to start reading | ||
281 | tl_i2c_tx_byte( GPIOout , 0 ); | ||
282 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) | ||
283 | return( FALSE ); | ||
284 | |||
285 | // Send a repeated start condition and select the | ||
286 | // slave for reading now. | ||
287 | if( tl_i2c_tx_start(GPIOin, GPIOout) ) | ||
288 | tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS ); | ||
289 | |||
290 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout) ) | ||
291 | return( FALSE ); | ||
292 | |||
293 | // this loop will now read out the data and store it | ||
294 | // in the buffer pointed to by buf | ||
295 | for ( i=0; i<count; i++) | ||
296 | { | ||
297 | *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout); | ||
298 | |||
299 | // Send ACK by holding data line low for 1 clock | ||
300 | if ( i < (count-1) ) | ||
301 | tl_i2c_clock_pulse( 0x08, GPIOout ); | ||
302 | else { | ||
303 | // Don't send ack for final byte | ||
304 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | tl_i2c_tx_stop(GPIOin, GPIOout); | ||
309 | |||
310 | return( TRUE ); | ||
311 | } | ||
312 | |||
313 | //**************************************************************** | ||
314 | // | ||
315 | // | ||
316 | // | ||
317 | // routines to set and clear the data and clock bits | ||
318 | // | ||
319 | // | ||
320 | // | ||
321 | //**************************************************************** | ||
322 | |||
323 | static void tl_set_clock(void* gpioreg) | ||
324 | { | ||
325 | ULONG ret_val; | ||
326 | |||
327 | ret_val = readl( gpioreg ); | ||
328 | ret_val &= 0xffffffFBL; // clear GPIO2 (SCL) | ||
329 | writel( ret_val, gpioreg); | ||
330 | } | ||
331 | |||
332 | static void tl_clr_clock(void* gpioreg) | ||
333 | { | ||
334 | ULONG ret_val; | ||
335 | |||
336 | ret_val = readl( gpioreg ); | ||
337 | ret_val |= SET_CLOCK_LO; | ||
338 | writel( ret_val, gpioreg); | ||
339 | } | ||
340 | |||
341 | //***************************************************************** | ||
342 | // | ||
343 | // | ||
344 | // This routine will advance the clock by one period | ||
345 | // | ||
346 | // | ||
347 | //***************************************************************** | ||
348 | static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout ) | ||
349 | { | ||
350 | ULONG ret_val; | ||
351 | |||
352 | // clear the clock bit | ||
353 | tl_clr_clock( GPIOout ); | ||
354 | |||
355 | i2c_delay(0); | ||
356 | |||
357 | |||
358 | // read the port to preserve non-I2C bits | ||
359 | ret_val = readl( GPIOout ); | ||
360 | |||
361 | // clear the data & clock bits | ||
362 | ret_val &= 0xFFFFFFf3; | ||
363 | |||
364 | // write the value passed in... | ||
365 | // data can only change while clock is LOW! | ||
366 | ret_val |= value; // the data | ||
367 | ret_val |= SET_CLOCK_LO; // the clock | ||
368 | writel( ret_val, GPIOout ); | ||
369 | |||
370 | i2c_delay(0); | ||
371 | |||
372 | |||
373 | //set clock bit | ||
374 | tl_set_clock( GPIOout); | ||
375 | } | ||
376 | |||
377 | |||
378 | |||
379 | |||
380 | //***************************************************************** | ||
381 | // | ||
382 | // | ||
383 | // This routine returns the 64-bit WWN | ||
384 | // | ||
385 | // | ||
386 | //***************************************************************** | ||
387 | int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf ) | ||
388 | { | ||
389 | ULONG len; | ||
390 | ULONG sub_len; | ||
391 | ULONG ptr_inc; | ||
392 | ULONG i; | ||
393 | ULONG j; | ||
394 | UCHAR *data_ptr; | ||
395 | UCHAR z; | ||
396 | UCHAR name; | ||
397 | UCHAR sub_name; | ||
398 | UCHAR done; | ||
399 | int iReturn=0; // def. 0 offset is failure to find WWN field | ||
400 | |||
401 | |||
402 | |||
403 | data_ptr = (UCHAR *)buf; | ||
404 | |||
405 | done = FALSE; | ||
406 | i = 0; | ||
407 | |||
408 | while ( (i < 128) && (!done) ) | ||
409 | { | ||
410 | z = data_ptr[i];\ | ||
411 | if ( !(z & 0x80) ) | ||
412 | { | ||
413 | len = 1 + (z & 0x07); | ||
414 | |||
415 | name = (z & 0x78) >> 3; | ||
416 | if (name == 0x0F) | ||
417 | done = TRUE; | ||
418 | } | ||
419 | else | ||
420 | { | ||
421 | name = z & 0x7F; | ||
422 | len = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8); | ||
423 | |||
424 | switch (name) | ||
425 | { | ||
426 | case 0x0D: | ||
427 | // | ||
428 | j = i + 3; | ||
429 | // | ||
430 | if ( data_ptr[j] == 0x3b ) { | ||
431 | len = 6; | ||
432 | break; | ||
433 | } | ||
434 | |||
435 | while ( j<(i+len) ) { | ||
436 | sub_name = (data_ptr[j] & 0x3f); | ||
437 | sub_len = data_ptr[j+1] + | ||
438 | (data_ptr[j+2] << 8); | ||
439 | ptr_inc = sub_len + 3; | ||
440 | switch (sub_name) | ||
441 | { | ||
442 | case 0x3C: | ||
443 | memcpy( wwnbuf, &data_ptr[j+3], 8); | ||
444 | iReturn = j+3; | ||
445 | break; | ||
446 | default: | ||
447 | break; | ||
448 | } | ||
449 | j += ptr_inc; | ||
450 | } | ||
451 | break; | ||
452 | default: | ||
453 | break; | ||
454 | } | ||
455 | } | ||
456 | // | ||
457 | i += len; | ||
458 | } // end while | ||
459 | return iReturn; | ||
460 | } | ||
461 | |||
462 | |||
463 | |||
464 | |||
465 | |||
466 | // define a short 5 micro sec delay, and longer (ms) delay | ||
467 | |||
468 | static void i2c_delay(ULONG mstime) | ||
469 | { | ||
470 | ULONG i; | ||
471 | |||
472 | // NOTE: we only expect to use these delays when reading | ||
473 | // our adapter's NVRAM, which happens only during adapter reset. | ||
474 | // Delay technique from "Linux Device Drivers", A. Rubini | ||
475 | // (1st Ed.) pg 137. | ||
476 | |||
477 | // printk(" delay %lx ", mstime); | ||
478 | if( mstime ) // ms delay? | ||
479 | { | ||
480 | // delay technique | ||
481 | for( i=0; i < mstime; i++) | ||
482 | udelay(1000); // 1ms per loop | ||
483 | |||
484 | } | ||
485 | else // 5 micro sec delay | ||
486 | |||
487 | udelay( 5 ); // micro secs | ||
488 | |||
489 | // printk("done\n"); | ||
490 | } | ||
491 | |||
492 | |||
493 | |||
diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c deleted file mode 100644 index 3fda8d455c5b..000000000000 --- a/drivers/scsi/cpqfcTSinit.c +++ /dev/null | |||
@@ -1,2096 +0,0 @@ | |||
1 | /* Copyright(c) 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter | ||
3 | * 64-bit, 66MHz PCI | ||
4 | * Originally developed and tested on: | ||
5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
6 | * SP# P225CXCBFIEL6T, Rev XC | ||
7 | * SP# 161290-001, Rev XD | ||
8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2, or (at your option) any | ||
13 | * later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * Written by Don Zimmerman | ||
20 | * IOCTL and procfs added by Jouke Numan | ||
21 | * SMP testing by Chel Van Gennip | ||
22 | * | ||
23 | * portions copied from: | ||
24 | * QLogic CPQFCTS SCSI-FCP | ||
25 | * Written by Erik H. Moe, ehm@cris.com | ||
26 | * Copyright 1995, Erik H. Moe | ||
27 | * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> | ||
28 | * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200 | ||
29 | */ | ||
30 | |||
31 | |||
32 | #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) | ||
33 | |||
34 | #include <linux/config.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/version.h> | ||
38 | #include <linux/blkdev.h> | ||
39 | #include <linux/kernel.h> | ||
40 | #include <linux/string.h> | ||
41 | #include <linux/types.h> | ||
42 | #include <linux/pci.h> | ||
43 | #include <linux/delay.h> | ||
44 | #include <linux/timer.h> | ||
45 | #include <linux/init.h> | ||
46 | #include <linux/ioport.h> // request_region() prototype | ||
47 | #include <linux/completion.h> | ||
48 | |||
49 | #include <asm/io.h> | ||
50 | #include <asm/uaccess.h> // ioctl related | ||
51 | #include <asm/irq.h> | ||
52 | #include <linux/spinlock.h> | ||
53 | #include "scsi.h" | ||
54 | #include <scsi/scsi_host.h> | ||
55 | #include <scsi/scsi_ioctl.h> | ||
56 | #include "cpqfcTSchip.h" | ||
57 | #include "cpqfcTSstructs.h" | ||
58 | #include "cpqfcTStrigger.h" | ||
59 | |||
60 | #include "cpqfcTS.h" | ||
61 | |||
62 | /* Embedded module documentation macros - see module.h */ | ||
63 | MODULE_AUTHOR("Compaq Computer Corporation"); | ||
64 | MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.5.4"); | ||
65 | MODULE_LICENSE("GPL"); | ||
66 | |||
67 | int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags); | ||
68 | |||
69 | // This struct was originally defined in | ||
70 | // /usr/src/linux/include/linux/proc_fs.h | ||
71 | // since it's only partially implemented, we only use first | ||
72 | // few fields... | ||
73 | // NOTE: proc_fs changes in 2.4 kernel | ||
74 | |||
75 | #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) | ||
76 | static struct proc_dir_entry proc_scsi_cpqfcTS = | ||
77 | { | ||
78 | PROC_SCSI_CPQFCTS, // ushort low_ino (enumerated list) | ||
79 | 7, // ushort namelen | ||
80 | DEV_NAME, // const char* name | ||
81 | S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode | ||
82 | 2 // nlink_t nlink | ||
83 | // etc. ... | ||
84 | }; | ||
85 | |||
86 | |||
87 | #endif | ||
88 | |||
89 | #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,7) | ||
90 | # define CPQFC_DECLARE_COMPLETION(x) DECLARE_COMPLETION(x) | ||
91 | # define CPQFC_WAITING waiting | ||
92 | # define CPQFC_COMPLETE(x) complete(x) | ||
93 | # define CPQFC_WAIT_FOR_COMPLETION(x) wait_for_completion(x); | ||
94 | #else | ||
95 | # define CPQFC_DECLARE_COMPLETION(x) DECLARE_MUTEX_LOCKED(x) | ||
96 | # define CPQFC_WAITING sem | ||
97 | # define CPQFC_COMPLETE(x) up(x) | ||
98 | # define CPQFC_WAIT_FOR_COMPLETION(x) down(x) | ||
99 | #endif | ||
100 | |||
101 | static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba); | ||
102 | |||
103 | /* local function to load our per-HBA (local) data for chip | ||
104 | registers, FC link state, all FC exchanges, etc. | ||
105 | |||
106 | We allocate space and compute address offsets for the | ||
107 | most frequently accessed addresses; others (like World Wide | ||
108 | Name) are not necessary. | ||
109 | */ | ||
110 | static void Cpqfc_initHBAdata(CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) | ||
111 | { | ||
112 | |||
113 | cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr | ||
114 | |||
115 | // since x86 port space is 64k, we only need the lower 16 bits | ||
116 | cpqfcHBAdata->fcChip.Registers.IOBaseL = | ||
117 | PciDev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; | ||
118 | |||
119 | cpqfcHBAdata->fcChip.Registers.IOBaseU = | ||
120 | PciDev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; | ||
121 | |||
122 | // 32-bit memory addresses | ||
123 | cpqfcHBAdata->fcChip.Registers.MemBase = | ||
124 | PciDev->resource[3].start & PCI_BASE_ADDRESS_MEM_MASK; | ||
125 | |||
126 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase = | ||
127 | ioremap( PciDev->resource[3].start & PCI_BASE_ADDRESS_MEM_MASK, | ||
128 | 0x200); | ||
129 | |||
130 | cpqfcHBAdata->fcChip.Registers.RAMBase = | ||
131 | PciDev->resource[4].start; | ||
132 | |||
133 | cpqfcHBAdata->fcChip.Registers.SROMBase = // NULL for HP TS adapter | ||
134 | PciDev->resource[5].start; | ||
135 | |||
136 | // now the Tachlite chip registers | ||
137 | // the REGISTER struct holds both the physical address & last | ||
138 | // written value (some TL registers are WRITE ONLY) | ||
139 | |||
140 | cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address = | ||
141 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX; | ||
142 | |||
143 | cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address = | ||
144 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX; | ||
145 | |||
146 | // TL Frame Manager | ||
147 | cpqfcHBAdata->fcChip.Registers.FMconfig.address = | ||
148 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG; | ||
149 | cpqfcHBAdata->fcChip.Registers.FMcontrol.address = | ||
150 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL; | ||
151 | cpqfcHBAdata->fcChip.Registers.FMstatus.address = | ||
152 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS; | ||
153 | cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address = | ||
154 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1; | ||
155 | cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address = | ||
156 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2; | ||
157 | cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address = | ||
158 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0; | ||
159 | |||
160 | // TL Control Regs | ||
161 | cpqfcHBAdata->fcChip.Registers.TYconfig.address = | ||
162 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG; | ||
163 | cpqfcHBAdata->fcChip.Registers.TYcontrol.address = | ||
164 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL; | ||
165 | cpqfcHBAdata->fcChip.Registers.TYstatus.address = | ||
166 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS; | ||
167 | cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address = | ||
168 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA; | ||
169 | cpqfcHBAdata->fcChip.Registers.ed_tov.address = | ||
170 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV; | ||
171 | |||
172 | |||
173 | cpqfcHBAdata->fcChip.Registers.INTEN.address = | ||
174 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN; | ||
175 | cpqfcHBAdata->fcChip.Registers.INTPEND.address = | ||
176 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND; | ||
177 | cpqfcHBAdata->fcChip.Registers.INTSTAT.address = | ||
178 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT; | ||
179 | |||
180 | DEBUG_PCI(printk(" cpqfcHBAdata->fcChip.Registers. :\n")); | ||
181 | DEBUG_PCI(printk(" IOBaseL = %x\n", | ||
182 | cpqfcHBAdata->fcChip.Registers.IOBaseL)); | ||
183 | DEBUG_PCI(printk(" IOBaseU = %x\n", | ||
184 | cpqfcHBAdata->fcChip.Registers.IOBaseU)); | ||
185 | |||
186 | /* printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase); */ | ||
187 | |||
188 | DEBUG_PCI(printk(" SFQconsumerIndex.address = %p\n", | ||
189 | cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address)); | ||
190 | DEBUG_PCI(printk(" ERQproducerIndex.address = %p\n", | ||
191 | cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address)); | ||
192 | DEBUG_PCI(printk(" TYconfig.address = %p\n", | ||
193 | cpqfcHBAdata->fcChip.Registers.TYconfig.address)); | ||
194 | DEBUG_PCI(printk(" FMconfig.address = %p\n", | ||
195 | cpqfcHBAdata->fcChip.Registers.FMconfig.address)); | ||
196 | DEBUG_PCI(printk(" FMcontrol.address = %p\n", | ||
197 | cpqfcHBAdata->fcChip.Registers.FMcontrol.address)); | ||
198 | |||
199 | // set default options for FC controller (chip) | ||
200 | cpqfcHBAdata->fcChip.Options.initiator = 1; // default: SCSI initiator | ||
201 | cpqfcHBAdata->fcChip.Options.target = 0; // default: SCSI target | ||
202 | cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC | ||
203 | cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip | ||
204 | |||
205 | // set highest and lowest FC-PH version the adapter/driver supports | ||
206 | // (NOT strict compliance) | ||
207 | cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3; | ||
208 | cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43; | ||
209 | |||
210 | // set function points for this controller / adapter | ||
211 | cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite; | ||
212 | cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite; | ||
213 | cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite; | ||
214 | cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues; | ||
215 | cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues; | ||
216 | cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite; | ||
217 | cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl; | ||
218 | cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry; | ||
219 | cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager; | ||
220 | cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN; | ||
221 | cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM; | ||
222 | |||
223 | if (cpqfc_alloc_private_data_pool(cpqfcHBAdata) != 0) { | ||
224 | printk(KERN_WARNING | ||
225 | "cpqfc: unable to allocate pool for passthru ioctls. " | ||
226 | "Passthru ioctls disabled.\n"); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | |||
231 | /* (borrowed from linux/drivers/scsi/hosts.c) */ | ||
232 | static void launch_FCworker_thread(struct Scsi_Host *HostAdapter) | ||
233 | { | ||
234 | DECLARE_MUTEX_LOCKED(sem); | ||
235 | |||
236 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
237 | |||
238 | ENTER("launch_FC_worker_thread"); | ||
239 | |||
240 | cpqfcHBAdata->notify_wt = &sem; | ||
241 | |||
242 | /* must unlock before kernel_thread(), for it may cause a reschedule. */ | ||
243 | spin_unlock_irq(HostAdapter->host_lock); | ||
244 | kernel_thread((int (*)(void *))cpqfcTSWorkerThread, | ||
245 | (void *) HostAdapter, 0); | ||
246 | /* | ||
247 | * Now wait for the kernel error thread to initialize itself | ||
248 | |||
249 | */ | ||
250 | down (&sem); | ||
251 | spin_lock_irq(HostAdapter->host_lock); | ||
252 | cpqfcHBAdata->notify_wt = NULL; | ||
253 | |||
254 | LEAVE("launch_FC_worker_thread"); | ||
255 | |||
256 | } | ||
257 | |||
258 | |||
259 | /* "Entry" point to discover if any supported PCI | ||
260 | bus adapter can be found | ||
261 | */ | ||
262 | /* We're supporting: | ||
263 | * Compaq 64-bit, 66MHz HBA with Tachyon TS | ||
264 | * Agilent XL2 | ||
265 | * HP Tachyon | ||
266 | */ | ||
267 | #define HBA_TYPES 3 | ||
268 | |||
269 | #ifndef PCI_DEVICE_ID_COMPAQ_ | ||
270 | #define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc | ||
271 | #endif | ||
272 | |||
273 | static struct SupportedPCIcards cpqfc_boards[] __initdata = { | ||
274 | {PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TACHYON}, | ||
275 | {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_TACHLITE}, | ||
276 | {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_TACHYON}, | ||
277 | }; | ||
278 | |||
279 | |||
280 | int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate) | ||
281 | { | ||
282 | int NumberOfAdapters=0; // how many of our PCI adapters are found? | ||
283 | struct pci_dev *PciDev = NULL; | ||
284 | struct Scsi_Host *HostAdapter = NULL; | ||
285 | CPQFCHBA *cpqfcHBAdata = NULL; | ||
286 | struct timer_list *cpqfcTStimer = NULL; | ||
287 | int i; | ||
288 | |||
289 | ENTER("cpqfcTS_detect"); | ||
290 | |||
291 | #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) | ||
292 | ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS; | ||
293 | #else | ||
294 | ScsiHostTemplate->proc_name = "cpqfcTS"; | ||
295 | #endif | ||
296 | |||
297 | for( i=0; i < HBA_TYPES; i++) | ||
298 | { | ||
299 | // look for all HBAs of each type | ||
300 | |||
301 | while((PciDev = pci_find_device(cpqfc_boards[i].vendor_id, | ||
302 | cpqfc_boards[i].device_id, PciDev))) | ||
303 | { | ||
304 | |||
305 | if (pci_enable_device(PciDev)) { | ||
306 | printk(KERN_ERR | ||
307 | "cpqfc: can't enable PCI device at %s\n", pci_name(PciDev)); | ||
308 | goto err_continue; | ||
309 | } | ||
310 | |||
311 | if (pci_set_dma_mask(PciDev, CPQFCTS_DMA_MASK) != 0) { | ||
312 | printk(KERN_WARNING | ||
313 | "cpqfc: HBA cannot support required DMA mask, skipping.\n"); | ||
314 | goto err_disable_dev; | ||
315 | } | ||
316 | |||
317 | // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes... | ||
318 | /* printk(" scsi_register allocating %d bytes for FC HBA\n", | ||
319 | (ULONG)sizeof(CPQFCHBA)); */ | ||
320 | |||
321 | HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) ); | ||
322 | |||
323 | if(HostAdapter == NULL) { | ||
324 | printk(KERN_WARNING | ||
325 | "cpqfc: can't register SCSI HBA, skipping.\n"); | ||
326 | goto err_disable_dev; | ||
327 | } | ||
328 | DEBUG_PCI( printk(" HBA found!\n")); | ||
329 | DEBUG_PCI( printk(" HostAdapter->PciDev->irq = %u\n", PciDev->irq) ); | ||
330 | DEBUG_PCI(printk(" PciDev->baseaddress[0]= %lx\n", | ||
331 | PciDev->resource[0].start)); | ||
332 | DEBUG_PCI(printk(" PciDev->baseaddress[1]= %lx\n", | ||
333 | PciDev->resource[1].start)); | ||
334 | DEBUG_PCI(printk(" PciDev->baseaddress[2]= %lx\n", | ||
335 | PciDev->resource[2].start)); | ||
336 | DEBUG_PCI(printk(" PciDev->baseaddress[3]= %lx\n", | ||
337 | PciDev->resource[3].start)); | ||
338 | |||
339 | HostAdapter->irq = PciDev->irq; // copy for Scsi layers | ||
340 | |||
341 | // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper), | ||
342 | // for a total I/O port address space of 512 bytes. | ||
343 | // mask out the I/O port address (lower) & record | ||
344 | HostAdapter->io_port = (unsigned int) | ||
345 | PciDev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; | ||
346 | HostAdapter->n_io_port = 0xff; | ||
347 | |||
348 | // i.e., expect 128 targets (arbitrary number), while the | ||
349 | // RA-4000 supports 32 LUNs | ||
350 | HostAdapter->max_id = 0; // incremented as devices log in | ||
351 | HostAdapter->max_lun = CPQFCTS_MAX_LUN; // LUNs per FC device | ||
352 | HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses? | ||
353 | |||
354 | // get the pointer to our HBA specific data... (one for | ||
355 | // each HBA on the PCI bus(ses)). | ||
356 | cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
357 | |||
358 | // make certain our data struct is clear | ||
359 | memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) ); | ||
360 | |||
361 | |||
362 | // initialize our HBA info | ||
363 | cpqfcHBAdata->HBAnum = NumberOfAdapters; | ||
364 | |||
365 | cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr | ||
366 | Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields | ||
367 | |||
368 | cpqfcHBAdata->HBAnum = NumberOfAdapters; | ||
369 | spin_lock_init(&cpqfcHBAdata->hba_spinlock); | ||
370 | |||
371 | // request necessary resources and check for conflicts | ||
372 | if( request_irq( HostAdapter->irq, | ||
373 | cpqfcTS_intr_handler, | ||
374 | SA_INTERRUPT | SA_SHIRQ, | ||
375 | DEV_NAME, | ||
376 | HostAdapter) ) | ||
377 | { | ||
378 | printk(KERN_WARNING "cpqfc: IRQ %u already used\n", HostAdapter->irq); | ||
379 | goto err_unregister; | ||
380 | } | ||
381 | |||
382 | // Since we have two 256-byte I/O port ranges (upper | ||
383 | // and lower), check them both | ||
384 | if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, | ||
385 | 0xff, DEV_NAME ) ) | ||
386 | { | ||
387 | printk(KERN_WARNING "cpqfc: address in use: %x\n", | ||
388 | cpqfcHBAdata->fcChip.Registers.IOBaseU); | ||
389 | goto err_free_irq; | ||
390 | } | ||
391 | |||
392 | if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, | ||
393 | 0xff, DEV_NAME ) ) | ||
394 | { | ||
395 | printk(KERN_WARNING "cpqfc: address in use: %x\n", | ||
396 | cpqfcHBAdata->fcChip.Registers.IOBaseL); | ||
397 | goto err_release_region_U; | ||
398 | } | ||
399 | |||
400 | // OK, we have grabbed everything we need now. | ||
401 | DEBUG_PCI(printk(" Reserved 255 I/O addresses @ %x\n", | ||
402 | cpqfcHBAdata->fcChip.Registers.IOBaseL )); | ||
403 | DEBUG_PCI(printk(" Reserved 255 I/O addresses @ %x\n", | ||
404 | cpqfcHBAdata->fcChip.Registers.IOBaseU )); | ||
405 | |||
406 | |||
407 | |||
408 | // start our kernel worker thread | ||
409 | |||
410 | spin_lock_irq(HostAdapter->host_lock); | ||
411 | launch_FCworker_thread(HostAdapter); | ||
412 | |||
413 | |||
414 | // start our TimerTask... | ||
415 | |||
416 | cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer; | ||
417 | |||
418 | init_timer( cpqfcTStimer); // Linux clears next/prev values | ||
419 | cpqfcTStimer->expires = jiffies + HZ; // one second | ||
420 | cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter | ||
421 | cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping | ||
422 | |||
423 | add_timer( cpqfcTStimer); // give it to Linux | ||
424 | |||
425 | |||
426 | // now initialize our hardware... | ||
427 | if (cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1)) { | ||
428 | printk(KERN_WARNING "cpqfc: initialization of HBA hardware failed.\n"); | ||
429 | goto err_release_region_L; | ||
430 | } | ||
431 | |||
432 | cpqfcHBAdata->fcStatsTime = jiffies; // (for FC Statistics delta) | ||
433 | |||
434 | // give our HBA time to initialize and login current devices... | ||
435 | { | ||
436 | // The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000, | ||
437 | // has the following algorithm for FL_Port startup: | ||
438 | // Time(sec) Action | ||
439 | // 0: Device Plugin and LIP(F7,F7) transmission | ||
440 | // 1.0 LIP incoming | ||
441 | // 1.027 LISA incoming, no CLS! (link not up) | ||
442 | // 1.028 NOS incoming (switch test for N_Port) | ||
443 | // 1.577 ED_TOV expired, transmit LIPs again | ||
444 | // 3.0 LIP(F8,F7) incoming (switch passes Tach Prim.Sig) | ||
445 | // 3.028 LILP received, link up, FLOGI starts | ||
446 | // slowest(worst) case, measured on 1Gb Finisar GT analyzer | ||
447 | |||
448 | unsigned long stop_time; | ||
449 | |||
450 | spin_unlock_irq(HostAdapter->host_lock); | ||
451 | stop_time = jiffies + 4*HZ; | ||
452 | while ( time_before(jiffies, stop_time) ) | ||
453 | schedule(); // (our worker task needs to run) | ||
454 | |||
455 | } | ||
456 | |||
457 | spin_lock_irq(HostAdapter->host_lock); | ||
458 | NumberOfAdapters++; | ||
459 | spin_unlock_irq(HostAdapter->host_lock); | ||
460 | |||
461 | continue; | ||
462 | |||
463 | err_release_region_L: | ||
464 | release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff ); | ||
465 | err_release_region_U: | ||
466 | release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff ); | ||
467 | err_free_irq: | ||
468 | free_irq( HostAdapter->irq, HostAdapter); | ||
469 | err_unregister: | ||
470 | scsi_unregister( HostAdapter); | ||
471 | err_disable_dev: | ||
472 | pci_disable_device( PciDev ); | ||
473 | err_continue: | ||
474 | continue; | ||
475 | } // end of while() | ||
476 | } | ||
477 | |||
478 | LEAVE("cpqfcTS_detect"); | ||
479 | |||
480 | return NumberOfAdapters; | ||
481 | } | ||
482 | |||
483 | #ifdef SUPPORT_RESET | ||
484 | static void my_ioctl_done (Scsi_Cmnd * SCpnt) | ||
485 | { | ||
486 | struct request * req; | ||
487 | |||
488 | req = SCpnt->request; | ||
489 | req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ | ||
490 | |||
491 | if (req->CPQFC_WAITING != NULL) | ||
492 | CPQFC_COMPLETE(req->CPQFC_WAITING); | ||
493 | } | ||
494 | #endif | ||
495 | |||
496 | static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba) | ||
497 | { | ||
498 | hba->private_data_bits = NULL; | ||
499 | hba->private_data_pool = NULL; | ||
500 | hba->private_data_bits = | ||
501 | kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) / | ||
502 | BITS_PER_LONG)*sizeof(unsigned long), | ||
503 | GFP_KERNEL); | ||
504 | if (hba->private_data_bits == NULL) | ||
505 | return -1; | ||
506 | memset(hba->private_data_bits, 0, | ||
507 | ((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) / | ||
508 | BITS_PER_LONG)*sizeof(unsigned long)); | ||
509 | hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) * | ||
510 | CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL); | ||
511 | if (hba->private_data_pool == NULL) { | ||
512 | kfree(hba->private_data_bits); | ||
513 | hba->private_data_bits = NULL; | ||
514 | return -1; | ||
515 | } | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static void cpqfc_free_private_data_pool(CPQFCHBA *hba) | ||
520 | { | ||
521 | kfree(hba->private_data_bits); | ||
522 | kfree(hba->private_data_pool); | ||
523 | } | ||
524 | |||
525 | int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer) | ||
526 | { | ||
527 | /* Is pointer within our private data pool? | ||
528 | We use Scsi_Request->upper_private_data (normally | ||
529 | reserved for upper layer drivers, e.g. the sg driver) | ||
530 | We check to see if the pointer is ours by looking at | ||
531 | its address. Is this ok? Hmm, it occurs to me that | ||
532 | a user app might do something bad by using sg to send | ||
533 | a cpqfc passthrough ioctl with upper_data_private | ||
534 | forged to be somewhere in our pool..., though they'd | ||
535 | normally have to be root already to do this. */ | ||
536 | |||
537 | return (pointer != NULL && | ||
538 | pointer >= (void *) hba->private_data_pool && | ||
539 | pointer < (void *) hba->private_data_pool + | ||
540 | sizeof(*hba->private_data_pool) * | ||
541 | CPQFC_MAX_PASSTHRU_CMDS); | ||
542 | } | ||
543 | |||
544 | cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba) | ||
545 | { | ||
546 | int i; | ||
547 | |||
548 | do { | ||
549 | i = find_first_zero_bit(hba->private_data_bits, | ||
550 | CPQFC_MAX_PASSTHRU_CMDS); | ||
551 | if (i == CPQFC_MAX_PASSTHRU_CMDS) | ||
552 | return NULL; | ||
553 | } while ( test_and_set_bit(i & (BITS_PER_LONG - 1), | ||
554 | hba->private_data_bits+(i/BITS_PER_LONG)) != 0); | ||
555 | return &hba->private_data_pool[i]; | ||
556 | } | ||
557 | |||
558 | void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data) | ||
559 | { | ||
560 | int i; | ||
561 | i = data - hba->private_data_pool; | ||
562 | clear_bit(i&(BITS_PER_LONG-1), | ||
563 | hba->private_data_bits+(i/BITS_PER_LONG)); | ||
564 | } | ||
565 | |||
566 | int cpqfcTS_ioctl( struct scsi_device *ScsiDev, int Cmnd, void *arg) | ||
567 | { | ||
568 | int result = 0; | ||
569 | struct Scsi_Host *HostAdapter = ScsiDev->host; | ||
570 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
571 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
572 | PFC_LOGGEDIN_PORT pLoggedInPort = NULL; | ||
573 | struct scsi_cmnd *DumCmnd; | ||
574 | int i, j; | ||
575 | VENDOR_IOCTL_REQ ioc; | ||
576 | cpqfc_passthru_t *vendor_cmd; | ||
577 | Scsi_Device *SDpnt; | ||
578 | Scsi_Request *ScsiPassThruReq; | ||
579 | cpqfc_passthru_private_t *privatedata; | ||
580 | |||
581 | ENTER("cpqfcTS_ioctl "); | ||
582 | |||
583 | // printk("ioctl CMND %d", Cmnd); | ||
584 | switch (Cmnd) { | ||
585 | // Passthrough provides a mechanism to bypass the RAID | ||
586 | // or other controller and talk directly to the devices | ||
587 | // (e.g. physical disk drive) | ||
588 | // Passthrough commands, unfortunately, tend to be vendor | ||
589 | // specific; this is tailored to COMPAQ's RAID (RA4x00) | ||
590 | case CPQFCTS_SCSI_PASSTHRU: | ||
591 | { | ||
592 | void *buf = NULL; // for kernel space buffer for user data | ||
593 | |||
594 | /* Check that our pool got allocated ok. */ | ||
595 | if (cpqfcHBAdata->private_data_pool == NULL) | ||
596 | return -ENOMEM; | ||
597 | |||
598 | if( !arg) | ||
599 | return -EINVAL; | ||
600 | |||
601 | // must be super user to send stuff directly to the | ||
602 | // controller and/or physical drives... | ||
603 | if( !capable(CAP_SYS_RAWIO) ) | ||
604 | return -EPERM; | ||
605 | |||
606 | // copy the caller's struct to our space. | ||
607 | if( copy_from_user( &ioc, arg, sizeof( VENDOR_IOCTL_REQ))) | ||
608 | return( -EFAULT); | ||
609 | |||
610 | vendor_cmd = ioc.argp; // i.e., CPQ specific command struct | ||
611 | |||
612 | // If necessary, grab a kernel/DMA buffer | ||
613 | if( vendor_cmd->len) | ||
614 | { | ||
615 | buf = kmalloc( vendor_cmd->len, GFP_KERNEL); | ||
616 | if( !buf) | ||
617 | return -ENOMEM; | ||
618 | } | ||
619 | // Now build a Scsi_Request to pass down... | ||
620 | ScsiPassThruReq = scsi_allocate_request(ScsiDev, GFP_KERNEL); | ||
621 | if (ScsiPassThruReq == NULL) { | ||
622 | kfree(buf); | ||
623 | return -ENOMEM; | ||
624 | } | ||
625 | ScsiPassThruReq->upper_private_data = | ||
626 | cpqfc_alloc_private_data(cpqfcHBAdata); | ||
627 | if (ScsiPassThruReq->upper_private_data == NULL) { | ||
628 | kfree(buf); | ||
629 | scsi_release_request(ScsiPassThruReq); // "de-allocate" | ||
630 | return -ENOMEM; | ||
631 | } | ||
632 | |||
633 | if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) { | ||
634 | if (vendor_cmd->len) { // Need data from user? | ||
635 | if (copy_from_user(buf, vendor_cmd->bufp, | ||
636 | vendor_cmd->len)) { | ||
637 | kfree(buf); | ||
638 | cpqfc_free_private_data(cpqfcHBAdata, | ||
639 | ScsiPassThruReq->upper_private_data); | ||
640 | scsi_release_request(ScsiPassThruReq); | ||
641 | return( -EFAULT); | ||
642 | } | ||
643 | } | ||
644 | ScsiPassThruReq->sr_data_direction = DMA_TO_DEVICE; | ||
645 | } else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) { | ||
646 | ScsiPassThruReq->sr_data_direction = DMA_FROM_DEVICE; | ||
647 | } else | ||
648 | // maybe this means a bug in the user app | ||
649 | ScsiPassThruReq->sr_data_direction = DMA_BIDIRECTIONAL; | ||
650 | |||
651 | ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req() | ||
652 | ScsiPassThruReq->sr_sense_buffer[0] = 0; | ||
653 | ScsiPassThruReq->sr_sense_buffer[2] = 0; | ||
654 | |||
655 | // We copy the scheme used by sd.c:spinup_disk() to submit commands | ||
656 | // to our own HBA. We do this in order to stall the | ||
657 | // thread calling the IOCTL until it completes, and use | ||
658 | // the same "_quecommand" function for synchronizing | ||
659 | // FC Link events with our "worker thread". | ||
660 | |||
661 | privatedata = ScsiPassThruReq->upper_private_data; | ||
662 | privatedata->bus = vendor_cmd->bus; | ||
663 | privatedata->pdrive = vendor_cmd->pdrive; | ||
664 | |||
665 | // eventually gets us to our own _quecommand routine | ||
666 | scsi_wait_req(ScsiPassThruReq, | ||
667 | &vendor_cmd->cdb[0], buf, vendor_cmd->len, | ||
668 | 10*HZ, // timeout | ||
669 | 1); // retries | ||
670 | result = ScsiPassThruReq->sr_result; | ||
671 | |||
672 | // copy any sense data back to caller | ||
673 | if( result != 0 ) | ||
674 | { | ||
675 | memcpy( vendor_cmd->sense_data, // see struct def - size=40 | ||
676 | ScsiPassThruReq->sr_sense_buffer, | ||
677 | sizeof(ScsiPassThruReq->sr_sense_buffer) < | ||
678 | sizeof(vendor_cmd->sense_data) ? | ||
679 | sizeof(ScsiPassThruReq->sr_sense_buffer) : | ||
680 | sizeof(vendor_cmd->sense_data) | ||
681 | ); | ||
682 | } | ||
683 | SDpnt = ScsiPassThruReq->sr_device; | ||
684 | /* upper_private_data is already freed in call_scsi_done() */ | ||
685 | scsi_release_request(ScsiPassThruReq); // "de-allocate" | ||
686 | ScsiPassThruReq = NULL; | ||
687 | |||
688 | // need to pass data back to user (space)? | ||
689 | if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) && | ||
690 | vendor_cmd->len ) | ||
691 | if( copy_to_user( vendor_cmd->bufp, buf, vendor_cmd->len)) | ||
692 | result = -EFAULT; | ||
693 | |||
694 | kfree(buf); | ||
695 | |||
696 | return result; | ||
697 | } | ||
698 | |||
699 | case CPQFCTS_GETPCIINFO: | ||
700 | { | ||
701 | cpqfc_pci_info_struct pciinfo; | ||
702 | |||
703 | if( !arg) | ||
704 | return -EINVAL; | ||
705 | |||
706 | |||
707 | |||
708 | pciinfo.bus = cpqfcHBAdata->PciDev->bus->number; | ||
709 | pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn; | ||
710 | pciinfo.board_id = cpqfcHBAdata->PciDev->device | | ||
711 | (cpqfcHBAdata->PciDev->vendor <<16); | ||
712 | |||
713 | if(copy_to_user( arg, &pciinfo, sizeof(cpqfc_pci_info_struct))) | ||
714 | return( -EFAULT); | ||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | case CPQFCTS_GETDRIVVER: | ||
719 | { | ||
720 | DriverVer_type DriverVer = | ||
721 | CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR); | ||
722 | |||
723 | if( !arg) | ||
724 | return -EINVAL; | ||
725 | |||
726 | if(copy_to_user( arg, &DriverVer, sizeof(DriverVer))) | ||
727 | return( -EFAULT); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | |||
732 | |||
733 | case CPQFC_IOCTL_FC_TARGET_ADDRESS: | ||
734 | // can we find an FC device mapping to this SCSI target? | ||
735 | /* DumCmnd.channel = ScsiDev->channel; */ // For searching | ||
736 | /* DumCmnd.target = ScsiDev->id; */ | ||
737 | /* DumCmnd.lun = ScsiDev->lun; */ | ||
738 | |||
739 | DumCmnd = scsi_get_command (ScsiDev, GFP_KERNEL); | ||
740 | if (!DumCmnd) | ||
741 | return -ENOMEM; | ||
742 | |||
743 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
744 | DumCmnd, // search Scsi Nexus | ||
745 | 0, // DON'T search linked list for FC port id | ||
746 | NULL, // DON'T search linked list for FC WWN | ||
747 | NULL); // DON'T care about end of list | ||
748 | scsi_put_command (DumCmnd); | ||
749 | if (pLoggedInPort == NULL) { | ||
750 | result = -ENXIO; | ||
751 | break; | ||
752 | } | ||
753 | result = access_ok(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)) ? 0 : -EFAULT; | ||
754 | if (result) break; | ||
755 | |||
756 | put_user(pLoggedInPort->port_id, | ||
757 | &((Scsi_FCTargAddress *) arg)->host_port_id); | ||
758 | |||
759 | for( i=3,j=0; i>=0; i--) // copy the LOGIN port's WWN | ||
760 | put_user(pLoggedInPort->u.ucWWN[i], | ||
761 | &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); | ||
762 | for( i=7; i>3; i--) // copy the LOGIN port's WWN | ||
763 | put_user(pLoggedInPort->u.ucWWN[i], | ||
764 | &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); | ||
765 | break; | ||
766 | |||
767 | |||
768 | case CPQFC_IOCTL_FC_TDR: | ||
769 | |||
770 | result = cpqfcTS_TargetDeviceReset( ScsiDev, 0); | ||
771 | |||
772 | break; | ||
773 | |||
774 | |||
775 | |||
776 | |||
777 | default: | ||
778 | result = -EINVAL; | ||
779 | break; | ||
780 | } | ||
781 | |||
782 | LEAVE("cpqfcTS_ioctl"); | ||
783 | return result; | ||
784 | } | ||
785 | |||
786 | |||
787 | /* "Release" the Host Bus Adapter... | ||
788 | disable interrupts, stop the HBA, release the interrupt, | ||
789 | and free all resources */ | ||
790 | |||
791 | int cpqfcTS_release(struct Scsi_Host *HostAdapter) | ||
792 | { | ||
793 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
794 | |||
795 | |||
796 | ENTER("cpqfcTS_release"); | ||
797 | |||
798 | DEBUG_PCI( printk(" cpqfcTS: delete timer...\n")); | ||
799 | del_timer( &cpqfcHBAdata->cpqfcTStimer); | ||
800 | |||
801 | // disable the hardware... | ||
802 | DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n")); | ||
803 | cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS); | ||
804 | |||
805 | // kill kernel thread | ||
806 | if( cpqfcHBAdata->worker_thread ) // (only if exists) | ||
807 | { | ||
808 | DECLARE_MUTEX_LOCKED(sem); // synchronize thread kill | ||
809 | |||
810 | cpqfcHBAdata->notify_wt = &sem; | ||
811 | DEBUG_PCI( printk(" killing kernel thread\n")); | ||
812 | send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1); | ||
813 | down( &sem); | ||
814 | cpqfcHBAdata->notify_wt = NULL; | ||
815 | |||
816 | } | ||
817 | |||
818 | cpqfc_free_private_data_pool(cpqfcHBAdata); | ||
819 | // free Linux resources | ||
820 | DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n")); | ||
821 | free_irq( HostAdapter->irq, HostAdapter); | ||
822 | scsi_unregister( HostAdapter); | ||
823 | release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff); | ||
824 | release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff); | ||
825 | /* we get "vfree: bad address" executing this - need to investigate... | ||
826 | if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) != | ||
827 | cpqfcHBAdata->fcChip.Registers.ReMapMemBase) | ||
828 | vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase); | ||
829 | */ | ||
830 | pci_disable_device( cpqfcHBAdata->PciDev); | ||
831 | |||
832 | LEAVE("cpqfcTS_release"); | ||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | |||
837 | const char * cpqfcTS_info(struct Scsi_Host *HostAdapter) | ||
838 | { | ||
839 | static char buf[300]; | ||
840 | CPQFCHBA *cpqfcHBA; | ||
841 | int BusSpeed, BusWidth; | ||
842 | |||
843 | // get the pointer to our Scsi layer HBA buffer | ||
844 | cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata; | ||
845 | |||
846 | BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ? | ||
847 | 64 : 32; | ||
848 | |||
849 | if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000) | ||
850 | BusSpeed = 66; | ||
851 | else | ||
852 | BusSpeed = 33; | ||
853 | |||
854 | sprintf(buf, | ||
855 | "%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d", | ||
856 | cpqfcHBA->fcChip.Name, | ||
857 | cpqfcHBA->fcChip.Registers.wwn_hi, | ||
858 | cpqfcHBA->fcChip.Registers.wwn_lo, | ||
859 | cpqfcHBA->PciDev->bus->number, | ||
860 | cpqfcHBA->PciDev->device, | ||
861 | HostAdapter->irq, | ||
862 | cpqfcHBA->fcChip.Registers.IOBaseL, | ||
863 | cpqfcHBA->fcChip.Registers.MemBase, | ||
864 | BusWidth, | ||
865 | BusSpeed, | ||
866 | VER_MAJOR, VER_MINOR, VER_SUBMINOR | ||
867 | ); | ||
868 | |||
869 | |||
870 | cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); | ||
871 | cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); | ||
872 | return buf; | ||
873 | } | ||
874 | |||
875 | // | ||
876 | // /proc/scsi support. The following routines allow us to do 'normal' | ||
877 | // sprintf like calls to return the currently requested piece (buflenght | ||
878 | // chars, starting at bufoffset) of the file. Although procfs allows for | ||
879 | // a 1 Kb bytes overflow after te supplied buffer, I consider it bad | ||
880 | // programming to use it to make programming a little simpler. This piece | ||
881 | // of coding is borrowed from ncr53c8xx.c with some modifications | ||
882 | // | ||
883 | struct info_str | ||
884 | { | ||
885 | char *buffer; // Pointer to output buffer | ||
886 | int buflength; // It's length | ||
887 | int bufoffset; // File offset corresponding with buf[0] | ||
888 | int buffillen; // Current filled length | ||
889 | int filpos; // Current file offset | ||
890 | }; | ||
891 | |||
892 | static void copy_mem_info(struct info_str *info, char *data, int datalen) | ||
893 | { | ||
894 | |||
895 | if (info->filpos < info->bufoffset) { // Current offset before buffer offset | ||
896 | if (info->filpos + datalen <= info->bufoffset) { | ||
897 | info->filpos += datalen; // Discard if completely before buffer | ||
898 | return; | ||
899 | } else { // Partial copy, set to begin | ||
900 | data += (info->bufoffset - info->filpos); | ||
901 | datalen -= (info->bufoffset - info->filpos); | ||
902 | info->filpos = info->bufoffset; | ||
903 | } | ||
904 | } | ||
905 | |||
906 | info->filpos += datalen; // Update current offset | ||
907 | |||
908 | if (info->buffillen == info->buflength) // Buffer full, discard | ||
909 | return; | ||
910 | |||
911 | if (info->buflength - info->buffillen < datalen) // Overflows buffer ? | ||
912 | datalen = info->buflength - info->buffillen; | ||
913 | |||
914 | memcpy(info->buffer + info->buffillen, data, datalen); | ||
915 | info->buffillen += datalen; | ||
916 | } | ||
917 | |||
918 | static int copy_info(struct info_str *info, char *fmt, ...) | ||
919 | { | ||
920 | va_list args; | ||
921 | char buf[400]; | ||
922 | int len; | ||
923 | |||
924 | va_start(args, fmt); | ||
925 | len = vsprintf(buf, fmt, args); | ||
926 | va_end(args); | ||
927 | |||
928 | copy_mem_info(info, buf, len); | ||
929 | return len; | ||
930 | } | ||
931 | |||
932 | |||
933 | // Routine to get data for /proc RAM filesystem | ||
934 | // | ||
935 | int cpqfcTS_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, | ||
936 | int inout) | ||
937 | { | ||
938 | struct scsi_cmnd *DumCmnd; | ||
939 | struct scsi_device *ScsiDev; | ||
940 | int Chan, Targ, i; | ||
941 | struct info_str info; | ||
942 | CPQFCHBA *cpqfcHBA; | ||
943 | PTACHYON fcChip; | ||
944 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
945 | char buf[81]; | ||
946 | |||
947 | if (inout) return -EINVAL; | ||
948 | |||
949 | // get the pointer to our Scsi layer HBA buffer | ||
950 | cpqfcHBA = (CPQFCHBA *)host->hostdata; | ||
951 | fcChip = &cpqfcHBA->fcChip; | ||
952 | |||
953 | *start = buffer; | ||
954 | |||
955 | info.buffer = buffer; | ||
956 | info.buflength = length; | ||
957 | info.bufoffset = offset; | ||
958 | info.filpos = 0; | ||
959 | info.buffillen = 0; | ||
960 | copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR); | ||
961 | cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]); | ||
962 | cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); | ||
963 | copy_info(&info, "%s\n", buf); | ||
964 | |||
965 | #define DISPLAY_WWN_INFO | ||
966 | #ifdef DISPLAY_WWN_INFO | ||
967 | ScsiDev = scsi_get_host_dev (host); | ||
968 | if (!ScsiDev) | ||
969 | return -ENOMEM; | ||
970 | DumCmnd = scsi_get_command (ScsiDev, GFP_KERNEL); | ||
971 | if (!DumCmnd) { | ||
972 | scsi_free_host_dev (ScsiDev); | ||
973 | return -ENOMEM; | ||
974 | } | ||
975 | copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n"); | ||
976 | for ( Chan=0; Chan <= host->max_channel; Chan++) { | ||
977 | DumCmnd->device->channel = Chan; | ||
978 | for (Targ=0; Targ <= host->max_id; Targ++) { | ||
979 | DumCmnd->device->id = Targ; | ||
980 | if ((pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
981 | DumCmnd, // search Scsi Nexus | ||
982 | 0, // DON'T search list for FC port id | ||
983 | NULL, // DON'T search list for FC WWN | ||
984 | NULL))){ // DON'T care about end of list | ||
985 | copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ", | ||
986 | host->host_no, Chan, Targ); | ||
987 | for( i=3; i>=0; i--) // copy the LOGIN port's WWN | ||
988 | copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); | ||
989 | for( i=7; i>3; i--) // copy the LOGIN port's WWN | ||
990 | copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); | ||
991 | copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id); | ||
992 | } | ||
993 | } | ||
994 | } | ||
995 | |||
996 | scsi_put_command (DumCmnd); | ||
997 | scsi_free_host_dev (ScsiDev); | ||
998 | #endif | ||
999 | |||
1000 | |||
1001 | |||
1002 | |||
1003 | |||
1004 | // Unfortunately, the proc_info buffer isn't big enough | ||
1005 | // for everything we would like... | ||
1006 | // For FC stats, compile this and turn off WWN stuff above | ||
1007 | //#define DISPLAY_FC_STATS | ||
1008 | #ifdef DISPLAY_FC_STATS | ||
1009 | // get the Fibre Channel statistics | ||
1010 | { | ||
1011 | int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ; | ||
1012 | int days,hours,minutes,secs; | ||
1013 | |||
1014 | days = DeltaSecs / (3600*24); // days | ||
1015 | hours = (DeltaSecs% (3600*24)) / 3600; // hours | ||
1016 | minutes = (DeltaSecs%3600 /60); // minutes | ||
1017 | secs = DeltaSecs%60; // secs | ||
1018 | copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n", | ||
1019 | days, hours, minutes, secs); | ||
1020 | } | ||
1021 | |||
1022 | cpqfcHBA->fcStatsTime = jiffies; // (for next delta) | ||
1023 | |||
1024 | copy_info( &info, " LinkUp %9u LinkDown %u\n", | ||
1025 | fcChip->fcStats.linkUp, fcChip->fcStats.linkDown); | ||
1026 | |||
1027 | copy_info( &info, " Loss of Signal %9u Loss of Sync %u\n", | ||
1028 | fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync); | ||
1029 | |||
1030 | copy_info( &info, " Discarded Frames %9u Bad CRC Frame %u\n", | ||
1031 | fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC); | ||
1032 | |||
1033 | copy_info( &info, " TACH LinkFailTX %9u TACH LinkFailRX %u\n", | ||
1034 | fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX); | ||
1035 | |||
1036 | copy_info( &info, " TACH RxEOFa %9u TACH Elastic Store %u\n", | ||
1037 | fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores); | ||
1038 | |||
1039 | copy_info( &info, " BufferCreditWait %9uus TACH FM Inits %u\n", | ||
1040 | fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits ); | ||
1041 | |||
1042 | copy_info( &info, " FC-2 Timeouts %9u FC-2 Logouts %u\n", | ||
1043 | fcChip->fcStats.timeouts, fcChip->fcStats.logouts); | ||
1044 | |||
1045 | copy_info( &info, " FC-2 Aborts %9u FC-4 Aborts %u\n", | ||
1046 | fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted); | ||
1047 | |||
1048 | // clear the counters | ||
1049 | cpqfcTSClearLinkStatusCounters( fcChip); | ||
1050 | #endif | ||
1051 | |||
1052 | return info.buffillen; | ||
1053 | } | ||
1054 | |||
1055 | |||
1056 | #if DEBUG_CMND | ||
1057 | |||
1058 | UCHAR *ScsiToAscii( UCHAR ScsiCommand) | ||
1059 | { | ||
1060 | |||
1061 | /*++ | ||
1062 | |||
1063 | Routine Description: | ||
1064 | |||
1065 | Converts a SCSI command to a text string for debugging purposes. | ||
1066 | |||
1067 | |||
1068 | Arguments: | ||
1069 | |||
1070 | ScsiCommand -- hex value SCSI Command | ||
1071 | |||
1072 | |||
1073 | Return Value: | ||
1074 | |||
1075 | An ASCII, null-terminated string if found, else returns NULL. | ||
1076 | |||
1077 | Original code from M. McGowen, Compaq | ||
1078 | --*/ | ||
1079 | |||
1080 | |||
1081 | switch (ScsiCommand) | ||
1082 | { | ||
1083 | case 0x00: | ||
1084 | return( "Test Unit Ready" ); | ||
1085 | |||
1086 | case 0x01: | ||
1087 | return( "Rezero Unit or Rewind" ); | ||
1088 | |||
1089 | case 0x02: | ||
1090 | return( "Request Block Address" ); | ||
1091 | |||
1092 | case 0x03: | ||
1093 | return( "Requese Sense" ); | ||
1094 | |||
1095 | case 0x04: | ||
1096 | return( "Format Unit" ); | ||
1097 | |||
1098 | case 0x05: | ||
1099 | return( "Read Block Limits" ); | ||
1100 | |||
1101 | case 0x07: | ||
1102 | return( "Reassign Blocks" ); | ||
1103 | |||
1104 | case 0x08: | ||
1105 | return( "Read (6)" ); | ||
1106 | |||
1107 | case 0x0a: | ||
1108 | return( "Write (6)" ); | ||
1109 | |||
1110 | case 0x0b: | ||
1111 | return( "Seek (6)" ); | ||
1112 | |||
1113 | case 0x12: | ||
1114 | return( "Inquiry" ); | ||
1115 | |||
1116 | case 0x15: | ||
1117 | return( "Mode Select (6)" ); | ||
1118 | |||
1119 | case 0x16: | ||
1120 | return( "Reserve" ); | ||
1121 | |||
1122 | case 0x17: | ||
1123 | return( "Release" ); | ||
1124 | |||
1125 | case 0x1a: | ||
1126 | return( "ModeSen(6)" ); | ||
1127 | |||
1128 | case 0x1b: | ||
1129 | return( "Start/Stop Unit" ); | ||
1130 | |||
1131 | case 0x1c: | ||
1132 | return( "Receive Diagnostic Results" ); | ||
1133 | |||
1134 | case 0x1d: | ||
1135 | return( "Send Diagnostic" ); | ||
1136 | |||
1137 | case 0x25: | ||
1138 | return( "Read Capacity" ); | ||
1139 | |||
1140 | case 0x28: | ||
1141 | return( "Read (10)" ); | ||
1142 | |||
1143 | case 0x2a: | ||
1144 | return( "Write (10)" ); | ||
1145 | |||
1146 | case 0x2b: | ||
1147 | return( "Seek (10)" ); | ||
1148 | |||
1149 | case 0x2e: | ||
1150 | return( "Write and Verify" ); | ||
1151 | |||
1152 | case 0x2f: | ||
1153 | return( "Verify" ); | ||
1154 | |||
1155 | case 0x34: | ||
1156 | return( "Pre-Fetch" ); | ||
1157 | |||
1158 | case 0x35: | ||
1159 | return( "Synchronize Cache" ); | ||
1160 | |||
1161 | case 0x37: | ||
1162 | return( "Read Defect Data (10)" ); | ||
1163 | |||
1164 | case 0x3b: | ||
1165 | return( "Write Buffer" ); | ||
1166 | |||
1167 | case 0x3c: | ||
1168 | return( "Read Buffer" ); | ||
1169 | |||
1170 | case 0x3e: | ||
1171 | return( "Read Long" ); | ||
1172 | |||
1173 | case 0x3f: | ||
1174 | return( "Write Long" ); | ||
1175 | |||
1176 | case 0x41: | ||
1177 | return( "Write Same" ); | ||
1178 | |||
1179 | case 0x4c: | ||
1180 | return( "Log Select" ); | ||
1181 | |||
1182 | case 0x4d: | ||
1183 | return( "Log Sense" ); | ||
1184 | |||
1185 | case 0x56: | ||
1186 | return( "Reserve (10)" ); | ||
1187 | |||
1188 | case 0x57: | ||
1189 | return( "Release (10)" ); | ||
1190 | |||
1191 | case 0xa0: | ||
1192 | return( "ReportLuns" ); | ||
1193 | |||
1194 | case 0xb7: | ||
1195 | return( "Read Defect Data (12)" ); | ||
1196 | |||
1197 | case 0xca: | ||
1198 | return( "Peripheral Device Addressing SCSI Passthrough" ); | ||
1199 | |||
1200 | case 0xcb: | ||
1201 | return( "Compaq Array Firmware Passthrough" ); | ||
1202 | |||
1203 | default: | ||
1204 | return( NULL ); | ||
1205 | } | ||
1206 | |||
1207 | } // end ScsiToAscii() | ||
1208 | |||
1209 | void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd) | ||
1210 | { | ||
1211 | |||
1212 | printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n", | ||
1213 | ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len); | ||
1214 | |||
1215 | if( cmd->cmnd[0] == 0) // Test Unit Ready? | ||
1216 | { | ||
1217 | int i; | ||
1218 | |||
1219 | printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n", | ||
1220 | cmd->request_bufflen, cmd->use_sg, cmd->bufflen); | ||
1221 | printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n", | ||
1222 | cmd->request_buffer, cmd->sglist_len, cmd->buffer); | ||
1223 | for (i = 0; i < cmd->cmd_len; i++) | ||
1224 | printk("0x%02x ", cmd->cmnd[i]); | ||
1225 | printk("\n"); | ||
1226 | } | ||
1227 | |||
1228 | } | ||
1229 | |||
1230 | #endif /* DEBUG_CMND */ | ||
1231 | |||
1232 | |||
1233 | |||
1234 | |||
1235 | static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) | ||
1236 | { | ||
1237 | int i; | ||
1238 | |||
1239 | for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++) | ||
1240 | { // find spare slot | ||
1241 | if( cpqfcHBAdata->BoardLockCmnd[i] == NULL ) | ||
1242 | { | ||
1243 | cpqfcHBAdata->BoardLockCmnd[i] = Cmnd; | ||
1244 | // printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n", | ||
1245 | // i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); | ||
1246 | break; | ||
1247 | } | ||
1248 | } | ||
1249 | if( i >= CPQFCTS_REQ_QUEUE_LEN) | ||
1250 | { | ||
1251 | printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd); | ||
1252 | } | ||
1253 | |||
1254 | } | ||
1255 | |||
1256 | |||
1257 | static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) | ||
1258 | { | ||
1259 | int indx; | ||
1260 | |||
1261 | // Remember the command ptr so we can return; we'll complete when | ||
1262 | // the device comes back, causing immediate retry | ||
1263 | for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++) | ||
1264 | { | ||
1265 | if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available? | ||
1266 | { | ||
1267 | #ifdef DUMMYCMND_DBG | ||
1268 | printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx); | ||
1269 | #endif | ||
1270 | cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd; | ||
1271 | break; | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd?? | ||
1276 | { | ||
1277 | // this will result in an _abort call later (with possible trouble) | ||
1278 | printk("no buffer for LinkDnCmnd!! %p\n", Cmnd); | ||
1279 | } | ||
1280 | } | ||
1281 | |||
1282 | |||
1283 | |||
1284 | |||
1285 | |||
1286 | // The file <scsi/scsi_host.h> says not to call scsi_done from | ||
1287 | // inside _queuecommand, so we'll do it from the heartbeat timer | ||
1288 | // (clarification: Turns out it's ok to call scsi_done from queuecommand | ||
1289 | // for cases that don't go to the hardware like scsi cmds destined | ||
1290 | // for LUNs we know don't exist, so this code might be simplified...) | ||
1291 | |||
1292 | static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd) | ||
1293 | { | ||
1294 | int i; | ||
1295 | // printk(" can't find target %d\n", Cmnd->target); | ||
1296 | |||
1297 | for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) | ||
1298 | { // find spare slot | ||
1299 | if( cpqfcHBAdata->BadTargetCmnd[i] == NULL ) | ||
1300 | { | ||
1301 | cpqfcHBAdata->BadTargetCmnd[i] = Cmnd; | ||
1302 | // printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n", | ||
1303 | // i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); | ||
1304 | break; | ||
1305 | } | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | |||
1310 | // This is the "main" entry point for Linux Scsi commands -- | ||
1311 | // it all starts here. | ||
1312 | |||
1313 | int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *)) | ||
1314 | { | ||
1315 | struct Scsi_Host *HostAdapter = Cmnd->device->host; | ||
1316 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
1317 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1318 | TachFCHDR_GCMND fchs; // only use for FC destination id field | ||
1319 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
1320 | ULONG ulStatus, SESTtype; | ||
1321 | LONG ExchangeID; | ||
1322 | |||
1323 | |||
1324 | |||
1325 | |||
1326 | ENTER("cpqfcTS_queuecommand"); | ||
1327 | |||
1328 | PCI_TRACEO( (ULONG)Cmnd, 0x98) | ||
1329 | |||
1330 | |||
1331 | Cmnd->scsi_done = done; | ||
1332 | #ifdef DEBUG_CMND | ||
1333 | cpqfcTS_print_scsi_cmd( Cmnd); | ||
1334 | #endif | ||
1335 | |||
1336 | // prevent board contention with kernel thread... | ||
1337 | |||
1338 | if( cpqfcHBAdata->BoardLock ) | ||
1339 | { | ||
1340 | // printk(" @BrdLck Hld@ "); | ||
1341 | QueCmndOnBoardLock( cpqfcHBAdata, Cmnd); | ||
1342 | } | ||
1343 | |||
1344 | else | ||
1345 | { | ||
1346 | |||
1347 | // in the current system (2.2.12), this routine is called | ||
1348 | // after spin_lock_irqsave(), so INTs are disabled. However, | ||
1349 | // we might have something pending in the LinkQ, which | ||
1350 | // might cause the WorkerTask to run. In case that | ||
1351 | // happens, make sure we lock it out. | ||
1352 | |||
1353 | |||
1354 | |||
1355 | PCI_TRACE( 0x98) | ||
1356 | CPQ_SPINLOCK_HBA( cpqfcHBAdata) | ||
1357 | PCI_TRACE( 0x98) | ||
1358 | |||
1359 | // can we find an FC device mapping to this SCSI target? | ||
1360 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
1361 | Cmnd, // search Scsi Nexus | ||
1362 | 0, // DON'T search linked list for FC port id | ||
1363 | NULL, // DON'T search linked list for FC WWN | ||
1364 | NULL); // DON'T care about end of list | ||
1365 | |||
1366 | if( pLoggedInPort == NULL ) // not found! | ||
1367 | { | ||
1368 | // printk(" @Q bad targ cmnd %p@ ", Cmnd); | ||
1369 | QueBadTargetCmnd( cpqfcHBAdata, Cmnd); | ||
1370 | } | ||
1371 | else if (Cmnd->device->lun >= CPQFCTS_MAX_LUN) | ||
1372 | { | ||
1373 | printk(KERN_WARNING "cpqfc: Invalid LUN: %d\n", Cmnd->device->lun); | ||
1374 | QueBadTargetCmnd( cpqfcHBAdata, Cmnd); | ||
1375 | } | ||
1376 | |||
1377 | else // we know what FC device to send to... | ||
1378 | { | ||
1379 | |||
1380 | // does this device support FCP target functions? | ||
1381 | // (determined by PRLI field) | ||
1382 | |||
1383 | if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) ) | ||
1384 | { | ||
1385 | printk(" Doesn't support TARGET functions port_id %Xh\n", | ||
1386 | pLoggedInPort->port_id ); | ||
1387 | QueBadTargetCmnd( cpqfcHBAdata, Cmnd); | ||
1388 | } | ||
1389 | |||
1390 | // In this case (previous login OK), the device is temporarily | ||
1391 | // unavailable waiting for re-login, in which case we expect it | ||
1392 | // to be back in between 25 - 500ms. | ||
1393 | // If the FC port doesn't log back in within several seconds | ||
1394 | // (i.e. implicit "logout"), or we get an explicit logout, | ||
1395 | // we set "device_blocked" in Scsi_Device struct; in this | ||
1396 | // case 30 seconds will elapse before Linux/Scsi sends another | ||
1397 | // command to the device. | ||
1398 | else if( pLoggedInPort->prli != TRUE ) | ||
1399 | { | ||
1400 | // printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n", | ||
1401 | // Cmnd->channel, Cmnd->target, pLoggedInPort->port_id); | ||
1402 | QueLinkDownCmnd( cpqfcHBAdata, Cmnd); | ||
1403 | // Need to use "blocked" flag?? | ||
1404 | // Cmnd->device->device_blocked = TRUE; // just let it timeout | ||
1405 | } | ||
1406 | else // device supports TARGET functions, and is logged in... | ||
1407 | { | ||
1408 | // (context of fchs is to "reply" to...) | ||
1409 | fchs.s_id = pLoggedInPort->port_id; // destination FC address | ||
1410 | |||
1411 | // what is the data direction? For data TO the device, | ||
1412 | // we need IWE (Intiator Write Entry). Otherwise, IRE. | ||
1413 | |||
1414 | if( Cmnd->cmnd[0] == WRITE_10 || | ||
1415 | Cmnd->cmnd[0] == WRITE_6 || | ||
1416 | Cmnd->cmnd[0] == WRITE_BUFFER || | ||
1417 | Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE || // CPQ specific | ||
1418 | Cmnd->cmnd[0] == MODE_SELECT ) | ||
1419 | { | ||
1420 | SESTtype = SCSI_IWE; // data from HBA to Device | ||
1421 | } | ||
1422 | else | ||
1423 | SESTtype = SCSI_IRE; // data from Device to HBA | ||
1424 | |||
1425 | ulStatus = cpqfcTSBuildExchange( | ||
1426 | cpqfcHBAdata, | ||
1427 | SESTtype, // e.g. Initiator Read Entry (IRE) | ||
1428 | &fchs, // we are originator; only use d_id | ||
1429 | Cmnd, // Linux SCSI command (with scatter/gather list) | ||
1430 | &ExchangeID );// fcController->fcExchanges index, -1 if failed | ||
1431 | |||
1432 | if( !ulStatus ) // Exchange setup? | ||
1433 | |||
1434 | { | ||
1435 | if( cpqfcHBAdata->BoardLock ) | ||
1436 | { | ||
1437 | TriggerHBA( fcChip->Registers.ReMapMemBase, 0); | ||
1438 | printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID); | ||
1439 | } | ||
1440 | |||
1441 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); | ||
1442 | if( !ulStatus ) | ||
1443 | { | ||
1444 | PCI_TRACEO( ExchangeID, 0xB8) | ||
1445 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
1446 | // waited for completion for ELS type (Login frames issued | ||
1447 | // synchronously) | ||
1448 | } | ||
1449 | else | ||
1450 | // check reason for Exchange not being started - we might | ||
1451 | // want to Queue and start later, or fail with error | ||
1452 | { | ||
1453 | printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus ); | ||
1454 | } | ||
1455 | } // end good BuildExchange status | ||
1456 | |||
1457 | else // SEST table probably full -- why? hardware hang? | ||
1458 | { | ||
1459 | printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus); | ||
1460 | } | ||
1461 | } // end can't do FCP-SCSI target functions | ||
1462 | } // end can't find target (FC device) | ||
1463 | |||
1464 | CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) | ||
1465 | } | ||
1466 | |||
1467 | PCI_TRACEO( (ULONG)Cmnd, 0x9C) | ||
1468 | LEAVE("cpqfcTS_queuecommand"); | ||
1469 | return 0; | ||
1470 | } | ||
1471 | |||
1472 | |||
1473 | // Entry point for upper Scsi layer intiated abort. Typically | ||
1474 | // this is called if the command (for hard disk) fails to complete | ||
1475 | // in 30 seconds. This driver intends to complete all disk commands | ||
1476 | // within Exchange ".timeOut" seconds (now 7) with target status, or | ||
1477 | // in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes | ||
1478 | // immediate retry. | ||
1479 | // If any disk commands get the _abort call, except for the case that | ||
1480 | // the physical device was removed or unavailable due to hardware | ||
1481 | // errors, it should be considered a driver error and reported to | ||
1482 | // the author. | ||
1483 | |||
1484 | int cpqfcTS_abort(Scsi_Cmnd *Cmnd) | ||
1485 | { | ||
1486 | // printk(" cpqfcTS_abort called?? \n"); | ||
1487 | return 0; | ||
1488 | } | ||
1489 | |||
1490 | int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd) | ||
1491 | { | ||
1492 | |||
1493 | struct Scsi_Host *HostAdapter = Cmnd->device->host; | ||
1494 | // get the pointer to our Scsi layer HBA buffer | ||
1495 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
1496 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1497 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
1498 | int i; | ||
1499 | ENTER("cpqfcTS_eh_abort"); | ||
1500 | |||
1501 | Cmnd->result = DID_ABORT <<16; // assume we'll find it | ||
1502 | |||
1503 | printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd); | ||
1504 | // See if we can find a Cmnd pointer that matches... | ||
1505 | // The most likely case is we accepted the command | ||
1506 | // from Linux Scsi (e.g. ceated a SEST entry) and it | ||
1507 | // got lost somehow. If we can't find any reference | ||
1508 | // to the passed pointer, we can only presume it | ||
1509 | // got completed as far as our driver is concerned. | ||
1510 | // If we found it, we will try to abort it through | ||
1511 | // common mechanism. If FC ABTS is successful (ACC) | ||
1512 | // or is rejected (RJT) by target, we will call | ||
1513 | // Scsi "done" quickly. Otherwise, the ABTS will timeout | ||
1514 | // and we'll call "done" later. | ||
1515 | |||
1516 | // Search the SEST exchanges for a matching Cmnd ptr. | ||
1517 | for( i=0; i< TACH_SEST_LEN; i++) | ||
1518 | { | ||
1519 | if( Exchanges->fcExchange[i].Cmnd == Cmnd ) | ||
1520 | { | ||
1521 | |||
1522 | // found it! | ||
1523 | printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type); | ||
1524 | |||
1525 | Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default | ||
1526 | Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later) | ||
1527 | |||
1528 | // Since we need to immediately return the aborted Cmnd to Scsi | ||
1529 | // upper layers, we can't make future reference to any of its | ||
1530 | // fields (e.g the Nexus). | ||
1531 | |||
1532 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i); | ||
1533 | |||
1534 | break; | ||
1535 | } | ||
1536 | } | ||
1537 | |||
1538 | if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST? | ||
1539 | { | ||
1540 | // now search our non-SEST buffers (i.e. Cmnd waiting to | ||
1541 | // start on the HBA or waiting to complete with error for retry). | ||
1542 | |||
1543 | // first check BadTargetCmnd | ||
1544 | for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) | ||
1545 | { | ||
1546 | if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd ) | ||
1547 | { | ||
1548 | cpqfcHBAdata->BadTargetCmnd[i] = NULL; | ||
1549 | printk("in BadTargetCmnd Q\n"); | ||
1550 | goto Done; // exit | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1554 | // if not found above... | ||
1555 | |||
1556 | for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++) | ||
1557 | { | ||
1558 | if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd ) | ||
1559 | { | ||
1560 | cpqfcHBAdata->LinkDnCmnd[i] = NULL; | ||
1561 | printk("in LinkDnCmnd Q\n"); | ||
1562 | goto Done; | ||
1563 | } | ||
1564 | } | ||
1565 | |||
1566 | |||
1567 | for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++) | ||
1568 | { // find spare slot | ||
1569 | if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd ) | ||
1570 | { | ||
1571 | cpqfcHBAdata->BoardLockCmnd[i] = NULL; | ||
1572 | printk("in BoardLockCmnd Q\n"); | ||
1573 | goto Done; | ||
1574 | } | ||
1575 | } | ||
1576 | |||
1577 | Cmnd->result = DID_ERROR <<16; // Hmmm... | ||
1578 | printk("Not found! "); | ||
1579 | // panic("_abort"); | ||
1580 | } | ||
1581 | |||
1582 | Done: | ||
1583 | |||
1584 | // panic("_abort"); | ||
1585 | LEAVE("cpqfcTS_eh_abort"); | ||
1586 | return 0; // (see scsi.h) | ||
1587 | } | ||
1588 | |||
1589 | |||
1590 | // FCP-SCSI Target Device Reset | ||
1591 | // See dpANS Fibre Channel Protocol for SCSI | ||
1592 | // X3.269-199X revision 12, pg 25 | ||
1593 | |||
1594 | #ifdef SUPPORT_RESET | ||
1595 | |||
1596 | int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, | ||
1597 | unsigned int reset_flags) | ||
1598 | { | ||
1599 | int timeout = 10*HZ; | ||
1600 | int retries = 1; | ||
1601 | char scsi_cdb[12]; | ||
1602 | int result; | ||
1603 | Scsi_Cmnd * SCpnt; | ||
1604 | Scsi_Device * SDpnt; | ||
1605 | |||
1606 | // FIXME, cpqfcTS_TargetDeviceReset needs to be fixed | ||
1607 | // similarly to how the passthrough ioctl was fixed | ||
1608 | // around the 2.5.30 kernel. Scsi_Cmnd replaced with | ||
1609 | // Scsi_Request, etc. | ||
1610 | // For now, so people don't fall into a hole... | ||
1611 | |||
1612 | // printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags); | ||
1613 | |||
1614 | if (ScsiDev->host->eh_active) return FAILED; | ||
1615 | |||
1616 | memset( scsi_cdb, 0, sizeof( scsi_cdb)); | ||
1617 | |||
1618 | scsi_cdb[0] = RELEASE; | ||
1619 | |||
1620 | SCpnt = scsi_get_command(ScsiDev, GFP_KERNEL); | ||
1621 | { | ||
1622 | CPQFC_DECLARE_COMPLETION(wait); | ||
1623 | |||
1624 | SCpnt->SCp.buffers_residual = FCP_TARGET_RESET; | ||
1625 | |||
1626 | // FIXME: this would panic, SCpnt->request would be NULL. | ||
1627 | SCpnt->request->CPQFC_WAITING = &wait; | ||
1628 | scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries); | ||
1629 | CPQFC_WAIT_FOR_COMPLETION(&wait); | ||
1630 | SCpnt->request->CPQFC_WAITING = NULL; | ||
1631 | } | ||
1632 | |||
1633 | |||
1634 | if(driver_byte(SCpnt->result) != 0) | ||
1635 | switch(SCpnt->sense_buffer[2] & 0xf) { | ||
1636 | case ILLEGAL_REQUEST: | ||
1637 | if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0; | ||
1638 | else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n"); | ||
1639 | break; | ||
1640 | case NOT_READY: // This happens if there is no disc in drive | ||
1641 | if(dev->removable && (cmd[0] != TEST_UNIT_READY)){ | ||
1642 | printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n"); | ||
1643 | break; | ||
1644 | } | ||
1645 | case UNIT_ATTENTION: | ||
1646 | if (dev->removable){ | ||
1647 | dev->changed = 1; | ||
1648 | SCpnt->result = 0; // This is no longer considered an error | ||
1649 | // gag this error, VFS will log it anyway /axboe | ||
1650 | // printk(KERN_INFO "Disc change detected.\n"); | ||
1651 | break; | ||
1652 | }; | ||
1653 | default: // Fall through for non-removable media | ||
1654 | printk("SCSI error: host %d id %d lun %d return code = %x\n", | ||
1655 | dev->host->host_no, | ||
1656 | dev->id, | ||
1657 | dev->lun, | ||
1658 | SCpnt->result); | ||
1659 | printk("\tSense class %x, sense error %x, extended sense %x\n", | ||
1660 | sense_class(SCpnt->sense_buffer[0]), | ||
1661 | sense_error(SCpnt->sense_buffer[0]), | ||
1662 | SCpnt->sense_buffer[2] & 0xf); | ||
1663 | |||
1664 | }; | ||
1665 | result = SCpnt->result; | ||
1666 | |||
1667 | SDpnt = SCpnt->device; | ||
1668 | scsi_put_command(SCpnt); | ||
1669 | SCpnt = NULL; | ||
1670 | |||
1671 | // printk(" LEAVING cpqfcTS_TargetDeviceReset() - return SUCCESS \n"); | ||
1672 | return SUCCESS; | ||
1673 | } | ||
1674 | |||
1675 | #else | ||
1676 | int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, | ||
1677 | unsigned int reset_flags) | ||
1678 | { | ||
1679 | return -ENOTSUPP; | ||
1680 | } | ||
1681 | |||
1682 | #endif /* SUPPORT_RESET */ | ||
1683 | |||
1684 | int cpqfcTS_eh_device_reset(Scsi_Cmnd *Cmnd) | ||
1685 | { | ||
1686 | int retval; | ||
1687 | Scsi_Device *SDpnt = Cmnd->device; | ||
1688 | // printk(" ENTERING cpqfcTS_eh_device_reset() \n"); | ||
1689 | spin_unlock_irq(Cmnd->device->host->host_lock); | ||
1690 | retval = cpqfcTS_TargetDeviceReset( SDpnt, 0); | ||
1691 | spin_lock_irq(Cmnd->device->host->host_lock); | ||
1692 | return retval; | ||
1693 | } | ||
1694 | |||
1695 | |||
1696 | int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags) | ||
1697 | { | ||
1698 | |||
1699 | ENTER("cpqfcTS_reset"); | ||
1700 | |||
1701 | LEAVE("cpqfcTS_reset"); | ||
1702 | return SCSI_RESET_ERROR; /* Bus Reset Not supported */ | ||
1703 | } | ||
1704 | |||
1705 | /* This function determines the bios parameters for a given | ||
1706 | harddisk. These tend to be numbers that are made up by the | ||
1707 | host adapter. Parameters: | ||
1708 | size, device number, list (heads, sectors,cylinders). | ||
1709 | (from hosts.h) | ||
1710 | */ | ||
1711 | |||
1712 | int cpqfcTS_biosparam(struct scsi_device *sdev, struct block_device *n, | ||
1713 | sector_t capacity, int ip[]) | ||
1714 | { | ||
1715 | int size = capacity; | ||
1716 | |||
1717 | ENTER("cpqfcTS_biosparam"); | ||
1718 | ip[0] = 64; | ||
1719 | ip[1] = 32; | ||
1720 | ip[2] = size >> 11; | ||
1721 | |||
1722 | if( ip[2] > 1024 ) | ||
1723 | { | ||
1724 | ip[0] = 255; | ||
1725 | ip[1] = 63; | ||
1726 | ip[2] = size / (ip[0] * ip[1]); | ||
1727 | } | ||
1728 | |||
1729 | LEAVE("cpqfcTS_biosparam"); | ||
1730 | return 0; | ||
1731 | } | ||
1732 | |||
1733 | |||
1734 | |||
1735 | irqreturn_t cpqfcTS_intr_handler( int irq, | ||
1736 | void *dev_id, | ||
1737 | struct pt_regs *regs) | ||
1738 | { | ||
1739 | |||
1740 | unsigned long flags, InfLoopBrk=0; | ||
1741 | struct Scsi_Host *HostAdapter = dev_id; | ||
1742 | CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata; | ||
1743 | int MoreMessages = 1; // assume we have something to do | ||
1744 | UCHAR IntPending; | ||
1745 | int handled = 0; | ||
1746 | |||
1747 | ENTER("intr_handler"); | ||
1748 | spin_lock_irqsave( HostAdapter->host_lock, flags); | ||
1749 | // is this our INT? | ||
1750 | IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address); | ||
1751 | |||
1752 | // broken boards can generate messages forever, so | ||
1753 | // prevent the infinite loop | ||
1754 | #define INFINITE_IMQ_BREAK 10000 | ||
1755 | if( IntPending ) | ||
1756 | { | ||
1757 | handled = 1; | ||
1758 | // mask our HBA interrupts until we handle it... | ||
1759 | writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address); | ||
1760 | |||
1761 | if( IntPending & 0x4) // "INT" - Tach wrote to IMQ | ||
1762 | { | ||
1763 | while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) ) | ||
1764 | { | ||
1765 | MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done | ||
1766 | } | ||
1767 | if( InfLoopBrk >= INFINITE_IMQ_BREAK ) | ||
1768 | { | ||
1769 | printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n"); | ||
1770 | printk("or investigate alternate causes (e.g. physical FC layer)\n"); | ||
1771 | } | ||
1772 | |||
1773 | else // working normally - re-enable INTs and continue | ||
1774 | writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address); | ||
1775 | |||
1776 | } // (...ProcessIMQEntry() clears INT by writing IMQ consumer) | ||
1777 | else // indications of errors or problems... | ||
1778 | // these usually indicate critical system hardware problems. | ||
1779 | { | ||
1780 | if( IntPending & 0x10 ) | ||
1781 | printk(" cpqfcTS adapter external memory parity error detected\n"); | ||
1782 | if( IntPending & 0x8 ) | ||
1783 | printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n"); | ||
1784 | if( IntPending & 0x2 ) | ||
1785 | printk(" cpqfcTS adapter DMA error detected\n"); | ||
1786 | if( IntPending & 0x1 ) { | ||
1787 | UCHAR IntStat; | ||
1788 | printk(" cpqfcTS adapter PCI error detected\n"); | ||
1789 | IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address); | ||
1790 | printk("cpqfc: ISR = 0x%02x\n", IntStat); | ||
1791 | if (IntStat & 0x1) { | ||
1792 | __u16 pcistat; | ||
1793 | /* read the pci status register */ | ||
1794 | pci_read_config_word(cpqfcHBA->PciDev, 0x06, &pcistat); | ||
1795 | printk("PCI status register is 0x%04x\n", pcistat); | ||
1796 | if (pcistat & 0x8000) printk("Parity Error Detected.\n"); | ||
1797 | if (pcistat & 0x4000) printk("Signalled System Error\n"); | ||
1798 | if (pcistat & 0x2000) printk("Received Master Abort\n"); | ||
1799 | if (pcistat & 0x1000) printk("Received Target Abort\n"); | ||
1800 | if (pcistat & 0x0800) printk("Signalled Target Abort\n"); | ||
1801 | } | ||
1802 | if (IntStat & 0x4) printk("(INT)\n"); | ||
1803 | if (IntStat & 0x8) | ||
1804 | printk("CRS: PCI master address crossed 46 bit bouandary\n"); | ||
1805 | if (IntStat & 0x10) printk("MRE: external memory parity error.\n"); | ||
1806 | } | ||
1807 | } | ||
1808 | } | ||
1809 | spin_unlock_irqrestore( HostAdapter->host_lock, flags); | ||
1810 | LEAVE("intr_handler"); | ||
1811 | return IRQ_RETVAL(handled); | ||
1812 | } | ||
1813 | |||
1814 | |||
1815 | |||
1816 | |||
1817 | int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]) | ||
1818 | { | ||
1819 | // Verify GBIC type (if any) and correct Tachyon Port State Machine | ||
1820 | // (GBIC) module definition is: | ||
1821 | // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0. The input states appear | ||
1822 | // to be inverted -- i.e., a setting of 111 is read when there is NO | ||
1823 | // GBIC present. The Module Def (MD) spec says 000 is "no GBIC" | ||
1824 | // Hard code the bit states to detect Copper, | ||
1825 | // Long wave (single mode), Short wave (multi-mode), and absent GBIC | ||
1826 | |||
1827 | ULONG ulBuff; | ||
1828 | |||
1829 | sprintf( cErrorString, "\nGBIC detected: "); | ||
1830 | |||
1831 | ulBuff = fcChip->Registers.TYstatus.value & 0x13; | ||
1832 | switch( ulBuff ) | ||
1833 | { | ||
1834 | case 0x13: // GPIO4, GPIO1, GPIO0 = 111; no GBIC! | ||
1835 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1836 | "NONE! "); | ||
1837 | return FALSE; | ||
1838 | |||
1839 | |||
1840 | case 0x11: // Copper GBIC detected | ||
1841 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1842 | "Copper. "); | ||
1843 | break; | ||
1844 | |||
1845 | case 0x10: // Long-wave (single mode) GBIC detected | ||
1846 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1847 | "Long-wave. "); | ||
1848 | break; | ||
1849 | case 0x1: // Short-wave (multi mode) GBIC detected | ||
1850 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1851 | "Short-wave. "); | ||
1852 | break; | ||
1853 | default: // unknown GBIC - presumably it will work (?) | ||
1854 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1855 | "Unknown. "); | ||
1856 | |||
1857 | break; | ||
1858 | } // end switch GBIC detection | ||
1859 | |||
1860 | return TRUE; | ||
1861 | } | ||
1862 | |||
1863 | |||
1864 | |||
1865 | |||
1866 | |||
1867 | |||
1868 | int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]) | ||
1869 | { | ||
1870 | // Tachyon's Frame Manager LPSM in LinkDown state? | ||
1871 | // (For non-loop port, check PSM instead.) | ||
1872 | // return string with state and FALSE is Link Down | ||
1873 | |||
1874 | int LinkUp; | ||
1875 | |||
1876 | if( fcChip->Registers.FMstatus.value & 0x80 ) | ||
1877 | LinkUp = FALSE; | ||
1878 | else | ||
1879 | LinkUp = TRUE; | ||
1880 | |||
1881 | sprintf( &cErrorString[ strlen( cErrorString)], | ||
1882 | " LPSM %Xh ", | ||
1883 | (fcChip->Registers.FMstatus.value >>4) & 0xf ); | ||
1884 | |||
1885 | |||
1886 | switch( fcChip->Registers.FMstatus.value & 0xF0) | ||
1887 | { | ||
1888 | // bits set in LPSM | ||
1889 | case 0x10: | ||
1890 | sprintf( &cErrorString[ strlen( cErrorString)], "ARB"); | ||
1891 | break; | ||
1892 | case 0x20: | ||
1893 | sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon"); | ||
1894 | break; | ||
1895 | case 0x30: | ||
1896 | sprintf( &cErrorString[ strlen( cErrorString)], "OPEN"); | ||
1897 | break; | ||
1898 | case 0x40: | ||
1899 | sprintf( &cErrorString[ strlen( cErrorString)], "OPENed"); | ||
1900 | break; | ||
1901 | case 0x50: | ||
1902 | sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS"); | ||
1903 | break; | ||
1904 | case 0x60: | ||
1905 | sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS"); | ||
1906 | break; | ||
1907 | case 0x70: | ||
1908 | sprintf( &cErrorString[ strlen( cErrorString)], "Xfer"); | ||
1909 | break; | ||
1910 | case 0x80: | ||
1911 | sprintf( &cErrorString[ strlen( cErrorString)], "Init"); | ||
1912 | break; | ||
1913 | case 0x90: | ||
1914 | sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin"); | ||
1915 | break; | ||
1916 | case 0xa0: | ||
1917 | sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol"); | ||
1918 | break; | ||
1919 | case 0xb0: | ||
1920 | sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd"); | ||
1921 | break; | ||
1922 | case 0xc0: | ||
1923 | sprintf( &cErrorString[ strlen( cErrorString)], "HostControl"); | ||
1924 | break; | ||
1925 | case 0xd0: | ||
1926 | sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail"); | ||
1927 | break; | ||
1928 | case 0xe0: | ||
1929 | sprintf( &cErrorString[ strlen( cErrorString)], "Offline"); | ||
1930 | break; | ||
1931 | case 0xf0: | ||
1932 | sprintf( &cErrorString[ strlen( cErrorString)], "OldPort"); | ||
1933 | break; | ||
1934 | case 0: | ||
1935 | default: | ||
1936 | sprintf( &cErrorString[ strlen( cErrorString)], "Monitor"); | ||
1937 | break; | ||
1938 | |||
1939 | } | ||
1940 | |||
1941 | return LinkUp; | ||
1942 | } | ||
1943 | |||
1944 | |||
1945 | |||
1946 | |||
1947 | #include "linux/slab.h" | ||
1948 | |||
1949 | // Dynamic memory allocation alignment routines | ||
1950 | // HP's Tachyon Fibre Channel Controller chips require | ||
1951 | // certain memory queues and register pointers to be aligned | ||
1952 | // on various boundaries, usually the size of the Queue in question. | ||
1953 | // Alignment might be on 2, 4, 8, ... or even 512 byte boundaries. | ||
1954 | // Since most O/Ss don't allow this (usually only Cache aligned - | ||
1955 | // 32-byte boundary), these routines provide generic alignment (after | ||
1956 | // O/S allocation) at any boundary, and store the original allocated | ||
1957 | // pointer for deletion (O/S free function). Typically, we expect | ||
1958 | // these functions to only be called at HBA initialization and | ||
1959 | // removal time (load and unload times) | ||
1960 | // ALGORITHM notes: | ||
1961 | // Memory allocation varies by compiler and platform. In the worst case, | ||
1962 | // we are only assured BYTE alignment, but in the best case, we can | ||
1963 | // request allocation on any desired boundary. Our strategy: pad the | ||
1964 | // allocation request size (i.e. waste memory) so that we are assured | ||
1965 | // of passing desired boundary near beginning of contiguous space, then | ||
1966 | // mask out lower address bits. | ||
1967 | // We define the following algorithm: | ||
1968 | // allocBoundary - compiler/platform specific address alignment | ||
1969 | // in number of bytes (default is single byte; i.e. 1) | ||
1970 | // n_alloc - number of bytes application wants @ aligned address | ||
1971 | // ab - alignment boundary, in bytes (e.g. 4, 32, ...) | ||
1972 | // t_alloc - total allocation needed to ensure desired boundary | ||
1973 | // mask - to clear least significant address bits for boundary | ||
1974 | // Compute: | ||
1975 | // t_alloc = n_alloc + (ab - allocBoundary) | ||
1976 | // allocate t_alloc bytes @ alloc_address | ||
1977 | // mask = NOT (ab - 1) | ||
1978 | // (e.g. if ab=32 _0001 1111 -> _1110 0000 | ||
1979 | // aligned_address = alloc_address & mask | ||
1980 | // set n_alloc bytes to 0 | ||
1981 | // return aligned_address (NULL if failed) | ||
1982 | // | ||
1983 | // If u32_AlignedAddress is non-zero, then search for BaseAddress (stored | ||
1984 | // from previous allocation). If found, invoke call to FREE the memory. | ||
1985 | // Return NULL if BaseAddress not found | ||
1986 | |||
1987 | // we need about 8 allocations per HBA. Figuring at most 10 HBAs per server | ||
1988 | // size the dynamic_mem array at 80. | ||
1989 | |||
1990 | void* fcMemManager( struct pci_dev *pdev, ALIGNED_MEM *dynamic_mem, | ||
1991 | ULONG n_alloc, ULONG ab, ULONG u32_AlignedAddress, | ||
1992 | dma_addr_t *dma_handle) | ||
1993 | { | ||
1994 | USHORT allocBoundary=1; // compiler specific - worst case 1 | ||
1995 | // best case - replace malloc() call | ||
1996 | // with function that allocates exactly | ||
1997 | // at desired boundary | ||
1998 | |||
1999 | unsigned long ulAddress; | ||
2000 | ULONG t_alloc, i; | ||
2001 | void *alloc_address = 0; // def. error code / address not found | ||
2002 | LONG mask; // must be 32-bits wide! | ||
2003 | |||
2004 | ENTER("fcMemManager"); | ||
2005 | if( u32_AlignedAddress ) // are we freeing existing memory? | ||
2006 | { | ||
2007 | // printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress); | ||
2008 | for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for the base address | ||
2009 | { | ||
2010 | // printk("dynamic_mem[%u].AlignedAddress %lX\n", i, dynamic_mem[i].AlignedAddress); | ||
2011 | if( dynamic_mem[i].AlignedAddress == u32_AlignedAddress ) | ||
2012 | { | ||
2013 | alloc_address = dynamic_mem[i].BaseAllocated; // 'success' status | ||
2014 | pci_free_consistent(pdev,dynamic_mem[i].size, | ||
2015 | alloc_address, | ||
2016 | dynamic_mem[i].dma_handle); | ||
2017 | dynamic_mem[i].BaseAllocated = 0; // clear for next use | ||
2018 | dynamic_mem[i].AlignedAddress = 0; | ||
2019 | dynamic_mem[i].size = 0; | ||
2020 | break; // quit for loop; done | ||
2021 | } | ||
2022 | } | ||
2023 | } | ||
2024 | else if( n_alloc ) // want new memory? | ||
2025 | { | ||
2026 | dma_addr_t handle; | ||
2027 | t_alloc = n_alloc + (ab - allocBoundary); // pad bytes for alignment | ||
2028 | // printk("pci_alloc_consistent() for Tach alignment: %ld bytes\n", t_alloc); | ||
2029 | |||
2030 | // (would like to) allow thread block to free pages | ||
2031 | alloc_address = // total bytes (NumberOfBytes) | ||
2032 | pci_alloc_consistent(pdev, t_alloc, &handle); | ||
2033 | |||
2034 | // now mask off least sig. bits of address | ||
2035 | if( alloc_address ) // (only if non-NULL) | ||
2036 | { | ||
2037 | // find place to store ptr, so we | ||
2038 | // can free it later... | ||
2039 | |||
2040 | mask = (LONG)(ab - 1); // mask all low-order bits | ||
2041 | mask = ~mask; // invert bits | ||
2042 | for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for free slot | ||
2043 | { | ||
2044 | if( dynamic_mem[i].BaseAllocated == 0) // take 1st available | ||
2045 | { | ||
2046 | dynamic_mem[i].BaseAllocated = alloc_address;// address from O/S | ||
2047 | dynamic_mem[i].dma_handle = handle; | ||
2048 | if (dma_handle != NULL) | ||
2049 | { | ||
2050 | // printk("handle = %p, ab=%d, boundary = %d, mask=0x%08x\n", | ||
2051 | // handle, ab, allocBoundary, mask); | ||
2052 | *dma_handle = (dma_addr_t) | ||
2053 | ((((ULONG)handle) + (ab - allocBoundary)) & mask); | ||
2054 | } | ||
2055 | dynamic_mem[i].size = t_alloc; | ||
2056 | break; | ||
2057 | } | ||
2058 | } | ||
2059 | ulAddress = (unsigned long)alloc_address; | ||
2060 | |||
2061 | ulAddress += (ab - allocBoundary); // add the alignment bytes- | ||
2062 | // then truncate address... | ||
2063 | alloc_address = (void*)(ulAddress & mask); | ||
2064 | |||
2065 | dynamic_mem[i].AlignedAddress = | ||
2066 | (ULONG)(ulAddress & mask); // 32bit Tach address | ||
2067 | memset( alloc_address, 0, n_alloc ); // clear new memory | ||
2068 | } | ||
2069 | else // O/S dynamic mem alloc failed! | ||
2070 | alloc_address = 0; // (for debugging breakpt) | ||
2071 | |||
2072 | } | ||
2073 | |||
2074 | LEAVE("fcMemManager"); | ||
2075 | return alloc_address; // good (or NULL) address | ||
2076 | } | ||
2077 | |||
2078 | |||
2079 | static Scsi_Host_Template driver_template = { | ||
2080 | .detect = cpqfcTS_detect, | ||
2081 | .release = cpqfcTS_release, | ||
2082 | .info = cpqfcTS_info, | ||
2083 | .proc_info = cpqfcTS_proc_info, | ||
2084 | .ioctl = cpqfcTS_ioctl, | ||
2085 | .queuecommand = cpqfcTS_queuecommand, | ||
2086 | .eh_device_reset_handler = cpqfcTS_eh_device_reset, | ||
2087 | .eh_abort_handler = cpqfcTS_eh_abort, | ||
2088 | .bios_param = cpqfcTS_biosparam, | ||
2089 | .can_queue = CPQFCTS_REQ_QUEUE_LEN, | ||
2090 | .this_id = -1, | ||
2091 | .sg_tablesize = SG_ALL, | ||
2092 | .cmd_per_lun = CPQFCTS_CMD_PER_LUN, | ||
2093 | .use_clustering = ENABLE_CLUSTERING, | ||
2094 | }; | ||
2095 | #include "scsi_module.c" | ||
2096 | |||
diff --git a/drivers/scsi/cpqfcTSioctl.h b/drivers/scsi/cpqfcTSioctl.h deleted file mode 100644 index 825536969126..000000000000 --- a/drivers/scsi/cpqfcTSioctl.h +++ /dev/null | |||
@@ -1,94 +0,0 @@ | |||
1 | // for user apps, make sure data size types are defined | ||
2 | // with | ||
3 | |||
4 | |||
5 | #define CCPQFCTS_IOC_MAGIC 'Z' | ||
6 | |||
7 | typedef struct | ||
8 | { | ||
9 | __u8 bus; | ||
10 | __u8 dev_fn; | ||
11 | __u32 board_id; | ||
12 | } cpqfc_pci_info_struct; | ||
13 | |||
14 | typedef __u32 DriverVer_type; | ||
15 | /* | ||
16 | typedef union | ||
17 | { | ||
18 | struct // Peripheral Unit Device | ||
19 | { | ||
20 | __u8 Bus:6; | ||
21 | __u8 Mode:2; // b00 | ||
22 | __u8 Dev; | ||
23 | } PeripDev; | ||
24 | struct // Volume Set Address | ||
25 | { | ||
26 | __u8 DevMSB:6; | ||
27 | __u8 Mode:2; // b01 | ||
28 | __u8 DevLSB; | ||
29 | } LogDev; | ||
30 | struct // Logical Unit Device (SCSI-3, SCC-2 defined) | ||
31 | { | ||
32 | __u8 Targ:6; | ||
33 | __u8 Mode:2; // b10 | ||
34 | __u8 Dev:5; | ||
35 | __u8 Bus:3; | ||
36 | |||
37 | } LogUnit; | ||
38 | } SCSI3Addr_struct; | ||
39 | |||
40 | |||
41 | typedef struct | ||
42 | { | ||
43 | SCSI3Addr_struct FCP_Nexus; | ||
44 | __u8 cdb[16]; | ||
45 | } PassThru_Command_struct; | ||
46 | */ | ||
47 | |||
48 | /* this is nearly duplicated in idashare.h */ | ||
49 | typedef struct { | ||
50 | int lc; /* Controller number */ | ||
51 | int node; /* Node (box) number */ | ||
52 | int ld; /* Logical Drive on this box, if required */ | ||
53 | __u32 nexus; /* SCSI Nexus */ | ||
54 | void *argp; /* Argument pointer */ | ||
55 | } VENDOR_IOCTL_REQ; | ||
56 | |||
57 | |||
58 | typedef struct { | ||
59 | char cdb[16]; /* SCSI CDB for the pass-through */ | ||
60 | ushort bus; /* Target bus on the box */ | ||
61 | ushort pdrive; /* Physical drive on the box */ | ||
62 | int len; /* Length of the data area of the CDB */ | ||
63 | int sense_len; /* Length of the sense data */ | ||
64 | char sense_data[40]; /* Sense data */ | ||
65 | void *bufp; /* Data area for the CDB */ | ||
66 | char rw_flag; /* Read CDB or Write CDB */ | ||
67 | } cpqfc_passthru_t; | ||
68 | |||
69 | /* | ||
70 | ** Defines for the IOCTLS. | ||
71 | */ | ||
72 | |||
73 | #define VENDOR_READ_OPCODE 0x26 | ||
74 | #define VENDOR_WRITE_OPCODE 0x27 | ||
75 | |||
76 | #define CPQFCTS_GETPCIINFO _IOR( CCPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct) | ||
77 | #define CPQFCTS_GETDRIVVER _IOR( CCPQFCTS_IOC_MAGIC, 9, DriverVer_type) | ||
78 | |||
79 | #define CPQFCTS_SCSI_PASSTHRU _IOWR( CCPQFCTS_IOC_MAGIC,11, VENDOR_IOCTL_REQ) | ||
80 | |||
81 | /* We would rather have equivalent generic, low-level driver agnostic | ||
82 | ioctls that do what CPQFC_IOCTL_FC_TARGET_ADDRESS and | ||
83 | CPQFC_IOCTL_FC_TDR 0x5388 do, but currently, we do not have them, | ||
84 | consequently applications would have to know they are talking to cpqfc. */ | ||
85 | |||
86 | /* Used to get Fibre Channel WWN and port_id from device */ | ||
87 | // #define CPQFC_IOCTL_FC_TARGET_ADDRESS 0x5387 | ||
88 | #define CPQFC_IOCTL_FC_TARGET_ADDRESS \ | ||
89 | _IOR( CCPQFCTS_IOC_MAGIC, 13, Scsi_FCTargAddress) | ||
90 | |||
91 | /* Used to invoke Target Defice Reset for Fibre Channel */ | ||
92 | // #define CPQFC_IOCTL_FC_TDR 0x5388 | ||
93 | #define CPQFC_IOCTL_FC_TDR _IO( CCPQFCTS_IOC_MAGIC, 15) | ||
94 | |||
diff --git a/drivers/scsi/cpqfcTSstructs.h b/drivers/scsi/cpqfcTSstructs.h deleted file mode 100644 index 0bae3298c44b..000000000000 --- a/drivers/scsi/cpqfcTSstructs.h +++ /dev/null | |||
@@ -1,1530 +0,0 @@ | |||
1 | /* Copyright(c) 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter 64-bit, 66MHz PCI | ||
3 | * Originally developed and tested on: | ||
4 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
5 | * SP# P225CXCBFIEL6T, Rev XC | ||
6 | * SP# 161290-001, Rev XD | ||
7 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2, or (at your option) any | ||
12 | * later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * Written by Don Zimmerman | ||
19 | */ | ||
20 | #ifndef CPQFCTSSTRUCTS_H | ||
21 | #define CPQFCTSSTRUCTS_H | ||
22 | |||
23 | #include <linux/timer.h> // timer declaration in our host data | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <asm/atomic.h> | ||
26 | #include "cpqfcTSioctl.h" | ||
27 | |||
28 | #define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \ | ||
29 | for( wait_time=jiffies + (secs*HZ); \ | ||
30 | time_before(jiffies, wait_time) ;) ; } | ||
31 | |||
32 | #define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) | ||
33 | // don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c | ||
34 | #define VER_MAJOR 2 | ||
35 | #define VER_MINOR 5 | ||
36 | #define VER_SUBMINOR 4 | ||
37 | |||
38 | // Macros for kernel (esp. SMP) tracing using a PCI analyzer | ||
39 | // (e.g. x86). | ||
40 | //#define PCI_KERNEL_TRACE | ||
41 | #ifdef PCI_KERNEL_TRACE | ||
42 | #define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x); | ||
43 | #define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y)); | ||
44 | #else | ||
45 | |||
46 | #define PCI_TRACE(x) | ||
47 | #define PCI_TRACEO(x,y) | ||
48 | #endif | ||
49 | |||
50 | |||
51 | //#define DEBUG_CMND 1 // debug output for Linux Scsi CDBs | ||
52 | //#define DUMMYCMND_DBG 1 | ||
53 | |||
54 | //#define DEBUG_CPQFCTS 1 | ||
55 | //#undef DEBUG_CPQFCTS | ||
56 | #ifdef DEBUG_CPQFCTS | ||
57 | #define ENTER(x) printk("cpqfcts : entering %s()\n", x); | ||
58 | #define LEAVE(x) printk("cpqfcts : leaving %s()\n", x); | ||
59 | #define DEBUG(x) x | ||
60 | #else | ||
61 | #define ENTER(x) | ||
62 | #define LEAVE(x) | ||
63 | #define DEBUG(x) | ||
64 | #endif /* DEBUG_CPQFCTS */ | ||
65 | |||
66 | //#define DEBUG_CPQFCTS_PCI 1 | ||
67 | //#undef DEBUG_CPQFCTS_PCI | ||
68 | #if DEBUG_CPQFCTS_PCI | ||
69 | #define DEBUG_PCI(x) x | ||
70 | #else | ||
71 | #define DEBUG_PCI(x) | ||
72 | #endif /* DEBUG_CPQFCTS_PCI */ | ||
73 | |||
74 | #define STACHLITE66_TS12 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2" | ||
75 | #define STACHLITE66_TS13 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3" | ||
76 | #define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??" | ||
77 | #define SAGILENT_XL2_21 "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1" | ||
78 | |||
79 | // PDA is Peripheral Device Address, VSA is Volume Set Addressing | ||
80 | // Linux SCSI parameters | ||
81 | #define CPQFCTS_MAX_TARGET_ID 64 | ||
82 | |||
83 | // Note, changing CPQFCTS_MAX_LUN to less than 32 (e.g, 8) will result in | ||
84 | // strange behavior if a box with more than, e.g. 8, is on the loop. | ||
85 | #define CPQFCTS_MAX_LUN 32 // The RA-4x00 supports 32 (Linux SCSI supports 8) | ||
86 | #define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA | ||
87 | |||
88 | #define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0 | ||
89 | #define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN | ||
90 | |||
91 | #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) | ||
92 | #ifndef DECLARE_MUTEX_LOCKED | ||
93 | #define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED | ||
94 | #endif | ||
95 | |||
96 | #define DEV_NAME "cpqfcTS" | ||
97 | |||
98 | struct SupportedPCIcards | ||
99 | { | ||
100 | __u16 vendor_id; | ||
101 | __u16 device_id; | ||
102 | }; | ||
103 | |||
104 | // nn:nn denotes bit field | ||
105 | // TachyonHeader struct def. | ||
106 | // the fields shared with ODB | ||
107 | // need to have same value | ||
108 | |||
109 | |||
110 | |||
111 | |||
112 | #ifndef BYTE | ||
113 | //typedef UCHAR BYTE; | ||
114 | typedef __u8 BYTE; | ||
115 | #endif | ||
116 | #ifndef UCHAR | ||
117 | typedef __u8 UCHAR; | ||
118 | #endif | ||
119 | #ifndef LONG | ||
120 | typedef __s32 LONG; | ||
121 | #endif | ||
122 | #ifndef ULONG | ||
123 | typedef __u32 ULONG; | ||
124 | #endif | ||
125 | #ifndef PVOID | ||
126 | typedef void * PVOID; | ||
127 | #endif | ||
128 | #ifndef USHORT | ||
129 | typedef __u16 USHORT; | ||
130 | #endif | ||
131 | #ifndef BOOLEAN | ||
132 | typedef __u8 BOOLEAN; | ||
133 | #endif | ||
134 | |||
135 | |||
136 | // macro for FC-PH reject codes | ||
137 | // payload format for LS_RJT (FC payloads are big endian): | ||
138 | // byte 0 1 2 3 (MSB) | ||
139 | // DWORD 0 01 00 00 00 | ||
140 | // DWORD 1 resvd code expl. vendor | ||
141 | |||
142 | #define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16)) | ||
143 | |||
144 | |||
145 | #define TachLiteSTATUS 0x12 | ||
146 | |||
147 | // Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software | ||
148 | // 32-bit ERROR word defines | ||
149 | #define INVALID_ARGS 0x1 | ||
150 | #define LNKDWN_OSLS 0x2 | ||
151 | #define LNKDWN_LASER 0x4 | ||
152 | #define OUTQUE_FULL 0x8 | ||
153 | #define DRIVERQ_FULL 0x10 | ||
154 | #define SEST_FULL 0x20 | ||
155 | #define BAD_ALPA 0x40 | ||
156 | #define OVERFLOW 0x80 // inbound CM | ||
157 | #define COUNT_ERROR 0x100 // inbound CM | ||
158 | #define LINKFAIL_RX 0x200 // inbound CM | ||
159 | #define ABORTSEQ_NOTIFY 0x400 // outbound CM | ||
160 | #define LINKFAIL_TX 0x800 // outbound CM | ||
161 | #define HOSTPROG_ERR 0x1000 // outbound CM | ||
162 | #define FRAME_TO 0x2000 // outbound CM | ||
163 | #define INV_ENTRY 0x4000 // outbound CM | ||
164 | #define SESTPROG_ERR 0x8000 // outbound CM | ||
165 | #define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM | ||
166 | #define INITIATOR_ABORT 0x20000L // initiator exchange timeout or O/S ABORT | ||
167 | #define MEMPOOL_FAIL 0x40000L // O/S memory pool allocation failed | ||
168 | #define FC2_TIMEOUT 0x80000L // driver timeout for lost frames | ||
169 | #define TARGET_ABORT 0x100000L // ABTS received from FC port | ||
170 | #define EXCHANGE_QUEUED 0x200000L // e.g. Link State was LDn on fcStart | ||
171 | #define PORTID_CHANGED 0x400000L // fc Port address changed | ||
172 | #define DEVICE_REMOVED 0x800000L // fc Port address changed | ||
173 | // Several error scenarios result in SEST Exchange frames | ||
174 | // unexpectedly arriving in the SFQ | ||
175 | #define SFQ_FRAME 0x1000000L // SFQ frames from open Exchange | ||
176 | |||
177 | // Maximum number of Host Bus Adapters (HBA) / controllers supported | ||
178 | // only important for mem allocation dimensions - increase as necessary | ||
179 | |||
180 | #define MAX_ADAPTERS 8 | ||
181 | #define MAX_RX_PAYLOAD 1024 // hardware dependent max frame payload | ||
182 | // Tach header struc defines | ||
183 | #define SOFi3 0x7 | ||
184 | #define SOFf 0x8 | ||
185 | #define SOFn3 0xB | ||
186 | #define EOFn 0x5 | ||
187 | #define EOFt 0x6 | ||
188 | |||
189 | // FCP R_CTL defines | ||
190 | #define FCP_CMND 0x6 | ||
191 | #define FCP_XFER_RDY 0x5 | ||
192 | #define FCP_RSP 0x7 | ||
193 | #define FCP_RESPONSE 0x777 // (arbitrary #) | ||
194 | #define NEED_FCP_RSP 0x77 // (arbitrary #) | ||
195 | #define FCP_DATA 0x1 | ||
196 | |||
197 | #define RESET_TACH 0x100 // Reset Tachyon/TachLite | ||
198 | #define SCSI_IWE 0x2000 // initiator write entry (for SEST) | ||
199 | #define SCSI_IRE 0x3000 // initiator read entry (for SEST) | ||
200 | #define SCSI_TRE 0x400 // target read entry (for SEST) | ||
201 | #define SCSI_TWE 0x500 // target write entry (for SEST) | ||
202 | #define TOGGLE_LASER 0x800 | ||
203 | #define LIP 0x900 | ||
204 | #define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports | ||
205 | #define FMINIT 0x707 // (arbitrary) for Frame Manager Init command | ||
206 | |||
207 | // BLS == Basic Link Service | ||
208 | // ELS == Extended Link Service | ||
209 | #define BLS_NOP 4 | ||
210 | #define BLS_ABTS 0x10 // FC-PH Basic Link Service Abort Sequence | ||
211 | #define BLS_ABTS_ACC 0x100 // FC-PH Basic Link Service Abort Sequence Accept | ||
212 | #define BLS_ABTS_RJT 0x101 // FC-PH Basic Link Service Abort Sequence Reject | ||
213 | #define ELS_PLOGI 0x03 // FC-PH Port Login (arbitrary assign) | ||
214 | #define ELS_SCR 0x70 // (arb assign) State Change Registration (Fabric) | ||
215 | #define FCS_NSR 0x72 // (arb assign) Name Service Request (Fabric) | ||
216 | #define ELS_FLOGI 0x44 // (arb assign) Fabric Login | ||
217 | #define ELS_FDISC 0x41 // (arb assign) Fabric Discovery (Login) | ||
218 | #define ELS_PDISC 0x50 // FC-PH2 Port Discovery | ||
219 | #define ELS_ABTX 0x06 // FC-PH Abort Exchange | ||
220 | #define ELS_LOGO 0x05 // FC-PH Port Logout | ||
221 | #define ELS_PRLI 0x20 // FCP-SCSI Process Login | ||
222 | #define ELS_PRLO 0x21 // FCP-SCSI Process Logout | ||
223 | #define ELS_LOGO_ACC 0x07 // {FC-PH} Port Logout Accept | ||
224 | #define ELS_PLOGI_ACC 0x08 // {FC-PH} Port Login Accept | ||
225 | #define ELS_ACC 0x18 // {FC-PH} (generic) ACCept | ||
226 | #define ELS_PRLI_ACC 0x22 // {FCP-SCSI} Process Login Accept | ||
227 | #define ELS_RJT 0x1000000 | ||
228 | #define SCSI_REPORT_LUNS 0x0A0 | ||
229 | #define FCP_TARGET_RESET 0x200 | ||
230 | |||
231 | #define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame | ||
232 | |||
233 | #define SFQ_UNASSISTED_FCP 1 // ICM, DWord3, "Type" unassisted FCP | ||
234 | #define SFQ_UNKNOWN 0x31 // (arbitrary) ICM, DWord3, "Type" unknown | ||
235 | |||
236 | // these "LINK" bits refer to loop or non-loop | ||
237 | #define LINKACTIVE 0x2 // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set | ||
238 | #define LINKDOWN 0xf2 // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set | ||
239 | |||
240 | //#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1 | ||
241 | |||
242 | typedef struct // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST) | ||
243 | { | ||
244 | ULONG reserved; // dword 0 (don't use) | ||
245 | ULONG sof_eof; | ||
246 | ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID | ||
247 | ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID | ||
248 | ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL | ||
249 | ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT | ||
250 | ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID | ||
251 | ULONG ro; // dword 7 - relative offset | ||
252 | } TachFCHDR; | ||
253 | |||
254 | // NOTE!! the following struct MUST be 64 bytes. | ||
255 | typedef struct // 32 bytes hdr + 32 bytes payload | ||
256 | { | ||
257 | ULONG reserved; // dword 0 (don't use - must clear to 0) | ||
258 | ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp | ||
259 | ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID | ||
260 | ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID | ||
261 | ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL | ||
262 | ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT | ||
263 | ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID | ||
264 | ULONG ro; // dword 7 - relative offset | ||
265 | //--------- | ||
266 | __u32 pl[8]; // dwords 8-15 frame data payload | ||
267 | } TachFCHDR_CMND; | ||
268 | |||
269 | |||
270 | typedef struct // 32 bytes hdr + 120 bytes payload | ||
271 | { | ||
272 | ULONG reserved; // dword 0 (don't use - must clear to 0) | ||
273 | ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp | ||
274 | ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID | ||
275 | ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID | ||
276 | ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL | ||
277 | ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT | ||
278 | ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID | ||
279 | ULONG ro; // dword 7 - relative offset | ||
280 | //--------- | ||
281 | __u32 pl[30]; // largest necessary payload (for LOGIN cmnds) | ||
282 | } TachFCHDR_GCMND; | ||
283 | |||
284 | typedef struct // 32 bytes hdr + 64 bytes payload | ||
285 | { | ||
286 | ULONG reserved; // dword 0 (don't use) | ||
287 | ULONG sof_eof; | ||
288 | ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID | ||
289 | ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID | ||
290 | ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL | ||
291 | ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT | ||
292 | ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID | ||
293 | ULONG ro; // dword 7 - relative offset | ||
294 | //--------- | ||
295 | __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes | ||
296 | } TachFCHDR_RSP; | ||
297 | |||
298 | |||
299 | |||
300 | |||
301 | |||
302 | |||
303 | // Inbound Message Queue structures... | ||
304 | typedef struct // each entry 8 words (32 bytes) | ||
305 | { | ||
306 | ULONG type; // IMQ completion message types | ||
307 | ULONG word[7]; // remainder of structure | ||
308 | // interpreted by IMQ type | ||
309 | } TachyonIMQE; | ||
310 | |||
311 | |||
312 | // Queues for TachLite not in original Tachyon | ||
313 | // ERQ - Exchange Request Queue (for outbound commands) | ||
314 | // SFQ - Single Frame Queue (for incoming frames) | ||
315 | |||
316 | // Define Tachyon Outbound Command Que | ||
317 | // (Since many Tachyon registers are Read | ||
318 | // only, maintain copies for debugging) | ||
319 | // most Tach ques need power-of-2 sizes, | ||
320 | // where registers are loaded with po2 -1 | ||
321 | #define TACH_SEST_LEN 512 // TachLite SEST | ||
322 | |||
323 | #define ELS_EXCHANGES 64 // e.g. PLOGI, RSCN, ... | ||
324 | // define the total number of outstanding (simultaneous) exchanges | ||
325 | #define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES) // ELS exchanges | ||
326 | |||
327 | #define ERQ_LEN 128 // power of 2, max 4096 | ||
328 | |||
329 | // Inbound Message Queue structures... | ||
330 | #define IMQ_LEN 512 // minimum 4 entries [(power of 2) - 1] | ||
331 | typedef struct // 8 words - 32 bytes | ||
332 | { | ||
333 | TachyonIMQE QEntry[IMQ_LEN]; | ||
334 | ULONG producerIndex; // IMQ Producer Index register | ||
335 | // @32 byte align | ||
336 | ULONG consumerIndex; // Consumer Index register (in Tachyon) | ||
337 | ULONG length; // Length register | ||
338 | ULONG base; | ||
339 | } TachyonIMQ; // @ 32 * IMQ_LEN align | ||
340 | |||
341 | |||
342 | |||
343 | typedef struct // inbound completion message | ||
344 | { | ||
345 | ULONG Type; | ||
346 | ULONG Index; | ||
347 | ULONG TransferLength; | ||
348 | } TachyonInbCM; | ||
349 | |||
350 | |||
351 | |||
352 | // arbitrary numeric tags for TL structures | ||
353 | #define TL_FCHS 1 // TachLite Fibre Channel Header Structure | ||
354 | #define TL_IWE 2 // initiator write entry (for SEST) | ||
355 | #define TL_TWE 3 // target write entry (for SEST) | ||
356 | #define TL_IRE 4 // initiator read entry (for SEST) | ||
357 | #define TL_TRE 5 // target read entry (for SEST) | ||
358 | #define TL_IRB 6 // I/O request block | ||
359 | |||
360 | // for INCOMING frames | ||
361 | #define SFQ_LEN 32 // minimum 32 entries, max 4096 | ||
362 | |||
363 | typedef struct // Single Frame Que | ||
364 | { | ||
365 | TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!! | ||
366 | ULONG producerIndex; // IMQ Producer Index register | ||
367 | // @32 byte align | ||
368 | ULONG consumerIndex; // Consumer Index register (in Tachyon) | ||
369 | ULONG length; // Length register | ||
370 | ULONG base; | ||
371 | } TachLiteSFQ; | ||
372 | |||
373 | |||
374 | typedef struct // I/O Request Block flags | ||
375 | { | ||
376 | UCHAR BRD : 1; | ||
377 | UCHAR : 1; // reserved | ||
378 | UCHAR SFA : 1; | ||
379 | UCHAR DNC : 1; | ||
380 | UCHAR DIN : 1; | ||
381 | UCHAR DCM : 1; | ||
382 | UCHAR CTS : 1; | ||
383 | UCHAR SBV : 1; // IRB entry valid - IRB'B' only | ||
384 | } IRBflags; | ||
385 | |||
386 | typedef struct // I/O Request Block | ||
387 | { // Request 'A' | ||
388 | ULONG Req_A_SFS_Len; // total frame len (hdr + payload), min 32 | ||
389 | ULONG Req_A_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent) | ||
390 | ULONG Req_A_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa) | ||
391 | ULONG Req_A_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST | ||
392 | // Request 'B' | ||
393 | ULONG Req_B_SFS_Len; // total frame len (hdr + payload), min 32 | ||
394 | ULONG Req_B_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent) | ||
395 | ULONG Req_B_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa) | ||
396 | ULONG Req_B_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST | ||
397 | } TachLiteIRB; | ||
398 | |||
399 | |||
400 | typedef struct // TachLite placeholder for IRBs | ||
401 | { // aligned @sizeof(ERQ) for TachLite | ||
402 | // MAX commands is sum of SEST len and ERQ | ||
403 | // we know that each SEST entry requires an | ||
404 | // IRB (ERQ) entry; in addition, we provide | ||
405 | // ERQ_LEN | ||
406 | TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea. | ||
407 | ULONG consumerIndex; // Consumer Index register | ||
408 | ULONG producerIndex; // ERQ Producer Index register | ||
409 | ULONG length; // Length register | ||
410 | ULONG base; // copy of base ptr for debug | ||
411 | // struct is sized for largest expected cmnd (LOGIN) | ||
412 | } TachLiteERQ; | ||
413 | |||
414 | // for now, just 32 bit DMA, eventually 40something, with code changes | ||
415 | #define CPQFCTS_DMA_MASK ((unsigned long) (0x00000000FFFFFFFF)) | ||
416 | |||
417 | #define TL_MAX_SG_ELEM_LEN 0x7ffff // Max buffer length a single S/G entry | ||
418 | // may represent (a hardware limitation). The | ||
419 | // only reason to ever change this is if you | ||
420 | // want to exercise very-hard-to-reach code in | ||
421 | // cpqfcTSworker.c:build_SEST_sglist(). | ||
422 | |||
423 | #define TL_DANGER_SGPAGES 7 // arbitrary high water mark for # of S/G pages | ||
424 | // we must exceed to elicit a warning indicative | ||
425 | // of EXTREMELY large data transfers or | ||
426 | // EXTREME memory fragmentation. | ||
427 | // (means we just used up 2048 S/G elements, | ||
428 | // Never seen this is real life, only in | ||
429 | // testing with tricked up driver.) | ||
430 | |||
431 | #define TL_EXT_SG_PAGE_COUNT 256 // Number of Extended Scatter/Gather a/l PAIRS | ||
432 | // Tachyon register (IOBaseU 0x68) | ||
433 | // power-of-2 value ONLY! 4 min, 256 max | ||
434 | |||
435 | // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG | ||
436 | #define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4) | ||
437 | |||
438 | |||
439 | |||
440 | // SEST entry types: IWE, IRE, TWE, TRE | ||
441 | typedef struct | ||
442 | { | ||
443 | ULONG Hdr_Len; | ||
444 | ULONG Hdr_Addr; | ||
445 | ULONG RSP_Len; | ||
446 | ULONG RSP_Addr; | ||
447 | ULONG Buff_Off; | ||
448 | #define USES_EXTENDED_SGLIST(this_sest, x_ID) \ | ||
449 | (!((this_sest)->u[ x_ID ].IWE.Buff_Off & 0x80000000)) | ||
450 | ULONG Link; | ||
451 | ULONG RX_ID; | ||
452 | ULONG Data_Len; | ||
453 | ULONG Exp_RO; | ||
454 | ULONG Exp_Byte_Cnt; | ||
455 | // --- extended/local Gather Len/Address pairs | ||
456 | ULONG GLen1; | ||
457 | ULONG GAddr1; | ||
458 | ULONG GLen2; | ||
459 | ULONG GAddr2; | ||
460 | ULONG GLen3; | ||
461 | ULONG GAddr3; | ||
462 | } TachLiteIWE; | ||
463 | |||
464 | |||
465 | typedef struct | ||
466 | { | ||
467 | ULONG Seq_Accum; | ||
468 | ULONG reserved; // must clear to 0 | ||
469 | ULONG RSP_Len; | ||
470 | ULONG RSP_Addr; | ||
471 | ULONG Buff_Off; | ||
472 | ULONG Buff_Index; // ULONG 5 | ||
473 | ULONG Exp_RO; | ||
474 | ULONG Byte_Count; | ||
475 | ULONG reserved_; // ULONG 8 | ||
476 | ULONG Exp_Byte_Cnt; | ||
477 | // --- extended/local Scatter Len/Address pairs | ||
478 | ULONG SLen1; | ||
479 | ULONG SAddr1; | ||
480 | ULONG SLen2; | ||
481 | ULONG SAddr2; | ||
482 | ULONG SLen3; | ||
483 | ULONG SAddr3; | ||
484 | } TachLiteIRE; | ||
485 | |||
486 | |||
487 | typedef struct // Target Write Entry | ||
488 | { | ||
489 | ULONG Seq_Accum; // dword 0 | ||
490 | ULONG reserved; // dword 1 must clear to 0 | ||
491 | ULONG Remote_Node_ID; | ||
492 | ULONG reserved1; // dword 3 must clear to 0 | ||
493 | ULONG Buff_Off; | ||
494 | ULONG Buff_Index; // ULONG 5 | ||
495 | ULONG Exp_RO; | ||
496 | ULONG Byte_Count; | ||
497 | ULONG reserved_; // ULONG 8 | ||
498 | ULONG Exp_Byte_Cnt; | ||
499 | // --- extended/local Scatter Len/Address pairs | ||
500 | ULONG SLen1; | ||
501 | ULONG SAddr1; | ||
502 | ULONG SLen2; | ||
503 | ULONG SAddr2; | ||
504 | ULONG SLen3; | ||
505 | ULONG SAddr3; | ||
506 | } TachLiteTWE; | ||
507 | |||
508 | typedef struct | ||
509 | { | ||
510 | ULONG Hdr_Len; | ||
511 | ULONG Hdr_Addr; | ||
512 | ULONG RSP_Len; // DWord 2 | ||
513 | ULONG RSP_Addr; | ||
514 | ULONG Buff_Off; | ||
515 | ULONG Buff_Index; // DWord 5 | ||
516 | ULONG reserved; | ||
517 | ULONG Data_Len; | ||
518 | ULONG reserved_; | ||
519 | ULONG reserved__; | ||
520 | // --- extended/local Gather Len/Address pairs | ||
521 | ULONG GLen1; // DWord A | ||
522 | ULONG GAddr1; | ||
523 | ULONG GLen2; | ||
524 | ULONG GAddr2; | ||
525 | ULONG GLen3; | ||
526 | ULONG GAddr3; | ||
527 | } TachLiteTRE; | ||
528 | |||
529 | typedef struct ext_sg_page_ptr_t *PSGPAGES; | ||
530 | typedef struct ext_sg_page_ptr_t | ||
531 | { | ||
532 | unsigned char page[TL_EXT_SG_PAGE_BYTELEN * 2]; // 2x for alignment | ||
533 | dma_addr_t busaddr; // need the bus addresses and | ||
534 | unsigned int maplen; // lengths for later pci unmapping. | ||
535 | PSGPAGES next; | ||
536 | } SGPAGES; // linked list of S/G pairs, by Exchange | ||
537 | |||
538 | typedef struct // SCSI Exchange State Table | ||
539 | { | ||
540 | union // Entry can be IWE, IRE, TWE, TRE | ||
541 | { // 64 bytes per entry | ||
542 | TachLiteIWE IWE; | ||
543 | TachLiteIRE IRE; | ||
544 | TachLiteTWE TWE; | ||
545 | TachLiteTRE TRE; | ||
546 | } u[TACH_SEST_LEN]; | ||
547 | |||
548 | TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl) | ||
549 | TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame | ||
550 | PSGPAGES sgPages[TACH_SEST_LEN]; // head of linked list of Pool-allocations | ||
551 | ULONG length; // Length register | ||
552 | ULONG base; // copy of base ptr for debug | ||
553 | } TachSEST; | ||
554 | |||
555 | |||
556 | |||
557 | typedef struct // each register has it's own address | ||
558 | // and value (used for write-only regs) | ||
559 | { | ||
560 | void* address; | ||
561 | volatile ULONG value; | ||
562 | } FCREGISTER; | ||
563 | |||
564 | typedef struct // Host copy - TachLite Registers | ||
565 | { | ||
566 | ULONG IOBaseL, IOBaseU; // I/O port lower and upper TL register addresses | ||
567 | ULONG MemBase; // memory mapped register addresses | ||
568 | void* ReMapMemBase; // O/S VM reference for MemBase | ||
569 | ULONG wwn_hi; // WWN is set once at startup | ||
570 | ULONG wwn_lo; | ||
571 | ULONG my_al_pa; // al_pa received after LIP() | ||
572 | ULONG ROMCTR; // flags for on-board RAM/ROM | ||
573 | ULONG RAMBase; // on-board RAM (i.e. some Tachlites) | ||
574 | ULONG SROMBase; // on-board EEPROM (some Tachlites) | ||
575 | ULONG PCIMCTR; // PCI Master Control Reg (has bus width) | ||
576 | |||
577 | FCREGISTER INTEN; // copy of interrupt enable mask | ||
578 | FCREGISTER INTPEND; // interrupt pending | ||
579 | FCREGISTER INTSTAT; // interrupt status | ||
580 | FCREGISTER SFQconsumerIndex; | ||
581 | FCREGISTER ERQproducerIndex; | ||
582 | FCREGISTER TYconfig; // TachYon (chip level) | ||
583 | FCREGISTER TYcontrol; | ||
584 | FCREGISTER TYstatus; | ||
585 | FCREGISTER FMconfig; // Frame Manager (FC loop level) | ||
586 | FCREGISTER FMcontrol; | ||
587 | FCREGISTER FMstatus; | ||
588 | FCREGISTER FMLinkStatus1; | ||
589 | FCREGISTER FMLinkStatus2; | ||
590 | FCREGISTER FMBB_CreditZero; | ||
591 | FCREGISTER status; | ||
592 | FCREGISTER ed_tov; // error detect time-out value | ||
593 | FCREGISTER rcv_al_pa; // received arb. loop physical address | ||
594 | FCREGISTER primitive; // e.g. LIP(), OPN(), ... | ||
595 | } TL_REGISTERS; | ||
596 | |||
597 | |||
598 | |||
599 | typedef struct | ||
600 | { | ||
601 | ULONG ok; | ||
602 | ULONG invalidArgs; | ||
603 | ULONG linkDown; | ||
604 | ULONG linkUp; | ||
605 | ULONG outQueFull; | ||
606 | ULONG SESTFull; | ||
607 | ULONG hpe; // host programming err (from Tach) | ||
608 | ULONG FC4aborted; // aborts from Application or upper driver layer | ||
609 | ULONG FC2aborted; // aborts from our driver's timeouts | ||
610 | ULONG timeouts; // our driver timeout (on individual exchanges) | ||
611 | ULONG logouts; // explicit - sent LOGO; implicit - device removed | ||
612 | ULONG retries; | ||
613 | ULONG linkFailTX; | ||
614 | ULONG linkFailRX; | ||
615 | ULONG CntErrors; // byte count expected != count received (typ. SEST) | ||
616 | ULONG e_stores; // elastic store errs | ||
617 | ULONG resets; // hard or soft controller resets | ||
618 | ULONG FMinits; // TACH Frame Manager Init (e.g. LIPs) | ||
619 | ULONG lnkQueFull; // too many LOGIN, loop commands | ||
620 | ULONG ScsiQueFull; // too many FCP-SCSI inbound frames | ||
621 | ULONG LossofSignal; // FM link status 1 regs | ||
622 | ULONG BadRXChar; // FM link status 1 regs | ||
623 | ULONG LossofSync; // FM link status 1 regs | ||
624 | ULONG Rx_EOFa; // FM link status 2 regs (received EOFa) | ||
625 | ULONG Dis_Frm; // FM link status 2 regs (discarded frames) | ||
626 | ULONG Bad_CRC; // FM link status 2 regs | ||
627 | ULONG BB0_Timer; // FM BB_Credit Zero Timer Reg | ||
628 | ULONG loopBreaks; // infinite loop exits | ||
629 | ULONG lastBB0timer; // static accum. buffer needed by Tachlite | ||
630 | } FCSTATS; | ||
631 | |||
632 | |||
633 | typedef struct // Config Options | ||
634 | { // LS Bit first | ||
635 | USHORT : 1; // bit0: | ||
636 | USHORT flogi : 1; // bit1: We sent FLOGI - wait for Fabric logins | ||
637 | USHORT fabric: 1; // bit2: Tachyon detected Fabric (FM stat LG) | ||
638 | USHORT LILPin: 1; // bit3: We can use an FC-AL LILP frame | ||
639 | USHORT target: 1; // bit4: this Port has SCSI target capability | ||
640 | USHORT initiator: 1; // bit5: this Port has SCSI initiator capability | ||
641 | USHORT extLoopback: 1; // bit6: loopback at GBIC | ||
642 | USHORT intLoopback: 1; // bit7: loopback in HP silicon | ||
643 | USHORT : 1; // bit8: | ||
644 | USHORT : 1; // bit9: | ||
645 | USHORT : 1; // bit10: | ||
646 | USHORT : 1; // bit11: | ||
647 | USHORT : 1; // bit12: | ||
648 | USHORT : 1; // bit13: | ||
649 | USHORT : 1; // bit14: | ||
650 | USHORT : 1; // bit15: | ||
651 | } FC_OPTIONS; | ||
652 | |||
653 | |||
654 | |||
655 | typedef struct dyn_mem_pair | ||
656 | { | ||
657 | void *BaseAllocated; // address as allocated from O/S; | ||
658 | unsigned long AlignedAddress; // aligned address (used by Tachyon DMA) | ||
659 | dma_addr_t dma_handle; | ||
660 | size_t size; | ||
661 | } ALIGNED_MEM; | ||
662 | |||
663 | |||
664 | |||
665 | |||
666 | // these structs contain only CRUCIAL (stuff we actually use) parameters | ||
667 | // from FC-PH(n) logins. (Don't save entire LOGIN payload to save mem.) | ||
668 | |||
669 | // Implicit logout happens when the loop goes down - we require PDISC | ||
670 | // to restore. Explicit logout is when WE decide never to talk to someone, | ||
671 | // or when a target refuses to talk to us, i.e. sends us a LOGO frame or | ||
672 | // LS_RJT reject in response to our PLOGI request. | ||
673 | |||
674 | #define IMPLICIT_LOGOUT 1 | ||
675 | #define EXPLICIT_LOGOUT 2 | ||
676 | |||
677 | typedef struct | ||
678 | { | ||
679 | UCHAR channel; // SCSI "bus" | ||
680 | UCHAR target; | ||
681 | UCHAR InqDeviceType; // byte 0 from SCSI Inquiry response | ||
682 | UCHAR VolumeSetAddressing; // FCP-SCSI LUN coding (40h for VSA) | ||
683 | UCHAR LunMasking; // True if selective presentation supported | ||
684 | UCHAR lun[CPQFCTS_MAX_LUN]; | ||
685 | } SCSI_NEXUS; | ||
686 | |||
687 | |||
688 | typedef struct | ||
689 | { | ||
690 | union | ||
691 | { | ||
692 | UCHAR ucWWN[8]; // a FC 64-bit World Wide Name/ PortID of target | ||
693 | // addressing of single target on single loop... | ||
694 | u64 liWWN; | ||
695 | } u; | ||
696 | |||
697 | ULONG port_id; // a FC 24-bit address of port (lower 8 bits = al_pa) | ||
698 | |||
699 | #define REPORT_LUNS_PL 256 | ||
700 | UCHAR ReportLunsPayload[REPORT_LUNS_PL]; | ||
701 | |||
702 | SCSI_NEXUS ScsiNexus; // LUNs per FC device | ||
703 | |||
704 | ULONG LOGO_counter; // might try several times before logging out for good | ||
705 | ULONG LOGO_timer; // after LIP, ports expecting PDISC must time-out and | ||
706 | // LOGOut if successful PDISC not completed in 2 secs | ||
707 | |||
708 | ULONG concurrent_seq; // must be 1 or greater | ||
709 | ULONG rx_data_size; // e.g. 128, 256, 1024, 2048 per FC-PH spec | ||
710 | ULONG BB_credit; | ||
711 | ULONG EE_credit; | ||
712 | |||
713 | ULONG fcp_info; // from PRLI (i.e. INITIATOR/ TARGET flags) | ||
714 | // flags for login process | ||
715 | BOOLEAN Originator; // Login sequence Originated (if false, we | ||
716 | // responded to another port's login sequence) | ||
717 | BOOLEAN plogi; // PLOGI frame ACCepted (originated or responded) | ||
718 | BOOLEAN pdisc; // PDISC frame was ORIGINATED (self-login logic) | ||
719 | BOOLEAN prli; // PRLI frame ACCepted (originated or responded) | ||
720 | BOOLEAN flogi; // FLOGI frame ACCepted (originated or responded) | ||
721 | BOOLEAN logo; // port permanently logged out (invalid login param) | ||
722 | BOOLEAN flogiReq; // Fabric login required (set in LIP process) | ||
723 | UCHAR highest_ver; | ||
724 | UCHAR lowest_ver; | ||
725 | |||
726 | |||
727 | // when the "target" (actually FC Port) is waiting for login | ||
728 | // (e.g. after Link reset), set the device_blocked bit; | ||
729 | // after Port completes login, un-block target. | ||
730 | UCHAR device_blocked; // see Scsi_Device struct | ||
731 | |||
732 | // define singly-linked list of logged-in ports | ||
733 | // once a port_id is identified, it is remembered, | ||
734 | // even if the port is removed indefinitely | ||
735 | PVOID pNextPort; // actually, type PFC_LOGGEDIN_PORT; void for Compiler | ||
736 | |||
737 | } FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT; | ||
738 | |||
739 | |||
740 | |||
741 | // This serves as the ESB (Exchange Status Block), | ||
742 | // and has timeout counter; used for ABORTs | ||
743 | typedef struct | ||
744 | { // FC-1 X_IDs | ||
745 | ULONG type; // ELS_PLOGI, SCSI_IWE, ... (0 if free) | ||
746 | PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange | ||
747 | Scsi_Cmnd *Cmnd; // Linux SCSI command packet includes S/G list | ||
748 | ULONG timeOut; // units of ??, DEC by driver, Abort when 0 | ||
749 | ULONG reTries; // need one or more retries? | ||
750 | ULONG status; // flags indicating errors (0 if none) | ||
751 | TachLiteIRB IRB; // I/O Request Block, gets copied to ERQ | ||
752 | TachFCHDR_GCMND fchs; // location of IRB's Req_A_SFS_Addr | ||
753 | } FC_EXCHANGE, *PFC_EXCHANGE; | ||
754 | |||
755 | // Unfortunately, Linux limits our kmalloc() allocations to 128k. | ||
756 | // Because of this and the fact that our ScsiRegister allocation | ||
757 | // is also constrained, we move this large structure out for | ||
758 | // allocation after Scsi Register. | ||
759 | // (In other words, this cumbersome indirection is necessary | ||
760 | // because of kernel memory allocation constraints!) | ||
761 | |||
762 | typedef struct // we will allocate this dynamically | ||
763 | { | ||
764 | FC_EXCHANGE fcExchange[ TACH_MAX_XID ]; | ||
765 | } FC_EXCHANGES; | ||
766 | |||
767 | |||
768 | |||
769 | |||
770 | |||
771 | |||
772 | |||
773 | |||
774 | |||
775 | |||
776 | |||
777 | typedef struct | ||
778 | { | ||
779 | char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus") | ||
780 | //PVOID pAdapterDevExt; // back pointer to device object/extension | ||
781 | ULONG ChipType; // local numeric key for Tachyon Type / Rev. | ||
782 | ULONG status; // our Driver - logical status | ||
783 | |||
784 | TL_REGISTERS Registers; // reg addresses & host memory copies | ||
785 | // FC-4 mapping of 'transaction' to X_IDs | ||
786 | UCHAR LILPmap[32*4]; // Loop Position Map of ALPAs (late FC-AL only) | ||
787 | FC_OPTIONS Options; // e.g. Target, Initiator, loopback... | ||
788 | UCHAR highest_FCPH_ver; // FC-PH version limits | ||
789 | UCHAR lowest_FCPH_ver; // FC-PH version limits | ||
790 | |||
791 | FC_EXCHANGES *Exchanges; | ||
792 | ULONG fcLsExchangeLRU; // Least Recently Used counter (Link Service) | ||
793 | ULONG fcSestExchangeLRU; // Least Recently Used counter (FCP-SCSI) | ||
794 | FC_LOGGEDIN_PORT fcPorts; // linked list of every FC port ever seen | ||
795 | FCSTATS fcStats; // FC comm err counters | ||
796 | |||
797 | // Host memory QUEUE pointers | ||
798 | TachLiteERQ *ERQ; // Exchange Request Que | ||
799 | TachyonIMQ *IMQ; // Inbound Message Que | ||
800 | TachLiteSFQ *SFQ; // Single Frame Queue | ||
801 | TachSEST *SEST; // SCSI Exchange State Table | ||
802 | |||
803 | dma_addr_t exch_dma_handle; | ||
804 | |||
805 | // these function pointers are for "generic" functions, which are | ||
806 | // replaced with Host Bus Adapter types at | ||
807 | // runtime. | ||
808 | int (*CreateTachyonQues)( void* , int); | ||
809 | int (*DestroyTachyonQues)( void* , int); | ||
810 | int (*LaserControl)(void*, int ); // e.g. On/Off | ||
811 | int (*ResetTachyon)(void*, int ); | ||
812 | void (*FreezeTachyon)(void*, int ); | ||
813 | void (*UnFreezeTachyon)(void*, int ); | ||
814 | int (*InitializeTachyon)(void*, int, int ); | ||
815 | int (*InitializeFrameManager)(void*, int ); | ||
816 | int (*ProcessIMQEntry)(void*); | ||
817 | int (*ReadWriteWWN)(void*, int ReadWrite); | ||
818 | int (*ReadWriteNVRAM)(void*, void*, int ReadWrite); | ||
819 | |||
820 | } TACHYON, *PTACHYON; | ||
821 | |||
822 | |||
823 | void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip); | ||
824 | |||
825 | int CpqTsCreateTachLiteQues( void* pHBA, int opcode); | ||
826 | int CpqTsDestroyTachLiteQues( void* , int); | ||
827 | int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2); | ||
828 | |||
829 | int CpqTsProcessIMQEntry(void* pHBA); | ||
830 | int CpqTsResetTachLite(void *pHBA, int type); | ||
831 | void CpqTsFreezeTachlite(void *pHBA, int type); | ||
832 | void CpqTsUnFreezeTachlite(void *pHBA, int type); | ||
833 | int CpqTsInitializeFrameManager(void *pHBA, int); | ||
834 | int CpqTsLaserControl( void* addrBase, int opcode ); | ||
835 | int CpqTsReadWriteWWN(void*, int ReadWrite); | ||
836 | int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite); | ||
837 | |||
838 | void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter); | ||
839 | void cpqfcTSWorkerThread( void *host); | ||
840 | |||
841 | int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf ); | ||
842 | ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count, | ||
843 | UCHAR *buf ); | ||
844 | |||
845 | BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout, | ||
846 | USHORT startOffset, // e.g. 0x2f for WWN start | ||
847 | USHORT count, | ||
848 | UCHAR *buf ); | ||
849 | |||
850 | |||
851 | // define misc functions | ||
852 | int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]); | ||
853 | int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]); | ||
854 | void* fcMemManager( struct pci_dev *pdev, | ||
855 | ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab, | ||
856 | ULONG ulAlignedAddress, dma_addr_t *dma_handle); | ||
857 | |||
858 | void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt); | ||
859 | |||
860 | //ULONG virt_to_phys( PVOID virtaddr ); | ||
861 | |||
862 | |||
863 | // Linux interrupt handler | ||
864 | irqreturn_t cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs); | ||
865 | void cpqfcTSheartbeat( unsigned long ptr ); | ||
866 | |||
867 | |||
868 | |||
869 | // The biggest Q element we deal with is Aborts - we | ||
870 | // need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes) | ||
871 | //#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4) | ||
872 | #define LINKQ_ITEM_SIZE (3*16) | ||
873 | typedef struct | ||
874 | { | ||
875 | ULONG Type; // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ... | ||
876 | ULONG ulBuff[ LINKQ_ITEM_SIZE ]; | ||
877 | } LINKQ_ITEM; | ||
878 | |||
879 | #define FC_LINKQ_DEPTH TACH_MAX_XID | ||
880 | typedef struct | ||
881 | { | ||
882 | ULONG producer; | ||
883 | ULONG consumer; // when producer equals consumer, Q empty | ||
884 | |||
885 | LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ]; | ||
886 | |||
887 | } FC_LINK_QUE, *PFC_LINK_QUE; | ||
888 | |||
889 | |||
890 | // DPC routines post to here on Inbound SCSI frames | ||
891 | // User thread processes | ||
892 | #define FC_SCSIQ_DEPTH 32 | ||
893 | |||
894 | typedef struct | ||
895 | { | ||
896 | int Type; // e.g. SCSI | ||
897 | ULONG ulBuff[ 3*16 ]; | ||
898 | } SCSIQ_ITEM; | ||
899 | |||
900 | typedef struct | ||
901 | { | ||
902 | ULONG producer; | ||
903 | ULONG consumer; // when producer equals consumer, Q empty | ||
904 | |||
905 | SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ]; | ||
906 | |||
907 | } FC_SCSI_QUE, *PFC_SCSI_QUE; | ||
908 | |||
909 | typedef struct { | ||
910 | /* This is tacked on to a Scsi_Request in upper_private_data | ||
911 | for pasthrough ioctls, as a place to hold data that can't | ||
912 | be stashed anywhere else in the Scsi_Request. We differentiate | ||
913 | this from _real_ upper_private_data by checking if the virt addr | ||
914 | is within our special pool. */ | ||
915 | ushort bus; | ||
916 | ushort pdrive; | ||
917 | } cpqfc_passthru_private_t; | ||
918 | |||
919 | #define CPQFC_MAX_PASSTHRU_CMDS 100 | ||
920 | |||
921 | #define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST | ||
922 | |||
923 | // Linux space allocated per HBA (chip state, etc.) | ||
924 | typedef struct | ||
925 | { | ||
926 | struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct | ||
927 | |||
928 | TACHYON fcChip; // All Tachyon registers, Queues, functions | ||
929 | ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS]; | ||
930 | |||
931 | struct pci_dev *PciDev; | ||
932 | dma_addr_t fcLQ_dma_handle; | ||
933 | |||
934 | Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn | ||
935 | // (for Acceptable targets) | ||
936 | Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full | ||
937 | |||
938 | Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets | ||
939 | |||
940 | u_char HBAnum; // 0-based host number | ||
941 | |||
942 | |||
943 | struct timer_list cpqfcTStimer; // FC utility timer for implicit | ||
944 | // logouts, FC protocol timeouts, etc. | ||
945 | int fcStatsTime; // Statistics delta reporting time | ||
946 | |||
947 | struct task_struct *worker_thread; // our kernel thread | ||
948 | int PortDiscDone; // set by SendLogins(), cleared by LDn | ||
949 | |||
950 | struct semaphore *TachFrozen; | ||
951 | struct semaphore *TYOBcomplete; // handshake for Tach outbound frames | ||
952 | struct semaphore *fcQueReady; // FibreChannel work for our kernel thread | ||
953 | struct semaphore *notify_wt; // synchronizes kernel thread kill | ||
954 | struct semaphore *BoardLock; | ||
955 | |||
956 | PFC_LINK_QUE fcLQ; // the WorkerThread operates on this | ||
957 | |||
958 | spinlock_t hba_spinlock; // held/released by WorkerThread | ||
959 | cpqfc_passthru_private_t *private_data_pool; | ||
960 | unsigned long *private_data_bits; | ||
961 | |||
962 | } CPQFCHBA; | ||
963 | |||
964 | #define CPQ_SPINLOCK_HBA( x ) spin_lock(&x->hba_spinlock); | ||
965 | #define CPQ_SPINUNLOCK_HBA(x) spin_unlock(&x->hba_spinlock); | ||
966 | |||
967 | |||
968 | |||
969 | void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata, | ||
970 | PFC_LOGGEDIN_PORT pFcPort); | ||
971 | |||
972 | |||
973 | void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int ); | ||
974 | |||
975 | PFC_LOGGEDIN_PORT fcPortLoggedIn( | ||
976 | CPQFCHBA *cpqfcHBAdata, | ||
977 | TachFCHDR_GCMND* fchs, | ||
978 | BOOLEAN, | ||
979 | BOOLEAN); | ||
980 | void fcProcessLoggedIn( | ||
981 | CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs); | ||
982 | |||
983 | |||
984 | ULONG cpqfcTSBuildExchange( | ||
985 | CPQFCHBA *cpqfcHBAdata, | ||
986 | ULONG type, // e.g. PLOGI | ||
987 | TachFCHDR_GCMND* InFCHS, // incoming FCHS | ||
988 | void *Data, // the CDB, scatter/gather, etc. | ||
989 | LONG *ExchangeID ); // allocated exchange ID | ||
990 | |||
991 | ULONG cpqfcTSStartExchange( | ||
992 | CPQFCHBA *cpqfcHBAdata, | ||
993 | LONG ExchangeID ); | ||
994 | |||
995 | void cpqfcTSCompleteExchange( | ||
996 | struct pci_dev *pcidev, | ||
997 | PTACHYON fcChip, | ||
998 | ULONG exchange_ID); | ||
999 | |||
1000 | |||
1001 | PFC_LOGGEDIN_PORT fcFindLoggedInPort( | ||
1002 | PTACHYON fcChip, | ||
1003 | Scsi_Cmnd *Cmnd, // (We want the channel/target/lun Nexus from Cmnd) | ||
1004 | ULONG port_id, // search linked list for al_pa, or | ||
1005 | UCHAR wwn[8], // search linked list for WWN, or... | ||
1006 | PFC_LOGGEDIN_PORT *pLastLoggedInPort | ||
1007 | ); | ||
1008 | |||
1009 | void cpqfcTSPutLinkQue( | ||
1010 | CPQFCHBA *cpqfcHBAdata, | ||
1011 | int Type, | ||
1012 | void *QueContent); | ||
1013 | |||
1014 | void fcPutScsiQue( | ||
1015 | CPQFCHBA *cpqfcHBAdata, | ||
1016 | int Type, | ||
1017 | void *QueContent); | ||
1018 | |||
1019 | void fcLinkQReset( | ||
1020 | CPQFCHBA *); | ||
1021 | void fcScsiQReset( | ||
1022 | CPQFCHBA *); | ||
1023 | void fcSestReset( | ||
1024 | CPQFCHBA *); | ||
1025 | |||
1026 | void cpqfc_pci_unmap(struct pci_dev *pcidev, | ||
1027 | Scsi_Cmnd *cmd, | ||
1028 | PTACHYON fcChip, | ||
1029 | ULONG x_ID); | ||
1030 | |||
1031 | extern const UCHAR valid_al_pa[]; | ||
1032 | extern const int number_of_al_pa; | ||
1033 | |||
1034 | #define FCP_RESID_UNDER 0x80000 | ||
1035 | #define FCP_RESID_OVER 0x40000 | ||
1036 | #define FCP_SNS_LEN_VALID 0x20000 | ||
1037 | #define FCP_RSP_LEN_VALID 0x10000 | ||
1038 | |||
1039 | // RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34) | ||
1040 | #define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000 | ||
1041 | #define FCP_CMND_FIELD_INVALID 0x2000000 | ||
1042 | #define FCP_DATA_RO_NOT_XRDY_RO 0x3000000 | ||
1043 | #define FCP_TASKFUNCTION_NS 0x4000000 | ||
1044 | #define FCP_TASKFUNCTION_FAIL 0x5000000 | ||
1045 | |||
1046 | // FCP-SCSI response status struct | ||
1047 | typedef struct // see "TachFCHDR_RSP" definition - 64 bytes | ||
1048 | { | ||
1049 | __u32 reserved; | ||
1050 | __u32 reserved1; | ||
1051 | __u32 fcp_status; // field validity and SCSI status | ||
1052 | __u32 fcp_resid; | ||
1053 | __u32 fcp_sns_len; // length of FCP_SNS_INFO field | ||
1054 | __u32 fcp_rsp_len; // length of FCP_RSP_INFO field (expect 8) | ||
1055 | __u32 fcp_rsp_info; // 4 bytes of FCP protocol response information | ||
1056 | __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8) | ||
1057 | __u8 fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ) | ||
1058 | |||
1059 | } FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE; | ||
1060 | |||
1061 | |||
1062 | // Fabric State Change Registration | ||
1063 | typedef struct scrpl | ||
1064 | { | ||
1065 | __u32 command; | ||
1066 | __u32 function; | ||
1067 | } SCR_PL; | ||
1068 | |||
1069 | // Fabric Name Service Request | ||
1070 | typedef struct nsrpl | ||
1071 | { | ||
1072 | __u32 CT_Rev; // (& IN_ID) WORD 0 | ||
1073 | __u32 FCS_Type; // WORD 1 | ||
1074 | __u32 Command_code; // WORD 2 | ||
1075 | __u32 reason_code; // WORD 3 | ||
1076 | __u32 FCP; // WORD 4 (lower byte) | ||
1077 | |||
1078 | } NSR_PL; | ||
1079 | |||
1080 | |||
1081 | |||
1082 | // "FC.H" | ||
1083 | #define MAX_RX_SIZE 0x800 // Max Receive Buffer Size is 2048 | ||
1084 | #define MIN_RX_SIZE 0x100 // Min Size is 256, per FC-PLDA Spec | ||
1085 | #define MAX_TARGET_RXIDS SEST_DEPTH | ||
1086 | #define TARGET_RX_SIZE SEST_BUFFER_LENGTH | ||
1087 | |||
1088 | #define CLASS_1 0x01 | ||
1089 | #define CLASS_2 0x02 | ||
1090 | #define CLASS_3 0x03 | ||
1091 | |||
1092 | #define FC_PH42 0x08 | ||
1093 | #define FC_PH43 0x09 | ||
1094 | #define FC_PH3 0x20 | ||
1095 | |||
1096 | #define RR_TOV 2 // Minimum Time for target to wait for | ||
1097 | // PDISC after a LIP. | ||
1098 | #define E_D_TOV 2 // Minimum Time to wait for Sequence | ||
1099 | // Completion. | ||
1100 | #define R_A_TOV 0 // Minimum Time for Target to wait | ||
1101 | // before reclaiming resources. | ||
1102 | // | ||
1103 | // R_CTL Field | ||
1104 | // | ||
1105 | // Routing Bits (31-28) | ||
1106 | // | ||
1107 | #define FC4_DEVICE_DATA 0x00000000 | ||
1108 | #define EXT_LINK_DATA 0x20000000 | ||
1109 | #define FC4_LINK_DATA 0x30000000 | ||
1110 | #define VIDEO_DATA 0x40000000 | ||
1111 | #define BASIC_LINK_DATA 0x80000000 | ||
1112 | #define LINK_CONTROL 0xC0000000 | ||
1113 | #define ROUTING_MASK 0xF0000000 | ||
1114 | |||
1115 | // | ||
1116 | // Information Bits (27-24) | ||
1117 | // | ||
1118 | #define UNCAT_INFORMATION 0x00000000 | ||
1119 | #define SOLICITED_DATA 0x01000000 | ||
1120 | #define UNSOLICITED_CONTROL 0x02000000 | ||
1121 | #define SOLICITED_CONTROL 0x03000000 | ||
1122 | #define UNSOLICITED_DATA 0x04000000 | ||
1123 | #define DATA_DESCRIPTOR 0x05000000 | ||
1124 | #define UNSOLICITED_COMMAND 0x06000000 | ||
1125 | #define COMMAND_STATUS 0x07000000 | ||
1126 | #define INFO_MASK 0x0F000000 | ||
1127 | // | ||
1128 | // (Link Control Codes) | ||
1129 | // | ||
1130 | #define ACK_1 0x00000000 | ||
1131 | #define ACK_0_OR_N 0x01000000 | ||
1132 | #define P_RJT 0x02000000 | ||
1133 | #define F_RJT 0x03000000 | ||
1134 | #define P_BSY 0x04000000 | ||
1135 | #define FABRIC_BUSY_TO_DF 0x05000000 // Fabric Busy to Data Frame | ||
1136 | #define FABRIC_BUSY_TO_LC 0x06000000 // Fabric Busy to Link Ctl Frame | ||
1137 | #define LINK_CREDIT_RESET 0x07000000 | ||
1138 | // | ||
1139 | // (Link Service Command Codes) | ||
1140 | // | ||
1141 | //#define LS_RJT 0x01000000 // LS Reject | ||
1142 | |||
1143 | #define LS_ACC 0x02000000 // LS Accept | ||
1144 | #define LS_PLOGI 0x03000000 // N_PORT Login | ||
1145 | #define LS_FLOGI 0x04000000 // F_PORT Login | ||
1146 | #define LS_LOGO 0x05000000 // Logout | ||
1147 | #define LS_ABTX 0x06000000 // Abort Exchange | ||
1148 | #define LS_RCS 0x07000000 // Read Connection Status | ||
1149 | #define LS_RES 0x08000000 // Read Exchange Status | ||
1150 | #define LS_RSS 0x09000000 // Read Sequence Status | ||
1151 | #define LS_RSI 0x0A000000 // Request Seq Initiative | ||
1152 | #define LS_ESTS 0x0B000000 // Establish Steaming | ||
1153 | #define LS_ESTC 0x0C000000 // Estimate Credit | ||
1154 | #define LS_ADVC 0x0D000000 // Advice Credit | ||
1155 | #define LS_RTV 0x0E000000 // Read Timeout Value | ||
1156 | #define LS_RLS 0x0F000000 // Read Link Status | ||
1157 | #define LS_ECHO 0x10000000 // Echo | ||
1158 | #define LS_TEST 0x11000000 // Test | ||
1159 | #define LS_RRQ 0x12000000 // Reinstate Rec. Qual. | ||
1160 | #define LS_PRLI 0x20000000 // Process Login | ||
1161 | #define LS_PRLO 0x21000000 // Process Logout | ||
1162 | #define LS_TPRLO 0x24000000 // 3rd Party Process Logout | ||
1163 | #define LS_PDISC 0x50000000 // Process Discovery | ||
1164 | #define LS_FDISC 0x51000000 // Fabric Discovery | ||
1165 | #define LS_ADISC 0x52000000 // Discover Address | ||
1166 | #define LS_RNC 0x53000000 // Report Node Capability | ||
1167 | #define LS_SCR 0x62000000 // State Change Registration | ||
1168 | #define LS_MASK 0xFF000000 | ||
1169 | |||
1170 | // | ||
1171 | // TYPE Bit Masks | ||
1172 | // | ||
1173 | #define BASIC_LINK_SERVICE 0x00000000 | ||
1174 | #define EXT_LINK_SERVICE 0x01000000 | ||
1175 | |||
1176 | #define LLC 0x04000000 | ||
1177 | #define LLC_SNAP 0x05000000 | ||
1178 | #define SCSI_FCP 0x08000000 | ||
1179 | #define SCSI_GPP 0x09000000 | ||
1180 | #define IPI3_MASTER 0x11000000 | ||
1181 | #define IPI3_SLAVE 0x12000000 | ||
1182 | #define IPI3_PEER 0x13000000 | ||
1183 | #define CP_IPI3_MASTER 0x15000000 | ||
1184 | #define CP_IPI3_SLAVE 0x16000000 | ||
1185 | #define CP_IPI3_PEER 0x17000000 | ||
1186 | #define SBCCS_CHANNEL 0x19000000 | ||
1187 | #define SBCCS_CONTROL 0x1A000000 | ||
1188 | #define FIBRE_SERVICES 0x20000000 | ||
1189 | #define FC_FG 0x21000000 | ||
1190 | #define FC_XS 0x22000000 | ||
1191 | #define FC_AL 0x23000000 | ||
1192 | #define SNMP 0x24000000 | ||
1193 | #define HIPPI_FP 0x40000000 | ||
1194 | #define TYPE_MASK 0xFF000000 | ||
1195 | |||
1196 | typedef struct { | ||
1197 | UCHAR seq_id_valid; | ||
1198 | UCHAR seq_id; | ||
1199 | USHORT reserved; // 2 bytes reserved | ||
1200 | ULONG ox_rx_id; | ||
1201 | USHORT low_seq_cnt; | ||
1202 | USHORT high_seq_cnt; | ||
1203 | } BA_ACC_PAYLOAD; | ||
1204 | |||
1205 | typedef struct { | ||
1206 | UCHAR reserved; | ||
1207 | UCHAR reason_code; | ||
1208 | UCHAR reason_explain; | ||
1209 | UCHAR vendor_unique; | ||
1210 | } BA_RJT_PAYLOAD; | ||
1211 | |||
1212 | |||
1213 | typedef struct { | ||
1214 | ULONG command_code; | ||
1215 | ULONG sid; | ||
1216 | USHORT ox_id; | ||
1217 | USHORT rx_id; | ||
1218 | } RRQ_MESSAGE; | ||
1219 | |||
1220 | typedef struct { | ||
1221 | ULONG command_code; | ||
1222 | UCHAR vendor; | ||
1223 | UCHAR explain; | ||
1224 | UCHAR reason; | ||
1225 | UCHAR reserved; | ||
1226 | } REJECT_MESSAGE; | ||
1227 | |||
1228 | |||
1229 | #define N_OR_F_PORT 0x1000 | ||
1230 | #define RANDOM_RELATIVE_OFFSET 0x4000 | ||
1231 | #define CONTINUOSLY_INCREASING 0x8000 | ||
1232 | |||
1233 | #define CLASS_VALID 0x8000 | ||
1234 | #define INTERMIX_MODE 0x4000 | ||
1235 | #define TRANSPARENT_STACKED 0x2000 | ||
1236 | #define LOCKDOWN_STACKED 0x1000 | ||
1237 | #define SEQ_DELIVERY 0x800 | ||
1238 | |||
1239 | #define XID_NOT_SUPPORTED 0x00 | ||
1240 | #define XID_SUPPORTED 0x4000 | ||
1241 | #define XID_REQUIRED 0xC000 | ||
1242 | |||
1243 | #define ASSOCIATOR_NOT_SUPPORTED 0x00 | ||
1244 | #define ASSOCIATOR_SUPPORTED 0x1000 | ||
1245 | #define ASSOCIATOR_REQUIRED 0x3000 | ||
1246 | |||
1247 | #define INIT_ACK0_SUPPORT 0x800 | ||
1248 | #define INIT_ACKN_SUPPORT 0x400 | ||
1249 | |||
1250 | #define RECIP_ACK0_SUPPORT 0x8000 | ||
1251 | #define RECIP_ACKN_SUPPORT 0x4000 | ||
1252 | |||
1253 | #define X_ID_INTERLOCK 0x2000 | ||
1254 | |||
1255 | #define ERROR_POLICY 0x1800 // Error Policy Supported | ||
1256 | #define ERROR_DISCARD 0x00 // Only Discard Supported | ||
1257 | #define ERROR_DISC_PROCESS 0x02 // Discard and process supported | ||
1258 | |||
1259 | #define NODE_ID 0x01 | ||
1260 | #define IEEE_EXT 0x20 | ||
1261 | |||
1262 | // | ||
1263 | // Categories Supported Per Sequence | ||
1264 | // | ||
1265 | #define CATEGORIES_PER_SEQUENCE 0x300 | ||
1266 | #define ONE_CATEGORY_SEQUENCE 0x00 // 1 Category per Sequence | ||
1267 | #define TWO_CATEGORY_SEQUENCE 0x01 // 2 Categories per Sequence | ||
1268 | #define MANY_CATEGORY_SEQUENCE 0x03 // > 2 Categories/Sequence | ||
1269 | |||
1270 | typedef struct { | ||
1271 | |||
1272 | USHORT initiator_control; | ||
1273 | USHORT service_options; | ||
1274 | |||
1275 | USHORT rx_data_size; | ||
1276 | USHORT recipient_control; | ||
1277 | |||
1278 | USHORT ee_credit; | ||
1279 | USHORT concurrent_sequences; | ||
1280 | |||
1281 | USHORT reserved; | ||
1282 | USHORT open_sequences; | ||
1283 | |||
1284 | } CLASS_PARAMETERS; | ||
1285 | |||
1286 | typedef struct { | ||
1287 | ULONG login_cmd; | ||
1288 | // | ||
1289 | // Common Service Parameters | ||
1290 | // | ||
1291 | struct { | ||
1292 | |||
1293 | USHORT bb_credit; | ||
1294 | UCHAR lowest_ver; | ||
1295 | UCHAR highest_ver; | ||
1296 | |||
1297 | USHORT bb_rx_size; | ||
1298 | USHORT common_features; | ||
1299 | |||
1300 | USHORT rel_offset; | ||
1301 | USHORT concurrent_seq; | ||
1302 | |||
1303 | |||
1304 | ULONG e_d_tov; | ||
1305 | } cmn_services; | ||
1306 | |||
1307 | // | ||
1308 | // Port Name | ||
1309 | // | ||
1310 | UCHAR port_name[8]; | ||
1311 | |||
1312 | // | ||
1313 | // Node/Fabric Name | ||
1314 | // | ||
1315 | UCHAR node_name[8]; | ||
1316 | |||
1317 | // | ||
1318 | // Class 1, 2 and 3 Service Parameters | ||
1319 | // | ||
1320 | CLASS_PARAMETERS class1; | ||
1321 | CLASS_PARAMETERS class2; | ||
1322 | CLASS_PARAMETERS class3; | ||
1323 | |||
1324 | ULONG reserved[4]; | ||
1325 | |||
1326 | // | ||
1327 | // Vendor Version Level | ||
1328 | // | ||
1329 | UCHAR vendor_id[2]; | ||
1330 | UCHAR vendor_version[6]; | ||
1331 | ULONG buffer_size; | ||
1332 | USHORT rxid_start; | ||
1333 | USHORT total_rxids; | ||
1334 | } LOGIN_PAYLOAD; | ||
1335 | |||
1336 | |||
1337 | typedef struct | ||
1338 | { | ||
1339 | ULONG cmd; // 4 bytes | ||
1340 | UCHAR n_port_identifier[3]; | ||
1341 | UCHAR reserved; | ||
1342 | UCHAR port_name[8]; | ||
1343 | } LOGOUT_PAYLOAD; | ||
1344 | |||
1345 | |||
1346 | // | ||
1347 | // PRLI Request Service Parameter Defines | ||
1348 | // | ||
1349 | #define PRLI_ACC 0x01 | ||
1350 | #define PRLI_REQ 0x02 | ||
1351 | #define ORIG_PROCESS_ASSOC_VALID 0x8000 | ||
1352 | #define RESP_PROCESS_ASSOC_VALID 0x4000 | ||
1353 | #define ESTABLISH_PAIR 0x2000 | ||
1354 | #define DATA_OVERLAY_ALLOWED 0x40 | ||
1355 | #define INITIATOR_FUNCTION 0x20 | ||
1356 | #define TARGET_FUNCTION 0x10 | ||
1357 | #define CMD_DATA_MIXED 0x08 | ||
1358 | #define DATA_RESP_MIXED 0x04 | ||
1359 | #define READ_XFER_RDY 0x02 | ||
1360 | #define WRITE_XFER_RDY 0x01 | ||
1361 | |||
1362 | #define RESPONSE_CODE_MASK 0xF00 | ||
1363 | #define REQUEST_EXECUTED 0x100 | ||
1364 | #define NO_RESOURCES 0x200 | ||
1365 | #define INIT_NOT_COMPLETE 0x300 | ||
1366 | #define IMAGE_DOES_NOT_EXIST 0x400 | ||
1367 | #define BAD_PREDEFINED_COND 0x500 | ||
1368 | #define REQ_EXEC_COND 0x600 | ||
1369 | #define NO_MULTI_PAGE 0x700 | ||
1370 | |||
1371 | typedef struct { | ||
1372 | USHORT payload_length; | ||
1373 | UCHAR page_length; | ||
1374 | UCHAR cmd; | ||
1375 | |||
1376 | |||
1377 | ULONG valid; | ||
1378 | |||
1379 | ULONG orig_process_associator; | ||
1380 | |||
1381 | ULONG resp_process_associator; | ||
1382 | |||
1383 | ULONG fcp_info; | ||
1384 | } PRLI_REQUEST; | ||
1385 | |||
1386 | typedef struct { | ||
1387 | |||
1388 | USHORT payload_length; | ||
1389 | UCHAR page_length; | ||
1390 | UCHAR cmd; | ||
1391 | |||
1392 | ULONG valid; | ||
1393 | ULONG orig_process_associator; | ||
1394 | |||
1395 | ULONG resp_process_associator; | ||
1396 | ULONG reserved; | ||
1397 | } PRLO_REQUEST; | ||
1398 | |||
1399 | typedef struct { | ||
1400 | ULONG cmd; | ||
1401 | |||
1402 | ULONG hard_address; | ||
1403 | |||
1404 | UCHAR port_name[8]; | ||
1405 | |||
1406 | UCHAR node_name[8]; | ||
1407 | |||
1408 | ULONG s_id; | ||
1409 | } ADISC_PAYLOAD; | ||
1410 | |||
1411 | struct ext_sg_entry_t { | ||
1412 | __u32 len:18; /* buffer length, bits 0-17 */ | ||
1413 | __u32 uba:13; /* upper bus address bits 18-31 */ | ||
1414 | __u32 lba; /* lower bus address bits 0-31 */ | ||
1415 | }; | ||
1416 | |||
1417 | |||
1418 | // J. McCarty's LINK.H | ||
1419 | // | ||
1420 | // LS_RJT Reason Codes | ||
1421 | // | ||
1422 | |||
1423 | #define INVALID_COMMAND_CODE 0x01 | ||
1424 | #define LOGICAL_ERROR 0x03 | ||
1425 | #define LOGICAL_BUSY 0x05 | ||
1426 | #define PROTOCOL_ERROR 0x07 | ||
1427 | #define UNABLE_TO_PERFORM 0x09 | ||
1428 | #define COMMAND_NOT_SUPPORTED 0x0B | ||
1429 | #define LS_VENDOR_UNIQUE 0xFF | ||
1430 | |||
1431 | // | ||
1432 | // LS_RJT Reason Codes Explanations | ||
1433 | // | ||
1434 | #define NO_REASON 0x00 | ||
1435 | #define OPTIONS_ERROR 0x01 | ||
1436 | #define INITIATOR_CTL_ERROR 0x03 | ||
1437 | #define RECIPIENT_CTL_ERROR 0x05 | ||
1438 | #define DATA_FIELD_SIZE_ERROR 0x07 | ||
1439 | #define CONCURRENT_SEQ_ERROR 0x09 | ||
1440 | #define CREDIT_ERROR 0x0B | ||
1441 | #define INVALID_PORT_NAME 0x0D | ||
1442 | #define INVALID_NODE_NAME 0x0E | ||
1443 | #define INVALID_CSP 0x0F // Invalid Service Parameters | ||
1444 | #define INVALID_ASSOC_HDR 0x11 // Invalid Association Header | ||
1445 | #define ASSOC_HDR_REQUIRED 0x13 // Association Header Required | ||
1446 | #define LS_INVALID_S_ID 0x15 | ||
1447 | #define INVALID_OX_RX_ID 0x17 // Invalid OX_ID RX_ID Combination | ||
1448 | #define CMD_IN_PROCESS 0x19 | ||
1449 | #define INVALID_IDENTIFIER 0x1F // Invalid N_PORT Identifier | ||
1450 | #define INVALID_SEQ_ID 0x21 | ||
1451 | #define ABT_INVALID_XCHNG 0x23 // Attempt to Abort an invalid Exchange | ||
1452 | #define ABT_INACTIVE_XCHNG 0x25 // Attempt to Abort an inactive Exchange | ||
1453 | #define NEED_REC_QUAL 0x27 // Recovery Qualifier required | ||
1454 | #define NO_LOGIN_RESOURCES 0x29 // No resources to support login | ||
1455 | #define NO_DATA 0x2A // Unable to supply requested data | ||
1456 | #define REQUEST_NOT_SUPPORTED 0x2C // Request Not Supported | ||
1457 | |||
1458 | // | ||
1459 | // Link Control Codes | ||
1460 | // | ||
1461 | |||
1462 | // | ||
1463 | // P_BSY Action Codes | ||
1464 | // | ||
1465 | #define SEQUENCE_TERMINATED 0x01000000 | ||
1466 | #define SEQUENCE_ACTIVE 0x02000000 | ||
1467 | |||
1468 | // | ||
1469 | // P_BSY Reason Codes | ||
1470 | // | ||
1471 | #define PHYS_NPORT_BUSY 0x010000 | ||
1472 | #define NPORT_RESOURCE_BUSY 0x020000 | ||
1473 | |||
1474 | // | ||
1475 | // P_RJT, F_RJT Action Codes | ||
1476 | // | ||
1477 | |||
1478 | #define RETRYABLE_ERROR 0x01000000 | ||
1479 | #define NON_RETRYABLE_ERROR 0x02000000 | ||
1480 | |||
1481 | // | ||
1482 | // P_RJT, F_RJT Reason Codes | ||
1483 | // | ||
1484 | #define INVALID_D_ID 0x010000 | ||
1485 | #define INVALID_S_ID 0x020000 | ||
1486 | #define NPORT_NOT_AVAIL_TMP 0x030000 | ||
1487 | #define NPORT_NOT_AVAIL_PERM 0x040000 | ||
1488 | #define CLASS_NOT_SUPPORTED 0x050000 | ||
1489 | #define USAGE_ERROR 0x060000 | ||
1490 | #define TYPE_NOT_SUPPORTED 0x070000 | ||
1491 | #define INVAL_LINK_CONTROL 0x080000 | ||
1492 | #define INVAL_R_CTL 0x090000 | ||
1493 | #define INVAL_F_CTL 0x0A0000 | ||
1494 | #define INVAL_OX_ID 0x0B0000 | ||
1495 | #define INVAL_RX_ID 0x0C0000 | ||
1496 | #define INVAL_SEQ_ID 0x0D0000 | ||
1497 | #define INVAL_DF_CTL 0x0E0000 | ||
1498 | #define INVAL_SEQ_CNT 0x0F0000 | ||
1499 | #define INVAL_PARAMS 0x100000 | ||
1500 | #define EXCHANGE_ERROR 0x110000 | ||
1501 | #define LS_PROTOCOL_ERROR 0x120000 | ||
1502 | #define INCORRECT_LENGTH 0x130000 | ||
1503 | #define UNEXPECTED_ACK 0x140000 | ||
1504 | #define LOGIN_REQ 0x160000 | ||
1505 | #define EXCESSIVE_SEQ 0x170000 | ||
1506 | #define NO_EXCHANGE 0x180000 | ||
1507 | #define SEC_HDR_NOT_SUPPORTED 0x190000 | ||
1508 | #define NO_FABRIC 0x1A0000 | ||
1509 | #define P_VENDOR_UNIQUE 0xFF0000 | ||
1510 | |||
1511 | // | ||
1512 | // BA_RJT Reason Codes | ||
1513 | // | ||
1514 | #define BA_INVALID_COMMAND 0x00010000 | ||
1515 | #define BA_LOGICAL_ERROR 0x00030000 | ||
1516 | #define BA_LOGICAL_BUSY 0x00050000 | ||
1517 | #define BA_PROTOCOL_ERROR 0x00070000 | ||
1518 | #define BA_UNABLE_TO_PERFORM 0x00090000 | ||
1519 | |||
1520 | // | ||
1521 | // BA_RJT Reason Explanation Codes | ||
1522 | // | ||
1523 | #define BA_NO_REASON 0x00000000 | ||
1524 | #define BA_INVALID_OX_RX 0x00000300 | ||
1525 | #define BA_SEQUENCE_ABORTED 0x00000500 | ||
1526 | |||
1527 | |||
1528 | |||
1529 | #endif /* CPQFCTSSTRUCTS_H */ | ||
1530 | |||
diff --git a/drivers/scsi/cpqfcTStrigger.c b/drivers/scsi/cpqfcTStrigger.c deleted file mode 100644 index dbb7e65159a0..000000000000 --- a/drivers/scsi/cpqfcTStrigger.c +++ /dev/null | |||
@@ -1,33 +0,0 @@ | |||
1 | // Routine to trigger Finisar GTA analyzer. Runs of GPIO2 | ||
2 | // NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation | ||
3 | // since it writes directly to the Tachyon board. This function | ||
4 | // developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB) | ||
5 | |||
6 | #include "cpqfcTStrigger.h" | ||
7 | #if TRIGGERABLE_HBA | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/ioport.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/pci.h> | ||
13 | #include <asm/io.h> | ||
14 | |||
15 | void TriggerHBA( void* IOBaseUpper, int Print) | ||
16 | { | ||
17 | __u32 long value; | ||
18 | |||
19 | // get initial value in hopes of not modifying any other GPIO line | ||
20 | IOBaseUpper += 0x188; // TachTL/TS Control reg | ||
21 | |||
22 | value = readl( IOBaseUpper); | ||
23 | // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA) | ||
24 | // The Finisar anaylzer triggers on low-to-high TTL transition | ||
25 | value |= 0x01; // set bit 0 | ||
26 | |||
27 | writel( value, IOBaseUpper); | ||
28 | |||
29 | if( Print) | ||
30 | printk( " -GPIO0 set- "); | ||
31 | } | ||
32 | |||
33 | #endif | ||
diff --git a/drivers/scsi/cpqfcTStrigger.h b/drivers/scsi/cpqfcTStrigger.h deleted file mode 100644 index c961792e6be0..000000000000 --- a/drivers/scsi/cpqfcTStrigger.h +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | // don't do this unless you have the right hardware! | ||
2 | #define TRIGGERABLE_HBA 0 | ||
3 | #if TRIGGERABLE_HBA | ||
4 | void TriggerHBA( void*, int); | ||
5 | #else | ||
6 | #define TriggerHBA(x, y) | ||
7 | #endif | ||
8 | |||
diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c deleted file mode 100644 index d822ddcc52b2..000000000000 --- a/drivers/scsi/cpqfcTSworker.c +++ /dev/null | |||
@@ -1,6516 +0,0 @@ | |||
1 | /* Copyright(c) 2000, Compaq Computer Corporation | ||
2 | * Fibre Channel Host Bus Adapter | ||
3 | * 64-bit, 66MHz PCI | ||
4 | * Originally developed and tested on: | ||
5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... | ||
6 | * SP# P225CXCBFIEL6T, Rev XC | ||
7 | * SP# 161290-001, Rev XD | ||
8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2, or (at your option) any | ||
13 | * later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but | ||
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * Written by Don Zimmerman | ||
20 | */ | ||
21 | |||
22 | #include <linux/sched.h> | ||
23 | #include <linux/timer.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/stat.h> | ||
29 | #include <linux/blkdev.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/smp_lock.h> | ||
33 | #include <linux/pci.h> | ||
34 | |||
35 | #define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) | ||
36 | |||
37 | #include <asm/system.h> | ||
38 | #include <asm/irq.h> | ||
39 | #include <asm/dma.h> | ||
40 | |||
41 | #include "scsi.h" | ||
42 | #include <scsi/scsi_host.h> // struct Scsi_Host definition for T handler | ||
43 | #include "cpqfcTSchip.h" | ||
44 | #include "cpqfcTSstructs.h" | ||
45 | #include "cpqfcTStrigger.h" | ||
46 | |||
47 | //#define LOGIN_DBG 1 | ||
48 | |||
49 | // REMARKS: | ||
50 | // Since Tachyon chips may be permitted to wait from 500ms up to 2 sec | ||
51 | // to empty an outgoing frame from its FIFO to the Fibre Channel stream, | ||
52 | // we cannot do everything we need to in the interrupt handler. Specifically, | ||
53 | // every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be | ||
54 | // suspended until the login sequences have been completed. Login commands | ||
55 | // are frames just like SCSI commands are frames; they are subject to the same | ||
56 | // timeout issues and delays. Also, various specs provide up to 2 seconds for | ||
57 | // devices to log back in (i.e. respond with ACC to a login frame), so I/O to | ||
58 | // that device has to be suspended. | ||
59 | // A serious problem here occurs on highly loaded FC-AL systems. If our FC port | ||
60 | // has a low priority (e.g. high arbitrated loop physical address, alpa), and | ||
61 | // some other device is hogging bandwidth (permissible under FC-AL), we might | ||
62 | // time out thinking the link is hung, when it's simply busy. Many such | ||
63 | // considerations complicate the design. Although Tachyon assumes control | ||
64 | // (in silicon) for many link-specific issues, the Linux driver is left with the | ||
65 | // rest, which turns out to be a difficult, time critical chore. | ||
66 | |||
67 | // These "worker" functions will handle things like FC Logins; all | ||
68 | // processes with I/O to our device must wait for the Login to complete | ||
69 | // and (if successful) I/O to resume. In the event of a malfunctioning or | ||
70 | // very busy loop, it may take hundreds of millisecs or even seconds to complete | ||
71 | // a frame send. We don't want to hang up the entire server (and all | ||
72 | // processes which don't depend on Fibre) during this wait. | ||
73 | |||
74 | // The Tachyon chip can have around 30,000 I/O operations ("exchanges") | ||
75 | // open at one time. However, each exchange must be initiated | ||
76 | // synchronously (i.e. each of the 30k I/O had to be started one at a | ||
77 | // time by sending a starting frame via Tachyon's outbound que). | ||
78 | |||
79 | // To accommodate kernel "module" build, this driver limits the exchanges | ||
80 | // to 256, because of the contiguous physical memory limitation of 128M. | ||
81 | |||
82 | // Typical FC Exchanges are opened presuming the FC frames start without errors, | ||
83 | // while Exchange completion is handled in the interrupt handler. This | ||
84 | // optimizes performance for the "everything's working" case. | ||
85 | // However, when we have FC related errors or hot plugging of FC ports, we pause | ||
86 | // I/O and handle FC-specific tasks in the worker thread. These FC-specific | ||
87 | // functions will handle things like FC Logins and Aborts. As the Login sequence | ||
88 | // completes to each and every target, I/O can resume to that target. | ||
89 | |||
90 | // Our kernel "worker thread" must share the HBA with threads calling | ||
91 | // "queuecommand". We define a "BoardLock" semaphore which indicates | ||
92 | // to "queuecommand" that the HBA is unavailable, and Cmnds are added to a | ||
93 | // board lock Q. When the worker thread finishes with the board, the board | ||
94 | // lock Q commands are completed with status causing immediate retry. | ||
95 | // Typically, the board is locked while Logins are in progress after an | ||
96 | // FC Link Down condition. When Cmnds are re-queued after board lock, the | ||
97 | // particular Scsi channel/target may or may not have logged back in. When | ||
98 | // the device is waiting for login, the "prli" flag is clear, in which case | ||
99 | // commands are passed to a Link Down Q. Whenever the login finally completes, | ||
100 | // the LinkDown Q is completed, again with status causing immediate retry. | ||
101 | // When FC devices are logged in, we build and start FC commands to the | ||
102 | // devices. | ||
103 | |||
104 | // NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices | ||
105 | // that never log back in (e.g. physically removed) is NOT completely | ||
106 | // understood. I've still seen instances of system hangs on failed Write | ||
107 | // commands (possibly from the ext2 layer?) on device removal. Such special | ||
108 | // cases need to be evaluated from a system/application view - e.g., how | ||
109 | // exactly does the system want me to complete commands when the device is | ||
110 | // physically removed?? | ||
111 | |||
112 | // local functions | ||
113 | |||
114 | static void SetLoginFields( | ||
115 | PFC_LOGGEDIN_PORT pLoggedInPort, | ||
116 | TachFCHDR_GCMND* fchs, | ||
117 | BOOLEAN PDisc, | ||
118 | BOOLEAN Originator); | ||
119 | |||
120 | static void AnalyzeIncomingFrame( | ||
121 | CPQFCHBA *cpqfcHBAdata, | ||
122 | ULONG QNdx ); | ||
123 | |||
124 | static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds ); | ||
125 | |||
126 | static int verify_PLOGI( PTACHYON fcChip, | ||
127 | TachFCHDR_GCMND* fchs, ULONG* reject_explain); | ||
128 | static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain); | ||
129 | |||
130 | static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type); | ||
131 | static void BuildLinkServicePayload( | ||
132 | PTACHYON fcChip, ULONG type, void* payload); | ||
133 | |||
134 | static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, | ||
135 | PFC_LOGGEDIN_PORT pLoggedInPort); | ||
136 | |||
137 | static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID); | ||
138 | |||
139 | static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata); | ||
140 | |||
141 | static void RevalidateSEST( struct Scsi_Host *HostAdapter, | ||
142 | PFC_LOGGEDIN_PORT pLoggedInPort); | ||
143 | |||
144 | static void IssueReportLunsCommand( | ||
145 | CPQFCHBA* cpqfcHBAdata, | ||
146 | TachFCHDR_GCMND* fchs); | ||
147 | |||
148 | // (see scsi_error.c comments on kernel task creation) | ||
149 | |||
150 | void cpqfcTSWorkerThread( void *host) | ||
151 | { | ||
152 | struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host; | ||
153 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
154 | #ifdef PCI_KERNEL_TRACE | ||
155 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
156 | #endif | ||
157 | DECLARE_MUTEX_LOCKED(fcQueReady); | ||
158 | DECLARE_MUTEX_LOCKED(fcTYOBcomplete); | ||
159 | DECLARE_MUTEX_LOCKED(TachFrozen); | ||
160 | DECLARE_MUTEX_LOCKED(BoardLock); | ||
161 | |||
162 | ENTER("WorkerThread"); | ||
163 | |||
164 | lock_kernel(); | ||
165 | daemonize("cpqfcTS_wt_%d", HostAdapter->host_no); | ||
166 | siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); | ||
167 | |||
168 | |||
169 | cpqfcHBAdata->fcQueReady = &fcQueReady; // primary wait point | ||
170 | cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete; | ||
171 | cpqfcHBAdata->TachFrozen = &TachFrozen; | ||
172 | |||
173 | |||
174 | cpqfcHBAdata->worker_thread = current; | ||
175 | |||
176 | unlock_kernel(); | ||
177 | |||
178 | if( cpqfcHBAdata->notify_wt != NULL ) | ||
179 | up( cpqfcHBAdata->notify_wt); // OK to continue | ||
180 | |||
181 | while(1) | ||
182 | { | ||
183 | unsigned long flags; | ||
184 | |||
185 | down_interruptible( &fcQueReady); // wait for something to do | ||
186 | |||
187 | if (signal_pending(current) ) | ||
188 | break; | ||
189 | |||
190 | PCI_TRACE( 0x90) | ||
191 | // first, take the IO lock so the SCSI upper layers can't call | ||
192 | // into our _quecommand function (this also disables INTs) | ||
193 | spin_lock_irqsave( HostAdapter->host_lock, flags); // STOP _que function | ||
194 | PCI_TRACE( 0x90) | ||
195 | |||
196 | CPQ_SPINLOCK_HBA( cpqfcHBAdata) | ||
197 | // next, set this pointer to indicate to the _quecommand function | ||
198 | // that the board is in use, so it should que the command and | ||
199 | // immediately return (we don't actually require the semaphore function | ||
200 | // in this driver rev) | ||
201 | |||
202 | cpqfcHBAdata->BoardLock = &BoardLock; | ||
203 | |||
204 | PCI_TRACE( 0x90) | ||
205 | |||
206 | // release the IO lock (and re-enable interrupts) | ||
207 | spin_unlock_irqrestore( HostAdapter->host_lock, flags); | ||
208 | |||
209 | // disable OUR HBA interrupt (keep them off as much as possible | ||
210 | // during error recovery) | ||
211 | disable_irq( cpqfcHBAdata->HostAdapter->irq); | ||
212 | |||
213 | // OK, let's process the Fibre Channel Link Q and do the work | ||
214 | cpqfcTS_WorkTask( HostAdapter); | ||
215 | |||
216 | // hopefully, no more "work" to do; | ||
217 | // re-enable our INTs for "normal" completion processing | ||
218 | enable_irq( cpqfcHBAdata->HostAdapter->irq); | ||
219 | |||
220 | |||
221 | cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued | ||
222 | CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) | ||
223 | |||
224 | |||
225 | // Now, complete any Cmnd we Q'd up while BoardLock was held | ||
226 | |||
227 | CompleteBoardLockCmnd( cpqfcHBAdata); | ||
228 | |||
229 | |||
230 | } | ||
231 | // hopefully, the signal was for our module exit... | ||
232 | if( cpqfcHBAdata->notify_wt != NULL ) | ||
233 | up( cpqfcHBAdata->notify_wt); // yep, we're outta here | ||
234 | } | ||
235 | |||
236 | |||
237 | // Freeze Tachyon routine. | ||
238 | // If Tachyon is already frozen, return FALSE | ||
239 | // If Tachyon is not frozen, call freeze function, return TRUE | ||
240 | // | ||
241 | static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata) | ||
242 | { | ||
243 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
244 | BOOLEAN FrozeTach = FALSE; | ||
245 | // It's possible that the chip is already frozen; if so, | ||
246 | // "Freezing" again will NOT! generate another Freeze | ||
247 | // Completion Message. | ||
248 | |||
249 | if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000) | ||
250 | { // (need to freeze...) | ||
251 | fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists | ||
252 | |||
253 | // 2. Get Tach freeze confirmation | ||
254 | // (synchronize SEST manipulation with Freeze Completion Message) | ||
255 | // we need INTs on so semaphore can be set. | ||
256 | enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore | ||
257 | down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem. | ||
258 | // can we TIMEOUT semaphore wait?? TBD | ||
259 | disable_irq( cpqfcHBAdata->HostAdapter->irq); | ||
260 | |||
261 | FrozeTach = TRUE; | ||
262 | } // (else, already frozen) | ||
263 | |||
264 | return FrozeTach; | ||
265 | } | ||
266 | |||
267 | |||
268 | |||
269 | |||
270 | // This is the kernel worker thread task, which processes FC | ||
271 | // tasks which were queued by the Interrupt handler or by | ||
272 | // other WorkTask functions. | ||
273 | |||
274 | #define DBG 1 | ||
275 | //#undef DBG | ||
276 | void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter) | ||
277 | { | ||
278 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
279 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
280 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
281 | ULONG QconsumerNdx; | ||
282 | LONG ExchangeID; | ||
283 | ULONG ulStatus=0; | ||
284 | TachFCHDR_GCMND fchs; | ||
285 | PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; | ||
286 | |||
287 | ENTER("WorkTask"); | ||
288 | |||
289 | // copy current index to work on | ||
290 | QconsumerNdx = fcLQ->consumer; | ||
291 | |||
292 | PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90) | ||
293 | |||
294 | |||
295 | // NOTE: when this switch completes, we will "consume" the Que item | ||
296 | // printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type); | ||
297 | switch( fcLQ->Qitem[QconsumerNdx].Type ) | ||
298 | { | ||
299 | // incoming frame - link service (ACC, UNSOL REQ, etc.) | ||
300 | // or FCP-SCSI command | ||
301 | case SFQ_UNKNOWN: | ||
302 | AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx ); | ||
303 | |||
304 | break; | ||
305 | |||
306 | |||
307 | |||
308 | case EXCHANGE_QUEUED: // an Exchange (i.e. FCP-SCSI) was previously | ||
309 | // Queued because the link was down. The | ||
310 | // heartbeat timer detected it and Queued it here. | ||
311 | // We attempt to start it again, and if | ||
312 | // successful we clear the EXCHANGE_Q flag. | ||
313 | // If the link doesn't come up, the Exchange | ||
314 | // will eventually time-out. | ||
315 | |||
316 | ExchangeID = (LONG) // x_ID copied from DPC timeout function | ||
317 | fcLQ->Qitem[QconsumerNdx].ulBuff[0]; | ||
318 | |||
319 | // It's possible that a Q'd exchange could have already | ||
320 | // been started by other logic (e.g. ABTS process) | ||
321 | // Don't start if already started (Q'd flag clear) | ||
322 | |||
323 | if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED ) | ||
324 | { | ||
325 | // printk(" *Start Q'd x_ID %Xh: type %Xh ", | ||
326 | // ExchangeID, Exchanges->fcExchange[ExchangeID].type); | ||
327 | |||
328 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID); | ||
329 | if( !ulStatus ) | ||
330 | { | ||
331 | // printk("success* "); | ||
332 | } | ||
333 | else | ||
334 | { | ||
335 | #ifdef DBG | ||
336 | |||
337 | if( ulStatus == EXCHANGE_QUEUED) | ||
338 | printk("Queued* "); | ||
339 | else | ||
340 | printk("failed* "); | ||
341 | |||
342 | #endif | ||
343 | } | ||
344 | } | ||
345 | break; | ||
346 | |||
347 | |||
348 | case LINKDOWN: | ||
349 | // (lots of things already done in INT handler) future here? | ||
350 | break; | ||
351 | |||
352 | |||
353 | case LINKACTIVE: // Tachyon set the Lup bit in FM status | ||
354 | // NOTE: some misbehaving FC ports (like Tach2.1) | ||
355 | // can re-LIP immediately after a LIP completes. | ||
356 | |||
357 | // if "initiator", need to verify LOGs with ports | ||
358 | // printk("\n*LNKUP* "); | ||
359 | |||
360 | if( fcChip->Options.initiator ) | ||
361 | SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data | ||
362 | // if SendLogins successfully completes, PortDiscDone | ||
363 | // will be set. | ||
364 | |||
365 | |||
366 | // If SendLogins was successful, then we expect to get incoming | ||
367 | // ACCepts or REJECTs, which are handled below. | ||
368 | |||
369 | break; | ||
370 | |||
371 | // LinkService and Fabric request/reply processing | ||
372 | case ELS_FDISC: // need to send Fabric Discovery (Login) | ||
373 | case ELS_FLOGI: // need to send Fabric Login | ||
374 | case ELS_SCR: // need to send State Change Registration | ||
375 | case FCS_NSR: // need to send Name Service Request | ||
376 | case ELS_PLOGI: // need to send PLOGI | ||
377 | case ELS_ACC: // send generic ACCept | ||
378 | case ELS_PLOGI_ACC: // need to send ELS ACCept frame to recv'd PLOGI | ||
379 | case ELS_PRLI_ACC: // need to send ELS ACCept frame to recv'd PRLI | ||
380 | case ELS_LOGO: // need to send ELS LOGO (logout) | ||
381 | case ELS_LOGO_ACC: // need to send ELS ACCept frame to recv'd PLOGI | ||
382 | case ELS_RJT: // ReJecT reply | ||
383 | case ELS_PRLI: // need to send ELS PRLI | ||
384 | |||
385 | |||
386 | // printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type); | ||
387 | // if PortDiscDone is not set, it means the SendLogins routine | ||
388 | // failed to complete -- assume that LDn occurred, so login frames | ||
389 | // are invalid | ||
390 | if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn | ||
391 | { | ||
392 | printk("Discard Q'd ELS login frame\n"); | ||
393 | break; | ||
394 | } | ||
395 | |||
396 | ulStatus = cpqfcTSBuildExchange( | ||
397 | cpqfcHBAdata, | ||
398 | fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI | ||
399 | (TachFCHDR_GCMND*) | ||
400 | fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs | ||
401 | NULL, // no data (no scatter/gather list) | ||
402 | &ExchangeID );// fcController->fcExchanges index, -1 if failed | ||
403 | |||
404 | if( !ulStatus ) // Exchange setup? | ||
405 | { | ||
406 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); | ||
407 | if( !ulStatus ) | ||
408 | { | ||
409 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
410 | // waited for completion for ELS type (Login frames issued | ||
411 | // synchronously) | ||
412 | } | ||
413 | else | ||
414 | // check reason for Exchange not being started - we might | ||
415 | // want to Queue and start later, or fail with error | ||
416 | { | ||
417 | |||
418 | } | ||
419 | } | ||
420 | |||
421 | else // Xchange setup failed... | ||
422 | printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); | ||
423 | |||
424 | break; | ||
425 | |||
426 | case SCSI_REPORT_LUNS: | ||
427 | // pass the incoming frame (actually, it's a PRLI frame) | ||
428 | // so we can send REPORT_LUNS, in order to determine VSA/PDU | ||
429 | // FCP-SCSI Lun address mode | ||
430 | IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*) | ||
431 | fcLQ->Qitem[QconsumerNdx].ulBuff); | ||
432 | |||
433 | break; | ||
434 | |||
435 | |||
436 | |||
437 | |||
438 | case BLS_ABTS: // need to ABORT one or more exchanges | ||
439 | { | ||
440 | LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0]; | ||
441 | BOOLEAN FrozeTach = FALSE; | ||
442 | |||
443 | if ( x_ID >= TACH_SEST_LEN ) // (in)sanity check | ||
444 | { | ||
445 | // printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID); | ||
446 | break; | ||
447 | } | ||
448 | |||
449 | |||
450 | if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE | ||
451 | { | ||
452 | // printk(" ABTS %Xh Scsi Cmnd null! ", x_ID); | ||
453 | |||
454 | break; // nothing to abort! | ||
455 | } | ||
456 | |||
457 | //#define ABTS_DBG | ||
458 | #ifdef ABTS_DBG | ||
459 | printk("INV SEST[%X] ", x_ID); | ||
460 | if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT) | ||
461 | { | ||
462 | printk("FC2TO"); | ||
463 | } | ||
464 | if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT) | ||
465 | { | ||
466 | printk("IA"); | ||
467 | } | ||
468 | if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED) | ||
469 | { | ||
470 | printk("PORTID"); | ||
471 | } | ||
472 | if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) | ||
473 | { | ||
474 | printk("DEVRM"); | ||
475 | } | ||
476 | if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX) | ||
477 | { | ||
478 | printk("LKF"); | ||
479 | } | ||
480 | if( Exchanges->fcExchange[x_ID].status & FRAME_TO) | ||
481 | { | ||
482 | printk("FRMTO"); | ||
483 | } | ||
484 | if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY) | ||
485 | { | ||
486 | printk("ABSQ"); | ||
487 | } | ||
488 | if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME) | ||
489 | { | ||
490 | printk("SFQFR"); | ||
491 | } | ||
492 | |||
493 | if( Exchanges->fcExchange[ x_ID].type == 0x2000) | ||
494 | printk(" WR"); | ||
495 | else if( Exchanges->fcExchange[ x_ID].type == 0x3000) | ||
496 | printk(" RD"); | ||
497 | else if( Exchanges->fcExchange[ x_ID].type == 0x10) | ||
498 | printk(" ABTS"); | ||
499 | else | ||
500 | printk(" %Xh", Exchanges->fcExchange[ x_ID].type); | ||
501 | |||
502 | if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)) | ||
503 | { | ||
504 | printk(" Cmd %p, ", | ||
505 | Exchanges->fcExchange[ x_ID].Cmnd); | ||
506 | |||
507 | printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", | ||
508 | cpqfcHBAdata->HBAnum, | ||
509 | Exchanges->fcExchange[ x_ID].Cmnd->channel, | ||
510 | Exchanges->fcExchange[ x_ID].Cmnd->target, | ||
511 | Exchanges->fcExchange[ x_ID].Cmnd->lun, | ||
512 | Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF); | ||
513 | } | ||
514 | else // assume that Cmnd ptr is invalid on _abort() | ||
515 | { | ||
516 | printk(" Cmd ptr invalid\n"); | ||
517 | } | ||
518 | |||
519 | #endif | ||
520 | |||
521 | |||
522 | // Steps to ABORT a SEST exchange: | ||
523 | // 1. Freeze TL SCSI assists & ERQ (everything) | ||
524 | // 2. Receive FROZEN inbound CM (must succeed!) | ||
525 | // 3. Invalidate x_ID SEST entry | ||
526 | // 4. Resume TL SCSI assists & ERQ (everything) | ||
527 | // 5. Build/start on exchange - change "type" to BLS_ABTS, | ||
528 | // timeout to X sec (RA_TOV from PLDA is actually 0) | ||
529 | // 6. Set Exchange Q'd status if ABTS cannot be started, | ||
530 | // or simply complete Exchange in "Terminate" condition | ||
531 | |||
532 | PCI_TRACEO( x_ID, 0xB4) | ||
533 | |||
534 | // 1 & 2 . Freeze Tach & get confirmation of freeze | ||
535 | FrozeTach = FreezeTach( cpqfcHBAdata); | ||
536 | |||
537 | // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange. | ||
538 | // FC2_TIMEOUT means we are originating the abort, while | ||
539 | // TARGET_ABORT means we are ACCepting an abort. | ||
540 | // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are | ||
541 | // all from Tachyon: | ||
542 | // Exchange was corrupted by LDn or other FC physical failure | ||
543 | // INITIATOR_ABORT means the upper layer driver/application | ||
544 | // requested the abort. | ||
545 | |||
546 | |||
547 | |||
548 | // clear bit 31 (VALid), to invalidate & take control from TL | ||
549 | fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; | ||
550 | |||
551 | |||
552 | // examine and Tach's "Linked List" for IWEs that | ||
553 | // received (nearly) simultaneous transfer ready (XRDY) | ||
554 | // repair linked list if necessary (TBD!) | ||
555 | // (If we ignore the "Linked List", we will time out | ||
556 | // WRITE commands where we received the FCP-SCSI XFRDY | ||
557 | // frame (because Tachyon didn't processes it). Linked List | ||
558 | // management should be done as an optimization. | ||
559 | |||
560 | // readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST )); | ||
561 | |||
562 | |||
563 | |||
564 | |||
565 | // 4. Resume all Tachlite functions (for other open Exchanges) | ||
566 | // as quickly as possible to allow other exchanges to other ports | ||
567 | // to resume. Freezing Tachyon may cause cascading errors, because | ||
568 | // any received SEST frame cannot be processed by the SEST. | ||
569 | // Don't "unfreeze" unless Link is operational | ||
570 | if( FrozeTach ) // did we just freeze it (above)? | ||
571 | fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists | ||
572 | |||
573 | |||
574 | PCI_TRACEO( x_ID, 0xB4) | ||
575 | |||
576 | // Note there is no confirmation that the chip is "unfrozen". Also, | ||
577 | // if the Link is down when unfreeze is called, it has no effect. | ||
578 | // Chip will unfreeze when the Link is back up. | ||
579 | |||
580 | // 5. Now send out Abort commands if possible | ||
581 | // Some Aborts can't be "sent" (Port_id changed or gone); | ||
582 | // if the device is gone, there is no port_id to send the ABTS to. | ||
583 | |||
584 | if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED) | ||
585 | && | ||
586 | !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) ) | ||
587 | { | ||
588 | Exchanges->fcExchange[ x_ID].type = BLS_ABTS; | ||
589 | fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id; | ||
590 | ulStatus = cpqfcTSBuildExchange( | ||
591 | cpqfcHBAdata, | ||
592 | BLS_ABTS, | ||
593 | &fchs, // (uses only s_id) | ||
594 | NULL, // (no scatter/gather list for ABTS) | ||
595 | &x_ID );// ABTS on this Exchange ID | ||
596 | |||
597 | if( !ulStatus ) // Exchange setup build OK? | ||
598 | { | ||
599 | |||
600 | // ABTS may be needed because an Exchange was corrupted | ||
601 | // by a Link disruption. If the Link is UP, we can | ||
602 | // presume that this ABTS can start immediately; otherwise, | ||
603 | // set Que'd status so the Login functions | ||
604 | // can restart it when the FC physical Link is restored | ||
605 | if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init? | ||
606 | { | ||
607 | // printk(" *set Q status x_ID %Xh on LDn* ", x_ID); | ||
608 | Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED; | ||
609 | } | ||
610 | |||
611 | else // what FC device (port_id) does the Cmd belong to? | ||
612 | { | ||
613 | PFC_LOGGEDIN_PORT pLoggedInPort = | ||
614 | Exchanges->fcExchange[ x_ID].pLoggedInPort; | ||
615 | |||
616 | // if Port is logged in, we might start the abort. | ||
617 | |||
618 | if( (pLoggedInPort != NULL) | ||
619 | && | ||
620 | (pLoggedInPort->prli == TRUE) ) | ||
621 | { | ||
622 | // it's possible that an Exchange has already been Queued | ||
623 | // to start after Login completes. Check and don't | ||
624 | // start it (again) here if Q'd status set | ||
625 | // printk(" ABTS xchg %Xh ", x_ID); | ||
626 | if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED) | ||
627 | { | ||
628 | // printk("already Q'd "); | ||
629 | } | ||
630 | else | ||
631 | { | ||
632 | // printk("starting "); | ||
633 | |||
634 | fcChip->fcStats.FC2aborted++; | ||
635 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID ); | ||
636 | if( !ulStatus ) | ||
637 | { | ||
638 | // OK | ||
639 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
640 | } | ||
641 | else | ||
642 | { | ||
643 | /* printk("ABTS exchange start failed -status %Xh, x_ID %Xh ", | ||
644 | ulStatus, x_ID); | ||
645 | */ | ||
646 | } | ||
647 | } | ||
648 | } | ||
649 | else | ||
650 | { | ||
651 | /* printk(" ABTS NOT starting xchg %Xh, %p ", | ||
652 | x_ID, pLoggedInPort); | ||
653 | if( pLoggedInPort ) | ||
654 | printk("prli %d ", pLoggedInPort->prli); | ||
655 | */ | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | else // what the #@! | ||
660 | { // how do we fail to build an Exchange for ABTS?? | ||
661 | printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n", | ||
662 | ulStatus, x_ID); | ||
663 | } | ||
664 | } | ||
665 | else // abort without ABTS -- just complete exchange/Cmnd to Linux | ||
666 | { | ||
667 | // printk(" *Terminating x_ID %Xh on %Xh* ", | ||
668 | // x_ID, Exchanges->fcExchange[x_ID].status); | ||
669 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, x_ID); | ||
670 | |||
671 | } | ||
672 | } // end of ABTS case | ||
673 | break; | ||
674 | |||
675 | |||
676 | |||
677 | case BLS_ABTS_ACC: // need to ACCept one ABTS | ||
678 | // (NOTE! this code not updated for Linux yet..) | ||
679 | |||
680 | |||
681 | printk(" *ABTS_ACC* "); | ||
682 | // 1. Freeze TL | ||
683 | |||
684 | fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists | ||
685 | |||
686 | memcpy( // copy the incoming ABTS frame | ||
687 | &fchs, | ||
688 | fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs | ||
689 | sizeof( fchs)); | ||
690 | |||
691 | // 3. OK, Tachyon is frozen so we can invalidate SEST entry | ||
692 | // (if necessary) | ||
693 | // Status FC2_TIMEOUT means we are originating the abort, while | ||
694 | // TARGET_ABORT means we are ACCepting an abort | ||
695 | |||
696 | ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange | ||
697 | // printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID); | ||
698 | |||
699 | |||
700 | // sanity check on received ExchangeID | ||
701 | if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT ) | ||
702 | { | ||
703 | // clear bit 31 (VALid), to invalidate & take control from TL | ||
704 | // printk("Invalidating SEST exchange %Xh\n", ExchangeID); | ||
705 | fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF; | ||
706 | } | ||
707 | |||
708 | |||
709 | // 4. Resume all Tachlite functions (for other open Exchanges) | ||
710 | // as quickly as possible to allow other exchanges to other ports | ||
711 | // to resume. Freezing Tachyon for too long may royally screw | ||
712 | // up everything! | ||
713 | fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists | ||
714 | |||
715 | // Note there is no confirmation that the chip is "unfrozen". Also, | ||
716 | // if the Link is down when unfreeze is called, it has no effect. | ||
717 | // Chip will unfreeze when the Link is back up. | ||
718 | |||
719 | // 5. Now send out Abort ACC reply for this exchange | ||
720 | Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC; | ||
721 | |||
722 | fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id; | ||
723 | ulStatus = cpqfcTSBuildExchange( | ||
724 | cpqfcHBAdata, | ||
725 | BLS_ABTS_ACC, | ||
726 | &fchs, | ||
727 | NULL, // no data (no scatter/gather list) | ||
728 | &ExchangeID );// fcController->fcExchanges index, -1 if failed | ||
729 | |||
730 | if( !ulStatus ) // Exchange setup? | ||
731 | { | ||
732 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); | ||
733 | if( !ulStatus ) | ||
734 | { | ||
735 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
736 | // waited for completion for ELS type (Login frames issued | ||
737 | // synchronously) | ||
738 | } | ||
739 | else | ||
740 | // check reason for Exchange not being started - we might | ||
741 | // want to Queue and start later, or fail with error | ||
742 | { | ||
743 | |||
744 | } | ||
745 | } | ||
746 | break; | ||
747 | |||
748 | |||
749 | case BLS_ABTS_RJT: // need to ReJecT one ABTS; reject implies the | ||
750 | // exchange doesn't exist in the TARGET context. | ||
751 | // ExchangeID has to come from LinkService space. | ||
752 | |||
753 | printk(" *ABTS_RJT* "); | ||
754 | ulStatus = cpqfcTSBuildExchange( | ||
755 | cpqfcHBAdata, | ||
756 | BLS_ABTS_RJT, | ||
757 | (TachFCHDR_GCMND*) | ||
758 | fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs | ||
759 | NULL, // no data (no scatter/gather list) | ||
760 | &ExchangeID );// fcController->fcExchanges index, -1 if failed | ||
761 | |||
762 | if( !ulStatus ) // Exchange setup OK? | ||
763 | { | ||
764 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); | ||
765 | // If it fails, we aren't required to retry. | ||
766 | } | ||
767 | if( ulStatus ) | ||
768 | { | ||
769 | printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); | ||
770 | } | ||
771 | else | ||
772 | { | ||
773 | printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); | ||
774 | |||
775 | } | ||
776 | |||
777 | break; | ||
778 | |||
779 | |||
780 | |||
781 | default: | ||
782 | break; | ||
783 | } // end switch | ||
784 | //doNothing: | ||
785 | // done with this item - now set the NEXT index | ||
786 | |||
787 | if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test | ||
788 | { | ||
789 | fcLQ->consumer = 0; | ||
790 | } | ||
791 | else | ||
792 | { | ||
793 | fcLQ->consumer++; | ||
794 | } | ||
795 | |||
796 | PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94) | ||
797 | |||
798 | LEAVE("WorkTask"); | ||
799 | return; | ||
800 | } | ||
801 | |||
802 | |||
803 | |||
804 | |||
805 | // When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login) | ||
806 | // commands come in, post to the LinkQ so that action can be taken outside the | ||
807 | // interrupt handler. | ||
808 | // This circular Q works like Tachyon's que - the producer points to the next | ||
809 | // (unused) entry. Called by Interrupt handler, WorkerThread, Timer | ||
810 | // sputlinkq | ||
811 | void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata, | ||
812 | int Type, | ||
813 | void *QueContent) | ||
814 | { | ||
815 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
816 | // FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
817 | PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; | ||
818 | ULONG ndx; | ||
819 | |||
820 | ENTER("cpqfcTSPutLinkQ"); | ||
821 | |||
822 | ndx = fcLQ->producer; | ||
823 | |||
824 | ndx += 1; // test for Que full | ||
825 | |||
826 | |||
827 | |||
828 | if( ndx >= FC_LINKQ_DEPTH ) // rollover test | ||
829 | ndx = 0; | ||
830 | |||
831 | if( ndx == fcLQ->consumer ) // QUE full test | ||
832 | { | ||
833 | // QUE was full! lost LK command (fatal to logic) | ||
834 | fcChip->fcStats.lnkQueFull++; | ||
835 | |||
836 | printk("*LinkQ Full!*"); | ||
837 | TriggerHBA( fcChip->Registers.ReMapMemBase, 1); | ||
838 | /* | ||
839 | { | ||
840 | int i; | ||
841 | printk("LinkQ PI %d, CI %d\n", fcLQ->producer, | ||
842 | fcLQ->consumer); | ||
843 | |||
844 | for( i=0; i< FC_LINKQ_DEPTH; ) | ||
845 | { | ||
846 | printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type); | ||
847 | if( (++i %8) == 0) printk("\n"); | ||
848 | } | ||
849 | |||
850 | } | ||
851 | */ | ||
852 | printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung | ||
853 | } | ||
854 | else // QUE next element | ||
855 | { | ||
856 | // Prevent certain multiple (back-to-back) requests. | ||
857 | // This is important in that we don't want to issue multiple | ||
858 | // ABTS for the same Exchange, or do multiple FM inits, etc. | ||
859 | // We can never be sure of the timing of events reported to | ||
860 | // us by Tach's IMQ, which can depend on system/bus speeds, | ||
861 | // FC physical link circumstances, etc. | ||
862 | |||
863 | if( (fcLQ->producer != fcLQ->consumer) | ||
864 | && | ||
865 | (Type == FMINIT) ) | ||
866 | { | ||
867 | LONG lastNdx; // compute previous producer index | ||
868 | if( fcLQ->producer) | ||
869 | lastNdx = fcLQ->producer- 1; | ||
870 | else | ||
871 | lastNdx = FC_LINKQ_DEPTH-1; | ||
872 | |||
873 | |||
874 | if( fcLQ->Qitem[lastNdx].Type == FMINIT) | ||
875 | { | ||
876 | // printk(" *skip FMINIT Q post* "); | ||
877 | // goto DoneWithPutQ; | ||
878 | } | ||
879 | |||
880 | } | ||
881 | |||
882 | // OK, add the Q'd item... | ||
883 | |||
884 | fcLQ->Qitem[fcLQ->producer].Type = Type; | ||
885 | |||
886 | memcpy( | ||
887 | fcLQ->Qitem[fcLQ->producer].ulBuff, | ||
888 | QueContent, | ||
889 | sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff)); | ||
890 | |||
891 | fcLQ->producer = ndx; // increment Que producer | ||
892 | |||
893 | // set semaphore to wake up Kernel (worker) thread | ||
894 | // | ||
895 | up( cpqfcHBAdata->fcQueReady ); | ||
896 | } | ||
897 | |||
898 | //DoneWithPutQ: | ||
899 | |||
900 | LEAVE("cpqfcTSPutLinkQ"); | ||
901 | } | ||
902 | |||
903 | |||
904 | |||
905 | |||
906 | // reset device ext FC link Q | ||
907 | void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata) | ||
908 | |||
909 | { | ||
910 | PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; | ||
911 | fcLQ->producer = 0; | ||
912 | fcLQ->consumer = 0; | ||
913 | |||
914 | } | ||
915 | |||
916 | |||
917 | |||
918 | |||
919 | |||
920 | // When Tachyon gets an unassisted FCP-SCSI frame, post here so | ||
921 | // an arbitrary context thread (e.g. IOCTL loopback test function) | ||
922 | // can process it. | ||
923 | |||
924 | // (NOTE: Not revised for Linux) | ||
925 | // This Q works like Tachyon's que - the producer points to the next | ||
926 | // (unused) entry. | ||
927 | void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata, | ||
928 | int Type, | ||
929 | void *QueContent) | ||
930 | { | ||
931 | // CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
932 | // PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
933 | |||
934 | // ULONG ndx; | ||
935 | |||
936 | // ULONG *pExchangeID; | ||
937 | // LONG ExchangeID; | ||
938 | |||
939 | /* | ||
940 | KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock); | ||
941 | ndx = pDevExt->fcScsiQue.producer + 1; // test for Que full | ||
942 | |||
943 | if( ndx >= FC_SCSIQ_DEPTH ) // rollover test | ||
944 | ndx = 0; | ||
945 | |||
946 | if( ndx == pDevExt->fcScsiQue.consumer ) // QUE full test | ||
947 | { | ||
948 | // QUE was full! lost LK command (fatal to logic) | ||
949 | fcChip->fcStats.ScsiQueFull++; | ||
950 | #ifdef DBG | ||
951 | printk( "fcPutScsiQue - FULL!\n"); | ||
952 | #endif | ||
953 | |||
954 | } | ||
955 | else // QUE next element | ||
956 | { | ||
957 | pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type; | ||
958 | |||
959 | if( Type == FCP_RSP ) | ||
960 | { | ||
961 | // this TL inbound message type means that a TL SEST exchange has | ||
962 | // copied an FCP response frame into a buffer pointed to by the SEST | ||
963 | // entry. That buffer is allocated in the SEST structure at ->RspHDR. | ||
964 | // Copy the RspHDR for use by the Que handler. | ||
965 | pExchangeID = (ULONG *)QueContent; | ||
966 | |||
967 | memcpy( | ||
968 | pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, | ||
969 | &fcChip->SEST->RspHDR[ *pExchangeID ], | ||
970 | sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size) | ||
971 | |||
972 | } | ||
973 | else | ||
974 | { | ||
975 | memcpy( | ||
976 | pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, | ||
977 | QueContent, | ||
978 | sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff)); | ||
979 | } | ||
980 | |||
981 | pDevExt->fcScsiQue.producer = ndx; // increment Que | ||
982 | |||
983 | |||
984 | KeSetEvent( &pDevExt->TYIBscsi, // signal any waiting thread | ||
985 | 0, // no priority boost | ||
986 | FALSE ); // no waiting later for this event | ||
987 | } | ||
988 | KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock); | ||
989 | */ | ||
990 | } | ||
991 | |||
992 | |||
993 | |||
994 | |||
995 | |||
996 | |||
997 | |||
998 | static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*); | ||
999 | |||
1000 | static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); | ||
1001 | |||
1002 | static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); | ||
1003 | |||
1004 | void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata, | ||
1005 | PFC_LOGGEDIN_PORT pFcPort) | ||
1006 | { | ||
1007 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1008 | |||
1009 | if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric | ||
1010 | { | ||
1011 | fcChip->fcStats.logouts++; | ||
1012 | printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", | ||
1013 | (ULONG)pFcPort->u.liWWN, | ||
1014 | (ULONG)(pFcPort->u.liWWN >>32), | ||
1015 | pFcPort->port_id); | ||
1016 | |||
1017 | // Terminate I/O with this (Linux) Scsi target | ||
1018 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1019 | &pFcPort->ScsiNexus, | ||
1020 | DEVICE_REMOVED); | ||
1021 | } | ||
1022 | |||
1023 | // Do an "implicit logout" - we can't really Logout the device | ||
1024 | // (i.e. with LOGOut Request) because of port_id confusion | ||
1025 | // (i.e. the Other port has no port_id). | ||
1026 | // A new login for that WWN will have to re-write port_id (0 invalid) | ||
1027 | pFcPort->port_id = 0; // invalid! | ||
1028 | pFcPort->pdisc = FALSE; | ||
1029 | pFcPort->prli = FALSE; | ||
1030 | pFcPort->plogi = FALSE; | ||
1031 | pFcPort->flogi = FALSE; | ||
1032 | pFcPort->LOGO_timer = 0; | ||
1033 | pFcPort->device_blocked = TRUE; // block Scsi Requests | ||
1034 | pFcPort->ScsiNexus.VolumeSetAddressing=0; | ||
1035 | } | ||
1036 | |||
1037 | |||
1038 | // On FC-AL, there is a chance that a previously known device can | ||
1039 | // be quietly removed (e.g. with non-managed hub), | ||
1040 | // while a NEW device (with different WWN) took the same alpa or | ||
1041 | // even 24-bit port_id. This chance is unlikely but we must always | ||
1042 | // check for it. | ||
1043 | static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata, | ||
1044 | PFC_LOGGEDIN_PORT pLoggedInPort) | ||
1045 | { | ||
1046 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1047 | // set "other port" at beginning of fcPorts list | ||
1048 | PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort; | ||
1049 | while( pOtherPortWithPortId ) | ||
1050 | { | ||
1051 | if( (pOtherPortWithPortId->port_id == | ||
1052 | pLoggedInPort->port_id) | ||
1053 | && | ||
1054 | (pOtherPortWithPortId != pLoggedInPort) ) | ||
1055 | { | ||
1056 | // trouble! (Implicitly) Log the other guy out | ||
1057 | printk(" *port_id %Xh is duplicated!* ", | ||
1058 | pOtherPortWithPortId->port_id); | ||
1059 | cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId); | ||
1060 | } | ||
1061 | pOtherPortWithPortId = pOtherPortWithPortId->pNextPort; | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | |||
1066 | |||
1067 | |||
1068 | |||
1069 | |||
1070 | // Dynamic Memory Allocation for newly discovered FC Ports. | ||
1071 | // For simplicity, maintain fcPorts structs for ALL | ||
1072 | // for discovered devices, including those we never do I/O with | ||
1073 | // (e.g. Fabric addresses) | ||
1074 | |||
1075 | static PFC_LOGGEDIN_PORT CreateFcPort( | ||
1076 | CPQFCHBA* cpqfcHBAdata, | ||
1077 | PFC_LOGGEDIN_PORT pLastLoggedInPort, | ||
1078 | TachFCHDR_GCMND* fchs, | ||
1079 | LOGIN_PAYLOAD* plogi) | ||
1080 | { | ||
1081 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1082 | PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL; | ||
1083 | int i; | ||
1084 | |||
1085 | |||
1086 | printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id); | ||
1087 | for( i=3; i>=0; i--) // copy the LOGIN port's WWN | ||
1088 | printk("%02X", plogi->port_name[i]); | ||
1089 | for( i=7; i>3; i--) // copy the LOGIN port's WWN | ||
1090 | printk("%02X", plogi->port_name[i]); | ||
1091 | |||
1092 | |||
1093 | // allocate mem for new port | ||
1094 | // (these are small and rare allocations...) | ||
1095 | pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC ); | ||
1096 | |||
1097 | |||
1098 | // allocation succeeded? Fill out NEW PORT | ||
1099 | if( pNextLoggedInPort ) | ||
1100 | { | ||
1101 | // clear out any garbage (sometimes exists) | ||
1102 | memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT)); | ||
1103 | |||
1104 | |||
1105 | // If we login to a Fabric, we don't want to treat it | ||
1106 | // as a SCSI device... | ||
1107 | if( (fchs->s_id & 0xFFF000) != 0xFFF000) | ||
1108 | { | ||
1109 | int i; | ||
1110 | |||
1111 | // create a unique "virtual" SCSI Nexus (for now, just a | ||
1112 | // new target ID) -- we will update channel/target on REPORT_LUNS | ||
1113 | // special case for very first SCSI target... | ||
1114 | if( cpqfcHBAdata->HostAdapter->max_id == 0) | ||
1115 | { | ||
1116 | pNextLoggedInPort->ScsiNexus.target = 0; | ||
1117 | fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub" | ||
1118 | } | ||
1119 | else | ||
1120 | { | ||
1121 | pNextLoggedInPort->ScsiNexus.target = | ||
1122 | cpqfcHBAdata->HostAdapter->max_id; | ||
1123 | } | ||
1124 | |||
1125 | // initialize the lun[] Nexus struct for lun masking | ||
1126 | for( i=0; i< CPQFCTS_MAX_LUN; i++) | ||
1127 | pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED | ||
1128 | |||
1129 | pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port | ||
1130 | |||
1131 | printk(" SCSI Chan/Trgt %d/%d", | ||
1132 | pNextLoggedInPort->ScsiNexus.channel, | ||
1133 | pNextLoggedInPort->ScsiNexus.target); | ||
1134 | |||
1135 | // tell Scsi layers about the new target... | ||
1136 | cpqfcHBAdata->HostAdapter->max_id++; | ||
1137 | // printk("HostAdapter->max_id = %d\n", | ||
1138 | // cpqfcHBAdata->HostAdapter->max_id); | ||
1139 | } | ||
1140 | else | ||
1141 | { | ||
1142 | // device is NOT SCSI (in case of Fabric) | ||
1143 | pNextLoggedInPort->ScsiNexus.target = -1; // invalid | ||
1144 | } | ||
1145 | |||
1146 | // create forward link to new port | ||
1147 | pLastLoggedInPort->pNextPort = pNextLoggedInPort; | ||
1148 | printk("\n"); | ||
1149 | |||
1150 | } | ||
1151 | return pNextLoggedInPort; // NULL on allocation failure | ||
1152 | } // end NEW PORT (WWN) logic | ||
1153 | |||
1154 | |||
1155 | |||
1156 | // For certain cases, we want to terminate exchanges without | ||
1157 | // sending ABTS to the device. Examples include when an FC | ||
1158 | // device changed it's port_id after Loop re-init, or when | ||
1159 | // the device sent us a logout. In the case of changed port_id, | ||
1160 | // we want to complete the command and return SOFT_ERROR to | ||
1161 | // force a re-try. In the case of LOGOut, we might return | ||
1162 | // BAD_TARGET if the device is really gone. | ||
1163 | // Since we must ensure that Tachyon is not operating on the | ||
1164 | // exchange, we have to freeze the chip | ||
1165 | // sterminateex | ||
1166 | void cpqfcTSTerminateExchange( | ||
1167 | CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus) | ||
1168 | { | ||
1169 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1170 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
1171 | ULONG x_ID; | ||
1172 | |||
1173 | if( ScsiNexus ) | ||
1174 | { | ||
1175 | // printk("TerminateExchange: ScsiNexus chan/target %d/%d\n", | ||
1176 | // ScsiNexus->channel, ScsiNexus->target); | ||
1177 | |||
1178 | } | ||
1179 | |||
1180 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | ||
1181 | { | ||
1182 | if( Exchanges->fcExchange[x_ID].type ) // in use? | ||
1183 | { | ||
1184 | if( ScsiNexus == NULL ) // our HBA changed - term. all | ||
1185 | { | ||
1186 | Exchanges->fcExchange[x_ID].status = TerminateStatus; | ||
1187 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); | ||
1188 | } | ||
1189 | else | ||
1190 | { | ||
1191 | // If a device, according to WWN, has been removed, it's | ||
1192 | // port_id may be used by another working device, so we | ||
1193 | // have to terminate by SCSI target, NOT port_id. | ||
1194 | if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress? | ||
1195 | { | ||
1196 | if( (Exchanges->fcExchange[x_ID].Cmnd->device->id == ScsiNexus->target) | ||
1197 | && | ||
1198 | (Exchanges->fcExchange[x_ID].Cmnd->device->channel == ScsiNexus->channel)) | ||
1199 | { | ||
1200 | Exchanges->fcExchange[x_ID].status = TerminateStatus; | ||
1201 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out | ||
1202 | } | ||
1203 | } | ||
1204 | |||
1205 | // (in case we ever need it...) | ||
1206 | // all SEST structures have a remote node ID at SEST DWORD 2 | ||
1207 | // if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8) | ||
1208 | // == port_id) | ||
1209 | } | ||
1210 | } | ||
1211 | } | ||
1212 | } | ||
1213 | |||
1214 | |||
1215 | static void ProcessELS_Request( | ||
1216 | CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) | ||
1217 | { | ||
1218 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1219 | // FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
1220 | // ULONG ox_id = (fchs->ox_rx_id >>16); | ||
1221 | PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort; | ||
1222 | BOOLEAN NeedReject = FALSE; | ||
1223 | ULONG ls_reject_code = 0; // default don'n know?? | ||
1224 | |||
1225 | |||
1226 | // Check the incoming frame for a supported ELS type | ||
1227 | switch( fchs->pl[0] & 0xFFFF) | ||
1228 | { | ||
1229 | case 0x0050: // PDISC? | ||
1230 | |||
1231 | // Payload for PLOGI and PDISC is identical (request & reply) | ||
1232 | if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload? | ||
1233 | { | ||
1234 | LOGIN_PAYLOAD logi; // FC-PH Port Login | ||
1235 | |||
1236 | // PDISC payload OK. If critical login fields | ||
1237 | // (e.g. WWN) matches last login for this port_id, | ||
1238 | // we may resume any prior exchanges | ||
1239 | // with the other port | ||
1240 | |||
1241 | |||
1242 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); | ||
1243 | |||
1244 | pLoggedInPort = fcFindLoggedInPort( | ||
1245 | fcChip, | ||
1246 | NULL, // don't search Scsi Nexus | ||
1247 | 0, // don't search linked list for port_id | ||
1248 | &logi.port_name[0], // search linked list for WWN | ||
1249 | &pLastLoggedInPort); // must return non-NULL; when a port_id | ||
1250 | // is not found, this pointer marks the | ||
1251 | // end of the singly linked list | ||
1252 | |||
1253 | if( pLoggedInPort != NULL) // WWN found (prior login OK) | ||
1254 | { | ||
1255 | |||
1256 | if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id) | ||
1257 | { | ||
1258 | // Yes. We were expecting PDISC? | ||
1259 | if( pLoggedInPort->pdisc ) | ||
1260 | { | ||
1261 | // Yes; set fields accordingly. (PDISC, not Originator) | ||
1262 | SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE); | ||
1263 | |||
1264 | // send 'ACC' reply | ||
1265 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1266 | ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC) | ||
1267 | fchs ); | ||
1268 | |||
1269 | // OK to resume I/O... | ||
1270 | } | ||
1271 | else | ||
1272 | { | ||
1273 | printk("Not expecting PDISC (pdisc=FALSE)\n"); | ||
1274 | NeedReject = TRUE; | ||
1275 | // set reject reason code | ||
1276 | ls_reject_code = | ||
1277 | LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); | ||
1278 | } | ||
1279 | } | ||
1280 | else | ||
1281 | { | ||
1282 | if( pLoggedInPort->port_id != 0) | ||
1283 | { | ||
1284 | printk("PDISC PortID change: old %Xh, new %Xh\n", | ||
1285 | pLoggedInPort->port_id, fchs->s_id &0xFFFFFF); | ||
1286 | } | ||
1287 | NeedReject = TRUE; | ||
1288 | // set reject reason code | ||
1289 | ls_reject_code = | ||
1290 | LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); | ||
1291 | |||
1292 | } | ||
1293 | } | ||
1294 | else | ||
1295 | { | ||
1296 | printk("PDISC Request from unknown WWN\n"); | ||
1297 | NeedReject = TRUE; | ||
1298 | |||
1299 | // set reject reason code | ||
1300 | ls_reject_code = | ||
1301 | LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME); | ||
1302 | } | ||
1303 | |||
1304 | } | ||
1305 | else // Payload unacceptable | ||
1306 | { | ||
1307 | printk("payload unacceptable\n"); | ||
1308 | NeedReject = TRUE; // reject code already set | ||
1309 | |||
1310 | } | ||
1311 | |||
1312 | if( NeedReject) | ||
1313 | { | ||
1314 | ULONG port_id; | ||
1315 | // The PDISC failed. Set login struct flags accordingly, | ||
1316 | // terminate any I/O to this port, and Q a PLOGI | ||
1317 | if( pLoggedInPort ) | ||
1318 | { | ||
1319 | pLoggedInPort->pdisc = FALSE; | ||
1320 | pLoggedInPort->prli = FALSE; | ||
1321 | pLoggedInPort->plogi = FALSE; | ||
1322 | |||
1323 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1324 | &pLoggedInPort->ScsiNexus, PORTID_CHANGED); | ||
1325 | port_id = pLoggedInPort->port_id; | ||
1326 | } | ||
1327 | else | ||
1328 | { | ||
1329 | port_id = fchs->s_id &0xFFFFFF; | ||
1330 | } | ||
1331 | fchs->reserved = ls_reject_code; // borrow this (unused) field | ||
1332 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs ); | ||
1333 | } | ||
1334 | |||
1335 | break; | ||
1336 | |||
1337 | |||
1338 | |||
1339 | case 0x0003: // PLOGI? | ||
1340 | |||
1341 | // Payload for PLOGI and PDISC is identical (request & reply) | ||
1342 | if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload? | ||
1343 | { | ||
1344 | LOGIN_PAYLOAD logi; // FC-PH Port Login | ||
1345 | BOOLEAN NeedReject = FALSE; | ||
1346 | |||
1347 | // PDISC payload OK. If critical login fields | ||
1348 | // (e.g. WWN) matches last login for this port_id, | ||
1349 | // we may resume any prior exchanges | ||
1350 | // with the other port | ||
1351 | |||
1352 | |||
1353 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); | ||
1354 | |||
1355 | pLoggedInPort = fcFindLoggedInPort( | ||
1356 | fcChip, | ||
1357 | NULL, // don't search Scsi Nexus | ||
1358 | 0, // don't search linked list for port_id | ||
1359 | &logi.port_name[0], // search linked list for WWN | ||
1360 | &pLastLoggedInPort); // must return non-NULL; when a port_id | ||
1361 | // is not found, this pointer marks the | ||
1362 | // end of the singly linked list | ||
1363 | |||
1364 | if( pLoggedInPort == NULL) // WWN not found -New Port | ||
1365 | { | ||
1366 | pLoggedInPort = CreateFcPort( | ||
1367 | cpqfcHBAdata, | ||
1368 | pLastLoggedInPort, | ||
1369 | fchs, | ||
1370 | &logi); | ||
1371 | if( pLoggedInPort == NULL ) | ||
1372 | { | ||
1373 | printk(" cpqfcTS: New port allocation failed - lost FC device!\n"); | ||
1374 | // Now Q a LOGOut Request, since we won't be talking to that device | ||
1375 | |||
1376 | NeedReject = TRUE; | ||
1377 | |||
1378 | // set reject reason code | ||
1379 | ls_reject_code = | ||
1380 | LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES); | ||
1381 | |||
1382 | } | ||
1383 | } | ||
1384 | if( !NeedReject ) | ||
1385 | { | ||
1386 | |||
1387 | // OK - we have valid fcPort ptr; set fields accordingly. | ||
1388 | // (not PDISC, not Originator) | ||
1389 | SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); | ||
1390 | |||
1391 | // send 'ACC' reply | ||
1392 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1393 | ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC) | ||
1394 | fchs ); | ||
1395 | } | ||
1396 | } | ||
1397 | else // Payload unacceptable | ||
1398 | { | ||
1399 | printk("payload unacceptable\n"); | ||
1400 | NeedReject = TRUE; // reject code already set | ||
1401 | } | ||
1402 | |||
1403 | if( NeedReject) | ||
1404 | { | ||
1405 | // The PDISC failed. Set login struct flags accordingly, | ||
1406 | // terminate any I/O to this port, and Q a PLOGI | ||
1407 | pLoggedInPort->pdisc = FALSE; | ||
1408 | pLoggedInPort->prli = FALSE; | ||
1409 | pLoggedInPort->plogi = FALSE; | ||
1410 | |||
1411 | fchs->reserved = ls_reject_code; // borrow this (unused) field | ||
1412 | |||
1413 | // send 'RJT' reply | ||
1414 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs ); | ||
1415 | } | ||
1416 | |||
1417 | // terminate any exchanges with this device... | ||
1418 | if( pLoggedInPort ) | ||
1419 | { | ||
1420 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1421 | &pLoggedInPort->ScsiNexus, PORTID_CHANGED); | ||
1422 | } | ||
1423 | break; | ||
1424 | |||
1425 | |||
1426 | |||
1427 | case 0x1020: // PRLI? | ||
1428 | { | ||
1429 | BOOLEAN NeedReject = TRUE; | ||
1430 | pLoggedInPort = fcFindLoggedInPort( | ||
1431 | fcChip, | ||
1432 | NULL, // don't search Scsi Nexus | ||
1433 | (fchs->s_id & 0xFFFFFF), // search linked list for port_id | ||
1434 | NULL, // DON'T search linked list for WWN | ||
1435 | NULL); // don't care | ||
1436 | |||
1437 | if( pLoggedInPort == NULL ) | ||
1438 | { | ||
1439 | // huh? | ||
1440 | printk(" Unexpected PRLI Request -not logged in!\n"); | ||
1441 | |||
1442 | // set reject reason code | ||
1443 | ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); | ||
1444 | |||
1445 | // Q a LOGOut here? | ||
1446 | } | ||
1447 | else | ||
1448 | { | ||
1449 | // verify the PRLI ACC payload | ||
1450 | if( !verify_PRLI( fchs, &ls_reject_code) ) | ||
1451 | { | ||
1452 | // PRLI Reply is acceptable; were we expecting it? | ||
1453 | if( pLoggedInPort->plogi ) | ||
1454 | { | ||
1455 | // yes, we expected the PRLI ACC (not PDISC; not Originator) | ||
1456 | SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); | ||
1457 | |||
1458 | // Q an ACCept Reply | ||
1459 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1460 | ELS_PRLI_ACC, | ||
1461 | fchs ); | ||
1462 | |||
1463 | NeedReject = FALSE; | ||
1464 | } | ||
1465 | else | ||
1466 | { | ||
1467 | // huh? | ||
1468 | printk(" (unexpected) PRLI REQEST with plogi FALSE\n"); | ||
1469 | |||
1470 | // set reject reason code | ||
1471 | ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR); | ||
1472 | |||
1473 | // Q a LOGOut here? | ||
1474 | |||
1475 | } | ||
1476 | } | ||
1477 | else | ||
1478 | { | ||
1479 | printk(" PRLI REQUEST payload failed verify\n"); | ||
1480 | // (reject code set by "verify") | ||
1481 | |||
1482 | // Q a LOGOut here? | ||
1483 | } | ||
1484 | } | ||
1485 | |||
1486 | if( NeedReject ) | ||
1487 | { | ||
1488 | // Q a ReJecT Reply with reason code | ||
1489 | fchs->reserved = ls_reject_code; | ||
1490 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1491 | ELS_RJT, // Q Type | ||
1492 | fchs ); | ||
1493 | } | ||
1494 | } | ||
1495 | break; | ||
1496 | |||
1497 | |||
1498 | |||
1499 | |||
1500 | case 0x0005: // LOGOut? | ||
1501 | { | ||
1502 | // was this LOGOUT because we sent a ELS_PDISC to an FC device | ||
1503 | // with changed (or new) port_id, or does the port refuse | ||
1504 | // to communicate to us? | ||
1505 | // We maintain a logout counter - if we get 3 consecutive LOGOuts, | ||
1506 | // give up! | ||
1507 | LOGOUT_PAYLOAD logo; | ||
1508 | BOOLEAN GiveUpOnDevice = FALSE; | ||
1509 | ULONG ls_reject_code = 0; | ||
1510 | |||
1511 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo)); | ||
1512 | |||
1513 | pLoggedInPort = fcFindLoggedInPort( | ||
1514 | fcChip, | ||
1515 | NULL, // don't search Scsi Nexus | ||
1516 | 0, // don't search linked list for port_id | ||
1517 | &logo.port_name[0], // search linked list for WWN | ||
1518 | NULL); // don't care about end of list | ||
1519 | |||
1520 | if( pLoggedInPort ) // found the device? | ||
1521 | { | ||
1522 | // Q an ACC reply | ||
1523 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1524 | ELS_LOGO_ACC, // Q Type | ||
1525 | fchs ); // device to respond to | ||
1526 | |||
1527 | // set login struct fields (LOGO_counter increment) | ||
1528 | SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); | ||
1529 | |||
1530 | // are we an Initiator? | ||
1531 | if( fcChip->Options.initiator) | ||
1532 | { | ||
1533 | // we're an Initiator, so check if we should | ||
1534 | // try (another?) login | ||
1535 | |||
1536 | // Fabrics routinely log out from us after | ||
1537 | // getting device info - don't try to log them | ||
1538 | // back in. | ||
1539 | if( (fchs->s_id & 0xFFF000) == 0xFFF000 ) | ||
1540 | { | ||
1541 | ; // do nothing | ||
1542 | } | ||
1543 | else if( pLoggedInPort->LOGO_counter <= 3) | ||
1544 | { | ||
1545 | // try (another) login (PLOGI request) | ||
1546 | |||
1547 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1548 | ELS_PLOGI, // Q Type | ||
1549 | fchs ); | ||
1550 | |||
1551 | // Terminate I/O with "retry" potential | ||
1552 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1553 | &pLoggedInPort->ScsiNexus, | ||
1554 | PORTID_CHANGED); | ||
1555 | } | ||
1556 | else | ||
1557 | { | ||
1558 | printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n", | ||
1559 | fchs->s_id &&0xFFFFFF); | ||
1560 | GiveUpOnDevice = TRUE; | ||
1561 | } | ||
1562 | } | ||
1563 | else | ||
1564 | { | ||
1565 | GiveUpOnDevice = TRUE; | ||
1566 | } | ||
1567 | |||
1568 | |||
1569 | if( GiveUpOnDevice == TRUE ) | ||
1570 | { | ||
1571 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1572 | &pLoggedInPort->ScsiNexus, | ||
1573 | DEVICE_REMOVED); | ||
1574 | } | ||
1575 | } | ||
1576 | else // we don't know this WWN! | ||
1577 | { | ||
1578 | // Q a ReJecT Reply with reason code | ||
1579 | fchs->reserved = ls_reject_code; | ||
1580 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1581 | ELS_RJT, // Q Type | ||
1582 | fchs ); | ||
1583 | } | ||
1584 | } | ||
1585 | break; | ||
1586 | |||
1587 | |||
1588 | |||
1589 | |||
1590 | // FABRIC only case | ||
1591 | case 0x0461: // ELS RSCN (Registered State Change Notification)? | ||
1592 | { | ||
1593 | int Ports; | ||
1594 | int i; | ||
1595 | __u32 Buff; | ||
1596 | // Typically, one or more devices have been added to or dropped | ||
1597 | // from the Fabric. | ||
1598 | // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997) | ||
1599 | // The first 32-bit word has a 2-byte Payload Length, which | ||
1600 | // includes the 4 bytes of the first word. Consequently, | ||
1601 | // this PL len must never be less than 4, must be a multiple of 4, | ||
1602 | // and has a specified max value 256. | ||
1603 | // (Endianess!) | ||
1604 | Ports = ((fchs->pl[0] >>24) - 4) / 4; | ||
1605 | Ports = Ports > 63 ? 63 : Ports; | ||
1606 | |||
1607 | printk(" RSCN ports: %d\n", Ports); | ||
1608 | if( Ports <= 0 ) // huh? | ||
1609 | { | ||
1610 | // ReJecT the command | ||
1611 | fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0); | ||
1612 | |||
1613 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1614 | ELS_RJT, // Q Type | ||
1615 | fchs ); | ||
1616 | |||
1617 | break; | ||
1618 | } | ||
1619 | else // Accept the command | ||
1620 | { | ||
1621 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1622 | ELS_ACC, // Q Type | ||
1623 | fchs ); | ||
1624 | } | ||
1625 | |||
1626 | // Check the "address format" to determine action. | ||
1627 | // We have 3 cases: | ||
1628 | // 0 = Port Address; 24-bit address of affected device | ||
1629 | // 1 = Area Address; MS 16 bits valid | ||
1630 | // 2 = Domain Address; MS 8 bits valid | ||
1631 | for( i=0; i<Ports; i++) | ||
1632 | { | ||
1633 | BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4); | ||
1634 | switch( Buff & 0xFF000000) | ||
1635 | { | ||
1636 | |||
1637 | case 0: // Port Address? | ||
1638 | |||
1639 | case 0x01000000: // Area Domain? | ||
1640 | case 0x02000000: // Domain Address | ||
1641 | // For example, "port_id" 0x201300 | ||
1642 | // OK, let's try a Name Service Request (Query) | ||
1643 | fchs->s_id = 0xFFFFFC; // Name Server Address | ||
1644 | cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs); | ||
1645 | |||
1646 | break; | ||
1647 | |||
1648 | |||
1649 | default: // huh? new value on version change? | ||
1650 | break; | ||
1651 | } | ||
1652 | } | ||
1653 | } | ||
1654 | break; | ||
1655 | |||
1656 | |||
1657 | |||
1658 | |||
1659 | default: // don't support this request (yet) | ||
1660 | // set reject reason code | ||
1661 | fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, | ||
1662 | REQUEST_NOT_SUPPORTED); | ||
1663 | |||
1664 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1665 | ELS_RJT, // Q Type | ||
1666 | fchs ); | ||
1667 | break; | ||
1668 | } | ||
1669 | } | ||
1670 | |||
1671 | |||
1672 | static void ProcessELS_Reply( | ||
1673 | CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) | ||
1674 | { | ||
1675 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
1676 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
1677 | ULONG ox_id = (fchs->ox_rx_id >>16); | ||
1678 | ULONG ls_reject_code; | ||
1679 | PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort; | ||
1680 | |||
1681 | // If this is a valid reply, then we MUST have sent a request. | ||
1682 | // Verify that we can find a valid request OX_ID corresponding to | ||
1683 | // this reply | ||
1684 | |||
1685 | |||
1686 | if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0) | ||
1687 | { | ||
1688 | printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ", | ||
1689 | ox_id, fchs->ox_rx_id & 0xffff); | ||
1690 | goto Quit; // exit this routine | ||
1691 | } | ||
1692 | |||
1693 | |||
1694 | // Is the reply a RJT (reject)? | ||
1695 | if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply? | ||
1696 | { | ||
1697 | // ****** REJECT REPLY ******** | ||
1698 | switch( Exchanges->fcExchange[ox_id].type ) | ||
1699 | { | ||
1700 | |||
1701 | case ELS_FDISC: // we sent out Fabric Discovery | ||
1702 | case ELS_FLOGI: // we sent out FLOGI | ||
1703 | |||
1704 | printk("RJT received on Fabric Login from %Xh, reason %Xh\n", | ||
1705 | fchs->s_id, fchs->pl[1]); | ||
1706 | |||
1707 | break; | ||
1708 | |||
1709 | default: | ||
1710 | break; | ||
1711 | } | ||
1712 | |||
1713 | goto Done; | ||
1714 | } | ||
1715 | |||
1716 | // OK, we have an ACCept... | ||
1717 | // What's the ACC type? (according to what we sent) | ||
1718 | switch( Exchanges->fcExchange[ox_id].type ) | ||
1719 | { | ||
1720 | |||
1721 | case ELS_PLOGI: // we sent out PLOGI | ||
1722 | if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) | ||
1723 | { | ||
1724 | LOGIN_PAYLOAD logi; // FC-PH Port Login | ||
1725 | |||
1726 | // login ACC payload acceptable; search for WWN in our list | ||
1727 | // of fcPorts | ||
1728 | |||
1729 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); | ||
1730 | |||
1731 | pLoggedInPort = fcFindLoggedInPort( | ||
1732 | fcChip, | ||
1733 | NULL, // don't search Scsi Nexus | ||
1734 | 0, // don't search linked list for port_id | ||
1735 | &logi.port_name[0], // search linked list for WWN | ||
1736 | &pLastLoggedInPort); // must return non-NULL; when a port_id | ||
1737 | // is not found, this pointer marks the | ||
1738 | // end of the singly linked list | ||
1739 | |||
1740 | if( pLoggedInPort == NULL) // WWN not found - new port | ||
1741 | { | ||
1742 | |||
1743 | pLoggedInPort = CreateFcPort( | ||
1744 | cpqfcHBAdata, | ||
1745 | pLastLoggedInPort, | ||
1746 | fchs, | ||
1747 | &logi); | ||
1748 | |||
1749 | if( pLoggedInPort == NULL ) | ||
1750 | { | ||
1751 | printk(" cpqfcTS: New port allocation failed - lost FC device!\n"); | ||
1752 | // Now Q a LOGOut Request, since we won't be talking to that device | ||
1753 | |||
1754 | goto Done; // exit with error! dropped login frame | ||
1755 | } | ||
1756 | } | ||
1757 | else // WWN was already known. Ensure that any open | ||
1758 | // exchanges for this WWN are terminated. | ||
1759 | // NOTE: It's possible that a device can change its | ||
1760 | // 24-bit port_id after a Link init or Fabric change | ||
1761 | // (e.g. LIP or Fabric RSCN). In that case, the old | ||
1762 | // 24-bit port_id may be duplicated, or no longer exist. | ||
1763 | { | ||
1764 | |||
1765 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1766 | &pLoggedInPort->ScsiNexus, PORTID_CHANGED); | ||
1767 | } | ||
1768 | |||
1769 | // We have an fcPort struct - set fields accordingly | ||
1770 | // not PDISC, originator | ||
1771 | SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE); | ||
1772 | |||
1773 | // We just set a "port_id"; is it duplicated? | ||
1774 | TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort); | ||
1775 | |||
1776 | // For Fabric operation, we issued PLOGI to 0xFFFFFC | ||
1777 | // so we can send SCR (State Change Registration) | ||
1778 | // Check for this special case... | ||
1779 | if( fchs->s_id == 0xFFFFFC ) | ||
1780 | { | ||
1781 | // PLOGI ACC was a Fabric response... issue SCR | ||
1782 | fchs->s_id = 0xFFFFFD; // address for SCR | ||
1783 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs); | ||
1784 | } | ||
1785 | |||
1786 | else | ||
1787 | { | ||
1788 | // Now we need a PRLI to enable FCP-SCSI operation | ||
1789 | // set flags and Q up a ELS_PRLI | ||
1790 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs); | ||
1791 | } | ||
1792 | } | ||
1793 | else | ||
1794 | { | ||
1795 | // login payload unacceptable - reason in ls_reject_code | ||
1796 | // Q up a Logout Request | ||
1797 | printk("Login Payload unacceptable\n"); | ||
1798 | |||
1799 | } | ||
1800 | break; | ||
1801 | |||
1802 | |||
1803 | // PDISC logic very similar to PLOGI, except we never want | ||
1804 | // to allocate mem for "new" port, and we set flags differently | ||
1805 | // (might combine later with PLOGI logic for efficiency) | ||
1806 | case ELS_PDISC: // we sent out PDISC | ||
1807 | if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) | ||
1808 | { | ||
1809 | LOGIN_PAYLOAD logi; // FC-PH Port Login | ||
1810 | BOOLEAN NeedLogin = FALSE; | ||
1811 | |||
1812 | // login payload acceptable; search for WWN in our list | ||
1813 | // of (previously seen) fcPorts | ||
1814 | |||
1815 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); | ||
1816 | |||
1817 | pLoggedInPort = fcFindLoggedInPort( | ||
1818 | fcChip, | ||
1819 | NULL, // don't search Scsi Nexus | ||
1820 | 0, // don't search linked list for port_id | ||
1821 | &logi.port_name[0], // search linked list for WWN | ||
1822 | &pLastLoggedInPort); // must return non-NULL; when a port_id | ||
1823 | // is not found, this pointer marks the | ||
1824 | // end of the singly linked list | ||
1825 | |||
1826 | if( pLoggedInPort != NULL) // WWN found? | ||
1827 | { | ||
1828 | // WWN has same port_id as last login? (Of course, a properly | ||
1829 | // working FC device should NEVER ACCept a PDISC if it's | ||
1830 | // port_id changed, but check just in case...) | ||
1831 | if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id) | ||
1832 | { | ||
1833 | // Yes. We were expecting PDISC? | ||
1834 | if( pLoggedInPort->pdisc ) | ||
1835 | { | ||
1836 | int i; | ||
1837 | |||
1838 | |||
1839 | // PDISC expected -- set fields. (PDISC, Originator) | ||
1840 | SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE); | ||
1841 | |||
1842 | // We are ready to resume FCP-SCSI to this device... | ||
1843 | // Do we need to start anything that was Queued? | ||
1844 | |||
1845 | for( i=0; i< TACH_SEST_LEN; i++) | ||
1846 | { | ||
1847 | // see if any exchange for this PDISC'd port was queued | ||
1848 | if( ((fchs->s_id &0xFFFFFF) == | ||
1849 | (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF)) | ||
1850 | && | ||
1851 | (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED)) | ||
1852 | { | ||
1853 | fchs->reserved = i; // copy ExchangeID | ||
1854 | // printk(" *Q x_ID %Xh after PDISC* ",i); | ||
1855 | |||
1856 | cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs ); | ||
1857 | } | ||
1858 | } | ||
1859 | |||
1860 | // Complete commands Q'd while we were waiting for Login | ||
1861 | |||
1862 | UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort); | ||
1863 | } | ||
1864 | else | ||
1865 | { | ||
1866 | printk("Not expecting PDISC (pdisc=FALSE)\n"); | ||
1867 | NeedLogin = TRUE; | ||
1868 | } | ||
1869 | } | ||
1870 | else | ||
1871 | { | ||
1872 | printk("PDISC PortID change: old %Xh, new %Xh\n", | ||
1873 | pLoggedInPort->port_id, fchs->s_id &0xFFFFFF); | ||
1874 | NeedLogin = TRUE; | ||
1875 | |||
1876 | } | ||
1877 | } | ||
1878 | else | ||
1879 | { | ||
1880 | printk("PDISC ACC from unknown WWN\n"); | ||
1881 | NeedLogin = TRUE; | ||
1882 | } | ||
1883 | |||
1884 | if( NeedLogin) | ||
1885 | { | ||
1886 | |||
1887 | // The PDISC failed. Set login struct flags accordingly, | ||
1888 | // terminate any I/O to this port, and Q a PLOGI | ||
1889 | if( pLoggedInPort ) // FC device previously known? | ||
1890 | { | ||
1891 | |||
1892 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
1893 | ELS_LOGO, // Q Type | ||
1894 | fchs ); // has port_id to send to | ||
1895 | |||
1896 | // There are a variety of error scenarios which can result | ||
1897 | // in PDISC failure, so as a catchall, add the check for | ||
1898 | // duplicate port_id. | ||
1899 | TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort); | ||
1900 | |||
1901 | // TriggerHBA( fcChip->Registers.ReMapMemBase, 0); | ||
1902 | pLoggedInPort->pdisc = FALSE; | ||
1903 | pLoggedInPort->prli = FALSE; | ||
1904 | pLoggedInPort->plogi = FALSE; | ||
1905 | |||
1906 | cpqfcTSTerminateExchange( cpqfcHBAdata, | ||
1907 | &pLoggedInPort->ScsiNexus, PORTID_CHANGED); | ||
1908 | } | ||
1909 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs ); | ||
1910 | } | ||
1911 | } | ||
1912 | else | ||
1913 | { | ||
1914 | // login payload unacceptable - reason in ls_reject_code | ||
1915 | // Q up a Logout Request | ||
1916 | printk("ERROR: Login Payload unacceptable!\n"); | ||
1917 | |||
1918 | } | ||
1919 | |||
1920 | break; | ||
1921 | |||
1922 | |||
1923 | |||
1924 | case ELS_PRLI: // we sent out PRLI | ||
1925 | |||
1926 | |||
1927 | pLoggedInPort = fcFindLoggedInPort( | ||
1928 | fcChip, | ||
1929 | NULL, // don't search Scsi Nexus | ||
1930 | (fchs->s_id & 0xFFFFFF), // search linked list for port_id | ||
1931 | NULL, // DON'T search linked list for WWN | ||
1932 | NULL); // don't care | ||
1933 | |||
1934 | if( pLoggedInPort == NULL ) | ||
1935 | { | ||
1936 | // huh? | ||
1937 | printk(" Unexpected PRLI ACCept frame!\n"); | ||
1938 | |||
1939 | // Q a LOGOut here? | ||
1940 | |||
1941 | goto Done; | ||
1942 | } | ||
1943 | |||
1944 | // verify the PRLI ACC payload | ||
1945 | if( !verify_PRLI( fchs, &ls_reject_code) ) | ||
1946 | { | ||
1947 | // PRLI Reply is acceptable; were we expecting it? | ||
1948 | if( pLoggedInPort->plogi ) | ||
1949 | { | ||
1950 | // yes, we expected the PRLI ACC (not PDISC; Originator) | ||
1951 | SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE); | ||
1952 | |||
1953 | // OK, let's send a REPORT_LUNS command to determine | ||
1954 | // whether VSA or PDA FCP-LUN addressing is used. | ||
1955 | |||
1956 | cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs ); | ||
1957 | |||
1958 | // It's possible that a device we were talking to changed | ||
1959 | // port_id, and has logged back in. This function ensures | ||
1960 | // that I/O will resume. | ||
1961 | UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort); | ||
1962 | |||
1963 | } | ||
1964 | else | ||
1965 | { | ||
1966 | // huh? | ||
1967 | printk(" (unexpected) PRLI ACCept with plogi FALSE\n"); | ||
1968 | |||
1969 | // Q a LOGOut here? | ||
1970 | goto Done; | ||
1971 | } | ||
1972 | } | ||
1973 | else | ||
1974 | { | ||
1975 | printk(" PRLI ACCept payload failed verify\n"); | ||
1976 | |||
1977 | // Q a LOGOut here? | ||
1978 | } | ||
1979 | |||
1980 | break; | ||
1981 | |||
1982 | case ELS_FLOGI: // we sent out FLOGI (Fabric Login) | ||
1983 | |||
1984 | // update the upper 16 bits of our port_id in Tachyon | ||
1985 | // the switch adds those upper 16 bits when responding | ||
1986 | // to us (i.e. we are the destination_id) | ||
1987 | fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF); | ||
1988 | writel( fcChip->Registers.my_al_pa, | ||
1989 | fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID); | ||
1990 | |||
1991 | // now send out a PLOGI to the well known port_id 0xFFFFFC | ||
1992 | fchs->s_id = 0xFFFFFC; | ||
1993 | cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs); | ||
1994 | |||
1995 | break; | ||
1996 | |||
1997 | |||
1998 | case ELS_FDISC: // we sent out FDISC (Fabric Discovery (Login)) | ||
1999 | |||
2000 | printk( " ELS_FDISC success "); | ||
2001 | break; | ||
2002 | |||
2003 | |||
2004 | case ELS_SCR: // we sent out State Change Registration | ||
2005 | // now we can issue Name Service Request to find any | ||
2006 | // Fabric-connected devices we might want to login to. | ||
2007 | |||
2008 | |||
2009 | fchs->s_id = 0xFFFFFC; // Name Server Address | ||
2010 | cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs); | ||
2011 | |||
2012 | |||
2013 | break; | ||
2014 | |||
2015 | |||
2016 | default: | ||
2017 | printk(" *Discarding unknown ACC frame, xID %04X/%04X* ", | ||
2018 | ox_id, fchs->ox_rx_id & 0xffff); | ||
2019 | break; | ||
2020 | } | ||
2021 | |||
2022 | |||
2023 | Done: | ||
2024 | // Regardless of whether the Reply is valid or not, the | ||
2025 | // the exchange is done - complete | ||
2026 | cpqfcTSCompleteExchange(cpqfcHBAdata->PciDev, fcChip, (fchs->ox_rx_id >>16)); | ||
2027 | |||
2028 | Quit: | ||
2029 | return; | ||
2030 | } | ||
2031 | |||
2032 | |||
2033 | |||
2034 | |||
2035 | |||
2036 | |||
2037 | // **************** Fibre Channel Services ************** | ||
2038 | // This is where we process the Directory (Name) Service Reply | ||
2039 | // to know which devices are on the Fabric | ||
2040 | |||
2041 | static void ProcessFCS_Reply( | ||
2042 | CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs) | ||
2043 | { | ||
2044 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
2045 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
2046 | ULONG ox_id = (fchs->ox_rx_id >>16); | ||
2047 | // ULONG ls_reject_code; | ||
2048 | // PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort; | ||
2049 | |||
2050 | // If this is a valid reply, then we MUST have sent a request. | ||
2051 | // Verify that we can find a valid request OX_ID corresponding to | ||
2052 | // this reply | ||
2053 | |||
2054 | if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0) | ||
2055 | { | ||
2056 | printk(" *Discarding Reply frame, xID %04X/%04X* ", | ||
2057 | ox_id, fchs->ox_rx_id & 0xffff); | ||
2058 | goto Quit; // exit this routine | ||
2059 | } | ||
2060 | |||
2061 | |||
2062 | // OK, we were expecting it. Now check to see if it's a | ||
2063 | // "Name Service" Reply, and if so force a re-validation of | ||
2064 | // Fabric device logins (i.e. Start the login timeout and | ||
2065 | // send PDISC or PLOGI) | ||
2066 | // (Endianess Byte Swap?) | ||
2067 | if( fchs->pl[1] == 0x02FC ) // Name Service | ||
2068 | { | ||
2069 | // got a new (or NULL) list of Fabric attach devices... | ||
2070 | // Invalidate current logins | ||
2071 | |||
2072 | PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; | ||
2073 | while( pLoggedInPort ) // for all ports which are expecting | ||
2074 | // PDISC after the next LIP, set the | ||
2075 | // logoutTimer | ||
2076 | { | ||
2077 | |||
2078 | if( (pLoggedInPort->port_id & 0xFFFF00) // Fabric device? | ||
2079 | && | ||
2080 | (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port | ||
2081 | { | ||
2082 | pLoggedInPort->LOGO_timer = 6; // what's the Fabric timeout?? | ||
2083 | // suspend any I/O in progress until | ||
2084 | // PDISC received... | ||
2085 | pLoggedInPort->prli = FALSE; // block FCP-SCSI commands | ||
2086 | } | ||
2087 | |||
2088 | pLoggedInPort = pLoggedInPort->pNextPort; | ||
2089 | } | ||
2090 | |||
2091 | if( fchs->pl[2] == 0x0280) // ACCept? | ||
2092 | { | ||
2093 | // Send PLOGI or PDISC to these Fabric devices | ||
2094 | SendLogins( cpqfcHBAdata, &fchs->pl[4] ); | ||
2095 | } | ||
2096 | |||
2097 | |||
2098 | // As of this writing, the only reason to reject is because NO | ||
2099 | // devices are left on the Fabric. We already started | ||
2100 | // "logged out" timers; if the device(s) don't come | ||
2101 | // back, we'll do the implicit logout in the heart beat | ||
2102 | // timer routine | ||
2103 | else // ReJecT | ||
2104 | { | ||
2105 | // this just means no Fabric device is visible at this instant | ||
2106 | } | ||
2107 | } | ||
2108 | |||
2109 | // Regardless of whether the Reply is valid or not, the | ||
2110 | // the exchange is done - complete | ||
2111 | cpqfcTSCompleteExchange(cpqfcHBAdata->PciDev, fcChip, (fchs->ox_rx_id >>16)); | ||
2112 | |||
2113 | Quit: | ||
2114 | return; | ||
2115 | } | ||
2116 | |||
2117 | |||
2118 | |||
2119 | |||
2120 | |||
2121 | |||
2122 | |||
2123 | static void AnalyzeIncomingFrame( | ||
2124 | CPQFCHBA *cpqfcHBAdata, | ||
2125 | ULONG QNdx ) | ||
2126 | { | ||
2127 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
2128 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
2129 | PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; | ||
2130 | TachFCHDR_GCMND* fchs = | ||
2131 | (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff; | ||
2132 | // ULONG ls_reject_code; // reason for rejecting login | ||
2133 | LONG ExchangeID; | ||
2134 | // FC_LOGGEDIN_PORT *pLoggedInPort; | ||
2135 | BOOLEAN AbortAccept; | ||
2136 | |||
2137 | ENTER("AnalyzeIncomingFrame"); | ||
2138 | |||
2139 | |||
2140 | |||
2141 | switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown | ||
2142 | { | ||
2143 | |||
2144 | case SFQ_UNKNOWN: // unknown frame (e.g. LIP position frame, NOP, etc.) | ||
2145 | |||
2146 | |||
2147 | // ********* FC-4 Device Data/ Fibre Channel Service ************* | ||
2148 | if( ((fchs->d_id &0xF0000000) == 0) // R_CTL (upper nibble) 0x0? | ||
2149 | && | ||
2150 | (fchs->f_ctl & 0x20000000) ) // TYPE 20h is Fibre Channel Service | ||
2151 | { | ||
2152 | |||
2153 | // ************** FCS Reply ********************** | ||
2154 | |||
2155 | if( (fchs->d_id & 0xff000000L) == 0x03000000L) // (31:23 R_CTL) | ||
2156 | { | ||
2157 | ProcessFCS_Reply( cpqfcHBAdata, fchs ); | ||
2158 | |||
2159 | } // end of FCS logic | ||
2160 | |||
2161 | } | ||
2162 | |||
2163 | |||
2164 | // *********** Extended Link Service ************** | ||
2165 | |||
2166 | else if( fchs->d_id & 0x20000000 // R_CTL 0x2? | ||
2167 | && | ||
2168 | (fchs->f_ctl & 0x01000000) ) // TYPE = 1 | ||
2169 | { | ||
2170 | |||
2171 | // these frames are either a response to | ||
2172 | // something we sent (0x23) or "unsolicited" | ||
2173 | // frames (0x22). | ||
2174 | |||
2175 | |||
2176 | // **************Extended Link REPLY ********************** | ||
2177 | // R_CTL Solicited Control Reply | ||
2178 | |||
2179 | if( (fchs->d_id & 0xff000000L) == 0x23000000L) // (31:23 R_CTL) | ||
2180 | { | ||
2181 | |||
2182 | ProcessELS_Reply( cpqfcHBAdata, fchs ); | ||
2183 | |||
2184 | } // end of "R_CTL Solicited Control Reply" | ||
2185 | |||
2186 | |||
2187 | |||
2188 | |||
2189 | // **************Extended Link REQUEST ********************** | ||
2190 | // (unsolicited commands from another port or task...) | ||
2191 | |||
2192 | // R_CTL Ext Link REQUEST | ||
2193 | else if( (fchs->d_id & 0xff000000L) == 0x22000000L && | ||
2194 | (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame) | ||
2195 | { | ||
2196 | |||
2197 | |||
2198 | |||
2199 | ProcessELS_Request( cpqfcHBAdata, fchs ); | ||
2200 | |||
2201 | } | ||
2202 | |||
2203 | |||
2204 | |||
2205 | // ************** LILP ********************** | ||
2206 | else if( (fchs->d_id & 0xff000000L) == 0x22000000L && | ||
2207 | (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames) | ||
2208 | |||
2209 | { | ||
2210 | // SANMark specifies that when available, we must use | ||
2211 | // the LILP frame to determine which ALPAs to send Port Discovery | ||
2212 | // to... | ||
2213 | |||
2214 | if( fchs->pl[0] == 0x0711L) // ELS_PLOGI? | ||
2215 | { | ||
2216 | // UCHAR *ptr = (UCHAR*)&fchs->pl[1]; | ||
2217 | // printk(" %d ALPAs found\n", *ptr); | ||
2218 | memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs | ||
2219 | fcChip->Options.LILPin = 1; // our LILPmap is valid! | ||
2220 | // now post to make Port Discovery happen... | ||
2221 | cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs); | ||
2222 | } | ||
2223 | } | ||
2224 | } | ||
2225 | |||
2226 | |||
2227 | // ***************** BASIC LINK SERVICE ***************** | ||
2228 | |||
2229 | else if( fchs->d_id & 0x80000000 // R_CTL: | ||
2230 | && // Basic Link Service Request | ||
2231 | !(fchs->f_ctl & 0xFF000000) ) // type=0 for BLS | ||
2232 | { | ||
2233 | |||
2234 | // Check for ABTS (Abort Sequence) | ||
2235 | if( (fchs->d_id & 0x8F000000) == 0x81000000) | ||
2236 | { | ||
2237 | // look for OX_ID, S_ID pair that matches in our | ||
2238 | // fcExchanges table; if found, reply with ACCept and complete | ||
2239 | // the exchange | ||
2240 | |||
2241 | // Per PLDA, an ABTS is sent by an initiator; therefore | ||
2242 | // assume that if we have an exhange open to the port who | ||
2243 | // sent ABTS, it will be the d_id of what we sent. | ||
2244 | for( ExchangeID = 0, AbortAccept=FALSE; | ||
2245 | ExchangeID < TACH_SEST_LEN; ExchangeID++) | ||
2246 | { | ||
2247 | // Valid "target" exchange 24-bit port_id matches? | ||
2248 | // NOTE: For the case of handling Intiator AND Target | ||
2249 | // functions on the same chip, we can have TWO Exchanges | ||
2250 | // with the same OX_ID -- OX_ID/FFFF for the CMND, and | ||
2251 | // OX_ID/RX_ID for the XRDY or DATA frame(s). Ideally, | ||
2252 | // we would like to support ABTS from Initiators or Targets, | ||
2253 | // but it's not clear that can be supported on Tachyon for | ||
2254 | // all cases (requires more investigation). | ||
2255 | |||
2256 | if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE || | ||
2257 | Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE) | ||
2258 | && | ||
2259 | ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == | ||
2260 | (fchs->s_id & 0xFFFFFF)) ) | ||
2261 | { | ||
2262 | |||
2263 | // target xchnge port_id matches -- how about OX_ID? | ||
2264 | if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000) | ||
2265 | == (fchs->ox_rx_id & 0xFFFF0000) ) | ||
2266 | // yes! post ACCept response; will be completed by fcStart | ||
2267 | { | ||
2268 | Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT; | ||
2269 | |||
2270 | // copy (add) rx_id field for simplified ACCept reply | ||
2271 | fchs->ox_rx_id = | ||
2272 | Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id; | ||
2273 | |||
2274 | cpqfcTSPutLinkQue( cpqfcHBAdata, | ||
2275 | BLS_ABTS_ACC, // Q Type | ||
2276 | fchs ); // void QueContent | ||
2277 | AbortAccept = TRUE; | ||
2278 | printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n", | ||
2279 | fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id); | ||
2280 | break; // ABTS can affect only ONE exchange -exit loop | ||
2281 | } | ||
2282 | } | ||
2283 | } // end of FOR loop | ||
2284 | if( !AbortAccept ) // can't ACCept ABTS - send Reject | ||
2285 | { | ||
2286 | printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n", | ||
2287 | fchs->ox_rx_id); | ||
2288 | if( Exchanges->fcExchange[ ExchangeID].type | ||
2289 | && | ||
2290 | !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len | ||
2291 | & 0x80000000)) | ||
2292 | { | ||
2293 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID); | ||
2294 | } | ||
2295 | else | ||
2296 | { | ||
2297 | printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n", | ||
2298 | ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len); | ||
2299 | } | ||
2300 | } | ||
2301 | } | ||
2302 | |||
2303 | // Check for BLS {ABTS? (Abort Sequence)} ACCept | ||
2304 | else if( (fchs->d_id & 0x8F000000) == 0x84000000) | ||
2305 | { | ||
2306 | // target has responded with ACC for our ABTS; | ||
2307 | // complete the indicated exchange with ABORTED status | ||
2308 | // Make no checks for correct RX_ID, since | ||
2309 | // all we need to conform ABTS ACC is the OX_ID. | ||
2310 | // Verify that the d_id matches! | ||
2311 | |||
2312 | ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC | ||
2313 | // printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n", | ||
2314 | // fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff, | ||
2315 | // Exchanges->fcExchange[ExchangeID].status); | ||
2316 | |||
2317 | |||
2318 | |||
2319 | if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense | ||
2320 | { | ||
2321 | // Does "target" exchange 24-bit port_id match? | ||
2322 | // (See "NOTE" above for handling Intiator AND Target in | ||
2323 | // the same device driver) | ||
2324 | // First, if this is a target response, then we originated | ||
2325 | // (initiated) it with BLS_ABTS: | ||
2326 | |||
2327 | if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS) | ||
2328 | |||
2329 | && | ||
2330 | // Second, does the source of this ACC match the destination | ||
2331 | // of who we originally sent it to? | ||
2332 | ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == | ||
2333 | (fchs->s_id & 0xFFFFFF)) ) | ||
2334 | { | ||
2335 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID ); | ||
2336 | } | ||
2337 | } | ||
2338 | } | ||
2339 | // Check for BLS {ABTS? (Abort Sequence)} ReJecT | ||
2340 | else if( (fchs->d_id & 0x8F000000) == 0x85000000) | ||
2341 | { | ||
2342 | // target has responded with RJT for our ABTS; | ||
2343 | // complete the indicated exchange with ABORTED status | ||
2344 | // Make no checks for correct RX_ID, since | ||
2345 | // all we need to conform ABTS ACC is the OX_ID. | ||
2346 | // Verify that the d_id matches! | ||
2347 | |||
2348 | ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC | ||
2349 | // printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n", | ||
2350 | // fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff); | ||
2351 | |||
2352 | if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense | ||
2353 | { | ||
2354 | // Does "target" exchange 24-bit port_id match? | ||
2355 | // (See "NOTE" above for handling Intiator AND Target in | ||
2356 | // the same device driver) | ||
2357 | // First, if this is a target response, then we originated | ||
2358 | // (initiated) it with BLS_ABTS: | ||
2359 | |||
2360 | if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS) | ||
2361 | |||
2362 | && | ||
2363 | // Second, does the source of this ACC match the destination | ||
2364 | // of who we originally sent it to? | ||
2365 | ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) == | ||
2366 | (fchs->s_id & 0xFFFFFF)) ) | ||
2367 | { | ||
2368 | // YES! NOTE: There is a bug in CPQ's RA-4000 box | ||
2369 | // where the "reason code" isn't returned in the payload | ||
2370 | // For now, simply presume the reject is because the target | ||
2371 | // already completed the exchange... | ||
2372 | |||
2373 | // printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID); | ||
2374 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID ); | ||
2375 | } | ||
2376 | } | ||
2377 | } // end of ABTS check | ||
2378 | } // end of Basic Link Service Request | ||
2379 | break; | ||
2380 | |||
2381 | default: | ||
2382 | printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n", | ||
2383 | fcLQ->Qitem[QNdx].Type, | ||
2384 | fcLQ->Qitem[QNdx].Type); | ||
2385 | break; | ||
2386 | } | ||
2387 | } | ||
2388 | |||
2389 | |||
2390 | // Function for Port Discovery necessary after every FC | ||
2391 | // initialization (e.g. LIP). | ||
2392 | // Also may be called if from Fabric Name Service logic. | ||
2393 | |||
2394 | static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds ) | ||
2395 | { | ||
2396 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
2397 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
2398 | ULONG ulStatus=0; | ||
2399 | TachFCHDR_GCMND fchs; // copy fields for transmission | ||
2400 | int i; | ||
2401 | ULONG loginType; | ||
2402 | LONG ExchangeID; | ||
2403 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
2404 | __u32 PortIds[ number_of_al_pa]; | ||
2405 | int NumberOfPorts=0; | ||
2406 | |||
2407 | // We're going to presume (for now) that our limit of Fabric devices | ||
2408 | // is the same as the number of alpa on a private loop (126 devices). | ||
2409 | // (Of course this could be changed to support however many we have | ||
2410 | // memory for). | ||
2411 | memset( &PortIds[0], 0, sizeof(PortIds)); | ||
2412 | |||
2413 | // First, check if this login is for our own Link Initialization | ||
2414 | // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices | ||
2415 | // from a switch. If we are logging into Fabric devices, we'll | ||
2416 | // have a non-NULL FabricPortId pointer | ||
2417 | |||
2418 | if( FabricPortIds != NULL) // may need logins | ||
2419 | { | ||
2420 | int LastPort=FALSE; | ||
2421 | i = 0; | ||
2422 | while( !LastPort) | ||
2423 | { | ||
2424 | // port IDs From NSR payload; byte swap needed? | ||
2425 | BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4); | ||
2426 | |||
2427 | // printk("FPortId[%d] %Xh ", i, PortIds[i]); | ||
2428 | if( PortIds[i] & 0x80000000) | ||
2429 | LastPort = TRUE; | ||
2430 | |||
2431 | PortIds[i] &= 0xFFFFFF; // get 24-bit port_id | ||
2432 | // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge) | ||
2433 | // erroneously use ALPA 0. | ||
2434 | if( PortIds[i] ) // need non-zero port_id... | ||
2435 | i++; | ||
2436 | |||
2437 | if( i >= number_of_al_pa ) // (in)sanity check | ||
2438 | break; | ||
2439 | FabricPortIds++; // next... | ||
2440 | } | ||
2441 | |||
2442 | NumberOfPorts = i; | ||
2443 | // printk("NumberOf Fabric ports %d", NumberOfPorts); | ||
2444 | } | ||
2445 | |||
2446 | else // need to send logins on our "local" link | ||
2447 | { | ||
2448 | |||
2449 | // are we a loop port? If so, check for reception of LILP frame, | ||
2450 | // and if received use it (SANMark requirement) | ||
2451 | if( fcChip->Options.LILPin ) | ||
2452 | { | ||
2453 | int j=0; | ||
2454 | // sanity check on number of ALPAs from LILP frame... | ||
2455 | // For format of LILP frame, see FC-AL specs or | ||
2456 | // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8) | ||
2457 | // First byte is number of ALPAs | ||
2458 | i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0]; | ||
2459 | NumberOfPorts = i; | ||
2460 | // printk(" LILP alpa count %d ", i); | ||
2461 | while( i > 0) | ||
2462 | { | ||
2463 | PortIds[j] = fcChip->LILPmap[1+ j]; | ||
2464 | j++; i--; | ||
2465 | } | ||
2466 | } | ||
2467 | else // have to send login to everybody | ||
2468 | { | ||
2469 | int j=0; | ||
2470 | i = number_of_al_pa; | ||
2471 | NumberOfPorts = i; | ||
2472 | while( i > 0) | ||
2473 | { | ||
2474 | PortIds[j] = valid_al_pa[j]; // all legal ALPAs | ||
2475 | j++; i--; | ||
2476 | } | ||
2477 | } | ||
2478 | } | ||
2479 | |||
2480 | |||
2481 | // Now we have a copy of the port_ids (and how many)... | ||
2482 | for( i = 0; i < NumberOfPorts; i++) | ||
2483 | { | ||
2484 | // 24-bit FC Port ID | ||
2485 | fchs.s_id = PortIds[i]; // note: only 8-bits used for ALPA | ||
2486 | |||
2487 | |||
2488 | // don't log into ourselves (Linux Scsi disk scan will stop on | ||
2489 | // no TARGET support error on us, and quit trying for rest of devices) | ||
2490 | if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) ) | ||
2491 | continue; | ||
2492 | |||
2493 | // fabric login needed? | ||
2494 | if( (fchs.s_id == 0) || | ||
2495 | (fcChip->Options.fabric == 1) ) | ||
2496 | { | ||
2497 | fcChip->Options.flogi = 1; // fabric needs longer for login | ||
2498 | // Do we need FLOGI or FDISC? | ||
2499 | pLoggedInPort = fcFindLoggedInPort( | ||
2500 | fcChip, | ||
2501 | NULL, // don't search SCSI Nexus | ||
2502 | 0xFFFFFC, // search linked list for Fabric port_id | ||
2503 | NULL, // don't search WWN | ||
2504 | NULL); // (don't care about end of list) | ||
2505 | |||
2506 | if( pLoggedInPort ) // If found, we have prior experience with | ||
2507 | // this port -- check whether PDISC is needed | ||
2508 | { | ||
2509 | if( pLoggedInPort->flogi ) | ||
2510 | { | ||
2511 | // does the switch support FDISC?? (FLOGI for now...) | ||
2512 | loginType = ELS_FLOGI; // prior FLOGI still valid | ||
2513 | } | ||
2514 | else | ||
2515 | loginType = ELS_FLOGI; // expired FLOGI | ||
2516 | } | ||
2517 | else // first FLOGI? | ||
2518 | loginType = ELS_FLOGI; | ||
2519 | |||
2520 | |||
2521 | fchs.s_id = 0xFFFFFE; // well known F_Port address | ||
2522 | |||
2523 | // Fabrics are not required to support FDISC, and | ||
2524 | // it's not clear if that helps us anyway, since | ||
2525 | // we'll want a Name Service Request to re-verify | ||
2526 | // visible devices... | ||
2527 | // Consequently, we always want our upper 16 bit | ||
2528 | // port_id to be zero (we'll be rejected if we | ||
2529 | // use our prior port_id if we've been plugged into | ||
2530 | // a different switch port). | ||
2531 | // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87) | ||
2532 | // If our ALPA is 55h for instance, we want the FC frame | ||
2533 | // s_id to be 0x000055, while Tach's my_al_pa register | ||
2534 | // must be 0x000155, to force an OPN at ALPA 0 | ||
2535 | // (the Fabric port) | ||
2536 | fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI | ||
2537 | writel( fcChip->Registers.my_al_pa | 0x0100, | ||
2538 | fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID); | ||
2539 | } | ||
2540 | |||
2541 | else // not FLOGI... | ||
2542 | { | ||
2543 | // should we send PLOGI or PDISC? Check if any prior port_id | ||
2544 | // (e.g. alpa) completed a PLOGI/PRLI exchange by checking | ||
2545 | // the pdisc flag. | ||
2546 | |||
2547 | pLoggedInPort = fcFindLoggedInPort( | ||
2548 | fcChip, | ||
2549 | NULL, // don't search SCSI Nexus | ||
2550 | fchs.s_id, // search linked list for al_pa | ||
2551 | NULL, // don't search WWN | ||
2552 | NULL); // (don't care about end of list) | ||
2553 | |||
2554 | |||
2555 | |||
2556 | if( pLoggedInPort ) // If found, we have prior experience with | ||
2557 | // this port -- check whether PDISC is needed | ||
2558 | { | ||
2559 | if( pLoggedInPort->pdisc ) | ||
2560 | { | ||
2561 | loginType = ELS_PDISC; // prior PLOGI and PRLI maybe still valid | ||
2562 | |||
2563 | } | ||
2564 | else | ||
2565 | loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC | ||
2566 | } | ||
2567 | else // never talked to this port_id before | ||
2568 | loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC | ||
2569 | } | ||
2570 | |||
2571 | |||
2572 | |||
2573 | ulStatus = cpqfcTSBuildExchange( | ||
2574 | cpqfcHBAdata, | ||
2575 | loginType, // e.g. PLOGI | ||
2576 | &fchs, // no incoming frame (we are originator) | ||
2577 | NULL, // no data (no scatter/gather list) | ||
2578 | &ExchangeID );// fcController->fcExchanges index, -1 if failed | ||
2579 | |||
2580 | if( !ulStatus ) // Exchange setup OK? | ||
2581 | { | ||
2582 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); | ||
2583 | if( !ulStatus ) | ||
2584 | { | ||
2585 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
2586 | // waited for completion for ELS type (Login frames issued | ||
2587 | // synchronously) | ||
2588 | |||
2589 | if( loginType == ELS_PDISC ) | ||
2590 | { | ||
2591 | // now, we really shouldn't Revalidate SEST exchanges until | ||
2592 | // we get an ACC reply from our target and verify that | ||
2593 | // the target address/WWN is unchanged. However, when a fast | ||
2594 | // target gets the PDISC, they can send SEST Exchange data | ||
2595 | // before we even get around to processing the PDISC ACC. | ||
2596 | // Consequently, we lose the I/O. | ||
2597 | // To avoid this, go ahead and Revalidate when the PDISC goes | ||
2598 | // out, anticipating that the ACC will be truly acceptable | ||
2599 | // (this happens 99.9999....% of the time). | ||
2600 | // If we revalidate a SEST write, and write data goes to a | ||
2601 | // target that is NOT the one we originated the WRITE to, | ||
2602 | // that target is required (FCP-SCSI specs, etc) to discard | ||
2603 | // our WRITE data. | ||
2604 | |||
2605 | // Re-validate SEST entries (Tachyon hardware assists) | ||
2606 | RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort); | ||
2607 | //TriggerHBA( fcChip->Registers.ReMapMemBase, 1); | ||
2608 | } | ||
2609 | } | ||
2610 | else // give up immediately on error | ||
2611 | { | ||
2612 | #ifdef LOGIN_DBG | ||
2613 | printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus ); | ||
2614 | #endif | ||
2615 | break; | ||
2616 | } | ||
2617 | |||
2618 | |||
2619 | if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc. | ||
2620 | { | ||
2621 | ulStatus = LNKDWN_OSLS; | ||
2622 | #ifdef LOGIN_DBG | ||
2623 | printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id); | ||
2624 | #endif | ||
2625 | break; | ||
2626 | } | ||
2627 | // Check the exchange for bad status (i.e. FrameTimeOut), | ||
2628 | // and complete on bad status (most likely due to BAD_ALPA) | ||
2629 | // on LDn, DPC function may already complete (ABORT) a started | ||
2630 | // exchange, so check type first (type = 0 on complete). | ||
2631 | if( Exchanges->fcExchange[ExchangeID].status ) | ||
2632 | { | ||
2633 | #ifdef LOGIN_DBG | ||
2634 | printk("completing x_ID %X on status %Xh\n", | ||
2635 | ExchangeID, Exchanges->fcExchange[ExchangeID].status); | ||
2636 | #endif | ||
2637 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID); | ||
2638 | } | ||
2639 | } | ||
2640 | else // Xchange setup failed... | ||
2641 | { | ||
2642 | #ifdef LOGIN_DBG | ||
2643 | printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); | ||
2644 | #endif | ||
2645 | break; | ||
2646 | } | ||
2647 | } | ||
2648 | if( !ulStatus ) | ||
2649 | { | ||
2650 | // set the event signifying that all ALPAs were sent out. | ||
2651 | #ifdef LOGIN_DBG | ||
2652 | printk("SendLogins: PortDiscDone\n"); | ||
2653 | #endif | ||
2654 | cpqfcHBAdata->PortDiscDone = 1; | ||
2655 | |||
2656 | |||
2657 | // TL/TS UG, pg. 184 | ||
2658 | // 0x0065 = 100ms for RT_TOV | ||
2659 | // 0x01f5 = 500ms for ED_TOV | ||
2660 | fcChip->Registers.ed_tov.value = 0x006501f5L; | ||
2661 | writel( fcChip->Registers.ed_tov.value, | ||
2662 | (fcChip->Registers.ed_tov.address)); | ||
2663 | |||
2664 | // set the LP_TOV back to ED_TOV (i.e. 500 ms) | ||
2665 | writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); | ||
2666 | } | ||
2667 | else | ||
2668 | { | ||
2669 | printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n", | ||
2670 | ExchangeID, fchs.s_id, ulStatus); | ||
2671 | } | ||
2672 | LEAVE("SendLogins"); | ||
2673 | |||
2674 | } | ||
2675 | |||
2676 | |||
2677 | // for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi", | ||
2678 | // D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9) | ||
2679 | static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd) | ||
2680 | { | ||
2681 | struct Scsi_Host *HostAdapter = Cmnd->device->host; | ||
2682 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
2683 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
2684 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
2685 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
2686 | int LunListLen=0; | ||
2687 | int i; | ||
2688 | ULONG x_ID = 0xFFFFFFFF; | ||
2689 | UCHAR *ucBuff = Cmnd->request_buffer; | ||
2690 | |||
2691 | // printk("cpqfcTS: ReportLunsDone \n"); | ||
2692 | // first, we need to find the Exchange for this command, | ||
2693 | // so we can find the fcPort struct to make the indicated | ||
2694 | // changes. | ||
2695 | for( i=0; i< TACH_SEST_LEN; i++) | ||
2696 | { | ||
2697 | if( Exchanges->fcExchange[i].type // exchange defined? | ||
2698 | && | ||
2699 | (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches? | ||
2700 | |||
2701 | { | ||
2702 | x_ID = i; // found exchange! | ||
2703 | break; | ||
2704 | } | ||
2705 | } | ||
2706 | if( x_ID == 0xFFFFFFFF) | ||
2707 | { | ||
2708 | // printk("cpqfcTS: ReportLuns failed - no FC Exchange\n"); | ||
2709 | goto Done; // Report Luns FC Exchange gone; | ||
2710 | // exchange probably Terminated by Implicit logout | ||
2711 | } | ||
2712 | |||
2713 | |||
2714 | // search linked list for the port_id we sent INQUIRY to | ||
2715 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
2716 | NULL, // DON'T search Scsi Nexus (we will set it) | ||
2717 | Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF, | ||
2718 | NULL, // DON'T search linked list for FC WWN | ||
2719 | NULL); // DON'T care about end of list | ||
2720 | |||
2721 | if( !pLoggedInPort ) | ||
2722 | { | ||
2723 | // printk("cpqfcTS: ReportLuns failed - device gone\n"); | ||
2724 | goto Done; // error! can't find logged in Port | ||
2725 | } | ||
2726 | LunListLen = ucBuff[3]; | ||
2727 | LunListLen += ucBuff[2]>>8; | ||
2728 | |||
2729 | if( !LunListLen ) // failed | ||
2730 | { | ||
2731 | // generically speaking, a soft error means we should retry... | ||
2732 | if( (Cmnd->result >> 16) == DID_SOFT_ERROR ) | ||
2733 | { | ||
2734 | if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) && | ||
2735 | (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset" | ||
2736 | { | ||
2737 | TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs; | ||
2738 | // did we fail because of "check condition, device reset?" | ||
2739 | // e.g. the device was reset (i.e., at every power up) | ||
2740 | // retry the Report Luns | ||
2741 | |||
2742 | // who are we sending it to? | ||
2743 | // we know this because we have a copy of the command | ||
2744 | // frame from the original Report Lun command - | ||
2745 | // switch the d_id/s_id fields, because the Exchange Build | ||
2746 | // context is "reply to source". | ||
2747 | |||
2748 | fchs->s_id = fchs->d_id; // (temporarily re-use the struct) | ||
2749 | cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs ); | ||
2750 | } | ||
2751 | } | ||
2752 | else // probably, the device doesn't support Report Luns | ||
2753 | pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0; | ||
2754 | } | ||
2755 | else // we have LUN info - check VSA mode | ||
2756 | { | ||
2757 | // for now, assume all LUNs will have same addr mode | ||
2758 | // for VSA, payload byte 8 will be 0x40; otherwise, 0 | ||
2759 | pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8]; | ||
2760 | |||
2761 | // Since we got a Report Luns answer, set lun masking flag | ||
2762 | pLoggedInPort->ScsiNexus.LunMasking = 1; | ||
2763 | |||
2764 | if( LunListLen > 8*CPQFCTS_MAX_LUN) // We expect CPQFCTS_MAX_LUN max | ||
2765 | LunListLen = 8*CPQFCTS_MAX_LUN; | ||
2766 | |||
2767 | /* | ||
2768 | printk("Device WWN %08X%08X Reports Luns @: ", | ||
2769 | (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), | ||
2770 | (ULONG)(pLoggedInPort->u.liWWN>>32)); | ||
2771 | |||
2772 | for( i=8; i<LunListLen+8; i+=8) | ||
2773 | { | ||
2774 | printk("%02X%02X ", ucBuff[i], ucBuff[i+1] ); | ||
2775 | } | ||
2776 | printk("\n"); | ||
2777 | */ | ||
2778 | |||
2779 | // Since the device was kind enough to tell us where the | ||
2780 | // LUNs are, lets ensure they are contiguous for Linux's | ||
2781 | // SCSI driver scan, which expects them to start at 0. | ||
2782 | // Since Linux only supports 8 LUNs, only copy the first | ||
2783 | // eight from the report luns command | ||
2784 | |||
2785 | // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report | ||
2786 | // LUNs 4001, 4004, etc., because other LUNs are masked from | ||
2787 | // this HBA (owned by someone else). We'll make those appear as | ||
2788 | // LUN 0, 1... to Linux | ||
2789 | { | ||
2790 | int j; | ||
2791 | int AppendLunList = 0; | ||
2792 | // Walk through the LUN list. The 'j' array number is | ||
2793 | // Linux's lun #, while the value of .lun[j] is the target's | ||
2794 | // lun #. | ||
2795 | // Once we build a LUN list, it's possible for a known device | ||
2796 | // to go offline while volumes (LUNs) are added. Later, | ||
2797 | // the device will do another PLOGI ... Report Luns command, | ||
2798 | // and we must not alter the existing Linux Lun map. | ||
2799 | // (This will be very rare). | ||
2800 | for( j=0; j < CPQFCTS_MAX_LUN; j++) | ||
2801 | { | ||
2802 | if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF ) | ||
2803 | { | ||
2804 | AppendLunList = 1; | ||
2805 | break; | ||
2806 | } | ||
2807 | } | ||
2808 | if( AppendLunList ) | ||
2809 | { | ||
2810 | int k; | ||
2811 | int FreeLunIndex; | ||
2812 | // printk("cpqfcTS: AppendLunList\n"); | ||
2813 | |||
2814 | // If we get a new Report Luns, we cannot change | ||
2815 | // any existing LUN mapping! (Only additive entry) | ||
2816 | // For all LUNs in ReportLun list | ||
2817 | // if RL lun != ScsiNexus lun | ||
2818 | // if RL lun present in ScsiNexus lun[], continue | ||
2819 | // else find ScsiNexus lun[]==FF and add, continue | ||
2820 | |||
2821 | for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++) | ||
2822 | { | ||
2823 | if( pLoggedInPort->ScsiNexus.lun[j] != ucBuff[i+1] ) | ||
2824 | { | ||
2825 | // something changed from the last Report Luns | ||
2826 | printk(" cpqfcTS: Report Lun change!\n"); | ||
2827 | for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN; | ||
2828 | k < CPQFCTS_MAX_LUN; k++) | ||
2829 | { | ||
2830 | if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF) | ||
2831 | { | ||
2832 | FreeLunIndex = k; | ||
2833 | break; | ||
2834 | } | ||
2835 | if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] ) | ||
2836 | break; // we already masked this lun | ||
2837 | } | ||
2838 | if( k >= CPQFCTS_MAX_LUN ) | ||
2839 | { | ||
2840 | printk(" no room for new LUN %d\n", ucBuff[i+1]); | ||
2841 | } | ||
2842 | else if( k == FreeLunIndex ) // need to add LUN | ||
2843 | { | ||
2844 | pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1]; | ||
2845 | // printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]); | ||
2846 | |||
2847 | } | ||
2848 | else | ||
2849 | { | ||
2850 | // lun already known | ||
2851 | } | ||
2852 | break; | ||
2853 | } | ||
2854 | } | ||
2855 | // print out the new list... | ||
2856 | for( j=0; j< CPQFCTS_MAX_LUN; j++) | ||
2857 | { | ||
2858 | if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF) | ||
2859 | break; // done | ||
2860 | // printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]); | ||
2861 | } | ||
2862 | } | ||
2863 | else | ||
2864 | { | ||
2865 | // printk("Linux SCSI LUNs[] -> Device LUNs: "); | ||
2866 | // first time - this is easy | ||
2867 | for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++) | ||
2868 | { | ||
2869 | pLoggedInPort->ScsiNexus.lun[j] = ucBuff[i+1]; | ||
2870 | // printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]); | ||
2871 | } | ||
2872 | // printk("\n"); | ||
2873 | } | ||
2874 | } | ||
2875 | } | ||
2876 | |||
2877 | Done: ; | ||
2878 | } | ||
2879 | |||
2880 | extern int is_private_data_of_cpqfc(CPQFCHBA *hba, void * pointer); | ||
2881 | extern void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data); | ||
2882 | |||
2883 | static void | ||
2884 | call_scsi_done(Scsi_Cmnd *Cmnd) | ||
2885 | { | ||
2886 | CPQFCHBA *hba; | ||
2887 | hba = (CPQFCHBA *) Cmnd->device->host->hostdata; | ||
2888 | // Was this command a cpqfc passthru ioctl ? | ||
2889 | if (Cmnd->sc_request != NULL && Cmnd->device->host != NULL && | ||
2890 | Cmnd->device->host->hostdata != NULL && | ||
2891 | is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->device->host->hostdata, | ||
2892 | Cmnd->sc_request->upper_private_data)) { | ||
2893 | cpqfc_free_private_data(hba, | ||
2894 | Cmnd->sc_request->upper_private_data); | ||
2895 | Cmnd->sc_request->upper_private_data = NULL; | ||
2896 | Cmnd->result &= 0xff00ffff; | ||
2897 | Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry | ||
2898 | } | ||
2899 | if (Cmnd->scsi_done != NULL) | ||
2900 | (*Cmnd->scsi_done)(Cmnd); | ||
2901 | } | ||
2902 | |||
2903 | // After successfully getting a "Process Login" (PRLI) from an | ||
2904 | // FC port, we want to Discover the LUNs so that we know the | ||
2905 | // addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral | ||
2906 | // Unit Device), and whether SSP (Selective Storage Presentation or | ||
2907 | // Lun Masking) has made the LUN numbers non-zero based or | ||
2908 | // non-contiguous. To remain backward compatible with the SCSI-2 | ||
2909 | // driver model, which expects a contiguous LUNs starting at 0, | ||
2910 | // will use the ReportLuns info to map from "device" to "Linux" | ||
2911 | // LUNs. | ||
2912 | static void IssueReportLunsCommand( | ||
2913 | CPQFCHBA* cpqfcHBAdata, | ||
2914 | TachFCHDR_GCMND* fchs) | ||
2915 | { | ||
2916 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
2917 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
2918 | struct scsi_cmnd *Cmnd = NULL; | ||
2919 | struct scsi_device *ScsiDev = NULL; | ||
2920 | LONG x_ID; | ||
2921 | ULONG ulStatus; | ||
2922 | UCHAR *ucBuff; | ||
2923 | |||
2924 | if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn | ||
2925 | { | ||
2926 | printk("Discard Q'd ReportLun command\n"); | ||
2927 | goto Done; | ||
2928 | } | ||
2929 | |||
2930 | // find the device (from port_id) we're talking to | ||
2931 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
2932 | NULL, // DON'T search Scsi Nexus | ||
2933 | fchs->s_id & 0xFFFFFF, | ||
2934 | NULL, // DON'T search linked list for FC WWN | ||
2935 | NULL); // DON'T care about end of list | ||
2936 | if( pLoggedInPort ) // we'd BETTER find it! | ||
2937 | { | ||
2938 | |||
2939 | |||
2940 | if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) ) | ||
2941 | goto Done; // forget it - FC device not a "target" | ||
2942 | |||
2943 | |||
2944 | ScsiDev = scsi_get_host_dev (cpqfcHBAdata->HostAdapter); | ||
2945 | if (!ScsiDev) | ||
2946 | goto Done; | ||
2947 | |||
2948 | Cmnd = scsi_get_command (ScsiDev, GFP_KERNEL); | ||
2949 | if (!Cmnd) | ||
2950 | goto Done; | ||
2951 | |||
2952 | ucBuff = pLoggedInPort->ReportLunsPayload; | ||
2953 | |||
2954 | memset( ucBuff, 0, REPORT_LUNS_PL); | ||
2955 | |||
2956 | Cmnd->scsi_done = ScsiReportLunsDone; | ||
2957 | |||
2958 | Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload; | ||
2959 | Cmnd->request_bufflen = REPORT_LUNS_PL; | ||
2960 | |||
2961 | Cmnd->cmnd[0] = 0xA0; | ||
2962 | Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8; | ||
2963 | Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL; | ||
2964 | Cmnd->cmd_len = 12; | ||
2965 | |||
2966 | Cmnd->device->channel = pLoggedInPort->ScsiNexus.channel; | ||
2967 | Cmnd->device->id = pLoggedInPort->ScsiNexus.target; | ||
2968 | |||
2969 | |||
2970 | ulStatus = cpqfcTSBuildExchange( | ||
2971 | cpqfcHBAdata, | ||
2972 | SCSI_IRE, | ||
2973 | fchs, | ||
2974 | Cmnd, // buffer for Report Lun data | ||
2975 | &x_ID );// fcController->fcExchanges index, -1 if failed | ||
2976 | |||
2977 | if( !ulStatus ) // Exchange setup? | ||
2978 | { | ||
2979 | ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID ); | ||
2980 | if( !ulStatus ) | ||
2981 | { | ||
2982 | // submitted to Tach's Outbound Que (ERQ PI incremented) | ||
2983 | // waited for completion for ELS type (Login frames issued | ||
2984 | // synchronously) | ||
2985 | } | ||
2986 | else | ||
2987 | // check reason for Exchange not being started - we might | ||
2988 | // want to Queue and start later, or fail with error | ||
2989 | { | ||
2990 | |||
2991 | } | ||
2992 | } | ||
2993 | |||
2994 | else // Xchange setup failed... | ||
2995 | printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); | ||
2996 | } | ||
2997 | else // like, we just got a PRLI ACC, and now the port is gone? | ||
2998 | { | ||
2999 | printk(" can't send ReportLuns - no login for port_id %Xh\n", | ||
3000 | fchs->s_id & 0xFFFFFF); | ||
3001 | } | ||
3002 | |||
3003 | |||
3004 | |||
3005 | Done: | ||
3006 | |||
3007 | if (Cmnd) | ||
3008 | scsi_put_command (Cmnd); | ||
3009 | if (ScsiDev) | ||
3010 | scsi_free_host_dev (ScsiDev); | ||
3011 | } | ||
3012 | |||
3013 | |||
3014 | |||
3015 | |||
3016 | |||
3017 | |||
3018 | |||
3019 | static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata) | ||
3020 | { | ||
3021 | int i; | ||
3022 | for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--) | ||
3023 | { | ||
3024 | if( cpqfcHBAdata->BoardLockCmnd[i] != NULL ) | ||
3025 | { | ||
3026 | Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i]; | ||
3027 | cpqfcHBAdata->BoardLockCmnd[i] = NULL; | ||
3028 | Cmnd->result = (DID_SOFT_ERROR << 16); // ask for retry | ||
3029 | // printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n", | ||
3030 | // i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); | ||
3031 | call_scsi_done(Cmnd); | ||
3032 | } | ||
3033 | } | ||
3034 | } | ||
3035 | |||
3036 | |||
3037 | |||
3038 | |||
3039 | |||
3040 | |||
3041 | // runs every 1 second for FC exchange timeouts and implicit FC device logouts | ||
3042 | |||
3043 | void cpqfcTSheartbeat( unsigned long ptr ) | ||
3044 | { | ||
3045 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr; | ||
3046 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
3047 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
3048 | PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; | ||
3049 | ULONG i; | ||
3050 | unsigned long flags; | ||
3051 | DECLARE_MUTEX_LOCKED(BoardLock); | ||
3052 | |||
3053 | PCI_TRACE( 0xA8) | ||
3054 | |||
3055 | if( cpqfcHBAdata->BoardLock) // Worker Task Running? | ||
3056 | goto Skip; | ||
3057 | |||
3058 | // STOP _que function | ||
3059 | spin_lock_irqsave( cpqfcHBAdata->HostAdapter->host_lock, flags); | ||
3060 | |||
3061 | PCI_TRACE( 0xA8) | ||
3062 | |||
3063 | |||
3064 | cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing | ||
3065 | |||
3066 | // release the IO lock (and re-enable interrupts) | ||
3067 | spin_unlock_irqrestore( cpqfcHBAdata->HostAdapter->host_lock, flags); | ||
3068 | |||
3069 | // Ensure no contention from _quecommand or Worker process | ||
3070 | CPQ_SPINLOCK_HBA( cpqfcHBAdata) | ||
3071 | |||
3072 | PCI_TRACE( 0xA8) | ||
3073 | |||
3074 | |||
3075 | disable_irq( cpqfcHBAdata->HostAdapter->irq); // our IRQ | ||
3076 | |||
3077 | // Complete the "bad target" commands (normally only used during | ||
3078 | // initialization, since we aren't supposed to call "scsi_done" | ||
3079 | // inside the queuecommand() function). (this is overly contorted, | ||
3080 | // scsi_done can be safely called from queuecommand for | ||
3081 | // this bad target case. May want to simplify this later) | ||
3082 | |||
3083 | for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) | ||
3084 | { | ||
3085 | if( cpqfcHBAdata->BadTargetCmnd[i] ) | ||
3086 | { | ||
3087 | Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i]; | ||
3088 | cpqfcHBAdata->BadTargetCmnd[i] = NULL; | ||
3089 | Cmnd->result = (DID_BAD_TARGET << 16); | ||
3090 | call_scsi_done(Cmnd); | ||
3091 | } | ||
3092 | else | ||
3093 | break; | ||
3094 | } | ||
3095 | |||
3096 | |||
3097 | // logged in ports -- re-login check (ports required to verify login with | ||
3098 | // PDISC after LIP within 2 secs) | ||
3099 | |||
3100 | // prevent contention | ||
3101 | while( pLoggedInPort ) // for all ports which are expecting | ||
3102 | // PDISC after the next LIP, check to see if | ||
3103 | // time is up! | ||
3104 | { | ||
3105 | // Important: we only detect "timeout" condition on TRANSITION | ||
3106 | // from non-zero to zero | ||
3107 | if( pLoggedInPort->LOGO_timer ) // time-out "armed"? | ||
3108 | { | ||
3109 | if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0? | ||
3110 | { | ||
3111 | // LOGOUT time! Per PLDA, PDISC hasn't complete in 2 secs, so | ||
3112 | // issue LOGO request and destroy all I/O with other FC port(s). | ||
3113 | |||
3114 | /* | ||
3115 | printk(" ~cpqfcTS heartbeat: LOGOut!~ "); | ||
3116 | printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n", | ||
3117 | pLoggedInPort->ScsiNexus.channel, | ||
3118 | pLoggedInPort->ScsiNexus.target, | ||
3119 | pLoggedInPort->port_id, | ||
3120 | (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), | ||
3121 | (ULONG)(pLoggedInPort->u.liWWN>>32)); | ||
3122 | |||
3123 | */ | ||
3124 | cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); | ||
3125 | |||
3126 | } | ||
3127 | // else simply decremented - maybe next time... | ||
3128 | } | ||
3129 | pLoggedInPort = pLoggedInPort->pNextPort; | ||
3130 | } | ||
3131 | |||
3132 | |||
3133 | |||
3134 | |||
3135 | |||
3136 | // ************ FC EXCHANGE TIMEOUT CHECK ************** | ||
3137 | |||
3138 | for( i=0; i< TACH_MAX_XID; i++) | ||
3139 | { | ||
3140 | if( Exchanges->fcExchange[i].type ) // exchange defined? | ||
3141 | { | ||
3142 | |||
3143 | if( !Exchanges->fcExchange[i].timeOut ) // time expired | ||
3144 | { | ||
3145 | // Set Exchange timeout status | ||
3146 | Exchanges->fcExchange[i].status |= FC2_TIMEOUT; | ||
3147 | |||
3148 | if( i >= TACH_SEST_LEN ) // Link Service Exchange | ||
3149 | { | ||
3150 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, i); // Don't "abort" LinkService | ||
3151 | } | ||
3152 | |||
3153 | else // SEST Exchange TO -- may post ABTS to Worker Thread Que | ||
3154 | { | ||
3155 | // (Make sure we don't keep timing it out; let other functions | ||
3156 | // complete it or set the timeOut as needed) | ||
3157 | Exchanges->fcExchange[i].timeOut = 30000; // seconds default | ||
3158 | |||
3159 | if( Exchanges->fcExchange[i].type | ||
3160 | & | ||
3161 | (BLS_ABTS | BLS_ABTS_ACC ) ) | ||
3162 | { | ||
3163 | // For BLS_ABTS*, an upper level might still have | ||
3164 | // an outstanding command waiting for low-level completion. | ||
3165 | // Also, in the case of a WRITE, we MUST get confirmation | ||
3166 | // of either ABTS ACC or RJT before re-using the Exchange. | ||
3167 | // It's possible that the RAID cache algorithm can hang | ||
3168 | // if we fail to complete a WRITE to a LBA, when a READ | ||
3169 | // comes later to that same LBA. Therefore, we must | ||
3170 | // ensure that the target verifies receipt of ABTS for | ||
3171 | // the exchange | ||
3172 | |||
3173 | printk("~TO Q'd ABTS (x_ID %Xh)~ ", i); | ||
3174 | // TriggerHBA( fcChip->Registers.ReMapMemBase); | ||
3175 | |||
3176 | // On timeout of a ABTS exchange, check to | ||
3177 | // see if the FC device has a current valid login. | ||
3178 | // If so, restart it. | ||
3179 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
3180 | Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus | ||
3181 | 0, // DON'T search linked list for FC port id | ||
3182 | NULL, // DON'T search linked list for FC WWN | ||
3183 | NULL); // DON'T care about end of list | ||
3184 | |||
3185 | // device exists? | ||
3186 | if( pLoggedInPort ) // device exists? | ||
3187 | { | ||
3188 | if( pLoggedInPort->prli ) // logged in for FCP-SCSI? | ||
3189 | { | ||
3190 | // attempt to restart the ABTS | ||
3191 | printk(" ~restarting ABTS~ "); | ||
3192 | cpqfcTSStartExchange( cpqfcHBAdata, i ); | ||
3193 | |||
3194 | } | ||
3195 | } | ||
3196 | } | ||
3197 | else // not an ABTS | ||
3198 | { | ||
3199 | |||
3200 | // We expect the WorkerThread to change the xchng type to | ||
3201 | // abort and set appropriate timeout. | ||
3202 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out | ||
3203 | } | ||
3204 | } | ||
3205 | } | ||
3206 | else // time not expired... | ||
3207 | { | ||
3208 | // decrement timeout: 1 or more seconds left | ||
3209 | --Exchanges->fcExchange[i].timeOut; | ||
3210 | } | ||
3211 | } | ||
3212 | } | ||
3213 | |||
3214 | |||
3215 | enable_irq( cpqfcHBAdata->HostAdapter->irq); | ||
3216 | |||
3217 | |||
3218 | CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) | ||
3219 | |||
3220 | cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued | ||
3221 | |||
3222 | // Now, complete any Cmnd we Q'd up while BoardLock was held | ||
3223 | |||
3224 | CompleteBoardLockCmnd( cpqfcHBAdata); | ||
3225 | |||
3226 | |||
3227 | // restart the timer to run again (1 sec later) | ||
3228 | Skip: | ||
3229 | mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ); | ||
3230 | |||
3231 | PCI_TRACEO( i, 0xA8) | ||
3232 | return; | ||
3233 | } | ||
3234 | |||
3235 | |||
3236 | // put valid FC-AL physical address in spec order | ||
3237 | static const UCHAR valid_al_pa[]={ | ||
3238 | 0xef, 0xe8, 0xe4, 0xe2, | ||
3239 | 0xe1, 0xE0, 0xDC, 0xDA, | ||
3240 | 0xD9, 0xD6, 0xD5, 0xD4, | ||
3241 | 0xD3, 0xD2, 0xD1, 0xCe, | ||
3242 | 0xCd, 0xCc, 0xCb, 0xCa, | ||
3243 | 0xC9, 0xC7, 0xC6, 0xC5, | ||
3244 | 0xC3, 0xBc, 0xBa, 0xB9, | ||
3245 | 0xB6, 0xB5, 0xB4, 0xB3, | ||
3246 | 0xB2, 0xB1, 0xae, 0xad, | ||
3247 | 0xAc, 0xAb, 0xAa, 0xA9, | ||
3248 | |||
3249 | 0xA7, 0xA6, 0xA5, 0xA3, | ||
3250 | 0x9f, 0x9e, 0x9d, 0x9b, | ||
3251 | 0x98, 0x97, 0x90, 0x8f, | ||
3252 | 0x88, 0x84, 0x82, 0x81, | ||
3253 | 0x80, 0x7c, 0x7a, 0x79, | ||
3254 | 0x76, 0x75, 0x74, 0x73, | ||
3255 | 0x72, 0x71, 0x6e, 0x6d, | ||
3256 | 0x6c, 0x6b, 0x6a, 0x69, | ||
3257 | 0x67, 0x66, 0x65, 0x63, | ||
3258 | 0x5c, 0x5a, 0x59, 0x56, | ||
3259 | |||
3260 | 0x55, 0x54, 0x53, 0x52, | ||
3261 | 0x51, 0x4e, 0x4d, 0x4c, | ||
3262 | 0x4b, 0x4a, 0x49, 0x47, | ||
3263 | 0x46, 0x45, 0x43, 0x3c, | ||
3264 | 0x3a, 0x39, 0x36, 0x35, | ||
3265 | 0x34, 0x33, 0x32, 0x31, | ||
3266 | 0x2e, 0x2d, 0x2c, 0x2b, | ||
3267 | 0x2a, 0x29, 0x27, 0x26, | ||
3268 | 0x25, 0x23, 0x1f, 0x1E, | ||
3269 | 0x1d, 0x1b, 0x18, 0x17, | ||
3270 | |||
3271 | 0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case | ||
3272 | |||
3273 | const int number_of_al_pa = (sizeof(valid_al_pa) ); | ||
3274 | |||
3275 | |||
3276 | |||
3277 | // this function looks up an al_pa from the table of valid al_pa's | ||
3278 | // we decrement from the last decimal loop ID, because soft al_pa | ||
3279 | // (our typical case) are assigned with highest priority (and high al_pa) | ||
3280 | // first. See "In-Depth FC-AL", R. Kembel pg. 38 | ||
3281 | // INPUTS: | ||
3282 | // al_pa - 24 bit port identifier (8 bit al_pa on private loop) | ||
3283 | // RETURN: | ||
3284 | // Loop ID - serves are index to array of logged in ports | ||
3285 | // -1 - invalid al_pa (not all 8 bit values are legal) | ||
3286 | |||
3287 | #if (0) | ||
3288 | static int GetLoopID( ULONG al_pa ) | ||
3289 | { | ||
3290 | int i; | ||
3291 | |||
3292 | for( i = number_of_al_pa -1; i >= 0; i--) // dec. | ||
3293 | { | ||
3294 | if( valid_al_pa[i] == (UCHAR)al_pa ) // take lowest 8 bits | ||
3295 | return i; // success - found valid al_pa; return decimal LoopID | ||
3296 | } | ||
3297 | return -1; // failed - not found | ||
3298 | } | ||
3299 | #endif | ||
3300 | |||
3301 | extern cpqfc_passthru_private_t *cpqfc_private(Scsi_Request *sr); | ||
3302 | |||
3303 | // Search the singly (forward) linked list "fcPorts" looking for | ||
3304 | // either the SCSI target (if != -1), port_id (if not NULL), | ||
3305 | // or WWN (if not null), in that specific order. | ||
3306 | // If we find a SCSI nexus (from Cmnd arg), set the SCp.phase | ||
3307 | // field according to VSA or PDU | ||
3308 | // RETURNS: | ||
3309 | // Ptr to logged in port struct if found | ||
3310 | // (NULL if not found) | ||
3311 | // pLastLoggedInPort - ptr to last struct (for adding new ones) | ||
3312 | // | ||
3313 | PFC_LOGGEDIN_PORT fcFindLoggedInPort( | ||
3314 | PTACHYON fcChip, | ||
3315 | Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun) | ||
3316 | ULONG port_id, // search linked list for al_pa, or | ||
3317 | UCHAR wwn[8], // search linked list for WWN, or... | ||
3318 | PFC_LOGGEDIN_PORT *pLastLoggedInPort ) | ||
3319 | |||
3320 | { | ||
3321 | PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts; | ||
3322 | BOOLEAN target_id_valid=FALSE; | ||
3323 | BOOLEAN port_id_valid=FALSE; | ||
3324 | BOOLEAN wwn_valid=FALSE; | ||
3325 | int i; | ||
3326 | |||
3327 | |||
3328 | if( Cmnd != NULL ) | ||
3329 | target_id_valid = TRUE; | ||
3330 | |||
3331 | else if( port_id ) // note! 24-bit NULL address is illegal | ||
3332 | port_id_valid = TRUE; | ||
3333 | |||
3334 | else | ||
3335 | { | ||
3336 | if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN) | ||
3337 | { | ||
3338 | for( i=0; i<8; i++) // valid WWN passed? NULL WWN invalid | ||
3339 | { | ||
3340 | if( wwn[i] != 0 ) | ||
3341 | wwn_valid = TRUE; // any non-zero byte makes (presumably) valid | ||
3342 | } | ||
3343 | } | ||
3344 | } | ||
3345 | // check other options ... | ||
3346 | |||
3347 | |||
3348 | // In case multiple search options are given, we use a priority | ||
3349 | // scheme: | ||
3350 | // While valid pLoggedIn Ptr | ||
3351 | // If port_id is valid | ||
3352 | // if port_id matches, return Ptr | ||
3353 | // If wwn is valid | ||
3354 | // if wwn matches, return Ptr | ||
3355 | // Next Ptr in list | ||
3356 | // | ||
3357 | // Return NULL (not found) | ||
3358 | |||
3359 | |||
3360 | while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid) | ||
3361 | { | ||
3362 | if( pLastLoggedInPort ) // caller's pointer valid? | ||
3363 | *pLastLoggedInPort = pLoggedInPort; // end of linked list | ||
3364 | |||
3365 | if( target_id_valid ) | ||
3366 | { | ||
3367 | // check Linux Scsi Cmnd for channel/target Nexus match | ||
3368 | // (all luns are accessed through matching "pLoggedInPort") | ||
3369 | if( (pLoggedInPort->ScsiNexus.target == Cmnd->device->id) | ||
3370 | && | ||
3371 | (pLoggedInPort->ScsiNexus.channel == Cmnd->device->channel)) | ||
3372 | { | ||
3373 | // For "passthru" modes, the IOCTL caller is responsible | ||
3374 | // for setting the FCP-LUN addressing | ||
3375 | if (Cmnd->sc_request != NULL && Cmnd->device->host != NULL && | ||
3376 | Cmnd->device->host->hostdata != NULL && | ||
3377 | is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->device->host->hostdata, | ||
3378 | Cmnd->sc_request->upper_private_data)) { | ||
3379 | /* This is a passthru... */ | ||
3380 | cpqfc_passthru_private_t *pd; | ||
3381 | pd = Cmnd->sc_request->upper_private_data; | ||
3382 | Cmnd->SCp.phase = pd->bus; | ||
3383 | // Cmnd->SCp.have_data_in = pd->pdrive; | ||
3384 | Cmnd->SCp.have_data_in = Cmnd->device->lun; | ||
3385 | } else { | ||
3386 | /* This is not a passthru... */ | ||
3387 | |||
3388 | // set the FCP-LUN addressing type | ||
3389 | Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing; | ||
3390 | |||
3391 | // set the Device Type we got from the snooped INQUIRY string | ||
3392 | Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType; | ||
3393 | |||
3394 | // handle LUN masking; if not "default" (illegal) lun value, | ||
3395 | // the use it. These lun values are set by a successful | ||
3396 | // Report Luns command | ||
3397 | if( pLoggedInPort->ScsiNexus.LunMasking == 1) | ||
3398 | { | ||
3399 | if (Cmnd->device->lun > sizeof(pLoggedInPort->ScsiNexus.lun)) | ||
3400 | return NULL; | ||
3401 | // we KNOW all the valid LUNs... 0xFF is invalid! | ||
3402 | Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->device->lun]; | ||
3403 | if (pLoggedInPort->ScsiNexus.lun[Cmnd->device->lun] == 0xFF) | ||
3404 | return NULL; | ||
3405 | // printk("xlating lun %d to 0x%02x\n", Cmnd->lun, | ||
3406 | // pLoggedInPort->ScsiNexus.lun[Cmnd->lun]); | ||
3407 | } | ||
3408 | else | ||
3409 | Cmnd->SCp.have_data_in = Cmnd->device->lun; // Linux & target luns match | ||
3410 | } | ||
3411 | break; // found it! | ||
3412 | } | ||
3413 | } | ||
3414 | |||
3415 | if( port_id_valid ) // look for alpa first | ||
3416 | { | ||
3417 | if( pLoggedInPort->port_id == port_id ) | ||
3418 | break; // found it! | ||
3419 | } | ||
3420 | if( wwn_valid ) // look for wwn second | ||
3421 | { | ||
3422 | |||
3423 | if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8)) | ||
3424 | { | ||
3425 | // all 8 bytes of WWN match | ||
3426 | break; // found it! | ||
3427 | } | ||
3428 | } | ||
3429 | |||
3430 | pLoggedInPort = pLoggedInPort->pNextPort; // try next port | ||
3431 | } | ||
3432 | |||
3433 | return pLoggedInPort; | ||
3434 | } | ||
3435 | |||
3436 | |||
3437 | |||
3438 | |||
3439 | // | ||
3440 | // We need to examine the SEST table and re-validate | ||
3441 | // any open Exchanges for this LoggedInPort | ||
3442 | // To make Tachyon pay attention, Freeze FCP assists, | ||
3443 | // set VAL bits, Unfreeze FCP assists | ||
3444 | static void RevalidateSEST( struct Scsi_Host *HostAdapter, | ||
3445 | PFC_LOGGEDIN_PORT pLoggedInPort) | ||
3446 | { | ||
3447 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
3448 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
3449 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
3450 | ULONG x_ID; | ||
3451 | BOOLEAN TachFroze = FALSE; | ||
3452 | |||
3453 | |||
3454 | // re-validate any SEST exchanges that are permitted | ||
3455 | // to survive the link down (e.g., good PDISC performed) | ||
3456 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | ||
3457 | { | ||
3458 | |||
3459 | // If the SEST entry port_id matches the pLoggedInPort, | ||
3460 | // we need to re-validate | ||
3461 | if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE) | ||
3462 | || | ||
3463 | (Exchanges->fcExchange[ x_ID].type == SCSI_IWE)) | ||
3464 | { | ||
3465 | |||
3466 | if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF) // (24-bit port ID) | ||
3467 | == pLoggedInPort->port_id) | ||
3468 | { | ||
3469 | // printk(" re-val xID %Xh ", x_ID); | ||
3470 | if( !TachFroze ) // freeze if not already frozen | ||
3471 | TachFroze |= FreezeTach( cpqfcHBAdata); | ||
3472 | fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit | ||
3473 | } | ||
3474 | } | ||
3475 | } | ||
3476 | |||
3477 | if( TachFroze) | ||
3478 | { | ||
3479 | fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists | ||
3480 | } | ||
3481 | } | ||
3482 | |||
3483 | |||
3484 | // Complete an Linux Cmnds that we Queued because | ||
3485 | // our FC link was down (cause immediate retry) | ||
3486 | |||
3487 | static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, | ||
3488 | PFC_LOGGEDIN_PORT pLoggedInPort) | ||
3489 | { | ||
3490 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | ||
3491 | Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0]; | ||
3492 | Scsi_Cmnd *Cmnd; | ||
3493 | int indx; | ||
3494 | |||
3495 | |||
3496 | |||
3497 | // if the device was previously "blocked", make sure | ||
3498 | // we unblock it so Linux SCSI will resume | ||
3499 | |||
3500 | pLoggedInPort->device_blocked = FALSE; // clear our flag | ||
3501 | |||
3502 | // check the Link Down command ptr buffer; | ||
3503 | // we can complete now causing immediate retry | ||
3504 | for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++) | ||
3505 | { | ||
3506 | if( *SCptr != NULL ) // scsi command to complete? | ||
3507 | { | ||
3508 | #ifdef DUMMYCMND_DBG | ||
3509 | printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx); | ||
3510 | #endif | ||
3511 | Cmnd = *SCptr; | ||
3512 | |||
3513 | |||
3514 | // Are there any Q'd commands for this target? | ||
3515 | if( (Cmnd->device->id == pLoggedInPort->ScsiNexus.target) | ||
3516 | && | ||
3517 | (Cmnd->device->channel == pLoggedInPort->ScsiNexus.channel) ) | ||
3518 | { | ||
3519 | Cmnd->result = (DID_SOFT_ERROR <<16); // force retry | ||
3520 | if( Cmnd->scsi_done == NULL) | ||
3521 | { | ||
3522 | printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n", | ||
3523 | pLoggedInPort->port_id); | ||
3524 | } | ||
3525 | else | ||
3526 | call_scsi_done(Cmnd); | ||
3527 | *SCptr = NULL; // free this slot for next use | ||
3528 | } | ||
3529 | } | ||
3530 | } | ||
3531 | } | ||
3532 | |||
3533 | |||
3534 | //#define WWN_DBG 1 | ||
3535 | |||
3536 | static void SetLoginFields( | ||
3537 | PFC_LOGGEDIN_PORT pLoggedInPort, | ||
3538 | TachFCHDR_GCMND* fchs, | ||
3539 | BOOLEAN PDisc, | ||
3540 | BOOLEAN Originator) | ||
3541 | { | ||
3542 | LOGIN_PAYLOAD logi; // FC-PH Port Login | ||
3543 | PRLI_REQUEST prli; // copy for BIG ENDIAN switch | ||
3544 | int i; | ||
3545 | #ifdef WWN_DBG | ||
3546 | ULONG ulBuff; | ||
3547 | #endif | ||
3548 | |||
3549 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi)); | ||
3550 | |||
3551 | pLoggedInPort->Originator = Originator; | ||
3552 | pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF; | ||
3553 | |||
3554 | switch( fchs->pl[0] & 0xffff ) | ||
3555 | { | ||
3556 | case 0x00000002: // PLOGI or PDISC ACCept? | ||
3557 | if( PDisc ) // PDISC accept | ||
3558 | goto PDISC_case; | ||
3559 | |||
3560 | case 0x00000003: // ELS_PLOGI or ELS_PLOGI_ACC | ||
3561 | |||
3562 | // Login BB_credit typically 0 for Tachyons | ||
3563 | pLoggedInPort->BB_credit = logi.cmn_services.bb_credit; | ||
3564 | |||
3565 | // e.g. 128, 256, 1024, 2048 per FC-PH spec | ||
3566 | // We have to use this when setting up SEST Writes, | ||
3567 | // since that determines frame size we send. | ||
3568 | pLoggedInPort->rx_data_size = logi.class3.rx_data_size; | ||
3569 | pLoggedInPort->plogi = TRUE; | ||
3570 | pLoggedInPort->pdisc = FALSE; | ||
3571 | pLoggedInPort->prli = FALSE; // ELS_PLOGI resets | ||
3572 | pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets | ||
3573 | pLoggedInPort->logo = FALSE; // ELS_PLOGI resets | ||
3574 | pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets | ||
3575 | pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets | ||
3576 | |||
3577 | // was this PLOGI to a Fabric? | ||
3578 | if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address | ||
3579 | pLoggedInPort->flogi = TRUE; | ||
3580 | |||
3581 | |||
3582 | for( i=0; i<8; i++) // copy the LOGIN port's WWN | ||
3583 | pLoggedInPort->u.ucWWN[i] = logi.port_name[i]; | ||
3584 | |||
3585 | #ifdef WWN_DBG | ||
3586 | ulBuff = (ULONG)pLoggedInPort->u.liWWN; | ||
3587 | if( pLoggedInPort->Originator) | ||
3588 | printk("o"); | ||
3589 | else | ||
3590 | printk("r"); | ||
3591 | printk("PLOGI port_id %Xh, WWN %08X", | ||
3592 | pLoggedInPort->port_id, ulBuff); | ||
3593 | |||
3594 | ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); | ||
3595 | printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort); | ||
3596 | #endif | ||
3597 | break; | ||
3598 | |||
3599 | |||
3600 | |||
3601 | |||
3602 | case 0x00000005: // ELS_LOGO (logout) | ||
3603 | |||
3604 | |||
3605 | pLoggedInPort->plogi = FALSE; | ||
3606 | pLoggedInPort->pdisc = FALSE; | ||
3607 | pLoggedInPort->prli = FALSE; // ELS_PLOGI resets | ||
3608 | pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets | ||
3609 | pLoggedInPort->logo = TRUE; // ELS_PLOGI resets | ||
3610 | pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets | ||
3611 | pLoggedInPort->LOGO_timer = 0; | ||
3612 | #ifdef WWN_DBG | ||
3613 | ulBuff = (ULONG)pLoggedInPort->u.liWWN; | ||
3614 | if( pLoggedInPort->Originator) | ||
3615 | printk("o"); | ||
3616 | else | ||
3617 | printk("r"); | ||
3618 | printk("LOGO port_id %Xh, WWN %08X", | ||
3619 | pLoggedInPort->port_id, ulBuff); | ||
3620 | |||
3621 | ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); | ||
3622 | printk("%08Xh\n", ulBuff); | ||
3623 | #endif | ||
3624 | break; | ||
3625 | |||
3626 | |||
3627 | |||
3628 | PDISC_case: | ||
3629 | case 0x00000050: // ELS_PDISC or ELS_PDISC_ACC | ||
3630 | pLoggedInPort->LOGO_timer = 0; // stop the time-out | ||
3631 | |||
3632 | pLoggedInPort->prli = TRUE; // ready to accept FCP-SCSI I/O | ||
3633 | |||
3634 | |||
3635 | |||
3636 | #ifdef WWN_DBG | ||
3637 | ulBuff = (ULONG)pLoggedInPort->u.liWWN; | ||
3638 | if( pLoggedInPort->Originator) | ||
3639 | printk("o"); | ||
3640 | else | ||
3641 | printk("r"); | ||
3642 | printk("PDISC port_id %Xh, WWN %08X", | ||
3643 | pLoggedInPort->port_id, ulBuff); | ||
3644 | |||
3645 | ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); | ||
3646 | printk("%08Xh\n", ulBuff); | ||
3647 | #endif | ||
3648 | |||
3649 | |||
3650 | |||
3651 | break; | ||
3652 | |||
3653 | |||
3654 | |||
3655 | case 0x1020L: // PRLI? | ||
3656 | case 0x1002L: // PRLI ACCept? | ||
3657 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli)); | ||
3658 | |||
3659 | pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags | ||
3660 | pLoggedInPort->prli = TRUE; // PLOGI resets, PDISC doesn't | ||
3661 | |||
3662 | pLoggedInPort->pdisc = TRUE; // expect to send (or receive) PDISC | ||
3663 | // next time | ||
3664 | pLoggedInPort->LOGO_timer = 0; // will be set next LinkDown | ||
3665 | #ifdef WWN_DBG | ||
3666 | ulBuff = (ULONG)pLoggedInPort->u.liWWN; | ||
3667 | if( pLoggedInPort->Originator) | ||
3668 | printk("o"); | ||
3669 | else | ||
3670 | printk("r"); | ||
3671 | printk("PRLI port_id %Xh, WWN %08X", | ||
3672 | pLoggedInPort->port_id, ulBuff); | ||
3673 | |||
3674 | ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32); | ||
3675 | printk("%08Xh\n", ulBuff); | ||
3676 | #endif | ||
3677 | |||
3678 | break; | ||
3679 | |||
3680 | } | ||
3681 | |||
3682 | return; | ||
3683 | } | ||
3684 | |||
3685 | |||
3686 | |||
3687 | |||
3688 | |||
3689 | |||
3690 | static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload) | ||
3691 | { | ||
3692 | LOGIN_PAYLOAD *plogi; // FC-PH Port Login | ||
3693 | LOGIN_PAYLOAD PlogiPayload; // copy for BIG ENDIAN switch | ||
3694 | PRLI_REQUEST *prli; // FCP-SCSI Process Login | ||
3695 | PRLI_REQUEST PrliPayload; // copy for BIG ENDIAN switch | ||
3696 | LOGOUT_PAYLOAD *logo; | ||
3697 | LOGOUT_PAYLOAD LogoutPayload; | ||
3698 | // PRLO_REQUEST *prlo; | ||
3699 | // PRLO_REQUEST PrloPayload; | ||
3700 | REJECT_MESSAGE rjt, *prjt; | ||
3701 | |||
3702 | memset( &PlogiPayload, 0, sizeof( PlogiPayload)); | ||
3703 | plogi = &PlogiPayload; // load into stack buffer, | ||
3704 | // then BIG-ENDIAN switch a copy to caller | ||
3705 | |||
3706 | |||
3707 | switch( type ) // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ... | ||
3708 | { | ||
3709 | case ELS_FDISC: | ||
3710 | case ELS_FLOGI: | ||
3711 | case ELS_PLOGI_ACC: // FC-PH PORT Login Accept | ||
3712 | case ELS_PLOGI: // FC-PH PORT Login | ||
3713 | case ELS_PDISC: // FC-PH2 Port Discovery - same payload as ELS_PLOGI | ||
3714 | plogi->login_cmd = LS_PLOGI; | ||
3715 | if( type == ELS_PDISC) | ||
3716 | plogi->login_cmd = LS_PDISC; | ||
3717 | else if( type == ELS_PLOGI_ACC ) | ||
3718 | plogi->login_cmd = LS_ACC; | ||
3719 | |||
3720 | plogi->cmn_services.bb_credit = 0x00; | ||
3721 | plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver; | ||
3722 | plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver; | ||
3723 | plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE; | ||
3724 | plogi->cmn_services.common_features = CONTINUOSLY_INCREASING | | ||
3725 | RANDOM_RELATIVE_OFFSET; | ||
3726 | |||
3727 | // fill in with World Wide Name based Port Name - 8 UCHARs | ||
3728 | // get from Tach registers WWN hi & lo | ||
3729 | LoadWWN( fcChip, plogi->port_name, 0); | ||
3730 | // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs | ||
3731 | // get from Tach registers WWN hi & lo | ||
3732 | LoadWWN( fcChip, plogi->node_name, 1); | ||
3733 | |||
3734 | // For Seagate Drives. | ||
3735 | // | ||
3736 | plogi->cmn_services.common_features |= 0x800; | ||
3737 | plogi->cmn_services.rel_offset = 0xFE; | ||
3738 | plogi->cmn_services.concurrent_seq = 1; | ||
3739 | plogi->class1.service_options = 0x00; | ||
3740 | plogi->class2.service_options = 0x00; | ||
3741 | plogi->class3.service_options = CLASS_VALID; | ||
3742 | plogi->class3.initiator_control = 0x00; | ||
3743 | plogi->class3.rx_data_size = MAX_RX_PAYLOAD; | ||
3744 | plogi->class3.recipient_control = | ||
3745 | ERROR_DISCARD | ONE_CATEGORY_SEQUENCE; | ||
3746 | plogi->class3.concurrent_sequences = 1; | ||
3747 | plogi->class3.open_sequences = 1; | ||
3748 | plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q'; | ||
3749 | plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q'; | ||
3750 | plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0'; | ||
3751 | plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0'; | ||
3752 | |||
3753 | |||
3754 | // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1) | ||
3755 | if( (type == ELS_FLOGI) || (type == ELS_FDISC) ) | ||
3756 | { | ||
3757 | if( type == ELS_FLOGI ) | ||
3758 | plogi->login_cmd = LS_FLOGI; | ||
3759 | else | ||
3760 | plogi->login_cmd = LS_FDISC; | ||
3761 | |||
3762 | plogi->cmn_services.lowest_ver = 0x20; | ||
3763 | plogi->cmn_services.common_features = 0x0800; | ||
3764 | plogi->cmn_services.rel_offset = 0; | ||
3765 | plogi->cmn_services.concurrent_seq = 0; | ||
3766 | |||
3767 | plogi->class3.service_options = 0x8800; | ||
3768 | plogi->class3.rx_data_size = 0; | ||
3769 | plogi->class3.recipient_control = 0; | ||
3770 | plogi->class3.concurrent_sequences = 0; | ||
3771 | plogi->class3.open_sequences = 0; | ||
3772 | } | ||
3773 | |||
3774 | // copy back to caller's buff, w/ BIG ENDIAN swap | ||
3775 | BigEndianSwap( (UCHAR*)&PlogiPayload, payload, sizeof(PlogiPayload)); | ||
3776 | break; | ||
3777 | |||
3778 | |||
3779 | case ELS_ACC: // generic Extended Link Service ACCept | ||
3780 | plogi->login_cmd = LS_ACC; | ||
3781 | // copy back to caller's buff, w/ BIG ENDIAN swap | ||
3782 | BigEndianSwap( (UCHAR*)&PlogiPayload, payload, 4); | ||
3783 | break; | ||
3784 | |||
3785 | |||
3786 | |||
3787 | case ELS_SCR: // Fabric State Change Registration | ||
3788 | { | ||
3789 | SCR_PL scr; // state change registration | ||
3790 | |||
3791 | memset( &scr, 0, sizeof(scr)); | ||
3792 | |||
3793 | scr.command = LS_SCR; // 0x62000000 | ||
3794 | // see FC-FLA, Rev 2.7, Table A.22 (pg 82) | ||
3795 | scr.function = 3; // 1 = Events detected by Fabric | ||
3796 | // 2 = N_Port detected registration | ||
3797 | // 3 = Full registration | ||
3798 | |||
3799 | // copy back to caller's buff, w/ BIG ENDIAN swap | ||
3800 | BigEndianSwap( (UCHAR*)&scr, payload, sizeof(SCR_PL)); | ||
3801 | } | ||
3802 | |||
3803 | break; | ||
3804 | |||
3805 | |||
3806 | case FCS_NSR: // Fabric Name Service Request | ||
3807 | { | ||
3808 | NSR_PL nsr; // Name Server Req. payload | ||
3809 | |||
3810 | memset( &nsr, 0, sizeof(NSR_PL)); | ||
3811 | |||
3812 | // see Brocade Fabric Programming Guide, | ||
3813 | // Rev 1.3, pg 4-44 | ||
3814 | nsr.CT_Rev = 0x01000000; | ||
3815 | nsr.FCS_Type = 0xFC020000; | ||
3816 | nsr.Command_code = 0x01710000; | ||
3817 | nsr.FCP = 8; | ||
3818 | |||
3819 | // copy back to caller's buff, w/ BIG ENDIAN swap | ||
3820 | BigEndianSwap( (UCHAR*)&nsr, payload, sizeof(NSR_PL)); | ||
3821 | } | ||
3822 | |||
3823 | break; | ||
3824 | |||
3825 | |||
3826 | |||
3827 | |||
3828 | case ELS_LOGO: // FC-PH PORT LogOut | ||
3829 | logo = &LogoutPayload; // load into stack buffer, | ||
3830 | // then BIG-ENDIAN switch a copy to caller | ||
3831 | logo->cmd = LS_LOGO; | ||
3832 | // load the 3 UCHARs of the node name | ||
3833 | // (if private loop, upper two UCHARs 0) | ||
3834 | logo->reserved = 0; | ||
3835 | |||
3836 | logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa); | ||
3837 | logo->n_port_identifier[1] = | ||
3838 | (UCHAR)(fcChip->Registers.my_al_pa>>8); | ||
3839 | logo->n_port_identifier[2] = | ||
3840 | (UCHAR)(fcChip->Registers.my_al_pa>>16); | ||
3841 | // fill in with World Wide Name based Port Name - 8 UCHARs | ||
3842 | // get from Tach registers WWN hi & lo | ||
3843 | LoadWWN( fcChip, logo->port_name, 0); | ||
3844 | |||
3845 | BigEndianSwap( (UCHAR*)&LogoutPayload, | ||
3846 | payload, sizeof(LogoutPayload) ); // 16 UCHAR struct | ||
3847 | break; | ||
3848 | |||
3849 | |||
3850 | case ELS_LOGO_ACC: // Logout Accept (FH-PH pg 149, table 74) | ||
3851 | logo = &LogoutPayload; // load into stack buffer, | ||
3852 | // then BIG-ENDIAN switch a copy to caller | ||
3853 | logo->cmd = LS_ACC; | ||
3854 | BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 ); // 4 UCHAR cmnd | ||
3855 | break; | ||
3856 | |||
3857 | |||
3858 | case ELS_RJT: // ELS_RJT link service reject (FH-PH pg 155) | ||
3859 | |||
3860 | prjt = (REJECT_MESSAGE*)payload; // pick up passed data | ||
3861 | rjt.command_code = ELS_RJT; | ||
3862 | // reverse fields, because of Swap that follows... | ||
3863 | rjt.vendor = prjt->reserved; // vendor specific | ||
3864 | rjt.explain = prjt->reason; // | ||
3865 | rjt.reason = prjt->explain; // | ||
3866 | rjt.reserved = prjt->vendor; // | ||
3867 | // BIG-ENDIAN switch a copy to caller | ||
3868 | BigEndianSwap( (UCHAR*)&rjt, payload, 8 ); // 8 UCHAR cmnd | ||
3869 | break; | ||
3870 | |||
3871 | |||
3872 | |||
3873 | |||
3874 | |||
3875 | case ELS_PRLI_ACC: // Process Login ACCept | ||
3876 | case ELS_PRLI: // Process Login | ||
3877 | case ELS_PRLO: // Process Logout | ||
3878 | memset( &PrliPayload, 0, sizeof( PrliPayload)); | ||
3879 | prli = &PrliPayload; // load into stack buffer, | ||
3880 | |||
3881 | if( type == ELS_PRLI ) | ||
3882 | prli->cmd = 0x20; // Login | ||
3883 | else if( type == ELS_PRLO ) | ||
3884 | prli->cmd = 0x21; // Logout | ||
3885 | else if( type == ELS_PRLI_ACC ) | ||
3886 | { | ||
3887 | prli->cmd = 0x02; // Login ACCept | ||
3888 | prli->valid = REQUEST_EXECUTED; | ||
3889 | } | ||
3890 | |||
3891 | |||
3892 | prli->valid |= SCSI_FCP | ESTABLISH_PAIR; | ||
3893 | prli->fcp_info = READ_XFER_RDY; | ||
3894 | prli->page_length = 0x10; | ||
3895 | prli->payload_length = 20; | ||
3896 | // Can be initiator AND target | ||
3897 | |||
3898 | if( fcChip->Options.initiator ) | ||
3899 | prli->fcp_info |= INITIATOR_FUNCTION; | ||
3900 | if( fcChip->Options.target ) | ||
3901 | prli->fcp_info |= TARGET_FUNCTION; | ||
3902 | |||
3903 | BigEndianSwap( (UCHAR*)&PrliPayload, payload, prli->payload_length); | ||
3904 | break; | ||
3905 | |||
3906 | |||
3907 | |||
3908 | default: // no can do - programming error | ||
3909 | printk(" BuildLinkServicePayload unknown!\n"); | ||
3910 | break; | ||
3911 | } | ||
3912 | } | ||
3913 | |||
3914 | // loads 8 UCHARs for PORT name or NODE name base on | ||
3915 | // controller's WWN. | ||
3916 | void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type) | ||
3917 | { | ||
3918 | UCHAR* bPtr, i; | ||
3919 | |||
3920 | switch( type ) | ||
3921 | { | ||
3922 | case 0: // Port_Name | ||
3923 | bPtr = (UCHAR*)&fcChip->Registers.wwn_hi; | ||
3924 | for( i =0; i<4; i++) | ||
3925 | dest[i] = *bPtr++; | ||
3926 | bPtr = (UCHAR*)&fcChip->Registers.wwn_lo; | ||
3927 | for( i =4; i<8; i++) | ||
3928 | dest[i] = *bPtr++; | ||
3929 | break; | ||
3930 | case 1: // Node/Fabric _Name | ||
3931 | bPtr = (UCHAR*)&fcChip->Registers.wwn_hi; | ||
3932 | for( i =0; i<4; i++) | ||
3933 | dest[i] = *bPtr++; | ||
3934 | bPtr = (UCHAR*)&fcChip->Registers.wwn_lo; | ||
3935 | for( i =4; i<8; i++) | ||
3936 | dest[i] = *bPtr++; | ||
3937 | break; | ||
3938 | } | ||
3939 | |||
3940 | } | ||
3941 | |||
3942 | |||
3943 | |||
3944 | // We check the Port Login payload for required values. Note that | ||
3945 | // ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload. | ||
3946 | |||
3947 | |||
3948 | int verify_PLOGI( PTACHYON fcChip, | ||
3949 | TachFCHDR_GCMND* fchs, | ||
3950 | ULONG* reject_explain) | ||
3951 | { | ||
3952 | LOGIN_PAYLOAD login; | ||
3953 | |||
3954 | // source, dest, len (should be mult. of 4) | ||
3955 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login, sizeof(login)); | ||
3956 | |||
3957 | // check FC version | ||
3958 | // if other port's highest supported version | ||
3959 | // is less than our lowest, and | ||
3960 | // if other port's lowest | ||
3961 | if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver || | ||
3962 | login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver ) | ||
3963 | { | ||
3964 | *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR); | ||
3965 | return LOGICAL_ERROR; | ||
3966 | } | ||
3967 | |||
3968 | // Receive Data Field Size must be >=128 | ||
3969 | // per FC-PH | ||
3970 | if (login.cmn_services.bb_rx_size < 128) | ||
3971 | { | ||
3972 | *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR); | ||
3973 | return LOGICAL_ERROR; | ||
3974 | } | ||
3975 | |||
3976 | // Only check Class 3 params | ||
3977 | if( login.class3.service_options & CLASS_VALID) | ||
3978 | { | ||
3979 | if (login.class3.rx_data_size < 128) | ||
3980 | { | ||
3981 | *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP); | ||
3982 | return LOGICAL_ERROR; | ||
3983 | } | ||
3984 | if( login.class3.initiator_control & XID_REQUIRED) | ||
3985 | { | ||
3986 | *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR); | ||
3987 | return LOGICAL_ERROR; | ||
3988 | } | ||
3989 | } | ||
3990 | return 0; // success | ||
3991 | } | ||
3992 | |||
3993 | |||
3994 | |||
3995 | |||
3996 | int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain) | ||
3997 | { | ||
3998 | PRLI_REQUEST prli; // buffer for BIG ENDIAN | ||
3999 | |||
4000 | // source, dest, len (should be mult. of 4) | ||
4001 | BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli)); | ||
4002 | |||
4003 | if( prli.fcp_info == 0 ) // i.e., not target or initiator? | ||
4004 | { | ||
4005 | *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR); | ||
4006 | return LOGICAL_ERROR; | ||
4007 | } | ||
4008 | |||
4009 | return 0; // success | ||
4010 | } | ||
4011 | |||
4012 | |||
4013 | // SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN) | ||
4014 | // INPUTS: | ||
4015 | // source - ptr to LITTLE ENDIAN ULONGS | ||
4016 | // cnt - number of UCHARs to switch (should be mult. of ULONG) | ||
4017 | // OUTPUTS: | ||
4018 | // dest - ptr to BIG ENDIAN copy | ||
4019 | // RETURN: | ||
4020 | // none | ||
4021 | // | ||
4022 | void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt) | ||
4023 | { | ||
4024 | int i,j; | ||
4025 | |||
4026 | source+=3; // start at MSB of 1st ULONG | ||
4027 | for( j=0; j < cnt; j+=4, source+=4, dest+=4) // every ULONG | ||
4028 | { | ||
4029 | for( i=0; i<4; i++) // every UCHAR in ULONG | ||
4030 | *(dest+i) = *(source-i); | ||
4031 | } | ||
4032 | } | ||
4033 | |||
4034 | |||
4035 | |||
4036 | |||
4037 | // Build FC Exchanges............ | ||
4038 | |||
4039 | static void buildFCPstatus( | ||
4040 | PTACHYON fcChip, | ||
4041 | ULONG ExchangeID); | ||
4042 | |||
4043 | static LONG FindFreeExchange( PTACHYON fcChip, ULONG type ); | ||
4044 | |||
4045 | static ULONG build_SEST_sgList( | ||
4046 | struct pci_dev *pcidev, | ||
4047 | ULONG *SESTalPairStart, | ||
4048 | Scsi_Cmnd *Cmnd, | ||
4049 | ULONG *sgPairs, | ||
4050 | PSGPAGES *sgPages_head // link list of TL Ext. S/G pages from O/S Pool | ||
4051 | ); | ||
4052 | |||
4053 | static int build_FCP_payload( Scsi_Cmnd *Cmnd, | ||
4054 | UCHAR* payload, ULONG type, ULONG fcp_dl ); | ||
4055 | |||
4056 | |||
4057 | /* | ||
4058 | IRB | ||
4059 | ERQ __________________ | ||
4060 | | | / | Req_A_SFS_Len | ____________________ | ||
4061 | |----------| / | Req_A_SFS_Addr |------->| Reserved | | ||
4062 | | IRB | / | Req_A_D_ID | | SOF EOF TimeStamp | | ||
4063 | |-----------/ | Req_A_SEST_Index |-+ | R_CTL | D_ID | | ||
4064 | | IRB | | Req_B... | | | CS_CTL| S_ID | | ||
4065 | |-----------\ | | | | TYPE | F_CTL | | ||
4066 | | IRB | \ | | | | SEQ_ID | SEQ_CNT | | ||
4067 | |----------- \ | | +-->+--| OX_ID | RX_ID | | ||
4068 | | | \ |__________________| | | RO | | ||
4069 | | | pl (payload/cmnd) | | ||
4070 | | | ..... | | ||
4071 | | |___________________| | ||
4072 | | | ||
4073 | | | ||
4074 | +-------------------------------------------+ | ||
4075 | | | ||
4076 | | | ||
4077 | | e.g. IWE | ||
4078 | | SEST __________________ for FCP_DATA | ||
4079 | | | | / | | Hdr_Len | ____________________ | ||
4080 | | |----------| / | Hdr_Addr_Addr |------->| Reserved | | ||
4081 | | | [0] | / |Remote_ID| RSP_Len| | SOF EOF TimeStamp | | ||
4082 | | |-----------/ | RSP_Addr |---+ | R_CTL | D_ID | | ||
4083 | +-> [1] | | | Buff_Off | | | CS_CTL| S_ID | | ||
4084 | |-----------\ |BuffIndex| Link | | | TYPE | F_CTL | | ||
4085 | | [2] | \ | Rsvd | RX_ID | | | SEQ_ID | SEQ_CNT | | ||
4086 | |----------- \ | Data_Len | | | OX_ID | RX_ID | | ||
4087 | | ... | \ | Exp_RO | | | RO | | ||
4088 | |----------| | Exp_Byte_Cnt | | |___________________| | ||
4089 | | SEST_LEN | +--| Len | | | ||
4090 | |__________| | | Address | | | ||
4091 | | | ... | | for FCP_RSP | ||
4092 | | |__________________| | ____________________ | ||
4093 | | +----| Reserved | | ||
4094 | | | SOF EOF TimeStamp | | ||
4095 | | | R_CTL | D_ID | | ||
4096 | | | CS_CTL| S_ID | | ||
4097 | +--- local or extended | .... | | ||
4098 | scatter/gather lists | ||
4099 | defining upper-layer | ||
4100 | data (e.g. from user's App) | ||
4101 | |||
4102 | |||
4103 | */ | ||
4104 | // All TachLite commands must start with a SFS (Single Frame Sequence) | ||
4105 | // command. In the simplest case (a NOP Basic Link command), | ||
4106 | // only one frame header and ERQ entry is required. The most complex | ||
4107 | // case is the SCSI assisted command, which requires an ERQ entry, | ||
4108 | // SEST entry, and several frame headers and data buffers all | ||
4109 | // logically linked together. | ||
4110 | // Inputs: | ||
4111 | // cpqfcHBAdata - controller struct | ||
4112 | // type - PLOGI, SCSI_IWE, etc. | ||
4113 | // InFCHS - Incoming Tachlite FCHS which prompted this exchange | ||
4114 | // (only s_id set if we are originating) | ||
4115 | // Data - PVOID to data struct consistent with "type" | ||
4116 | // fcExchangeIndex - pointer to OX/RD ID value of built exchange | ||
4117 | // Return: | ||
4118 | // fcExchangeIndex - OX/RD ID value if successful | ||
4119 | // 0 - success | ||
4120 | // INVALID_ARGS - NULL/ invalid passed args | ||
4121 | // BAD_ALPA - Bad source al_pa address | ||
4122 | // LNKDWN_OSLS - Link Down (according to this controller) | ||
4123 | // OUTQUE_FULL - Outbound Que full | ||
4124 | // DRIVERQ_FULL - controller's Exchange array full | ||
4125 | // SEST_FULL - SEST table full | ||
4126 | // | ||
4127 | // Remarks: | ||
4128 | // Psuedo code: | ||
4129 | // Check for NULL pointers / bad args | ||
4130 | // Build outgoing FCHS - the header/payload struct | ||
4131 | // Build IRB (for ERQ entry) | ||
4132 | // if SCSI command, build SEST entry (e.g. IWE, TRE,...) | ||
4133 | // return success | ||
4134 | |||
4135 | //sbuildex | ||
4136 | ULONG cpqfcTSBuildExchange( | ||
4137 | CPQFCHBA *cpqfcHBAdata, | ||
4138 | ULONG type, // e.g. PLOGI | ||
4139 | TachFCHDR_GCMND* InFCHS, // incoming FCHS | ||
4140 | void *Data, // the CDB, scatter/gather, etc. | ||
4141 | LONG *fcExchangeIndex ) // points to allocated exchange, | ||
4142 | { | ||
4143 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
4144 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
4145 | ULONG ulStatus = 0; // assume OK | ||
4146 | USHORT ox_ID, rx_ID=0xFFFF; | ||
4147 | ULONG SfsLen=0L; | ||
4148 | TachLiteIRB* pIRB; | ||
4149 | IRBflags IRB_flags; | ||
4150 | UCHAR *pIRB_flags = (UCHAR*)&IRB_flags; | ||
4151 | TachFCHDR_GCMND* CMDfchs; | ||
4152 | TachFCHDR* dataHDR; // 32 byte HEADER ONLY FCP-DATA buffer | ||
4153 | TachFCHDR_RSP* rspHDR; // 32 byte header + RSP payload | ||
4154 | Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data; // Linux Scsi CDB, S/G, ... | ||
4155 | TachLiteIWE* pIWE; | ||
4156 | TachLiteIRE* pIRE; | ||
4157 | TachLiteTWE* pTWE; | ||
4158 | TachLiteTRE* pTRE; | ||
4159 | ULONG fcp_dl; // total byte length of DATA transferred | ||
4160 | ULONG fl; // frame length (FC frame size, 128, 256, 512, 1024) | ||
4161 | ULONG sgPairs; // number of valid scatter/gather pairs | ||
4162 | int FCP_SCSI_command; | ||
4163 | BA_ACC_PAYLOAD *ba_acc; | ||
4164 | BA_RJT_PAYLOAD *ba_rjt; | ||
4165 | |||
4166 | // check passed ARGS | ||
4167 | if( !fcChip->ERQ ) // NULL ptr means uninitialized Tachlite chip | ||
4168 | return INVALID_ARGS; | ||
4169 | |||
4170 | |||
4171 | if( type == SCSI_IRE || | ||
4172 | type == SCSI_TRE || | ||
4173 | type == SCSI_IWE || | ||
4174 | type == SCSI_TWE) | ||
4175 | FCP_SCSI_command = 1; | ||
4176 | |||
4177 | else | ||
4178 | FCP_SCSI_command = 0; | ||
4179 | |||
4180 | |||
4181 | // for commands that pass payload data (e.g. SCSI write) | ||
4182 | // examine command struct - verify that the | ||
4183 | // length of s/g buffers is adequate for total payload | ||
4184 | // length (end of list is NULL address) | ||
4185 | |||
4186 | if( FCP_SCSI_command ) | ||
4187 | { | ||
4188 | if( Data ) // must have data descriptor (S/G list -- at least | ||
4189 | // one address with at least 1 byte of data) | ||
4190 | { | ||
4191 | // something to do (later)? | ||
4192 | } | ||
4193 | |||
4194 | else | ||
4195 | return INVALID_ARGS; // invalid DATA ptr | ||
4196 | } | ||
4197 | |||
4198 | |||
4199 | |||
4200 | // we can build an Exchange for later Queuing (on the TL chip) | ||
4201 | // if an empty slot is available in the DevExt for this controller | ||
4202 | // look for available Exchange slot... | ||
4203 | |||
4204 | if( type != FCP_RESPONSE && | ||
4205 | type != BLS_ABTS && | ||
4206 | type != BLS_ABTS_ACC ) // already have Exchange slot! | ||
4207 | *fcExchangeIndex = FindFreeExchange( fcChip, type ); | ||
4208 | |||
4209 | if( *fcExchangeIndex != -1 ) // Exchange is available? | ||
4210 | { | ||
4211 | // assign tmp ptr (shorthand) | ||
4212 | CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs; | ||
4213 | |||
4214 | if( Cmnd != NULL ) // (necessary for ABTS cases) | ||
4215 | { | ||
4216 | Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi | ||
4217 | Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort = | ||
4218 | fcFindLoggedInPort( fcChip, | ||
4219 | Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus | ||
4220 | 0, // DON'T search linked list for FC port id | ||
4221 | NULL, // DON'T search linked list for FC WWN | ||
4222 | NULL); // DON'T care about end of list | ||
4223 | |||
4224 | } | ||
4225 | |||
4226 | |||
4227 | // Build the command frame header (& data) according | ||
4228 | // to command type | ||
4229 | |||
4230 | // fields common for all SFS frame types | ||
4231 | CMDfchs->reserved = 0L; // must clear | ||
4232 | CMDfchs->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; LCr=0, no TS | ||
4233 | |||
4234 | // get the destination port_id from incoming FCHS | ||
4235 | // (initialized before calling if we're Originator) | ||
4236 | // Frame goes to port it was from - the source_id | ||
4237 | |||
4238 | CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later) | ||
4239 | CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 | ||
4240 | |||
4241 | |||
4242 | // now enter command-specific fields | ||
4243 | switch( type ) | ||
4244 | { | ||
4245 | |||
4246 | case BLS_NOP: // FC defined basic link service command NO-OP | ||
4247 | // ensure unique X_IDs! (use tracking function) | ||
4248 | |||
4249 | *pIRB_flags = 0; // clear IRB flags | ||
4250 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4251 | SfsLen = *pIRB_flags; | ||
4252 | |||
4253 | SfsLen <<= 24; // shift flags to MSB | ||
4254 | SfsLen += 32L; // add len to LSB (header only - no payload) | ||
4255 | |||
4256 | // TYPE[31-24] 00 Basic Link Service | ||
4257 | // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. | ||
4258 | CMDfchs->d_id |= 0x80000000L; // R_CTL = 80 for NOP (Basic Link Ser.) | ||
4259 | CMDfchs->f_ctl = 0x00310000L; // xchng originator, 1st seq,.... | ||
4260 | CMDfchs->seq_cnt = 0x0L; | ||
4261 | CMDfchs->ox_rx_id = 0xFFFF; // RX_ID for now; OX_ID on start | ||
4262 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4263 | CMDfchs->pl[0] = 0xaabbccddL; // words 8-15 frame data payload (n/a) | ||
4264 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds | ||
4265 | // (NOP should complete ~instantly) | ||
4266 | break; | ||
4267 | |||
4268 | |||
4269 | |||
4270 | |||
4271 | case BLS_ABTS_ACC: // Abort Sequence ACCept | ||
4272 | *pIRB_flags = 0; // clear IRB flags | ||
4273 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4274 | SfsLen = *pIRB_flags; | ||
4275 | |||
4276 | SfsLen <<= 24; // shift flags to MSB | ||
4277 | SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload) | ||
4278 | |||
4279 | CMDfchs->d_id |= 0x84000000L; // R_CTL = 84 for BASIC ACCept | ||
4280 | // TYPE[31-24] 00 Basic Link Service | ||
4281 | // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. | ||
4282 | CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI | ||
4283 | // CMDfchs->seq_id & count might be set from DataHdr? | ||
4284 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4285 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds | ||
4286 | // (Timeout in case of weird error) | ||
4287 | |||
4288 | // now set the ACCept payload... | ||
4289 | ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0]; | ||
4290 | memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD)); | ||
4291 | // Since PLDA requires (only) entire Exchange aborts, we don't need | ||
4292 | // to worry about what the last sequence was. | ||
4293 | |||
4294 | // We expect that a "target" task is accepting the abort, so we | ||
4295 | // can use the OX/RX ID pair | ||
4296 | ba_acc->ox_rx_id = CMDfchs->ox_rx_id; | ||
4297 | |||
4298 | // source, dest, #bytes | ||
4299 | BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4); | ||
4300 | |||
4301 | ba_acc->low_seq_cnt = 0; | ||
4302 | ba_acc->high_seq_cnt = 0xFFFF; | ||
4303 | |||
4304 | |||
4305 | break; | ||
4306 | |||
4307 | |||
4308 | case BLS_ABTS_RJT: // Abort Sequence ACCept | ||
4309 | *pIRB_flags = 0; // clear IRB flags | ||
4310 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4311 | SfsLen = *pIRB_flags; | ||
4312 | |||
4313 | SfsLen <<= 24; // shift flags to MSB | ||
4314 | SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload) | ||
4315 | |||
4316 | CMDfchs->d_id |= 0x85000000L; // R_CTL = 85 for BASIC ReJecT | ||
4317 | // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. | ||
4318 | // TYPE[31-24] 00 Basic Link Service | ||
4319 | CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI | ||
4320 | // CMDfchs->seq_id & count might be set from DataHdr? | ||
4321 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4322 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds | ||
4323 | // (Timeout in case of weird error) | ||
4324 | |||
4325 | CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender! | ||
4326 | |||
4327 | // now set the ReJecT payload... | ||
4328 | ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0]; | ||
4329 | memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD)); | ||
4330 | |||
4331 | // We expect that a "target" task couldn't find the Exhange in the | ||
4332 | // array of active exchanges, so we use a new LinkService X_ID. | ||
4333 | // See Reject payload description in FC-PH (Rev 4.3), pg. 140 | ||
4334 | ba_rjt->reason_code = 0x09; // "unable to perform command request" | ||
4335 | ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair | ||
4336 | |||
4337 | |||
4338 | break; | ||
4339 | |||
4340 | |||
4341 | case BLS_ABTS: // FC defined basic link service command ABTS | ||
4342 | // Abort Sequence | ||
4343 | |||
4344 | |||
4345 | *pIRB_flags = 0; // clear IRB flags | ||
4346 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4347 | SfsLen = *pIRB_flags; | ||
4348 | |||
4349 | SfsLen <<= 24; // shift flags to MSB | ||
4350 | SfsLen += 32L; // add len to LSB (header only - no payload) | ||
4351 | |||
4352 | // TYPE[31-24] 00 Basic Link Service | ||
4353 | // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I. | ||
4354 | CMDfchs->d_id |= 0x81000000L; // R_CTL = 81 for ABTS | ||
4355 | CMDfchs->f_ctl = 0x00110000L; // xchnge originator, last seq, xfer SI | ||
4356 | // CMDfchs->seq_id & count might be set from DataHdr? | ||
4357 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4358 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds | ||
4359 | // (ABTS must timeout when responder is gone) | ||
4360 | break; | ||
4361 | |||
4362 | |||
4363 | |||
4364 | case FCS_NSR: // Fabric Name Service Request | ||
4365 | Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2; | ||
4366 | |||
4367 | |||
4368 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds | ||
4369 | // OX_ID, linked to Driver Transaction ID | ||
4370 | // (fix-up at Queing time) | ||
4371 | CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify | ||
4372 | // OX_ID set at ERQueing time | ||
4373 | *pIRB_flags = 0; // clear IRB flags | ||
4374 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4375 | SfsLen = *pIRB_flags; | ||
4376 | |||
4377 | SfsLen <<= 24; // shift flags to MSB | ||
4378 | SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload) | ||
4379 | |||
4380 | CMDfchs->d_id |= 0x02000000L; // R_CTL = 02 for - | ||
4381 | // Name Service Request: Unsolicited | ||
4382 | // TYPE[31-24] 01 Extended Link Service | ||
4383 | // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. | ||
4384 | CMDfchs->f_ctl = 0x20210000L; | ||
4385 | // OX_ID will be fixed-up at Tachyon enqueing time | ||
4386 | CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt | ||
4387 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4388 | |||
4389 | BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); | ||
4390 | |||
4391 | |||
4392 | |||
4393 | |||
4394 | |||
4395 | |||
4396 | break; | ||
4397 | |||
4398 | |||
4399 | |||
4400 | |||
4401 | case ELS_PLOGI: // FC-PH extended link service command Port Login | ||
4402 | // (May, 2000) | ||
4403 | // NOTE! This special case facilitates SANMark testing. The SANMark | ||
4404 | // test script for initialization-timeout.fcal.SANMark-1.fc | ||
4405 | // "eats" the OPN() primitive without issuing an R_RDY, causing | ||
4406 | // Tachyon to report LST (loop state timeout), which causes a | ||
4407 | // LIP. To avoid this, simply send out the frame (i.e. assuming a | ||
4408 | // buffer credit of 1) without waiting for R_RDY. Many FC devices | ||
4409 | // (other than Tachyon) have been doing this for years. We don't | ||
4410 | // ever want to do this for non-Link Service frames unless the | ||
4411 | // other device really did report non-zero login BB credit (i.e. | ||
4412 | // in the PLOGI ACCept frame). | ||
4413 | // CMDfchs->sof_eof |= 0x00000400L; // LCr=1 | ||
4414 | |||
4415 | case ELS_FDISC: // Fabric Discovery (Login) | ||
4416 | case ELS_FLOGI: // Fabric Login | ||
4417 | case ELS_SCR: // Fabric State Change Registration | ||
4418 | case ELS_LOGO: // FC-PH extended link service command Port Logout | ||
4419 | case ELS_PDISC: // FC-PH extended link service cmnd Port Discovery | ||
4420 | case ELS_PRLI: // FC-PH extended link service cmnd Process Login | ||
4421 | |||
4422 | Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2; | ||
4423 | |||
4424 | |||
4425 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds | ||
4426 | // OX_ID, linked to Driver Transaction ID | ||
4427 | // (fix-up at Queing time) | ||
4428 | CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify | ||
4429 | // OX_ID set at ERQueing time | ||
4430 | *pIRB_flags = 0; // clear IRB flags | ||
4431 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4432 | SfsLen = *pIRB_flags; | ||
4433 | |||
4434 | SfsLen <<= 24; // shift flags to MSB | ||
4435 | if( type == ELS_LOGO ) | ||
4436 | SfsLen += (32L + 16L); // add len (header & PLOGI payload) | ||
4437 | else if( type == ELS_PRLI ) | ||
4438 | SfsLen += (32L + 20L); // add len (header & PRLI payload) | ||
4439 | else if( type == ELS_SCR ) | ||
4440 | SfsLen += (32L + sizeof(SCR_PL)); // add len (header & SCR payload) | ||
4441 | else | ||
4442 | SfsLen += (32L + 116L); // add len (header & PLOGI payload) | ||
4443 | |||
4444 | CMDfchs->d_id |= 0x22000000L; // R_CTL = 22 for - | ||
4445 | // Extended Link_Data: Unsolicited Control | ||
4446 | // TYPE[31-24] 01 Extended Link Service | ||
4447 | // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. | ||
4448 | CMDfchs->f_ctl = 0x01210000L; | ||
4449 | // OX_ID will be fixed-up at Tachyon enqueing time | ||
4450 | CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt | ||
4451 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4452 | |||
4453 | BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); | ||
4454 | |||
4455 | break; | ||
4456 | |||
4457 | |||
4458 | |||
4459 | case ELS_LOGO_ACC: // FC-PH extended link service logout accept | ||
4460 | case ELS_RJT: // extended link service reject (add reason) | ||
4461 | case ELS_ACC: // ext. link service generic accept | ||
4462 | case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC) | ||
4463 | case ELS_PRLI_ACC: // ext. link service process login accept | ||
4464 | |||
4465 | |||
4466 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done | ||
4467 | // ensure unique X_IDs! (use tracking function) | ||
4468 | // OX_ID from initiator cmd | ||
4469 | ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16); | ||
4470 | rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID | ||
4471 | |||
4472 | *pIRB_flags = 0; // clear IRB flags | ||
4473 | IRB_flags.SFA = 1; // send SFS (not SEST index) | ||
4474 | SfsLen = *pIRB_flags; | ||
4475 | |||
4476 | SfsLen <<= 24; // shift flags to MSB | ||
4477 | if( type == ELS_RJT ) | ||
4478 | { | ||
4479 | SfsLen += (32L + 8L); // add len (header + payload) | ||
4480 | |||
4481 | // ELS_RJT reason codes (utilize unused "reserved" field) | ||
4482 | CMDfchs->pl[0] = 1; | ||
4483 | CMDfchs->pl[1] = InFCHS->reserved; | ||
4484 | |||
4485 | } | ||
4486 | else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC) ) | ||
4487 | SfsLen += (32L + 4L); // add len (header + payload) | ||
4488 | else if( type == ELS_PLOGI_ACC ) | ||
4489 | SfsLen += (32L + 116L); // add len (header + payload) | ||
4490 | else if( type == ELS_PRLI_ACC ) | ||
4491 | SfsLen += (32L + 20L); // add len (header + payload) | ||
4492 | |||
4493 | CMDfchs->d_id |= 0x23000000L; // R_CTL = 23 for - | ||
4494 | // Extended Link_Data: Control Reply | ||
4495 | // TYPE[31-24] 01 Extended Link Service | ||
4496 | // f_ctl[23:0] exchg responder, last seq, e_s, tsi | ||
4497 | CMDfchs->f_ctl = 0x01990000L; | ||
4498 | CMDfchs->seq_cnt = 0x0L; | ||
4499 | CMDfchs->ox_rx_id = 0L; // clear | ||
4500 | CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits | ||
4501 | CMDfchs->ox_rx_id <<= 16; // shift them | ||
4502 | |||
4503 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4504 | |||
4505 | BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]); | ||
4506 | |||
4507 | break; | ||
4508 | |||
4509 | |||
4510 | // Fibre Channel SCSI 'originator' sequences... | ||
4511 | // (originator means 'initiator' in FCP-SCSI) | ||
4512 | |||
4513 | case SCSI_IWE: // TachLite Initiator Write Entry | ||
4514 | { | ||
4515 | PFC_LOGGEDIN_PORT pLoggedInPort = | ||
4516 | Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort; | ||
4517 | |||
4518 | Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1; | ||
4519 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout | ||
4520 | |||
4521 | // first, build FCP_CMND | ||
4522 | // unique X_ID fix-ups in StartExchange | ||
4523 | |||
4524 | *pIRB_flags = 0; // clear IRB flags | ||
4525 | IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index) | ||
4526 | |||
4527 | // NOTE: unlike FC LinkService login frames, normal | ||
4528 | // SCSI commands are sent without outgoing verification | ||
4529 | IRB_flags.DCM = 1; // Disable completion message for Cmnd frame | ||
4530 | SfsLen = *pIRB_flags; | ||
4531 | |||
4532 | SfsLen <<= 24; // shift flags to MSB | ||
4533 | SfsLen += 64L; // add len to LSB (header & CMND payload) | ||
4534 | |||
4535 | CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command | ||
4536 | |||
4537 | // TYPE[31-24] 8 for FCP SCSI | ||
4538 | // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. | ||
4539 | // valid RO | ||
4540 | CMDfchs->f_ctl = 0x08210008L; | ||
4541 | CMDfchs->seq_cnt = 0x0L; | ||
4542 | CMDfchs->ox_rx_id = 0L; // clear for now (-or- in later) | ||
4543 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4544 | |||
4545 | // now, fill out FCP-DATA header | ||
4546 | // (use buffer inside SEST object) | ||
4547 | dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ]; | ||
4548 | dataHDR->reserved = 0L; // must clear | ||
4549 | dataHDR->sof_eof = 0x75002000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS | ||
4550 | dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA | ||
4551 | dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 | ||
4552 | // TYPE[31-24] 8 for FCP SCSI | ||
4553 | // f_ctl[23:0] xfer S.I.| valid RO | ||
4554 | dataHDR->f_ctl = 0x08010008L; | ||
4555 | dataHDR->seq_cnt = 0x02000000L; // sequence ID: df_ctl : seqence count | ||
4556 | dataHDR->ox_rx_id = 0L; // clear; fix-up dataHDR fields later | ||
4557 | dataHDR->ro = 0x0L; // relative offset (n/a) | ||
4558 | |||
4559 | // Now setup the SEST entry | ||
4560 | pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE; | ||
4561 | |||
4562 | // fill out the IWE: | ||
4563 | |||
4564 | // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len | ||
4565 | pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes | ||
4566 | |||
4567 | |||
4568 | // from login parameters with other port, what's the largest frame | ||
4569 | // we can send? | ||
4570 | if( pLoggedInPort == NULL) | ||
4571 | { | ||
4572 | ulStatus = INVALID_ARGS; // failed! give up | ||
4573 | break; | ||
4574 | } | ||
4575 | if( pLoggedInPort->rx_data_size >= 2048) | ||
4576 | fl = 0x00020000; // 2048 code (only support 1024!) | ||
4577 | else if( pLoggedInPort->rx_data_size >= 1024) | ||
4578 | fl = 0x00020000; // 1024 code | ||
4579 | else if( pLoggedInPort->rx_data_size >= 512) | ||
4580 | fl = 0x00010000; // 512 code | ||
4581 | else | ||
4582 | fl = 0; // 128 bytes -- should never happen | ||
4583 | |||
4584 | |||
4585 | pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase | ||
4586 | pIWE->Hdr_Addr = fcChip->SEST->base + | ||
4587 | ((unsigned long)&fcChip->SEST->DataHDR[*fcExchangeIndex] - | ||
4588 | (unsigned long)fcChip->SEST); | ||
4589 | |||
4590 | pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame) | ||
4591 | pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID | ||
4592 | |||
4593 | memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0, | ||
4594 | sizeof( FCP_STATUS_RESPONSE) ); // clear out previous status | ||
4595 | |||
4596 | pIWE->RSP_Addr = fcChip->SEST->base + | ||
4597 | ((unsigned long)&fcChip->SEST->RspHDR[*fcExchangeIndex] - | ||
4598 | (unsigned long)fcChip->SEST); | ||
4599 | |||
4600 | // Do we need local or extended gather list? | ||
4601 | // depends on size - we can handle 3 len/addr pairs | ||
4602 | // locally. | ||
4603 | |||
4604 | fcp_dl = build_SEST_sgList( | ||
4605 | cpqfcHBAdata->PciDev, | ||
4606 | &pIWE->GLen1, | ||
4607 | Cmnd, // S/G list | ||
4608 | &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) | ||
4609 | &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) | ||
4610 | |||
4611 | if( !fcp_dl ) // error building S/G list? | ||
4612 | { | ||
4613 | ulStatus = MEMPOOL_FAIL; | ||
4614 | break; // give up | ||
4615 | } | ||
4616 | |||
4617 | // Now that we know total data length in | ||
4618 | // the passed S/G buffer, set FCP CMND frame | ||
4619 | build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); | ||
4620 | |||
4621 | |||
4622 | |||
4623 | if( sgPairs > 3 ) // need extended s/g list | ||
4624 | pIWE->Buff_Off = 0x78000000L; // extended data | (no offset) | ||
4625 | else // local data pointers (in SEST) | ||
4626 | pIWE->Buff_Off = 0xf8000000L; // local data | (no offset) | ||
4627 | |||
4628 | // ULONG 5 | ||
4629 | pIWE->Link = 0x0000ffffL; // Buff_Index | Link | ||
4630 | |||
4631 | pIWE->RX_ID = 0x0L; // DWord 6: RX_ID set by target XFER_RDY | ||
4632 | |||
4633 | // DWord 7 | ||
4634 | pIWE->Data_Len = 0L; // TL enters rcv'd XFER_RDY BURST_LEN | ||
4635 | pIWE->Exp_RO = 0L; // DWord 8 | ||
4636 | // DWord 9 | ||
4637 | pIWE->Exp_Byte_Cnt = fcp_dl; // sum of gather buffers | ||
4638 | } | ||
4639 | break; | ||
4640 | |||
4641 | |||
4642 | |||
4643 | |||
4644 | |||
4645 | case SCSI_IRE: // TachLite Initiator Read Entry | ||
4646 | |||
4647 | if( Cmnd->timeout != 0) | ||
4648 | { | ||
4649 | // printk("Cmnd->timeout %d\n", Cmnd->timeout); | ||
4650 | // per Linux Scsi | ||
4651 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout; | ||
4652 | } | ||
4653 | else // use our best guess, based on FC & device | ||
4654 | { | ||
4655 | |||
4656 | if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY) | ||
4657 | { | ||
4658 | // turn off our timeouts (for now...) | ||
4659 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF; | ||
4660 | } | ||
4661 | else | ||
4662 | { | ||
4663 | Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1; | ||
4664 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req. | ||
4665 | } | ||
4666 | } | ||
4667 | |||
4668 | |||
4669 | // first, build FCP_CMND | ||
4670 | |||
4671 | |||
4672 | *pIRB_flags = 0; // clear IRB flags | ||
4673 | IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index) | ||
4674 | // NOTE: unlike FC LinkService login frames, | ||
4675 | // normal SCSI commands are sent "open loop" | ||
4676 | IRB_flags.DCM = 1; // Disable completion message for Cmnd frame | ||
4677 | SfsLen = *pIRB_flags; | ||
4678 | |||
4679 | SfsLen <<= 24; // shift flags to MSB | ||
4680 | SfsLen += 64L; // add len to LSB (header & CMND payload) | ||
4681 | |||
4682 | CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command | ||
4683 | |||
4684 | // TYPE[31-24] 8 for FCP SCSI | ||
4685 | // f_ctl[23:0] exchg originator, 1st seq, xfer S.I. | ||
4686 | // valid RO | ||
4687 | CMDfchs->f_ctl = 0x08210008L; | ||
4688 | CMDfchs->seq_cnt = 0x0L; | ||
4689 | // x_ID & data direction bit set later | ||
4690 | CMDfchs->ox_rx_id = 0xFFFF; // clear | ||
4691 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4692 | |||
4693 | |||
4694 | |||
4695 | // Now setup the SEST entry | ||
4696 | pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE; | ||
4697 | |||
4698 | // fill out the IRE: | ||
4699 | // VALid entry:Dir outbound:enable CM:enal INT: | ||
4700 | pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP | ||
4701 | |||
4702 | pIRE->reserved = 0L; | ||
4703 | pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame) | ||
4704 | pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID | ||
4705 | |||
4706 | pIRE->RSP_Addr = fcChip->SEST->base + | ||
4707 | ((unsigned long)&fcChip->SEST->RspHDR[*fcExchangeIndex] - | ||
4708 | (unsigned long)fcChip->SEST); | ||
4709 | |||
4710 | // Do we need local or extended gather list? | ||
4711 | // depends on size - we can handle 3 len/addr pairs | ||
4712 | // locally. | ||
4713 | |||
4714 | fcp_dl = build_SEST_sgList( | ||
4715 | cpqfcHBAdata->PciDev, | ||
4716 | &pIRE->SLen1, | ||
4717 | Cmnd, // SCSI command Data desc. with S/G list | ||
4718 | &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) | ||
4719 | &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) | ||
4720 | |||
4721 | |||
4722 | if( !fcp_dl ) // error building S/G list? | ||
4723 | { | ||
4724 | // It is permissible to have a ZERO LENGTH Read command. | ||
4725 | // If there is the case, simply set fcp_dl (and Exp_Byte_Cnt) | ||
4726 | // to 0 and continue. | ||
4727 | if( Cmnd->request_bufflen == 0 ) | ||
4728 | { | ||
4729 | fcp_dl = 0; // no FC DATA frames expected | ||
4730 | |||
4731 | } | ||
4732 | else | ||
4733 | { | ||
4734 | ulStatus = MEMPOOL_FAIL; | ||
4735 | break; // give up | ||
4736 | } | ||
4737 | } | ||
4738 | |||
4739 | // now that we know the S/G length, build CMND payload | ||
4740 | build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); | ||
4741 | |||
4742 | |||
4743 | if( sgPairs > 3 ) // need extended s/g list | ||
4744 | pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset | ||
4745 | else | ||
4746 | pIRE->Buff_Off = 0x80000000; // local data, no offset | ||
4747 | |||
4748 | pIRE->Buff_Index = 0x0L; // DWord 5: Buff_Index | Reserved | ||
4749 | |||
4750 | pIRE->Exp_RO = 0x0L; // DWord 6: Expected Rel. Offset | ||
4751 | |||
4752 | pIRE->Byte_Count = 0; // DWord 7: filled in by TL on err | ||
4753 | pIRE->reserved_ = 0; // DWord 8: reserved | ||
4754 | // NOTE: 0 length READ is OK. | ||
4755 | pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers | ||
4756 | |||
4757 | break; | ||
4758 | |||
4759 | |||
4760 | |||
4761 | |||
4762 | // Fibre Channel SCSI 'responder' sequences... | ||
4763 | // (originator means 'target' in FCP-SCSI) | ||
4764 | case SCSI_TWE: // TachLite Target Write Entry | ||
4765 | |||
4766 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req. | ||
4767 | |||
4768 | // first, build FCP_CMND | ||
4769 | |||
4770 | *pIRB_flags = 0; // clear IRB flags | ||
4771 | IRB_flags.SFA = 1; // send SFS (XFER_RDY) | ||
4772 | SfsLen = *pIRB_flags; | ||
4773 | |||
4774 | SfsLen <<= 24; // shift flags to MSB | ||
4775 | SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload) | ||
4776 | |||
4777 | CMDfchs->d_id |= (0x05000000L); // R_CTL = 5 for XFER_RDY | ||
4778 | |||
4779 | // TYPE[31-24] 8 for FCP SCSI | ||
4780 | // f_ctl[23:0] exchg responder, 1st seq, xfer S.I. | ||
4781 | // valid RO | ||
4782 | CMDfchs->f_ctl = 0x08810008L; | ||
4783 | CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count | ||
4784 | // use originator (other port's) OX_ID | ||
4785 | CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // we want upper 16 bits | ||
4786 | CMDfchs->ro = 0x0L; // relative offset (n/a) | ||
4787 | |||
4788 | // now, fill out FCP-RSP header | ||
4789 | // (use buffer inside SEST object) | ||
4790 | |||
4791 | rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ]; | ||
4792 | rspHDR->reserved = 0L; // must clear | ||
4793 | rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS | ||
4794 | rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP | ||
4795 | rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 | ||
4796 | // TYPE[31-24] 8 for FCP SCSI | ||
4797 | // f_ctl[23:0] responder|last seq| xfer S.I. | ||
4798 | rspHDR->f_ctl = 0x08910000L; | ||
4799 | rspHDR->seq_cnt = 0x03000000; // sequence ID | ||
4800 | rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID | ||
4801 | rspHDR->ro = 0x0L; // relative offset (n/a) | ||
4802 | |||
4803 | |||
4804 | // Now setup the SEST entry | ||
4805 | |||
4806 | pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE; | ||
4807 | |||
4808 | // fill out the TWE: | ||
4809 | |||
4810 | // VALid entry:Dir outbound:enable CM:enal INT: | ||
4811 | pTWE->Seq_Accum = 0xC4000000L; // upper word flags | ||
4812 | pTWE->reserved = 0L; | ||
4813 | pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change) | ||
4814 | pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID | ||
4815 | |||
4816 | |||
4817 | // Do we need local or extended gather list? | ||
4818 | // depends on size - we can handle 3 len/addr pairs | ||
4819 | // locally. | ||
4820 | |||
4821 | fcp_dl = build_SEST_sgList( | ||
4822 | cpqfcHBAdata->PciDev, | ||
4823 | &pTWE->SLen1, | ||
4824 | Cmnd, // S/G list | ||
4825 | &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) | ||
4826 | &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) | ||
4827 | |||
4828 | |||
4829 | if( !fcp_dl ) // error building S/G list? | ||
4830 | { | ||
4831 | ulStatus = MEMPOOL_FAIL; | ||
4832 | break; // give up | ||
4833 | } | ||
4834 | |||
4835 | // now that we know the S/G length, build CMND payload | ||
4836 | build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl ); | ||
4837 | |||
4838 | |||
4839 | if( sgPairs > 3 ) // need extended s/g list | ||
4840 | pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset | ||
4841 | else | ||
4842 | pTWE->Buff_Off = 0x80000000; // local data, no offset | ||
4843 | |||
4844 | pTWE->Buff_Index = 0; // Buff_Index | Link | ||
4845 | pTWE->Exp_RO = 0; | ||
4846 | pTWE->Byte_Count = 0; // filled in by TL on err | ||
4847 | pTWE->reserved_ = 0; | ||
4848 | pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers | ||
4849 | |||
4850 | break; | ||
4851 | |||
4852 | |||
4853 | |||
4854 | |||
4855 | |||
4856 | |||
4857 | case SCSI_TRE: // TachLite Target Read Entry | ||
4858 | |||
4859 | // It doesn't make much sense for us to "time-out" a READ, | ||
4860 | // but we'll use it for design consistency and internal error recovery. | ||
4861 | Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req. | ||
4862 | |||
4863 | // I/O request block settings... | ||
4864 | *pIRB_flags = 0; // clear IRB flags | ||
4865 | // check PRLI (process login) info | ||
4866 | // to see if Initiator Requires XFER_RDY | ||
4867 | // if not, don't send one! | ||
4868 | // { PRLI check...} | ||
4869 | IRB_flags.SFA = 0; // don't send XFER_RDY - start data | ||
4870 | SfsLen = *pIRB_flags; | ||
4871 | |||
4872 | SfsLen <<= 24; // shift flags to MSB | ||
4873 | SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload) | ||
4874 | |||
4875 | |||
4876 | |||
4877 | // now, fill out FCP-DATA header | ||
4878 | // (use buffer inside SEST object) | ||
4879 | dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ]; | ||
4880 | |||
4881 | dataHDR->reserved = 0L; // must clear | ||
4882 | dataHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS,noLCr,no TS | ||
4883 | dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA | ||
4884 | dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 | ||
4885 | |||
4886 | |||
4887 | // TYPE[31-24] 8 for FCP SCSI | ||
4888 | // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I. | ||
4889 | // valid RO | ||
4890 | dataHDR->f_ctl = 0x08810008L; | ||
4891 | dataHDR->seq_cnt = 0x01000000; // sequence ID (no XRDY) | ||
4892 | dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits | ||
4893 | dataHDR->ro = 0x0L; // relative offset (n/a) | ||
4894 | |||
4895 | // now, fill out FCP-RSP header | ||
4896 | // (use buffer inside SEST object) | ||
4897 | rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ]; | ||
4898 | |||
4899 | rspHDR->reserved = 0L; // must clear | ||
4900 | rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS | ||
4901 | rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP | ||
4902 | rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0 | ||
4903 | // TYPE[31-24] 8 for FCP SCSI | ||
4904 | // f_ctl[23:0] responder|last seq| xfer S.I. | ||
4905 | rspHDR->f_ctl = 0x08910000L; | ||
4906 | rspHDR->seq_cnt = 0x02000000; // sequence ID: df_ctl: sequence count | ||
4907 | |||
4908 | rspHDR->ro = 0x0L; // relative offset (n/a) | ||
4909 | |||
4910 | |||
4911 | // Now setup the SEST entry | ||
4912 | pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE; | ||
4913 | |||
4914 | |||
4915 | // VALid entry:Dir outbound:enable CM:enal INT: | ||
4916 | pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes | ||
4917 | pTRE->Hdr_Addr = // bus address of dataHDR; | ||
4918 | fcChip->SEST->base + | ||
4919 | ((unsigned long)&fcChip->SEST->DataHDR[ *fcExchangeIndex ] - | ||
4920 | (unsigned long)fcChip->SEST); | ||
4921 | |||
4922 | pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame) | ||
4923 | pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID | ||
4924 | pTRE->RSP_Addr = // bus address of rspHDR | ||
4925 | fcChip->SEST->base + | ||
4926 | ((unsigned long)&fcChip->SEST->RspHDR[ *fcExchangeIndex ] - | ||
4927 | (unsigned long)fcChip->SEST); | ||
4928 | |||
4929 | // Do we need local or extended gather list? | ||
4930 | // depends on size - we can handle 3 len/addr pairs | ||
4931 | // locally. | ||
4932 | |||
4933 | fcp_dl = build_SEST_sgList( | ||
4934 | cpqfcHBAdata->PciDev, | ||
4935 | &pTRE->GLen1, | ||
4936 | Cmnd, // S/G list | ||
4937 | &sgPairs, // return # of pairs in S/G list (from "Data" descriptor) | ||
4938 | &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later) | ||
4939 | |||
4940 | |||
4941 | if( !fcp_dl ) // error building S/G list? | ||
4942 | { | ||
4943 | ulStatus = MEMPOOL_FAIL; | ||
4944 | break; // give up | ||
4945 | } | ||
4946 | |||
4947 | // no payload or command to build -- READ doesn't need XRDY | ||
4948 | |||
4949 | |||
4950 | if( sgPairs > 3 ) // need extended s/g list | ||
4951 | pTRE->Buff_Off = 0x78000000L; // extended data | (no offset) | ||
4952 | else // local data pointers (in SEST) | ||
4953 | pTRE->Buff_Off = 0xf8000000L; // local data | (no offset) | ||
4954 | |||
4955 | // ULONG 5 | ||
4956 | pTRE->Buff_Index = 0L; // Buff_Index | reserved | ||
4957 | pTRE->reserved = 0x0L; // DWord 6 | ||
4958 | |||
4959 | // DWord 7: NOTE: zero length will | ||
4960 | // hang TachLite! | ||
4961 | pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers | ||
4962 | |||
4963 | pTRE->reserved_ = 0L; // DWord 8 | ||
4964 | pTRE->reserved__ = 0L; // DWord 9 | ||
4965 | |||
4966 | break; | ||
4967 | |||
4968 | |||
4969 | |||
4970 | |||
4971 | |||
4972 | |||
4973 | |||
4974 | case FCP_RESPONSE: | ||
4975 | // Target response frame: this sequence uses an OX/RX ID | ||
4976 | // pair from a completed SEST exchange. We built most | ||
4977 | // of the response frame when we created the TWE/TRE. | ||
4978 | |||
4979 | *pIRB_flags = 0; // clear IRB flags | ||
4980 | IRB_flags.SFA = 1; // send SFS (RSP) | ||
4981 | SfsLen = *pIRB_flags; | ||
4982 | |||
4983 | SfsLen <<= 24; // shift flags to MSB | ||
4984 | SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload) | ||
4985 | |||
4986 | |||
4987 | Exchanges->fcExchange[ *fcExchangeIndex].type = | ||
4988 | FCP_RESPONSE; // change Exchange type to "response" phase | ||
4989 | |||
4990 | // take advantage of prior knowledge of OX/RX_ID pair from | ||
4991 | // previous XFER outbound frame (still in fchs of exchange) | ||
4992 | fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id = | ||
4993 | CMDfchs->ox_rx_id; | ||
4994 | |||
4995 | // Check the status of the DATA phase of the exchange so we can report | ||
4996 | // status to the initiator | ||
4997 | buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields | ||
4998 | |||
4999 | memcpy( | ||
5000 | CMDfchs, // re-use same XFER fchs for Response frame | ||
5001 | &fcChip->SEST->RspHDR[ *fcExchangeIndex ], | ||
5002 | sizeof( TachFCHDR_RSP )); | ||
5003 | |||
5004 | |||
5005 | break; | ||
5006 | |||
5007 | default: | ||
5008 | printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type); | ||
5009 | break; | ||
5010 | |||
5011 | } | ||
5012 | |||
5013 | |||
5014 | |||
5015 | if( !ulStatus) // no errors above? | ||
5016 | { | ||
5017 | // FCHS is built; now build IRB | ||
5018 | |||
5019 | // link the just built FCHS (the "command") to the IRB entry | ||
5020 | // for this Exchange. | ||
5021 | pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB; | ||
5022 | |||
5023 | // len & flags according to command type above | ||
5024 | pIRB->Req_A_SFS_Len = SfsLen; // includes IRB flags & len | ||
5025 | pIRB->Req_A_SFS_Addr = // TL needs physical addr of frame to send | ||
5026 | fcChip->exch_dma_handle + (unsigned long)CMDfchs - | ||
5027 | (unsigned long)Exchanges; | ||
5028 | |||
5029 | pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent! | ||
5030 | |||
5031 | // Exchange is complete except for "fix-up" fields to be set | ||
5032 | // at Tachyon Queuing time: | ||
5033 | // IRB->Req_A_Trans_ID (OX_ID/ RX_ID): | ||
5034 | // for SEST entry, lower bits correspond to actual FC Exchange ID | ||
5035 | // fchs->OX_ID or RX_ID | ||
5036 | } | ||
5037 | else | ||
5038 | { | ||
5039 | #ifdef DBG | ||
5040 | printk( "FC Error: SEST build Pool Allocation failed\n"); | ||
5041 | #endif | ||
5042 | // return resources... | ||
5043 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, *fcExchangeIndex); // SEST build failed | ||
5044 | } | ||
5045 | } | ||
5046 | else // no Exchanges available | ||
5047 | { | ||
5048 | ulStatus = SEST_FULL; | ||
5049 | printk( "FC Error: no fcExchanges available\n"); | ||
5050 | } | ||
5051 | return ulStatus; | ||
5052 | } | ||
5053 | |||
5054 | |||
5055 | |||
5056 | |||
5057 | |||
5058 | |||
5059 | // set RSP payload fields | ||
5060 | static void buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID) | ||
5061 | { | ||
5062 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
5063 | FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID]; // shorthand | ||
5064 | PFCP_STATUS_RESPONSE pFcpStatus; | ||
5065 | |||
5066 | memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0, | ||
5067 | sizeof( FCP_STATUS_RESPONSE) ); | ||
5068 | if( pExchange->status ) // something wrong? | ||
5069 | { | ||
5070 | pFcpStatus = (PFCP_STATUS_RESPONSE) // cast RSP buffer for this xchng | ||
5071 | &fcChip->SEST->RspHDR[ ExchangeID ].pl; | ||
5072 | if( pExchange->status & COUNT_ERROR ) | ||
5073 | { | ||
5074 | |||
5075 | // set FCP response len valid (so we can report count error) | ||
5076 | pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID; | ||
5077 | pFcpStatus->fcp_rsp_len = 0x04000000; // 4 byte len (BIG Endian) | ||
5078 | |||
5079 | pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE | ||
5080 | } | ||
5081 | } | ||
5082 | } | ||
5083 | |||
5084 | |||
5085 | static dma_addr_t | ||
5086 | cpqfc_pci_map_sg_page( | ||
5087 | struct pci_dev *pcidev, | ||
5088 | ULONG *hw_paddr, // where to put phys addr for HW use | ||
5089 | void *sgp_vaddr, // the virtual address of the sg page | ||
5090 | dma_addr_t *umap_paddr, // where to put phys addr for unmap | ||
5091 | unsigned int *maplen, // where to store sg entry length | ||
5092 | int PairCount) // number of sg pairs used in the page. | ||
5093 | { | ||
5094 | unsigned long aligned_addr = (unsigned long) sgp_vaddr; | ||
5095 | |||
5096 | *maplen = PairCount * 8; | ||
5097 | aligned_addr += TL_EXT_SG_PAGE_BYTELEN; | ||
5098 | aligned_addr &= ~(TL_EXT_SG_PAGE_BYTELEN -1); | ||
5099 | |||
5100 | *umap_paddr = pci_map_single(pcidev, (void *) aligned_addr, | ||
5101 | *maplen, PCI_DMA_TODEVICE); | ||
5102 | *hw_paddr = (ULONG) *umap_paddr; | ||
5103 | |||
5104 | # if BITS_PER_LONG > 32 | ||
5105 | if( *umap_paddr >>32 ) { | ||
5106 | printk("cqpfcTS:Tach SG DMA addr %p>32 bits\n", | ||
5107 | (void*)umap_paddr); | ||
5108 | return 0; | ||
5109 | } | ||
5110 | # endif | ||
5111 | return *umap_paddr; | ||
5112 | } | ||
5113 | |||
5114 | static void | ||
5115 | cpqfc_undo_SEST_mappings(struct pci_dev *pcidev, | ||
5116 | unsigned long contigaddr, int len, int dir, | ||
5117 | struct scatterlist *sgl, int use_sg, | ||
5118 | PSGPAGES *sgPages_head, | ||
5119 | int allocated_pages) | ||
5120 | { | ||
5121 | PSGPAGES i, next; | ||
5122 | |||
5123 | if (contigaddr != (unsigned long) NULL) | ||
5124 | pci_unmap_single(pcidev, contigaddr, len, dir); | ||
5125 | |||
5126 | if (sgl != NULL) | ||
5127 | pci_unmap_sg(pcidev, sgl, use_sg, dir); | ||
5128 | |||
5129 | for (i=*sgPages_head; i != NULL ;i = next) | ||
5130 | { | ||
5131 | pci_unmap_single(pcidev, i->busaddr, i->maplen, | ||
5132 | PCI_DMA_TODEVICE); | ||
5133 | i->busaddr = (dma_addr_t) NULL; | ||
5134 | i->maplen = 0L; | ||
5135 | next = i->next; | ||
5136 | kfree(i); | ||
5137 | } | ||
5138 | *sgPages_head = NULL; | ||
5139 | } | ||
5140 | |||
5141 | // This routine builds scatter/gather lists into SEST entries | ||
5142 | // INPUTS: | ||
5143 | // SESTalPair - SEST address @DWordA "Local Buffer Length" | ||
5144 | // sgList - Scatter/Gather linked list of Len/Address data buffers | ||
5145 | // OUTPUT: | ||
5146 | // sgPairs - number of valid address/length pairs | ||
5147 | // Remarks: | ||
5148 | // The SEST data buffer pointers only depend on number of | ||
5149 | // length/ address pairs, NOT on the type (IWE, TRE,...) | ||
5150 | // Up to 3 pairs can be referenced in the SEST - more than 3 | ||
5151 | // require this Extended S/G list page. The page holds 4, 8, 16... | ||
5152 | // len/addr pairs, per Scatter/Gather List Page Length Reg. | ||
5153 | // TachLite allows pages to be linked to any depth. | ||
5154 | |||
5155 | //#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages | ||
5156 | |||
5157 | static int ap_hi_water = TL_DANGER_SGPAGES; | ||
5158 | |||
5159 | static ULONG build_SEST_sgList( | ||
5160 | struct pci_dev *pcidev, | ||
5161 | ULONG *SESTalPairStart, // the 3 len/address buffers in SEST | ||
5162 | Scsi_Cmnd *Cmnd, | ||
5163 | ULONG *sgPairs, | ||
5164 | PSGPAGES *sgPages_head) // link list of TL Ext. S/G pages from O/S Pool | ||
5165 | |||
5166 | { | ||
5167 | ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations | ||
5168 | ULONG* alPair = SESTalPairStart; | ||
5169 | ULONG* ext_sg_page_phys_addr_place = NULL; | ||
5170 | int PairCount; | ||
5171 | unsigned long ulBuff, contigaddr; | ||
5172 | ULONG total_data_len=0; // (in bytes) | ||
5173 | ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum) | ||
5174 | ULONG thisMappingLen; | ||
5175 | struct scatterlist *sgl = NULL; // S/G list (Linux format) | ||
5176 | int sg_count, totalsgs; | ||
5177 | dma_addr_t busaddr; | ||
5178 | unsigned long thislen, offset; | ||
5179 | PSGPAGES *sgpage = sgPages_head; | ||
5180 | PSGPAGES prev_page = NULL; | ||
5181 | |||
5182 | # define WE_HAVE_SG_LIST (sgl != (unsigned long) NULL) | ||
5183 | contigaddr = (unsigned long) NULL; | ||
5184 | |||
5185 | if( !Cmnd->use_sg ) // no S/G list? | ||
5186 | { | ||
5187 | if (bytes_to_go <= TL_MAX_SG_ELEM_LEN) | ||
5188 | { | ||
5189 | *sgPairs = 1; // use "local" S/G pair in SEST entry | ||
5190 | // (for now, ignore address bits above #31) | ||
5191 | |||
5192 | *alPair++ = bytes_to_go; // bits 18-0, length | ||
5193 | |||
5194 | if (bytes_to_go != 0) { | ||
5195 | contigaddr = ulBuff = pci_map_single(pcidev, | ||
5196 | Cmnd->request_buffer, | ||
5197 | Cmnd->request_bufflen, | ||
5198 | Cmnd->sc_data_direction); | ||
5199 | // printk("ms %p ", ulBuff); | ||
5200 | } | ||
5201 | else { | ||
5202 | // No data transfer, (e.g.: Test Unit Ready) | ||
5203 | // printk("btg=0 "); | ||
5204 | *sgPairs = 0; | ||
5205 | memset(alPair, 0, sizeof(*alPair)); | ||
5206 | return 0; | ||
5207 | } | ||
5208 | |||
5209 | # if BITS_PER_LONG > 32 | ||
5210 | if( ulBuff >>32 ) { | ||
5211 | printk("FATAL! Tachyon DMA address %p " | ||
5212 | "exceeds 32 bits\n", (void*)ulBuff ); | ||
5213 | return 0; | ||
5214 | } | ||
5215 | # endif | ||
5216 | *alPair = (ULONG)ulBuff; | ||
5217 | return bytes_to_go; | ||
5218 | } | ||
5219 | else // We have a single large (too big) contiguous buffer. | ||
5220 | { // We will have to break it up. We'll use the scatter | ||
5221 | // gather code way below, but use contigaddr instead | ||
5222 | // of sg_dma_addr(). (this is a very rare case). | ||
5223 | |||
5224 | unsigned long btg; | ||
5225 | contigaddr = pci_map_single(pcidev, Cmnd->request_buffer, | ||
5226 | Cmnd->request_bufflen, | ||
5227 | Cmnd->sc_data_direction); | ||
5228 | |||
5229 | // printk("contigaddr = %p, len = %d\n", | ||
5230 | // (void *) contigaddr, bytes_to_go); | ||
5231 | totalsgs = 0; | ||
5232 | for (btg = bytes_to_go; btg > 0; ) { | ||
5233 | btg -= ( btg > TL_MAX_SG_ELEM_LEN ? | ||
5234 | TL_MAX_SG_ELEM_LEN : btg ); | ||
5235 | totalsgs++; | ||
5236 | } | ||
5237 | sgl = NULL; | ||
5238 | *sgPairs = totalsgs; | ||
5239 | } | ||
5240 | } | ||
5241 | else // we do have a scatter gather list | ||
5242 | { | ||
5243 | // [TBD - update for Linux to support > 32 bits addressing] | ||
5244 | // since the format for local & extended S/G lists is different, | ||
5245 | // check if S/G pairs exceeds 3. | ||
5246 | // *sgPairs = Cmnd->use_sg; Nope, that's wrong. | ||
5247 | |||
5248 | sgl = (struct scatterlist*)Cmnd->request_buffer; | ||
5249 | sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg, | ||
5250 | Cmnd->sc_data_direction); | ||
5251 | if( sg_count <= 3 ) { | ||
5252 | |||
5253 | // we need to be careful here that no individual mapping | ||
5254 | // is too large, and if any is, that breaking it up | ||
5255 | // doesn't push us over 3 sgs, or, if it does, that we | ||
5256 | // handle that case. Tachyon can take 0x7FFFF bits for length, | ||
5257 | // but sg structure uses "unsigned int", on the face of it, | ||
5258 | // up to 0xFFFFFFFF or even more. | ||
5259 | |||
5260 | int i; | ||
5261 | unsigned long thislen; | ||
5262 | |||
5263 | totalsgs = 0; | ||
5264 | for (i=0;i<sg_count;i++) { | ||
5265 | thislen = sg_dma_len(&sgl[i]); | ||
5266 | while (thislen >= TL_MAX_SG_ELEM_LEN) { | ||
5267 | totalsgs++; | ||
5268 | thislen -= TL_MAX_SG_ELEM_LEN; | ||
5269 | } | ||
5270 | if (thislen > 0) totalsgs++; | ||
5271 | } | ||
5272 | *sgPairs = totalsgs; | ||
5273 | } else totalsgs = 999; // as a first estimate, definitely >3, | ||
5274 | |||
5275 | // if (totalsgs != sg_count) | ||
5276 | // printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count); | ||
5277 | } | ||
5278 | |||
5279 | if( totalsgs <= 3 ) // can (must) use "local" SEST list | ||
5280 | { | ||
5281 | while( bytes_to_go) | ||
5282 | { | ||
5283 | offset = 0L; | ||
5284 | |||
5285 | if ( WE_HAVE_SG_LIST ) | ||
5286 | thisMappingLen = sg_dma_len(sgl); | ||
5287 | else // or contiguous buffer? | ||
5288 | thisMappingLen = bytes_to_go; | ||
5289 | |||
5290 | while (thisMappingLen > 0) | ||
5291 | { | ||
5292 | thislen = thisMappingLen > TL_MAX_SG_ELEM_LEN ? | ||
5293 | TL_MAX_SG_ELEM_LEN : thisMappingLen; | ||
5294 | bytes_to_go = bytes_to_go - thislen; | ||
5295 | |||
5296 | // we have L/A pair; L = thislen, A = physicalAddress | ||
5297 | // load into SEST... | ||
5298 | |||
5299 | total_data_len += thislen; | ||
5300 | *alPair = thislen; // bits 18-0, length | ||
5301 | |||
5302 | alPair++; | ||
5303 | |||
5304 | if ( WE_HAVE_SG_LIST ) | ||
5305 | ulBuff = sg_dma_address(sgl) + offset; | ||
5306 | else | ||
5307 | ulBuff = contigaddr + offset; | ||
5308 | |||
5309 | offset += thislen; | ||
5310 | |||
5311 | # if BITS_PER_LONG > 32 | ||
5312 | if( ulBuff >>32 ) { | ||
5313 | printk("cqpfcTS: 2Tach DMA address %p > 32 bits\n", | ||
5314 | (void*)ulBuff ); | ||
5315 | printk("%s = %p, offset = %ld\n", | ||
5316 | WE_HAVE_SG_LIST ? "ulBuff" : "contigaddr", | ||
5317 | WE_HAVE_SG_LIST ? (void *) ulBuff : (void *) contigaddr, | ||
5318 | offset); | ||
5319 | return 0; | ||
5320 | } | ||
5321 | # endif | ||
5322 | *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0) | ||
5323 | thisMappingLen -= thislen; | ||
5324 | } | ||
5325 | |||
5326 | if ( WE_HAVE_SG_LIST ) ++sgl; // next S/G pair | ||
5327 | else if (bytes_to_go != 0) printk("BTG not zero!\n"); | ||
5328 | |||
5329 | # ifdef DBG_SEST_SGLIST | ||
5330 | printk("L=%d ", thisMappingLen); | ||
5331 | printk("btg=%d ", bytes_to_go); | ||
5332 | # endif | ||
5333 | |||
5334 | } | ||
5335 | // printk("i:%d\n", *sgPairs); | ||
5336 | } | ||
5337 | else // more than 3 pairs requires Extended S/G page (Pool Allocation) | ||
5338 | { | ||
5339 | // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic) | ||
5340 | for( i=2; i<6; i++) | ||
5341 | alPair[i] = 0; | ||
5342 | |||
5343 | PairCount = TL_EXT_SG_PAGE_COUNT; // forces initial page allocation | ||
5344 | totalsgs = 0; | ||
5345 | while( bytes_to_go ) | ||
5346 | { | ||
5347 | // Per SEST format, we can support 524287 byte lengths per | ||
5348 | // S/G pair. Typical user buffers are 4k, and very rarely | ||
5349 | // exceed 12k due to fragmentation of physical memory pages. | ||
5350 | // However, on certain O/S system (not "user") buffers (on platforms | ||
5351 | // with huge memories), it's possible to exceed this | ||
5352 | // length in a single S/G address/len mapping, so we have to handle | ||
5353 | // that. | ||
5354 | |||
5355 | offset = 0L; | ||
5356 | if ( WE_HAVE_SG_LIST ) | ||
5357 | thisMappingLen = sg_dma_len(sgl); | ||
5358 | else | ||
5359 | thisMappingLen = bytes_to_go; | ||
5360 | |||
5361 | while (thisMappingLen > 0) | ||
5362 | { | ||
5363 | thislen = thisMappingLen > TL_MAX_SG_ELEM_LEN ? | ||
5364 | TL_MAX_SG_ELEM_LEN : thisMappingLen; | ||
5365 | // printk("%d/%d/%d\n", thislen, thisMappingLen, bytes_to_go); | ||
5366 | |||
5367 | // should we load into "this" extended S/G page, or allocate | ||
5368 | // new page? | ||
5369 | |||
5370 | if( PairCount >= TL_EXT_SG_PAGE_COUNT ) | ||
5371 | { | ||
5372 | // Now, we have to map the previous page, (triggering buffer bounce) | ||
5373 | // The first time thru the loop, there won't be a previous page. | ||
5374 | if (prev_page != NULL) // is there a prev page? | ||
5375 | { | ||
5376 | // this code is normally kind of hard to trigger, | ||
5377 | // you have to use up more than 256 scatter gather | ||
5378 | // elements to get here. Cranking down TL_MAX_SG_ELEM_LEN | ||
5379 | // to an absurdly low value (128 bytes or so) to artificially | ||
5380 | // break i/o's into a zillion pieces is how I tested it. | ||
5381 | busaddr = cpqfc_pci_map_sg_page(pcidev, | ||
5382 | ext_sg_page_phys_addr_place, | ||
5383 | prev_page->page, | ||
5384 | &prev_page->busaddr, | ||
5385 | &prev_page->maplen, | ||
5386 | PairCount); | ||
5387 | } | ||
5388 | // Allocate the TL Extended S/G list page. We have | ||
5389 | // to allocate twice what we want to ensure required TL alignment | ||
5390 | // (Tachlite TL/TS User Man. Rev 6.0, p 168) | ||
5391 | // We store the original allocated PVOID so we can free later | ||
5392 | *sgpage = kmalloc( sizeof(SGPAGES), GFP_ATOMIC); | ||
5393 | if ( ! *sgpage ) | ||
5394 | { | ||
5395 | printk("cpqfc: Allocation failed @ %d S/G page allocations\n", | ||
5396 | AllocatedPages); | ||
5397 | total_data_len = 0; // failure!! Ext. S/G is All-or-none affair | ||
5398 | |||
5399 | // unmap the previous mappings, if any. | ||
5400 | |||
5401 | cpqfc_undo_SEST_mappings(pcidev, contigaddr, | ||
5402 | Cmnd->request_bufflen, | ||
5403 | Cmnd->sc_data_direction, | ||
5404 | sgl, Cmnd->use_sg, sgPages_head, AllocatedPages+1); | ||
5405 | |||
5406 | // FIXME: testing shows that if we get here, | ||
5407 | // it's bad news. (this has been this way for a long | ||
5408 | // time though, AFAIK. Not that that excuses it.) | ||
5409 | |||
5410 | return 0; // give up (and probably hang the system) | ||
5411 | } | ||
5412 | // clear out memory we just allocated | ||
5413 | memset( (*sgpage)->page,0,TL_EXT_SG_PAGE_BYTELEN*2); | ||
5414 | (*sgpage)->next = NULL; | ||
5415 | (*sgpage)->busaddr = (dma_addr_t) NULL; | ||
5416 | (*sgpage)->maplen = 0L; | ||
5417 | |||
5418 | // align the memory - TL requires sizeof() Ext. S/G page alignment. | ||
5419 | // We doubled the actual required size so we could mask off LSBs | ||
5420 | // to get desired offset | ||
5421 | |||
5422 | ulBuff = (unsigned long) (*sgpage)->page; | ||
5423 | ulBuff += TL_EXT_SG_PAGE_BYTELEN; | ||
5424 | ulBuff &= ~(TL_EXT_SG_PAGE_BYTELEN -1); | ||
5425 | |||
5426 | // set pointer, in SEST if first Ext. S/G page, or in last pair | ||
5427 | // of linked Ext. S/G pages... (Only 32-bit PVOIDs, so just | ||
5428 | // load lower 32 bits) | ||
5429 | // NOTE: the Len field must be '0' if this is the first Ext. S/G | ||
5430 | // pointer in SEST, and not 0 otherwise (we know thislen != 0). | ||
5431 | |||
5432 | *alPair = (alPair != SESTalPairStart) ? thislen : 0; | ||
5433 | |||
5434 | # ifdef DBG_SEST_SGLIST | ||
5435 | printk("PairCount %d @%p even %Xh, ", | ||
5436 | PairCount, alPair, *alPair); | ||
5437 | # endif | ||
5438 | |||
5439 | // Save the place where we need to store the physical | ||
5440 | // address of this scatter gather page which we get when we map it | ||
5441 | // (and mapping we can do only after we fill it in.) | ||
5442 | alPair++; // next DWORD, will contain phys addr of the ext page | ||
5443 | ext_sg_page_phys_addr_place = alPair; | ||
5444 | |||
5445 | // Now, set alPair = the virtual addr of the (Extended) S/G page | ||
5446 | // which will accept the Len/ PhysicalAddress pairs | ||
5447 | alPair = (ULONG *) ulBuff; | ||
5448 | |||
5449 | AllocatedPages++; | ||
5450 | if (AllocatedPages >= ap_hi_water) | ||
5451 | { | ||
5452 | // This message should rarely, if ever, come out. | ||
5453 | // Previously (cpqfc version <= 2.0.5) the driver would | ||
5454 | // just puke if more than 4 SG pages were used, and nobody | ||
5455 | // ever complained about that. This only comes out if | ||
5456 | // more than 8 pages are used. | ||
5457 | |||
5458 | printk(KERN_WARNING | ||
5459 | "cpqfc: Possible danger. %d scatter gather pages used.\n" | ||
5460 | "cpqfc: detected seemingly extreme memory " | ||
5461 | "fragmentation or huge data transfers.\n", | ||
5462 | AllocatedPages); | ||
5463 | ap_hi_water = AllocatedPages+1; | ||
5464 | } | ||
5465 | |||
5466 | PairCount = 1; // starting new Ext. S/G page | ||
5467 | prev_page = (*sgpage); // remember this page, for next time thru | ||
5468 | sgpage = &((*sgpage)->next); | ||
5469 | } // end of new TL Ext. S/G page allocation | ||
5470 | |||
5471 | *alPair = thislen; // bits 18-0, length (range check above) | ||
5472 | |||
5473 | # ifdef DBG_SEST_SGLIST | ||
5474 | printk("PairCount %d @%p, even %Xh, ", PairCount, alPair, *alPair); | ||
5475 | # endif | ||
5476 | |||
5477 | alPair++; // next DWORD, physical address | ||
5478 | |||
5479 | if ( WE_HAVE_SG_LIST ) | ||
5480 | ulBuff = sg_dma_address(sgl) + offset; | ||
5481 | else | ||
5482 | ulBuff = contigaddr + offset; | ||
5483 | offset += thislen; | ||
5484 | |||
5485 | # if BITS_PER_LONG > 32 | ||
5486 | if( ulBuff >>32 ) | ||
5487 | { | ||
5488 | printk("cqpfcTS: 1Tach DMA address %p > 32 bits\n", (void*)ulBuff ); | ||
5489 | printk("%s = %p, offset = %ld\n", | ||
5490 | WE_HAVE_SG_LIST ? "ulBuff" : "contigaddr", | ||
5491 | WE_HAVE_SG_LIST ? (void *) ulBuff : (void *) contigaddr, | ||
5492 | offset); | ||
5493 | return 0; | ||
5494 | } | ||
5495 | # endif | ||
5496 | |||
5497 | *alPair = (ULONG) ulBuff; // lower 32 bits (31-0) | ||
5498 | |||
5499 | # ifdef DBG_SEST_SGLIST | ||
5500 | printk("odd %Xh\n", *alPair); | ||
5501 | # endif | ||
5502 | alPair++; // next DWORD, next address/length pair | ||
5503 | |||
5504 | PairCount++; // next Length/Address pair | ||
5505 | |||
5506 | // if (PairCount > pc_hi_water) | ||
5507 | // { | ||
5508 | // printk("pc hi = %d ", PairCount); | ||
5509 | // pc_hi_water = PairCount; | ||
5510 | // } | ||
5511 | bytes_to_go -= thislen; | ||
5512 | total_data_len += thislen; | ||
5513 | thisMappingLen -= thislen; | ||
5514 | totalsgs++; | ||
5515 | } // while (thisMappingLen > 0) | ||
5516 | if ( WE_HAVE_SG_LIST ) sgl++; // next S/G pair | ||
5517 | } // while (bytes_to_go) | ||
5518 | |||
5519 | // printk("Totalsgs=%d\n", totalsgs); | ||
5520 | *sgPairs = totalsgs; | ||
5521 | |||
5522 | // PCI map (and bounce) the last (and usually only) extended SG page | ||
5523 | busaddr = cpqfc_pci_map_sg_page(pcidev, | ||
5524 | ext_sg_page_phys_addr_place, | ||
5525 | prev_page->page, | ||
5526 | &prev_page->busaddr, | ||
5527 | &prev_page->maplen, | ||
5528 | PairCount); | ||
5529 | } | ||
5530 | return total_data_len; | ||
5531 | } | ||
5532 | |||
5533 | |||
5534 | |||
5535 | // The Tachlite SEST table is referenced to OX_ID (or RX_ID). To optimize | ||
5536 | // performance and debuggability, we index the Exchange structure to FC X_ID | ||
5537 | // This enables us to build exchanges for later en-queing to Tachyon, | ||
5538 | // provided we have an open X_ID slot. At Tachyon queing time, we only | ||
5539 | // need an ERQ slot; then "fix-up" references in the | ||
5540 | // IRB, FCHS, etc. as needed. | ||
5541 | // RETURNS: | ||
5542 | // 0 if successful | ||
5543 | // non-zero on error | ||
5544 | //sstartex | ||
5545 | ULONG cpqfcTSStartExchange( | ||
5546 | CPQFCHBA *cpqfcHBAdata, | ||
5547 | LONG ExchangeID ) | ||
5548 | { | ||
5549 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | ||
5550 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
5551 | FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand | ||
5552 | USHORT producer, consumer; | ||
5553 | ULONG ulStatus=0; | ||
5554 | short int ErqIndex; | ||
5555 | BOOLEAN CompleteExchange = FALSE; // e.g. ACC replies are complete | ||
5556 | BOOLEAN SestType=FALSE; | ||
5557 | ULONG InboundData=0; | ||
5558 | |||
5559 | // We will manipulate Tachlite chip registers here to successfully | ||
5560 | // start exchanges. | ||
5561 | |||
5562 | // Check that link is not down -- we can't start an exchange on a | ||
5563 | // down link! | ||
5564 | |||
5565 | if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline? | ||
5566 | { | ||
5567 | printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n", | ||
5568 | fcChip->Registers.FMstatus.value & 0xFF, | ||
5569 | ExchangeID, | ||
5570 | pExchange->type, | ||
5571 | pExchange->fchs.d_id); | ||
5572 | |||
5573 | if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame? | ||
5574 | { | ||
5575 | // Our most popular LinkService commands are port discovery types | ||
5576 | // (PLOGI/ PDISC...), which are implicitly nullified by Link Down | ||
5577 | // events, so it makes no sense to Que them. However, ABTS should | ||
5578 | // be queued, since exchange sequences are likely destroyed by | ||
5579 | // Link Down events, and we want to notify other ports of broken | ||
5580 | // sequences by aborting the corresponding exchanges. | ||
5581 | if( pExchange->type != BLS_ABTS ) | ||
5582 | { | ||
5583 | ulStatus = LNKDWN_OSLS; | ||
5584 | goto Done; | ||
5585 | // don't Que most LinkServ exchanges on LINK DOWN | ||
5586 | } | ||
5587 | } | ||
5588 | |||
5589 | printk("fcStartExchange: Que x_ID %Xh, type %Xh\n", | ||
5590 | ExchangeID, pExchange->type); | ||
5591 | pExchange->status |= EXCHANGE_QUEUED; | ||
5592 | ulStatus = EXCHANGE_QUEUED; | ||
5593 | goto Done; | ||
5594 | } | ||
5595 | |||
5596 | // Make sure ERQ has available space. | ||
5597 | |||
5598 | producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith. | ||
5599 | consumer = (USHORT)fcChip->ERQ->consumerIndex; | ||
5600 | producer++; // We are testing for full que by incrementing | ||
5601 | |||
5602 | if( producer >= ERQ_LEN ) // rollover condition? | ||
5603 | producer = 0; | ||
5604 | if( consumer != producer ) // ERQ not full? | ||
5605 | { | ||
5606 | // ****************** Need Atomic access to chip registers!!******** | ||
5607 | |||
5608 | // remember ERQ PI for copying IRB | ||
5609 | ErqIndex = (USHORT)fcChip->ERQ->producerIndex; | ||
5610 | fcChip->ERQ->producerIndex = producer; // this is written to Tachyon | ||
5611 | // we have an ERQ slot! If SCSI command, need SEST slot | ||
5612 | // otherwise we are done. | ||
5613 | |||
5614 | // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be | ||
5615 | // set according to direction of data to/from Tachyon for SEST assists. | ||
5616 | // For consistency, enforce this rule for Link Service (non-SEST) | ||
5617 | // exchanges as well. | ||
5618 | |||
5619 | // fix-up the X_ID field in IRB | ||
5620 | pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field | ||
5621 | |||
5622 | // fix-up the X_ID field in fchs -- depends on Originator or Responder, | ||
5623 | // outgoing or incoming data? | ||
5624 | switch( pExchange->type ) | ||
5625 | { | ||
5626 | // ORIGINATOR types... we're setting our OX_ID and | ||
5627 | // defaulting the responder's RX_ID to 0xFFFF | ||
5628 | |||
5629 | case SCSI_IRE: | ||
5630 | // Requirement: set MSB of x_ID for Incoming TL data | ||
5631 | // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) | ||
5632 | InboundData = 0x8000; | ||
5633 | |||
5634 | case SCSI_IWE: | ||
5635 | SestType = TRUE; | ||
5636 | pExchange->fchs.ox_rx_id = (ExchangeID | InboundData); | ||
5637 | pExchange->fchs.ox_rx_id <<= 16; // MSW shift | ||
5638 | pExchange->fchs.ox_rx_id |= 0xffff; // add default RX_ID | ||
5639 | |||
5640 | // now fix-up the Data HDR OX_ID (TL automatically does rx_id) | ||
5641 | // (not necessary for IRE -- data buffer unused) | ||
5642 | if( pExchange->type == SCSI_IWE) | ||
5643 | { | ||
5644 | fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id = | ||
5645 | pExchange->fchs.ox_rx_id; | ||
5646 | |||
5647 | } | ||
5648 | |||
5649 | break; | ||
5650 | |||
5651 | |||
5652 | case FCS_NSR: // ext. link service Name Service Request | ||
5653 | case ELS_SCR: // ext. link service State Change Registration | ||
5654 | case ELS_FDISC:// ext. link service login | ||
5655 | case ELS_FLOGI:// ext. link service login | ||
5656 | case ELS_LOGO: // FC-PH extended link service logout | ||
5657 | case BLS_NOP: // Basic link service No OPeration | ||
5658 | case ELS_PLOGI:// ext. link service login (PLOGI) | ||
5659 | case ELS_PDISC:// ext. link service login (PDISC) | ||
5660 | case ELS_PRLI: // ext. link service process login | ||
5661 | |||
5662 | pExchange->fchs.ox_rx_id = ExchangeID; | ||
5663 | pExchange->fchs.ox_rx_id <<= 16; // MSW shift | ||
5664 | pExchange->fchs.ox_rx_id |= 0xffff; // and RX_ID | ||
5665 | |||
5666 | break; | ||
5667 | |||
5668 | |||
5669 | |||
5670 | |||
5671 | // RESPONDER types... we must set our RX_ID while preserving | ||
5672 | // sender's OX_ID | ||
5673 | // outgoing (or no) data | ||
5674 | case ELS_RJT: // extended link service reject | ||
5675 | case ELS_LOGO_ACC: // FC-PH extended link service logout accept | ||
5676 | case ELS_ACC: // ext. generic link service accept | ||
5677 | case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC) | ||
5678 | case ELS_PRLI_ACC: // ext. link service process login accept | ||
5679 | |||
5680 | CompleteExchange = TRUE; // Reply (ACC or RJT) is end of exchange | ||
5681 | pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF); | ||
5682 | |||
5683 | break; | ||
5684 | |||
5685 | |||
5686 | // since we are a Responder, OX_ID should already be set by | ||
5687 | // cpqfcTSBuildExchange(). We need to -OR- in RX_ID | ||
5688 | case SCSI_TWE: | ||
5689 | SestType = TRUE; | ||
5690 | // Requirement: set MSB of x_ID for Incoming TL data | ||
5691 | // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) | ||
5692 | |||
5693 | pExchange->fchs.ox_rx_id &= 0xFFFF0000; // clear RX_ID | ||
5694 | // Requirement: set MSB of RX_ID for Incoming TL data | ||
5695 | // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50) | ||
5696 | pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000); | ||
5697 | break; | ||
5698 | |||
5699 | |||
5700 | case SCSI_TRE: | ||
5701 | SestType = TRUE; | ||
5702 | |||
5703 | // there is no XRDY for SEST target read; the data | ||
5704 | // header needs to be updated. Also update the RSP | ||
5705 | // exchange IDs for the status frame, in case it is sent automatically | ||
5706 | fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID; | ||
5707 | fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id = | ||
5708 | fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id; | ||
5709 | |||
5710 | // for easier FCP response logic (works for TWE and TRE), | ||
5711 | // copy exchange IDs. (Not needed if TRE 'RSP' bit set) | ||
5712 | pExchange->fchs.ox_rx_id = | ||
5713 | fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id; | ||
5714 | |||
5715 | break; | ||
5716 | |||
5717 | |||
5718 | case FCP_RESPONSE: // using existing OX_ID/ RX_ID pair, | ||
5719 | // start SFS FCP-RESPONSE frame | ||
5720 | // OX/RX_ID should already be set! (See "fcBuild" above) | ||
5721 | CompleteExchange = TRUE; // RSP is end of FCP-SCSI exchange | ||
5722 | |||
5723 | |||
5724 | break; | ||
5725 | |||
5726 | |||
5727 | case BLS_ABTS_RJT: // uses new RX_ID, since SEST x_ID non-existent | ||
5728 | case BLS_ABTS_ACC: // using existing OX_ID/ RX_ID pair from SEST entry | ||
5729 | CompleteExchange = TRUE; // ACC or RJT marks end of FCP-SCSI exchange | ||
5730 | case BLS_ABTS: // using existing OX_ID/ RX_ID pair from SEST entry | ||
5731 | |||
5732 | |||
5733 | break; | ||
5734 | |||
5735 | |||
5736 | default: | ||
5737 | printk("Error on fcStartExchange: undefined type %Xh(%d)\n", | ||
5738 | pExchange->type, pExchange->type); | ||
5739 | return INVALID_ARGS; | ||
5740 | } | ||
5741 | |||
5742 | |||
5743 | // X_ID fields are entered -- copy IRB to Tachyon's ERQ | ||
5744 | |||
5745 | |||
5746 | memcpy( | ||
5747 | &fcChip->ERQ->QEntry[ ErqIndex ], // dest. | ||
5748 | &pExchange->IRB, | ||
5749 | 32); // fixed (hardware) length! | ||
5750 | |||
5751 | PCI_TRACEO( ExchangeID, 0xA0) | ||
5752 | |||
5753 | // ACTION! May generate INT and IMQ entry | ||
5754 | writel( fcChip->ERQ->producerIndex, | ||
5755 | fcChip->Registers.ERQproducerIndex.address); | ||
5756 | |||
5757 | |||
5758 | if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame? | ||
5759 | { | ||
5760 | |||
5761 | // wait for completion! (TDB -- timeout and chip reset) | ||
5762 | |||
5763 | |||
5764 | PCI_TRACEO( ExchangeID, 0xA4) | ||
5765 | |||
5766 | enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem. | ||
5767 | |||
5768 | down_interruptible( cpqfcHBAdata->TYOBcomplete); | ||
5769 | |||
5770 | disable_irq( cpqfcHBAdata->HostAdapter->irq); | ||
5771 | PCI_TRACE( 0xA4) | ||
5772 | |||
5773 | // On login exchanges, BAD_ALPA (non-existent port_id) results in | ||
5774 | // FTO (Frame Time Out) on the Outbound Completion message. | ||
5775 | // If we got an FTO status, complete the exchange (free up slot) | ||
5776 | if( CompleteExchange || // flag from Reply frames | ||
5777 | pExchange->status ) // typically, can get FRAME_TO | ||
5778 | { | ||
5779 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID); | ||
5780 | } | ||
5781 | } | ||
5782 | |||
5783 | else // SEST Exchange | ||
5784 | { | ||
5785 | ulStatus = 0; // ship & pray success (e.g. FCP-SCSI) | ||
5786 | |||
5787 | if( CompleteExchange ) // by Type of exchange (e.g. end-of-xchng) | ||
5788 | { | ||
5789 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID); | ||
5790 | } | ||
5791 | |||
5792 | else | ||
5793 | pExchange->status &= ~EXCHANGE_QUEUED; // clear ExchangeQueued flag | ||
5794 | |||
5795 | } | ||
5796 | } | ||
5797 | |||
5798 | |||
5799 | else // ERQ 'producer' = 'consumer' and QUE is full | ||
5800 | { | ||
5801 | ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full | ||
5802 | } | ||
5803 | |||
5804 | Done: | ||
5805 | PCI_TRACE( 0xA0) | ||
5806 | return ulStatus; | ||
5807 | } | ||
5808 | |||
5809 | |||
5810 | |||
5811 | |||
5812 | |||
5813 | // Scan fcController->fcExchanges array for a usuable index (a "free" | ||
5814 | // exchange). | ||
5815 | // Inputs: | ||
5816 | // fcChip - pointer to TachLite chip structure | ||
5817 | // Return: | ||
5818 | // index - exchange array element where exchange can be built | ||
5819 | // -1 - exchange array is full | ||
5820 | // REMARKS: | ||
5821 | // Although this is a (yuk!) linear search, we presume | ||
5822 | // that the system will complete exchanges about as quickly as | ||
5823 | // they are submitted. A full Exchange array (and hence, max linear | ||
5824 | // search time for free exchange slot) almost guarantees a Fibre problem | ||
5825 | // of some sort. | ||
5826 | // In the interest of making exchanges easier to debug, we want a LRU | ||
5827 | // (Least Recently Used) scheme. | ||
5828 | |||
5829 | |||
5830 | static LONG FindFreeExchange( PTACHYON fcChip, ULONG type ) | ||
5831 | { | ||
5832 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
5833 | ULONG i; | ||
5834 | ULONG ulStatus=-1; // assume failure | ||
5835 | |||
5836 | |||
5837 | if( type == SCSI_IRE || | ||
5838 | type == SCSI_TRE || | ||
5839 | type == SCSI_IWE || | ||
5840 | type == SCSI_TWE) | ||
5841 | { | ||
5842 | // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1 | ||
5843 | if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover? | ||
5844 | fcChip->fcSestExchangeLRU = 0; | ||
5845 | i = fcChip->fcSestExchangeLRU; // typically it's already free! | ||
5846 | |||
5847 | if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element | ||
5848 | { | ||
5849 | ulStatus = 0; // success! | ||
5850 | } | ||
5851 | |||
5852 | else | ||
5853 | { // YUK! we need to do a linear search for free element. | ||
5854 | // Fragmentation of the fcExchange array is due to excessively | ||
5855 | // long completions or timeouts. | ||
5856 | |||
5857 | while( TRUE ) | ||
5858 | { | ||
5859 | if( ++i >= TACH_SEST_LEN ) // rollover check | ||
5860 | i = 0; // beginning of SEST X_IDs | ||
5861 | |||
5862 | // printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n", | ||
5863 | // i, Exchanges->fcExchange[i].type); | ||
5864 | |||
5865 | if( Exchanges->fcExchange[i].type == 0 ) // "free"? | ||
5866 | { | ||
5867 | ulStatus = 0; // success! | ||
5868 | break; | ||
5869 | } | ||
5870 | if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array? | ||
5871 | { | ||
5872 | printk( "SEST X_ID space full\n"); | ||
5873 | break; // failed - prevent inf. loop | ||
5874 | } | ||
5875 | } | ||
5876 | } | ||
5877 | fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass) | ||
5878 | } | ||
5879 | |||
5880 | |||
5881 | |||
5882 | else // Link Service type - X_IDs should be from TACH_SEST_LEN | ||
5883 | // to TACH_MAX_XID | ||
5884 | { | ||
5885 | if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check | ||
5886 | fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup) | ||
5887 | fcChip->fcLsExchangeLRU = TACH_SEST_LEN; | ||
5888 | |||
5889 | i = fcChip->fcLsExchangeLRU; // typically it's already free! | ||
5890 | if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element | ||
5891 | { | ||
5892 | ulStatus = 0; // success! | ||
5893 | } | ||
5894 | |||
5895 | else | ||
5896 | { // YUK! we need to do a linear search for free element | ||
5897 | // Fragmentation of the fcExchange array is due to excessively | ||
5898 | // long completions or timeouts. | ||
5899 | |||
5900 | while( TRUE ) | ||
5901 | { | ||
5902 | if( ++i >= TACH_MAX_XID ) // rollover check | ||
5903 | i = TACH_SEST_LEN;// beginning of Link Service X_IDs | ||
5904 | |||
5905 | // printk( "looping for xchng ID: i=%d, type=%Xh\n", | ||
5906 | // i, Exchanges->fcExchange[i].type); | ||
5907 | |||
5908 | if( Exchanges->fcExchange[i].type == 0 ) // "free"? | ||
5909 | { | ||
5910 | ulStatus = 0; // success! | ||
5911 | break; | ||
5912 | } | ||
5913 | if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array? | ||
5914 | { | ||
5915 | printk( "LinkService X_ID space full\n"); | ||
5916 | break; // failed - prevent inf. loop | ||
5917 | } | ||
5918 | } | ||
5919 | } | ||
5920 | fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass) | ||
5921 | |||
5922 | } | ||
5923 | |||
5924 | if( !ulStatus ) // success? | ||
5925 | Exchanges->fcExchange[i].type = type; // allocate it. | ||
5926 | |||
5927 | else | ||
5928 | i = -1; // error - all exchanges "open" | ||
5929 | |||
5930 | return i; | ||
5931 | } | ||
5932 | |||
5933 | static void | ||
5934 | cpqfc_pci_unmap_extended_sg(struct pci_dev *pcidev, | ||
5935 | PTACHYON fcChip, | ||
5936 | ULONG x_ID) | ||
5937 | { | ||
5938 | // Unmaps the memory regions used to hold the scatter gather lists | ||
5939 | |||
5940 | PSGPAGES i; | ||
5941 | |||
5942 | // Were there any such regions needing unmapping? | ||
5943 | if (! USES_EXTENDED_SGLIST(fcChip->SEST, x_ID)) | ||
5944 | return; // No such regions, we're outta here. | ||
5945 | |||
5946 | // for each extended scatter gather region needing unmapping... | ||
5947 | for (i=fcChip->SEST->sgPages[x_ID] ; i != NULL ; i = i->next) | ||
5948 | pci_unmap_single(pcidev, i->busaddr, i->maplen, | ||
5949 | PCI_DMA_TODEVICE); | ||
5950 | } | ||
5951 | |||
5952 | // Called also from cpqfcTScontrol.o, so can't be static | ||
5953 | void | ||
5954 | cpqfc_pci_unmap(struct pci_dev *pcidev, | ||
5955 | Scsi_Cmnd *cmd, | ||
5956 | PTACHYON fcChip, | ||
5957 | ULONG x_ID) | ||
5958 | { | ||
5959 | // Undo the DMA mappings | ||
5960 | if (cmd->use_sg) { // Used scatter gather list for data buffer? | ||
5961 | cpqfc_pci_unmap_extended_sg(pcidev, fcChip, x_ID); | ||
5962 | pci_unmap_sg(pcidev, cmd->buffer, cmd->use_sg, | ||
5963 | cmd->sc_data_direction); | ||
5964 | // printk("umsg %d\n", cmd->use_sg); | ||
5965 | } | ||
5966 | else if (cmd->request_bufflen) { | ||
5967 | // printk("ums %p ", fcChip->SEST->u[ x_ID ].IWE.GAddr1); | ||
5968 | pci_unmap_single(pcidev, fcChip->SEST->u[ x_ID ].IWE.GAddr1, | ||
5969 | cmd->request_bufflen, | ||
5970 | cmd->sc_data_direction); | ||
5971 | } | ||
5972 | } | ||
5973 | |||
5974 | // We call this routine to free an Exchange for any reason: | ||
5975 | // completed successfully, completed with error, aborted, etc. | ||
5976 | |||
5977 | // returns FALSE if Exchange failed and "retry" is acceptable | ||
5978 | // returns TRUE if Exchange was successful, or retry is impossible | ||
5979 | // (e.g. port/device gone). | ||
5980 | //scompleteexchange | ||
5981 | |||
5982 | void cpqfcTSCompleteExchange( | ||
5983 | struct pci_dev *pcidev, | ||
5984 | PTACHYON fcChip, | ||
5985 | ULONG x_ID) | ||
5986 | { | ||
5987 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
5988 | int already_unmapped = 0; | ||
5989 | |||
5990 | if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange) | ||
5991 | { | ||
5992 | if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@! | ||
5993 | { | ||
5994 | // TriggerHBA( fcChip->Registers.ReMapMemBase, 0); | ||
5995 | printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID, | ||
5996 | Exchanges->fcExchange[ x_ID ].type); | ||
5997 | |||
5998 | goto CleanUpSestResources; // this path should be very rare. | ||
5999 | } | ||
6000 | |||
6001 | // we have Linux Scsi Cmnd ptr..., now check our Exchange status | ||
6002 | // to decide how to complete this SEST FCP exchange | ||
6003 | |||
6004 | if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem, | ||
6005 | // or abnormal exchange completion | ||
6006 | { | ||
6007 | // set FCP Link statistics | ||
6008 | |||
6009 | if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) | ||
6010 | fcChip->fcStats.timeouts++; | ||
6011 | if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT) | ||
6012 | fcChip->fcStats.FC4aborted++; | ||
6013 | if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR) | ||
6014 | fcChip->fcStats.CntErrors++; | ||
6015 | if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) | ||
6016 | fcChip->fcStats.linkFailTX++; | ||
6017 | if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX) | ||
6018 | fcChip->fcStats.linkFailRX++; | ||
6019 | if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW) | ||
6020 | fcChip->fcStats.CntErrors++; | ||
6021 | |||
6022 | // First, see if the Scsi upper level initiated an ABORT on this | ||
6023 | // exchange... | ||
6024 | if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT ) | ||
6025 | { | ||
6026 | printk(" DID_ABORT, x_ID %Xh, Cmnd %p ", | ||
6027 | x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6028 | goto CleanUpSestResources; // (we don't expect Linux _aborts) | ||
6029 | } | ||
6030 | |||
6031 | // Did our driver timeout the Exchange, or did Tachyon indicate | ||
6032 | // a failure during transmission? Ask for retry with "SOFT_ERROR" | ||
6033 | else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT) | ||
6034 | { | ||
6035 | // printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", | ||
6036 | // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6037 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); | ||
6038 | } | ||
6039 | |||
6040 | // Did frame(s) for an open exchange arrive in the SFQ, | ||
6041 | // meaning the SEST was unable to process them? | ||
6042 | else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME) | ||
6043 | { | ||
6044 | // printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", | ||
6045 | // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6046 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); | ||
6047 | } | ||
6048 | |||
6049 | // Did our driver timeout the Exchange, or did Tachyon indicate | ||
6050 | // a failure during transmission? Ask for retry with "SOFT_ERROR" | ||
6051 | else if( | ||
6052 | (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) || | ||
6053 | (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) || | ||
6054 | (Exchanges->fcExchange[ x_ID ].status & FRAME_TO) || | ||
6055 | (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY) || | ||
6056 | (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY) ) | ||
6057 | |||
6058 | |||
6059 | { | ||
6060 | // printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n", | ||
6061 | // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6062 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); | ||
6063 | |||
6064 | |||
6065 | } | ||
6066 | |||
6067 | // e.g., a LOGOut happened, or device never logged back in. | ||
6068 | else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED) | ||
6069 | { | ||
6070 | // printk(" *LOGOut or timeout on login!* "); | ||
6071 | // trigger? | ||
6072 | // TriggerHBA( fcChip->Registers.ReMapMemBase, 0); | ||
6073 | |||
6074 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16); | ||
6075 | } | ||
6076 | |||
6077 | |||
6078 | // Did Tachyon indicate a CNT error? We need further analysis | ||
6079 | // to determine if the exchange is acceptable | ||
6080 | else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR) | ||
6081 | { | ||
6082 | UCHAR ScsiStatus; | ||
6083 | FCP_STATUS_RESPONSE *pFcpStatus = | ||
6084 | (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl; | ||
6085 | |||
6086 | ScsiStatus = pFcpStatus->fcp_status >>24; | ||
6087 | |||
6088 | // If the command is a SCSI Read/Write type, we don't tolerate | ||
6089 | // count errors of any kind; assume the count error is due to | ||
6090 | // a dropped frame and ask for retry... | ||
6091 | |||
6092 | if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) || | ||
6093 | (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) || | ||
6094 | (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) || | ||
6095 | (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) ) | ||
6096 | && | ||
6097 | ScsiStatus == 0 ) | ||
6098 | { | ||
6099 | // ask for retry | ||
6100 | /* printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n", | ||
6101 | x_ID, Exchanges->fcExchange[ x_ID ].status, | ||
6102 | Exchanges->fcExchange[ x_ID ].Cmnd);*/ | ||
6103 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16); | ||
6104 | } | ||
6105 | |||
6106 | else // need more analysis | ||
6107 | { | ||
6108 | cpqfcTSCheckandSnoopFCP(fcChip, x_ID); // (will set ->result) | ||
6109 | } | ||
6110 | } | ||
6111 | |||
6112 | // default: NOTE! We don't ever want to get here. Getting here | ||
6113 | // implies something new is happening that we've never had a test | ||
6114 | // case for. Need code maintenance! Return "ERROR" | ||
6115 | else | ||
6116 | { | ||
6117 | unsigned int stat = Exchanges->fcExchange[ x_ID ].status; | ||
6118 | printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p", | ||
6119 | Exchanges->fcExchange[ x_ID ].status, x_ID, | ||
6120 | Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6121 | |||
6122 | if (stat & INVALID_ARGS) printk(" INVALID_ARGS "); | ||
6123 | if (stat & LNKDWN_OSLS) printk(" LNKDWN_OSLS "); | ||
6124 | if (stat & LNKDWN_LASER) printk(" LNKDWN_LASER "); | ||
6125 | if (stat & OUTQUE_FULL) printk(" OUTQUE_FULL "); | ||
6126 | if (stat & DRIVERQ_FULL) printk(" DRIVERQ_FULL "); | ||
6127 | if (stat & SEST_FULL) printk(" SEST_FULL "); | ||
6128 | if (stat & BAD_ALPA) printk(" BAD_ALPA "); | ||
6129 | if (stat & OVERFLOW) printk(" OVERFLOW "); | ||
6130 | if (stat & COUNT_ERROR) printk(" COUNT_ERROR "); | ||
6131 | if (stat & LINKFAIL_RX) printk(" LINKFAIL_RX "); | ||
6132 | if (stat & ABORTSEQ_NOTIFY) printk(" ABORTSEQ_NOTIFY "); | ||
6133 | if (stat & LINKFAIL_TX) printk(" LINKFAIL_TX "); | ||
6134 | if (stat & HOSTPROG_ERR) printk(" HOSTPROG_ERR "); | ||
6135 | if (stat & FRAME_TO) printk(" FRAME_TO "); | ||
6136 | if (stat & INV_ENTRY) printk(" INV_ENTRY "); | ||
6137 | if (stat & SESTPROG_ERR) printk(" SESTPROG_ERR "); | ||
6138 | if (stat & OUTBOUND_TIMEOUT) printk(" OUTBOUND_TIMEOUT "); | ||
6139 | if (stat & INITIATOR_ABORT) printk(" INITIATOR_ABORT "); | ||
6140 | if (stat & MEMPOOL_FAIL) printk(" MEMPOOL_FAIL "); | ||
6141 | if (stat & FC2_TIMEOUT) printk(" FC2_TIMEOUT "); | ||
6142 | if (stat & TARGET_ABORT) printk(" TARGET_ABORT "); | ||
6143 | if (stat & EXCHANGE_QUEUED) printk(" EXCHANGE_QUEUED "); | ||
6144 | if (stat & PORTID_CHANGED) printk(" PORTID_CHANGED "); | ||
6145 | if (stat & DEVICE_REMOVED) printk(" DEVICE_REMOVED "); | ||
6146 | if (stat & SFQ_FRAME) printk(" SFQ_FRAME "); | ||
6147 | printk("\n"); | ||
6148 | |||
6149 | Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16); | ||
6150 | } | ||
6151 | } | ||
6152 | else // definitely no Tach problem, but perhaps an FCP problem | ||
6153 | { | ||
6154 | // set FCP Link statistic | ||
6155 | fcChip->fcStats.ok++; | ||
6156 | cpqfcTSCheckandSnoopFCP( fcChip, x_ID); // (will set ->result) | ||
6157 | } | ||
6158 | |||
6159 | cpqfc_pci_unmap(pcidev, Exchanges->fcExchange[x_ID].Cmnd, | ||
6160 | fcChip, x_ID); // undo DMA mappings. | ||
6161 | already_unmapped = 1; | ||
6162 | |||
6163 | // OK, we've set the Scsi "->result" field, so proceed with calling | ||
6164 | // Linux Scsi "done" (if not NULL), and free any kernel memory we | ||
6165 | // may have allocated for the exchange. | ||
6166 | |||
6167 | PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC); | ||
6168 | // complete the command back to upper Scsi drivers | ||
6169 | if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL) | ||
6170 | { | ||
6171 | // Calling "done" on an Linux _abort() aborted | ||
6172 | // Cmnd causes a kernel panic trying to re-free mem. | ||
6173 | // Actually, we shouldn't do anything with an _abort CMND | ||
6174 | if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) ) | ||
6175 | { | ||
6176 | PCI_TRACE(0xAC) | ||
6177 | call_scsi_done(Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6178 | } | ||
6179 | else | ||
6180 | { | ||
6181 | // printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n", | ||
6182 | // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); | ||
6183 | } | ||
6184 | } | ||
6185 | else{ | ||
6186 | printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID, | ||
6187 | Exchanges->fcExchange[ x_ID ].type, | ||
6188 | Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]); | ||
6189 | printk(" cpqfcTS: Null scsi_done function pointer!\n"); | ||
6190 | } | ||
6191 | |||
6192 | |||
6193 | // Now, clean up non-Scsi_Cmnd items... | ||
6194 | CleanUpSestResources: | ||
6195 | |||
6196 | if (!already_unmapped) | ||
6197 | cpqfc_pci_unmap(pcidev, Exchanges->fcExchange[x_ID].Cmnd, | ||
6198 | fcChip, x_ID); // undo DMA mappings. | ||
6199 | |||
6200 | // Was an Extended Scatter/Gather page allocated? We know | ||
6201 | // this by checking DWORD 4, bit 31 ("LOC") of SEST entry | ||
6202 | if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000)) | ||
6203 | { | ||
6204 | PSGPAGES p, next; | ||
6205 | |||
6206 | // extended S/G list was used -- Free the allocated ext. S/G pages | ||
6207 | for (p = fcChip->SEST->sgPages[x_ID]; p != NULL; p = next) { | ||
6208 | next = p->next; | ||
6209 | kfree(p); | ||
6210 | } | ||
6211 | fcChip->SEST->sgPages[x_ID] = NULL; | ||
6212 | } | ||
6213 | |||
6214 | Exchanges->fcExchange[ x_ID ].Cmnd = NULL; | ||
6215 | } // Done with FCP (SEST) exchanges | ||
6216 | |||
6217 | |||
6218 | // the remaining logic is common to ALL Exchanges: | ||
6219 | // FCP(SEST) and LinkServ. | ||
6220 | |||
6221 | Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE! | ||
6222 | Exchanges->fcExchange[ x_ID ].status = 0; | ||
6223 | |||
6224 | PCI_TRACEO( x_ID, 0xAC) | ||
6225 | |||
6226 | |||
6227 | return; | ||
6228 | } // (END of CompleteExchange function) | ||
6229 | |||
6230 | |||
6231 | |||
6232 | |||
6233 | // Unfortunately, we must snoop all command completions in | ||
6234 | // order to manipulate certain return fields, and take note of | ||
6235 | // device types, etc., to facilitate the Fibre-Channel to SCSI | ||
6236 | // "mapping". | ||
6237 | // (Watch for BIG Endian confusion on some payload fields) | ||
6238 | void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID) | ||
6239 | { | ||
6240 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | ||
6241 | Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd; | ||
6242 | FCP_STATUS_RESPONSE *pFcpStatus = | ||
6243 | (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl; | ||
6244 | UCHAR ScsiStatus; | ||
6245 | |||
6246 | ScsiStatus = pFcpStatus->fcp_status >>24; | ||
6247 | |||
6248 | #ifdef FCP_COMPLETION_DBG | ||
6249 | printk("ScsiStatus = 0x%X\n", ScsiStatus); | ||
6250 | #endif | ||
6251 | |||
6252 | // First, check FCP status | ||
6253 | if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID ) | ||
6254 | { | ||
6255 | // check response code (RSP_CODE) -- most popular is bad len | ||
6256 | // 1st 4 bytes of rsp info -- only byte 3 interesting | ||
6257 | if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN ) | ||
6258 | { | ||
6259 | |||
6260 | // do we EVER get here? | ||
6261 | printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID); | ||
6262 | } | ||
6263 | } | ||
6264 | |||
6265 | // for now, go by the ScsiStatus, and manipulate certain | ||
6266 | // commands when necessary... | ||
6267 | if( ScsiStatus == 0) // SCSI status byte "good"? | ||
6268 | { | ||
6269 | Cmnd->result = 0; // everything's OK | ||
6270 | |||
6271 | if( (Cmnd->cmnd[0] == INQUIRY)) | ||
6272 | { | ||
6273 | UCHAR *InquiryData = Cmnd->request_buffer; | ||
6274 | PFC_LOGGEDIN_PORT pLoggedInPort; | ||
6275 | |||
6276 | // We need to manipulate INQUIRY | ||
6277 | // strings for COMPAQ RAID controllers to force | ||
6278 | // Linux to scan additional LUNs. Namely, set | ||
6279 | // the Inquiry string byte 2 (ANSI-approved version) | ||
6280 | // to 2. | ||
6281 | |||
6282 | if( !memcmp( &InquiryData[8], "COMPAQ", 6 )) | ||
6283 | { | ||
6284 | InquiryData[2] = 0x2; // claim SCSI-2 compliance, | ||
6285 | // so multiple LUNs may be scanned. | ||
6286 | // (no SCSI-2 problems known in CPQ) | ||
6287 | } | ||
6288 | |||
6289 | // snoop the Inquiry to detect Disk, Tape, etc. type | ||
6290 | // (search linked list for the port_id we sent INQUIRY to) | ||
6291 | pLoggedInPort = fcFindLoggedInPort( fcChip, | ||
6292 | NULL, // DON'T search Scsi Nexus (we will set it) | ||
6293 | Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF, | ||
6294 | NULL, // DON'T search linked list for FC WWN | ||
6295 | NULL); // DON'T care about end of list | ||
6296 | |||
6297 | if( pLoggedInPort ) | ||
6298 | { | ||
6299 | pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0]; | ||
6300 | } | ||
6301 | else | ||
6302 | { | ||
6303 | printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n", | ||
6304 | Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF); | ||
6305 | } | ||
6306 | } | ||
6307 | } | ||
6308 | |||
6309 | |||
6310 | // Scsi Status not good -- pass it back to caller | ||
6311 | |||
6312 | else | ||
6313 | { | ||
6314 | Cmnd->result = ScsiStatus; // SCSI status byte is 1st | ||
6315 | |||
6316 | // check for valid "sense" data | ||
6317 | |||
6318 | if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID ) | ||
6319 | { // limit Scsi Sense field length! | ||
6320 | int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte | ||
6321 | |||
6322 | SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ? | ||
6323 | sizeof( Cmnd->sense_buffer) : SenseLen; | ||
6324 | |||
6325 | |||
6326 | #ifdef FCP_COMPLETION_DBG | ||
6327 | printk("copy sense_buffer %p, len %d, result %Xh\n", | ||
6328 | Cmnd->sense_buffer, SenseLen, Cmnd->result); | ||
6329 | #endif | ||
6330 | |||
6331 | // NOTE: There is some dispute over the FCP response | ||
6332 | // format. Most FC devices assume that FCP_RSP_INFO | ||
6333 | // is 8 bytes long, in spite of the fact that FCP_RSP_LEN | ||
6334 | // is (virtually) always 0 and the field is "invalid". | ||
6335 | // Some other devices assume that | ||
6336 | // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0) | ||
6337 | // when the FCP_RSP is invalid (this almost appears to be | ||
6338 | // one of those "religious" issues). | ||
6339 | // Consequently, we test the usual position of FCP_SNS_INFO | ||
6340 | // for 7Xh, since the SCSI sense format says the first | ||
6341 | // byte ("error code") should be 0x70 or 0x71. In practice, | ||
6342 | // we find that every device does in fact have 0x70 or 0x71 | ||
6343 | // in the first byte position, so this test works for all | ||
6344 | // FC devices. | ||
6345 | // (This logic is especially effective for the CPQ/DEC HSG80 | ||
6346 | // & HSG60 controllers). | ||
6347 | |||
6348 | if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 ) | ||
6349 | memcpy( Cmnd->sense_buffer, | ||
6350 | &pFcpStatus->fcp_sns_info[0], SenseLen); | ||
6351 | else | ||
6352 | { | ||
6353 | unsigned char *sbPtr = | ||
6354 | (unsigned char *)&pFcpStatus->fcp_sns_info[0]; | ||
6355 | sbPtr -= 8; // back up 8 bytes hoping to find the | ||
6356 | // start of the sense buffer | ||
6357 | memcpy( Cmnd->sense_buffer, sbPtr, SenseLen); | ||
6358 | } | ||
6359 | |||
6360 | // in the special case of Device Reset, tell upper layer | ||
6361 | // to immediately retry (with SOFT_ERROR status) | ||
6362 | // look for Sense Key Unit Attention (0x6) with ASC Device | ||
6363 | // Reset (0x29) | ||
6364 | // printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n", | ||
6365 | // SenseLen, Cmnd->sense_buffer[2], | ||
6366 | // Cmnd->sense_buffer[12]); | ||
6367 | if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) && | ||
6368 | (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset" | ||
6369 | { | ||
6370 | Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd | ||
6371 | } | ||
6372 | |||
6373 | // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure | ||
6374 | else if( ((Cmnd->sense_buffer[2] & 0xF) == 0x4) && // "hardware error" | ||
6375 | (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code | ||
6376 | { | ||
6377 | // printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n", | ||
6378 | // Cmnd->channel, Cmnd->target, Cmnd->lun); | ||
6379 | Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd | ||
6380 | } | ||
6381 | |||
6382 | } // (end of sense len valid) | ||
6383 | |||
6384 | // there is no sense data to help out Linux's Scsi layers... | ||
6385 | // We'll just return the Scsi status and hope he will "do the | ||
6386 | // right thing" | ||
6387 | else | ||
6388 | { | ||
6389 | // as far as we know, the Scsi status is sufficient | ||
6390 | Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd | ||
6391 | } | ||
6392 | } | ||
6393 | } | ||
6394 | |||
6395 | |||
6396 | |||
6397 | //PPPPPPPPPPPPPPPPPPPPPPPPP PAYLOAD PPPPPPPPP | ||
6398 | // build data PAYLOAD; SCSI FCP_CMND I.U. | ||
6399 | // remember BIG ENDIAN payload - DWord values must be byte-reversed | ||
6400 | // (hence the affinity for byte pointer building). | ||
6401 | |||
6402 | static int build_FCP_payload( Scsi_Cmnd *Cmnd, | ||
6403 | UCHAR* payload, ULONG type, ULONG fcp_dl ) | ||
6404 | { | ||
6405 | int i; | ||
6406 | |||
6407 | |||
6408 | switch( type) | ||
6409 | { | ||
6410 | |||
6411 | case SCSI_IWE: | ||
6412 | case SCSI_IRE: | ||
6413 | // 8 bytes FCP_LUN | ||
6414 | // Peripheral Device or Volume Set addressing, and LUN mapping | ||
6415 | // When the FC port was looked up, we copied address mode | ||
6416 | // and any LUN mask to the scratch pad SCp.phase & .mode | ||
6417 | |||
6418 | *payload++ = (UCHAR)Cmnd->SCp.phase; | ||
6419 | |||
6420 | // Now, because of "lun masking" | ||
6421 | // (aka selective storage presentation), | ||
6422 | // the contiguous Linux Scsi lun number may not match the | ||
6423 | // device's lun number, so we may have to "map". | ||
6424 | |||
6425 | *payload++ = (UCHAR)Cmnd->SCp.have_data_in; | ||
6426 | |||
6427 | // We don't know of anyone in the FC business using these | ||
6428 | // extra "levels" of addressing. In fact, confusion still exists | ||
6429 | // just using the FIRST level... ;-) | ||
6430 | |||
6431 | *payload++ = 0; // 2nd level addressing | ||
6432 | *payload++ = 0; | ||
6433 | *payload++ = 0; // 3rd level addressing | ||
6434 | *payload++ = 0; | ||
6435 | *payload++ = 0; // 4th level addressing | ||
6436 | *payload++ = 0; | ||
6437 | |||
6438 | // 4 bytes Control Field FCP_CNTL | ||
6439 | *payload++ = 0; // byte 0: (MSB) reserved | ||
6440 | *payload++ = 0; // byte 1: task codes | ||
6441 | |||
6442 | // byte 2: task management flags | ||
6443 | // another "use" of the spare field to accomplish TDR | ||
6444 | // note combination needed | ||
6445 | if( (Cmnd->cmnd[0] == RELEASE) && | ||
6446 | (Cmnd->SCp.buffers_residual == FCP_TARGET_RESET) ) | ||
6447 | { | ||
6448 | Cmnd->cmnd[0] = 0; // issue "Test Unit Ready" for TDR | ||
6449 | *payload++ = 0x20; // target device reset bit | ||
6450 | } | ||
6451 | else | ||
6452 | *payload++ = 0; // no TDR | ||
6453 | // byte 3: (LSB) execution management codes | ||
6454 | // bit 0 write, bit 1 read (don't set together) | ||
6455 | |||
6456 | if( fcp_dl != 0 ) | ||
6457 | { | ||
6458 | if( type == SCSI_IWE ) // WRITE | ||
6459 | *payload++ = 1; | ||
6460 | else // READ | ||
6461 | *payload++ = 2; | ||
6462 | } | ||
6463 | else | ||
6464 | { | ||
6465 | // On some devices, if RD or WR bits are set, | ||
6466 | // and fcp_dl is 0, they will generate an error on the command. | ||
6467 | // (i.e., if direction is specified, they insist on a length). | ||
6468 | *payload++ = 0; // no data (necessary for CPQ) | ||
6469 | } | ||
6470 | |||
6471 | |||
6472 | // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16 | ||
6473 | // FCP_CDB allows 16 byte SCSI command descriptor blk; | ||
6474 | // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...) | ||
6475 | for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++) | ||
6476 | *payload++ = Cmnd->cmnd[i]; | ||
6477 | |||
6478 | // if( Cmnd->cmd_len == 16 ) | ||
6479 | // { | ||
6480 | // memcpy( payload, &Cmnd->SCp.buffers_residual, 4); | ||
6481 | // } | ||
6482 | payload+= (16 - i); | ||
6483 | |||
6484 | // FCP_DL is largest number of expected data bytes | ||
6485 | // per CDB (i.e. read/write command) | ||
6486 | *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL | ||
6487 | *payload++ = (UCHAR)(fcp_dl >>16); | ||
6488 | *payload++ = (UCHAR)(fcp_dl >>8); | ||
6489 | *payload++ = (UCHAR)fcp_dl; // (LSB) | ||
6490 | break; | ||
6491 | |||
6492 | case SCSI_TWE: // need FCP_XFER_RDY | ||
6493 | *payload++ = 0; // (4 bytes) DATA_RO (MSB byte 0) | ||
6494 | *payload++ = 0; | ||
6495 | *payload++ = 0; | ||
6496 | *payload++ = 0; // LSB (byte 3) | ||
6497 | // (4 bytes) BURST_LEN | ||
6498 | // size of following FCP_DATA payload | ||
6499 | *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL | ||
6500 | *payload++ = (UCHAR)(fcp_dl >>16); | ||
6501 | *payload++ = (UCHAR)(fcp_dl >>8); | ||
6502 | *payload++ = (UCHAR)fcp_dl; // (LSB) | ||
6503 | // 4 bytes RESERVED | ||
6504 | *payload++ = 0; | ||
6505 | *payload++ = 0; | ||
6506 | *payload++ = 0; | ||
6507 | *payload++ = 0; | ||
6508 | break; | ||
6509 | |||
6510 | default: | ||
6511 | break; | ||
6512 | } | ||
6513 | |||
6514 | return 0; | ||
6515 | } | ||
6516 | |||