/* fc.c: Generic Fibre Channel and FC4 SCSI driver.
*
* Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz)
*
* There are two kinds of Fibre Channel adapters used in Linux. Either
* the adapter is "smart" and does all FC bookkeeping by itself and
* just presents a standard SCSI interface to the operating system
* (that's e.g. the case with Qlogic FC cards), or leaves most of the FC
* bookkeeping to the OS (e.g. soc, socal). Drivers for the former adapters
* will look like normal SCSI drivers (with the exception of max_id will be
* usually 127), the latter on the other side allows SCSI, IP over FC and other
* protocols. This driver tree is for the latter adapters.
*
* This file should support both Point-to-Point and Arbitrated Loop topologies.
*
* Sources:
* Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
* dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
* Fibre Channel Arbitrated Loop (FC-AL), Rev. 4.5, 1995
* Fibre Channel Private Loop SCSI Direct Attach (FC-PLDA), Rev. 2.1, 1997
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/semaphore.h>
#include "fcp_impl.h"
#include <scsi/scsi_host.h>
/* #define FCDEBUG */
#define fc_printk printk ("%s: ", fc->name); printk
#ifdef FCDEBUG
#define FCD(x) fc_printk x;
#define FCND(x) printk ("FC: "); printk x;
#else
#define FCD(x)
#define FCND(x)
#endif
#ifdef __sparc__
#define dma_alloc_consistent(d,s,p) sbus_alloc_consistent(d,s,p)
#define dma_free_consistent(d,s,v,h) sbus_free_consistent(d,s,v,h)
#define dma_map_single(d,v,s,dir) sbus_map_single(d,v,s,dir)
#define dma_unmap_single(d,h,s,dir) sbus_unmap_single(d,h,s,dir)
#define dma_map_sg(d,s,n,dir) sbus_map_sg(d,s,n,dir)
#define dma_unmap_sg(d,s,n,dir) sbus_unmap_sg(d,s,n,dir)
#else
#define dma_alloc_consistent(d,s,p) pci_alloc_consistent(d,s,p)
#define dma_free_consistent(d,s,v,h) pci_free_consistent(d,s,v,h)
#define dma_map_single(d,v,s,dir) pci_map_single(d,v,s,dir)
#define dma_unmap_single(d,h,s,dir) pci_unmap_single(d,h,s,dir)
#define dma_map_sg(d,s,n,dir) pci_map_sg(d,s,n,dir)
#define dma_unmap_sg(d,s,n,dir) pci_unmap_sg(d,s,n,dir)
#endif
#define FCP_CMND(SCpnt) ((fcp_cmnd *)&(SCpnt->SCp))
#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->device->host->hostdata[0]))
#define SC_FCMND(fcmnd) ((struct scsi_cmnd *)((long)fcmnd - (long)&(((struct scsi_cmnd *)0)->SCp)))
static int fcp_scsi_queue_it(fc_channel *, struct scsi_cmnd *, fcp_cmnd *, int);
void fcp_queue_empty(fc_channel *);
static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd)
{
if (!fc->scsi_que) {
fc->scsi_que = fcmd;
fcmd->next = fcmd;
fcmd->prev = fcmd;
} else {
fc->scsi_que->prev->next = fcmd;
fcmd->prev = fc->scsi_que->prev;
fc->scsi_que->prev = fcmd;
fcmd->next = fc->scsi_que;
}
}
static void fcp_scsi_remove_queue (fc_channel *fc, fcp_cmnd *fcmd)
{
if (fcmd == fcmd->next) {
fc->scsi_que = NULL;
return;
}
if (fcmd == fc->scsi_que)
fc->scsi_que = fcmd->next;
fcmd->prev->next = fcmd->next;
fcmd->next->prev = fcmd->prev;
}
fc_channel *fc_channels = NULL;
#define LSMAGIC 620829043
typedef struct {
/* Must be first */
struct semaphore sem;
int magic;
int count;
logi *logi;
fcp_cmnd *fcmds;
atomic_t todo;
struct timer_list timer;
unsigned char grace[0];
} ls;
#define LSOMAGIC 654907799
typedef struct {
/* Must be first */
struct semaphore sem;
int magic;
int count;
fcp_cmnd *fcmds;
atomic_t todo;
struct timer_list timer;
} lso;
#define LSEMAGIC 84482456
typedef struct {
/* Must be first */
struct semaphore sem;
int magic;
int status;
struct timer_list timer;
} lse;
static void fcp_login_timeout(unsigned long data)
{
ls *l = (ls *)data;
FCND(("Login timeout\n"))
up(&l->sem);
}
static void fcp_login_done(fc_channel *fc, int i, int