v2.4.8 -> v2.4.8.1
[opensuse:kernel.git] / drivers / scsi / cpqfcTSworker.c
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/blk.h>
30 #include <linux/interrupt.h>
31 #include <linux/delay.h>
32 #include <linux/smp_lock.h>
33
34 #define __KERNEL_SYSCALLS__
35
36 #define SHUTDOWN_SIGS   (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
37
38 #include <linux/unistd.h>
39
40 #include <asm/system.h>
41 #include <asm/irq.h>
42 #include <asm/dma.h>
43
44
45
46 #include "sd.h"
47 #include "hosts.h"   // struct Scsi_Host definition for T handler
48 #include "cpqfcTSchip.h"
49 #include "cpqfcTSstructs.h"
50 #include "cpqfcTStrigger.h"
51
52 //#define LOGIN_DBG 1
53
54 // REMARKS:
55 // Since Tachyon chips may be permitted to wait from 500ms up to 2 sec
56 // to empty an outgoing frame from its FIFO to the Fibre Channel stream,
57 // we cannot do everything we need to in the interrupt handler.  Specifically,
58 // every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be
59 // suspended until the login sequences have been completed.  Login commands
60 // are frames just like SCSI commands are frames; they are subject to the same
61 // timeout issues and delays.  Also, various specs provide up to 2 seconds for
62 // devices to log back in (i.e. respond with ACC to a login frame), so I/O to
63 // that device has to be suspended.
64 // A serious problem here occurs on highly loaded FC-AL systems.  If our FC port
65 // has a low priority (e.g. high arbitrated loop physical address, alpa), and
66 // some other device is hogging bandwidth (permissible under FC-AL), we might
67 // time out thinking the link is hung, when it's simply busy.  Many such
68 // considerations complicate the design.  Although Tachyon assumes control
69 // (in silicon) for many link-specific issues, the Linux driver is left with the
70 // rest, which turns out to be a difficult, time critical chore.
71
72 // These "worker" functions will handle things like FC Logins; all
73 // processes with I/O to our device must wait for the Login to complete
74 // and (if successful) I/O to resume.  In the event of a malfunctioning or  
75 // very busy loop, it may take hundreds of millisecs or even seconds to complete
76 // a frame send.  We don't want to hang up the entire server (and all
77 // processes which don't depend on Fibre) during this wait.
78
79 // The Tachyon chip can have around 30,000 I/O operations ("exchanges")
80 // open at one time.  However, each exchange must be initiated 
81 // synchronously (i.e. each of the 30k I/O had to be started one at a
82 // time by sending a starting frame via Tachyon's outbound que).  
83
84 // To accomodate kernel "module" build, this driver limits the exchanges
85 // to 256, because of the contiguous physical memory limitation of 128M.
86
87 // Typical FC Exchanges are opened presuming the FC frames start without errors,
88 // while Exchange completion is handled in the interrupt handler.  This
89 // optimizes performance for the "everything's working" case.
90 // However, when we have FC related errors or hot plugging of FC ports, we pause
91 // I/O and handle FC-specific tasks in the worker thread.  These FC-specific
92 // functions will handle things like FC Logins and Aborts.  As the Login sequence
93 // completes to each and every target, I/O can resume to that target.  
94
95 // Our kernel "worker thread" must share the HBA with threads calling 
96 // "queuecommand".  We define a "BoardLock" semaphore which indicates
97 // to "queuecommand" that the HBA is unavailable, and Cmnds are added to a
98 // board lock Q.  When the worker thread finishes with the board, the board
99 // lock Q commands are completed with status causing immediate retry.
100 // Typically, the board is locked while Logins are in progress after an
101 // FC Link Down condition.  When Cmnds are re-queued after board lock, the
102 // particular Scsi channel/target may or may not have logged back in.  When
103 // the device is waiting for login, the "prli" flag is clear, in which case
104 // commands are passed to a Link Down Q.  Whenever the login finally completes,
105 // the LinkDown Q is completed, again with status causing immediate retry.
106 // When FC devices are logged in, we build and start FC commands to the
107 // devices.
108
109 // NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices 
110 // that never log back in (e.g. physically removed) is NOT completely
111 // understood.  I've still seen instances of system hangs on failed Write 
112 // commands (possibly from the ext2 layer?) on device removal.  Such special
113 // cases need to be evaluated from a system/application view - e.g., how
114 // exactly does the system want me to complete commands when the device is
115 // physically removed??
116
117 // local functions
118
119 static void SetLoginFields(
120   PFC_LOGGEDIN_PORT pLoggedInPort,
121   TachFCHDR_GCMND* fchs,
122   BOOLEAN PDisc,
123   BOOLEAN Originator);
124
125 static void AnalyzeIncomingFrame( 
126        CPQFCHBA *cpqfcHBAdata,
127        ULONG QNdx );
128
129 static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds );
130
131 static int verify_PLOGI( PTACHYON fcChip,
132       TachFCHDR_GCMND* fchs, ULONG* reject_explain);
133 static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain);
134
135 static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type);
136 static void BuildLinkServicePayload( 
137               PTACHYON fcChip, ULONG type, void* payload);
138
139 static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, 
140         PFC_LOGGEDIN_PORT pLoggedInPort);
141
142 static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID);
143
144 static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata);
145
146 static void RevalidateSEST( struct Scsi_Host *HostAdapter, 
147                         PFC_LOGGEDIN_PORT pLoggedInPort);
148
149 static void IssueReportLunsCommand( 
150               CPQFCHBA* cpqfcHBAdata, 
151               TachFCHDR_GCMND* fchs);
152
153 // (see scsi_error.c comments on kernel task creation)
154
155 void cpqfcTSWorkerThread( void *host)
156 {
157   struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host;
158   CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; 
159 #ifdef PCI_KERNEL_TRACE
160   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
161 #endif
162   struct fs_struct *fs;
163   DECLARE_MUTEX_LOCKED(fcQueReady);
164   DECLARE_MUTEX_LOCKED(fcTYOBcomplete); 
165   DECLARE_MUTEX_LOCKED(TachFrozen);  
166   DECLARE_MUTEX_LOCKED(BoardLock);  
167
168   ENTER("WorkerThread");
169
170   lock_kernel();
171         /*
172          * If we were started as result of loading a module, close all of the
173          * user space pages.  We don't need them, and if we didn't close them
174          * they would be locked into memory.
175          */
176   exit_mm(current);
177
178   current->session = 1;
179   current->pgrp = 1;
180         
181   /* Become as one with the init task */
182         
183   exit_fs(current);     /* current->fs->count--; */
184   fs = init_task.fs;
185   // Some kernels compiled for SMP, while actually running
186   // on a uniproc machine, will return NULL for this call
187   if( !fs)
188   {
189     printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n ");
190   }
191  
192   else
193   { 
194     current->fs = fs;
195     atomic_inc(&fs->count);
196   }
197
198   siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
199
200
201   /*
202    * Set the name of this process.
203    */
204   sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no);
205
206   cpqfcHBAdata->fcQueReady = &fcQueReady;  // primary wait point
207   cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete;
208   cpqfcHBAdata->TachFrozen = &TachFrozen;
209     
210  
211   cpqfcHBAdata->worker_thread = current;
212   
213   unlock_kernel();
214
215   if( cpqfcHBAdata->notify_wt != NULL )
216     up( cpqfcHBAdata->notify_wt); // OK to continue
217
218   while(1)
219   {
220     unsigned long flags;
221
222     down_interruptible( &fcQueReady);  // wait for something to do
223
224     if (signal_pending(current) )
225       break;
226     
227     PCI_TRACE( 0x90)
228     // first, take the IO lock so the SCSI upper layers can't call
229     // into our _quecommand function (this also disables INTs)
230     spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
231     PCI_TRACE( 0x90)
232          
233     CPQ_SPINLOCK_HBA( cpqfcHBAdata)
234     // next, set this pointer to indicate to the _quecommand function
235     // that the board is in use, so it should que the command and 
236     // immediately return (we don't actually require the semaphore function
237     // in this driver rev)
238
239     cpqfcHBAdata->BoardLock = &BoardLock;
240
241     PCI_TRACE( 0x90)
242
243     // release the IO lock (and re-enable interrupts)
244     spin_unlock_irqrestore( &io_request_lock, flags);
245
246     // disable OUR HBA interrupt (keep them off as much as possible
247     // during error recovery)
248     disable_irq( cpqfcHBAdata->HostAdapter->irq);
249
250     // OK, let's process the Fibre Channel Link Q and do the work
251     cpqfcTS_WorkTask( HostAdapter);
252
253     // hopefully, no more "work" to do;
254     // re-enable our INTs for "normal" completion processing
255     enable_irq( cpqfcHBAdata->HostAdapter->irq);
256  
257
258     cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued
259     CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
260
261
262     // Now, complete any Cmnd we Q'd up while BoardLock was held
263
264     CompleteBoardLockCmnd( cpqfcHBAdata);
265   
266
267   }
268   // hopefully, the signal was for our module exit...
269   if( cpqfcHBAdata->notify_wt != NULL )
270     up( cpqfcHBAdata->notify_wt); // yep, we're outta here
271 }
272
273
274 // Freeze Tachyon routine.
275 // If Tachyon is already frozen, return FALSE
276 // If Tachyon is not frozen, call freeze function, return TRUE
277 //
278 static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata)
279 {
280   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
281   BOOLEAN FrozeTach = FALSE;
282   // It's possible that the chip is already frozen; if so,
283   // "Freezing" again will NOT! generate another Freeze
284   // Completion Message.
285
286   if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000)
287   {  // (need to freeze...)
288     fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
289
290     // 2. Get Tach freeze confirmation
291     // (synchronize SEST manipulation with Freeze Completion Message)
292     // we need INTs on so semaphore can be set. 
293     enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore
294     down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem.
295     // can we TIMEOUT semaphore wait?? TBD
296     disable_irq( cpqfcHBAdata->HostAdapter->irq); 
297
298     FrozeTach = TRUE;
299   }  // (else, already frozen)
300  
301   return FrozeTach;
302 }  
303
304
305
306
307 // This is the kernel worker thread task, which processes FC
308 // tasks which were queued by the Interrupt handler or by
309 // other WorkTask functions.
310
311 #define DBG 1
312 //#undef DBG
313 void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter)
314 {
315   CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
316   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
317   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
318   ULONG QconsumerNdx;
319   LONG ExchangeID;
320   ULONG ulStatus=0;
321   TachFCHDR_GCMND fchs;
322   PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
323
324   ENTER("WorkTask");
325
326   // copy current index to work on
327   QconsumerNdx = fcLQ->consumer;
328
329   PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90)
330   
331
332   // NOTE: when this switch completes, we will "consume" the Que item
333 //  printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type);
334   switch( fcLQ->Qitem[QconsumerNdx].Type )
335   {
336       // incoming frame - link service (ACC, UNSOL REQ, etc.)
337       // or FCP-SCSI command
338     case SFQ_UNKNOWN:  
339       AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx );
340
341       break;
342   
343     
344     
345     case EXCHANGE_QUEUED:  // an Exchange (i.e. FCP-SCSI) was previously
346                            // Queued because the link was down.  The  
347                            // heartbeat timer detected it and Queued it here.
348                            // We attempt to start it again, and if
349                            // successful we clear the EXCHANGE_Q flag.
350                            // If the link doesn't come up, the Exchange
351                            // will eventually time-out.
352
353       ExchangeID = (LONG)  // x_ID copied from DPC timeout function
354                    fcLQ->Qitem[QconsumerNdx].ulBuff[0];
355
356       // It's possible that a Q'd exchange could have already
357       // been started by other logic (e.g. ABTS process)
358       // Don't start if already started (Q'd flag clear)
359
360       if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED )
361       {
362 //        printk(" *Start Q'd x_ID %Xh: type %Xh ", 
363 //          ExchangeID, Exchanges->fcExchange[ExchangeID].type);
364       
365         ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID);
366         if( !ulStatus )
367         {
368 //          printk("success* ");
369         }       
370         else
371         {
372 #ifdef DBG
373       
374           if( ulStatus == EXCHANGE_QUEUED)
375             printk("Queued* ");
376           else
377             printk("failed* ");
378                 
379 #endif
380         } 
381       }
382       break;
383
384
385     case LINKDOWN:  
386       // (lots of things already done in INT handler) future here?
387       break;
388     
389     
390     case LINKACTIVE:   // Tachyon set the Lup bit in FM status
391                        // NOTE: some misbehaving FC ports (like Tach2.1)
392                        // can re-LIP immediately after a LIP completes.
393       
394       // if "initiator", need to verify LOGs with ports
395 //      printk("\n*LNKUP* ");
396
397       if( fcChip->Options.initiator )
398         SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data
399                   // if SendLogins successfully completes, PortDiscDone
400                   // will be set.
401       
402       
403       // If SendLogins was successful, then we expect to get incoming
404       // ACCepts or REJECTs, which are handled below.
405
406       break;
407
408     // LinkService and Fabric request/reply processing
409     case ELS_FDISC:      // need to send Fabric Discovery (Login)
410     case ELS_FLOGI:      // need to send Fabric Login
411     case ELS_SCR:        // need to send State Change Registration
412     case FCS_NSR:        // need to send Name Service Request
413     case ELS_PLOGI:      // need to send PLOGI
414     case ELS_ACC:        // send generic ACCept
415     case ELS_PLOGI_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
416     case ELS_PRLI_ACC:   // need to send ELS ACCept frame to recv'd PRLI
417     case ELS_LOGO:      // need to send ELS LOGO (logout)
418     case ELS_LOGO_ACC:  // need to send ELS ACCept frame to recv'd PLOGI
419     case ELS_RJT:         // ReJecT reply
420     case ELS_PRLI:       // need to send ELS PRLI
421  
422     
423 //      printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
424       // if PortDiscDone is not set, it means the SendLogins routine
425       // failed to complete -- assume that LDn occurred, so login frames
426       // are invalid
427       if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
428       {
429         printk("Discard Q'd ELS login frame\n");
430         break;  
431       }
432
433       ulStatus = cpqfcTSBuildExchange(
434           cpqfcHBAdata,
435           fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI
436           (TachFCHDR_GCMND*)
437             fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
438           NULL,         // no data (no scatter/gather list)
439           &ExchangeID );// fcController->fcExchanges index, -1 if failed
440
441       if( !ulStatus ) // Exchange setup?
442       {
443         ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
444         if( !ulStatus )
445         {
446           // submitted to Tach's Outbound Que (ERQ PI incremented)
447           // waited for completion for ELS type (Login frames issued
448           // synchronously)
449         }
450         else
451           // check reason for Exchange not being started - we might
452           // want to Queue and start later, or fail with error
453         {
454
455         }
456       }
457
458       else   // Xchange setup failed...
459         printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
460
461       break;
462
463     case SCSI_REPORT_LUNS:
464       // pass the incoming frame (actually, it's a PRLI frame)
465       // so we can send REPORT_LUNS, in order to determine VSA/PDU
466       // FCP-SCSI Lun address mode
467       IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*)
468             fcLQ->Qitem[QconsumerNdx].ulBuff); 
469
470       break;
471       
472
473
474
475     case BLS_ABTS:       // need to ABORT one or more exchanges
476     {
477       LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0];
478       BOOLEAN FrozeTach = FALSE;   
479      
480       if( x_ID > TACH_SEST_LEN )  // (in)sanity check
481       {
482 //      printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID);
483         break;
484       }
485
486
487       if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE
488       {
489 //      printk(" ABTS %Xh Scsi Cmnd null! ", x_ID);
490         
491        break;  // nothing to abort!
492       }
493
494 //#define ABTS_DBG
495 #ifdef ABTS_DBG
496       printk("INV SEST[%X] ", x_ID); 
497       if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT)
498       {
499         printk("FC2TO");
500       }
501       if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)
502       {
503         printk("IA");
504       }
505       if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED)
506       {
507         printk("PORTID");
508       }
509       if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED)
510       {
511         printk("DEVRM");
512       }
513       if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX)
514       {
515         printk("LKF");
516       }
517       if( Exchanges->fcExchange[x_ID].status & FRAME_TO)
518       {
519         printk("FRMTO");
520       }
521       if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY)
522       {
523         printk("ABSQ");
524       }
525       if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME)
526       {
527         printk("SFQFR");
528       }
529
530       if( Exchanges->fcExchange[ x_ID].type == 0x2000)
531         printk(" WR");
532       else if( Exchanges->fcExchange[ x_ID].type == 0x3000)
533         printk(" RD");
534       else if( Exchanges->fcExchange[ x_ID].type == 0x10)
535         printk(" ABTS");
536       else
537         printk(" %Xh", Exchanges->fcExchange[ x_ID].type); 
538
539       if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT))
540       {
541         printk(" Cmd %p, ", 
542           Exchanges->fcExchange[ x_ID].Cmnd);
543
544         printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", 
545           cpqfcHBAdata->HBAnum,
546           Exchanges->fcExchange[ x_ID].Cmnd->channel,
547           Exchanges->fcExchange[ x_ID].Cmnd->target,
548           Exchanges->fcExchange[ x_ID].Cmnd->lun,
549           Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
550       }
551       else  // assume that Cmnd ptr is invalid on _abort()
552       {
553         printk(" Cmd ptr invalid\n");
554       }
555      
556 #endif      
557
558       
559       // Steps to ABORT a SEST exchange:
560       // 1. Freeze TL SCSI assists & ERQ (everything)
561       // 2. Receive FROZEN inbound CM (must succeed!)
562       // 3. Invalidate x_ID SEST entry 
563       // 4. Resume TL SCSI assists & ERQ (everything)
564       // 5. Build/start on exchange - change "type" to BLS_ABTS,
565       //    timeout to X sec (RA_TOV from PLDA is actually 0)
566       // 6. Set Exchange Q'd status if ABTS cannot be started,
567       //    or simply complete Exchange in "Terminate" condition
568
569   PCI_TRACEO( x_ID, 0xB4)
570       
571       // 1 & 2 . Freeze Tach & get confirmation of freeze
572       FrozeTach = FreezeTach( cpqfcHBAdata);
573
574       // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange.
575       // FC2_TIMEOUT means we are originating the abort, while
576       // TARGET_ABORT means we are ACCepting an abort.
577       // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are 
578       // all from Tachyon:
579       // Exchange was corrupted by LDn or other FC physical failure
580       // INITIATOR_ABORT means the upper layer driver/application
581       // requested the abort.
582
583
584           
585       // clear bit 31 (VALid), to invalidate & take control from TL
586       fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
587
588
589       // examine and Tach's "Linked List" for IWEs that 
590       // received (nearly) simultaneous transfer ready (XRDY) 
591       // repair linked list if necessary (TBD!)
592       // (If we ignore the "Linked List", we will time out
593       // WRITE commands where we received the FCP-SCSI XFRDY
594       // frame (because Tachyon didn't processes it).  Linked List
595       // management should be done as an optimization.
596
597 //       readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST ));
598
599
600       
601
602       // 4. Resume all Tachlite functions (for other open Exchanges)
603       // as quickly as possible to allow other exchanges to other ports
604       // to resume.  Freezing Tachyon may cause cascading errors, because
605       // any received SEST frame cannot be processed by the SEST.
606       // Don't "unfreeze" unless Link is operational
607       if( FrozeTach )  // did we just freeze it (above)?
608         fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
609       
610
611   PCI_TRACEO( x_ID, 0xB4)
612
613       // Note there is no confirmation that the chip is "unfrozen".  Also,
614       // if the Link is down when unfreeze is called, it has no effect.
615       // Chip will unfreeze when the Link is back up.
616
617       // 5. Now send out Abort commands if possible
618       // Some Aborts can't be "sent" (Port_id changed or gone);
619       // if the device is gone, there is no port_id to send the ABTS to.
620
621       if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED)
622                           &&
623           !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) )
624       {
625         Exchanges->fcExchange[ x_ID].type = BLS_ABTS;
626         fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id;
627         ulStatus = cpqfcTSBuildExchange(
628           cpqfcHBAdata,
629           BLS_ABTS,
630           &fchs,        // (uses only s_id)
631           NULL,         // (no scatter/gather list for ABTS)
632           &x_ID );// ABTS on this Exchange ID
633
634         if( !ulStatus ) // Exchange setup build OK?
635         {
636
637             // ABTS may be needed because an Exchange was corrupted
638             // by a Link disruption.  If the Link is UP, we can
639             // presume that this ABTS can start immediately; otherwise,
640             // set Que'd status so the Login functions
641             // can restart it when the FC physical Link is restored
642           if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init?
643           {                         
644 //                printk(" *set Q status x_ID %Xh on LDn* ", x_ID);
645                 Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED;
646           }
647
648           else  // what FC device (port_id) does the Cmd belong to?
649           {
650             PFC_LOGGEDIN_PORT pLoggedInPort = 
651               Exchanges->fcExchange[ x_ID].pLoggedInPort;
652             
653             // if Port is logged in, we might start the abort.
654         
655             if( (pLoggedInPort != NULL) 
656                               &&
657                 (pLoggedInPort->prli == TRUE) ) 
658             {
659               // it's possible that an Exchange has already been Queued
660               // to start after Login completes.  Check and don't
661               // start it (again) here if Q'd status set
662 //          printk(" ABTS xchg %Xh ", x_ID);            
663               if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED)
664               {
665 //                  printk("already Q'd ");
666               }
667               else
668               {
669 //                  printk("starting ");
670                 
671                 fcChip->fcStats.FC2aborted++; 
672                 ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
673                 if( !ulStatus )
674                 {
675                     // OK
676                     // submitted to Tach's Outbound Que (ERQ PI incremented)
677                 }
678                 else
679                 {
680 /*                   printk("ABTS exchange start failed -status %Xh, x_ID %Xh ",
681                         ulStatus, x_ID);
682 */
683                 } 
684               }
685             }
686             else
687             {
688 /*                printk(" ABTS NOT starting xchg %Xh, %p ",
689                                x_ID, pLoggedInPort);
690                   if( pLoggedInPort )
691                     printk("prli %d ", pLoggedInPort->prli);
692 */
693             }           
694           }
695         }
696         else  // what the #@!
697         {  // how do we fail to build an Exchange for ABTS??
698               printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n",
699                 ulStatus, x_ID);
700         }
701       }
702       else   // abort without ABTS -- just complete exchange/Cmnd to Linux
703       {
704 //            printk(" *Terminating x_ID %Xh on %Xh* ", 
705 //                  x_ID, Exchanges->fcExchange[x_ID].status);
706         cpqfcTSCompleteExchange( fcChip, x_ID);
707
708
709       }
710     } // end of ABTS case
711       break;
712
713
714
715     case BLS_ABTS_ACC:   // need to ACCept one ABTS
716                          // (NOTE! this code not updated for Linux yet..)
717       
718
719       printk(" *ABTS_ACC* ");
720       // 1. Freeze TL
721
722       fcChip->FreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
723
724       memcpy(  // copy the incoming ABTS frame
725         &fchs,
726         fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
727         sizeof( fchs));
728
729       // 3. OK, Tachyon is frozen so we can invalidate SEST entry 
730       // (if necessary)
731       // Status FC2_TIMEOUT means we are originating the abort, while
732       // TARGET_ABORT means we are ACCepting an abort
733       
734       ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange
735 //      printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID);
736
737
738       // sanity check on received ExchangeID
739       if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT )
740       {
741           // clear bit 31 (VALid), to invalidate & take control from TL
742 //          printk("Invalidating SEST exchange %Xh\n", ExchangeID);
743           fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF;
744       }
745
746
747       // 4. Resume all Tachlite functions (for other open Exchanges)
748       // as quickly as possible to allow other exchanges to other ports
749       // to resume.  Freezing Tachyon for too long may royally screw
750       // up everything!
751       fcChip->UnFreezeTachyon( fcChip, 2);  // both ERQ and FCP assists
752       
753       // Note there is no confirmation that the chip is "unfrozen".  Also,
754       // if the Link is down when unfreeze is called, it has no effect.
755       // Chip will unfreeze when the Link is back up.
756
757       // 5. Now send out Abort ACC reply for this exchange
758       Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC;
759       
760       fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id;
761       ulStatus = cpqfcTSBuildExchange(
762             cpqfcHBAdata,
763             BLS_ABTS_ACC,
764             &fchs,
765             NULL,         // no data (no scatter/gather list)
766             &ExchangeID );// fcController->fcExchanges index, -1 if failed
767
768       if( !ulStatus ) // Exchange setup?
769       {
770         ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
771         if( !ulStatus )
772         {
773           // submitted to Tach's Outbound Que (ERQ PI incremented)
774           // waited for completion for ELS type (Login frames issued
775           // synchronously)
776         }
777         else
778           // check reason for Exchange not being started - we might
779           // want to Queue and start later, or fail with error
780         {
781
782         } 
783       }
784       break;
785
786
787     case BLS_ABTS_RJT:   // need to ReJecT one ABTS; reject implies the
788                          // exchange doesn't exist in the TARGET context.
789                          // ExchangeID has to come from LinkService space.
790
791       printk(" *ABTS_RJT* ");
792       ulStatus = cpqfcTSBuildExchange(
793             cpqfcHBAdata,
794             BLS_ABTS_RJT,
795             (TachFCHDR_GCMND*)
796               fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
797             NULL,         // no data (no scatter/gather list)
798             &ExchangeID );// fcController->fcExchanges index, -1 if failed
799
800       if( !ulStatus ) // Exchange setup OK?
801       {
802         ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
803         // If it fails, we aren't required to retry.
804       }
805       if( ulStatus )
806       {
807         printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
808       }
809       else
810       {
811         printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
812       
813       }
814
815       break;
816
817
818
819     default:
820       break;
821   }                   // end switch
822 //doNothing:
823     // done with this item - now set the NEXT index
824
825   if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test
826   {
827     fcLQ->consumer = 0;
828   }
829   else
830   { 
831     fcLQ->consumer++;
832   }
833
834   PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94)
835
836   LEAVE("WorkTask");
837   return;
838 }
839
840
841
842
843 // When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login)
844 // commands come in, post to the LinkQ so that action can be taken outside the
845 // interrupt handler.  
846 // This circular Q works like Tachyon's que - the producer points to the next
847 // (unused) entry.  Called by Interrupt handler, WorkerThread, Timer
848 // sputlinkq
849 void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata,
850   int Type, 
851   void *QueContent)
852 {
853   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
854 //  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
855   PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
856   ULONG ndx;
857   
858   ENTER("cpqfcTSPutLinkQ");
859
860   ndx = fcLQ->producer;
861   
862   ndx += 1;  // test for Que full
863
864
865   
866   if( ndx >= FC_LINKQ_DEPTH ) // rollover test
867     ndx = 0;
868
869   if( ndx == fcLQ->consumer )   // QUE full test
870   {
871                        // QUE was full! lost LK command (fatal to logic)
872     fcChip->fcStats.lnkQueFull++;
873
874     printk("*LinkQ Full!*");
875     TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
876 /*
877     {
878       int i;
879       printk("LinkQ PI %d, CI %d\n", fcLQ->producer,
880         fcLQ->consumer);
881                       
882       for( i=0; i< FC_LINKQ_DEPTH; )
883       {
884         printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type);
885         if( (++i %8) == 0) printk("\n");
886       }
887   
888     }
889 */    
890     printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung
891   }
892   else                        // QUE next element
893   {
894     // Prevent certain multiple (back-to-back) requests.
895     // This is important in that we don't want to issue multiple
896     // ABTS for the same Exchange, or do multiple FM inits, etc.
897     // We can never be sure of the timing of events reported to
898     // us by Tach's IMQ, which can depend on system/bus speeds,
899     // FC physical link circumstances, etc.
900      
901     if( (fcLQ->producer != fcLQ->consumer)
902             && 
903         (Type == FMINIT)  )
904     {
905       LONG lastNdx;  // compute previous producer index
906       if( fcLQ->producer)
907         lastNdx = fcLQ->producer- 1;
908       else
909         lastNdx = FC_LINKQ_DEPTH-1;
910
911
912       if( fcLQ->Qitem[lastNdx].Type == FMINIT)
913       {
914 //        printk(" *skip FMINIT Q post* ");
915 //        goto DoneWithPutQ;
916       }
917
918     }
919
920     // OK, add the Q'd item...
921     
922     fcLQ->Qitem[fcLQ->producer].Type = Type;
923    
924     memcpy(
925         fcLQ->Qitem[fcLQ->producer].ulBuff,
926         QueContent, 
927         sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff));
928
929     fcLQ->producer = ndx;  // increment Que producer
930
931     // set semaphore to wake up Kernel (worker) thread
932     // 
933     up( cpqfcHBAdata->fcQueReady );
934   }
935
936 //DoneWithPutQ:
937
938   LEAVE("cpqfcTSPutLinkQ");
939 }
940
941
942
943
944 // reset device ext FC link Q
945 void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata)
946    
947 {
948   PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
949   fcLQ->producer = 0;
950   fcLQ->consumer = 0;
951
952 }
953
954
955
956
957
958 // When Tachyon gets an unassisted FCP-SCSI frame, post here so
959 // an arbitrary context thread (e.g. IOCTL loopback test function)
960 // can process it.
961
962 // (NOTE: Not revised for Linux)
963 // This Q works like Tachyon's que - the producer points to the next
964 // (unused) entry.
965 void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata,
966   int Type, 
967   void *QueContent)
968 {
969 //  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
970 //  PTACHYON fcChip = &cpqfcHBAdata->fcChip;
971
972 //  ULONG ndx;
973
974 //  ULONG *pExchangeID;
975 //  LONG ExchangeID;
976
977 /*
978   KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock);
979   ndx = pDevExt->fcScsiQue.producer + 1;  // test for Que full
980
981   if( ndx >= FC_SCSIQ_DEPTH ) // rollover test
982     ndx = 0;
983
984   if( ndx == pDevExt->fcScsiQue.consumer )   // QUE full test
985   {
986                        // QUE was full! lost LK command (fatal to logic)
987     fcChip->fcStats.ScsiQueFull++;
988 #ifdef DBG
989     printk( "fcPutScsiQue - FULL!\n");
990 #endif
991
992   }
993   else                        // QUE next element
994   {
995     pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type;
996     
997     if( Type == FCP_RSP )
998     {
999       // this TL inbound message type means that a TL SEST exchange has
1000       // copied an FCP response frame into a buffer pointed to by the SEST
1001       // entry.  That buffer is allocated in the SEST structure at ->RspHDR.
1002       // Copy the RspHDR for use by the Que handler.
1003       pExchangeID = (ULONG *)QueContent;
1004       
1005       memcpy(
1006               pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
1007         &fcChip->SEST->RspHDR[ *pExchangeID ],
1008               sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size)
1009       
1010     }
1011     else
1012     {
1013       memcpy(
1014               pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
1015         QueContent, 
1016               sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff));
1017     }
1018       
1019     pDevExt->fcScsiQue.producer = ndx;  // increment Que
1020
1021
1022     KeSetEvent( &pDevExt->TYIBscsi,  // signal any waiting thread
1023        0,                    // no priority boost
1024                    FALSE );              // no waiting later for this event
1025   }
1026   KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock);
1027 */
1028 }
1029
1030
1031
1032
1033
1034
1035
1036 static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*);
1037
1038 static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
1039
1040 static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
1041
1042 void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
1043                 PFC_LOGGEDIN_PORT pFcPort)
1044 {
1045   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1046
1047   if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric
1048   {
1049     fcChip->fcStats.logouts++;
1050     printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", 
1051         (ULONG)pFcPort->u.liWWN,
1052         (ULONG)(pFcPort->u.liWWN >>32),
1053         pFcPort->port_id);
1054
1055   // Terminate I/O with this (Linux) Scsi target
1056     cpqfcTSTerminateExchange( cpqfcHBAdata, 
1057                             &pFcPort->ScsiNexus,
1058                             DEVICE_REMOVED);
1059   }
1060                         
1061   // Do an "implicit logout" - we can't really Logout the device
1062   // (i.e. with LOGOut Request) because of port_id confusion
1063   // (i.e. the Other port has no port_id).
1064   // A new login for that WWN will have to re-write port_id (0 invalid)
1065   pFcPort->port_id = 0;  // invalid!
1066   pFcPort->pdisc = FALSE;
1067   pFcPort->prli = FALSE;
1068   pFcPort->plogi = FALSE;
1069   pFcPort->flogi = FALSE;
1070   pFcPort->LOGO_timer = 0;
1071   pFcPort->device_blocked = TRUE; // block Scsi Requests
1072   pFcPort->ScsiNexus.VolumeSetAddressing=0;     
1073 }
1074
1075   
1076 // On FC-AL, there is a chance that a previously known device can
1077 // be quietly removed (e.g. with non-managed hub), 
1078 // while a NEW device (with different WWN) took the same alpa or
1079 // even 24-bit port_id.  This chance is unlikely but we must always
1080 // check for it.
1081 static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata,
1082                 PFC_LOGGEDIN_PORT pLoggedInPort)
1083 {
1084   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1085   // set "other port" at beginning of fcPorts list
1086   PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort;
1087   while( pOtherPortWithPortId ) 
1088   {
1089     if( (pOtherPortWithPortId->port_id == 
1090          pLoggedInPort->port_id) 
1091                     &&
1092          (pOtherPortWithPortId != pLoggedInPort) )
1093     {
1094       // trouble!  (Implicitly) Log the other guy out
1095       printk(" *port_id %Xh is duplicated!* ", 
1096         pOtherPortWithPortId->port_id);
1097       cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId); 
1098    }
1099     pOtherPortWithPortId = pOtherPortWithPortId->pNextPort;
1100   }
1101 }
1102
1103
1104
1105
1106
1107
1108 // Dynamic Memory Allocation for newly discovered FC Ports.
1109 // For simplicity, maintain fcPorts structs for ALL
1110 // for discovered devices, including those we never do I/O with
1111 // (e.g. Fabric addresses)
1112
1113 static PFC_LOGGEDIN_PORT CreateFcPort( 
1114           CPQFCHBA* cpqfcHBAdata, 
1115           PFC_LOGGEDIN_PORT pLastLoggedInPort, 
1116           TachFCHDR_GCMND* fchs,
1117           LOGIN_PAYLOAD* plogi)
1118 {
1119   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1120   PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL;
1121   int i;
1122
1123
1124   printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id);
1125   for( i=3; i>=0; i--)   // copy the LOGIN port's WWN
1126     printk("%02X", plogi->port_name[i]);
1127   for( i=7; i>3; i--)   // copy the LOGIN port's WWN
1128     printk("%02X", plogi->port_name[i]);
1129
1130
1131   // allocate mem for new port
1132   // (these are small and rare allocations...)
1133   pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC );
1134
1135     
1136   // allocation succeeded?  Fill out NEW PORT
1137   if( pNextLoggedInPort )
1138   {    
1139                               // clear out any garbage (sometimes exists)
1140     memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT));
1141
1142
1143     // If we login to a Fabric, we don't want to treat it
1144     // as a SCSI device...
1145     if( (fchs->s_id & 0xFFF000) != 0xFFF000)
1146     {
1147       int i;
1148       
1149       // create a unique "virtual" SCSI Nexus (for now, just a
1150       // new target ID) -- we will update channel/target on REPORT_LUNS
1151       // special case for very first SCSI target...
1152       if( cpqfcHBAdata->HostAdapter->max_id == 0)
1153       {
1154         pNextLoggedInPort->ScsiNexus.target = 0;
1155         fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub"
1156       }
1157       else
1158       {
1159         pNextLoggedInPort->ScsiNexus.target =
1160           cpqfcHBAdata->HostAdapter->max_id;
1161       }
1162
1163       // initialize the lun[] Nexus struct for lun masking      
1164       for( i=0; i< CPQFCTS_MAX_LUN; i++)
1165         pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED
1166       
1167       pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port
1168       
1169       printk(" SCSI Chan/Trgt %d/%d", 
1170           pNextLoggedInPort->ScsiNexus.channel,
1171           pNextLoggedInPort->ScsiNexus.target);
1172  
1173       // tell Scsi layers about the new target...
1174       cpqfcHBAdata->HostAdapter->max_id++; 
1175 //    printk("HostAdapter->max_id = %d\n",
1176 //      cpqfcHBAdata->HostAdapter->max_id);                 
1177     }                          
1178     else
1179     {
1180       // device is NOT SCSI (in case of Fabric)
1181       pNextLoggedInPort->ScsiNexus.target = -1;  // invalid
1182     }
1183
1184           // create forward link to new port
1185     pLastLoggedInPort->pNextPort = pNextLoggedInPort;
1186     printk("\n");
1187
1188   }     
1189   return pNextLoggedInPort;  // NULL on allocation failure
1190 }   // end NEW PORT (WWN) logic
1191
1192
1193
1194 // For certain cases, we want to terminate exchanges without
1195 // sending ABTS to the device.  Examples include when an FC
1196 // device changed it's port_id after Loop re-init, or when
1197 // the device sent us a logout.  In the case of changed port_id,
1198 // we want to complete the command and return SOFT_ERROR to
1199 // force a re-try.  In the case of LOGOut, we might return
1200 // BAD_TARGET if the device is really gone.
1201 // Since we must ensure that Tachyon is not operating on the
1202 // exchange, we have to freeze the chip
1203 // sterminateex
1204 void cpqfcTSTerminateExchange( 
1205   CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus)
1206 {
1207   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1208   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
1209   ULONG x_ID;
1210
1211   if( ScsiNexus )
1212   {
1213 //    printk("TerminateExchange: ScsiNexus chan/target %d/%d\n",
1214 //                  ScsiNexus->channel, ScsiNexus->target);
1215
1216   } 
1217   
1218   for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
1219   {
1220     if( Exchanges->fcExchange[x_ID].type )  // in use?
1221     {
1222       if( ScsiNexus == NULL ) // our HBA changed - term. all
1223       {
1224         Exchanges->fcExchange[x_ID].status = TerminateStatus;
1225         cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); 
1226       }
1227       else
1228       {
1229         // If a device, according to WWN, has been removed, it's
1230         // port_id may be used by another working device, so we
1231         // have to terminate by SCSI target, NOT port_id.
1232         if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress?
1233         {                        
1234           if( (Exchanges->fcExchange[x_ID].Cmnd->target == ScsiNexus->target)
1235                         &&
1236             (Exchanges->fcExchange[x_ID].Cmnd->channel == ScsiNexus->channel)) 
1237           {
1238             Exchanges->fcExchange[x_ID].status = TerminateStatus;
1239             cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out
1240           }
1241         }
1242
1243         // (in case we ever need it...)
1244         // all SEST structures have a remote node ID at SEST DWORD 2
1245         //          if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8)
1246         //                ==  port_id)
1247       } 
1248     }
1249   }
1250 }
1251
1252
1253 static void ProcessELS_Request( 
1254               CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
1255 {
1256   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1257 //  FC_EXCHANGES *Exchanges = fcChip->Exchanges;
1258 //  ULONG ox_id = (fchs->ox_rx_id >>16);
1259   PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort;
1260   BOOLEAN NeedReject = FALSE;
1261   ULONG ls_reject_code = 0; // default don'n know??
1262
1263
1264   // Check the incoming frame for a supported ELS type
1265   switch( fchs->pl[0] & 0xFFFF)
1266   {
1267   case 0x0050: //  PDISC?
1268
1269     // Payload for PLOGI and PDISC is identical (request & reply)
1270     if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
1271     {
1272       LOGIN_PAYLOAD logi;       // FC-PH Port Login
1273       
1274       // PDISC payload OK. If critical login fields
1275       // (e.g. WWN) matches last login for this port_id,
1276       // we may resume any prior exchanges
1277       // with the other port
1278
1279       
1280       BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
1281    
1282       pLoggedInPort = fcFindLoggedInPort( 
1283              fcChip, 
1284              NULL,     // don't search Scsi Nexus
1285              0,        // don't search linked list for port_id
1286              &logi.port_name[0],     // search linked list for WWN
1287              &pLastLoggedInPort);  // must return non-NULL; when a port_id
1288                                    // is not found, this pointer marks the
1289                                    // end of the singly linked list
1290     
1291       if( pLoggedInPort != NULL)   // WWN found (prior login OK)
1292       { 
1293            
1294         if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
1295         {
1296           // Yes.  We were expecting PDISC?
1297           if( pLoggedInPort->pdisc )
1298           {
1299             // Yes; set fields accordingly.     (PDISC, not Originator)
1300             SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE);
1301        
1302             // send 'ACC' reply 
1303             cpqfcTSPutLinkQue( cpqfcHBAdata, 
1304                           ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
1305                           fchs );
1306
1307             // OK to resume I/O...
1308           }
1309           else
1310           {
1311             printk("Not expecting PDISC (pdisc=FALSE)\n");
1312             NeedReject = TRUE;
1313             // set reject reason code 
1314             ls_reject_code = 
1315               LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
1316           }
1317         }
1318         else
1319         {
1320           if( pLoggedInPort->port_id != 0)
1321           {
1322             printk("PDISC PortID change: old %Xh, new %Xh\n",
1323               pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
1324           }
1325           NeedReject = TRUE;
1326           // set reject reason code 
1327           ls_reject_code = 
1328             LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
1329                   
1330         }
1331       }
1332       else
1333       {
1334         printk("PDISC Request from unknown WWN\n");
1335         NeedReject = TRUE;
1336           
1337         // set reject reason code 
1338         ls_reject_code = 
1339           LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME);
1340       }
1341
1342     }
1343     else // Payload unacceptable
1344     {
1345       printk("payload unacceptable\n");
1346       NeedReject = TRUE;  // reject code already set
1347       
1348     }
1349
1350     if( NeedReject)
1351     {
1352       ULONG port_id;
1353       // The PDISC failed.  Set login struct flags accordingly,
1354       // terminate any I/O to this port, and Q a PLOGI
1355       if( pLoggedInPort )
1356       {
1357         pLoggedInPort->pdisc = FALSE;
1358         pLoggedInPort->prli = FALSE;
1359         pLoggedInPort->plogi = FALSE;
1360         
1361         cpqfcTSTerminateExchange( cpqfcHBAdata, 
1362           &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
1363         port_id = pLoggedInPort->port_id;
1364       }
1365       else
1366       {
1367         port_id = fchs->s_id &0xFFFFFF;
1368       }
1369       fchs->reserved = ls_reject_code; // borrow this (unused) field
1370       cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
1371     }
1372    
1373     break;
1374
1375
1376
1377   case 0x0003: //  PLOGI?
1378
1379     // Payload for PLOGI and PDISC is identical (request & reply)
1380     if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
1381     {
1382       LOGIN_PAYLOAD logi;       // FC-PH Port Login
1383       BOOLEAN NeedReject = FALSE;
1384       
1385       // PDISC payload OK. If critical login fields
1386       // (e.g. WWN) matches last login for this port_id,
1387       // we may resume any prior exchanges
1388       // with the other port
1389
1390       
1391       BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
1392    
1393       pLoggedInPort = fcFindLoggedInPort( 
1394              fcChip, 
1395              NULL,       // don't search Scsi Nexus
1396              0,        // don't search linked list for port_id
1397              &logi.port_name[0],     // search linked list for WWN
1398              &pLastLoggedInPort);  // must return non-NULL; when a port_id
1399                                    // is not found, this pointer marks the
1400                                    // end of the singly linked list
1401     
1402       if( pLoggedInPort == NULL)   // WWN not found -New Port
1403       { 
1404         pLoggedInPort = CreateFcPort( 
1405                           cpqfcHBAdata, 
1406                           pLastLoggedInPort, 
1407                           fchs,
1408                           &logi);
1409         if( pLoggedInPort == NULL )
1410         {
1411           printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
1412           // Now Q a LOGOut Request, since we won't be talking to that device
1413         
1414           NeedReject = TRUE;  
1415           
1416           // set reject reason code 
1417           ls_reject_code = 
1418             LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES);
1419             
1420         }
1421       }
1422       if( !NeedReject )
1423       {
1424       
1425         // OK - we have valid fcPort ptr; set fields accordingly.   
1426         //                         (not PDISC, not Originator)
1427         SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE); 
1428
1429         // send 'ACC' reply 
1430         cpqfcTSPutLinkQue( cpqfcHBAdata, 
1431                       ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
1432                       fchs );
1433       }
1434     }
1435     else // Payload unacceptable
1436     {
1437       printk("payload unacceptable\n");
1438       NeedReject = TRUE;  // reject code already set
1439     }
1440
1441     if( NeedReject)
1442     {
1443       // The PDISC failed.  Set login struct flags accordingly,
1444       // terminate any I/O to this port, and Q a PLOGI
1445       pLoggedInPort->pdisc = FALSE;
1446       pLoggedInPort->prli = FALSE;
1447       pLoggedInPort->plogi = FALSE;
1448         
1449       fchs->reserved = ls_reject_code; // borrow this (unused) field
1450
1451       // send 'RJT' reply 
1452       cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
1453     }
1454    
1455     // terminate any exchanges with this device...
1456     if( pLoggedInPort )
1457     {
1458       cpqfcTSTerminateExchange( cpqfcHBAdata, 
1459         &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
1460     }
1461     break;
1462
1463
1464
1465   case 0x1020:  // PRLI?
1466   {
1467     BOOLEAN NeedReject = TRUE;
1468     pLoggedInPort = fcFindLoggedInPort( 
1469            fcChip, 
1470            NULL,       // don't search Scsi Nexus
1471            (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
1472            NULL,     // DON'T search linked list for WWN
1473            NULL);    // don't care
1474       
1475     if( pLoggedInPort == NULL ) 
1476     {
1477       // huh?
1478       printk(" Unexpected PRLI Request -not logged in!\n");
1479
1480       // set reject reason code 
1481       ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
1482       
1483       // Q a LOGOut here?
1484     }
1485     else
1486     {
1487       // verify the PRLI ACC payload
1488       if( !verify_PRLI( fchs, &ls_reject_code) )
1489       {
1490         // PRLI Reply is acceptable; were we expecting it?
1491         if( pLoggedInPort->plogi ) 
1492         { 
1493           // yes, we expected the PRLI ACC  (not PDISC; not Originator)
1494           SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
1495
1496           // Q an ACCept Reply
1497           cpqfcTSPutLinkQue( cpqfcHBAdata,
1498                         ELS_PRLI_ACC, 
1499                         fchs );   
1500           
1501           NeedReject = FALSE;
1502         }
1503         else
1504         {
1505           // huh?
1506           printk(" (unexpected) PRLI REQEST with plogi FALSE\n");
1507
1508           // set reject reason code 
1509           ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
1510     
1511           // Q a LOGOut here?
1512           
1513         }
1514       }
1515       else
1516       {
1517         printk(" PRLI REQUEST payload failed verify\n");
1518         // (reject code set by "verify")
1519
1520         // Q a LOGOut here?
1521       }
1522     }
1523
1524     if( NeedReject )
1525     {
1526       // Q a ReJecT Reply with reason code
1527       fchs->reserved = ls_reject_code;
1528       cpqfcTSPutLinkQue( cpqfcHBAdata,
1529                     ELS_RJT, // Q Type
1530                     fchs );  
1531     }
1532   }
1533     break;
1534  
1535
1536     
1537
1538   case  0x0005:  // LOGOut?
1539   {
1540   // was this LOGOUT because we sent a ELS_PDISC to an FC device
1541   // with changed (or new) port_id, or does the port refuse 
1542   // to communicate to us?
1543   // We maintain a logout counter - if we get 3 consecutive LOGOuts,
1544   // give up!
1545     LOGOUT_PAYLOAD logo;
1546     BOOLEAN GiveUpOnDevice = FALSE;
1547     ULONG ls_reject_code = 0;
1548     
1549     BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo));
1550
1551     pLoggedInPort = fcFindLoggedInPort( 
1552            fcChip, 
1553            NULL,     // don't search Scsi Nexus
1554            0,        // don't search linked list for port_id
1555            &logo.port_name[0],     // search linked list for WWN
1556            NULL);    // don't care about end of list
1557     
1558     if( pLoggedInPort ) // found the device?
1559     {
1560       // Q an ACC reply 
1561       cpqfcTSPutLinkQue( cpqfcHBAdata,
1562                     ELS_LOGO_ACC, // Q Type
1563                     fchs );       // device to respond to
1564
1565       // set login struct fields (LOGO_counter increment)
1566       SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
1567       
1568       // are we an Initiator?
1569       if( fcChip->Options.initiator)  
1570       {
1571         // we're an Initiator, so check if we should 
1572         // try (another?) login
1573
1574         // Fabrics routinely log out from us after
1575         // getting device info - don't try to log them
1576         // back in.
1577         if( (fchs->s_id & 0xFFF000) == 0xFFF000 )
1578         {
1579           ; // do nothing
1580         }
1581         else if( pLoggedInPort->LOGO_counter <= 3)
1582         {
1583           // try (another) login (PLOGI request)
1584           
1585           cpqfcTSPutLinkQue( cpqfcHBAdata,
1586                     ELS_PLOGI, // Q Type
1587                     fchs );  
1588         
1589           // Terminate I/O with "retry" potential
1590           cpqfcTSTerminateExchange( cpqfcHBAdata, 
1591                                     &pLoggedInPort->ScsiNexus,
1592                                     PORTID_CHANGED);
1593         }
1594         else
1595         {
1596           printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n",
1597                           fchs->s_id &&0xFFFFFF);
1598           GiveUpOnDevice = TRUE;
1599         }
1600       }
1601       else
1602       {
1603         GiveUpOnDevice = TRUE;
1604       }
1605
1606
1607       if( GiveUpOnDevice == TRUE )
1608       {
1609         cpqfcTSTerminateExchange( cpqfcHBAdata, 
1610                                   &pLoggedInPort->ScsiNexus,
1611                                   DEVICE_REMOVED);
1612       }
1613     }  
1614     else  // we don't know this WWN!
1615     {
1616       // Q a ReJecT Reply with reason code
1617       fchs->reserved = ls_reject_code;
1618       cpqfcTSPutLinkQue( cpqfcHBAdata,
1619                     ELS_RJT, // Q Type
1620                     fchs );  
1621     }
1622   }
1623     break;
1624
1625
1626
1627
1628   // FABRIC only case
1629   case 0x0461:  // ELS RSCN (Registered State Change Notification)?
1630   {
1631     int Ports;
1632     int i;
1633     __u32 Buff;
1634     // Typically, one or more devices have been added to or dropped
1635     // from the Fabric.
1636     // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997)
1637     // The first 32-bit word has a 2-byte Payload Length, which
1638     // includes the 4 bytes of the first word.  Consequently,
1639     // this PL len must never be less than 4, must be a multiple of 4,
1640     // and has a specified max value 256.
1641     // (Endianess!)
1642     Ports = ((fchs->pl[0] >>24) - 4) / 4;
1643     Ports = Ports > 63 ? 63 : Ports;
1644     
1645     printk(" RSCN ports: %d\n", Ports);
1646     if( Ports <= 0 )  // huh?
1647     {
1648       // ReJecT the command
1649       fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0);
1650     
1651       cpqfcTSPutLinkQue( cpqfcHBAdata,
1652                     ELS_RJT, // Q Type
1653                     fchs ); 
1654       
1655       break;
1656     }
1657     else  // Accept the command
1658     {
1659        cpqfcTSPutLinkQue( cpqfcHBAdata,
1660                     ELS_ACC, // Q Type
1661                     fchs ); 
1662     }
1663     
1664       // Check the "address format" to determine action.
1665       // We have 3 cases:
1666       // 0 = Port Address; 24-bit address of affected device
1667       // 1 = Area Address; MS 16 bits valid
1668       // 2 = Domain Address; MS 8 bits valid
1669     for( i=0; i<Ports; i++)
1670     { 
1671       BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4);
1672       switch( Buff & 0xFF000000)
1673       {
1674
1675       case 0:  // Port Address?
1676         
1677       case 0x01000000: // Area Domain?
1678       case 0x02000000: // Domain Address
1679         // For example, "port_id" 0x201300 
1680         // OK, let's try a Name Service Request (Query)
1681       fchs->s_id = 0xFFFFFC;  // Name Server Address
1682       cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
1683
1684       break;
1685         
1686         
1687       default:  // huh? new value on version change?
1688       break;
1689       }
1690     }
1691   }    
1692   break;    
1693
1694
1695
1696     
1697   default:  // don't support this request (yet)
1698     // set reject reason code 
1699     fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 
1700                                     REQUEST_NOT_SUPPORTED);
1701     
1702     cpqfcTSPutLinkQue( cpqfcHBAdata,
1703                     ELS_RJT, // Q Type
1704                     fchs );     
1705     break;  
1706   }
1707 }
1708
1709
1710 static void ProcessELS_Reply( 
1711                 CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
1712 {
1713   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
1714   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
1715   ULONG ox_id = (fchs->ox_rx_id >>16);
1716   ULONG ls_reject_code;
1717   PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
1718   
1719   // If this is a valid reply, then we MUST have sent a request.
1720   // Verify that we can find a valid request OX_ID corresponding to
1721   // this reply
1722
1723   
1724   if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
1725   {
1726     printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ", 
1727                     ox_id, fchs->ox_rx_id & 0xffff);
1728     goto Quit;  // exit this routine
1729   }
1730
1731
1732   // Is the reply a RJT (reject)?
1733   if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply?
1734   {
1735 //  ******  REJECT REPLY  ********
1736     switch( Exchanges->fcExchange[ox_id].type )
1737     {
1738           
1739     case ELS_FDISC:  // we sent out Fabric Discovery
1740     case ELS_FLOGI:  // we sent out FLOGI
1741
1742       printk("RJT received on Fabric Login from %Xh, reason %Xh\n", 
1743         fchs->s_id, fchs->pl[1]);    
1744
1745     break;
1746
1747     default:
1748     break;
1749     }
1750       
1751     goto Done;
1752   }
1753
1754   // OK, we have an ACCept...
1755   // What's the ACC type? (according to what we sent)
1756   switch( Exchanges->fcExchange[ox_id].type )
1757   {
1758           
1759   case ELS_PLOGI:  // we sent out PLOGI
1760     if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
1761     {
1762       LOGIN_PAYLOAD logi;       // FC-PH Port Login
1763       
1764       // login ACC payload acceptable; search for WWN in our list
1765       // of fcPorts
1766       
1767       BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
1768    
1769       pLoggedInPort = fcFindLoggedInPort( 
1770              fcChip, 
1771              NULL,     // don't search Scsi Nexus
1772              0,        // don't search linked list for port_id
1773              &logi.port_name[0],     // search linked list for WWN
1774              &pLastLoggedInPort);  // must return non-NULL; when a port_id
1775                                    // is not found, this pointer marks the
1776                                    // end of the singly linked list
1777     
1778       if( pLoggedInPort == NULL)         // WWN not found - new port
1779       {
1780
1781         pLoggedInPort = CreateFcPort( 
1782                           cpqfcHBAdata, 
1783                           pLastLoggedInPort, 
1784                           fchs,
1785                           &logi);
1786
1787         if( pLoggedInPort == NULL )
1788         {
1789           printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
1790           // Now Q a LOGOut Request, since we won't be talking to that device
1791         
1792           goto Done;  // exit with error! dropped login frame
1793         }
1794       }
1795       else      // WWN was already known.  Ensure that any open
1796                 // exchanges for this WWN are terminated.
1797         // NOTE: It's possible that a device can change its 
1798         // 24-bit port_id after a Link init or Fabric change 
1799         // (e.g. LIP or Fabric RSCN).  In that case, the old
1800         // 24-bit port_id may be duplicated, or no longer exist.
1801       {
1802
1803         cpqfcTSTerminateExchange( cpqfcHBAdata, 
1804           &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
1805       }
1806
1807       // We have an fcPort struct - set fields accordingly
1808                                     // not PDISC, originator 
1809       SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
1810                         
1811       // We just set a "port_id"; is it duplicated?
1812       TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
1813
1814       // For Fabric operation, we issued PLOGI to 0xFFFFFC
1815       // so we can send SCR (State Change Registration) 
1816       // Check for this special case...
1817       if( fchs->s_id == 0xFFFFFC ) 
1818       {
1819         // PLOGI ACC was a Fabric response... issue SCR
1820         fchs->s_id = 0xFFFFFD;  // address for SCR
1821         cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs);
1822       }
1823
1824       else
1825       {
1826       // Now we need a PRLI to enable FCP-SCSI operation
1827       // set flags and Q up a ELS_PRLI
1828         cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs);
1829       }
1830     }
1831     else
1832     {
1833       // login payload unacceptable - reason in ls_reject_code
1834       // Q up a Logout Request
1835       printk("Login Payload unacceptable\n");
1836
1837     }
1838     break;
1839
1840
1841   // PDISC logic very similar to PLOGI, except we never want
1842   // to allocate mem for "new" port, and we set flags differently
1843   // (might combine later with PLOGI logic for efficiency)  
1844   case ELS_PDISC:  // we sent out PDISC
1845     if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
1846     {
1847       LOGIN_PAYLOAD logi;       // FC-PH Port Login
1848       BOOLEAN NeedLogin = FALSE;
1849       
1850       // login payload acceptable; search for WWN in our list
1851       // of (previously seen) fcPorts
1852       
1853       BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
1854    
1855       pLoggedInPort = fcFindLoggedInPort( 
1856              fcChip, 
1857              NULL,     // don't search Scsi Nexus
1858              0,        // don't search linked list for port_id
1859              &logi.port_name[0],     // search linked list for WWN
1860              &pLastLoggedInPort);  // must return non-NULL; when a port_id
1861                                    // is not found, this pointer marks the
1862                                    // end of the singly linked list
1863     
1864       if( pLoggedInPort != NULL)   // WWN found?
1865       {
1866         // WWN has same port_id as last login?  (Of course, a properly
1867         // working FC device should NEVER ACCept a PDISC if it's
1868         // port_id changed, but check just in case...)
1869         if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
1870         {
1871           // Yes.  We were expecting PDISC?
1872           if( pLoggedInPort->pdisc )
1873           {
1874             int i;
1875             
1876             
1877             // PDISC expected -- set fields.  (PDISC, Originator)
1878             SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE);
1879
1880             // We are ready to resume FCP-SCSI to this device...
1881             // Do we need to start anything that was Queued?
1882
1883             for( i=0; i< TACH_SEST_LEN; i++)
1884             {
1885               // see if any exchange for this PDISC'd port was queued
1886               if( ((fchs->s_id &0xFFFFFF) == 
1887                    (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF))
1888                       &&
1889                   (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED))
1890               {
1891                 fchs->reserved = i; // copy ExchangeID
1892 //                printk(" *Q x_ID %Xh after PDISC* ",i);
1893
1894                 cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs );
1895               }
1896             }
1897
1898             // Complete commands Q'd while we were waiting for Login
1899
1900             UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
1901           }
1902           else
1903           {
1904             printk("Not expecting PDISC (pdisc=FALSE)\n");
1905             NeedLogin = TRUE;
1906           }
1907         }
1908         else
1909         {
1910           printk("PDISC PortID change: old %Xh, new %Xh\n",
1911             pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
1912           NeedLogin = TRUE;
1913                   
1914         }
1915       }
1916       else
1917       {
1918         printk("PDISC ACC from unknown WWN\n");
1919         NeedLogin = TRUE;
1920       }
1921
1922       if( NeedLogin)
1923       {
1924         
1925         // The PDISC failed.  Set login struct flags accordingly,
1926         // terminate any I/O to this port, and Q a PLOGI
1927         if( pLoggedInPort )  // FC device previously known?
1928         {
1929
1930           cpqfcTSPutLinkQue( cpqfcHBAdata,
1931                     ELS_LOGO, // Q Type
1932                     fchs );   // has port_id to send to 
1933
1934           // There are a variety of error scenarios which can result
1935           // in PDISC failure, so as a catchall, add the check for
1936           // duplicate port_id.
1937           TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
1938
1939 //    TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
1940           pLoggedInPort->pdisc = FALSE;
1941           pLoggedInPort->prli = FALSE;
1942           pLoggedInPort->plogi = FALSE;
1943         
1944           cpqfcTSTerminateExchange( cpqfcHBAdata, 
1945             &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
1946         }
1947         cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs );
1948       }
1949     }
1950     else
1951     {
1952       // login payload unacceptable - reason in ls_reject_code
1953       // Q up a Logout Request
1954       printk("ERROR: Login Payload unacceptable!\n");
1955
1956     }
1957    
1958     break;
1959     
1960
1961
1962   case ELS_PRLI:  // we sent out PRLI
1963
1964
1965     pLoggedInPort = fcFindLoggedInPort( 
1966            fcChip, 
1967            NULL,       // don't search Scsi Nexus
1968            (fchs->s_id & 0xFFFFFF),  // search linked list for port_id
1969            NULL,     // DON'T search linked list for WWN
1970            NULL);    // don't care
1971       
1972     if( pLoggedInPort == NULL ) 
1973     {
1974       // huh?
1975       printk(" Unexpected PRLI ACCept frame!\n");
1976
1977       // Q a LOGOut here?
1978
1979       goto Done;
1980     }
1981
1982     // verify the PRLI ACC payload
1983     if( !verify_PRLI( fchs, &ls_reject_code) )
1984     {
1985       // PRLI Reply is acceptable; were we expecting it?
1986       if( pLoggedInPort->plogi ) 
1987       { 
1988         // yes, we expected the PRLI ACC  (not PDISC; Originator)
1989         SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
1990
1991         // OK, let's send a REPORT_LUNS command to determine
1992         // whether VSA or PDA FCP-LUN addressing is used.
1993         
1994         cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
1995         
1996         // It's possible that a device we were talking to changed 
1997         // port_id, and has logged back in.  This function ensures
1998         // that I/O will resume.
1999         UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
2000
2001       }
2002       else
2003       {
2004         // huh?
2005         printk(" (unexpected) PRLI ACCept with plogi FALSE\n");
2006
2007         // Q a LOGOut here?
2008         goto Done;
2009       }
2010     }
2011     else
2012     {
2013       printk(" PRLI ACCept payload failed verify\n");
2014
2015       // Q a LOGOut here?
2016     }
2017
2018     break;
2019  
2020   case ELS_FLOGI:  // we sent out FLOGI (Fabric Login)
2021
2022     // update the upper 16 bits of our port_id in Tachyon
2023     // the switch adds those upper 16 bits when responding
2024     // to us (i.e. we are the destination_id)
2025     fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF);
2026     writel( fcChip->Registers.my_al_pa,  
2027       fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
2028
2029     // now send out a PLOGI to the well known port_id 0xFFFFFC
2030     fchs->s_id = 0xFFFFFC;
2031     cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs);
2032   
2033    break; 
2034
2035
2036   case ELS_FDISC:  // we sent out FDISC (Fabric Discovery (Login))
2037
2038    printk( " ELS_FDISC success ");
2039    break;
2040    
2041
2042   case ELS_SCR:  // we sent out State Change Registration
2043     // now we can issue Name Service Request to find any
2044     // Fabric-connected devices we might want to login to.
2045    
2046         
2047     fchs->s_id = 0xFFFFFC;  // Name Server Address
2048     cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
2049     
2050
2051     break;
2052
2053     
2054   default:
2055     printk(" *Discarding unknown ACC frame, xID %04X/%04X* ", 
2056                     ox_id, fchs->ox_rx_id & 0xffff);
2057     break;
2058   }
2059
2060   
2061 Done:
2062   // Regardless of whether the Reply is valid or not, the
2063   // the exchange is done - complete
2064   cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
2065           
2066 Quit:    
2067   return;
2068 }
2069
2070
2071
2072
2073
2074
2075 // ****************  Fibre Channel Services  **************
2076 // This is where we process the Directory (Name) Service Reply
2077 // to know which devices are on the Fabric
2078
2079 static void ProcessFCS_Reply( 
2080         CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
2081 {
2082   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
2083   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
2084   ULONG ox_id = (fchs->ox_rx_id >>16);
2085 //  ULONG ls_reject_code;
2086 //  PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
2087   
2088   // If this is a valid reply, then we MUST have sent a request.
2089   // Verify that we can find a valid request OX_ID corresponding to
2090   // this reply
2091
2092   if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
2093   {
2094     printk(" *Discarding Reply frame, xID %04X/%04X* ", 
2095                     ox_id, fchs->ox_rx_id & 0xffff);
2096     goto Quit;  // exit this routine
2097   }
2098
2099
2100   // OK, we were expecting it.  Now check to see if it's a
2101   // "Name Service" Reply, and if so force a re-validation of
2102   // Fabric device logins (i.e. Start the login timeout and
2103   // send PDISC or PLOGI)
2104   // (Endianess Byte Swap?)
2105   if( fchs->pl[1] == 0x02FC )  // Name Service
2106   {
2107     // got a new (or NULL) list of Fabric attach devices... 
2108     // Invalidate current logins
2109     
2110     PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
2111     while( pLoggedInPort ) // for all ports which are expecting
2112                            // PDISC after the next LIP, set the
2113                            // logoutTimer
2114     {
2115
2116       if( (pLoggedInPort->port_id & 0xFFFF00)  // Fabric device?
2117                       &&
2118           (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port
2119       {
2120         pLoggedInPort->LOGO_timer = 6;  // what's the Fabric timeout??
2121                                 // suspend any I/O in progress until
2122                                 // PDISC received...
2123         pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands
2124       }
2125             
2126       pLoggedInPort = pLoggedInPort->pNextPort;
2127     }
2128     
2129     if( fchs->pl[2] == 0x0280)  // ACCept?
2130     {
2131       // Send PLOGI or PDISC to these Fabric devices
2132       SendLogins( cpqfcHBAdata, &fchs->pl[4] );  
2133     }
2134
2135
2136     // As of this writing, the only reason to reject is because NO
2137     // devices are left on the Fabric.  We already started
2138     // "logged out" timers; if the device(s) don't come
2139     // back, we'll do the implicit logout in the heart beat 
2140     // timer routine
2141     else  // ReJecT
2142     {
2143       // this just means no Fabric device is visible at this instant
2144     } 
2145   }
2146
2147   // Regardless of whether the Reply is valid or not, the
2148   // the exchange is done - complete
2149   cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
2150           
2151 Quit:    
2152   return;
2153 }
2154
2155
2156
2157
2158
2159
2160
2161 static void AnalyzeIncomingFrame( 
2162         CPQFCHBA *cpqfcHBAdata,
2163         ULONG QNdx )
2164 {
2165   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
2166   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
2167   PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
2168   TachFCHDR_GCMND* fchs = 
2169     (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff;
2170 //  ULONG ls_reject_code;  // reason for rejecting login
2171   LONG ExchangeID;
2172 //  FC_LOGGEDIN_PORT *pLoggedInPort;
2173   BOOLEAN AbortAccept;  
2174
2175   ENTER("AnalyzeIncomingFrame");
2176
2177
2178
2179   switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown
2180   {
2181
2182   case SFQ_UNKNOWN:  // unknown frame (e.g. LIP position frame, NOP, etc.)
2183  
2184
2185       // *********  FC-4 Device Data/ Fibre Channel Service *************
2186     if( ((fchs->d_id &0xF0000000) == 0)   // R_CTL (upper nibble) 0x0?
2187                 &&   
2188       (fchs->f_ctl & 0x20000000) )  // TYPE 20h is Fibre Channel Service
2189     {
2190
2191       // ************** FCS Reply **********************
2192
2193       if( (fchs->d_id & 0xff000000L) == 0x03000000L)  // (31:23 R_CTL)
2194       {
2195         ProcessFCS_Reply( cpqfcHBAdata, fchs );
2196
2197       }  // end of  FCS logic
2198
2199     }
2200     
2201
2202       // ***********  Extended Link Service **************
2203
2204     else if( fchs->d_id & 0x20000000   // R_CTL 0x2?
2205                   &&   
2206       (fchs->f_ctl & 0x01000000) )  // TYPE = 1
2207     {
2208
2209                            // these frames are either a response to
2210                            // something we sent (0x23) or "unsolicited"
2211                            // frames (0x22).
2212
2213
2214       // **************Extended Link REPLY **********************
2215                            // R_CTL Solicited Control Reply
2216
2217       if( (fchs->d_id & 0xff000000L) == 0x23000000L)  // (31:23 R_CTL)
2218       {
2219
2220         ProcessELS_Reply( cpqfcHBAdata, fchs );
2221
2222       }  // end of  "R_CTL Solicited Control Reply"
2223
2224
2225
2226
2227        // **************Extended Link REQUEST **********************
2228        // (unsolicited commands from another port or task...)
2229
2230                            // R_CTL Ext Link REQUEST
2231       else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
2232               (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame)
2233       {
2234
2235
2236
2237         ProcessELS_Request( cpqfcHBAdata, fchs );
2238
2239       }
2240
2241
2242
2243         // ************** LILP **********************
2244       else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
2245                (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames)
2246
2247       {
2248         // SANMark specifies that when available, we must use
2249         // the LILP frame to determine which ALPAs to send Port Discovery
2250         // to...
2251
2252         if( fchs->pl[0] == 0x0711L) //  ELS_PLOGI?
2253         {
2254 //        UCHAR *ptr = (UCHAR*)&fchs->pl[1];
2255 //        printk(" %d ALPAs found\n", *ptr);
2256           memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs
2257           fcChip->Options.LILPin = 1; // our LILPmap is valid!
2258           // now post to make Port Discovery happen...
2259           cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs);  
2260         }
2261       }
2262     }
2263
2264      
2265     // *****************  BASIC LINK SERVICE *****************
2266     
2267     else if( fchs->d_id & 0x80000000  // R_CTL:
2268                     &&           // Basic Link Service Request
2269            !(fchs->f_ctl & 0xFF000000) )  // type=0 for BLS
2270     {
2271
2272       // Check for ABTS (Abort Sequence)
2273       if( (fchs->d_id & 0x8F000000) == 0x81000000)
2274       {
2275         // look for OX_ID, S_ID pair that matches in our
2276         // fcExchanges table; if found, reply with ACCept and complete
2277         // the exchange
2278
2279         // Per PLDA, an ABTS is sent by an initiator; therefore
2280         // assume that if we have an exhange open to the port who
2281         // sent ABTS, it will be the d_id of what we sent.  
2282         for( ExchangeID = 0, AbortAccept=FALSE;
2283              ExchangeID < TACH_SEST_LEN; ExchangeID++)
2284         {
2285             // Valid "target" exchange 24-bit port_id matches? 
2286             // NOTE: For the case of handling Intiator AND Target
2287             // functions on the same chip, we can have TWO Exchanges
2288             // with the same OX_ID -- OX_ID/FFFF for the CMND, and
2289             // OX_ID/RX_ID for the XRDY or DATA frame(s).  Ideally,
2290             // we would like to support ABTS from Initiators or Targets,
2291             // but it's not clear that can be supported on Tachyon for
2292             // all cases (requires more investigation).
2293             
2294           if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE ||
2295                Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE)
2296                   &&
2297              ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
2298              (fchs->s_id & 0xFFFFFF)) )
2299           {
2300               
2301               // target xchnge port_id matches -- how about OX_ID?
2302             if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000)
2303                     == (fchs->ox_rx_id & 0xFFFF0000) )
2304                     // yes! post ACCept response; will be completed by fcStart
2305             {
2306               Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT;
2307                 
2308                 // copy (add) rx_id field for simplified ACCept reply
2309               fchs->ox_rx_id = 
2310                 Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id;
2311                 
2312               cpqfcTSPutLinkQue( cpqfcHBAdata,
2313                             BLS_ABTS_ACC, // Q Type 
2314                             fchs );    // void QueContent
2315               AbortAccept = TRUE;
2316       printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n", 
2317              fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id);
2318               break;      // ABTS can affect only ONE exchange -exit loop
2319             }
2320           }
2321         }  // end of FOR loop
2322         if( !AbortAccept ) // can't ACCept ABTS - send Reject
2323         {
2324       printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n", 
2325             fchs->ox_rx_id);
2326           if( Exchanges->fcExchange[ ExchangeID].type 
2327                 &&
2328               !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len
2329                & 0x80000000))
2330           {
2331             cpqfcTSCompleteExchange( fcChip, ExchangeID);
2332           }
2333           else
2334           {
2335             printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n", 
2336               ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len);
2337           }
2338         }
2339       }
2340
2341       // Check for BLS {ABTS? (Abort Sequence)} ACCept
2342       else if( (fchs->d_id & 0x8F000000) == 0x84000000)
2343       {
2344         // target has responded with ACC for our ABTS;
2345         // complete the indicated exchange with ABORTED status 
2346         // Make no checks for correct RX_ID, since
2347         // all we need to conform ABTS ACC is the OX_ID.
2348         // Verify that the d_id matches!
2349  
2350         ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
2351 //      printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n", 
2352 //          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff,
2353 //          Exchanges->fcExchange[ExchangeID].status);
2354
2355
2356         
2357         if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
2358         {
2359             // Does "target" exchange 24-bit port_id match? 
2360             // (See "NOTE" above for handling Intiator AND Target in
2361             // the same device driver)
2362             // First, if this is a target response, then we originated
2363             // (initiated) it with BLS_ABTS:
2364           
2365           if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
2366
2367                   &&
2368             // Second, does the source of this ACC match the destination
2369             // of who we originally sent it to?
2370              ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
2371              (fchs->s_id & 0xFFFFFF)) )
2372           {
2373             cpqfcTSCompleteExchange( fcChip, ExchangeID );
2374           }
2375         }              
2376       }
2377       // Check for BLS {ABTS? (Abort Sequence)} ReJecT
2378       else if( (fchs->d_id & 0x8F000000) == 0x85000000)
2379       {
2380         // target has responded with RJT for our ABTS;
2381         // complete the indicated exchange with ABORTED status 
2382         // Make no checks for correct RX_ID, since
2383         // all we need to conform ABTS ACC is the OX_ID.
2384         // Verify that the d_id matches!
2385  
2386         ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
2387 //      printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n", 
2388 //          fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff);
2389
2390         if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
2391         {  
2392             // Does "target" exchange 24-bit port_id match? 
2393             // (See "NOTE" above for handling Intiator AND Target in
2394             // the same device driver)
2395             // First, if this is a target response, then we originated
2396             // (initiated) it with BLS_ABTS:
2397                   
2398           if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
2399
2400                   &&
2401             // Second, does the source of this ACC match the destination
2402             // of who we originally sent it to?
2403              ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
2404              (fchs->s_id & 0xFFFFFF)) )
2405           {
2406             // YES! NOTE: There is a bug in CPQ's RA-4000 box 
2407             // where the "reason code" isn't returned in the payload
2408             // For now, simply presume the reject is because the target
2409             // already completed the exchange...
2410             
2411 //            printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID);
2412             cpqfcTSCompleteExchange( fcChip, ExchangeID );
2413           }
2414         } 
2415       }  // end of ABTS check
2416     }  // end of Basic Link Service Request
2417     break;
2418   
2419     default:
2420       printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n",
2421         fcLQ->Qitem[QNdx].Type,
2422         fcLQ->Qitem[QNdx].Type);
2423     break;
2424   }
2425 }
2426
2427
2428 // Function for Port Discovery necessary after every FC 
2429 // initialization (e.g. LIP).
2430 // Also may be called if from Fabric Name Service logic.
2431
2432 static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds )
2433 {
2434   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
2435   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
2436   ULONG ulStatus=0;
2437   TachFCHDR_GCMND fchs;  // copy fields for transmission
2438   int i;
2439   ULONG loginType;
2440   LONG ExchangeID;
2441   PFC_LOGGEDIN_PORT pLoggedInPort;
2442   __u32 PortIds[ number_of_al_pa];
2443   int NumberOfPorts=0;
2444
2445   // We're going to presume (for now) that our limit of Fabric devices
2446   // is the same as the number of alpa on a private loop (126 devices).
2447   // (Of course this could be changed to support however many we have
2448   // memory for).
2449   memset( &PortIds[0], 0, sizeof(PortIds));
2450    
2451   // First, check if this login is for our own Link Initialization
2452   // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices
2453   // from a switch.  If we are logging into Fabric devices, we'll
2454   // have a non-NULL FabricPortId pointer
2455   
2456   if( FabricPortIds != NULL) // may need logins
2457   {
2458     int LastPort=FALSE;
2459     i = 0;
2460     while( !LastPort)
2461     {
2462       // port IDs From NSR payload; byte swap needed?
2463       BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4);
2464  
2465 //      printk("FPortId[%d] %Xh ", i, PortIds[i]);
2466       if( PortIds[i] & 0x80000000)
2467         LastPort = TRUE;
2468       
2469       PortIds[i] &= 0xFFFFFF; // get 24-bit port_id
2470       // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge)
2471       // erroneously use ALPA 0.
2472       if( PortIds[i]  ) // need non-zero port_id...
2473         i++;
2474       
2475       if( i >= number_of_al_pa ) // (in)sanity check
2476         break;
2477       FabricPortIds++;  // next...
2478     }
2479
2480     NumberOfPorts = i;
2481 //    printk("NumberOf Fabric ports %d", NumberOfPorts);
2482   }
2483   
2484   else  // need to send logins on our "local" link
2485   {
2486   
2487     // are we a loop port?  If so, check for reception of LILP frame,
2488     // and if received use it (SANMark requirement)
2489     if( fcChip->Options.LILPin )
2490     {
2491       int j=0;
2492       // sanity check on number of ALPAs from LILP frame...
2493       // For format of LILP frame, see FC-AL specs or 
2494       // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8)
2495       // First byte is number of ALPAs
2496       i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0];
2497       NumberOfPorts = i;
2498 //      printk(" LILP alpa count %d ", i);
2499       while( i > 0)
2500       {
2501         PortIds[j] = fcChip->LILPmap[1+ j];
2502         j++; i--;
2503       }
2504     }
2505     else  // have to send login to everybody
2506     {
2507       int j=0;
2508       i = number_of_al_pa;
2509       NumberOfPorts = i;
2510       while( i > 0)
2511       {
2512         PortIds[j] = valid_al_pa[j]; // all legal ALPAs
2513         j++; i--;
2514       }
2515     }
2516   }
2517
2518
2519   // Now we have a copy of the port_ids (and how many)...
2520   for( i = 0; i < NumberOfPorts; i++)
2521   {
2522     // 24-bit FC Port ID
2523     fchs.s_id = PortIds[i];  // note: only 8-bits used for ALPA
2524
2525
2526     // don't log into ourselves (Linux Scsi disk scan will stop on
2527     // no TARGET support error on us, and quit trying for rest of devices)
2528     if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) )
2529       continue;
2530
2531     // fabric login needed?
2532     if( (fchs.s_id == 0) || 
2533         (fcChip->Options.fabric == 1) )
2534     {
2535       fcChip->Options.flogi = 1;  // fabric needs longer for login
2536       // Do we need FLOGI or FDISC?
2537       pLoggedInPort = fcFindLoggedInPort( 
2538              fcChip, 
2539              NULL,           // don't search SCSI Nexus
2540              0xFFFFFC,       // search linked list for Fabric port_id
2541              NULL,           // don't search WWN
2542              NULL);          // (don't care about end of list)
2543
2544       if( pLoggedInPort )    // If found, we have prior experience with
2545                              // this port -- check whether PDISC is needed
2546       {
2547         if( pLoggedInPort->flogi )
2548         {
2549           // does the switch support FDISC?? (FLOGI for now...)
2550           loginType = ELS_FLOGI;  // prior FLOGI still valid
2551         }
2552         else
2553           loginType = ELS_FLOGI;  // expired FLOGI
2554       }
2555       else                      // first FLOGI?
2556         loginType = ELS_FLOGI;  
2557
2558
2559       fchs.s_id = 0xFFFFFE;   // well known F_Port address
2560
2561       // Fabrics are not required to support FDISC, and
2562       // it's not clear if that helps us anyway, since
2563       // we'll want a Name Service Request to re-verify
2564       // visible devices...
2565       // Consequently, we always want our upper 16 bit
2566       // port_id to be zero (we'll be rejected if we
2567       // use our prior port_id if we've been plugged into
2568       // a different switch port).
2569       // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87)
2570       // If our ALPA is 55h for instance, we want the FC frame
2571       // s_id to be 0x000055, while Tach's my_al_pa register
2572       // must be 0x000155, to force an OPN at ALPA 0 
2573       // (the Fabric port)
2574       fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI
2575       writel( fcChip->Registers.my_al_pa | 0x0100,  
2576         fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
2577     }
2578
2579     else // not FLOGI...
2580     {
2581       // should we send PLOGI or PDISC?  Check if any prior port_id
2582       // (e.g. alpa) completed a PLOGI/PRLI exchange by checking 
2583       // the pdisc flag.
2584
2585       pLoggedInPort = fcFindLoggedInPort( 
2586              fcChip, 
2587              NULL,           // don't search SCSI Nexus
2588              fchs.s_id,      // search linked list for al_pa
2589              NULL,           // don't search WWN
2590              NULL);          // (don't care about end of list)
2591
2592              
2593
2594       if( pLoggedInPort )      // If found, we have prior experience with
2595                              // this port -- check whether PDISC is needed
2596       {
2597         if( pLoggedInPort->pdisc )
2598         {
2599           loginType = ELS_PDISC;  // prior PLOGI and PRLI maybe still valid
2600            
2601         }
2602         else
2603           loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
2604       }
2605       else                      // never talked to this port_id before
2606         loginType = ELS_PLOGI;  // prior knowledge, but can't use PDISC
2607     }
2608
2609
2610     
2611     ulStatus = cpqfcTSBuildExchange(
2612           cpqfcHBAdata,
2613           loginType,            // e.g. PLOGI
2614           &fchs,        // no incoming frame (we are originator)
2615           NULL,         // no data (no scatter/gather list)
2616           &ExchangeID );// fcController->fcExchanges index, -1 if failed
2617
2618     if( !ulStatus ) // Exchange setup OK?
2619     {
2620       ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
2621       if( !ulStatus )
2622       {
2623           // submitted to Tach's Outbound Que (ERQ PI incremented)
2624           // waited for completion for ELS type (Login frames issued
2625           // synchronously)
2626
2627         if( loginType == ELS_PDISC )
2628         {
2629           // now, we really shouldn't Revalidate SEST exchanges until
2630           // we get an ACC reply from our target and verify that
2631           // the target address/WWN is unchanged.  However, when a fast
2632           // target gets the PDISC, they can send SEST Exchange data
2633           // before we even get around to processing the PDISC ACC.
2634           // Consequently, we lose the I/O.
2635           // To avoid this, go ahead and Revalidate when the PDISC goes
2636           // out, anticipating that the ACC will be truly acceptable
2637           // (this happens 99.9999....% of the time).
2638           // If we revalidate a SEST write, and write data goes to a
2639           // target that is NOT the one we originated the WRITE to,
2640           // that target is required (FCP-SCSI specs, etc) to discard 
2641           // our WRITE data.
2642
2643           // Re-validate SEST entries (Tachyon hardware assists)
2644           RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort); 
2645     //TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
2646         }
2647       }
2648       else  // give up immediately on error
2649       {
2650 #ifdef LOGIN_DBG
2651         printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus );
2652 #endif
2653         break;
2654       }
2655
2656               
2657       if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc.
2658       {
2659         ulStatus = LNKDWN_OSLS;
2660 #ifdef LOGIN_DBG
2661         printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id);
2662 #endif
2663         break;
2664       }
2665         // Check the exchange for bad status (i.e. FrameTimeOut),
2666         // and complete on bad status (most likely due to BAD_ALPA)
2667         // on LDn, DPC function may already complete (ABORT) a started
2668         // exchange, so check type first (type = 0 on complete).
2669       if( Exchanges->fcExchange[ExchangeID].status )
2670       {
2671 #ifdef LOGIN_DBG 
2672         printk("completing x_ID %X on status %Xh\n", 
2673           ExchangeID, Exchanges->fcExchange[ExchangeID].status);
2674 #endif
2675         cpqfcTSCompleteExchange( fcChip, ExchangeID);
2676       }
2677     }
2678     else   // Xchange setup failed...
2679     {
2680 #ifdef LOGIN_DBG
2681       printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
2682 #endif
2683       break;
2684     }
2685   }
2686   if( !ulStatus )
2687   {
2688     // set the event signifying that all ALPAs were sent out.
2689 #ifdef LOGIN_DBG
2690     printk("SendLogins: PortDiscDone\n");
2691 #endif
2692     cpqfcHBAdata->PortDiscDone = 1;
2693
2694
2695     // TL/TS UG, pg. 184
2696     // 0x0065 = 100ms for RT_TOV
2697     // 0x01f5 = 500ms for ED_TOV
2698     fcChip->Registers.ed_tov.value = 0x006501f5L; 
2699     writel( fcChip->Registers.ed_tov.value,
2700       (fcChip->Registers.ed_tov.address));
2701
2702     // set the LP_TOV back to ED_TOV (i.e. 500 ms)
2703     writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
2704   }
2705   else
2706   {
2707     printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n", 
2708       ExchangeID, fchs.s_id, ulStatus);
2709   }
2710   LEAVE("SendLogins");
2711
2712 }
2713
2714
2715 // for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi",
2716 // D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9)
2717 static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
2718 {
2719   struct Scsi_Host *HostAdapter = Cmnd->host;
2720   CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
2721   PTACHYON fcChip = &cpqfcHBAdata->fcChip;
2722   FC_EXCHANGES *Exchanges = fcChip->Exchanges;
2723   PFC_LOGGEDIN_PORT pLoggedInPort; 
2724   int LunListLen=0;
2725   int i;
2726   ULONG x_ID = 0xFFFFFFFF;
2727   UCHAR *ucBuff = Cmnd->request_buffer;
2728
2729 //  printk("cpqfcTS: ReportLunsDone \n");
2730   // first, we need to find the Exchange for this command,
2731   // so we can find the fcPort struct to make the indicated
2732   // changes.
2733   for( i=0; i< TACH_SEST_LEN; i++)
2734   {
2735     if( Exchanges->fcExchange[i].type   // exchange defined?
2736                    &&
2737        (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches?
2738               
2739     {
2740       x_ID = i;  // found exchange!
2741       break;
2742     }
2743   }
2744   if( x_ID == 0xFFFFFFFF)
2745   {
2746 //    printk("cpqfcTS: ReportLuns failed - no FC Exchange\n");
2747     goto Done;  // Report Luns FC Exchange gone; 
2748                 // exchange probably Terminated by Implicit logout
2749   }
2750
2751
2752   // search linked list for the port_id we sent INQUIRY to
2753   pLoggedInPort = fcFindLoggedInPort( fcChip,
2754     NULL,     // DON'T search Scsi Nexus (we will set it)
2755     Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,        
2756     NULL,     // DON'T search linked list for FC WWN
2757     NULL);    // DON'T care about end of list
2758  
2759   if( !pLoggedInPort )
2760   {
2761 //    printk("cpqfcTS: ReportLuns failed - device gone\n");
2762     goto Done; // error! can't find logged in Port
2763   }    
2764   LunListLen = ucBuff[3];
2765   LunListLen += ucBuff[2]>>8;
2766
2767   if( !LunListLen )  // failed
2768   {
2769     // generically speaking, a soft error means we should retry...
2770     if( (Cmnd->result >> 16) == DID_SOFT_ERROR )
2771     {
2772       if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
2773                 (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
2774       {
2775         TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs;
2776       // did we fail because of "check condition, device reset?"
2777       // e.g. the device was reset (i.e., at every power up)
2778       // retry the Report Luns
2779       
2780       // who are we sending it to?
2781       // we know this because we have a copy of the command
2782       // frame from the original Report Lun command -
2783       // switch the d_id/s_id fields, because the Exchange Build
2784       // context is "reply to source".
2785       
2786         fchs->s_id = fchs->d_id; // (temporarily re-use the struct)
2787         cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
2788       }
2789     }
2790     else  // probably, the device doesn't support Report Luns
2791       pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0;  
2792   }
2793   else  // we have LUN info - check VSA mode
2794   {
2795     // for now, assume all LUNs will have same addr mode
2796     // for VSA, payload byte 8 will be 0x40; otherwise, 0
2797     pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8];  
2798       
2799     // Since we got a Report Luns answer, set lun masking flag
2800     pLoggedInPort->ScsiNexus.LunMasking = 1;
2801
2802     if( LunListLen > 8*CPQFCTS_MAX_LUN)   // We expect CPQFCTS_MAX_LUN max
2803       LunListLen = 8*CPQFCTS_MAX_LUN;
2804
2805 /*   
2806     printk("Device WWN %08X%08X Reports Luns @: ", 
2807           (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF), 
2808           (ULONG)(pLoggedInPort->u.liWWN>>32));
2809             
2810     for( i=8; i<LunListLen+8; i+=8)
2811     {  
2812       printk("%02X%02X ", ucBuff[i], ucBuff[i+1] );
2813     }
2814     printk("\n");
2815 */
2816     
2817     // Since the device was kind enough to tell us where the
2818     // LUNs are, lets ensure they are contiguous for Linux's
2819     // SCSI driver scan, which expects them to start at 0.
2820     // Since Linux only supports 8 LUNs, only copy the first
2821     // eight from the report luns command
2822
2823     // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report
2824     // LUNs 4001, 4004, etc., because other LUNs are masked from
2825     // this HBA (owned by someone else).  We'll make those appear as
2826     // LUN 0, 1... to Linux
2827     {
2828       int j;
2829       int AppendLunList = 0;
2830       // Walk through the LUN list.  The 'j' array number is
2831       // Linux's lun #, while the value of .lun[j] is the target's
2832       // lun #.
2833       // Once we build a LUN list, it's possible for a known device 
2834       // to go offline while volumes (LUNs) are added.  Later,
2835       // the device will do another PLOGI ... Report Luns command,
2836       // and we must not alter the existing Linux Lun map.
2837       // (This will be very rare).
2838       for( j=0; j < CPQFCTS_MAX_LUN; j++)
2839       {
2840         if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF )
2841         {
2842           AppendLunList = 1;
2843           break;
2844         }