From 2e15d726af858d01d2ae3b2f754d16aa80da3278 Mon Sep 17 00:00:00 2001 From: Planet-Lab Support Date: Fri, 21 Jan 2005 03:41:43 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'kernel_org'. --- arch/ia64/sn/include/ioerror.h | 81 + arch/ia64/sn/include/tio.h | 37 + arch/ia64/sn/include/xtalk/hubdev.h | 67 + arch/ia64/sn/include/xtalk/xbow.h | 291 ++ arch/ia64/sn/include/xtalk/xwidgetdev.h | 70 + arch/ia64/sn/kernel/bte_error.c | 188 ++ arch/ia64/sn/kernel/klconflib.c | 108 + arch/ia64/sn/pci/Makefile | 10 + arch/ia64/sn/pci/pcibr/Makefile | 11 + arch/ia64/sn/pci/pcibr/pcibr_reg.c | 282 ++ drivers/scsi/ahci.c | 1045 +++++++ drivers/scsi/lpfc/Makefile | 7 + drivers/scsi/lpfc/lpfc.h | 449 +++ drivers/scsi/lpfc/lpfc_compat.h | 120 + drivers/scsi/lpfc/lpfc_crtn.h | 265 ++ drivers/scsi/lpfc/lpfc_ct.c | 1288 +++++++++ drivers/scsi/lpfc/lpfc_disc.h | 270 ++ drivers/scsi/lpfc/lpfc_els.c | 3235 ++++++++++++++++++++++ drivers/scsi/lpfc/lpfc_hbadisc.c | 2703 ++++++++++++++++++ drivers/scsi/lpfc/lpfc_hw.h | 2688 ++++++++++++++++++ drivers/scsi/lpfc/lpfc_init.c | 1345 +++++++++ drivers/scsi/lpfc/lpfc_logmsg.h | 46 + drivers/scsi/lpfc/lpfc_mbox.c | 672 +++++ drivers/scsi/lpfc/lpfc_mem.c | 192 ++ drivers/scsi/lpfc/lpfc_nportdisc.c | 2145 +++++++++++++++ drivers/scsi/lpfc/lpfc_scsi.h | 92 + drivers/scsi/lpfc/lpfc_sli.c | 3349 +++++++++++++++++++++++ drivers/scsi/lpfc/lpfc_sli.h | 218 ++ drivers/scsi/lpfc/lpfc_version.h | 37 + drivers/scsi/sata_uli.c | 283 ++ drivers/usb/atm/Makefile | 7 + drivers/usb/atm/speedtch.c | 863 ++++++ drivers/usb/atm/usb_atm.c | 1201 ++++++++ drivers/usb/atm/usb_atm.h | 160 ++ drivers/usb/media/pwc/Makefile | 20 + drivers/usb/media/pwc/pwc-ctrl.c | 1630 +++++++++++ drivers/usb/media/pwc/pwc-if.c | 2211 +++++++++++++++ drivers/usb/media/pwc/pwc-ioctl.h | 292 ++ drivers/usb/media/pwc/pwc-kiara.c | 891 ++++++ drivers/usb/media/pwc/pwc-timon.c | 1446 ++++++++++ drivers/usb/media/pwc/pwc-uncompress.c | 147 + drivers/usb/media/pwc/pwc.h | 278 ++ include/asm-ia64/sn/shub_mmr.h | 199 ++ 43 files changed, 30939 insertions(+) create mode 100644 arch/ia64/sn/include/ioerror.h create mode 100644 arch/ia64/sn/include/tio.h create mode 100644 arch/ia64/sn/include/xtalk/hubdev.h create mode 100644 arch/ia64/sn/include/xtalk/xbow.h create mode 100644 arch/ia64/sn/include/xtalk/xwidgetdev.h create mode 100644 arch/ia64/sn/kernel/bte_error.c create mode 100644 arch/ia64/sn/kernel/klconflib.c create mode 100644 arch/ia64/sn/pci/Makefile create mode 100644 arch/ia64/sn/pci/pcibr/Makefile create mode 100644 arch/ia64/sn/pci/pcibr/pcibr_reg.c create mode 100644 drivers/scsi/ahci.c create mode 100644 drivers/scsi/lpfc/Makefile create mode 100644 drivers/scsi/lpfc/lpfc.h create mode 100644 drivers/scsi/lpfc/lpfc_compat.h create mode 100644 drivers/scsi/lpfc/lpfc_crtn.h create mode 100644 drivers/scsi/lpfc/lpfc_ct.c create mode 100644 drivers/scsi/lpfc/lpfc_disc.h create mode 100644 drivers/scsi/lpfc/lpfc_els.c create mode 100644 drivers/scsi/lpfc/lpfc_hbadisc.c create mode 100644 drivers/scsi/lpfc/lpfc_hw.h create mode 100644 drivers/scsi/lpfc/lpfc_init.c create mode 100644 drivers/scsi/lpfc/lpfc_logmsg.h create mode 100644 drivers/scsi/lpfc/lpfc_mbox.c create mode 100644 drivers/scsi/lpfc/lpfc_mem.c create mode 100644 drivers/scsi/lpfc/lpfc_nportdisc.c create mode 100644 drivers/scsi/lpfc/lpfc_scsi.h create mode 100644 drivers/scsi/lpfc/lpfc_sli.c create mode 100644 drivers/scsi/lpfc/lpfc_sli.h create mode 100644 drivers/scsi/lpfc/lpfc_version.h create mode 100644 drivers/scsi/sata_uli.c create mode 100644 drivers/usb/atm/Makefile create mode 100644 drivers/usb/atm/speedtch.c create mode 100644 drivers/usb/atm/usb_atm.c create mode 100644 drivers/usb/atm/usb_atm.h create mode 100644 drivers/usb/media/pwc/Makefile create mode 100644 drivers/usb/media/pwc/pwc-ctrl.c create mode 100644 drivers/usb/media/pwc/pwc-if.c create mode 100644 drivers/usb/media/pwc/pwc-ioctl.h create mode 100644 drivers/usb/media/pwc/pwc-kiara.c create mode 100644 drivers/usb/media/pwc/pwc-timon.c create mode 100644 drivers/usb/media/pwc/pwc-uncompress.c create mode 100644 drivers/usb/media/pwc/pwc.h create mode 100644 include/asm-ia64/sn/shub_mmr.h diff --git a/arch/ia64/sn/include/ioerror.h b/arch/ia64/sn/include/ioerror.h new file mode 100644 index 000000000..e68f2b078 --- /dev/null +++ b/arch/ia64/sn/include/ioerror.h @@ -0,0 +1,81 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_IOERROR_H +#define _ASM_IA64_SN_IOERROR_H + +/* + * IO error structure. + * + * This structure would expand to hold the information retrieved from + * all IO related error registers. + * + * This structure is defined to hold all system specific + * information related to a single error. + * + * This serves a couple of purpose. + * - Error handling often involves translating one form of address to other + * form. So, instead of having different data structures at each level, + * we have a single structure, and the appropriate fields get filled in + * at each layer. + * - This provides a way to dump all error related information in any layer + * of erorr handling (debugging aid). + * + * A second possibility is to allow each layer to define its own error + * data structure, and fill in the proper fields. This has the advantage + * of isolating the layers. + * A big concern is the potential stack usage (and overflow), if each layer + * defines these structures on stack (assuming we don't want to do kmalloc. + * + * Any layer wishing to pass extra information to a layer next to it in + * error handling hierarchy, can do so as a separate parameter. + */ + +typedef struct io_error_s { + /* Bit fields indicating which structure fields are valid */ + union { + struct { + unsigned ievb_errortype:1; + unsigned ievb_widgetnum:1; + unsigned ievb_widgetdev:1; + unsigned ievb_srccpu:1; + unsigned ievb_srcnode:1; + unsigned ievb_errnode:1; + unsigned ievb_sysioaddr:1; + unsigned ievb_xtalkaddr:1; + unsigned ievb_busspace:1; + unsigned ievb_busaddr:1; + unsigned ievb_vaddr:1; + unsigned ievb_memaddr:1; + unsigned ievb_epc:1; + unsigned ievb_ef:1; + unsigned ievb_tnum:1; + } iev_b; + unsigned iev_a; + } ie_v; + + short ie_errortype; /* error type: extra info about error */ + short ie_widgetnum; /* Widget number that's in error */ + short ie_widgetdev; /* Device within widget in error */ + cpuid_t ie_srccpu; /* CPU on srcnode generating error */ + cnodeid_t ie_srcnode; /* Node which caused the error */ + cnodeid_t ie_errnode; /* Node where error was noticed */ + iopaddr_t ie_sysioaddr; /* Sys specific IO address */ + iopaddr_t ie_xtalkaddr; /* Xtalk (48bit) addr of Error */ + iopaddr_t ie_busspace; /* Bus specific address space */ + iopaddr_t ie_busaddr; /* Bus specific address */ + caddr_t ie_vaddr; /* Virtual address of error */ + iopaddr_t ie_memaddr; /* Physical memory address */ + caddr_t ie_epc; /* pc when error reported */ + caddr_t ie_ef; /* eframe when error reported */ + short ie_tnum; /* Xtalk TNUM field */ +} ioerror_t; + +#define IOERROR_INIT(e) do { (e)->ie_v.iev_a = 0; } while (0) +#define IOERROR_SETVALUE(e,f,v) do { (e)->ie_ ## f = (v); (e)->ie_v.iev_b.ievb_ ## f = 1; } while (0) + +#endif /* _ASM_IA64_SN_IOERROR_H */ diff --git a/arch/ia64/sn/include/tio.h b/arch/ia64/sn/include/tio.h new file mode 100644 index 000000000..0139124dd --- /dev/null +++ b/arch/ia64/sn/include/tio.h @@ -0,0 +1,37 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#ifndef _ASM_IA64_SN_TIO_H +#define _ASM_IA64_SN_TIO_H + +#define TIO_MMR_ADDR_MOD + +#define TIO_NODE_ID TIO_MMR_ADDR_MOD(0x0000000090060e80) + +#define TIO_ITTE_BASE 0xb0008800 /* base of translation table entries */ +#define TIO_ITTE(bigwin) (TIO_ITTE_BASE + 8*(bigwin)) + +#define TIO_ITTE_OFFSET_BITS 8 /* size of offset field */ +#define TIO_ITTE_OFFSET_MASK ((1<> TIO_BWIN_SIZE_BITS) & \ + TIO_ITTE_OFFSET_MASK) << TIO_ITTE_OFFSET_SHIFT) | \ + (((widget) & TIO_ITTE_WIDGET_MASK) << TIO_ITTE_WIDGET_SHIFT)) | \ + (( (valid) & TIO_ITTE_VALID_MASK) << TIO_ITTE_VALID_SHIFT)) + +#endif /* _ASM_IA64_SN_TIO_H */ diff --git a/arch/ia64/sn/include/xtalk/hubdev.h b/arch/ia64/sn/include/xtalk/hubdev.h new file mode 100644 index 000000000..868e7ecae --- /dev/null +++ b/arch/ia64/sn/include/xtalk/hubdev.h @@ -0,0 +1,67 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_XTALK_HUBDEV_H +#define _ASM_IA64_SN_XTALK_HUBDEV_H + +#define HUB_WIDGET_ID_MAX 0xf +#define DEV_PER_WIDGET (2*2*8) +#define IIO_ITTE_WIDGET_BITS 4 /* size of widget field */ +#define IIO_ITTE_WIDGET_MASK ((1<= XBOW_PORT_8 && (wid) <= XBOW_PORT_F) + +/* whether to use upper or lower arbitration register, given source widget id */ +#define XBOW_ARB_IS_UPPER(wid) ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_B) +#define XBOW_ARB_IS_LOWER(wid) ((wid) >= XBOW_PORT_C && (wid) <= XBOW_PORT_F) + +/* offset of arbitration register, given source widget id */ +#define XBOW_ARB_OFF(wid) (XBOW_ARB_IS_UPPER(wid) ? 0x1c : 0x24) + +#define XBOW_WID_ID WIDGET_ID +#define XBOW_WID_STAT WIDGET_STATUS +#define XBOW_WID_ERR_UPPER WIDGET_ERR_UPPER_ADDR +#define XBOW_WID_ERR_LOWER WIDGET_ERR_LOWER_ADDR +#define XBOW_WID_CONTROL WIDGET_CONTROL +#define XBOW_WID_REQ_TO WIDGET_REQ_TIMEOUT +#define XBOW_WID_INT_UPPER WIDGET_INTDEST_UPPER_ADDR +#define XBOW_WID_INT_LOWER WIDGET_INTDEST_LOWER_ADDR +#define XBOW_WID_ERR_CMDWORD WIDGET_ERR_CMD_WORD +#define XBOW_WID_LLP WIDGET_LLP_CFG +#define XBOW_WID_STAT_CLR WIDGET_TFLUSH +#define XBOW_WID_ARB_RELOAD 0x5c +#define XBOW_WID_PERF_CTR_A 0x64 +#define XBOW_WID_PERF_CTR_B 0x6c +#define XBOW_WID_NIC 0x74 + +/* Xbridge only */ +#define XBOW_W0_RST_FNC 0x00007C +#define XBOW_L8_RST_FNC 0x000084 +#define XBOW_L9_RST_FNC 0x00008c +#define XBOW_LA_RST_FNC 0x000094 +#define XBOW_LB_RST_FNC 0x00009c +#define XBOW_LC_RST_FNC 0x0000a4 +#define XBOW_LD_RST_FNC 0x0000ac +#define XBOW_LE_RST_FNC 0x0000b4 +#define XBOW_LF_RST_FNC 0x0000bc +#define XBOW_RESET_FENCE(x) ((x) > 7 && (x) < 16) ? \ + (XBOW_W0_RST_FNC + ((x) - 7) * 8) : \ + ((x) == 0) ? XBOW_W0_RST_FNC : 0 +#define XBOW_LOCK 0x0000c4 +#define XBOW_LOCK_CLR 0x0000cc +/* End of Xbridge only */ + +/* used only in ide, but defined here within the reserved portion */ +/* of the widget0 address space (before 0xf4) */ +#define XBOW_WID_UNDEF 0xe4 + +/* xbow link register set base, legal value for x is 0x8..0xf */ +#define XB_LINK_BASE 0x100 +#define XB_LINK_OFFSET 0x40 +#define XB_LINK_REG_BASE(x) (XB_LINK_BASE + ((x) & (MAX_XBOW_PORTS - 1)) * XB_LINK_OFFSET) + +#define XB_LINK_IBUF_FLUSH(x) (XB_LINK_REG_BASE(x) + 0x4) +#define XB_LINK_CTRL(x) (XB_LINK_REG_BASE(x) + 0xc) +#define XB_LINK_STATUS(x) (XB_LINK_REG_BASE(x) + 0x14) +#define XB_LINK_ARB_UPPER(x) (XB_LINK_REG_BASE(x) + 0x1c) +#define XB_LINK_ARB_LOWER(x) (XB_LINK_REG_BASE(x) + 0x24) +#define XB_LINK_STATUS_CLR(x) (XB_LINK_REG_BASE(x) + 0x2c) +#define XB_LINK_RESET(x) (XB_LINK_REG_BASE(x) + 0x34) +#define XB_LINK_AUX_STATUS(x) (XB_LINK_REG_BASE(x) + 0x3c) + +/* link_control(x) */ +#define XB_CTRL_LINKALIVE_IE 0x80000000 /* link comes alive */ + /* reserved: 0x40000000 */ +#define XB_CTRL_PERF_CTR_MODE_MSK 0x30000000 /* perf counter mode */ +#define XB_CTRL_IBUF_LEVEL_MSK 0x0e000000 /* input packet buffer level */ +#define XB_CTRL_8BIT_MODE 0x01000000 /* force link into 8 bit mode */ +#define XB_CTRL_BAD_LLP_PKT 0x00800000 /* force bad LLP packet */ +#define XB_CTRL_WIDGET_CR_MSK 0x007c0000 /* LLP widget credit mask */ +#define XB_CTRL_WIDGET_CR_SHFT 18 /* LLP widget credit shift */ +#define XB_CTRL_ILLEGAL_DST_IE 0x00020000 /* illegal destination */ +#define XB_CTRL_OALLOC_IBUF_IE 0x00010000 /* overallocated input buffer */ + /* reserved: 0x0000fe00 */ +#define XB_CTRL_BNDWDTH_ALLOC_IE 0x00000100 /* bandwidth alloc */ +#define XB_CTRL_RCV_CNT_OFLOW_IE 0x00000080 /* rcv retry overflow */ +#define XB_CTRL_XMT_CNT_OFLOW_IE 0x00000040 /* xmt retry overflow */ +#define XB_CTRL_XMT_MAX_RTRY_IE 0x00000020 /* max transmit retry */ +#define XB_CTRL_RCV_IE 0x00000010 /* receive */ +#define XB_CTRL_XMT_RTRY_IE 0x00000008 /* transmit retry */ + /* reserved: 0x00000004 */ +#define XB_CTRL_MAXREQ_TOUT_IE 0x00000002 /* maximum request timeout */ +#define XB_CTRL_SRC_TOUT_IE 0x00000001 /* source timeout */ + +/* link_status(x) */ +#define XB_STAT_LINKALIVE XB_CTRL_LINKALIVE_IE + /* reserved: 0x7ff80000 */ +#define XB_STAT_MULTI_ERR 0x00040000 /* multi error */ +#define XB_STAT_ILLEGAL_DST_ERR XB_CTRL_ILLEGAL_DST_IE +#define XB_STAT_OALLOC_IBUF_ERR XB_CTRL_OALLOC_IBUF_IE +#define XB_STAT_BNDWDTH_ALLOC_ID_MSK 0x0000ff00 /* port bitmask */ +#define XB_STAT_RCV_CNT_OFLOW_ERR XB_CTRL_RCV_CNT_OFLOW_IE +#define XB_STAT_XMT_CNT_OFLOW_ERR XB_CTRL_XMT_CNT_OFLOW_IE +#define XB_STAT_XMT_MAX_RTRY_ERR XB_CTRL_XMT_MAX_RTRY_IE +#define XB_STAT_RCV_ERR XB_CTRL_RCV_IE +#define XB_STAT_XMT_RTRY_ERR XB_CTRL_XMT_RTRY_IE + /* reserved: 0x00000004 */ +#define XB_STAT_MAXREQ_TOUT_ERR XB_CTRL_MAXREQ_TOUT_IE +#define XB_STAT_SRC_TOUT_ERR XB_CTRL_SRC_TOUT_IE + +/* link_aux_status(x) */ +#define XB_AUX_STAT_RCV_CNT 0xff000000 +#define XB_AUX_STAT_XMT_CNT 0x00ff0000 +#define XB_AUX_STAT_TOUT_DST 0x0000ff00 +#define XB_AUX_LINKFAIL_RST_BAD 0x00000040 +#define XB_AUX_STAT_PRESENT 0x00000020 +#define XB_AUX_STAT_PORT_WIDTH 0x00000010 + /* reserved: 0x0000000f */ + +/* + * link_arb_upper/link_arb_lower(x), (reg) should be the link_arb_upper + * register if (x) is 0x8..0xb, link_arb_lower if (x) is 0xc..0xf + */ +#define XB_ARB_GBR_MSK 0x1f +#define XB_ARB_RR_MSK 0x7 +#define XB_ARB_GBR_SHFT(x) (((x) & 0x3) * 8) +#define XB_ARB_RR_SHFT(x) (((x) & 0x3) * 8 + 5) +#define XB_ARB_GBR_CNT(reg,x) ((reg) >> XB_ARB_GBR_SHFT(x) & XB_ARB_GBR_MSK) +#define XB_ARB_RR_CNT(reg,x) ((reg) >> XB_ARB_RR_SHFT(x) & XB_ARB_RR_MSK) + +/* XBOW_WID_STAT */ +#define XB_WID_STAT_LINK_INTR_SHFT (24) +#define XB_WID_STAT_LINK_INTR_MASK (0xFF << XB_WID_STAT_LINK_INTR_SHFT) +#define XB_WID_STAT_LINK_INTR(x) (0x1 << (((x)&7) + XB_WID_STAT_LINK_INTR_SHFT)) +#define XB_WID_STAT_WIDGET0_INTR 0x00800000 +#define XB_WID_STAT_SRCID_MASK 0x000003c0 /* Xbridge only */ +#define XB_WID_STAT_REG_ACC_ERR 0x00000020 +#define XB_WID_STAT_RECV_TOUT 0x00000010 /* Xbridge only */ +#define XB_WID_STAT_ARB_TOUT 0x00000008 /* Xbridge only */ +#define XB_WID_STAT_XTALK_ERR 0x00000004 +#define XB_WID_STAT_DST_TOUT 0x00000002 /* Xbridge only */ +#define XB_WID_STAT_MULTI_ERR 0x00000001 + +#define XB_WID_STAT_SRCID_SHFT 6 + +/* XBOW_WID_CONTROL */ +#define XB_WID_CTRL_REG_ACC_IE XB_WID_STAT_REG_ACC_ERR +#define XB_WID_CTRL_RECV_TOUT XB_WID_STAT_RECV_TOUT +#define XB_WID_CTRL_ARB_TOUT XB_WID_STAT_ARB_TOUT +#define XB_WID_CTRL_XTALK_IE XB_WID_STAT_XTALK_ERR + +/* XBOW_WID_INT_UPPER */ +/* defined in xwidget.h for WIDGET_INTDEST_UPPER_ADDR */ + +/* XBOW WIDGET part number, in the ID register */ +#define XBOW_WIDGET_PART_NUM 0x0 /* crossbow */ +#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbridge */ +#define XBOW_WIDGET_MFGR_NUM 0x0 +#define XXBOW_WIDGET_MFGR_NUM 0x0 +#define PXBOW_WIDGET_PART_NUM 0xd100 /* PIC */ + +#define XBOW_REV_1_0 0x1 /* xbow rev 1.0 is "1" */ +#define XBOW_REV_1_1 0x2 /* xbow rev 1.1 is "2" */ +#define XBOW_REV_1_2 0x3 /* xbow rev 1.2 is "3" */ +#define XBOW_REV_1_3 0x4 /* xbow rev 1.3 is "4" */ +#define XBOW_REV_2_0 0x5 /* xbow rev 2.0 is "5" */ + +#define XXBOW_PART_REV_1_0 (XXBOW_WIDGET_PART_NUM << 4 | 0x1 ) +#define XXBOW_PART_REV_2_0 (XXBOW_WIDGET_PART_NUM << 4 | 0x2 ) + +/* XBOW_WID_ARB_RELOAD */ +#define XBOW_WID_ARB_RELOAD_INT 0x3f /* GBR reload interval */ + +#define IS_XBRIDGE_XBOW(wid) \ + (XWIDGET_PART_NUM(wid) == XXBOW_WIDGET_PART_NUM && \ + XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM) + +#define IS_PIC_XBOW(wid) \ + (XWIDGET_PART_NUM(wid) == PXBOW_WIDGET_PART_NUM && \ + XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM) + +#define XBOW_WAR_ENABLED(pv, widid) ((1 << XWIDGET_REV_NUM(widid)) & pv) + +#endif /* _ASM_IA64_SN_XTALK_XBOW_H */ diff --git a/arch/ia64/sn/include/xtalk/xwidgetdev.h b/arch/ia64/sn/include/xtalk/xwidgetdev.h new file mode 100644 index 000000000..c5f4bc5cc --- /dev/null +++ b/arch/ia64/sn/include/xtalk/xwidgetdev.h @@ -0,0 +1,70 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992-1997,2000-2003 Silicon Graphics, Inc. All Rights Reserved. + */ +#ifndef _ASM_IA64_SN_XTALK_XWIDGET_H +#define _ASM_IA64_SN_XTALK_XWIDGET_H + +/* WIDGET_ID */ +#define WIDGET_REV_NUM 0xf0000000 +#define WIDGET_PART_NUM 0x0ffff000 +#define WIDGET_MFG_NUM 0x00000ffe +#define WIDGET_REV_NUM_SHFT 28 +#define WIDGET_PART_NUM_SHFT 12 +#define WIDGET_MFG_NUM_SHFT 1 + +#define XWIDGET_PART_NUM(widgetid) (((widgetid) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT) +#define XWIDGET_REV_NUM(widgetid) (((widgetid) & WIDGET_REV_NUM) >> WIDGET_REV_NUM_SHFT) +#define XWIDGET_MFG_NUM(widgetid) (((widgetid) & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT) +#define XWIDGET_PART_REV_NUM(widgetid) ((XWIDGET_PART_NUM(widgetid) << 4) | \ + XWIDGET_REV_NUM(widgetid)) +#define XWIDGET_PART_REV_NUM_REV(partrev) (partrev & 0xf) + +/* widget configuration registers */ +struct widget_cfg{ + uint32_t w_id; /* 0x04 */ + uint32_t w_pad_0; /* 0x00 */ + uint32_t w_status; /* 0x0c */ + uint32_t w_pad_1; /* 0x08 */ + uint32_t w_err_upper_addr; /* 0x14 */ + uint32_t w_pad_2; /* 0x10 */ + uint32_t w_err_lower_addr; /* 0x1c */ + uint32_t w_pad_3; /* 0x18 */ + uint32_t w_control; /* 0x24 */ + uint32_t w_pad_4; /* 0x20 */ + uint32_t w_req_timeout; /* 0x2c */ + uint32_t w_pad_5; /* 0x28 */ + uint32_t w_intdest_upper_addr; /* 0x34 */ + uint32_t w_pad_6; /* 0x30 */ + uint32_t w_intdest_lower_addr; /* 0x3c */ + uint32_t w_pad_7; /* 0x38 */ + uint32_t w_err_cmd_word; /* 0x44 */ + uint32_t w_pad_8; /* 0x40 */ + uint32_t w_llp_cfg; /* 0x4c */ + uint32_t w_pad_9; /* 0x48 */ + uint32_t w_tflush; /* 0x54 */ + uint32_t w_pad_10; /* 0x50 */ +}; + +/* + * Crosstalk Widget Hardware Identification, as defined in the Crosstalk spec. + */ +struct xwidget_hwid{ + int mfg_num; + int rev_num; + int part_num; +}; + +struct xwidget_info{ + + struct xwidget_hwid xwi_hwid; /* Widget Identification */ + char xwi_masterxid; /* Hub's Widget Port Number */ + void *xwi_hubinfo; /* Hub's provider private info */ + uint64_t *xwi_hub_provider; /* prom provider functions */ + void *xwi_vertex; +}; + +#endif /* _ASM_IA64_SN_XTALK_XWIDGET_H */ diff --git a/arch/ia64/sn/kernel/bte_error.c b/arch/ia64/sn/kernel/bte_error.c new file mode 100644 index 000000000..3591c2cf1 --- /dev/null +++ b/arch/ia64/sn/kernel/bte_error.c @@ -0,0 +1,188 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include "ioerror.h" +#include +#include "shubio.h" +#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" +#include + +/* + * Bte error handling is done in two parts. The first captures + * any crb related errors. Since there can be multiple crbs per + * interface and multiple interfaces active, we need to wait until + * all active crbs are completed. This is the first job of the + * second part error handler. When all bte related CRBs are cleanly + * completed, it resets the interfaces and gets them ready for new + * transfers to be queued. + */ + +void bte_error_handler(unsigned long); + +/* + * Wait until all BTE related CRBs are completed + * and then reset the interfaces. + */ +void bte_error_handler(unsigned long _nodepda) +{ + struct nodepda_s *err_nodepda = (struct nodepda_s *)_nodepda; + spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock; + struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer; + nasid_t nasid; + int i; + int valid_crbs; + unsigned long irq_flags; + volatile u64 *notify; + bte_result_t bh_error; + ii_imem_u_t imem; /* II IMEM Register */ + ii_icrb0_d_u_t icrbd; /* II CRB Register D */ + ii_ibcr_u_t ibcr; + ii_icmr_u_t icmr; + + BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda, + smp_processor_id())); + + spin_lock_irqsave(recovery_lock, irq_flags); + + if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) && + (err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) { + BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda, + smp_processor_id())); + spin_unlock_irqrestore(recovery_lock, irq_flags); + return; + } + /* + * Lock all interfaces on this node to prevent new transfers + * from being queued. + */ + for (i = 0; i < BTES_PER_NODE; i++) { + if (err_nodepda->bte_if[i].cleanup_active) { + continue; + } + spin_lock(&err_nodepda->bte_if[i].spinlock); + BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda, + smp_processor_id(), i)); + err_nodepda->bte_if[i].cleanup_active = 1; + } + + /* Determine information about our hub */ + nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode); + + /* + * A BTE transfer can use multiple CRBs. We need to make sure + * that all the BTE CRBs are complete (or timed out) before + * attempting to clean up the error. Resetting the BTE while + * there are still BTE CRBs active will hang the BTE. + * We should look at all the CRBs to see if they are allocated + * to the BTE and see if they are still active. When none + * are active, we can continue with the cleanup. + * + * We also want to make sure that the local NI port is up. + * When a router resets the NI port can go down, while it + * goes through the LLP handshake, but then comes back up. + */ + icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR); + if (icmr.ii_icmr_fld_s.i_crb_mark != 0) { + /* + * There are errors which still need to be cleaned up by + * hubiio_crb_error_handler + */ + mod_timer(recovery_timer, HZ * 5); + BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda, + smp_processor_id())); + spin_unlock_irqrestore(recovery_lock, irq_flags); + return; + } + if (icmr.ii_icmr_fld_s.i_crb_vld != 0) { + + valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld; + + for (i = 0; i < IIO_NUM_CRBS; i++) { + if (!((1 << i) & valid_crbs)) { + /* This crb was not marked as valid, ignore */ + continue; + } + icrbd.ii_icrb0_d_regval = + REMOTE_HUB_L(nasid, IIO_ICRB_D(i)); + if (icrbd.d_bteop) { + mod_timer(recovery_timer, HZ * 5); + BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n", + err_nodepda, smp_processor_id(), + i)); + spin_unlock_irqrestore(recovery_lock, + irq_flags); + return; + } + } + } + + BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id())); + /* Reenable both bte interfaces */ + imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM); + imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1; + REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval); + + /* Reinitialize both BTE state machines. */ + ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR); + ibcr.ii_ibcr_fld_s.i_soft_reset = 1; + REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval); + + for (i = 0; i < BTES_PER_NODE; i++) { + bh_error = err_nodepda->bte_if[i].bh_error; + if (bh_error != BTE_SUCCESS) { + /* There is an error which needs to be notified */ + notify = err_nodepda->bte_if[i].most_rcnt_na; + BTE_PRINTK(("cnode %d bte %d error=0x%lx\n", + err_nodepda->bte_if[i].bte_cnode, + err_nodepda->bte_if[i].bte_num, + IBLS_ERROR | (u64) bh_error)); + *notify = IBLS_ERROR | bh_error; + err_nodepda->bte_if[i].bh_error = BTE_SUCCESS; + } + + err_nodepda->bte_if[i].cleanup_active = 0; + BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda, + smp_processor_id(), i)); + spin_unlock(&pda->cpu_bte_if[i]->spinlock); + } + + del_timer(recovery_timer); + + spin_unlock_irqrestore(recovery_lock, irq_flags); +} + +/* + * First part error handler. This is called whenever any error CRB interrupt + * is generated by the II. + */ +void +bte_crb_error_handler(cnodeid_t cnode, int btenum, + int crbnum, ioerror_t * ioe, int bteop) +{ + struct bteinfo_s *bte; + + + bte = &(NODEPDA(cnode)->bte_if[btenum]); + + /* + * The caller has already figured out the error type, we save that + * in the bte handle structure for the thread excercising the + * interface to consume. + */ + bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET; + bte->bte_error_count++; + + BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n", + bte->bte_cnode, bte->bte_num, ioe->ie_errortype)); + bte_error_handler((unsigned long) NODEPDA(cnode)); +} + diff --git a/arch/ia64/sn/kernel/klconflib.c b/arch/ia64/sn/kernel/klconflib.c new file mode 100644 index 000000000..0f11a3299 --- /dev/null +++ b/arch/ia64/sn/kernel/klconflib.c @@ -0,0 +1,108 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +char brick_types[MAX_BRICK_TYPES + 1] = "cri.xdpn%#=vo^kjbf890123456789..."; +/* + * Format a module id for printing. + * + * There are three possible formats: + * + * MODULE_FORMAT_BRIEF is the brief 6-character format, including + * the actual brick-type as recorded in the + * moduleid_t, eg. 002c15 for a C-brick, or + * 101#17 for a PX-brick. + * + * MODULE_FORMAT_LONG is the hwgraph format, eg. rack/002/bay/15 + * of rack/101/bay/17 (note that the brick + * type does not appear in this format). + * + * MODULE_FORMAT_LCD is like MODULE_FORMAT_BRIEF, except that it + * ensures that the module id provided appears + * exactly as it would on the LCD display of + * the corresponding brick, eg. still 002c15 + * for a C-brick, but 101p17 for a PX-brick. + * + * maule (9/13/04): Removed top-level check for (fmt == MODULE_FORMAT_LCD) + * making MODULE_FORMAT_LCD equivalent to MODULE_FORMAT_BRIEF. It was + * decided that all callers should assume the returned string should be what + * is displayed on the brick L1 LCD. + */ +void +format_module_id(char *buffer, moduleid_t m, int fmt) +{ + int rack, position; + unsigned char brickchar; + + rack = MODULE_GET_RACK(m); + brickchar = MODULE_GET_BTCHAR(m); + + /* Be sure we use the same brick type character as displayed + * on the brick's LCD + */ + switch (brickchar) + { + case L1_BRICKTYPE_GA: + case L1_BRICKTYPE_OPUS_TIO: + brickchar = L1_BRICKTYPE_C; + break; + + case L1_BRICKTYPE_PX: + case L1_BRICKTYPE_PE: + case L1_BRICKTYPE_PA: + case L1_BRICKTYPE_SA: /* we can move this to the "I's" later + * if that makes more sense + */ + brickchar = L1_BRICKTYPE_P; + break; + + case L1_BRICKTYPE_IX: + case L1_BRICKTYPE_IA: + + brickchar = L1_BRICKTYPE_I; + break; + } + + position = MODULE_GET_BPOS(m); + + if ((fmt == MODULE_FORMAT_BRIEF) || (fmt == MODULE_FORMAT_LCD)) { + /* Brief module number format, eg. 002c15 */ + + /* Decompress the rack number */ + *buffer++ = '0' + RACK_GET_CLASS(rack); + *buffer++ = '0' + RACK_GET_GROUP(rack); + *buffer++ = '0' + RACK_GET_NUM(rack); + + /* Add the brick type */ + *buffer++ = brickchar; + } + else if (fmt == MODULE_FORMAT_LONG) { + /* Fuller hwgraph format, eg. rack/002/bay/15 */ + + strcpy(buffer, "rack" "/"); buffer += strlen(buffer); + + *buffer++ = '0' + RACK_GET_CLASS(rack); + *buffer++ = '0' + RACK_GET_GROUP(rack); + *buffer++ = '0' + RACK_GET_NUM(rack); + + strcpy(buffer, "/" "bay" "/"); buffer += strlen(buffer); + } + + /* Add the bay position, using at least two digits */ + if (position < 10) + *buffer++ = '0'; + sprintf(buffer, "%d", position); + +} diff --git a/arch/ia64/sn/pci/Makefile b/arch/ia64/sn/pci/Makefile new file mode 100644 index 000000000..b5dca0097 --- /dev/null +++ b/arch/ia64/sn/pci/Makefile @@ -0,0 +1,10 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# Makefile for the sn pci general routines. + +obj-y := pci_dma.o pcibr/ diff --git a/arch/ia64/sn/pci/pcibr/Makefile b/arch/ia64/sn/pci/pcibr/Makefile new file mode 100644 index 000000000..1850c4a94 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/Makefile @@ -0,0 +1,11 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2002-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# Makefile for the sn2 io routines. + +obj-y += pcibr_dma.o pcibr_reg.o \ + pcibr_ate.o pcibr_provider.o diff --git a/arch/ia64/sn/pci/pcibr/pcibr_reg.c b/arch/ia64/sn/pci/pcibr/pcibr_reg.c new file mode 100644 index 000000000..74a74a7d2 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_reg.c @@ -0,0 +1,282 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/tiocp.h" +#include "pci/pic.h" +#include "pci/pcibr_provider.h" + +union br_ptr { + struct tiocp tio; + struct pic pic; +}; + +/* + * Control Register Access -- Read/Write 0000_0020 + */ +void pcireg_control_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_control &= ~bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_wid_control &= ~bits; + break; + default: + panic + ("pcireg_control_bit_clr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +void pcireg_control_bit_set(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_control |= bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_wid_control |= bits; + break; + default: + panic + ("pcireg_control_bit_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * PCI/PCIX Target Flush Register Access -- Read Only 0000_0050 + */ +uint64_t pcireg_tflush_get(struct pcibus_info *pcibus_info) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_tflush; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_wid_tflush; + break; + default: + panic + ("pcireg_tflush_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + + /* Read of the Target Flush should always return zero */ + if (ret != 0) + panic("pcireg_tflush_get:Target Flush failed\n"); + + return ret; +} + +/* + * Interrupt Status Register Access -- Read Only 0000_0100 + */ +uint64_t pcireg_intr_status_get(struct pcibus_info * pcibus_info) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_int_status; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_int_status; + break; + default: + panic + ("pcireg_intr_status_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + return ret; +} + +/* + * Interrupt Enable Register Access -- Read/Write 0000_0108 + */ +void pcireg_intr_enable_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_enable &= ~bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_enable &= ~bits; + break; + default: + panic + ("pcireg_intr_enable_bit_clr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +void pcireg_intr_enable_bit_set(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_enable |= bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_enable |= bits; + break; + default: + panic + ("pcireg_intr_enable_bit_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Intr Host Address Register (int_addr) -- Read/Write 0000_0130 - 0000_0168 + */ +void pcireg_intr_addr_addr_set(struct pcibus_info *pcibus_info, int int_n, + uint64_t addr) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_addr[int_n] &= ~TIOCP_HOST_INTR_ADDR; + ptr->tio.cp_int_addr[int_n] |= + (addr & TIOCP_HOST_INTR_ADDR); + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_addr[int_n] &= ~PIC_HOST_INTR_ADDR; + ptr->pic.p_int_addr[int_n] |= + (addr & PIC_HOST_INTR_ADDR); + break; + default: + panic + ("pcireg_intr_addr_addr_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Force Interrupt Register Access -- Write Only 0000_01C0 - 0000_01F8 + */ +void pcireg_force_intr_set(struct pcibus_info *pcibus_info, int int_n) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_force_pin[int_n] = 1; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_force_pin[int_n] = 1; + break; + default: + panic + ("pcireg_force_intr_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Device(x) Write Buffer Flush Reg Access -- Read Only 0000_0240 - 0000_0258 + */ +uint64_t pcireg_wrb_flush_get(struct pcibus_info *pcibus_info, int device) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_wr_req_buf[device]; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_wr_req_buf[device]; + break; + default: + panic("pcireg_wrb_flush_get: unknown bridgetype bridge 0x%p", (void *)ptr); + } + + } + /* Read of the Write Buffer Flush should always return zero */ + return ret; +} + +void pcireg_int_ate_set(struct pcibus_info *pcibus_info, int ate_index, + uint64_t val) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_ate_ram[ate_index] = (uint64_t) val; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_ate_ram[ate_index] = (uint64_t) val; + break; + default: + panic + ("pcireg_int_ate_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +uint64_t *pcireg_int_ate_addr(struct pcibus_info *pcibus_info, int ate_index) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t *ret = (uint64_t *) 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = + (uint64_t *) & (ptr->tio.cp_int_ate_ram[ate_index]); + break; + case PCIBR_BRIDGETYPE_PIC: + ret = + (uint64_t *) & (ptr->pic.p_int_ate_ram[ate_index]); + break; + default: + panic + ("pcireg_int_ate_addr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + return ret; +} diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c new file mode 100644 index 000000000..8dd5d1f09 --- /dev/null +++ b/drivers/scsi/ahci.c @@ -0,0 +1,1045 @@ +/* + * ahci.c - AHCI SATA support + * + * Copyright 2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + * Version 1.0 of the AHCI specification: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include + +#define DRV_NAME "ahci" +#define DRV_VERSION "1.00" + + +enum { + AHCI_PCI_BAR = 5, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_USE_CLUSTERING = 0, + AHCI_CMD_SLOT_SZ = 32 * 32, + AHCI_RX_FIS_SZ = 256, + AHCI_CMD_TBL_HDR = 0x80, + AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16), + AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ + + AHCI_RX_FIS_SZ, + AHCI_IRQ_ON_SG = (1 << 31), + AHCI_CMD_ATAPI = (1 << 5), + AHCI_CMD_WRITE = (1 << 6), + + RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ + + board_ahci = 0, + + /* global controller registers */ + HOST_CAP = 0x00, /* host capabilities */ + HOST_CTL = 0x04, /* global host control */ + HOST_IRQ_STAT = 0x08, /* interrupt status */ + HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ + HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ + + /* HOST_CTL bits */ + HOST_RESET = (1 << 0), /* reset controller; self-clear */ + HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ + HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ + + /* HOST_CAP bits */ + HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ + + /* registers for each SATA port */ + PORT_LST_ADDR = 0x00, /* command list DMA addr */ + PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ + PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ + PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ + PORT_IRQ_STAT = 0x10, /* interrupt status */ + PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ + PORT_CMD = 0x18, /* port command */ + PORT_TFDATA = 0x20, /* taskfile data */ + PORT_SIG = 0x24, /* device TF signature */ + PORT_CMD_ISSUE = 0x38, /* command issue */ + PORT_SCR = 0x28, /* SATA phy register block */ + PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ + PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ + PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ + PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + + /* PORT_IRQ_{STAT,MASK} bits */ + PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ + PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ + PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ + PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ + PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ + PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ + PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ + PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ + + PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ + PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ + PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ + PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ + PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ + PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ + PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ + PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ + + PORT_IRQ_FATAL = PORT_IRQ_TF_ERR | + PORT_IRQ_HBUS_ERR | + PORT_IRQ_HBUS_DATA_ERR | + PORT_IRQ_IF_ERR, + DEF_PORT_IRQ = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY | + PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE | + PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS | + PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS | + PORT_IRQ_D2H_REG_FIS, + + /* PORT_CMD bits */ + PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ + PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ + PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ + PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ + PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ + PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ + + PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ + PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ + PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ +}; + +struct ahci_cmd_hdr { + u32 opts; + u32 status; + u32 tbl_addr; + u32 tbl_addr_hi; + u32 reserved[4]; +}; + +struct ahci_sg { + u32 addr; + u32 addr_hi; + u32 reserved; + u32 flags_size; +}; + +struct ahci_host_priv { + unsigned long flags; + u32 cap; /* cache of HOST_CAP register */ + u32 port_map; /* cache of HOST_PORTS_IMPL reg */ +}; + +struct ahci_port_priv { + struct ahci_cmd_hdr *cmd_slot; + dma_addr_t cmd_slot_dma; + void *cmd_tbl; + dma_addr_t cmd_tbl_dma; + struct ahci_sg *cmd_tbl_sg; + void *rx_fis; + dma_addr_t rx_fis_dma; +}; + +static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static int ahci_qc_issue(struct ata_queued_cmd *qc); +static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +static void ahci_phy_reset(struct ata_port *ap); +static void ahci_irq_clear(struct ata_port *ap); +static void ahci_eng_timeout(struct ata_port *ap); +static int ahci_port_start(struct ata_port *ap); +static void ahci_port_stop(struct ata_port *ap); +static void ahci_host_stop(struct ata_host_set *host_set); +static void ahci_qc_prep(struct ata_queued_cmd *qc); +static u8 ahci_check_status(struct ata_port *ap); +static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); + +static Scsi_Host_Template ahci_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = AHCI_MAX_SG, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = AHCI_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = AHCI_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations ahci_ops = { + .port_disable = ata_port_disable, + + .check_status = ahci_check_status, + .dev_select = ata_noop_dev_select, + + .phy_reset = ahci_phy_reset, + + .qc_prep = ahci_qc_prep, + .qc_issue = ahci_qc_issue, + + .eng_timeout = ahci_eng_timeout, + + .irq_handler = ahci_interrupt, + .irq_clear = ahci_irq_clear, + + .scr_read = ahci_scr_read, + .scr_write = ahci_scr_write, + + .port_start = ahci_port_start, + .port_stop = ahci_port_stop, + .host_stop = ahci_host_stop, +}; + +static struct ata_port_info ahci_port_info[] = { + /* board_ahci */ + { + .sht = &ahci_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &ahci_ops, + }, +}; + +static struct pci_device_id ahci_pci_tbl[] = { + { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, + { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, + { } /* terminate list */ +}; + + +static struct pci_driver ahci_pci_driver = { + .name = DRV_NAME, + .id_table = ahci_pci_tbl, + .probe = ahci_init_one, + .remove = ata_pci_remove_one, +}; + + +static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port) +{ + return base + 0x100 + (port * 0x80); +} + +static inline void *ahci_port_base (void *base, unsigned int port) +{ + return (void *) ahci_port_base_ul((unsigned long)base, port); +} + +static void ahci_host_stop(struct ata_host_set *host_set) +{ + struct ahci_host_priv *hpriv = host_set->private_data; + kfree(hpriv); +} + +static int ahci_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + struct ahci_port_priv *pp; + int rc; + void *mem, *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + dma_addr_t mem_dma; + + rc = ata_port_start(ap); + if (rc) + return rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + rc = -ENOMEM; + goto err_out; + } + memset(pp, 0, sizeof(*pp)); + + mem = pci_alloc_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma); + if (!mem) { + rc = -ENOMEM; + goto err_out_kfree; + } + memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = mem; + pp->cmd_slot_dma = mem_dma; + + mem += AHCI_CMD_SLOT_SZ; + mem_dma += AHCI_CMD_SLOT_SZ; + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = mem; + pp->rx_fis_dma = mem_dma; + + mem += AHCI_RX_FIS_SZ; + mem_dma += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + pp->cmd_tbl_dma = mem_dma; + + pp->cmd_tbl_sg = mem + AHCI_CMD_TBL_HDR; + + ap->private_data = pp; + + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + readl(port_mmio + PORT_LST_ADDR); /* flush */ + + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + readl(port_mmio + PORT_FIS_ADDR); /* flush */ + + writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | + PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | + PORT_CMD_START, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + return 0; + +err_out_kfree: + kfree(pp); +err_out: + ata_port_stop(ap); + return rc; +} + + +static void ahci_port_stop(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct ahci_port_priv *pp = ap->private_data; + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 tmp; + + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so + * this is slightly incorrect. + */ + msleep(500); + + ap->private_data = NULL; + pci_free_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, + pp->cmd_slot, pp->cmd_slot_dma); + kfree(pp); + ata_port_stop(ap); +} + +static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) +{ + unsigned int sc_reg; + + switch (sc_reg_in) { + case SCR_STATUS: sc_reg = 0; break; + case SCR_CONTROL: sc_reg = 1; break; + case SCR_ERROR: sc_reg = 2; break; + case SCR_ACTIVE: sc_reg = 3; break; + default: + return 0xffffffffU; + } + + return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, + u32 val) +{ + unsigned int sc_reg; + + switch (sc_reg_in) { + case SCR_STATUS: sc_reg = 0; break; + case SCR_CONTROL: sc_reg = 1; break; + case SCR_ERROR: sc_reg = 2; break; + case SCR_ACTIVE: sc_reg = 3; break; + default: + return; + } + + writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + +static void ahci_phy_reset(struct ata_port *ap) +{ + void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; + struct ata_taskfile tf; + struct ata_device *dev = &ap->device[0]; + u32 tmp; + + __sata_phy_reset(ap); + + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + tmp = readl(port_mmio + PORT_SIG); + tf.lbah = (tmp >> 24) & 0xff; + tf.lbam = (tmp >> 16) & 0xff; + tf.lbal = (tmp >> 8) & 0xff; + tf.nsect = (tmp) & 0xff; + + dev->class = ata_dev_classify(&tf); + if (!ata_dev_present(dev)) + ata_port_disable(ap); +} + +static u8 ahci_check_status(struct ata_port *ap) +{ + void *mmio = (void *) ap->ioaddr.cmd_addr; + + return readl(mmio + PORT_TFDATA) & 0xFF; +} + +static void ahci_fill_sg(struct ata_queued_cmd *qc) +{ + struct ahci_port_priv *pp = qc->ap->private_data; + unsigned int i; + + VPRINTK("ENTER\n"); + + /* + * Next, the S/G list. + */ + for (i = 0; i < qc->n_elem; i++) { + u32 sg_len; + dma_addr_t addr; + + addr = sg_dma_address(&qc->sg[i]); + sg_len = sg_dma_len(&qc->sg[i]); + + pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff); + pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); + pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1); + } +} + +static void ahci_qc_prep(struct ata_queued_cmd *qc) +{ + struct ahci_port_priv *pp = qc->ap->private_data; + u32 opts; + const u32 cmd_fis_len = 5; /* five dwords */ + + /* + * Fill in command slot information (currently only one slot, + * slot 0, is currently since we don't do queueing) + */ + + opts = (qc->n_elem << 16) | cmd_fis_len; + if (qc->tf.flags & ATA_TFLAG_WRITE) + opts |= AHCI_CMD_WRITE; + + switch (qc->tf.protocol) { + case ATA_PROT_ATAPI: + case ATA_PROT_ATAPI_NODATA: + case ATA_PROT_ATAPI_DMA: + opts |= AHCI_CMD_ATAPI; + break; + + default: + /* do nothing */ + break; + } + + pp->cmd_slot[0].opts = cpu_to_le32(opts); + pp->cmd_slot[0].status = 0; + pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff); + pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); + + /* + * Fill in command table information. First, the header, + * a SATA Register - Host to Device command FIS. + */ + ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0); + + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + ahci_fill_sg(qc); +} + +static inline void ahci_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, + int have_err) +{ + /* get drive status; clear intr; complete txn */ + ata_qc_complete(ata_qc_from_tag(ap, ap->active_tag), + have_err ? ATA_ERR : 0); +} + +static void ahci_intr_error(struct ata_port *ap, u32 irq_stat) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 tmp; + int work; + + /* stop DMA */ + tmp = readl(port_mmio + PORT_CMD); + tmp &= PORT_CMD_START | PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for engine to stop. TODO: this could be + * as long as 500 msec + */ + work = 1000; + while (work-- > 0) { + tmp = readl(port_mmio + PORT_CMD); + if ((tmp & PORT_CMD_LIST_ON) == 0) + break; + udelay(10); + } + + /* clear SATA phy error, if any */ + tmp = readl(port_mmio + PORT_SCR_ERR); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* if DRQ/BSY is set, device needs to be reset. + * if so, issue COMRESET + */ + tmp = readl(port_mmio + PORT_TFDATA); + if (tmp & (ATA_BUSY | ATA_DRQ)) { + writel(0x301, port_mmio + PORT_SCR_CTL); + readl(port_mmio + PORT_SCR_CTL); /* flush */ + udelay(10); + writel(0x300, port_mmio + PORT_SCR_CTL); + readl(port_mmio + PORT_SCR_CTL); /* flush */ + } + + /* re-start DMA */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_START | PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->port_no); +} + +static void ahci_eng_timeout(struct ata_port *ap) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT)); + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + } else { + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + ata_qc_complete(qc, ATA_ERR); + } + +} + +static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 status, serr, ci; + + serr = readl(port_mmio + PORT_SCR_ERR); + writel(serr, port_mmio + PORT_SCR_ERR); + + status = readl(port_mmio + PORT_IRQ_STAT); + writel(status, port_mmio + PORT_IRQ_STAT); + + ci = readl(port_mmio + PORT_CMD_ISSUE); + if (likely((ci & 0x1) == 0)) { + if (qc) { + ata_qc_complete(qc, 0); + qc = NULL; + } + } + + if (status & PORT_IRQ_FATAL) { + ahci_intr_error(ap, status); + if (qc) + ata_qc_complete(qc, ATA_ERR); + } + + return 1; +} + +static void ahci_irq_clear(struct ata_port *ap) +{ + /* TODO */ +} + +static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int i, handled = 0; + void *mmio; + u32 irq_stat, irq_ack = 0; + + VPRINTK("ENTER\n"); + + hpriv = host_set->private_data; + mmio = host_set->mmio_base; + + /* sigh. 0xffffffff is a valid return from h/w */ + irq_stat = readl(mmio + HOST_IRQ_STAT); + irq_stat &= hpriv->port_map; + if (!irq_stat) + return IRQ_NONE; + + spin_lock(&host_set->lock); + + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap; + u32 tmp; + + VPRINTK("port %u\n", i); + ap = host_set->ports[i]; + tmp = irq_stat & (1 << i); + if (tmp && ap) { + struct ata_queued_cmd *qc; + qc = ata_qc_from_tag(ap, ap->active_tag); + if (ahci_host_intr(ap, qc)) + irq_ack |= (1 << i); + } + } + + if (irq_ack) { + writel(irq_ack, mmio + HOST_IRQ_STAT); + handled = 1; + } + + spin_unlock(&host_set->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} + +static int ahci_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void *port_mmio = (void *) ap->ioaddr.cmd_addr; + + writel(1, port_mmio + PORT_SCR_ACT); + readl(port_mmio + PORT_SCR_ACT); /* flush */ + + writel(1, port_mmio + PORT_CMD_ISSUE); + readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + + return 0; +} + +static void ahci_setup_port(struct ata_ioports *port, unsigned long base, + unsigned int port_idx) +{ + VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx); + base = ahci_port_base_ul(base, port_idx); + VPRINTK("base now==0x%lx\n", base); + + port->cmd_addr = base; + port->scr_addr = base + PORT_SCR; + + VPRINTK("EXIT\n"); +} + +static int ahci_host_init(struct ata_probe_ent *probe_ent) +{ + struct ahci_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = probe_ent->pdev; + void __iomem *mmio = probe_ent->mmio_base; + u32 tmp, cap_save; + u16 tmp16; + unsigned int i, j, using_dac; + int rc; + void __iomem *port_mmio; + + cap_save = readl(mmio + HOST_CAP); + cap_save &= ( (1<<28) | (1<<17) ); + cap_save |= (1 << 27); + + /* global controller reset */ + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) { + writel(tmp | HOST_RESET, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + /* reset must complete within 1 second, or + * the hardware should be considered fried. + */ + ssleep(1); + + tmp = readl(mmio + HOST_CTL); + if (tmp & HOST_RESET) { + printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n", + pci_name(pdev), tmp); + return -EIO; + } + + writel(HOST_AHCI_EN, mmio + HOST_CTL); + (void) readl(mmio + HOST_CTL); /* flush */ + writel(cap_save, mmio + HOST_CAP); + writel(0xf, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + pci_read_config_word(pdev, 0x92, &tmp16); + tmp16 |= 0xf; + pci_write_config_word(pdev, 0x92, tmp16); + + hpriv->cap = readl(mmio + HOST_CAP); + hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); + probe_ent->n_ports = (hpriv->cap & 0x1f) + 1; + + VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", + hpriv->cap, hpriv->port_map, probe_ent->n_ports); + + using_dac = hpriv->cap & HOST_CAP_64; + if (using_dac && + !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) { + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n", + pci_name(pdev)); + return rc; + } + } + + hpriv->flags |= HOST_CAP_64; + } else { + rc = pci_set_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n", + pci_name(pdev)); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n", + pci_name(pdev)); + return rc; + } + } + + for (i = 0; i < probe_ent->n_ports; i++) { +#if 0 /* BIOSen initialize this incorrectly */ + if (!(hpriv->port_map & (1 << i))) + continue; +#endif + + port_mmio = ahci_port_base(mmio, i); + VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio); + + ahci_setup_port(&probe_ent->port[i], + (unsigned long) mmio, i); + + /* make sure port is not active */ + tmp = readl(port_mmio + PORT_CMD); + VPRINTK("PORT_CMD 0x%x\n", tmp); + if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START)) { + tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START); + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + /* spec says 500 msecs for each bit, so + * this is slightly incorrect. + */ + msleep(500); + } + + writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD); + + j = 0; + while (j < 100) { + msleep(10); + tmp = readl(port_mmio + PORT_SCR_STAT); + if ((tmp & 0xf) == 0x3) + break; + j++; + } + + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* ack any pending irq events for this port */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << i, mmio + HOST_IRQ_STAT); + + /* set irq mask (enables interrupts) */ + writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK); + } + + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + + pci_set_master(pdev); + + return 0; +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static void ahci_print_info(struct ata_probe_ent *probe_ent) +{ + struct ahci_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = probe_ent->pdev; + void *mmio = probe_ent->mmio_base; + u32 vers, cap, impl, speed; + const char *speed_s; + u16 cc; + const char *scc_s; + + vers = readl(mmio + HOST_VERSION); + cap = hpriv->cap; + impl = hpriv->port_map; + + speed = (cap >> 20) & 0xf; + if (speed == 1) + speed_s = "1.5"; + else if (speed == 2) + speed_s = "3"; + else + speed_s = "?"; + + pci_read_config_word(pdev, 0x0a, &cc); + if (cc == 0x0101) + scc_s = "IDE"; + else if (cc == 0x0106) + scc_s = "SATA"; + else if (cc == 0x0104) + scc_s = "RAID"; + else + scc_s = "unknown"; + + printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x " + "%u slots %u ports %s Gbps 0x%x impl %s mode\n" + , + pci_name(pdev), + + (vers >> 24) & 0xff, + (vers >> 16) & 0xff, + (vers >> 8) & 0xff, + vers & 0xff, + + ((cap >> 8) & 0x1f) + 1, + (cap & 0x1f) + 1, + speed_s, + impl, + scc_s); + + printk(KERN_INFO DRV_NAME "(%s) flags: " + "%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n" + , + pci_name(pdev), + + cap & (1 << 31) ? "64bit " : "", + cap & (1 << 30) ? "ncq " : "", + cap & (1 << 28) ? "ilck " : "", + cap & (1 << 27) ? "stag " : "", + cap & (1 << 26) ? "pm " : "", + cap & (1 << 25) ? "led " : "", + + cap & (1 << 24) ? "clo " : "", + cap & (1 << 19) ? "nz " : "", + cap & (1 << 18) ? "only " : "", + cap & (1 << 17) ? "pmp " : "", + cap & (1 << 15) ? "pio " : "", + cap & (1 << 14) ? "slum " : "", + cap & (1 << 13) ? "part " : "" + ); +} + +static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + struct ahci_host_priv *hpriv; + unsigned long base; + void *mmio_base; + unsigned int board_idx = (unsigned int) ent->driver_data; + int rc; + + VPRINTK("ENTER\n"); + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + pci_enable_intx(pdev); + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR), + pci_resource_len(pdev, AHCI_PCI_BAR)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err_out_iounmap; + } + memset(hpriv, 0, sizeof(*hpriv)); + + probe_ent->sht = ahci_port_info[board_idx].sht; + probe_ent->host_flags = ahci_port_info[board_idx].host_flags; + probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask; + probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask; + probe_ent->port_ops = ahci_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + probe_ent->private_data = hpriv; + + /* initialize adapter */ + rc = ahci_host_init(probe_ent); + if (rc) + goto err_out_hpriv; + + ahci_print_info(probe_ent); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + kfree(probe_ent); + + return 0; + +err_out_hpriv: + kfree(hpriv); +err_out_iounmap: + iounmap(mmio_base); +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +static int __init ahci_init(void) +{ + return pci_module_init(&ahci_pci_driver); +} + + +static void __exit ahci_exit(void) +{ + pci_unregister_driver(&ahci_pci_driver); +} + + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("AHCI SATA low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +module_init(ahci_init); +module_exit(ahci_exit); diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile new file mode 100644 index 000000000..5f06e633c --- /dev/null +++ b/drivers/scsi/lpfc/Makefile @@ -0,0 +1,7 @@ +# Driver for Emulex LightPulse fibre channel host bus adapters. +EXTRA_CFLAGS += -DFC_TRANS_VER1 +obj-$(CONFIG_SCSI_LPFC) := lpfc.o + +lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \ +lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsiport.o \ +lpfc_fcp.o diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h new file mode 100644 index 000000000..4fea35cc1 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc.h @@ -0,0 +1,449 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc.h 1.134 2004/11/18 14:53:45EST sf_support Exp $ + */ + +#ifndef _H_LPFC +#define _H_LPFC + +struct lpfc_sli2_slim; + +#define LPFC_MAX_TARGET 256 /* max nunber of targets + supported */ +#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els + requests */ +#define LPFC_MAX_NS_RETRY 3 /* Try to get to the NameServer + 3 times and then give up. */ +#define LPFC_DFT_HBA_Q_DEPTH 2048 /* max cmds per hba */ +#define LPFC_LC_HBA_Q_DEPTH 1024 /* max cmds per low cost hba */ +#define LPFC_LP101_HBA_Q_DEPTH 128 /* max cmds per low cost hba */ + +/* Define the SLIM2 page size. */ +#define LPFC_SLIM2_PAGE_AREA 8192 + +/* Define macros for 64 bit support */ +#define putPaddrLow(addr) ((uint32_t) (0xffffffff & (u64)(addr))) +#define putPaddrHigh(addr) ((uint32_t) (0xffffffff & (((u64)(addr))>>32))) +#define getPaddr(high, low) ((dma_addr_t)( \ + (( (u64)(high)<<16 ) << 16)|( (u64)(low)))) +/* Provide maximum configuration definitions. */ +#define LPFC_DRVR_TIMEOUT 16 /* driver iocb timeout value in sec */ +#define MAX_FCP_TARGET 256 /* max num of FCP targets supported */ +#define FC_MAX_ADPTMSG 64 + +#define MAX_HBAEVT 32 + +#if __LITTLE_ENDIAN + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = swab16(lun); \ + } + +#else /* BIG_ENDIAN_HOST */ + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = (uint32_t)(lun << 16); \ + } +#endif + +/****************************************************************************/ +/* Device VPD save area */ +/****************************************************************************/ +typedef struct lpfc_vpd { + uint32_t status; /* vpd status value */ + uint32_t length; /* number of bytes actually returned */ + struct { + uint32_t rsvd1; /* Revision numbers */ + uint32_t biuRev; + uint32_t smRev; + uint32_t smFwRev; + uint32_t endecRev; + uint16_t rBit; + uint8_t fcphHigh; + uint8_t fcphLow; + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + } rev; +} lpfc_vpd_t; + +struct lpfc_scsi_buf; + +struct lpfc_hba_event { + uint32_t fc_eventcode; + uint32_t fc_evdata1; + uint32_t fc_evdata2; + uint32_t fc_evdata3; + uint32_t fc_evdata4; +}; + +/* + * lpfc stat counters + */ +struct lpfc_stats { + /* Statistics for ELS commands */ + uint32_t elsLogiCol; + uint32_t elsRetryExceeded; + uint32_t elsXmitRetry; + uint32_t elsDelayRetry; + uint32_t elsRcvDrop; + uint32_t elsRcvFrame; + uint32_t elsRcvRSCN; + uint32_t elsRcvRNID; + uint32_t elsRcvFARP; + uint32_t elsRcvFARPR; + uint32_t elsRcvFLOGI; + uint32_t elsRcvPLOGI; + uint32_t elsRcvADISC; + uint32_t elsRcvPDISC; + uint32_t elsRcvFAN; + uint32_t elsRcvLOGO; + uint32_t elsRcvPRLO; + uint32_t elsRcvPRLI; + uint32_t elsRcvRRQ; + uint32_t elsXmitFLOGI; + uint32_t elsXmitPLOGI; + uint32_t elsXmitPRLI; + uint32_t elsXmitADISC; + uint32_t elsXmitLOGO; + uint32_t elsXmitSCR; + uint32_t elsXmitRNID; + uint32_t elsXmitFARP; + uint32_t elsXmitFARPR; + uint32_t elsXmitACC; + uint32_t elsXmitLSRJT; + + uint32_t frameRcvBcast; + uint32_t frameRcvMulti; + uint32_t strayXmitCmpl; + uint32_t frameXmitDelay; + uint32_t xriCmdCmpl; + uint32_t xriStatErr; + uint32_t LinkUp; + uint32_t LinkDown; + uint32_t LinkMultiEvent; + uint32_t NoRcvBuf; + uint32_t fcpCmd; + uint32_t fcpCmpl; + uint32_t fcpRspErr; + uint32_t fcpRemoteStop; + uint32_t fcpPortRjt; + uint32_t fcpPortBusy; + uint32_t fcpError; + uint32_t fcpLocalErr; +}; + +enum sysfs_mbox_state { + SMBOX_IDLE, + SMBOX_WRITING, + SMBOX_READING +}; + +struct lpfc_sysfs_mbox { + enum sysfs_mbox_state state; + size_t offset; + struct lpfcMboxq * mbox; +}; + +struct lpfc_hba { + uint32_t intr_inited; /* flag for interrupt registration */ + struct list_head hba_list; /* List of hbas/ports */ + struct lpfc_sli sli; + struct lpfc_sli2_slim *slim2p; + dma_addr_t slim2p_mapping; + + uint32_t hba_state; + +#define LPFC_INIT_START 1 /* Initial state after board reset */ +#define LPFC_INIT_MBX_CMDS 2 /* Initialize HBA with mbox commands */ +#define LPFC_LINK_DOWN 3 /* HBA initialized, link is down */ +#define LPFC_LINK_UP 4 /* Link is up - issue READ_LA */ +#define LPFC_LOCAL_CFG_LINK 5 /* local NPORT Id configured */ +#define LPFC_FLOGI 6 /* FLOGI sent to Fabric */ +#define LPFC_FABRIC_CFG_LINK 7 /* Fabric assigned NPORT Id + configured */ +#define LPFC_NS_REG 8 /* Register with NameServer */ +#define LPFC_NS_QRY 9 /* Query NameServer for NPort ID list */ +#define LPFC_BUILD_DISC_LIST 10 /* Build ADISC and PLOGI lists for + * device authentication / discovery */ +#define LPFC_DISC_AUTH 11 /* Processing ADISC list */ +#define LPFC_CLEAR_LA 12 /* authentication cmplt - issue + CLEAR_LA */ +#define LPFC_HBA_READY 32 +#define LPFC_HBA_ERROR 0xff + + uint8_t fc_linkspeed; /* Link speed after last READ_LA */ + + uint32_t fc_eventTag; /* event tag for link attention */ + uint32_t fc_prli_sent; /* cntr for outstanding PRLIs */ + + uint32_t num_disc_nodes; /*in addition to hba_state */ + + uint8_t fcp_mapping; /* Map FCP devices based on WWNN WWPN or DID */ +#define FCP_SEED_WWNN 0x1 +#define FCP_SEED_WWPN 0x2 +#define FCP_SEED_DID 0x4 +#define FCP_SEED_MASK 0x7 +#define FCP_SEED_AUTO 0x8 /* binding was created by auto mapping */ + + struct timer_list fc_estabtmo; /* link establishment timer */ + struct timer_list fc_disctmo; /* Discovery rescue timer */ + struct timer_list fc_fdmitmo; /* fdmi timer */ + struct timer_list fc_scantmo; /* scsi scan host timer */ + + + void *fc_evt_head; /* waiting for event queue */ + void *fc_evt_tail; /* waiting for event queue */ + + uint16_t hba_event_put; /* hbaevent event put word anchor */ + uint16_t hba_event_get; /* hbaevent event get word anchor */ + uint32_t hba_event_missed; /* hbaevent missed event word anchor */ + uint32_t sid_cnt; /* SCSI ID counter */ + + struct lpfc_hba_event hbaevt[MAX_HBAEVT]; + + /* These fields used to be binfo */ + struct lpfc_name fc_nodename; /* fc nodename */ + struct lpfc_name fc_portname; /* fc portname */ + uint32_t fc_pref_DID; /* preferred D_ID */ + uint8_t fc_pref_ALPA; /* preferred AL_PA */ + uint32_t fc_edtov; /* E_D_TOV timer value */ + uint32_t fc_arbtov; /* ARB_TOV timer value */ + uint32_t fc_ratov; /* R_A_TOV timer value */ + uint32_t fc_rttov; /* R_T_TOV timer value */ + uint32_t fc_altov; /* AL_TOV timer value */ + uint32_t fc_crtov; /* C_R_TOV timer value */ + uint32_t fc_citov; /* C_I_TOV timer value */ + uint32_t fc_myDID; /* fibre channel S_ID */ + uint32_t fc_prevDID; /* previous fibre channel S_ID */ + + struct serv_parm fc_sparam; /* buffer for our service parameters */ + struct serv_parm fc_fabparam; /* fabric service parameters buffer */ + uint8_t alpa_map[128]; /* AL_PA map from READ_LA */ + + uint8_t fc_ns_retry; /* retries for fabric nameserver */ + uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */ + uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */ + struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN]; + uint32_t lmt; + uint32_t fc_flag; /* FC flags */ +#define FC_PT2PT 0x1 /* pt2pt with no fabric */ +#define FC_PT2PT_PLOGI 0x2 /* pt2pt initiate PLOGI */ +#define FC_DISC_TMO 0x4 /* Discovery timer running */ +#define FC_PUBLIC_LOOP 0x8 /* Public loop */ +#define FC_LBIT 0x10 /* LOGIN bit in loopinit set */ +#define FC_RSCN_MODE 0x20 /* RSCN cmd rcv'ed */ +#define FC_NLP_MORE 0x40 /* More node to process in node tbl */ +#define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */ +#define FC_FABRIC 0x100 /* We are fabric attached */ +#define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */ +#define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/ +#define FC_LOADING 0x1000 /* HBA in process of loading drvr */ +#define FC_UNLOADING 0x2000 /* HBA in process of unloading drvr */ +#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */ +#define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */ + + uint32_t fc_topology; /* link topology, from LINK INIT */ + + struct lpfc_stats fc_stat; + + /* These are the head/tail pointers for the bind, plogi, adisc, unmap, + * and map lists. Their counters are immediately following. + */ + struct list_head fc_nlpbind_list; + struct list_head fc_plogi_list; + struct list_head fc_adisc_list; + struct list_head fc_reglogin_list; + struct list_head fc_prli_list; + struct list_head fc_nlpunmap_list; + struct list_head fc_nlpmap_list; + struct list_head fc_npr_list; + struct list_head fc_unused_list; + + /* Keep counters for the number of entries in each list. */ + uint16_t fc_bind_cnt; + uint16_t fc_plogi_cnt; + uint16_t fc_adisc_cnt; + uint16_t fc_reglogin_cnt; + uint16_t fc_prli_cnt; + uint16_t fc_unmap_cnt; + uint16_t fc_map_cnt; + uint16_t fc_npr_cnt; + uint16_t fc_unused_cnt; + struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */ + uint32_t nport_event_cnt; /* timestamp for nlplist entry */ + + struct lpfc_target *device_queue_hash[MAX_FCP_TARGET]; +#define LPFC_RPI_HASH_SIZE 64 +#define LPFC_RPI_HASH_FUNC(x) ((x) & (0x3f)) + /* ptr to active D_ID / RPIs */ + struct lpfc_nodelist *fc_nlplookup[LPFC_RPI_HASH_SIZE]; + uint32_t wwnn[2]; + uint32_t RandomData[7]; + + uint32_t cfg_log_verbose; + uint32_t cfg_lun_queue_depth; + uint32_t cfg_nodev_tmo; + uint32_t cfg_hba_queue_depth; + uint32_t cfg_automap; + uint32_t cfg_fcp_class; + uint32_t cfg_use_adisc; + uint32_t cfg_ack0; + uint32_t cfg_topology; + uint32_t cfg_scan_down; + uint32_t cfg_link_speed; + uint32_t cfg_cr_delay; + uint32_t cfg_cr_count; + uint32_t cfg_fdmi_on; + uint32_t cfg_fcp_bind_method; + uint32_t cfg_discovery_threads; + uint32_t cfg_max_luns; + uint32_t cfg_scsi_hotplug; + + lpfc_vpd_t vpd; /* vital product data */ + +#if defined(FC_TRANS_265_BLKPATCH) + /* + * Provide a per-HBA timer for 2.6.5 kernels patched with the + * block/unblock FC transport patch. + */ + struct timer_list dev_loss_timer; +#endif + + struct Scsi_Host *host; + struct pci_dev *pcidev; + struct list_head dpc_disc; + + pid_t dpc_pid; + int dpc_kill; + struct completion dpc_startup; + struct completion dpc_exiting; + struct semaphore *dpc_wait; + + unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */ + unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */ + void *slim_memmap_p; /* Kernel memory mapped address for PCI + BAR0 */ + void *ctrl_regs_memmap_p; /* Kernel memory mapped address for PCI + BAR2 */ + + void *MBslimaddr; /* virtual address for mbox cmds */ + void *HAregaddr; /* virtual address for host attn reg */ + void *CAregaddr; /* virtual address for chip attn reg */ + void *HSregaddr; /* virtual address for host status reg */ + void *HCregaddr; /* virtual address for host ctl reg */ + wait_queue_head_t linkevtwq; + wait_queue_head_t rscnevtwq; + wait_queue_head_t ctevtwq; + + uint8_t brd_no; /* FC board number */ + + char SerialNumber[32]; /* adapter Serial Number */ + char OptionROMVersion[32]; /* adapter BIOS / Fcode version */ + + struct timer_list els_tmofunc; + + void *link_stats; + + /* + * stat counters + */ + uint64_t fc4InputRequests; + uint64_t fc4OutputRequests; + uint64_t fc4ControlRequests; + + struct lpfc_sysfs_mbox sysfs_mbox; +; + /* pci_mem_pools */ + struct pci_pool *lpfc_scsi_dma_ext_pool; + struct pci_pool *lpfc_mbuf_pool; + struct lpfc_dma_pool lpfc_mbuf_safety_pool; + mempool_t *scsibuf_mem_pool; + + mempool_t *iocb_mem_pool; + mempool_t *mbox_mem_pool; + mempool_t *nlp_mem_pool; + mempool_t *bind_mem_pool; + struct list_head freebufList; + struct list_head ctrspbuflist; + struct list_head rnidrspbuflist; +}; + +/* event mask definitions */ +#define FC_REG_LINK_EVENT 0x1 /* Register for link up / down events */ +#define FC_REG_RSCN_EVENT 0x2 /* Register for RSCN events */ +#define FC_REG_CT_EVENT 0x4 /* Register for CT request events */ + +#define FC_FSTYPE_ALL 0xffff /* match on all fsTypes */ + +typedef struct fcEVT { /* Kernel level Event structure */ + uint32_t evt_handle; + uint32_t evt_mask; + uint32_t evt_data0; + uint16_t evt_sleep; + uint16_t evt_flags; + void *evt_type; + void *evt_next; + void *evt_data1; + uint32_t evt_data2; +} fcEVT_t; + +typedef struct fcEVTHDR { /* Kernel level Event Header */ + uint32_t e_handle; + uint32_t e_mask; + uint16_t e_mode; +#define E_SLEEPING_MODE 0x0001 + uint16_t e_refcnt; + uint16_t e_flag; +#define E_GET_EVENT_ACTIVE 0x0001 + fcEVT_t *e_head; + fcEVT_t *e_tail; + void *e_next_header; + void *e_type; +} fcEVTHDR_t; + +struct rnidrsp { + void *buf; + uint32_t uniqueid; + struct list_head list; + uint32_t data; +}; + +#endif /* _H_LPFC */ diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h new file mode 100644 index 000000000..43531b50c --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -0,0 +1,120 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_compat.h 1.28 2004/11/09 14:49:24EST sf_support Exp $ + * + * This file provides macros to aid compilation in the Linux 2.4 kernel + * over various platform architectures. + */ + +#ifndef _H_LPFC_COMPAT +#define _H_LPFC_COMPAT + + +/******************************************************************* +Note: HBA's SLI memory contains little-endian LW. +Thus to access it from a little-endian host, +memcpy_toio() and memcpy_fromio() can be used. +However on a big-endian host, copy 4 bytes at a time, +using writel() and readl(). + *******************************************************************/ + +#if __BIG_ENDIAN + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* write input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + writel( *src32, dest32); + readl(dest32); /* flush */ + dest32++; + src32++; + } + + return; +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* read input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + *dest32 = readl( src32); + dest32++; + src32++; + } + + return; +} + +#else + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_toio( dest, src, bytes); +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_fromio( dest, src, bytes); +} + +#endif /* __BIG_ENDIAN */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) +#define msleep(x) do { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x)); \ + } while (0); +/* Provide local msecs_to_jiffies call for earlier kernels */ +static inline unsigned long msecs_to_jiffies(const unsigned int m) +{ +#if HZ <= 1000 && !(1000 % HZ) + return (m + (1000 / HZ) - 1) / (1000 / HZ); +#elif HZ > 1000 && !(HZ % 1000) + return m * (HZ / 1000); +#else + return (m * HZ + 999) / 1000; +#endif +} +#endif +#endif /* _H_LPFC_COMPAT */ diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h new file mode 100644 index 000000000..ac414f82d --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -0,0 +1,265 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_crtn.h 1.140 2004/11/17 19:00:57EST sf_support Exp $ + */ + +#ifndef _H_LPFC_CRTN +#define _H_LPFC_CRTN + +#include +#include +#include + +#include "lpfc_disc.h" +#include "lpfc_logmsg.h" +#include "lpfc_scsi.h" +#include "lpfc_sli.h" + + +void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); +int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *, + uint32_t); +void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_unreg_did(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); + + +int lpfc_linkdown(struct lpfc_hba *); +void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); + +void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_consistent_bind_save(struct lpfc_hba *, struct lpfc_bindlist *); +int lpfc_nlp_plogi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_adisc(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_unmapped(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_mapped(struct lpfc_hba *, struct lpfc_nodelist *, + struct lpfc_bindlist *); +int lpfc_nlp_list(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_set_disctmo(struct lpfc_hba *); +int lpfc_can_disctmo(struct lpfc_hba *); +int lpfc_unreg_rpi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, struct lpfc_nodelist *); +int lpfc_nlp_remove(struct lpfc_hba *, struct lpfc_nodelist *); +void lpfc_nlp_init(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t); +struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_hba *, uint32_t); +struct lpfc_nodelist *lpfc_setup_rscn_node(struct lpfc_hba *, uint32_t); +void lpfc_disc_list_loopmap(struct lpfc_hba *); +void lpfc_disc_start(struct lpfc_hba *); +void lpfc_disc_flush_list(struct lpfc_hba *); +void lpfc_establish_link_tmo(unsigned long); +void lpfc_disc_timeout(unsigned long); +void lpfc_scan_timeout(unsigned long); +struct lpfc_target *lpfc_find_target(struct lpfc_hba *, uint32_t, + struct lpfc_nodelist *); +void lpfc_set_failmask(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t, + uint32_t); +void lpfc_process_nodev_timeout(struct lpfc_hba *, struct lpfc_nodelist *); + +struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi); +struct lpfc_nodelist *lpfc_findnode_remove_rpi(struct lpfc_hba * phba, + uint16_t rpi); +void lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi); + +int lpfc_discq_post_event(struct lpfc_hba *, void *, void *, uint32_t); +int lpfc_do_dpc(void *); +int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + +uint32_t lpfc_cmpl_prli_reglogin_issue(struct lpfc_hba *, + struct lpfc_nodelist *, void *, + uint32_t); +uint32_t lpfc_cmpl_plogi_prli_issue(struct lpfc_hba *, struct lpfc_nodelist *, + void *, uint32_t); + +int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *, + struct serv_parm *, uint32_t); +int lpfc_els_abort_flogi(struct lpfc_hba *); +int lpfc_initial_flogi(struct lpfc_hba *); +int lpfc_issue_els_plogi(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_prli(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_adisc(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_logo(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_scr(struct lpfc_hba *, uint32_t, uint8_t); +int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *); +int lpfc_els_rsp_acc(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *, LPFC_MBOXQ_t *, uint8_t); +int lpfc_els_rsp_reject(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_adisc_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_prli_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +void lpfc_els_retry_delay(unsigned long); +void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_els_handle_rscn(struct lpfc_hba *); +int lpfc_els_flush_rscn(struct lpfc_hba *); +int lpfc_rscn_payload_check(struct lpfc_hba *, uint32_t); +void lpfc_els_flush_cmd(struct lpfc_hba *); +int lpfc_els_disc_adisc(struct lpfc_hba *); +int lpfc_els_disc_plogi(struct lpfc_hba *); +void lpfc_els_timeout_handler(unsigned long ptr); + +void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_ns_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +int lpfc_fdmi_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_fdmi_tmo(unsigned long); + +int lpfc_config_port_prep(struct lpfc_hba *); +int lpfc_config_port_post(struct lpfc_hba *); +int lpfc_hba_down_prep(struct lpfc_hba *); +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +void lpfc_hba_init(struct lpfc_hba *, uint32_t *); +int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int); +void lpfc_cleanup(struct lpfc_hba *, uint32_t); +int lpfc_scsi_free(struct lpfc_hba *); +void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int); +uint8_t *lpfc_get_lpfchba_info(struct lpfc_hba *, uint8_t *); +int lpfc_fcp_abort(struct lpfc_hba *, int, int, int); +int lpfc_put_event(struct lpfc_hba *, uint32_t, uint32_t, void *, + uint32_t, uint32_t); +void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *); +int lpfc_online(struct lpfc_hba *); +int lpfc_offline(struct lpfc_hba *); + + + +int lpfc_sli_queue_setup(struct lpfc_hba *); +void lpfc_slim_access(struct lpfc_hba *); + +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +irqreturn_t lpfc_intr_handler(int, void *, struct pt_regs *); + +void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *); +void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *); +LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *); + +int lpfc_mem_alloc(struct lpfc_hba *); +void lpfc_mem_free(struct lpfc_hba *); + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd); + +int lpfc_sli_hba_setup(struct lpfc_hba *); +int lpfc_sli_hba_down(struct lpfc_hba *); +int lpfc_sli_intr(struct lpfc_hba *); +int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t); +int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, uint32_t); +void lpfc_sli_pcimem_bcopy(uint32_t *, uint32_t *, uint32_t); +int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_dmabuf *); +struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *, + struct lpfc_sli_ring *, + dma_addr_t); +uint32_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_sli_abort_iocb_ctx(struct lpfc_hba *, struct lpfc_sli_ring *, + uint32_t); +int lpfc_sli_sum_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_abort_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *, int); +int lpfc_sli_sum_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t); +int lpfc_sli_abort_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t, int); +int lpfc_sli_abort_iocb_tgt(struct lpfc_hba *, struct lpfc_sli_ring *, + uint16_t, int); +void lpfc_mbox_timeout(unsigned long); + +void lpfc_map_fcp_cmnd_to_bpl(struct lpfc_hba *, struct lpfc_scsi_buf *); +void lpfc_free_scsi_cmd(struct lpfc_scsi_buf *); +uint32_t lpfc_os_timeout_transform(struct lpfc_hba *, uint32_t); + +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn); +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn); +struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, + uint32_t did); + +void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp); + +int lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout); + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout); +int lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq, + uint32_t timeout); +void lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2); +void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *); +void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t); + +int lpfc_stop_timer(struct lpfc_hba *); + + +/* Function prototypes. */ +int lpfc_queuecommand(struct scsi_cmnd *, void (*done) (struct scsi_cmnd *)); +int lpfc_abort_handler(struct scsi_cmnd *); +int lpfc_reset_bus_handler(struct scsi_cmnd *); +int lpfc_reset_lun_handler(struct scsi_cmnd *); + +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) +void lpfc_target_unblock(struct lpfc_hba *, struct lpfc_target *); +void lpfc_target_block(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_remove(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_add(struct lpfc_hba *, struct lpfc_target *); +#endif + +#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) +#define HBA_EVENT_RSCN 5 +#define HBA_EVENT_LINK_UP 2 +#define HBA_EVENT_LINK_DOWN 3 +#endif /* _H_LPFC_CRTN */ diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c new file mode 100644 index 000000000..83342c469 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -0,0 +1,1288 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_ct.c 1.143 2004/11/17 14:50:38EST sf_support Exp $ + * + * Fibre Channel SCSI LAN Device Driver CT support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" + + +#define HBA_PORTSPEED_UNKNOWN 0 /* Unknown - transceiver + * incapable of reporting */ +#define HBA_PORTSPEED_1GBIT 1 /* 1 GBit/sec */ +#define HBA_PORTSPEED_2GBIT 2 /* 2 GBit/sec */ +#define HBA_PORTSPEED_4GBIT 8 /* 4 GBit/sec */ +#define HBA_PORTSPEED_8GBIT 16 /* 8 GBit/sec */ +#define HBA_PORTSPEED_10GBIT 4 /* 10 GBit/sec */ +#define HBA_PORTSPEED_NOT_NEGOTIATED 5 /* Speed not established */ + +#define FOURBYTES 4 + + +static char *lpfc_release_version = LPFC_DRIVER_VERSION; + +/* + * lpfc_ct_unsol_event + */ +void +lpfc_ct_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocbq) +{ + + struct lpfc_iocbq *next_piocbq; + struct lpfc_dmabuf *pmbuf = NULL; + struct lpfc_dmabuf *matp, *next_matp; + uint32_t ctx = 0, count = 0; + IOCB_t *icmd = &piocbq->iocb; + int i, status, go_exit = 0; + struct list_head head; + + if (icmd->ulpStatus) + return; + + list_add_tail(&head, &piocbq->list); + list_for_each_entry_safe(piocbq, next_piocbq, &head, list) { + icmd = &piocbq->iocb; + if (ctx == 0) + ctx = (uint32_t) (icmd->ulpContext); + if (icmd->ulpStatus) { + if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((icmd->un.ulpWord[4] & 0xff) + == IOERR_RCV_BUFFER_WAITING)) { + phba->fc_stat.NoRcvBuf++; + lpfc_post_buffer(phba, pring, 0, 1); + } + go_exit = 1; + goto ct_unsol_event_exit_piocbq; + } + + if (icmd->ulpBdeCount == 0) + continue; + + for (i = 0; i < icmd->ulpBdeCount; i++) { + matp = lpfc_sli_ringpostbuf_get(phba, pring, + getPaddr(icmd->un. + cont64[i]. + addrHigh, + icmd->un. + cont64[i]. + addrLow)); + if (!matp) { + /* Insert lpfc log message here */ + go_exit = 1; + goto ct_unsol_event_exit_piocbq; + } + + /* Typically for Unsolicited CT requests */ + if (!pmbuf) { + pmbuf = matp; + INIT_LIST_HEAD(&pmbuf->list); + } else + list_add_tail(&matp->list, &pmbuf->list); + + count += icmd->un.cont64[i].tus.f.bdeSize; + } + + lpfc_post_buffer(phba, pring, i, 1); + icmd->ulpBdeCount = 0; + } +ct_unsol_event_exit_piocbq: + list_del(&head); + /* + * if not early-exiting and there is pmbuf, + * then do FC_REG_CT_EVENT for libdfc + */ + if (!go_exit && pmbuf) { + status = lpfc_put_event(phba, FC_REG_CT_EVENT, ctx, + (void *)pmbuf, count, 0); + if (status) + /* Need to free IOCB buffer ? */ + return; + } + if (pmbuf) { + list_for_each_entry_safe(matp, next_matp, &pmbuf->list, list) { + lpfc_mbuf_free(phba, matp->virt, matp->phys); + list_del(&matp->list); + kfree(matp); + } + lpfc_mbuf_free(phba, pmbuf->virt, pmbuf->phys); + kfree(pmbuf); + } + return; +} + +static void +lpfc_free_ct_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mlist) +{ + struct lpfc_dmabuf *mlast, *next_mlast; + + list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) { + lpfc_mbuf_free(phba, mlast->virt, mlast->phys); + list_del(&mlast->list); + kfree(mlast); + } + lpfc_mbuf_free(phba, mlist->virt, mlist->phys); + kfree(mlist); + return; +} + +static struct lpfc_dmabuf * +lpfc_alloc_ct_rsp(struct lpfc_hba * phba, int cmdcode, struct ulp_bde64 * bpl, + uint32_t size, int *entries) +{ + struct lpfc_dmabuf *mlist = NULL; + struct lpfc_dmabuf *mp; + int cnt, i = 0; + + /* We get chucks of FCELSSIZE */ + cnt = size > FCELSSIZE ? FCELSSIZE: size; + + while (size) { + /* Allocate buffer for rsp payload */ + mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) { + if (mlist) + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + INIT_LIST_HEAD(&mp->list); + + if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT)) + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + else + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + + if (!mp->virt) { + kfree(mp); + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + /* Queue it to a linked list */ + if (!mlist) + mlist = mp; + else + list_add_tail(&mp->list, &mlist->list); + + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + /* build buffer ptr list for IOCB */ + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->tus.f.bdeSize = (uint16_t) cnt; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + i++; + size -= cnt; + } + + *entries = i; + return mlist; +} + +static int +lpfc_gen_req(struct lpfc_hba *phba, struct lpfc_dmabuf *bmp, + struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry, + uint32_t tmo) +{ + + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + IOCB_t *icmd; + struct lpfc_iocbq *geniocb; + + /* Allocate buffer for command iocb */ + geniocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!geniocb) { + return 1; + } + memset(geniocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &geniocb->iocb; + + icmd->un.genreq64.bdl.ulpIoTag32 = 0; + icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); + icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); + icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64)); + + if (usr_flg) + geniocb->context3 = NULL; + else + geniocb->context3 = (uint8_t *) bmp; + + /* Save for completion so we can release these resources */ + geniocb->context1 = (uint8_t *) inp; + geniocb->context2 = (uint8_t *) outp; + + /* Fill in payload, bp points to frame payload */ + icmd->ulpCommand = CMD_GEN_REQUEST64_CR; + + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + + /* Fill in rest of iocb */ + icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); + icmd->un.genreq64.w5.hcsw.Dfctl = 0; + icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; + icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + + if (!tmo) + tmo = (2 * phba->fc_ratov) + 1; + icmd->ulpTimeout = tmo; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + icmd->ulpContext = ndlp->nlp_rpi; + + /* Issue GEN REQ IOCB for NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0119 Issue GEN REQ IOCB for NPORT x%x " + "Data: x%x x%x\n", phba->brd_no, icmd->un.ulpWord[5], + icmd->ulpIoTag, phba->hba_state); + geniocb->iocb_cmpl = cmpl; + geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT; + if (lpfc_sli_issue_iocb(phba, pring, geniocb, 0) == IOCB_ERROR) { + mempool_free( geniocb, phba->iocb_mem_pool); + return 1; + } + + return 0; +} + +static int +lpfc_ct_cmd(struct lpfc_hba *phba, struct lpfc_dmabuf *inmp, + struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + uint32_t rsp_size) +{ + struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt; + struct lpfc_dmabuf *outmp; + int cnt = 0, status; + int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)-> + CommandResponse.bits.CmdRsp; + + bpl++; /* Skip past ct request */ + + /* Put buffer(s) for ct rsp in bpl */ + outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt); + if (!outmp) + return -ENOMEM; + + status = lpfc_gen_req(phba, bmp, inmp, outmp, cmpl, ndlp, 0, + cnt+1, 0); + if (status) { + lpfc_free_ct_rsp(phba, outmp); + return -ENOMEM; + } + return 0; +} + +static int +lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size) +{ + struct lpfc_sli_ct_request *Response = + (struct lpfc_sli_ct_request *) mp->virt; + struct lpfc_nodelist *ndlp = NULL; + struct lpfc_dmabuf *mlast, *next_mp; + uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType; + uint32_t Did; + uint32_t CTentry; + int Cnt; + struct list_head head; + + lpfc_set_disctmo(phba); + + Cnt = Size > FCELSSIZE ? FCELSSIZE : Size; + + list_add_tail(&head, &mp->list); + list_for_each_entry_safe(mp, next_mp, &head, list) { + mlast = mp; + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + Size -= Cnt; + + if (!ctptr) + ctptr = (uint32_t *) mlast->virt; + else + Cnt -= 16; /* subtract length of CT header */ + + /* Loop through entire NameServer list of DIDs */ + while (Cnt) { + + /* Get next DID from NameServer List */ + CTentry = *ctptr++; + Did = ((be32_to_cpu(CTentry)) & Mask_DID); + + ndlp = NULL; + if (Did != phba->fc_myDID) { + /* Check for rscn processing or not */ + ndlp = lpfc_setup_disc_node(phba, Did); + } + /* Mark all node table entries that are in the + Nameserver */ + if (ndlp) { + /* NameServer Rsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0238 Process x%x NameServer" + " Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, ndlp->nlp_flag, + phba->fc_flag, + phba->fc_rscn_id_cnt); + } else { + /* NameServer Rsp */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0239 Skip x%x NameServer " + "Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, Size, phba->fc_flag, + phba->fc_rscn_id_cnt); + } + + if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY))) + goto nsout1; + Cnt -= sizeof (uint32_t); + } + ctptr = NULL; + + } + +nsout1: + list_del(&head); + + /* Here we are finished in the case RSCN */ + if (phba->hba_state == LPFC_HBA_READY) { + lpfc_els_flush_rscn(phba); + phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */ + } + return 0; +} + + + + +static void +lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + struct lpfc_nodelist *ndlp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) || + (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) { + goto out; + } + + /* Check for retry */ + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + phba->fc_ns_retry++; + /* CT command is being retried */ + ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID); + if (ndlp) { + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + } + } else { + /* Good status, continue checking */ + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) { + lpfc_ns_rsp(phba, outp, + (uint32_t) (irsp->un.genreq64.bdl.bdeSize)); + } else if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0240 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } else { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0241 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } + } + /* Link up / RSCN discovery */ + lpfc_disc_start(phba); +out: + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + IOCB_t *irsp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + irsp = &rspiocb->iocb; + + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + + /* RFT request completes status CmdRsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0209 RFT request completes ulpStatus x%x " + "CmdRsp x%x\n", phba->brd_no, irsp->ulpStatus, + CTrsp->CommandResponse.bits.CmdRsp); + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +static void +lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +void +lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp) +{ + uint8_t buf[16]; + char fwrev[16]; + + lpfc_decode_firmware_rev(phba, fwrev, 0); + lpfc_get_hba_model_desc(phba, buf, NULL); + sprintf(symbp, "Emulex %s FV%s DV%s", buf, fwrev, lpfc_release_version); +} + +/* + * lpfc_ns_cmd + * Description: + * Issue Cmd to NameServer + * SLI_CTNS_GID_FT + * LI_CTNS_RFT_ID + */ +int +lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *) = NULL; + uint32_t rsp_size = 1024; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto ns_cmd_exit; + + INIT_LIST_HEAD(&mp->list); + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + if (!mp->virt) + goto ns_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto ns_cmd_free_mpvirt; + + INIT_LIST_HEAD(&bmp->list); + bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(bmp->phys)); + if (!bmp->virt) + goto ns_cmd_free_bmp; + + /* NameServer Req */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0236 NameServer Req Data: x%x x%x x%x\n", + phba->brd_no, cmdcode, phba->fc_flag, + phba->fc_rscn_id_cnt); + + bpl = (struct ulp_bde64 *) bmp->virt; + memset(bpl, 0, sizeof(struct ulp_bde64)); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + if (cmdcode == SLI_CTNS_GID_FT) + bpl->tus.f.bdeSize = GID_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RFT_ID) + bpl->tus.f.bdeSize = RFT_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RNN_ID) + bpl->tus.f.bdeSize = RNN_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RSNN_NN) + bpl->tus.f.bdeSize = RSNN_REQUEST_SZ; + else + bpl->tus.f.bdeSize = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + CtReq->FsType = SLI_CT_DIRECTORY_SERVICE; + CtReq->FsSubType = SLI_CT_DIRECTORY_NAME_SERVER; + CtReq->CommandResponse.bits.Size = 0; + switch (cmdcode) { + case SLI_CTNS_GID_FT: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_GID_FT); + CtReq->un.gid.Fc4Type = SLI_CTPT_FCP; + if (phba->hba_state < LPFC_HBA_READY) + phba->hba_state = LPFC_NS_QRY; + lpfc_set_disctmo(phba); + cmpl = lpfc_cmpl_ct_cmd_gid_ft; + rsp_size = FC_MAX_NS_RSP; + break; + + case SLI_CTNS_RFT_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RFT_ID); + CtReq->un.rft.PortId = be32_to_cpu(phba->fc_myDID); + CtReq->un.rft.fcpReg = 1; + cmpl = lpfc_cmpl_ct_cmd_rft_id; + break; + + case SLI_CTNS_RNN_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RNN_ID); + CtReq->un.rnn.PortId = be32_to_cpu(phba->fc_myDID); + memcpy(CtReq->un.rnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + cmpl = lpfc_cmpl_ct_cmd_rnn_id; + break; + + case SLI_CTNS_RSNN_NN: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RSNN_NN); + memcpy(CtReq->un.rsnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + lpfc_get_hba_sym_node_name(phba, CtReq->un.rsnn.symbname); + CtReq->un.rsnn.len = strlen(CtReq->un.rsnn.symbname); + cmpl = lpfc_cmpl_ct_cmd_rsnn_nn; + break; + } + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, rsp_size)) + /* On success, The cmpl function will free the buffers */ + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +ns_cmd_free_bmp: + kfree(bmp); +ns_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +ns_cmd_free_mp: + kfree(mp); +ns_cmd_exit: + return 1; +} + +static void +lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *bmp = cmdiocb->context3; + struct lpfc_dmabuf *inp = cmdiocb->context1; + struct lpfc_dmabuf *outp = cmdiocb->context2; + struct lpfc_sli_ct_request *CTrsp = outp->virt; + struct lpfc_sli_ct_request *CTcmd = inp->virt; + struct lpfc_nodelist *ndlp; + uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp; + uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp; + + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* FDMI rsp failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0220 FDMI rsp failed Data: x%x\n", + phba->brd_no, + be16_to_cpu(fdmi_cmd)); + } + + switch (be16_to_cpu(fdmi_cmd)) { + case SLI_MGMT_RHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RPA); + break; + + case SLI_MGMT_RPA: + break; + + case SLI_MGMT_DHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DPRT); + break; + + case SLI_MGMT_DPRT: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RHBA); + break; + } + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free(cmdiocb, phba->iocb_mem_pool); + return; +} +int +lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + uint32_t size; + REG_HBA *rh; + PORT_ENTRY *pe; + REG_PORT_ATTRIBUTE *pab; + ATTRIBUTE_BLOCK *ab; + ATTRIBUTE_ENTRY *ae; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto fdmi_cmd_exit; + + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + if (!mp->virt) + goto fdmi_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto fdmi_cmd_free_mpvirt; + + bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys)); + if (!bmp->virt) + goto fdmi_cmd_free_bmp; + + INIT_LIST_HEAD(&mp->list); + INIT_LIST_HEAD(&bmp->list); + + /* FDMI request */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0218 FDMI Request Data: x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, phba->hba_state, cmdcode); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + + memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + + CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE; + CtReq->FsSubType = SLI_CT_FDMI_Subtypes; + size = 0; + + switch (cmdcode) { + case SLI_MGMT_RHBA: + { + lpfc_vpd_t *vp = &phba->vpd; + uint32_t i, j, incr; + int len; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RHBA); + CtReq->CommandResponse.bits.Size = 0; + rh = (REG_HBA *) & CtReq->un.PortID; + memcpy(&rh->hi.PortName, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* One entry (port) per adapter */ + rh->rpl.EntryCnt = be32_to_cpu(1); + memcpy(&rh->rpl.pe, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + + /* point to the HBA attribute block */ + size = 2 * sizeof (struct lpfc_name) + FOURBYTES; + ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size); + ab->EntryCnt = 0; + + /* Point to the beginning of the first HBA attribute + entry */ + /* #1 HBA attribute entry */ + size += FOURBYTES; + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + + sizeof (struct lpfc_name)); + memcpy(&ae->un.NodeName, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + ab->EntryCnt++; + size += FOURBYTES + sizeof (struct lpfc_name); + + /* #2 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER); + strcpy(ae->un.Manufacturer, "Emulex Corporation"); + len = strlen(ae->un.Manufacturer); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #3 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER); + strcpy(ae->un.SerialNumber, phba->SerialNumber); + len = strlen(ae->un.SerialNumber); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #4 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL); + lpfc_get_hba_model_desc(phba, ae->un.Model, NULL); + len = strlen(ae->un.Model); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #5 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION); + lpfc_get_hba_model_desc(phba, NULL, + ae->un.ModelDescription); + len = strlen(ae->un.ModelDescription); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #6 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8); + /* Convert JEDEC ID to ascii for hardware version */ + incr = vp->rev.biuRev; + for (i = 0; i < 8; i++) { + j = (incr & 0xf); + if (j <= 9) + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x30 + + (uint8_t) j); + else + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x61 + + (uint8_t) (j - 10)); + incr = (incr >> 4); + } + ab->EntryCnt++; + size += FOURBYTES + 8; + + /* #7 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION); + strcpy(ae->un.DriverVersion, lpfc_release_version); + len = strlen(ae->un.DriverVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #8 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION); + strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion); + len = strlen(ae->un.OptionROMVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #9 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION); + lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion, + 1); + len = strlen(ae->un.FirmwareVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #10 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION); + sprintf(ae->un.OsNameVersion, "%s %s %s", + system_utsname.sysname, system_utsname.release, + system_utsname.version); + len = strlen(ae->un.OsNameVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #11 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + ae->un.MaxCTPayloadLen = (65 * 4096); + ab->EntryCnt++; + size += FOURBYTES + 4; + + ab->EntryCnt = be32_to_cpu(ab->EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_RPA: + { + lpfc_vpd_t *vp; + struct serv_parm *hsp; + int len; + + vp = &phba->vpd; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RPA); + CtReq->CommandResponse.bits.Size = 0; + pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID; + size = sizeof (struct lpfc_name) + FOURBYTES; + memcpy((uint8_t *) & pab->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + pab->ab.EntryCnt = 0; + + /* #1 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32); + ae->un.SupportFC4Types[2] = 1; + ae->un.SupportFC4Types[7] = 1; + pab->ab.EntryCnt++; + size += FOURBYTES + 32; + + /* #2 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + if (FC_JEDEC_ID(vp->rev.biuRev) == VIPER_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_10GBIT; + else if (FC_JEDEC_ID(vp->rev.biuRev) == HELIOS_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_4GBIT; + else if ((FC_JEDEC_ID(vp->rev.biuRev) == + CENTAUR_2G_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + PEGASUS_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + THOR_JEDEC_ID)) + ae->un.SupportSpeed = HBA_PORTSPEED_2GBIT; + else + ae->un.SupportSpeed = HBA_PORTSPEED_1GBIT; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #3 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + switch(phba->fc_linkspeed) { + case LA_1GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_1GBIT; + break; + case LA_2GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_2GBIT; + break; + case LA_4GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_4GBIT; + break; + default: + ae->un.PortSpeed = + HBA_PORTSPEED_UNKNOWN; + break; + } + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #4 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + hsp = (struct serv_parm *) & phba->fc_sparam; + ae->un.MaxFrameSize = + (((uint32_t) hsp->cmn. + bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn. + bbRcvSizeLsb; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #5 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME); + strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME); + len = strlen((char *)ae->un.OsDeviceName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + + if (phba->cfg_fdmi_on == 2) { + /* #6 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + + size); + ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME); + sprintf(ae->un.HostName, "%s", + system_utsname.nodename); + len = strlen(ae->un.HostName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = + be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + } + + pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_DHBA: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + + case SLI_MGMT_DPRT: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + } + + bpl = (struct ulp_bde64 *) bmp->virt; + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = size; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + cmpl = lpfc_cmpl_ct_cmd_fdmi; + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP)) + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +fdmi_cmd_free_bmp: + kfree(bmp); +fdmi_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +fdmi_cmd_free_mp: + kfree(mp); +fdmi_cmd_exit: + /* Issue FDMI request failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0244 Issue FDMI request failed Data: x%x\n", + phba->brd_no, + cmdcode); + return 1; +} + + +void +lpfc_fdmi_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)ptr; + struct lpfc_nodelist *ndlp; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (ndlp) { + if (system_utsname.nodename[0] != '\0') { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + + +void +lpfc_decode_firmware_rev(struct lpfc_hba * phba, char *fwrevision, int flag) +{ + struct lpfc_sli *psli = &phba->sli; + lpfc_vpd_t *vp = &phba->vpd; + uint32_t b1, b2, b3, b4, i, rev; + char c; + uint32_t *ptr, str[4]; + uint8_t *fwname; + + if (vp->rev.rBit) { + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + rev = vp->rev.sli2FwRev; + else + rev = vp->rev.sli1FwRev; + + b1 = (rev & 0x0000f000) >> 12; + b2 = (rev & 0x00000f00) >> 8; + b3 = (rev & 0x000000c0) >> 6; + b4 = (rev & 0x00000030) >> 4; + + switch (b4) { + case 0: + c = 'N'; + break; + case 1: + c = 'A'; + break; + case 2: + c = 'B'; + break; + default: + c = 0; + break; + } + b4 = (rev & 0x0000000f); + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + fwname = vp->rev.sli2FwName; + else + fwname = vp->rev.sli1FwName; + + for (i = 0; i < 16; i++) + if(fwname[i] == 0x20) + fwname[i] = 0; + + ptr = (uint32_t*)fwname; + + for (i = 0; i < 3; i++) + str[i] = be32_to_cpu(*ptr++); + + if (c == 0) { + if (flag) + sprintf(fwrevision, "%d.%d%d (%s)", + b1, b2, b3, (char *)str); + else + sprintf(fwrevision, "%d.%d%d", b1, + b2, b3); + } else { + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d (%s)", + b1, b2, b3, c, + b4, (char *)str); + else + sprintf(fwrevision, "%d.%d%d%c%d", + b1, b2, b3, c, b4); + } + } else { + rev = vp->rev.smFwRev; + + b1 = (rev & 0xff000000) >> 24; + b2 = (rev & 0x00f00000) >> 20; + b3 = (rev & 0x000f0000) >> 16; + c = (rev & 0x0000ff00) >> 8; + b4 = (rev & 0x000000ff); + + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + else + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + } + return; +} + +void +lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) +{ + lpfc_vpd_t *vp; + uint32_t id; + char str[16]; + + vp = &phba->vpd; + pci_read_config_dword(phba->pcidev, PCI_VENDOR_ID, &id); + + switch ((id >> 16) & 0xffff) { + case PCI_DEVICE_ID_SUPERFLY: + if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) + strcpy(str, "LP7000 1"); + else + strcpy(str, "LP7000E 1"); + break; + case PCI_DEVICE_ID_DRAGONFLY: + strcpy(str, "LP8000 1"); + break; + case PCI_DEVICE_ID_CENTAUR: + if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) + strcpy(str, "LP9002 2"); + else + strcpy(str, "LP9000 1"); + break; + case PCI_DEVICE_ID_RFLY: + strcpy(str, "LP952 2"); + break; + case PCI_DEVICE_ID_PEGASUS: + strcpy(str, "LP9802 2"); + break; + case PCI_DEVICE_ID_THOR: + strcpy(str, "LP10000 2"); + break; + case PCI_DEVICE_ID_VIPER: + strcpy(str, "LPX1000 10"); + break; + case PCI_DEVICE_ID_PFLY: + strcpy(str, "LP982 2"); + break; + case PCI_DEVICE_ID_TFLY: + strcpy(str, "LP1050 2"); + break; + case PCI_DEVICE_ID_HELIOS: + strcpy(str, "LP11000 4"); + break; + case PCI_DEVICE_ID_BMID: + strcpy(str, "LP1150 4"); + break; + case PCI_DEVICE_ID_BSMB: + strcpy(str, "LP111 4"); + break; + case PCI_DEVICE_ID_ZEPHYR: + strcpy(str, "LP11000e 4"); + break; + case PCI_DEVICE_ID_ZMID: + strcpy(str, "LP1150e 4"); + break; + case PCI_DEVICE_ID_ZSMB: + strcpy(str, "LP111e 4"); + break; + case PCI_DEVICE_ID_LP101: + strcpy(str, "LP101 2"); + break; + } + if (mdp) + sscanf(str, "%s", mdp); + if (descp) + sprintf(descp, "Emulex LightPulse %s Gigabit PCI Fibre " + "Channel Adapter", str); +} + diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h new file mode 100644 index 000000000..0b7ae7f55 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -0,0 +1,270 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_disc.h 1.45 2004/11/10 11:40:40EST sf_support Exp $ + */ + +#ifndef _H_LPFC_DISC +#define _H_LPFC_DISC + +#include "lpfc_hw.h" + +struct lpfc_target; + +#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */ +#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */ +#define FC_MAXLOOP 126 /* max devices supported on a fc loop */ +#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */ + +/* Defines for failMask bitmask + * These are reasons that the device is not currently available + * for I/O to be sent. + */ +#define LPFC_DEV_LINK_DOWN 0x1 /* Link is down */ +#define LPFC_DEV_DISAPPEARED 0x2 /* Device disappeared from mapped + list */ +#define LPFC_DEV_DISCOVERY_INP 0x4 /* Device to go through discovery */ +#define LPFC_DEV_DISCONNECTED 0x8 /* noactive connection to remote dev */ + +/* These defines are used for set failMask routines */ +#define LPFC_SET_BITMASK 1 +#define LPFC_CLR_BITMASK 2 + +/* Provide an enumeration for the Types of addresses a FARP can resolve. */ +typedef enum lpfc_farp_addr_type { + LPFC_FARP_BY_IEEE, + LPFC_FARP_BY_WWPN, + LPFC_FARP_BY_WWNN, +} LPFC_FARP_ADDR_TYPE; + +/* This is the protocol dependent definition for a Node List Entry. + * This is used by Fibre Channel protocol to support FCP. + */ + +struct lpfc_bindlist { + struct list_head nlp_listp; + struct lpfc_target *nlp_Target; /* ptr to the tgt structure */ + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint16_t nlp_bind_type; + uint16_t nlp_sid; /* scsi id */ + uint32_t nlp_DID; /* FibreChannel D_ID of entry */ +}; + +struct lpfc_nodelist { + struct list_head nlp_listp; + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint32_t nlp_failMask; /* failure mask for device */ + uint32_t nlp_flag; /* entry flags */ + uint32_t nlp_DID; /* FC D_ID of entry */ + uint32_t nlp_last_elscmd; /* Last ELS cmd sent */ + uint16_t nlp_type; +#define NLP_FC_NODE 0x1 /* entry is an FC node */ +#define NLP_FABRIC 0x4 /* entry rep a Fabric entity */ +#define NLP_FCP_TARGET 0x8 /* entry is an FCP target */ + + uint16_t nlp_rpi; + uint16_t nlp_state; /* state transition indicator */ + uint16_t nlp_xri; /* output exchange id for RPI */ + uint16_t nlp_sid; /* scsi id */ + uint8_t nlp_retry; /* used for ELS retries */ + uint8_t nlp_disc_refcnt; /* used for DSM */ + uint8_t nlp_fcp_info; /* class info, bits 0-3 */ +#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ + + struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ + struct timer_list nlp_tmofunc; /* Used for nodev tmo */ + struct lpfc_target *nlp_Target; /* Pointer to the target + structure */ + + struct lpfc_bindlist *nlp_listp_bind; /* Linked list bounded remote + ports */ + struct lpfc_nodelist *nlp_rpi_hash_next; + struct lpfc_hba *nlp_phba; +}; + +/*++ + * lpfc_node_farp_list: + * This data structure defines the attributes associated with + * an outstanding FARP REQ to a remote node. + * + * listentry - head of this list of pending farp requests. + * rnode_addr - The address of the remote node. Either the IEEE, WWPN, or + * WWNN. Used in the FARP request. + * + --*/ +struct lpfc_node_farp_pend { + struct list_head listentry; + struct lpfc_name rnode_addr; +}; + +/* Defines for nlp_flag (uint32) */ +#define NLP_NO_LIST 0x0 /* Indicates immediately free node */ +#define NLP_UNUSED_LIST 0x1 /* Flg to indicate node will be freed */ +#define NLP_PLOGI_LIST 0x2 /* Flg to indicate sent PLOGI */ +#define NLP_ADISC_LIST 0x3 /* Flg to indicate sent ADISC */ +#define NLP_REGLOGIN_LIST 0x4 /* Flg to indicate sent REG_LOGIN */ +#define NLP_PRLI_LIST 0x5 /* Flg to indicate sent PRLI */ +#define NLP_UNMAPPED_LIST 0x6 /* Node is now unmapped */ +#define NLP_MAPPED_LIST 0x7 /* Node is now mapped */ +#define NLP_NPR_LIST 0x8 /* Node is in NPort Recovery state */ +#define NLP_JUST_DQ 0x9 /* just deque ndlp in lpfc_nlp_list */ +#define NLP_LIST_MASK 0xf /* mask to see what list node is on */ +#define NLP_PLOGI_SND 0x20 /* sent PLOGI request for this entry */ +#define NLP_PRLI_SND 0x40 /* sent PRLI request for this entry */ +#define NLP_ADISC_SND 0x80 /* sent ADISC request for this entry */ +#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */ +#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */ +#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */ +#define NLP_AUTOMAP 0x800 /* Entry was automap'ed */ +#define NLP_SEED_WWPN 0x1000 /* Entry scsi id is seeded for WWPN */ +#define NLP_SEED_WWNN 0x2000 /* Entry scsi id is seeded for WWNN */ +#define NLP_SEED_DID 0x4000 /* Entry scsi id is seeded for DID */ +#define NLP_SEED_MASK 0x807000 /* mask for seeded flags */ +#define NLP_NS_NODE 0x8000 /* Authenticated entry by NameServer */ +#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */ +#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */ +#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */ +#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */ +#define NLP_LOGO_ACC 0x100000 /* Process LOGO after ACC completes */ +#define NLP_TGT_NO_SCSIID 0x200000 /* good PRLI but no binding for scsid */ +#define NLP_SEED_ALPA 0x800000 /* SCSI id is derived from alpa array */ +#define NLP_ACC_REGLOGIN 0x1000000 /* Issue Reg Login after successful + ACC */ +#define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from + NPR list */ +#define NLP_DELAY_REMOVE 0x4000000 /* Defer removal till end of DSM */ + +/* Defines for list searchs */ +#define NLP_SEARCH_MAPPED 0x1 /* search mapped */ +#define NLP_SEARCH_UNMAPPED 0x2 /* search unmapped */ +#define NLP_SEARCH_PLOGI 0x4 /* search plogi */ +#define NLP_SEARCH_ADISC 0x8 /* search adisc */ +#define NLP_SEARCH_REGLOGIN 0x10 /* search reglogin */ +#define NLP_SEARCH_PRLI 0x20 /* search prli */ +#define NLP_SEARCH_NPR 0x40 /* search npr */ +#define NLP_SEARCH_UNUSED 0x80 /* search mapped */ +#define NLP_SEARCH_ALL 0xff /* search all lists */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used + * when Link Up discovery or Registered State Change Notification (RSCN) + * processing is needed. Each list holds the nodes that require a PLOGI or + * ADISC Extended Link Service (ELS) request. These lists keep track of the + * nodes affected by an RSCN, or a Link Up (Typically, all nodes are effected + * by Link Up) event. The unmapped_list contains all nodes that have + * successfully logged into at the Fibre Channel level. The + * mapped_list will contain all nodes that are mapped FCP targets. + * + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ + +/* Defines for nlp_state */ +#define NLP_STE_UNUSED_NODE 0x0 /* node is just allocated */ +#define NLP_STE_PLOGI_ISSUE 0x1 /* PLOGI was sent to NL_PORT */ +#define NLP_STE_ADISC_ISSUE 0x2 /* ADISC was sent to NL_PORT */ +#define NLP_STE_REG_LOGIN_ISSUE 0x3 /* REG_LOGIN was issued for NL_PORT */ +#define NLP_STE_PRLI_ISSUE 0x4 /* PRLI was sent to NL_PORT */ +#define NLP_STE_UNMAPPED_NODE 0x5 /* PRLI completed from NL_PORT */ +#define NLP_STE_MAPPED_NODE 0x6 /* Identified as a FCP Target */ +#define NLP_STE_NPR_NODE 0x7 /* NPort disappeared */ +#define NLP_STE_MAX_STATE 0x8 +#define NLP_STE_FREED_NODE 0xff /* node entry was freed to MEM_NLP */ + +/* For UNUSED_NODE state, the node has just been allocated. + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to PRLI_COMPL. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added (DEVICE_ADD) or removed (DEVICE_RM) to / + * from the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * identically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +/* Defines for Node List Entry Events that could happen */ +#define NLP_EVT_RCV_PLOGI 0x0 /* Rcv'd an ELS PLOGI command */ +#define NLP_EVT_RCV_PRLI 0x1 /* Rcv'd an ELS PRLI command */ +#define NLP_EVT_RCV_LOGO 0x2 /* Rcv'd an ELS LOGO command */ +#define NLP_EVT_RCV_ADISC 0x3 /* Rcv'd an ELS ADISC command */ +#define NLP_EVT_RCV_PDISC 0x4 /* Rcv'd an ELS PDISC command */ +#define NLP_EVT_RCV_PRLO 0x5 /* Rcv'd an ELS PRLO command */ +#define NLP_EVT_CMPL_PLOGI 0x6 /* Sent an ELS PLOGI command */ +#define NLP_EVT_CMPL_PRLI 0x7 /* Sent an ELS PRLI command */ +#define NLP_EVT_CMPL_LOGO 0x8 /* Sent an ELS LOGO command */ +#define NLP_EVT_CMPL_ADISC 0x9 /* Sent an ELS ADISC command */ +#define NLP_EVT_CMPL_REG_LOGIN 0xa /* REG_LOGIN mbox cmd completed */ +#define NLP_EVT_DEVICE_RM 0xb /* Device not found in NS / ALPAmap */ +#define NLP_EVT_DEVICE_RECOVERY 0xc /* Device existence unknown */ +#define NLP_EVT_MAX_EVENT 0xd + +/* structure used to queue event to the discovery tasklet */ +struct lpfc_disc_evt { + struct list_head evt_listp; + void * evt_arg1; + void * evt_arg2; + uint32_t evt; +}; +typedef struct lpfc_disc_evt LPFC_DISC_EVT_t; + +#define LPFC_EVT_MBOX 0x1 +#define LPFC_EVT_SOL_IOCB 0x2 +#define LPFC_EVT_UNSOL_IOCB 0x3 +#define LPFC_EVT_NODEV_TMO 0x4 +#define LPFC_EVT_SCAN 0x5 + +/* Definitions for Binding Entry Type for lpfc_parse_binding_entry() */ +#define LPFC_BIND_WW_NN_PN 0 +#define LPFC_BIND_DID 1 + +#endif /* _H_LPFC_DISC */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c new file mode 100644 index 000000000..427f68b1d --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -0,0 +1,3235 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_els.c 1.152 2004/11/18 18:27:53EST sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + + +static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); +static int lpfc_max_els_tries = 3; + +static int +lpfc_els_chk_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + uint32_t ha_copy; + + psli = &phba->sli; + + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_LINK_DOWN)) { + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + if (ha_copy & HA_LATT) { /* Link Attention interrupt */ + + /* Pending Link Event during Discovery */ + lpfc_printf_log(phba, KERN_WARNING, LOG_DISCOVERY, + "%d:0237 Pending Link Event during " + "Discovery: State x%x\n", + phba->brd_no, phba->hba_state); + + /* CLEAR_LA should re-enable link attention events and + * we should then imediately take a LATT event. The + * LATT processing should call lpfc_linkdown() which + * will cleanup any left over in-progress discovery + * events. + */ + phba->fc_flag |= FC_ABORT_DISCOVERY; + + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + phba->hba_state = + LPFC_HBA_ERROR; + } + } + } + return (1); + } + } + + return (0); +} + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *elsiocb; + struct lpfc_dmabuf *pcmd, *prsp, *pbuflist; + struct ulp_bde64 *bpl; + IOCB_t *icmd; + uint32_t tag; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + if (phba->hba_state < LPFC_LINK_UP) + return NULL; + + + /* Allocate buffer for command iocb */ + elsiocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!elsiocb) + return NULL; + + memset(elsiocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &elsiocb->iocb; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((pcmd->virt = lpfc_mbuf_alloc(phba, + MEM_PRI, &(pcmd->phys))) == 0)) { + if (pcmd) + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + + INIT_LIST_HEAD(&pcmd->list); + + /* Allocate buffer for response payload */ + if (expectRsp) { + prsp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (prsp) + prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &prsp->phys); + if (prsp == 0 || prsp->virt == 0) { + if (prsp) + kfree(prsp); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + INIT_LIST_HEAD(&prsp->list); + } else { + prsp = NULL; + } + + /* Allocate buffer for Buffer ptr list */ + pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (pbuflist) + pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &pbuflist->phys); + if (pbuflist == 0 || pbuflist->virt == 0) { + mempool_free( elsiocb, phba->iocb_mem_pool); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + lpfc_mbuf_free(phba, prsp->virt, prsp->phys); + kfree(pcmd); + kfree(prsp); + if (pbuflist) + kfree(pbuflist); + return NULL; + } + + INIT_LIST_HEAD(&pbuflist->list); + + icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys); + icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys); + icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + if (expectRsp) { + icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); + icmd->un.elsreq64.remoteID = ndlp->nlp_DID; /* DID */ + icmd->ulpCommand = CMD_ELS_REQUEST64_CR; + } else { + icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64); + icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX; + } + + /* NOTE: we don't use ulpIoTag0 because it is a t2 structure */ + tag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpIoTag = (uint16_t)(tag & 0xffff); + icmd->un.elsreq64.bdl.ulpIoTag32 = tag; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + + bpl = (struct ulp_bde64 *) pbuflist->virt; + bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys)); + bpl->tus.f.bdeSize = cmdSize; + bpl->tus.f.bdeFlags = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + if (expectRsp) { + bpl++; + bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys)); + bpl->tus.f.bdeSize = FCELSSIZE; + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + } + + /* Save for completion so we can release these resources */ + elsiocb->context1 = (uint8_t *) ndlp; + elsiocb->context2 = (uint8_t *) pcmd; + elsiocb->context3 = (uint8_t *) pbuflist; + elsiocb->retry = retry; + elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT; + + if (prsp) { + list_add(&prsp->list, &pcmd->list); + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + pci_dma_sync_single_for_device(phba->pcidev, pbuflist->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + if (expectRsp) { + /* Xmit ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0116 Xmit ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state); + } else { + /* Xmit ELS response to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0117 Xmit ELS response x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, cmdSize); + } + + return (elsiocb); +} + +static void +lpfc_cmpl_els_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd, *prsp; + struct serv_parm *sp; + uint32_t *lp; + LPFC_MBOXQ_t *mbox; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int rc; + + psli = &phba->sli; + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_nlp_remove(phba, ndlp); + goto out; + } + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* FLOGI failed, so there is no fabric */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + + /* If private loop, then allow max outstandting els to be + * LPFC_MAX_DISC_THREADS (32). Scanning in the case of no + * alpa map would take too long otherwise. + */ + if (phba->alpa_map[0] == 0) { + phba->cfg_discovery_threads = + LPFC_MAX_DISC_THREADS; + } + + /* FLOGI failure */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0100 FLOGI failure Data: x%x x%x\n", + phba->brd_no, + irsp->ulpStatus, irsp->un.ulpWord[4]); + } else { + /* The FLogI succeeded. Sync the data for the CPU before + * accessing it. + */ + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + /* The HBA populated the response buffer. Flush cpu cache to + * before the driver touches this memory. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + /* FLOGI completes successfully */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0101 FLOGI completes sucessfully " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->un.ulpWord[4], sp->cmn.e_d_tov, + sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution); + + if (phba->hba_state == LPFC_FLOGI) { + /* If Common Service Parameters indicate Nport + * we are point to point, if Fport we are Fabric. + */ + if (sp->cmn.fPort) { + phba->fc_flag |= FC_FABRIC; + if (sp->cmn.edtovResolution) { + /* E_D_TOV ticks are in nanoseconds */ + phba->fc_edtov = + (be32_to_cpu(sp->cmn.e_d_tov) + + 999999) / 1000000; + } else { + /* E_D_TOV ticks are in milliseconds */ + phba->fc_edtov = + be32_to_cpu(sp->cmn.e_d_tov); + } + phba->fc_ratov = + (be32_to_cpu(sp->cmn.w2.r_a_tov) + + 999) / 1000; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + phba->fc_flag |= FC_PUBLIC_LOOP; + } else { + /* If we are a N-port connected to a + * Fabric, fixup sparam's so logins to + * devices on remote loops work. + */ + phba->fc_sparam.cmn.altBbCredit = 1; + } + + phba->fc_myDID = irsp->un.ulpWord[4] & Mask_DID; + + memcpy(&ndlp->nlp_portname, &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, &sp->nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_fabparam, sp, + sizeof (struct serv_parm)); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + phba->hba_state = LPFC_FABRIC_CFG_LINK; + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + if (lpfc_reg_login(phba, Fabric_DID, + (uint8_t *) sp, mbox, + 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fabric_reg_login; + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + } else { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + } else { + /* We FLOGIed into an NPort, initiate pt2pt + protocol */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof(struct lpfc_name)); + if (rc >= 0) { + /* This side will initiate the PLOGI */ + phba->fc_flag |= FC_PT2PT_PLOGI; + + /* N_Port ID cannot be 0, set our to + * LocalID the other side will be + * RemoteID. + */ + + /* not equal */ + if (rc) + phba->fc_myDID = PT2PT_LocalID; + + if ((mbox = + mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) + == 0) { + goto flogifail; + } + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + mempool_free( ndlp, phba->nlp_mem_pool); + + if ((ndlp = + lpfc_findnode_did(phba, + NLP_SEARCH_ALL, + PT2PT_RemoteID)) + == 0) { + /* Cannot find existing Fabric + ndlp, so allocate a new + one */ + if ((ndlp = + mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + lpfc_nlp_init(phba, ndlp, + PT2PT_RemoteID); + } + memcpy(&ndlp->nlp_portname, + &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, + &sp->nodeName, + sizeof (struct lpfc_name)); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + /* This side will wait for the PLOGI */ + mempool_free( ndlp, phba->nlp_mem_pool); + } + + phba->fc_flag |= FC_PT2PT; + + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + goto out; + } + } + +flogifail: + lpfc_nlp_remove(phba, ndlp); + + if((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) || + ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) && + (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) { + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static int +lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + uint32_t tmo; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_FLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For FLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_FLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + /* Setup CSPs accordingly for Fabric */ + sp->cmn.e_d_tov = 0; + sp->cmn.w2.r_a_tov = 0; + sp->cls1.classValid = 0; + sp->cls2.seqDelivery = 1; + sp->cls3.seqDelivery = 1; + if (sp->cmn.fcphLow < FC_PH3) + sp->cmn.fcphLow = FC_PH3; + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + tmo = phba->fc_ratov; + phba->fc_ratov = LPFC_DISC_FLOGI_TMO; + lpfc_set_disctmo(phba); + phba->fc_ratov = tmo; + + /* Flush the els buffer to main store for the HBA. This context always + * comes from the driver's dma pool and is always LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitFLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_abort_flogi(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_nodelist *ndlp; + IOCB_t *icmd; + struct list_head *curr, *next; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0201 Abort outstanding I/O on NPort x%x\n", + phba->brd_no, Fabric_DID); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* check the txcmplq */ + list_for_each_safe(curr, next, &pring->txcmplq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, list); + iocb = next_iocb; + /* Check to see if iocb matches the nport we are + looking for */ + icmd = &iocb->iocb; + if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) { + ndlp = (struct lpfc_nodelist *)(iocb->context1); + if(ndlp && (ndlp->nlp_DID == Fabric_DID)) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if ((icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32 + (phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + } + return (0); +} + +int +lpfc_initial_flogi(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + /* First look for Fabric ndlp on the unmapped list */ + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + Fabric_DID)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + return (0); + } + lpfc_nlp_init(phba, ndlp, Fabric_DID); + } + else { + phba->fc_unmap_cnt--; + list_del(&ndlp->nlp_listp); + ndlp->nlp_flag &= ~NLP_LIST_MASK; + } + if (lpfc_issue_els_flogi(phba, ndlp, 0)) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + return (1); +} + +static void +lpfc_more_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with PLOGIs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0232 Continue discovery with %d PLOGIs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_plogi_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more PLOGIs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + /* go thru NPR list and issue any remaining ELS PLOGIs */ + sentplogi = lpfc_els_disc_plogi(phba); + } + return; +} + +static void +lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int disc, rc, did, type; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + rc = 0; + + /* PLOGI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0102 PLOGI completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + /* ndlp could be freed in DSM, save these values now */ + type = ndlp->nlp_type; + did = ndlp->nlp_DID; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + goto out; + } + + /* PLOGI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + } else { + /* Good status, call state machine */ + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + + if(type & NLP_FABRIC) { + /* If we cannot login to Nameserver, kick off discovery now */ + if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) { + lpfc_disc_start(phba); + } + goto out; + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more PLOGIs to be sent */ + lpfc_more_plogi(phba); + } + + if (rc != NLP_STE_FREED_NODE) + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + if (phba->num_disc_nodes == 0) { + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_PLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + if (sp->cmn.fcphLow < FC_PH_4_3) + sp->cmn.fcphLow = FC_PH_4_3; + + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitPLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi; + ndlp->nlp_flag |= NLP_PLOGI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_PRLI_SND; + + /* PRLI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0103 PRLI completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + phba->fc_prli_sent--; + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* PRLI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PRLI); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + PRLI *npr; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (PRLI)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PRLI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PRLI request, remainder of payload is service parameters */ + memset(pcmd, 0, (sizeof (PRLI) + sizeof (uint32_t))); + *((uint32_t *) (pcmd)) = ELS_CMD_PRLI; + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + npr = (PRLI *) pcmd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (phba->vpd.rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + + /* For FCP support */ + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitPRLI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_prli; + ndlp->nlp_flag |= NLP_PRLI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PRLI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + phba->fc_prli_sent++; + return (0); +} + +static void +lpfc_more_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with ADISCs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0210 Continue discovery with %d ADISCs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_adisc_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more ADISCs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + lpfc_set_disctmo(phba); + + /* go thru NPR list and issue any remaining ELS ADISCs */ + sentadisc = lpfc_els_disc_adisc(phba); + } + return; +} + +static void +lpfc_rscn_disc(struct lpfc_hba * phba) +{ + /* RSCN discovery */ + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + lpfc_els_disc_plogi(phba); + return; + } + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } +} + +static void +lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + int disc; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_ADISC_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + + /* ADISC completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0104 ADISC completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + lpfc_set_disctmo(phba); + } + goto out; + } + /* ADISC failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more ADISCs to be sent */ + lpfc_more_adisc(phba); + + /* Check to see if we are done with ADISC authentication */ + if (phba->num_disc_nodes == 0) { + /* If we get here, there is nothing left to wait for */ + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_CLEAR_LA)) { + /* Link up discovery */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = + LPFC_HBA_READY; + } + } + } else { + lpfc_rscn_disc(phba); + } + } + } + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + ADISC *ap; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (ADISC)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_ADISC)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For ADISC request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_ADISC; + pcmd += sizeof (uint32_t); + + /* Fill in ADISC payload */ + ap = (ADISC *) pcmd; + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitADISC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc; + ndlp->nlp_flag |= NLP_ADISC_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_ADISC_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_LOGO_SND; + + /* LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0105 LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* LOGO failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_LOGO); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + lpfc_unreg_rpi(phba, ndlp); + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_LOGO)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_LOGO; + pcmd += sizeof (uint32_t); + + /* Fill in LOGO payload */ + *((uint32_t *) (pcmd)) = be32_to_cpu(phba->fc_myDID); + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_portname, sizeof (struct lpfc_name)); + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitLOGO++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo; + ndlp->nlp_flag |= NLP_LOGO_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_LOGO_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + + /* ELS cmd tag completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n", + phba->brd_no, + irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]); + + /* Check to see if link went down during discovery */ + lpfc_els_chk_latt(phba); + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (SCR)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_SCR)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_SCR; + pcmd += sizeof (uint32_t); + + /* For SCR, remainder of payload is SCR parameter page */ + memset(pcmd, 0, sizeof (SCR)); + ((SCR *) pcmd)->Function = SCR_FUNC_FULL; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitSCR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +static int +lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + FARP *fp; + uint8_t *pcmd; + uint32_t *lp; + uint16_t cmdsize; + struct lpfc_nodelist *ondlp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (FARP)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_RNID)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_FARPR; + pcmd += sizeof (uint32_t); + + /* Fill in FARPR payload */ + fp = (FARP *) (pcmd); + memset(fp, 0, sizeof (FARP)); + lp = (uint32_t *) pcmd; + *lp++ = be32_to_cpu(nportid); + *lp++ = be32_to_cpu(phba->fc_myDID); + fp->Rflags = 0; + fp->Mflags = (FARP_MATCH_PORT | FARP_MATCH_NODE); + + memcpy(&fp->RportName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&fp->RnodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + if ((ondlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, nportid))) { + memcpy(&fp->OportName, &ondlp->nlp_portname, + sizeof (struct lpfc_name)); + memcpy(&fp->OnodeName, &ondlp->nlp_nodename, + sizeof (struct lpfc_name)); + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitFARPR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +void +lpfc_els_retry_delay(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + uint32_t cmd; + uint32_t did; + uint8_t retry; + unsigned long iflag; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + spin_lock_irqsave(phba->host->host_lock, iflag); + did = (uint32_t) (ndlp->nlp_DID); + cmd = (uint32_t) (ndlp->nlp_last_elscmd); + + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + retry = ndlp->nlp_retry; + + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, retry); + break; + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, retry); + break; + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, retry); + break; + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, retry); + break; + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, retry); + break; + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +static int +lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd; + struct lpfc_nodelist *ndlp; + uint32_t *elscmd; + struct ls_rjt stat; + int retry, maxretry; + int delay; + uint32_t cmd; + + retry = 0; + delay = 0; + maxretry = lpfc_max_els_tries; + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + cmd = 0; + /* Note: context2 may be 0 for internal driver abort + * of delays ELS command. + */ + + if (pcmd && pcmd->virt) { + elscmd = (uint32_t *) (pcmd->virt); + cmd = *elscmd++; + } + + switch (irsp->ulpStatus) { + case IOSTAT_FCP_RSP_ERROR: + case IOSTAT_REMOTE_STOP: + break; + + case IOSTAT_LOCAL_REJECT: + switch ((irsp->un.ulpWord[4] & 0xff)) { + case IOERR_LOOP_OPEN_FAILURE: + if (cmd == ELS_CMD_PLOGI) { + if (cmdiocb->retry == 0) { + delay = 1; + } + } + retry = 1; + break; + + case IOERR_SEQUENCE_TIMEOUT: + retry = 1; + if ((cmd == ELS_CMD_FLOGI) + && (phba->fc_topology != TOPOLOGY_LOOP)) { + delay = 1; + maxretry = 48; + } + break; + + case IOERR_NO_RESOURCES: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + } + retry = 1; + break; + + case IOERR_INVALID_RPI: + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_RJT: + case IOSTAT_FABRIC_RJT: + if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) { + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_BSY: + case IOSTAT_FABRIC_BSY: + retry = 1; + break; + + case IOSTAT_LS_RJT: + stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]); + /* Added for Vendor specifc support + * Just keep retrying for these Rsn / Exp codes + */ + switch (stat.un.b.lsRjtRsnCode) { + case LSRJT_UNABLE_TPC: + if (stat.un.b.lsRjtRsnCodeExp == + LSEXP_CMD_IN_PROGRESS) { + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = lpfc_max_els_tries + 1; + retry = 1; + break; + } + break; + + case LSRJT_LOGICAL_BSY: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + break; + + case IOSTAT_INTERMED_RSP: + case IOSTAT_BA_RJT: + break; + + default: + break; + } + + if (ndlp->nlp_DID == FDMI_DID) { + retry = 1; + } + + if ((++cmdiocb->retry) >= maxretry) { + phba->fc_stat.elsRetryExceeded++; + retry = 0; + } + + if (retry) { + + /* Retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0107 Retry ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, delay); + + if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) { + /* If discovery / RSCN timer is running, reset it */ + if (timer_pending(&phba->fc_disctmo) || + (phba->fc_flag & FC_RSCN_MODE)) { + lpfc_set_disctmo(phba); + } + } + + phba->fc_stat.elsXmitRetry++; + if (delay) { + phba->fc_stat.elsDelayRetry++; + ndlp->nlp_retry = cmdiocb->retry; + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_last_elscmd = cmd; + + return (1); + } + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry); + return (1); + } + } + + /* No retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0108 No retry ELS command x%x to remote NPORT x%x " + "Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag); + + return (0); +} + +int +lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + + /* context2 = cmd, context2->next = rsp, context3 = bpl */ + if (elsiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2; + /* Free the response before processing the command. */ + if (!list_empty(&buf_ptr1->list)) { + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (elsiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + + mempool_free( elsiocb, phba->iocb_mem_pool); + return 0; +} + +static void +lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + + /* ACC to LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0109 ACC to LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + ndlp->nlp_flag &= ~NLP_LOGO_ACC; + + switch (ndlp->nlp_state) { + case NLP_STE_UNUSED_NODE: /* node is just allocated */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + break; + case NLP_STE_NPR_NODE: /* NPort Recovery mode */ + lpfc_unreg_rpi(phba, ndlp); + break; + default: + break; + } + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static void +lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox = NULL; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + if (cmdiocb->context_un.mbox) + mbox = cmdiocb->context_un.mbox; + + + /* Check to see if link went down during discovery */ + if ((lpfc_els_chk_latt(phba)) || !ndlp) { + if (mbox) { + mempool_free( mbox, phba->mbox_mem_pool); + } + goto out; + } + + /* ELS response tag completes */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0110 ELS response tag x%x completes " + "Data: x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus, + rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + if (mbox) { + if ((rspiocb->iocb.ulpStatus == 0) + && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { + /* set_slim mailbox command needs to execute first, + * queue this command to be processed later. + */ + lpfc_unreg_rpi(phba, ndlp); + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST); + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + goto out; + } + /* NOTE: we should have messages for unsuccessful + reglogin */ + mempool_free( mbox, phba->mbox_mem_pool); + } else { + mempool_free( mbox, phba->mbox_mem_pool); + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + } +out: + if(ndlp) + ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN; + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp, + LPFC_MBOXQ_t * mbox, uint8_t newnode) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + oldcmd = &oldiocb->iocb; + + switch (flag) { + case ELS_CMD_ACC: + cmdsize = sizeof (uint32_t); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + break; + case ELS_CMD_PLOGI: + cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t)); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + if (mbox) + elsiocb->context_un.mbox = mbox; + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + break; + default: + return (1); + } + + if (newnode) + elsiocb->context1 = NULL; + + /* Xmit ELS ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0128 Xmit ELS ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + if (ndlp->nlp_flag & NLP_LOGO_ACC) { + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc; + } else { + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + } + + phba->fc_stat.elsXmitACC++; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = 2 * sizeof (uint32_t); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_LS_RJT)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT; + pcmd += sizeof (uint32_t); + *((uint32_t *) (pcmd)) = rejectError; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + /* Xmit ELS RJT response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0129 Xmit ELS RJT x%x response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + rejectError, elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + phba->fc_stat.elsXmitLSRJT++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + ADISC *ap; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (ADISC); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit ADISC ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0130 Xmit ADISC ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + ap = (ADISC *) (pcmd); + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_prli_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + PRLI *npr; + lpfc_vpd_t *vpd; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (PRLI); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, + (ELS_CMD_ACC | + (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) == + 0) { + return (1); + } + + /* Xmit PRLI ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0131 Xmit PRLI ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)); + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + memset(pcmd, 0, sizeof (PRLI)); + + npr = (PRLI *) pcmd; + vpd = &phba->vpd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (vpd->rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + + npr->acceptRspCode = PRLI_REQ_EXECUTED; + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + npr->ConfmComplAllowed = 1; + + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static int +lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba, + uint8_t format, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + RNID *rn; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = sizeof (uint32_t) + sizeof (uint32_t) + + (2 * sizeof (struct lpfc_name)); + if (format) + cmdsize += sizeof (RNID_TOP_DISC); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit RNID ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0132 Xmit RNID ACC response tag x%x " + "Data: x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + memset(pcmd, 0, sizeof (RNID)); + rn = (RNID *) (pcmd); + rn->Format = format; + rn->CommonLen = (2 * sizeof (struct lpfc_name)); + memcpy(&rn->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&rn->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + switch (format) { + case 0: + rn->SpecificLen = 0; + break; + case RNID_TOPOLOGY_DISC: + rn->SpecificLen = sizeof (RNID_TOP_DISC); + memcpy(&rn->un.topologyDisc.portName, + &phba->fc_portname, sizeof (struct lpfc_name)); + rn->un.topologyDisc.unitType = RNID_HBA; + rn->un.topologyDisc.physPort = 0; + rn->un.topologyDisc.attachedNodes = 0; + break; + default: + rn->CommonLen = 0; + rn->SpecificLen = 0; + break; + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + elsiocb->context1 = NULL; /* Don't need ndlp for cmpl, + * it could be freed */ + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_disc_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentadisc = 0; + /* go thru NPR list and issue any remaining ELS ADISCs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + sentadisc++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentadisc == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentadisc); +} + +int +lpfc_els_disc_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentplogi = 0; + /* go thru NPR list and issue any remaining ELS PLOGIs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if((ndlp->nlp_flag & NLP_NPR_2B_DISC) && + (!(ndlp->nlp_flag & NLP_DELAY_TMO))) { + if(!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + sentplogi++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentplogi == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentplogi); +} + +int +lpfc_els_flush_rscn(struct lpfc_hba * phba) +{ + struct lpfc_dmabuf *mp; + int i; + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + phba->fc_rscn_id_list[i] = NULL; + } + phba->fc_rscn_id_cnt = 0; + phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY); + lpfc_can_disctmo(phba); + return (0); +} + +int +lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did) +{ + D_ID ns_did; + D_ID rscn_did; + struct lpfc_dmabuf *mp; + uint32_t *lp; + uint32_t payload_len, cmd, i, match; + + ns_did.un.word = did; + match = 0; + + /* If we are doing a FULL RSCN rediscovery, match everything */ + if (phba->fc_flag & FC_RSCN_DISCOVERY) { + return (did); + } + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + while (payload_len) { + rscn_did.un.word = *lp++; + rscn_did.un.word = be32_to_cpu(rscn_did.un.word); + payload_len -= sizeof (uint32_t); + switch (rscn_did.un.b.resv) { + case 0: /* Single N_Port ID effected */ + if (ns_did.un.word == rscn_did.un.word) { + match = did; + } + break; + case 1: /* Whole N_Port Area effected */ + if ((ns_did.un.b.domain == rscn_did.un.b.domain) + && (ns_did.un.b.area == rscn_did.un.b.area)) + { + match = did; + } + break; + case 2: /* Whole N_Port Domain effected */ + if (ns_did.un.b.domain == rscn_did.un.b.domain) + { + match = did; + } + break; + case 3: /* Whole Fabric effected */ + match = did; + break; + default: + /* Unknown Identifier in RSCN list */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0217 Unknown Identifier in " + "RSCN payload Data: x%x\n", + phba->brd_no, rscn_did.un.word); + break; + } + if (match) { + break; + } + } + } + return (match); +} + +static int +lpfc_rscn_recovery_check(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp = NULL, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + /* Look at all nodes effected by pending RSCNs and move + * them to NPR list. + */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + if((lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) { + /* part of RSCN, process this entry */ + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + + lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&ndlp->nlp_delayfunc); + } + } + } + } + return (0); +} + +static int +lpfc_els_rcv_rscn(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t payload_len, cmd; + + icmd = &cmdiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + cmd &= ELS_CMD_MASK; + + /* RSCN received */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0214 RSCN received Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt); + + /* if we are already processing an RSCN, save the received + * RSCN payload buffer, cmdiocb->context2 to process later. + * If we zero, cmdiocb->context2, the calling routine will + * not try to free it. + */ + if (phba->fc_flag & FC_RSCN_MODE) { + if ((phba->fc_rscn_id_cnt < FC_MAX_HOLD_RSCN) && + !(phba->fc_flag & FC_RSCN_DISCOVERY)) { + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + cmdiocb->context2 = NULL; + /* Deferred RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0235 Deferred RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } else { + phba->fc_flag |= FC_RSCN_DISCOVERY; + /* ReDiscovery RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0234 ReDiscovery RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, + newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + return (0); + } + + phba->fc_flag |= FC_RSCN_MODE; + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + /* + * If we zero, cmdiocb->context2, the calling routine will + * not try to free it. + */ + cmdiocb->context2 = NULL; + + lpfc_set_disctmo(phba); + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + + return (lpfc_els_handle_rscn(phba)); +} + +int +lpfc_els_handle_rscn(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + lpfc_put_event(phba, HBA_EVENT_RSCN, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_myDID), 0, 0); + + /* Start timer for RSCN processing */ + lpfc_set_disctmo(phba); + + /* RSCN processed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0215 RSCN processed Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, 0, phba->fc_rscn_id_cnt, + phba->hba_state); + + /* To process RSCN, first compare RSCN data with NameServer */ + phba->fc_ns_retry = 0; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + /* Good ndlp, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) { + /* Wait for NameServer query cmpl before we can + continue */ + return (1); + } + } else { + /* If login to NameServer does not exist, issue one */ + /* Good status, issue PLOGI to NameServer */ + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) { + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_els_flush_rscn(phba); + return (0); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp, 0); + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + } + + lpfc_els_flush_rscn(phba); + return (0); +} + +static int +lpfc_els_rcv_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + uint32_t *lp = (uint32_t *) pcmd->virt; + IOCB_t *icmd = &cmdiocb->iocb; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + uint32_t cmd, did; + + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + sp = (struct serv_parm *) lp; + + /* FLOGI received */ + + lpfc_set_disctmo(phba); + + if (phba->fc_topology == TOPOLOGY_LOOP) { + /* We should never receive a FLOGI in loop mode, ignore it */ + did = icmd->un.elsreq64.remoteID; + + /* An FLOGI ELS command was received from DID in + Loop Mode */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0113 An FLOGI ELS command x%x was received " + "from DID x%x in Loop Mode\n", + phba->brd_no, cmd, did); + return (1); + } + + did = Fabric_DID; + + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* For a FLOGI we accept, then if our portname is greater + * then the remote portname we initiate Nport login. + */ + int rc; + + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (!rc) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + return (1); + } + + else if (rc > 0) { /* greater than */ + phba->fc_flag |= FC_PT2PT_PLOGI; + } + phba->fc_flag |= FC_PT2PT; + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + } else { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (1); + } + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode); + + return (0); +} + +static int +lpfc_els_rcv_rnid(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + RNID *rn; + struct ls_rjt stat; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + rn = (RNID *) lp; + + /* RNID received */ + + switch (rn->Format) { + case 0: + case RNID_TOPOLOGY_DISC: + /* Send back ACC */ + lpfc_els_rsp_rnid_acc(phba, rn->Format, cmdiocb, ndlp); + break; + default: + /* Reject this request because format not supported */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + return (0); +} + +static int +lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + RRQ *rrq; + uint32_t cmd, did; + + psli = &phba->sli; + pring = &psli->ring[LPFC_FCP_RING]; + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + rrq = (RRQ *) lp; + + /* RRQ received */ + /* Get oxid / rxid from payload and abort it */ + if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Oxid); + } else { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Rxid); + } + /* ACCEPT the rrq request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_farp(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + FARP *fp; + uint32_t cmd, cnt, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + fp = (FARP *) lp; + + /* FARP-REQ received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_IP, + "%d:0601 FARP-REQ received from DID x%x\n", + phba->brd_no, did); + + /* We will only support match on WWPN or WWNN */ + if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) { + return (0); + } + + cnt = 0; + /* If this FARP command is searching for my portname */ + if (fp->Mflags & FARP_MATCH_PORT) { + if (memcmp(&fp->RportName, &phba->fc_portname, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + /* If this FARP command is searching for my nodename */ + if (fp->Mflags & FARP_MATCH_NODE) { + if (memcmp(&fp->RnodeName, &phba->fc_nodename, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + if (cnt) { + if((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) || + (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) { + /* Log back into the node before sending the FARP. */ + if (fp->Rflags & FARP_REQUEST_PLOGI) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + + /* Send a FARP response to that node */ + if (fp->Rflags & FARP_REQUEST_FARPR) { + lpfc_issue_els_farpr(phba, did, 0); + } + } + } + return (0); +} + +static int +lpfc_els_rcv_farpr(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + /* FARP-RSP received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_IP, + "%d:0600 FARP-RSP received from DID x%x\n", + phba->brd_no, did); + + /* ACCEPT the Farp resp request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + FAN *fp; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + fp = (FAN *) lp; + + /* FAN received */ + + /* ACCEPT the FAN request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* The discovery state machine needs to take a different + * action if this node has switched fabrics + */ + if ((memcmp(&fp->FportName, &phba->fc_fabparam.portName, + sizeof (struct lpfc_name)) != 0) + || + (memcmp(&fp->FnodeName, &phba->fc_fabparam.nodeName, + sizeof (struct lpfc_name)) != 0)) { + /* This node has switched fabrics. An FLOGI is required + * after the timeout + */ + return (0); + } + + /* Start discovery */ + lpfc_disc_start(phba); + } + + return (0); +} + +void +lpfc_els_timeout_handler(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + struct list_head *dlp; + uint32_t *elscmd; + uint32_t els_command; + uint32_t timeout; + uint32_t remote_ID; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if(phba == 0) + return; + spin_lock_irqsave(phba->host->host_lock, iflag); + timeout = (uint32_t)(phba->fc_ratov << 1); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + dlp = &pring->txcmplq; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if ((els_command == ELS_CMD_FARP) + || (els_command == ELS_CMD_FARPR)) { + continue; + } + + if (piocb->drvrTimeout > 0) { + if (piocb->drvrTimeout >= timeout) { + piocb->drvrTimeout -= timeout; + } else { + piocb->drvrTimeout = 0; + } + continue; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (cmd->un.elsreq64.bdl.ulpIoTag32) { + lpfc_sli_issue_abort_iotag32(phba, + pring, piocb); + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + lpfc_printf_log(phba, + KERN_ERR, + LOG_ELS, + "%d:0127 ELS timeout Data: x%x x%x x%x x%x\n", + phba->brd_no, els_command, + remote_ID, cmd->ulpCommand, cmd->ulpIoTag); + + /* + * The iocb has timed out; abort it. + */ + if (piocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free(piocb, phba->iocb_mem_pool); + } + } + + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +void +lpfc_els_flush_cmd(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + uint32_t *elscmd; + uint32_t els_command; + uint32_t remote_ID; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + + /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */ + if ((cmd->ulpCommand == CMD_QUE_RING_BUF_CN) || + (cmd->ulpCommand == CMD_QUE_RING_BUF64_CN) || + (cmd->ulpCommand == CMD_CLOSE_XRI_CN) || + (cmd->ulpCommand == CMD_ABORT_XRI_CN)) { + continue; + } + + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + return; +} + +void +lpfc_els_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + struct lpfc_dmabuf *mp; + uint32_t *lp; + IOCB_t *icmd; + struct ls_rjt stat; + uint32_t cmd; + uint32_t did; + uint32_t newnode; + uint32_t drop_cmd = 0; /* by default do NOT drop received cmd */ + uint32_t rjt_err = 0; + + psli = &phba->sli; + icmd = &elsiocb->iocb; + + /* type of ELS cmd is first 32bit word in packet */ + mp = lpfc_sli_ringpostbuf_get(phba, pring, getPaddr(icmd->un. + cont64[0]. + addrHigh, + icmd->un. + cont64[0].addrLow)); + if (mp == 0) { + drop_cmd = 1; + goto dropit; + } + + newnode = 0; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1); + + if (icmd->ulpStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + did = icmd->un.rcvels.remoteID; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + lpfc_nlp_init(phba, ndlp, did); + newnode = 1; + if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) { + ndlp->nlp_type |= NLP_FABRIC; + } + } + + phba->fc_stat.elsRcvFrame++; + elsiocb->context1 = ndlp; + elsiocb->context2 = mp; + + if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) { + cmd &= ELS_CMD_MASK; + } + /* ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0112 ELS command x%x received from NPORT x%x " + "Data: x%x\n", phba->brd_no, cmd, did, phba->hba_state); + + switch (cmd) { + case ELS_CMD_PLOGI: + phba->fc_stat.elsRcvPLOGI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); + break; + case ELS_CMD_FLOGI: + phba->fc_stat.elsRcvFLOGI++; + lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_LOGO: + phba->fc_stat.elsRcvLOGO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO); + break; + case ELS_CMD_PRLO: + phba->fc_stat.elsRcvPRLO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO); + break; + case ELS_CMD_RSCN: + phba->fc_stat.elsRcvRSCN++; + lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_ADISC: + phba->fc_stat.elsRcvADISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC); + break; + case ELS_CMD_PDISC: + phba->fc_stat.elsRcvPDISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC); + break; + case ELS_CMD_FARPR: + phba->fc_stat.elsRcvFARPR++; + lpfc_els_rcv_farpr(phba, elsiocb, ndlp); + break; + case ELS_CMD_FARP: + phba->fc_stat.elsRcvFARP++; + lpfc_els_rcv_farp(phba, elsiocb, ndlp); + break; + case ELS_CMD_FAN: + phba->fc_stat.elsRcvFAN++; + lpfc_els_rcv_fan(phba, elsiocb, ndlp); + break; + case ELS_CMD_RRQ: + phba->fc_stat.elsRcvRRQ++; + lpfc_els_rcv_rrq(phba, elsiocb, ndlp); + break; + case ELS_CMD_PRLI: + phba->fc_stat.elsRcvPRLI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI); + break; + case ELS_CMD_RNID: + phba->fc_stat.elsRcvRNID++; + lpfc_els_rcv_rnid(phba, elsiocb, ndlp); + break; + default: + /* Unsupported ELS command, reject */ + rjt_err = LSEXP_NOTHING_MORE; + + /* Unknown ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0115 Unknown ELS command x%x received from " + "NPORT x%x\n", phba->brd_no, cmd, did); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + } + + /* check if need to LS_RJT received ELS cmd */ + if (rjt_err) { + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = rjt_err; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp); + } + + if (elsiocb->context2) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } +dropit: + /* check if need to drop received ELS cmd */ + if (drop_cmd == 1) { + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0111 Dropping received ELS cmd " + "Data: x%x x%x\n", phba->brd_no, + icmd->ulpStatus, icmd->un.ulpWord[4]); + phba->fc_stat.elsRcvDrop++; + } + return; +} diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c new file mode 100644 index 000000000..74c454311 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -0,0 +1,2703 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hbadisc.c 1.199 2004/11/18 20:19:30EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_fcp.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +/* AlpaArray for assignment of scsid for scan-down and bind_method */ +uint8_t lpfcAlpaArray[] = { + 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6, + 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, + 0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5, + 0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, + 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97, + 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79, + 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B, + 0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56, + 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, + 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35, + 0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, + 0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17, + 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01 +}; + +static void +lpfc_evt_iocb_free(struct lpfc_hba * phba, struct lpfc_iocbq * saveq) +{ + struct lpfc_iocbq *rspiocbp, *tmpiocbp; + + /* Free up iocb buffer chain for cmd just processed */ + list_for_each_entry_safe(rspiocbp, tmpiocbp, + &saveq->list, list) { + list_del(&rspiocbp->list); + mempool_free( rspiocbp, phba->iocb_mem_pool); + } + mempool_free( saveq, phba->iocb_mem_pool); +} + +void +lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +{ + struct lpfc_target *targetp; + int scsid; + + if (!(ndlp->nlp_type & NLP_FABRIC)) { + /* Nodev timeout on NPort */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0203 Nodev timeout on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + } + + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + scsid, LPFC_ABORT_ALLQ); + } + } + } + + lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM); + return; +} + +static void +lpfc_disc_done(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + LPFC_DISC_EVT_t *evtp, *next_evtp; + LPFC_MBOXQ_t *pmb; + struct lpfc_iocbq *cmdiocbp, *saveq; + struct lpfc_nodelist *ndlp; + LPFC_RING_MASK_t *func; + struct Scsi_Host *shost; + LIST_HEAD(local_dpc_disc); + + list_splice_init(&phba->dpc_disc, &local_dpc_disc); + + /* check discovery event list */ + list_for_each_entry_safe(evtp, next_evtp, &local_dpc_disc, evt_listp) { + list_del(&evtp->evt_listp); + + switch(evtp->evt) { + case LPFC_EVT_MBOX: + pmb = (LPFC_MBOXQ_t *)(evtp->evt_arg1); + (pmb->mbox_cmpl) (phba, pmb); + break; + case LPFC_EVT_SOL_IOCB: + cmdiocbp = (struct lpfc_iocbq *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_UNSOL_IOCB: + func = (LPFC_RING_MASK_t *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (func->lpfc_sli_rcv_unsol_event) (phba, + &psli->ring[LPFC_ELS_RING], saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_NODEV_TMO: + ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); + lpfc_process_nodev_timeout(phba, ndlp); + break; + case LPFC_EVT_SCAN: + /* SCSI HOTPLUG supported */ + shost = phba->host; +#ifdef USE_SCAN_TARGET + { + struct lpfc_target *targetp; + + targetp = (struct lpfc_target *)(evtp->evt_arg1); + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0251 Rescanning scsi target %d\n", + phba->brd_no, targetp->scsi_id); + + if(targetp == + phba->device_queue_hash[targetp->scsi_id]) { + spin_unlock_irq(phba->host->host_lock); + scsi_scan_single_target(shost, 0, + targetp->scsi_id); + spin_lock_irq(phba->host->host_lock); + } + } +#else + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0251 Rescanning scsi host\n", phba->brd_no); + spin_unlock_irq(shost->host_lock); + scsi_scan_host(shost); + spin_lock_irq(shost->host_lock); +#endif + break; + } + kfree(evtp); + } +} + +int +lpfc_do_dpc(void *p) +{ + unsigned long flags; + DECLARE_MUTEX_LOCKED(sem); + struct lpfc_hba *phba = (struct lpfc_hba *)p; + + lock_kernel(); + + daemonize("lpfc_dpc_%d", phba->brd_no); + allow_signal(SIGHUP); + + phba->dpc_wait = &sem; + set_user_nice(current, -20); + + unlock_kernel(); + + complete(&phba->dpc_startup); + + while (1) { + if (down_interruptible(&sem)) + break; + + if (signal_pending(current)) + break; + + if (phba->dpc_kill) + break; + + spin_lock_irqsave(phba->host->host_lock, flags); + lpfc_disc_done(phba); + spin_unlock_irqrestore(phba->host->host_lock, flags); + } + + /* Zero out semaphore we were waiting on. */ + phba->dpc_wait = NULL; + complete_and_exit(&phba->dpc_exiting, 0); + return(0); +} + +/* + * This is only called to handle FC discovery events. Since this a rare + * occurance, we allocate an LPFC_DISC_EVT_t structure here instead of + * embedding it in the IOCB. + */ +int +lpfc_discq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2, + uint32_t evt) +{ + LPFC_DISC_EVT_t *evtp; + + /* All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events + * will be queued to DPC for processing + */ + evtp = (LPFC_DISC_EVT_t *) kmalloc(sizeof(LPFC_DISC_EVT_t), GFP_ATOMIC); + if (!evtp) + return 0; + + evtp->evt_arg1 = arg1; + evtp->evt_arg2 = arg2; + evtp->evt = evt; + evtp->evt_listp.next = NULL; + evtp->evt_listp.prev = NULL; + + /* Queue the event to the DPC to be processed later */ + list_add_tail(&evtp->evt_listp, &phba->dpc_disc); + if (phba->dpc_wait) + up(phba->dpc_wait); + + return 1; +} + +int +lpfc_linkdown(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + LPFC_MBOXQ_t *mb; + int rc, i; + + psli = &phba->sli; + phba->hba_state = LPFC_LINK_DOWN; + +#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH) + /* Stop all requests to the driver from the midlayer. */ + scsi_block_requests(phba->host); +#endif + + lpfc_put_event(phba, HBA_EVENT_LINK_DOWN, phba->fc_myDID, NULL, 0, 0); + + /* Clean up any firmware default rpi's */ + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_did(phba, 0xffffffff, mb); + if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + + /* Cleanup any outstanding RSCN activity */ + lpfc_els_flush_rscn(phba); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + /* + * If this function was called by the lpfc_do_dpc, don't recurse into + * the routine again. If not, just process any outstanding + * discovery events. + */ + if (!list_empty(&phba->dpc_disc)) { + lpfc_disc_done(phba); + } + + /* Issue a LINK DOWN event to all nodes */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + /* Fabric nodes are not handled thru state machine for + link down */ + if (ndlp->nlp_type & NLP_FABRIC) { + /* Remove ALL Fabric nodes except Fabric_DID */ + if (ndlp->nlp_DID != Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, + NLP_NO_LIST); + } + } + else { + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_LINK_DOWN, + LPFC_SET_BITMASK); + + rc = lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + } + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Setup myDID for link up if we are in pt2pt mode */ + if (phba->fc_flag & FC_PT2PT) { + phba->fc_myDID = 0; + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_config_link(phba, mb); + if (lpfc_sli_issue_mbox + (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI); + } + phba->fc_flag &= ~FC_LBIT; + + /* Turn off discovery timer if its running */ + lpfc_can_disctmo(phba); + + /* Must process IOCBs on all rings to handle ABORTed I/Os */ + return (0); +} + +static int +lpfc_linkup(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + phba->hba_state = LPFC_LINK_UP; + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | + FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY); + phba->fc_ns_retry = 0; + + + lpfc_put_event(phba, HBA_EVENT_LINK_UP, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_topology), + 0, phba->fc_linkspeed); + + /* + * Clean up old Fabric NLP_FABRIC logins. + */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + if (ndlp->nlp_DID == Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Mark all nodes for LINK UP */ + node_list[0] = &phba->fc_plogi_list; + node_list[1] = &phba->fc_adisc_list; + node_list[2] = &phba->fc_reglogin_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_nlpunmap_list; + node_list[5] = &phba->fc_nlpmap_list; + node_list[6] = &phba->fc_npr_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry(ndlp, listp, nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_LINK_DOWN, + LPFC_CLR_BITMASK); + } + } + +#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH) + spin_unlock_irq(phba->host->host_lock); + scsi_unblock_requests(phba->host); + spin_lock_irq(phba->host->host_lock); +#endif + return 0; +} + +/* + * This routine handles processing a CLEAR_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + uint32_t control; + + psli = &phba->sli; + mb = &pmb->mb; + /* Since we don't do discovery right now, turn these off here */ + psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + /* Check for error */ + if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) { + /* CLEAR_LA mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0320 CLEAR_LA mbxStatus error x%x hba " + "state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if(phba->fc_flag & FC_ABORT_DISCOVERY) + goto out; + + phba->num_disc_nodes = 0; + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + lpfc_els_disc_plogi(phba); + } + + phba->hba_state = LPFC_HBA_READY; + +out: + phba->fc_flag &= ~FC_ABORT_DISCOVERY; + /* Device Discovery completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0225 Device Discovery completes\n", + phba->brd_no); + + mempool_free( pmb, phba->mbox_mem_pool); + if (phba->fc_flag & FC_ESTABLISH_LINK) { + phba->fc_flag &= ~FC_ESTABLISH_LINK; + } + del_timer_sync(&phba->fc_estabtmo); + lpfc_can_disctmo(phba); + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + return; +} + +static void +lpfc_mbx_cmpl_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + + psli = &phba->sli; + mb = &pmb->mb; + /* Check for error */ + if (mb->mbxStatus) { + /* CONFIG_LINK mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0306 CONFIG_LINK mbxStatus error x%x " + "HBA state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + if (phba->fc_topology == TOPOLOGY_LOOP) { + /* If we are public loop and L bit was set */ + if ((phba->fc_flag & FC_PUBLIC_LOOP) && + !(phba->fc_flag & FC_LBIT)) { + /* Need to wait for FAN - use discovery timer + * for timeout. hba_state is identically + * LPFC_LOCAL_CFG_LINK while waiting for FAN + */ + lpfc_set_disctmo(phba); + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + } + + /* Start discovery by sending a FLOGI hba_state is identically + * LPFC_FLOGI while waiting for FLOGI cmpl + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + +out: + /* CONFIG_LINK bad hba state */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0200 CONFIG_LINK bad hba state x%x\n", + phba->brd_no, phba->hba_state); + + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +static void +lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1; + + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_SPARAM mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0319 READ_SPARAM mbxStatus error x%x " + "hba state x%x>\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt, + sizeof (struct serv_parm)); + memcpy((uint8_t *) & phba->fc_nodename, + (uint8_t *) & phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy((uint8_t *) & phba->fc_portname, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; + +out: + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +/* + * This routine handles processing a READ_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + READ_LA_VAR *la; + LPFC_MBOXQ_t *mbox; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1); + uint32_t control; + int i; + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_LA mbox error state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_LINK_EVENT, + "%d:1307 READ_LA mbox error x%x state x%x\n", + phba->brd_no, + mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + return; + } + la = (READ_LA_VAR *) & pmb->mb.un.varReadLA; + + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + /* Get Loop Map information */ + if (mp) { + memcpy(&phba->alpa_map[0], mp->virt, 128); + } else { + memset(&phba->alpa_map[0], 0, 128); + } + + if (((phba->fc_eventTag + 1) < la->eventTag) || + (phba->fc_eventTag == la->eventTag)) { + phba->fc_stat.LinkMultiEvent++; + if (la->attType == AT_LINK_UP) { + if (phba->fc_eventTag != 0) { + + lpfc_linkdown(phba); + } + } + } + + phba->fc_eventTag = la->eventTag; + + if (la->attType == AT_LINK_UP) { + phba->fc_stat.LinkUp++; + /* Link Up Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1303 Link Up Event x%x received " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + la->granted_AL_PA, la->UlnkSpeed, + phba->alpa_map[0]); + + switch(la->UlnkSpeed) { + case LA_1GHZ_LINK: + phba->fc_linkspeed = LA_1GHZ_LINK; + break; + case LA_2GHZ_LINK: + phba->fc_linkspeed = LA_2GHZ_LINK; + break; + case LA_4GHZ_LINK: + phba->fc_linkspeed = LA_4GHZ_LINK; + break; + default: + phba->fc_linkspeed = LA_UNKNW_LINK; + break; + } + + if ((phba->fc_topology = la->topology) == TOPOLOGY_LOOP) { + + if (la->il) { + phba->fc_flag |= FC_LBIT; + } + + phba->fc_myDID = la->granted_AL_PA; + + i = la->un.lilpBde64.tus.f.bdeSize; + if (i == 0) { + phba->alpa_map[0] = 0; + } else { + if (phba->cfg_log_verbose + & LOG_LINK_EVENT) { + int numalpa, j, k; + union { + uint8_t pamap[16]; + struct { + uint32_t wd1; + uint32_t wd2; + uint32_t wd3; + uint32_t wd4; + } pa; + } un; + + numalpa = phba->alpa_map[0]; + j = 0; + while (j < numalpa) { + memset(un.pamap, 0, 16); + for (k = 1; j < numalpa; k++) { + un.pamap[k - 1] = + phba->alpa_map[j + + 1]; + j++; + if (k == 16) + break; + } + /* Link Up Event ALPA map */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1304 Link Up Event " + "ALPA map Data: x%x " + "x%x x%x x%x\n", + phba->brd_no, + un.pa.wd1, un.pa.wd2, + un.pa.wd3, un.pa.wd4); + } + } + } + } else { + phba->fc_myDID = phba->fc_pref_DID; + phba->fc_flag |= FC_LBIT; + } + + lpfc_linkup(phba); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_read_sparam(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_LOCAL_CFG_LINK; + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_config_link; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + } else { + phba->fc_stat.LinkDown++; + /* Link Down Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1305 Link Down Event x%x received " + "Data: x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + phba->hba_state, phba->fc_flag); + + lpfc_linkdown(phba); + + /* turn on Link Attention interrupts - no CLEAR_LA needed */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + } + + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; +} + +/* + * This routine handles processing a REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a Fabric REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + struct lpfc_nodelist *ndlp_fdmi; + + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + mempool_free( ndlp, phba->nlp_mem_pool); + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* This NPort has been assigned an NPort_ID by the fabric as a + * result of the completed fabric login. Issue a State Change + * Registration (SCR) ELS request to the fabric controller + * (SCR_DID) so that this NPort gets RSCN events from the + * fabric. + */ + lpfc_issue_els_scr(phba, SCR_DID, 0); + + /* Allocate a new node instance. If the pool is empty, just + * start the discovery process and skip the Nameserver login + * process. This is attempted again later on. Otherwise, issue + * a Port Login (PLOGI) to the NameServer + */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_disc_start(phba); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + if (phba->cfg_fdmi_on) { + if ((ndlp_fdmi = mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC))) { + lpfc_nlp_init(phba, ndlp_fdmi, + FDMI_DID); + ndlp_fdmi->nlp_type |= NLP_FABRIC; + ndlp_fdmi->nlp_state = + NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp_fdmi, + 0); + } + } + } + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + + /* RegLogin failed, so just use loop map to make discovery + list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state < LPFC_HBA_READY) { + /* Link up discovery requires Fabrib registration. */ + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID); + } + + phba->fc_ns_retry = 0; + /* Good status, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT)) { + /* Cannot issue NameServer Query, so finish up discovery */ + lpfc_disc_start(phba); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* Put blp on the bind list */ +int +lpfc_consistent_bind_save(struct lpfc_hba * phba, struct lpfc_bindlist * blp) +{ + /* Put it at the end of the bind list */ + list_add_tail(&blp->nlp_listp, &phba->fc_nlpbind_list); + phba->fc_bind_cnt++; + + /* Add scsiid to BIND list */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0903 Add scsiid %d to BIND list " + "Data: x%x x%x x%x x%p\n", + phba->brd_no, blp->nlp_sid, phba->fc_bind_cnt, + blp->nlp_DID, blp->nlp_bind_type, blp); + + return (0); +} + +int +lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) +{ + struct lpfc_bindlist *blp; + struct lpfc_target *targetp; + struct lpfc_sli *psli; + psli = &phba->sli; + + /* Sanity check to ensure we are not moving to / from the same list */ + if((nlp->nlp_flag & NLP_LIST_MASK) == list) { + if(list != NLP_NO_LIST) + return(0); + } + + blp = nlp->nlp_listp_bind; + + switch(nlp->nlp_flag & NLP_LIST_MASK) { + case NLP_NO_LIST: /* Not on any list */ + break; + case NLP_UNUSED_LIST: + phba->fc_unused_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PLOGI_LIST: + phba->fc_plogi_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_ADISC_LIST: + phba->fc_adisc_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_REGLOGIN_LIST: + phba->fc_reglogin_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PRLI_LIST: + phba->fc_prli_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_UNMAPPED_LIST: + phba->fc_unmap_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; + nlp->nlp_type &= ~NLP_FC_NODE; + phba->nport_event_cnt++; + break; + case NLP_MAPPED_LIST: + phba->fc_map_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + phba->nport_event_cnt++; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_SET_BITMASK); + nlp->nlp_type &= ~NLP_FCP_TARGET; + targetp = nlp->nlp_Target; + if (targetp && (list != NLP_MAPPED_LIST)) { + nlp->nlp_Target = NULL; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + /* + * Do not block the target if the driver has just reset + * its interface to the hardware. + */ + if (phba->hba_state != LPFC_INIT_START) + lpfc_target_block(phba, targetp); +#endif + } + + break; + case NLP_NPR_LIST: + phba->fc_npr_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + /* Stop delay tmo if taking node off NPR list */ + if ((nlp->nlp_flag & NLP_DELAY_TMO) && + (list != NLP_NPR_LIST)) { + nlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&nlp->nlp_delayfunc); + } + break; + } + + /* Add NPort to list */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0904 Add NPort x%x to %d list Data: x%x x%p\n", + phba->brd_no, + nlp->nlp_DID, list, nlp->nlp_flag, blp); + + nlp->nlp_listp_bind = NULL; + + switch(list) { + case NLP_NO_LIST: /* No list, just remove it */ + lpfc_nlp_remove(phba, nlp); + break; + case NLP_UNUSED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unused list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list); + phba->fc_unused_cnt++; + break; + case NLP_PLOGI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the plogi list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list); + phba->fc_plogi_cnt++; + break; + case NLP_ADISC_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the adisc list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list); + phba->fc_adisc_cnt++; + break; + case NLP_REGLOGIN_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the reglogin list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list); + phba->fc_reglogin_cnt++; + break; + case NLP_PRLI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the prli list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list); + phba->fc_prli_cnt++; + break; + case NLP_UNMAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unmap list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); + phba->fc_unmap_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&nlp->nlp_tmofunc); + } + nlp->nlp_flag &= ~NLP_NODEV_TMO; + nlp->nlp_type |= NLP_FC_NODE; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + break; + case NLP_MAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the map list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); + phba->fc_map_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&nlp->nlp_tmofunc); + } + nlp->nlp_flag &= ~NLP_NODEV_TMO; + nlp->nlp_type |= NLP_FCP_TARGET; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_CLR_BITMASK); + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + + targetp = phba->device_queue_hash[nlp->nlp_sid]; + if (targetp && targetp->pnode) { + nlp->nlp_Target = targetp; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + /* Unblock I/Os on target */ + if(targetp->blocked) + lpfc_target_unblock(phba, targetp); +#endif + } + break; + case NLP_NPR_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the npr list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); + phba->fc_npr_cnt++; + + /* + * Sanity check for Fabric entity. + * Set nodev_tmo for NPR state, for Fabric use 1 sec. + */ + if (nlp->nlp_type & NLP_FABRIC) { + mod_timer(&nlp->nlp_tmofunc, jiffies + HZ); + } + else { + mod_timer(&nlp->nlp_tmofunc, + jiffies + HZ * phba->cfg_nodev_tmo); + } + nlp->nlp_flag |= NLP_NODEV_TMO; + nlp->nlp_flag &= ~NLP_RCV_PLOGI; + break; + case NLP_JUST_DQ: + break; + } + + if (blp) { + nlp->nlp_sid = 0; + nlp->nlp_flag &= ~NLP_SEED_MASK; + nlp->nlp_Target = NULL; + lpfc_consistent_bind_save(phba, blp); + } + return (0); +} + +/* + * Start / ReStart rescue timer for Discovery / RSCN handling + */ +void +lpfc_set_disctmo(struct lpfc_hba * phba) +{ + uint32_t tmo; + + tmo = ((phba->fc_ratov * 2) + 1); + + mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo); + phba->fc_flag |= FC_DISC_TMO; + + /* Start Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0247 Start Discovery Timer state x%x " + "Data: x%x x%lx x%x x%x\n", + phba->brd_no, + phba->hba_state, tmo, (unsigned long)&phba->fc_disctmo, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return; +} + +/* + * Cancel rescue timer for Discovery / RSCN handling + */ +int +lpfc_can_disctmo(struct lpfc_hba * phba) +{ + /* Turn off discovery timer if its running */ + if(phba->fc_flag & FC_DISC_TMO) { + del_timer_sync(&phba->fc_disctmo); + } + phba->fc_flag &= ~FC_DISC_TMO; + + /* Cancel Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0248 Cancel Discovery Timer state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return (0); +} + +/* + * Check specified ring for outstanding IOCB on the SLI queue + * Return true if iocb matches the specified nport + */ +int +lpfc_check_sli_ndlp(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * iocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + + psli = &phba->sli; + icmd = &iocb->iocb; + if (pring->ringno == LPFC_ELS_RING) { + switch (icmd->ulpCommand) { + case CMD_GEN_REQUEST64_CR: + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) + return (1); + case CMD_ELS_REQUEST64_CR: + case CMD_XMIT_ELS_RSP64_CX: + if (iocb->context1 == (uint8_t *) ndlp) + return (1); + } + } else if (pring->ringno == psli->ip_ring) { + + } else if (pring->ringno == psli->fcp_ring) { + /* Skip match check if waiting to relogin to FCP target */ + if ((ndlp->nlp_type & NLP_FCP_TARGET) && + (ndlp->nlp_flag & NLP_DELAY_TMO)) { + return (0); + } + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) { + return (1); + } + } else if (pring->ringno == psli->next_ring) { + + } + return (0); +} + +/* + * Free resources / clean up outstanding I/Os + * associated with nlp_rpi in the LPFC_NODELIST entry. + */ +static int +lpfc_no_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd; + uint32_t rpi, i; + + psli = &phba->sli; + rpi = ndlp->nlp_rpi; + if (rpi) { + /* Now process each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, + list) { + /* + * Check to see if iocb matches the nport we are + * looking for + */ + if ((lpfc_check_sli_ndlp + (phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, + iocb, iocb); + } else { + mempool_free(iocb, + phba->iocb_mem_pool); + } + } + } + /* Everything that matches on txcmplq will be returned + * by firmware with a no rpi error. + */ + } + } + return (0); +} + +/* + * Free rpi associated with LPFC_NODELIST entry. + * This routine is called from lpfc_freenode(), when we are removing + * a LPFC_NODELIST entry. It is also called if the driver initiates a + * LOGO that completes successfully, and we are waiting to PLOGI back + * to the remote NPort. In addition, it is called after we receive + * and unsolicated ELS cmd, send back a rsp, the rsp completes and + * we are waiting to PLOGI back to the remote NPort. + */ +int +lpfc_unreg_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + LPFC_MBOXQ_t *mbox; + + if (ndlp->nlp_rpi) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_login(phba, ndlp->nlp_rpi, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + lpfc_no_rpi(phba, ndlp); + ndlp->nlp_rpi = 0; + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + return 1; + } + return 0; +} + +/* + * Free resources associated with LPFC_NODELIST entry + * so it can be freed. + */ +static int +lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_target *targetp; + struct lpfc_sli *psli; + int scsid; + + /* The psli variable gets rid of the long pointer deference. */ + psli = &phba->sli; + + /* Cleanup node for NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0900 Cleanup node for NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ); + + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&ndlp->nlp_tmofunc); + } + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + del_timer_sync(&ndlp->nlp_delayfunc); + } + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + + lpfc_unreg_rpi(phba, ndlp); + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + targetp->pnode = NULL; + ndlp->nlp_Target = NULL; +#ifdef FC_TRANS_VER1 + /* + * This code does not apply to SLES9 since there + * is no starget defined in the midlayer. + * Additionally, dynamic target discovery to the + * midlayer is not supported yet. + */ + if (targetp->starget) { + /* Remove SCSI target / SCSI Hotplug */ + lpfc_target_remove(phba, targetp); + } +#endif + break; + } + } + } + return (0); +} + +/* + * Check to see if we can free the nlp back to the freelist. + * If we are in the middle of using the nlp in the discovery state + * machine, defer the free till we reach the end of the state machine. + */ +int +lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&ndlp->nlp_tmofunc); + } + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + del_timer_sync(&ndlp->nlp_delayfunc); + } + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + + if (ndlp->nlp_disc_refcnt) { + ndlp->nlp_flag |= NLP_DELAY_REMOVE; + } + else { + lpfc_freenode(phba, ndlp); + mempool_free( ndlp, phba->nlp_mem_pool); + } + return(0); +} + +static int +lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did) +{ + D_ID mydid; + D_ID ndlpdid; + D_ID matchdid; + + if (did == Bcast_DID) + return (0); + + if (ndlp->nlp_DID == 0) { + return (0); + } + + /* First check for Direct match */ + if (ndlp->nlp_DID == did) + return (1); + + /* Next check for area/domain identically equals 0 match */ + mydid.un.word = phba->fc_myDID; + if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) { + return (0); + } + + matchdid.un.word = did; + ndlpdid.un.word = ndlp->nlp_DID; + if (matchdid.un.b.id == ndlpdid.un.b.id) { + if ((mydid.un.b.domain == matchdid.un.b.domain) && + (mydid.un.b.area == matchdid.un.b.area)) { + if ((ndlpdid.un.b.domain == 0) && + (ndlpdid.un.b.area == 0)) { + if (ndlpdid.un.b.id) + return (1); + } + return (0); + } + + matchdid.un.word = ndlp->nlp_DID; + if ((mydid.un.b.domain == ndlpdid.un.b.domain) && + (mydid.un.b.area == ndlpdid.un.b.area)) { + if ((matchdid.un.b.domain == 0) && + (matchdid.un.b.area == 0)) { + if (matchdid.un.b.id) + return (1); + } + } + } + return (0); +} + +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0911 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0901 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0910 FIND node DID unmapped" + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node did mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0902 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0929 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0930 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PLOGI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PLOGI */ + /* FIND node DID plogi */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0908 FIND node DID plogi " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_ADISC) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to ADISC */ + /* FIND node DID adisc */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID adisc " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_REGLOGIN) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_reglogin_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to REGLOGIN */ + /* FIND node DID reglogin */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID reglogin" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PRLI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PRLI */ + /* FIND node DID prli */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID prli " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_NPR) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to NPR */ + /* FIND node DID npr */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID npr " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_UNUSED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to UNUSED */ + /* FIND node DID unused */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID unused " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* FIND node did NOT FOUND */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n", + phba->brd_no, did, order); + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} + +struct lpfc_nodelist * +lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did) +{ + struct lpfc_nodelist *ndlp; + uint32_t flg; + + if((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + if ((phba->hba_state == LPFC_HBA_READY) && + ((lpfc_rscn_payload_check(phba, did) == 0))) + return NULL; + ndlp = (struct lpfc_nodelist *) + mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); + if (!ndlp) + return NULL; + lpfc_nlp_init(phba, ndlp, did); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + return ndlp; + } + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + if(lpfc_rscn_payload_check(phba, did)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp = NULL; + } + } + else { + flg = ndlp->nlp_flag & NLP_LIST_MASK; + if ((flg == NLP_ADISC_LIST) || + (flg == NLP_PLOGI_LIST)) { + return NULL; + } + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + return ndlp; +} + +/* Build a list of nodes to discover based on the loopmap */ +void +lpfc_disc_list_loopmap(struct lpfc_hba * phba) +{ + int j; + uint32_t alpa, index; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->fc_topology != TOPOLOGY_LOOP) { + return; + } + + /* Check for loop map present or not */ + if (phba->alpa_map[0]) { + for (j = 1; j <= phba->alpa_map[0]; j++) { + alpa = phba->alpa_map[j]; + + if (((phba->fc_myDID & 0xff) == alpa) || (alpa == 0)) { + continue; + } + lpfc_setup_disc_node(phba, alpa); + } + } else { + /* No alpamap, so try all alpa's */ + for (j = 0; j < FC_MAXLOOP; j++) { + /* If cfg_scan_down is set, start from highest + * ALPA (0xef) to lowest (0x1). + */ + if (phba->cfg_scan_down) + index = j; + else + index = FC_MAXLOOP - j - 1; + alpa = lpfcAlpaArray[index]; + if ((phba->fc_myDID & 0xff) == alpa) { + continue; + } + + lpfc_setup_disc_node(phba, alpa); + } + } + return; +} + +/* Start Link up / RSCN discovery on NPR list */ +void +lpfc_disc_start(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t did_changed, num_sent; + uint32_t clear_la_pending; + + psli = &phba->sli; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->hba_state == LPFC_CLEAR_LA) + clear_la_pending = 1; + else + clear_la_pending = 0; + + if (phba->hba_state < LPFC_HBA_READY) { + phba->hba_state = LPFC_DISC_AUTH; + } + lpfc_set_disctmo(phba); + + if (phba->fc_prevDID == phba->fc_myDID) { + did_changed = 0; + } else { + did_changed = 1; + } + phba->fc_prevDID = phba->fc_myDID; + phba->num_disc_nodes = 0; + + /* Start Discovery state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0202 Start Discovery hba state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + /* If our did changed, we MUST do PLOGI */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(did_changed) + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + } + } + + /* First do ADISCs - if any */ + num_sent = lpfc_els_disc_adisc(phba); + + if(num_sent) + return; + + if ((phba->hba_state < LPFC_HBA_READY) && (!clear_la_pending)) { + /* If we get here, there is nothing to ADISC */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } + } else { + /* Next do PLOGIs - if any */ + num_sent = lpfc_els_disc_plogi(phba); + + if(num_sent) + return; + + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we + * were processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } + } + return; +} + +/* + * Ignore completion for all IOCBs on tx and txcmpl queue for ELS + * ring the match the sppecified nodelist. + */ +static void +lpfc_free_tx(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_sli_ring *pring; + struct lpfc_dmabuf *mp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + list_del(&iocb->list); + pring->txq_cnt--; + lpfc_els_free_iocb(phba, iocb); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + iocb->iocb_cmpl = NULL; + /* context2 = cmd, context2->next = rsp, context3 = + bpl */ + if (iocb->context2) { + /* Free the response IOCB before handling the + command. */ + + mp = (struct lpfc_dmabuf *) + (((struct lpfc_dmabuf *) (iocb->context2)) + ->list.next); + if (mp) { + /* Delay before releasing rsp buffer to + * give UNREG mbox a chance to take + * effect. + */ + list_add(&mp->list, + &phba->freebufList); + } + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context2)->virt, + ((struct lpfc_dmabuf *) + iocb->context2)->phys); + kfree(iocb->context2); + } + + if (iocb->context3) { + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context3)->virt, + ((struct lpfc_dmabuf *) + iocb->context3)->phys); + kfree(iocb->context3); + } + } + } + + return; +} + +void +lpfc_disc_flush_list(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + if (phba->fc_plogi_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + if (phba->fc_adisc_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_disc_timeout + * + * FUNCTION: Fibre Channel driver discovery timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_disc_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (!phba) { + return; + } + spin_lock_irqsave(phba->host->host_lock, iflag); + + psli = &phba->sli; + phba->fc_flag &= ~FC_DISC_TMO; + + /* hba_state is identically LPFC_LOCAL_CFG_LINK while waiting for FAN */ + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* FAN timeout */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0221 FAN timeout\n", + phba->brd_no); + + /* Forget about FAN, Start discovery by sending a FLOGI + * hba_state is identically LPFC_FLOGI while waiting for FLOGI + * cmpl + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + goto out; + } + + /* hba_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */ + if (phba->hba_state == LPFC_FLOGI) { + /* Initial FLOGI timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0222 Initial FLOGI timeout\n", + phba->brd_no); + + /* Assume no Fabric and go on with discovery. + * Check for outstanding ELS FLOGI to abort. + */ + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for + NameServer login */ + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* Timeout while waiting for NameServer login */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0223 Timeout while waiting for NameServer " + "login\n", phba->brd_no); + + /* Next look for NameServer ndlp */ + if ((ndlp = lpfc_findnode_did(phba, + NLP_SEARCH_ALL, NameServer_DID))) { + lpfc_nlp_remove(phba, ndlp); + } + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* Check for wait for NameServer Rsp timeout */ + if (phba->hba_state == LPFC_NS_QRY) { + /* NameServer Query timeout */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0224 NameServer Query timeout " + "Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + /* Try it one more time */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + phba->fc_ns_retry = 0; + } + + /* Nothing to authenticate, so CLEAR_LA right now */ + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } else { + /* Device Discovery completion error */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0226 Device Discovery " + "completion error\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + } + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + goto out; + } + + if (phba->hba_state == LPFC_DISC_AUTH) { + /* Node Authentication timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0227 Node Authentication timeout\n", + phba->brd_no); + lpfc_disc_flush_list(phba); + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } + } + goto out; + } + + if (phba->hba_state == LPFC_CLEAR_LA) { + /* CLEAR LA timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0228 CLEAR LA timeout\n", + phba->brd_no); +clrlaerr: + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + goto out; + } + + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + /* RSCN timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0231 RSCN timeout Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + lpfc_els_flush_rscn(phba); + lpfc_disc_flush_list(phba); + goto out; + } + +out: + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_scan_timeout + * + * FUNCTION: Fibre Channel driver scsi_scan_host timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_scan_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (!phba) { + return; + } + spin_lock_irqsave(phba->host->host_lock, iflag); + phba->fc_flag &= ~FC_SCSI_SCAN_TMO; + lpfc_discq_post_event(phba, NULL, NULL, LPFC_EVT_SCAN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +static void +lpfc_nodev_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + unsigned long iflag; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* All nodev timeouts are posted to discovery tasklet */ + lpfc_discq_post_event(phba, ndlp, NULL, LPFC_EVT_NODEV_TMO); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_find_target + * + * FUNCTION: Fibre Channel bus/target/LUN to struct lpfc_target lookup + * + * EXECUTION ENVIRONMENT: + * + * RETURNS: + * ptr to desired struct lpfc_target + */ +/*****************************************************************************/ +struct lpfc_target * +lpfc_find_target(struct lpfc_hba * phba, uint32_t tgt, + struct lpfc_nodelist *nlp) +{ + struct lpfc_target *targetp; + + /* search the mapped list for this target ID */ + if(!nlp) { + list_for_each_entry(nlp, &phba->fc_nlpmap_list, nlp_listp) { + if (tgt == nlp->nlp_sid) + break; + } + + if (&(nlp->nlp_listp) == &(phba->fc_nlpmap_list)) + return NULL; + } + + targetp = phba->device_queue_hash[tgt]; + + /* First see if the SCSI ID has an allocated struct lpfc_target */ + if (!targetp) { + targetp = kmalloc(sizeof (struct lpfc_target), GFP_ATOMIC); + if (!targetp) + return NULL; + + memset(targetp, 0, sizeof (struct lpfc_target)); + phba->device_queue_hash[tgt] = targetp; + targetp->scsi_id = tgt; + + /* Create SCSI Target */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0204 Create SCSI Target %d\n", + phba->brd_no, tgt); + } + + if (targetp->pnode == NULL) { + targetp->pnode = nlp; + nlp->nlp_Target = targetp; +#ifdef FC_TRANS_VER1 + /* + * This code does not apply to SLES9 since there is no + * starget defined in the midlayer. Additionally, + * dynamic target discovery to the midlayer is not + * supported yet. + */ + if(!(phba->fc_flag & FC_LOADING)) { + /* Add SCSI target / SCSI Hotplug if called + * after initial driver load. + */ + lpfc_target_add(phba, targetp); + } +#endif + } + else { + if(targetp->pnode != nlp) { + /* + * Get rid of the old nlp before updating + * targetp with the new one. + */ + lpfc_nlp_list(phba, targetp->pnode, NLP_NO_LIST); + targetp->pnode = nlp; + } + } + nlp->nlp_Target = targetp; + return (targetp); +} + +/* + * lpfc_set_failmask + * Set, or clear, failMask bits in struct lpfc_nodelist + */ +void +lpfc_set_failmask(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint32_t bitmask, uint32_t flag) +{ + uint32_t oldmask; + uint32_t changed; + + /* Failmask change on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0208 Failmask change on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_failMask, bitmask, flag); + + if (flag == LPFC_SET_BITMASK) { + oldmask = ndlp->nlp_failMask; + /* Set failMask event */ + ndlp->nlp_failMask |= bitmask; + if (oldmask != ndlp->nlp_failMask) { + changed = 1; + } else { + changed = 0; + } + + } else { + /* Clear failMask event */ + ndlp->nlp_failMask &= ~bitmask; + changed = 1; + } + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + /* Start issuing Fabric-Device Management Interface (FDMI) + * command to 0xfffffa (FDMI well known port) + */ + if (phba->cfg_fdmi_on == 1) { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + /* + * Delay issuing FDMI command if fdmi-on=2 + * (supporting RPA/hostnmae) + */ + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine looks up the ndlp hash + * table for the given RPI. If rpi found + * it return the node list pointer + * else return 0. + */ +struct lpfc_nodelist * +lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + while ((ret != 0) && (ret->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + return ret; +} + +/* + * This routine looks up the ndlp hash table for the + * given RPI. If rpi found it return the node list + * pointer else return 0 after deleting the entry + * from hash table. + */ +struct lpfc_nodelist * +lpfc_findnode_remove_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret, *temp;; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + if (ret == 0) + return NULL; + + if (ret->nlp_rpi == rpi) { + phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)] = + ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = NULL; + return ret; + } + + while ((ret->nlp_rpi_hash_next != 0) && + (ret->nlp_rpi_hash_next->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + + if (ret->nlp_rpi_hash_next != 0) { + temp = ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = temp->nlp_rpi_hash_next; + temp->nlp_rpi_hash_next = NULL; + return temp; + } else { + return NULL; + } +} + +/* + * This routine adds the node list entry to the + * ndlp hash table. + */ +void +lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi) +{ + + uint32_t index; + + index = LPFC_RPI_HASH_FUNC(rpi); + ndlp->nlp_rpi_hash_next = phba->fc_nlplookup[index]; + phba->fc_nlplookup[index] = ndlp; + return; +} + +void +lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint32_t did) +{ + memset(ndlp, 0, sizeof (struct lpfc_nodelist)); + init_timer(&ndlp->nlp_tmofunc); + ndlp->nlp_tmofunc.function = lpfc_nodev_timeout; + ndlp->nlp_tmofunc.data = (unsigned long)ndlp; + init_timer(&ndlp->nlp_delayfunc); + ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; + ndlp->nlp_delayfunc.data = (unsigned long)ndlp; + ndlp->nlp_DID = did; + ndlp->nlp_phba = phba; + return; +} + diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h new file mode 100644 index 000000000..b5c023a95 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -0,0 +1,2688 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hw.h 1.29 2004/11/18 17:28:05EST sf_support Exp $ + */ + +#ifndef _H_LPFC_HW +#define _H_LPFC_HW + +#define FDMI_DID ((uint32_t)0xfffffa) +#define NameServer_DID ((uint32_t)0xfffffc) +#define SCR_DID ((uint32_t)0xfffffd) +#define Fabric_DID ((uint32_t)0xfffffe) +#define Bcast_DID ((uint32_t)0xffffff) +#define Mask_DID ((uint32_t)0xffffff) +#define CT_DID_MASK ((uint32_t)0xffff00) +#define Fabric_DID_MASK ((uint32_t)0xfff000) +#define WELL_KNOWN_DID_MASK ((uint32_t)0xfffff0) + +#define PT2PT_LocalID ((uint32_t)1) +#define PT2PT_RemoteID ((uint32_t)2) + +#define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */ +#define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */ +#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */ +#define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */ + +#define LPFC_BUF_RING0 64 /* Number of buffers to post to RING + 0 */ + +#define FCELSSIZE 1024 /* maximum ELS transfer size */ + +#define LPFC_FCP_RING 0 /* ring 2 for FCP initiator commands */ +#define LPFC_IP_RING 1 /* ring 1 for IP commands */ +#define LPFC_ELS_RING 2 /* ring 0 for ELS commands */ +#define LPFC_FCP_NEXT_RING 3 + +#define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ +#define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ +#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 IP command ring entries */ +#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 IP response ring entries */ +#define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36 /* SLI-2 extra FCP cmd ring entries */ +#define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52 /* SLI-2 extra FCP rsp ring entries */ +#define SLI2_IOCB_CMD_R2_ENTRIES 20 /* SLI-2 ELS command ring entries */ +#define SLI2_IOCB_RSP_R2_ENTRIES 20 /* SLI-2 ELS response ring entries */ +#define SLI2_IOCB_CMD_R3_ENTRIES 0 +#define SLI2_IOCB_RSP_R3_ENTRIES 0 +#define SLI2_IOCB_CMD_R3XTRA_ENTRIES 24 +#define SLI2_IOCB_RSP_R3XTRA_ENTRIES 32 + +/* Common Transport structures and definitions */ + +union CtRevisionId { + /* Structure is in Big Endian format */ + struct { + uint32_t Revision:8; + uint32_t InId:24; + } bits; + uint32_t word; +}; + +union CtCommandResponse { + /* Structure is in Big Endian format */ + struct { + uint32_t CmdRsp:16; + uint32_t Size:16; + } bits; + uint32_t word; +}; + +struct lpfc_sli_ct_request { + /* Structure is in Big Endian format */ + union CtRevisionId RevisionId; + uint8_t FsType; + uint8_t FsSubType; + uint8_t Options; + uint8_t Rsrvd1; + union CtCommandResponse CommandResponse; + uint8_t Rsrvd2; + uint8_t ReasonCode; + uint8_t Explanation; + uint8_t VendorUnique; + + union { + uint32_t PortID; + struct gid { + uint8_t PortType; /* for GID_PT requests */ + uint8_t DomainScope; + uint8_t AreaScope; + uint8_t Fc4Type; /* for GID_FT requests */ + } gid; + struct rft { + uint32_t PortId; /* For RFT_ID requests */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:16; + uint32_t rsvd1:7; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd2:2; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd3:5; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rsvd0:16; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd1:7; + uint32_t rsvd3:5; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd2:2; +#endif + + uint32_t rsvd[7]; + } rft; + struct rnn { + uint32_t PortId; /* For RNN_ID requests */ + uint8_t wwnn[8]; + } rnn; + struct rsnn { /* For RSNN_ID requests */ + uint8_t wwnn[8]; + uint8_t len; + uint8_t symbname[255]; + } rsnn; + } un; +}; + +#define SLI_CT_REVISION 1 +#define GID_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 260) +#define RFT_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 228) +#define RNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 252) +#define RSNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request)) + +/* + * FsType Definitions + */ + +#define SLI_CT_MANAGEMENT_SERVICE 0xFA +#define SLI_CT_TIME_SERVICE 0xFB +#define SLI_CT_DIRECTORY_SERVICE 0xFC +#define SLI_CT_FABRIC_CONTROLLER_SERVICE 0xFD + +/* + * Directory Service Subtypes + */ + +#define SLI_CT_DIRECTORY_NAME_SERVER 0x02 + +/* + * Response Codes + */ + +#define SLI_CT_RESPONSE_FS_RJT 0x8001 +#define SLI_CT_RESPONSE_FS_ACC 0x8002 + +/* + * Reason Codes + */ + +#define SLI_CT_NO_ADDITIONAL_EXPL 0x0 +#define SLI_CT_INVALID_COMMAND 0x01 +#define SLI_CT_INVALID_VERSION 0x02 +#define SLI_CT_LOGICAL_ERROR 0x03 +#define SLI_CT_INVALID_IU_SIZE 0x04 +#define SLI_CT_LOGICAL_BUSY 0x05 +#define SLI_CT_PROTOCOL_ERROR 0x07 +#define SLI_CT_UNABLE_TO_PERFORM_REQ 0x09 +#define SLI_CT_REQ_NOT_SUPPORTED 0x0b +#define SLI_CT_HBA_INFO_NOT_REGISTERED 0x10 +#define SLI_CT_MULTIPLE_HBA_ATTR_OF_SAME_TYPE 0x11 +#define SLI_CT_INVALID_HBA_ATTR_BLOCK_LEN 0x12 +#define SLI_CT_HBA_ATTR_NOT_PRESENT 0x13 +#define SLI_CT_PORT_INFO_NOT_REGISTERED 0x20 +#define SLI_CT_MULTIPLE_PORT_ATTR_OF_SAME_TYPE 0x21 +#define SLI_CT_INVALID_PORT_ATTR_BLOCK_LEN 0x22 +#define SLI_CT_VENDOR_UNIQUE 0xff + +/* + * Name Server SLI_CT_UNABLE_TO_PERFORM_REQ Explanations + */ + +#define SLI_CT_NO_PORT_ID 0x01 +#define SLI_CT_NO_PORT_NAME 0x02 +#define SLI_CT_NO_NODE_NAME 0x03 +#define SLI_CT_NO_CLASS_OF_SERVICE 0x04 +#define SLI_CT_NO_IP_ADDRESS 0x05 +#define SLI_CT_NO_IPA 0x06 +#define SLI_CT_NO_FC4_TYPES 0x07 +#define SLI_CT_NO_SYMBOLIC_PORT_NAME 0x08 +#define SLI_CT_NO_SYMBOLIC_NODE_NAME 0x09 +#define SLI_CT_NO_PORT_TYPE 0x0A +#define SLI_CT_ACCESS_DENIED 0x10 +#define SLI_CT_INVALID_PORT_ID 0x11 +#define SLI_CT_DATABASE_EMPTY 0x12 + +/* + * Name Server Command Codes + */ + +#define SLI_CTNS_GA_NXT 0x0100 +#define SLI_CTNS_GPN_ID 0x0112 +#define SLI_CTNS_GNN_ID 0x0113 +#define SLI_CTNS_GCS_ID 0x0114 +#define SLI_CTNS_GFT_ID 0x0117 +#define SLI_CTNS_GSPN_ID 0x0118 +#define SLI_CTNS_GPT_ID 0x011A +#define SLI_CTNS_GID_PN 0x0121 +#define SLI_CTNS_GID_NN 0x0131 +#define SLI_CTNS_GIP_NN 0x0135 +#define SLI_CTNS_GIPA_NN 0x0136 +#define SLI_CTNS_GSNN_NN 0x0139 +#define SLI_CTNS_GNN_IP 0x0153 +#define SLI_CTNS_GIPA_IP 0x0156 +#define SLI_CTNS_GID_FT 0x0171 +#define SLI_CTNS_GID_PT 0x01A1 +#define SLI_CTNS_RPN_ID 0x0212 +#define SLI_CTNS_RNN_ID 0x0213 +#define SLI_CTNS_RCS_ID 0x0214 +#define SLI_CTNS_RFT_ID 0x0217 +#define SLI_CTNS_RSPN_ID 0x0218 +#define SLI_CTNS_RPT_ID 0x021A +#define SLI_CTNS_RIP_NN 0x0235 +#define SLI_CTNS_RIPA_NN 0x0236 +#define SLI_CTNS_RSNN_NN 0x0239 +#define SLI_CTNS_DA_ID 0x0300 + +/* + * Port Types + */ + +#define SLI_CTPT_N_PORT 0x01 +#define SLI_CTPT_NL_PORT 0x02 +#define SLI_CTPT_FNL_PORT 0x03 +#define SLI_CTPT_IP 0x04 +#define SLI_CTPT_FCP 0x08 +#define SLI_CTPT_NX_PORT 0x7F +#define SLI_CTPT_F_PORT 0x81 +#define SLI_CTPT_FL_PORT 0x82 +#define SLI_CTPT_E_PORT 0x84 + +#define SLI_CT_LAST_ENTRY 0x80000000 + +/* Fibre Channel Service Parameter definitions */ + +#define FC_PH_4_0 6 /* FC-PH version 4.0 */ +#define FC_PH_4_1 7 /* FC-PH version 4.1 */ +#define FC_PH_4_2 8 /* FC-PH version 4.2 */ +#define FC_PH_4_3 9 /* FC-PH version 4.3 */ + +#define FC_PH_LOW 8 /* Lowest supported FC-PH version */ +#define FC_PH_HIGH 9 /* Highest supported FC-PH version */ +#define FC_PH3 0x20 /* FC-PH-3 version */ + +#define FF_FRAME_SIZE 2048 + +struct lpfc_name { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ +#endif + +#define NAME_IEEE 0x1 /* IEEE name - nameType */ +#define NAME_IEEE_EXT 0x2 /* IEEE extended name */ +#define NAME_FC_TYPE 0x3 /* FC native name type */ +#define NAME_IP_TYPE 0x4 /* IP address */ +#define NAME_CCITT_TYPE 0xC +#define NAME_CCITT_GR_TYPE 0xE + uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ + uint8_t IEEE[6]; /* FC IEEE address */ +}; + +struct csp { + uint8_t fcphHigh; /* FC Word 0, byte 0 */ + uint8_t fcphLow; + uint8_t bbCreditMsb; + uint8_t bbCreditlsb; /* FC Word 0, byte 3 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ +#endif + + uint8_t bbRcvSizeMsb; /* Upper nibble is reserved */ + uint8_t bbRcvSizeLsb; /* FC Word 1, byte 3 */ + union { + struct { + uint8_t word2Reserved1; /* FC Word 2 byte 0 */ + + uint8_t totalConcurrSeq; /* FC Word 2 byte 1 */ + uint8_t roByCategoryMsb; /* FC Word 2 byte 2 */ + + uint8_t roByCategoryLsb; /* FC Word 2 byte 3 */ + } nPort; + uint32_t r_a_tov; /* R_A_TOV must be in B.E. format */ + } w2; + + uint32_t e_d_tov; /* E_D_TOV must be in B.E. format */ +}; + +struct class_parms { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t classValid:1; /* FC Word 0, bit 31 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t classValid:1; /* FC Word 0, bit 31 */ + +#endif + + uint8_t word0Reserved2; /* FC Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ +#endif + + uint8_t word0Reserved4; /* FC Word 0, bit 0: 7 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ +#endif + + uint8_t word1Reserved2; /* FC Word 1, bit 16:23 */ + uint8_t rcvDataSizeMsb; /* FC Word 1, bit 8:15 */ + uint8_t rcvDataSizeLsb; /* FC Word 1, bit 0: 7 */ + + uint8_t concurrentSeqMsb; /* FC Word 2, bit 24:31 */ + uint8_t concurrentSeqLsb; /* FC Word 2, bit 16:23 */ + uint8_t EeCreditSeqMsb; /* FC Word 2, bit 8:15 */ + uint8_t EeCreditSeqLsb; /* FC Word 2, bit 0: 7 */ + + uint8_t openSeqPerXchgMsb; /* FC Word 3, bit 24:31 */ + uint8_t openSeqPerXchgLsb; /* FC Word 3, bit 16:23 */ + uint8_t word3Reserved1; /* Fc Word 3, bit 8:15 */ + uint8_t word3Reserved2; /* Fc Word 3, bit 0: 7 */ +}; + +struct serv_parm { /* Structure is in Big Endian format */ + struct csp cmn; + struct lpfc_name portName; + struct lpfc_name nodeName; + struct class_parms cls1; + struct class_parms cls2; + struct class_parms cls3; + struct class_parms cls4; + uint8_t vendorVersion[16]; +}; + +/* + * Extended Link Service LS_COMMAND codes (Payload Word 0) + */ +#ifdef __BIG_ENDIAN_BITFIELD +#define ELS_CMD_MASK 0xffff0000 +#define ELS_RSP_MASK 0xff000000 +#define ELS_CMD_LS_RJT 0x01000000 +#define ELS_CMD_ACC 0x02000000 +#define ELS_CMD_PLOGI 0x03000000 +#define ELS_CMD_FLOGI 0x04000000 +#define ELS_CMD_LOGO 0x05000000 +#define ELS_CMD_ABTX 0x06000000 +#define ELS_CMD_RCS 0x07000000 +#define ELS_CMD_RES 0x08000000 +#define ELS_CMD_RSS 0x09000000 +#define ELS_CMD_RSI 0x0A000000 +#define ELS_CMD_ESTS 0x0B000000 +#define ELS_CMD_ESTC 0x0C000000 +#define ELS_CMD_ADVC 0x0D000000 +#define ELS_CMD_RTV 0x0E000000 +#define ELS_CMD_RLS 0x0F000000 +#define ELS_CMD_ECHO 0x10000000 +#define ELS_CMD_TEST 0x11000000 +#define ELS_CMD_RRQ 0x12000000 +#define ELS_CMD_PRLI 0x20100014 +#define ELS_CMD_PRLO 0x21100014 +#define ELS_CMD_PDISC 0x50000000 +#define ELS_CMD_FDISC 0x51000000 +#define ELS_CMD_ADISC 0x52000000 +#define ELS_CMD_FARP 0x54000000 +#define ELS_CMD_FARPR 0x55000000 +#define ELS_CMD_FAN 0x60000000 +#define ELS_CMD_RSCN 0x61040000 +#define ELS_CMD_SCR 0x62000000 +#define ELS_CMD_RNID 0x78000000 +#else /* __LITTLE_ENDIAN_BITFIELD */ +#define ELS_CMD_MASK 0xffff +#define ELS_RSP_MASK 0xff +#define ELS_CMD_LS_RJT 0x01 +#define ELS_CMD_ACC 0x02 +#define ELS_CMD_PLOGI 0x03 +#define ELS_CMD_FLOGI 0x04 +#define ELS_CMD_LOGO 0x05 +#define ELS_CMD_ABTX 0x06 +#define ELS_CMD_RCS 0x07 +#define ELS_CMD_RES 0x08 +#define ELS_CMD_RSS 0x09 +#define ELS_CMD_RSI 0x0A +#define ELS_CMD_ESTS 0x0B +#define ELS_CMD_ESTC 0x0C +#define ELS_CMD_ADVC 0x0D +#define ELS_CMD_RTV 0x0E +#define ELS_CMD_RLS 0x0F +#define ELS_CMD_ECHO 0x10 +#define ELS_CMD_TEST 0x11 +#define ELS_CMD_RRQ 0x12 +#define ELS_CMD_PRLI 0x14001020 +#define ELS_CMD_PRLO 0x14001021 +#define ELS_CMD_PDISC 0x50 +#define ELS_CMD_FDISC 0x51 +#define ELS_CMD_ADISC 0x52 +#define ELS_CMD_FARP 0x54 +#define ELS_CMD_FARPR 0x55 +#define ELS_CMD_FAN 0x60 +#define ELS_CMD_RSCN 0x0461 +#define ELS_CMD_SCR 0x62 +#define ELS_CMD_RNID 0x78 +#endif + +/* + * LS_RJT Payload Definition + */ + +struct ls_rjt { /* Structure is in Big Endian format */ + union { + uint32_t lsRjtError; + struct { + uint8_t lsRjtRsvd0; /* FC Word 0, bit 24:31 */ + + uint8_t lsRjtRsnCode; /* FC Word 0, bit 16:23 */ + /* LS_RJT reason codes */ +#define LSRJT_INVALID_CMD 0x01 +#define LSRJT_LOGICAL_ERR 0x03 +#define LSRJT_LOGICAL_BSY 0x05 +#define LSRJT_PROTOCOL_ERR 0x07 +#define LSRJT_UNABLE_TPC 0x09 /* Unable to perform command */ +#define LSRJT_CMD_UNSUPPORTED 0x0B +#define LSRJT_VENDOR_UNIQUE 0xFF /* See Byte 3 */ + + uint8_t lsRjtRsnCodeExp; /* FC Word 0, bit 8:15 */ + /* LS_RJT reason explanation */ +#define LSEXP_NOTHING_MORE 0x00 +#define LSEXP_SPARM_OPTIONS 0x01 +#define LSEXP_SPARM_ICTL 0x03 +#define LSEXP_SPARM_RCTL 0x05 +#define LSEXP_SPARM_RCV_SIZE 0x07 +#define LSEXP_SPARM_CONCUR_SEQ 0x09 +#define LSEXP_SPARM_CREDIT 0x0B +#define LSEXP_INVALID_PNAME 0x0D +#define LSEXP_INVALID_NNAME 0x0E +#define LSEXP_INVALID_CSP 0x0F +#define LSEXP_INVALID_ASSOC_HDR 0x11 +#define LSEXP_ASSOC_HDR_REQ 0x13 +#define LSEXP_INVALID_O_SID 0x15 +#define LSEXP_INVALID_OX_RX 0x17 +#define LSEXP_CMD_IN_PROGRESS 0x19 +#define LSEXP_INVALID_NPORT_ID 0x1F +#define LSEXP_INVALID_SEQ_ID 0x21 +#define LSEXP_INVALID_XCHG 0x23 +#define LSEXP_INACTIVE_XCHG 0x25 +#define LSEXP_RQ_REQUIRED 0x27 +#define LSEXP_OUT_OF_RESOURCE 0x29 +#define LSEXP_CANT_GIVE_DATA 0x2A +#define LSEXP_REQ_UNSUPPORTED 0x2C + uint8_t vendorUnique; /* FC Word 0, bit 0: 7 */ + } b; + } un; +}; + +/* + * N_Port Login (FLOGO/PLOGO Request) Payload Definition + */ + +typedef struct _LOGO { /* Structure is in Big Endian format */ + union { + uint32_t nPortId32; /* Access nPortId as a word */ + struct { + uint8_t word1Reserved1; /* FC Word 1, bit 31:24 */ + uint8_t nPortIdByte0; /* N_port ID bit 16:23 */ + uint8_t nPortIdByte1; /* N_port ID bit 8:15 */ + uint8_t nPortIdByte2; /* N_port ID bit 0: 7 */ + } b; + } un; + struct lpfc_name portName; /* N_port name field */ +} LOGO; + +/* + * FCP Login (PRLI Request / ACC) Payload Definition + */ + +#define PRLX_PAGE_LEN 0x10 +#define TPRLO_PAGE_LEN 0x14 + +typedef struct _PRLI { /* Structure is in Big Endian format */ + uint8_t prliType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLI_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + + /* ACC = imagePairEstablished */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + /* ACC = imagePairEstablished */ +#endif + +#define PRLI_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLI_NO_RESOURCES 0x2 +#define PRLI_INIT_INCOMPLETE 0x3 +#define PRLI_NO_SUCH_PA 0x4 +#define PRLI_PREDEF_CONFIG 0x5 +#define PRLI_PARTIAL_SUCCESS 0x6 +#define PRLI_INVALID_PAGE_CNT 0x7 + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint8_t word3Reserved1; /* FC Parm Word 3, bit 24:31 */ + uint8_t word3Reserved2; /* FC Parm Word 3, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ +#endif +} PRLI; + +/* + * FCP Logout (PRLO Request / ACC) Payload Definition + */ + +typedef struct _PRLO { /* Structure is in Big Endian format */ + uint8_t prloType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLO_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ +#endif + +#define PRLO_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLO_NO_SUCH_IMAGE 0x4 +#define PRLO_INVALID_PAGE_CNT 0x7 + + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint32_t word3Reserved1; /* FC Parm Word 3, bit 0:31 */ +} PRLO; + +typedef struct _ADISC { /* Structure is in Big Endian format */ + uint32_t hardAL_PA; + struct lpfc_name portName; + struct lpfc_name nodeName; + uint32_t DID; +} ADISC; + +typedef struct _FARP { /* Structure is in Big Endian format */ + uint32_t Mflags:8; + uint32_t Odid:24; +#define FARP_NO_ACTION 0 /* FARP information enclosed, no + action */ +#define FARP_MATCH_PORT 0x1 /* Match on Responder Port Name */ +#define FARP_MATCH_NODE 0x2 /* Match on Responder Node Name */ +#define FARP_MATCH_IP 0x4 /* Match on IP address, not supported */ +#define FARP_MATCH_IPV4 0x5 /* Match on IPV4 address, not + supported */ +#define FARP_MATCH_IPV6 0x6 /* Match on IPV6 address, not + supported */ + uint32_t Rflags:8; + uint32_t Rdid:24; +#define FARP_REQUEST_PLOGI 0x1 /* Request for PLOGI */ +#define FARP_REQUEST_FARPR 0x2 /* Request for FARP Response */ + struct lpfc_name OportName; + struct lpfc_name OnodeName; + struct lpfc_name RportName; + struct lpfc_name RnodeName; + uint8_t Oipaddr[16]; + uint8_t Ripaddr[16]; +} FARP; + +typedef struct _FAN { /* Structure is in Big Endian format */ + uint32_t Fdid; + struct lpfc_name FportName; + struct lpfc_name FnodeName; +} FAN; + +typedef struct _SCR { /* Structure is in Big Endian format */ + uint8_t resvd1; + uint8_t resvd2; + uint8_t resvd3; + uint8_t Function; +#define SCR_FUNC_FABRIC 0x01 +#define SCR_FUNC_NPORT 0x02 +#define SCR_FUNC_FULL 0x03 +#define SCR_CLEAR 0xff +} SCR; + +typedef struct _RNID_TOP_DISC { + struct lpfc_name portName; + uint8_t resvd[8]; + uint32_t unitType; +#define RNID_HBA 0x7 +#define RNID_HOST 0xa +#define RNID_DRIVER 0xd + uint32_t physPort; + uint32_t attachedNodes; + uint16_t ipVersion; +#define RNID_IPV4 0x1 +#define RNID_IPV6 0x2 + uint16_t UDPport; + uint8_t ipAddr[16]; + uint16_t resvd1; + uint16_t flags; +#define RNID_TD_SUPPORT 0x1 +#define RNID_LP_VALID 0x2 +} RNID_TOP_DISC; + +typedef struct _RNID { /* Structure is in Big Endian format */ + uint8_t Format; +#define RNID_TOPOLOGY_DISC 0xdf + uint8_t CommonLen; + uint8_t resvd1; + uint8_t SpecificLen; + struct lpfc_name portName; + struct lpfc_name nodeName; + union { + RNID_TOP_DISC topologyDisc; /* topology disc (0xdf) */ + } un; +} RNID; + +typedef struct _RRQ { /* Structure is in Big Endian format */ + uint32_t SID; + uint16_t Oxid; + uint16_t Rxid; + uint8_t resv[32]; /* optional association hdr */ +} RRQ; + +/* This is used for RSCN command */ +typedef struct _D_ID { /* Structure is in Big Endian format */ + union { + uint32_t word; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t resv; + uint8_t domain; + uint8_t area; + uint8_t id; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t id; + uint8_t area; + uint8_t domain; + uint8_t resv; +#endif + } b; + } un; +} D_ID; + +/* + * Structure to define all ELS Payload types + */ + +typedef struct _ELS_PKT { /* Structure is in Big Endian format */ + uint8_t elsCode; /* FC Word 0, bit 24:31 */ + uint8_t elsByte1; + uint8_t elsByte2; + uint8_t elsByte3; + union { + struct ls_rjt lsRjt; /* Payload for LS_RJT ELS response */ + struct serv_parm logi; /* Payload for PLOGI/FLOGI/PDISC/ACC */ + LOGO logo; /* Payload for PLOGO/FLOGO/ACC */ + PRLI prli; /* Payload for PRLI/ACC */ + PRLO prlo; /* Payload for PRLO/ACC */ + ADISC adisc; /* Payload for ADISC/ACC */ + FARP farp; /* Payload for FARP/ACC */ + FAN fan; /* Payload for FAN */ + SCR scr; /* Payload for SCR/ACC */ + RRQ rrq; /* Payload for RRQ */ + RNID rnid; /* Payload for RNID */ + uint8_t pad[128 - 4]; /* Pad out to payload of 128 bytes */ + } un; +} ELS_PKT; + +/* + * FDMI + * HBA MAnagement Operations Command Codes + */ +#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */ +#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */ +#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */ +#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */ +#define SLI_MGMT_RHBA 0x200 /* Register HBA */ +#define SLI_MGMT_RHAT 0x201 /* Register HBA atttributes */ +#define SLI_MGMT_RPRT 0x210 /* Register Port */ +#define SLI_MGMT_RPA 0x211 /* Register Port attributes */ +#define SLI_MGMT_DHBA 0x300 /* De-register HBA */ +#define SLI_MGMT_DPRT 0x310 /* De-register Port */ + +/* + * Management Service Subtypes + */ +#define SLI_CT_FDMI_Subtypes 0x10 + +/* + * HBA Management Service Reject Code + */ +#define REJECT_CODE 0x9 /* Unable to perform command request */ + +/* + * HBA Management Service Reject Reason Code + * Please refer to the Reason Codes above + */ + +/* + * HBA Attribute Types + */ +#define NODE_NAME 0x1 +#define MANUFACTURER 0x2 +#define SERIAL_NUMBER 0x3 +#define MODEL 0x4 +#define MODEL_DESCRIPTION 0x5 +#define HARDWARE_VERSION 0x6 +#define DRIVER_VERSION 0x7 +#define OPTION_ROM_VERSION 0x8 +#define FIRMWARE_VERSION 0x9 +#define OS_NAME_VERSION 0xa +#define MAX_CT_PAYLOAD_LEN 0xb + +/* + * Port Attrubute Types + */ +#define SUPPORTED_FC4_TYPES 0x1 +#define SUPPORTED_SPEED 0x2 +#define PORT_SPEED 0x3 +#define MAX_FRAME_SIZE 0x4 +#define OS_DEVICE_NAME 0x5 +#define HOST_NAME 0x6 + +union AttributesDef { + /* Structure is in Big Endian format */ + struct { + uint32_t AttrType:16; + uint32_t AttrLen:16; + } bits; + uint32_t word; +}; + + +/* + * HBA Attribute Entry (8 - 260 bytes) + */ +typedef struct { + union AttributesDef ad; + union { + uint32_t VendorSpecific; + uint8_t Manufacturer[64]; + uint8_t SerialNumber[64]; + uint8_t Model[256]; + uint8_t ModelDescription[256]; + uint8_t HardwareVersion[256]; + uint8_t DriverVersion[256]; + uint8_t OptionROMVersion[256]; + uint8_t FirmwareVersion[256]; + struct lpfc_name NodeName; + uint8_t SupportFC4Types[32]; + uint32_t SupportSpeed; + uint32_t PortSpeed; + uint32_t MaxFrameSize; + uint8_t OsDeviceName[256]; + uint8_t OsNameVersion[256]; + uint32_t MaxCTPayloadLen; + uint8_t HostName[256]; + } un; +} ATTRIBUTE_ENTRY; + +/* + * HBA Attribute Block + */ +typedef struct { + uint32_t EntryCnt; /* Number of HBA attribute entries */ + ATTRIBUTE_ENTRY Entry; /* Variable-length array */ +} ATTRIBUTE_BLOCK; + +/* + * Port Entry + */ +typedef struct { + struct lpfc_name PortName; +} PORT_ENTRY; + +/* + * HBA Identifier + */ +typedef struct { + struct lpfc_name PortName; +} HBA_IDENTIFIER; + +/* + * Registered Port List Format + */ +typedef struct { + uint32_t EntryCnt; + PORT_ENTRY pe; /* Variable-length array */ +} REG_PORT_LIST; + +/* + * Register HBA(RHBA) + */ +typedef struct { + HBA_IDENTIFIER hi; + REG_PORT_LIST rpl; /* variable-length array */ +/* ATTRIBUTE_BLOCK ab; */ +} REG_HBA; + +/* + * Register HBA Attributes (RHAT) + */ +typedef struct { + struct lpfc_name HBA_PortName; + ATTRIBUTE_BLOCK ab; +} REG_HBA_ATTRIBUTE; + +/* + * Register Port Attributes (RPA) + */ +typedef struct { + struct lpfc_name PortName; + ATTRIBUTE_BLOCK ab; +} REG_PORT_ATTRIBUTE; + +/* + * Get Registered HBA List (GRHL) Accept Payload Format + */ +typedef struct { + uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */ + struct lpfc_name HBA_PortName; /* Variable-length array */ +} GRHL_ACC_PAYLOAD; + +/* + * Get Registered Port List (GRPL) Accept Payload Format + */ +typedef struct { + uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */ + PORT_ENTRY Reg_Port_Entry[1]; /* Variable-length array */ +} GRPL_ACC_PAYLOAD; + +/* + * Get Port Attributes (GPAT) Accept Payload Format + */ + +typedef struct { + ATTRIBUTE_BLOCK pab; +} GPAT_ACC_PAYLOAD; + + +/* + * Begin HBA configuration parameters. + * The PCI configuration register BAR assignments are: + * BAR0, offset 0x10 - SLIM base memory address + * BAR1, offset 0x14 - SLIM base memory high address + * BAR2, offset 0x18 - REGISTER base memory address + * BAR3, offset 0x1c - REGISTER base memory high address + * BAR4, offset 0x20 - BIU I/O registers + * BAR5, offset 0x24 - REGISTER base io high address + */ + +/* Number of rings currently used and available. */ +#define MAX_CONFIGURED_RINGS 3 +#define MAX_RINGS 4 + +/* IOCB / Mailbox is owned by FireFly */ +#define OWN_CHIP 1 + +/* IOCB / Mailbox is owned by Host */ +#define OWN_HOST 0 + +/* Number of 4-byte words in an IOCB. */ +#define IOCB_WORD_SZ 8 + +/* defines for type field in fc header */ +#define FC_ELS_DATA 0x1 +#define FC_LLC_SNAP 0x5 +#define FC_FCP_DATA 0x8 +#define FC_COMMON_TRANSPORT_ULP 0x20 + +/* defines for rctl field in fc header */ +#define FC_DEV_DATA 0x0 +#define FC_UNSOL_CTL 0x2 +#define FC_SOL_CTL 0x3 +#define FC_UNSOL_DATA 0x4 +#define FC_FCP_CMND 0x6 +#define FC_ELS_REQ 0x22 +#define FC_ELS_RSP 0x23 + +/* network headers for Dfctl field */ +#define FC_NET_HDR 0x20 + +/* Start FireFly Register definitions */ +#define PCI_VENDOR_ID_EMULEX 0x10df +#define PCI_DEVICE_ID_FIREFLY 0x1ae5 +#define PCI_DEVICE_ID_SUPERFLY 0xf700 +#define PCI_DEVICE_ID_DRAGONFLY 0xf800 +#define PCI_DEVICE_ID_RFLY 0xf095 +#define PCI_DEVICE_ID_PFLY 0xf098 +#define PCI_DEVICE_ID_TFLY 0xf0a5 +#define PCI_DEVICE_ID_CENTAUR 0xf900 +#define PCI_DEVICE_ID_PEGASUS 0xf980 +#define PCI_DEVICE_ID_THOR 0xfa00 +#define PCI_DEVICE_ID_VIPER 0xfb00 +#define PCI_DEVICE_ID_HELIOS 0xfd00 +#define PCI_DEVICE_ID_BMID 0xf0d5 +#define PCI_DEVICE_ID_BSMB 0xf0d1 +#define PCI_DEVICE_ID_ZEPHYR 0xfe00 +#define PCI_DEVICE_ID_ZMID 0xf0e5 +#define PCI_DEVICE_ID_ZSMB 0xf0e1 +#define PCI_DEVICE_ID_LP101 0xf0a1 + +#define JEDEC_ID_ADDRESS 0x0080001c +#define FIREFLY_JEDEC_ID 0x1ACC +#define SUPERFLY_JEDEC_ID 0x0020 +#define DRAGONFLY_JEDEC_ID 0x0021 +#define DRAGONFLY_V2_JEDEC_ID 0x0025 +#define CENTAUR_2G_JEDEC_ID 0x0026 +#define CENTAUR_1G_JEDEC_ID 0x0028 +#define PEGASUS_ORION_JEDEC_ID 0x0036 +#define PEGASUS_JEDEC_ID 0x0038 +#define THOR_JEDEC_ID 0x0012 +#define HELIOS_JEDEC_ID 0x0364 +#define ZEPHYR_JEDEC_ID 0x0577 +#define VIPER_JEDEC_ID 0x4838 + +#define JEDEC_ID_MASK 0x0FFFF000 +#define JEDEC_ID_SHIFT 12 +#define FC_JEDEC_ID(id) ((id & JEDEC_ID_MASK) >> JEDEC_ID_SHIFT) + +typedef struct { /* FireFly BIU registers */ + uint32_t hostAtt; /* See definitions for Host Attention + register */ + uint32_t chipAtt; /* See definitions for Chip Attention + register */ + uint32_t hostStatus; /* See definitions for Host Status register */ + uint32_t hostControl; /* See definitions for Host Control register */ + uint32_t buiConfig; /* See definitions for BIU configuration + register */ +} FF_REGS; + +/* IO Register size in bytes */ +#define FF_REG_AREA_SIZE 256 + +/* Host Attention Register */ + +#define HA_REG_OFFSET 0 /* Word offset from register base address */ + +#define HA_R0RE_REQ 0x00000001 /* Bit 0 */ +#define HA_R0CE_RSP 0x00000002 /* Bit 1 */ +#define HA_R0ATT 0x00000008 /* Bit 3 */ +#define HA_R1RE_REQ 0x00000010 /* Bit 4 */ +#define HA_R1CE_RSP 0x00000020 /* Bit 5 */ +#define HA_R1ATT 0x00000080 /* Bit 7 */ +#define HA_R2RE_REQ 0x00000100 /* Bit 8 */ +#define HA_R2CE_RSP 0x00000200 /* Bit 9 */ +#define HA_R2ATT 0x00000800 /* Bit 11 */ +#define HA_R3RE_REQ 0x00001000 /* Bit 12 */ +#define HA_R3CE_RSP 0x00002000 /* Bit 13 */ +#define HA_R3ATT 0x00008000 /* Bit 15 */ +#define HA_LATT 0x20000000 /* Bit 29 */ +#define HA_MBATT 0x40000000 /* Bit 30 */ +#define HA_ERATT 0x80000000 /* Bit 31 */ + +#define HA_RXRE_REQ 0x00000001 /* Bit 0 */ +#define HA_RXCE_RSP 0x00000002 /* Bit 1 */ +#define HA_RXATT 0x00000008 /* Bit 3 */ +#define HA_RXMASK 0x0000000f + +/* Chip Attention Register */ + +#define CA_REG_OFFSET 1 /* Word offset from register base address */ + +#define CA_R0CE_REQ 0x00000001 /* Bit 0 */ +#define CA_R0RE_RSP 0x00000002 /* Bit 1 */ +#define CA_R0ATT 0x00000008 /* Bit 3 */ +#define CA_R1CE_REQ 0x00000010 /* Bit 4 */ +#define CA_R1RE_RSP 0x00000020 /* Bit 5 */ +#define CA_R1ATT 0x00000080 /* Bit 7 */ +#define CA_R2CE_REQ 0x00000100 /* Bit 8 */ +#define CA_R2RE_RSP 0x00000200 /* Bit 9 */ +#define CA_R2ATT 0x00000800 /* Bit 11 */ +#define CA_R3CE_REQ 0x00001000 /* Bit 12 */ +#define CA_R3RE_RSP 0x00002000 /* Bit 13 */ +#define CA_R3ATT 0x00008000 /* Bit 15 */ +#define CA_MBATT 0x40000000 /* Bit 30 */ + +/* Host Status Register */ + +#define HS_REG_OFFSET 2 /* Word offset from register base address */ + +#define HS_MBRDY 0x00400000 /* Bit 22 */ +#define HS_FFRDY 0x00800000 /* Bit 23 */ +#define HS_FFER8 0x01000000 /* Bit 24 */ +#define HS_FFER7 0x02000000 /* Bit 25 */ +#define HS_FFER6 0x04000000 /* Bit 26 */ +#define HS_FFER5 0x08000000 /* Bit 27 */ +#define HS_FFER4 0x10000000 /* Bit 28 */ +#define HS_FFER3 0x20000000 /* Bit 29 */ +#define HS_FFER2 0x40000000 /* Bit 30 */ +#define HS_FFER1 0x80000000 /* Bit 31 */ +#define HS_FFERM 0xFF000000 /* Mask for error bits 31:24 */ + +/* Host Control Register */ + +#define HC_REG_OFFSET 3 /* Word offset from register base address */ + +#define HC_MBINT_ENA 0x00000001 /* Bit 0 */ +#define HC_R0INT_ENA 0x00000002 /* Bit 1 */ +#define HC_R1INT_ENA 0x00000004 /* Bit 2 */ +#define HC_R2INT_ENA 0x00000008 /* Bit 3 */ +#define HC_R3INT_ENA 0x00000010 /* Bit 4 */ +#define HC_INITHBI 0x02000000 /* Bit 25 */ +#define HC_INITMB 0x04000000 /* Bit 26 */ +#define HC_INITFF 0x08000000 /* Bit 27 */ +#define HC_LAINT_ENA 0x20000000 /* Bit 29 */ +#define HC_ERINT_ENA 0x80000000 /* Bit 31 */ + +/* Mailbox Commands */ +#define MBX_SHUTDOWN 0x00 /* terminate testing */ +#define MBX_LOAD_SM 0x01 +#define MBX_READ_NV 0x02 +#define MBX_WRITE_NV 0x03 +#define MBX_RUN_BIU_DIAG 0x04 +#define MBX_INIT_LINK 0x05 +#define MBX_DOWN_LINK 0x06 +#define MBX_CONFIG_LINK 0x07 +#define MBX_CONFIG_RING 0x09 +#define MBX_RESET_RING 0x0A +#define MBX_READ_CONFIG 0x0B +#define MBX_READ_RCONFIG 0x0C +#define MBX_READ_SPARM 0x0D +#define MBX_READ_STATUS 0x0E +#define MBX_READ_RPI 0x0F +#define MBX_READ_XRI 0x10 +#define MBX_READ_REV 0x11 +#define MBX_READ_LNK_STAT 0x12 +#define MBX_REG_LOGIN 0x13 +#define MBX_UNREG_LOGIN 0x14 +#define MBX_READ_LA 0x15 +#define MBX_CLEAR_LA 0x16 +#define MBX_DUMP_MEMORY 0x17 +#define MBX_DUMP_CONTEXT 0x18 +#define MBX_RUN_DIAGS 0x19 +#define MBX_RESTART 0x1A +#define MBX_UPDATE_CFG 0x1B +#define MBX_DOWN_LOAD 0x1C +#define MBX_DEL_LD_ENTRY 0x1D +#define MBX_RUN_PROGRAM 0x1E +#define MBX_SET_MASK 0x20 +#define MBX_SET_SLIM 0x21 +#define MBX_UNREG_D_ID 0x23 +#define MBX_CONFIG_FARP 0x25 + +#define MBX_LOAD_AREA 0x81 +#define MBX_RUN_BIU_DIAG64 0x84 +#define MBX_CONFIG_PORT 0x88 +#define MBX_READ_SPARM64 0x8D +#define MBX_READ_RPI64 0x8F +#define MBX_REG_LOGIN64 0x93 +#define MBX_READ_LA64 0x95 + +#define MBX_FLASH_WR_ULA 0x98 +#define MBX_SET_DEBUG 0x99 +#define MBX_LOAD_EXP_ROM 0x9C + +#define MBX_MAX_CMDS 0x9D +#define MBX_SLI2_CMD_MASK 0x80 + +/* IOCB Commands */ + +#define CMD_RCV_SEQUENCE_CX 0x01 +#define CMD_XMIT_SEQUENCE_CR 0x02 +#define CMD_XMIT_SEQUENCE_CX 0x03 +#define CMD_XMIT_BCAST_CN 0x04 +#define CMD_XMIT_BCAST_CX 0x05 +#define CMD_QUE_RING_BUF_CN 0x06 +#define CMD_QUE_XRI_BUF_CX 0x07 +#define CMD_IOCB_CONTINUE_CN 0x08 +#define CMD_RET_XRI_BUF_CX 0x09 +#define CMD_ELS_REQUEST_CR 0x0A +#define CMD_ELS_REQUEST_CX 0x0B +#define CMD_RCV_ELS_REQ_CX 0x0D +#define CMD_ABORT_XRI_CN 0x0E +#define CMD_ABORT_XRI_CX 0x0F +#define CMD_CLOSE_XRI_CN 0x10 +#define CMD_CLOSE_XRI_CX 0x11 +#define CMD_CREATE_XRI_CR 0x12 +#define CMD_CREATE_XRI_CX 0x13 +#define CMD_GET_RPI_CN 0x14 +#define CMD_XMIT_ELS_RSP_CX 0x15 +#define CMD_GET_RPI_CR 0x16 +#define CMD_XRI_ABORTED_CX 0x17 +#define CMD_FCP_IWRITE_CR 0x18 +#define CMD_FCP_IWRITE_CX 0x19 +#define CMD_FCP_IREAD_CR 0x1A +#define CMD_FCP_IREAD_CX 0x1B +#define CMD_FCP_ICMND_CR 0x1C +#define CMD_FCP_ICMND_CX 0x1D + +#define CMD_ADAPTER_MSG 0x20 +#define CMD_ADAPTER_DUMP 0x22 + +/* SLI_2 IOCB Command Set */ + +#define CMD_RCV_SEQUENCE64_CX 0x81 +#define CMD_XMIT_SEQUENCE64_CR 0x82 +#define CMD_XMIT_SEQUENCE64_CX 0x83 +#define CMD_XMIT_BCAST64_CN 0x84 +#define CMD_XMIT_BCAST64_CX 0x85 +#define CMD_QUE_RING_BUF64_CN 0x86 +#define CMD_QUE_XRI_BUF64_CX 0x87 +#define CMD_IOCB_CONTINUE64_CN 0x88 +#define CMD_RET_XRI_BUF64_CX 0x89 +#define CMD_ELS_REQUEST64_CR 0x8A +#define CMD_ELS_REQUEST64_CX 0x8B +#define CMD_ABORT_MXRI64_CN 0x8C +#define CMD_RCV_ELS_REQ64_CX 0x8D +#define CMD_XMIT_ELS_RSP64_CX 0x95 +#define CMD_FCP_IWRITE64_CR 0x98 +#define CMD_FCP_IWRITE64_CX 0x99 +#define CMD_FCP_IREAD64_CR 0x9A +#define CMD_FCP_IREAD64_CX 0x9B +#define CMD_FCP_ICMND64_CR 0x9C +#define CMD_FCP_ICMND64_CX 0x9D + +#define CMD_GEN_REQUEST64_CR 0xC2 +#define CMD_GEN_REQUEST64_CX 0xC3 + +#define CMD_MAX_IOCB_CMD 0xE6 +#define CMD_IOCB_MASK 0xff + +#define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG + iocb */ +#define LPFC_MAX_ADPTMSG 32 /* max msg data */ +/* + * Define Status + */ +#define MBX_SUCCESS 0 +#define MBXERR_NUM_RINGS 1 +#define MBXERR_NUM_IOCBS 2 +#define MBXERR_IOCBS_EXCEEDED 3 +#define MBXERR_BAD_RING_NUMBER 4 +#define MBXERR_MASK_ENTRIES_RANGE 5 +#define MBXERR_MASKS_EXCEEDED 6 +#define MBXERR_BAD_PROFILE 7 +#define MBXERR_BAD_DEF_CLASS 8 +#define MBXERR_BAD_MAX_RESPONDER 9 +#define MBXERR_BAD_MAX_ORIGINATOR 10 +#define MBXERR_RPI_REGISTERED 11 +#define MBXERR_RPI_FULL 12 +#define MBXERR_NO_RESOURCES 13 +#define MBXERR_BAD_RCV_LENGTH 14 +#define MBXERR_DMA_ERROR 15 +#define MBXERR_ERROR 16 +#define MBX_NOT_FINISHED 255 + +#define MBX_BUSY 0xffffff /* Attempted cmd to busy Mailbox */ +#define MBX_TIMEOUT 0xfffffe /* time-out expired waiting for */ + +/* + * Begin Structure Definitions for Mailbox Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t tval; + uint8_t tmask; + uint8_t rval; + uint8_t rmask; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rmask; + uint8_t rval; + uint8_t tmask; + uint8_t tval; +#endif +} RR_REG; + +struct ulp_bde { + uint32_t bdeAddress; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeReserved:4; + uint32_t bdeAddrHigh:4; + uint32_t bdeSize:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; + uint32_t bdeAddrHigh:4; + uint32_t bdeReserved:4; +#endif +}; + +struct ulp_bde64 { /* SLI-2 */ + union ULP_BDE_TUS { + uint32_t w; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ +#endif + +#define BUFF_USE_RSVD 0x01 /* bdeFlags */ +#define BUFF_USE_INTRPT 0x02 /* Not Implemented with LP6000 */ +#define BUFF_USE_CMND 0x04 /* Optional, 1=cmd/rsp 0=data buffer */ +#define BUFF_USE_RCV 0x08 /* "" "", 1=rcv buffer, 0=xmit + buffer */ +#define BUFF_TYPE_32BIT 0x10 /* "" "", 1=32 bit addr 0=64 bit + addr */ +#define BUFF_TYPE_SPECIAL 0x20 /* Not Implemented with LP6000 */ +#define BUFF_TYPE_BDL 0x40 /* Optional, may be set in BDL */ +#define BUFF_TYPE_INVALID 0x80 /* "" "" */ + } f; + } tus; + uint32_t addrLow; + uint32_t addrHigh; +}; +#define BDE64_SIZE_WORD 0 +#define BPL64_SIZE_WORD 0x40 + +typedef struct ULP_BDL { /* SLI-2 */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDL Flags */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ + uint32_t bdeFlags:8; /* BDL Flags */ +#endif + + uint32_t addrLow; /* Address 0:31 */ + uint32_t addrHigh; /* Address 32:63 */ + uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */ +} ULP_BDL; + +/* Structure for MB Command LOAD_SM and DOWN_LOAD */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:25; + uint32_t acknowledgment:1; + uint32_t version:1; + uint32_t erase_or_prog:1; + uint32_t update_flash:1; + uint32_t update_ram:1; + uint32_t method:1; + uint32_t load_cmplt:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t load_cmplt:1; + uint32_t method:1; + uint32_t update_ram:1; + uint32_t update_flash:1; + uint32_t erase_or_prog:1; + uint32_t version:1; + uint32_t acknowledgment:1; + uint32_t rsvd2:25; +#endif + + uint32_t dl_to_adr_low; + uint32_t dl_to_adr_high; + uint32_t dl_len; + union { + uint32_t dl_from_mbx_offset; + struct ulp_bde dl_from_bde; + struct ulp_bde64 dl_from_bde64; + } un; + +} LOAD_SM_VAR; + +/* Structure for MB Command READ_NVPARM (02) */ + +typedef struct { + uint32_t rsvd1[3]; /* Read as all one's */ + uint32_t rsvd2; /* Read as all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Read as all one's */ +} READ_NV_VAR; + +/* Structure for MB Command WRITE_NVPARMS (03) */ + +typedef struct { + uint32_t rsvd1[3]; /* Must be all one's */ + uint32_t rsvd2; /* Must be all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Must be all one's */ +} WRITE_NV_VAR; + +/* Structure for MB Command RUN_BIU_DIAG (04) */ +/* Structure for MB Command RUN_BIU_DIAG64 (0x84) */ + +typedef struct { + uint32_t rsvd1; + union { + struct { + struct ulp_bde xmit_bde; + struct ulp_bde rcv_bde; + } s1; + struct { + struct ulp_bde64 xmit_bde64; + struct ulp_bde64 rcv_bde64; + } s2; + } un; +} BIU_DIAG_VAR; + +/* Structure for MB Command INIT_LINK (05) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:24; + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ + uint32_t rsvd1:24; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ + uint8_t rsvd2; + uint16_t link_flags; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t link_flags; + uint8_t rsvd2; + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ +#endif + +#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */ +#define FLAGS_TOPOLOGY_MODE_LOOP_PT 0x00 /* Attempt loop then pt-pt */ +#define FLAGS_TOPOLOGY_MODE_PT_PT 0x02 /* Attempt pt-pt only */ +#define FLAGS_TOPOLOGY_MODE_LOOP 0x04 /* Attempt loop only */ +#define FLAGS_TOPOLOGY_MODE_PT_LOOP 0x06 /* Attempt pt-pt then loop */ +#define FLAGS_LIRP_LILP 0x80 /* LIRP / LILP is disabled */ + +#define FLAGS_TOPOLOGY_FAILOVER 0x0400 /* Bit 10 */ +#define FLAGS_LINK_SPEED 0x0800 /* Bit 11 */ + + uint32_t link_speed; +#define LINK_SPEED_AUTO 0 /* Auto selection */ +#define LINK_SPEED_1G 1 /* 1 Gigabaud */ +#define LINK_SPEED_2G 2 /* 2 Gigabaud */ +#define LINK_SPEED_4G 4 /* 4 Gigabaud */ +#define LINK_SPEED_8G 8 /* 4 Gigabaud */ +#define LINK_SPEED_10G 16 /* 10 Gigabaud */ + +} INIT_LINK_VAR; + +/* Structure for MB Command DOWN_LINK (06) */ + +typedef struct { + uint32_t rsvd1; +} DOWN_LINK_VAR; + +/* Structure for MB Command CONFIG_LINK (07) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t rsvd1:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t rsvd1:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + + uint32_t myId; + uint32_t rsvd2; + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t crtov; + uint32_t citov; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rrq_enable:1; + uint32_t rrq_immed:1; + uint32_t rsvd4:29; + uint32_t ack0_enable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0_enable:1; + uint32_t rsvd4:29; + uint32_t rrq_immed:1; + uint32_t rrq_enable:1; +#endif +} CONFIG_LINK; + +/* Structure for MB Command PART_SLIM (08) + * will be removed since SLI1 is no longer supported! + */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t offCiocb; + uint16_t numCiocb; + uint16_t offRiocb; + uint16_t numRiocb; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t numCiocb; + uint16_t offCiocb; + uint16_t numRiocb; + uint16_t offRiocb; +#endif +} RING_DEF; + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused1:24; + uint32_t numRing:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t numRing:8; + uint32_t unused1:24; +#endif + + RING_DEF ringdef[4]; + uint32_t hbainit; +} PART_SLIM_VAR; + +/* Structure for MB Command CONFIG_RING (09) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused2:6; + uint32_t recvSeq:1; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t unused1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t unused1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t recvSeq:1; + uint32_t unused2:6; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxRespXchg; + uint16_t maxOrigXchg; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrigXchg; + uint16_t maxRespXchg; +#endif + + RR_REG rrRegs[6]; +} CONFIG_RING_VAR; + +/* Structure for MB Command RESET_RING (10) */ + +typedef struct { + uint32_t ring_no; +} RESET_RING_VAR; + +/* Structure for MB Command READ_CONFIG (11) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t InitBBC:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t InitBBC:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t topology:8; + uint32_t myDid:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myDid:24; + uint32_t topology:8; +#endif + + /* Defines for topology (defined previously) */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t AR:1; + uint32_t IR:1; + uint32_t rsvd1:29; + uint32_t ack0:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0:1; + uint32_t rsvd1:29; + uint32_t IR:1; + uint32_t AR:1; +#endif + + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t lmt; +#define LMT_RESERVED 0x0 /* Not used */ +#define LMT_266_10bit 0x1 /* 265.625 Mbaud 10 bit iface */ +#define LMT_532_10bit 0x2 /* 531.25 Mbaud 10 bit iface */ +#define LMT_1063_20bit 0x3 /* 1062.5 Mbaud 20 bit iface */ +#define LMT_1063_10bit 0x4 /* 1062.5 Mbaud 10 bit iface */ +#define LMT_2125_10bit 0x8 /* 2125 Mbaud 10 bit iface */ +#define LMT_4250_10bit 0x40 /* 4250 Mbaud 10 bit iface */ + + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t max_xri; + uint32_t max_iocb; + uint32_t max_rpi; + uint32_t avail_xri; + uint32_t avail_iocb; + uint32_t avail_rpi; + uint32_t default_rpi; +} READ_CONFIG_VAR; + +/* Structure for MB Command READ_RCONFIG (12) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:7; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t rsvd1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t rsvd1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t rsvd2:7; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxResp; + uint16_t maxOrig; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrig; + uint16_t maxResp; +#endif + + RR_REG rrRegs[6]; + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t cmdRingOffset; + uint16_t cmdEntryCnt; + uint16_t rspRingOffset; + uint16_t rspEntryCnt; + uint16_t nextCmdOffset; + uint16_t rsvd3; + uint16_t nextRspOffset; + uint16_t rsvd4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t cmdEntryCnt; + uint16_t cmdRingOffset; + uint16_t rspEntryCnt; + uint16_t rspRingOffset; + uint16_t rsvd3; + uint16_t nextCmdOffset; + uint16_t rsvd4; + uint16_t nextRspOffset; +#endif +} READ_RCONF_VAR; + +/* Structure for MB Command READ_SPARM (13) */ +/* Structure for MB Command READ_SPARM64 (0x8D) */ + +typedef struct { + uint32_t rsvd1; + uint32_t rsvd2; + union { + struct ulp_bde sp; /* This BDE points to struct serv_parm + structure */ + struct ulp_bde64 sp64; + } un; +} READ_SPARM_VAR; + +/* Structure for MB Command READ_STATUS (14) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:31; + uint32_t clrCounters:1; + uint16_t activeXriCnt; + uint16_t activeRpiCnt; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t clrCounters:1; + uint32_t rsvd1:31; + uint16_t activeRpiCnt; + uint16_t activeXriCnt; +#endif + + uint32_t xmitByteCnt; + uint32_t rcvByteCnt; + uint32_t xmitFrameCnt; + uint32_t rcvFrameCnt; + uint32_t xmitSeqCnt; + uint32_t rcvSeqCnt; + uint32_t totalOrigExchanges; + uint32_t totalRespExchanges; + uint32_t rcvPbsyCnt; + uint32_t rcvFbsyCnt; +} READ_STATUS_VAR; + +/* Structure for MB Command READ_RPI (15) */ +/* Structure for MB Command READ_RPI64 (0x8F) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextRpi; + uint16_t reqRpi; + uint32_t rsvd2:8; + uint32_t DID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqRpi; + uint16_t nextRpi; + uint32_t DID:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} READ_RPI_VAR; + +/* Structure for MB Command READ_XRI (16) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextXri; + uint16_t reqXri; + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t DID:24; + uint32_t rsvd3:8; + uint32_t SID:24; + uint32_t rsvd4; + uint8_t seqId; + uint8_t rsvd5; + uint16_t seqCount; + uint16_t oxId; + uint16_t rxId; + uint32_t rsvd6:30; + uint32_t si:1; + uint32_t exchOrig:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqXri; + uint16_t nextXri; + uint16_t rpi; + uint16_t rsvd1; + uint32_t DID:24; + uint32_t rsvd2:8; + uint32_t SID:24; + uint32_t rsvd3:8; + uint32_t rsvd4; + uint16_t seqCount; + uint8_t rsvd5; + uint8_t seqId; + uint16_t rxId; + uint16_t oxId; + uint32_t exchOrig:1; + uint32_t si:1; + uint32_t rsvd6:30; +#endif +} READ_XRI_VAR; + +/* Structure for MB Command READ_REV (17) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cv:1; + uint32_t rr:1; + uint32_t rsvd1:29; + uint32_t rv:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rv:1; + uint32_t rsvd1:29; + uint32_t rr:1; + uint32_t cv:1; +#endif + + uint32_t biuRev; + uint32_t smRev; + union { + uint32_t smFwRev; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t ProgType; + uint8_t ProgId; + uint16_t ProgVer:4; + uint16_t ProgRev:4; + uint16_t ProgFixLvl:2; + uint16_t ProgDistType:2; + uint16_t DistCnt:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t DistCnt:4; + uint16_t ProgDistType:2; + uint16_t ProgFixLvl:2; + uint16_t ProgRev:4; + uint16_t ProgVer:4; + uint8_t ProgId; + uint8_t ProgType; +#endif + + } b; + } un; + uint32_t endecRev; +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint8_t fcphHigh; + uint8_t fcphLow; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t fcphLow; + uint8_t fcphHigh; + uint8_t feaLevelLow; + uint8_t feaLevelHigh; +#endif + + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + uint32_t rsvd2; + uint32_t RandomData[7]; +} READ_REV_VAR; + +/* Structure for MB Command READ_LINK_STAT (18) */ + +typedef struct { + uint32_t rsvd1; + uint32_t linkFailureCnt; + uint32_t lossSyncCnt; + + uint32_t lossSignalCnt; + uint32_t primSeqErrCnt; + uint32_t invalidXmitWord; + uint32_t crcCnt; + uint32_t primSeqTimeout; + uint32_t elasticOverrun; + uint32_t arbTimeout; +} READ_LNK_VAR; + +/* Structure for MB Command REG_LOGIN (19) */ +/* Structure for MB Command REG_LOGIN64 (0x93) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t did:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; + uint32_t did:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} REG_LOGIN_VAR; + +/* Word 30 contents for REG_LOGIN */ +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1:12; + uint16_t wd30_class:4; + uint16_t xri; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t xri; + uint16_t wd30_class:4; + uint16_t rsvd1:12; +#endif + } f; + uint32_t word; +} REG_WD30; + +/* Structure for MB Command UNREG_LOGIN (20) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; +#endif +} UNREG_LOGIN_VAR; + +/* Structure for MB Command UNREG_D_ID (0x23) */ + +typedef struct { + uint32_t did; +} UNREG_D_ID_VAR; + +/* Structure for MB Command READ_LA (21) */ +/* Structure for MB Command READ_LA64 (0x95) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:22; + uint32_t pb:1; + uint32_t il:1; + uint32_t attType:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t attType:8; + uint32_t il:1; + uint32_t pb:1; + uint32_t rsvd1:22; +#endif + +#define AT_RESERVED 0x00 /* Reserved - attType */ +#define AT_LINK_UP 0x01 /* Link is up */ +#define AT_LINK_DOWN 0x02 /* Link is down */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t granted_AL_PA; + uint8_t lipAlPs; + uint8_t lipType; + uint8_t topology; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t topology; + uint8_t lipType; + uint8_t lipAlPs; + uint8_t granted_AL_PA; +#endif + +#define TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */ +#define TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */ + + union { + struct ulp_bde lilpBde; /* This BDE points to a 128 byte buffer + to */ + /* store the LILP AL_PA position map into */ + struct ulp_bde64 lilpBde64; + } un; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Dlu:1; + uint32_t Dtf:1; + uint32_t Drsvd2:14; + uint32_t DlnkSpeed:8; + uint32_t DnlPort:4; + uint32_t Dtx:2; + uint32_t Drx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Drx:2; + uint32_t Dtx:2; + uint32_t DnlPort:4; + uint32_t DlnkSpeed:8; + uint32_t Drsvd2:14; + uint32_t Dtf:1; + uint32_t Dlu:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Ulu:1; + uint32_t Utf:1; + uint32_t Ursvd2:14; + uint32_t UlnkSpeed:8; + uint32_t UnlPort:4; + uint32_t Utx:2; + uint32_t Urx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Urx:2; + uint32_t Utx:2; + uint32_t UnlPort:4; + uint32_t UlnkSpeed:8; + uint32_t Ursvd2:14; + uint32_t Utf:1; + uint32_t Ulu:1; +#endif + +#define LA_UNKNW_LINK 0x0 /* lnkSpeed */ +#define LA_1GHZ_LINK 0x04 /* lnkSpeed */ +#define LA_2GHZ_LINK 0x08 /* lnkSpeed */ +#define LA_4GHZ_LINK 0x10 /* lnkSpeed */ +#define LA_8GHZ_LINK 0x20 /* lnkSpeed */ +#define LA_10GHZ_LINK 0x40 /* lnkSpeed */ + +} READ_LA_VAR; + +/* Structure for MB Command CLEAR_LA (22) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ + uint32_t rsvd1; +} CLEAR_LA_VAR; + +/* Structure for MB Command DUMP */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd:25; + uint32_t ra:1; + uint32_t co:1; + uint32_t cv:1; + uint32_t type:4; + uint32_t entry_index:16; + uint32_t region_id:16; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t type:4; + uint32_t cv:1; + uint32_t co:1; + uint32_t ra:1; + uint32_t rsvd:25; + uint32_t region_id:16; + uint32_t entry_index:16; +#endif + + uint32_t rsvd1; + uint32_t word_cnt; + uint32_t resp_offset; +} DUMP_VAR; + +#define DMP_MEM_REG 0x1 +#define DMP_NV_PARAMS 0x2 + +#define DMP_REGION_VPD 0xe +#define DMP_VPD_SIZE 0x100 + +/* Structure for MB Command CONFIG_PORT (0x88) */ + +typedef struct { + uint32_t pcbLen; + uint32_t pcbLow; /* bit 31:0 of memory based port config block */ + uint32_t pcbHigh; /* bit 63:32 of memory based port config block */ + uint32_t hbainit[5]; +} CONFIG_PORT_VAR; + +/* SLI-2 Port Control Block */ + +/* SLIM POINTER */ +#define SLIMOFF 0x30 /* WORD */ + +typedef struct _SLI2_RDSC { + uint32_t cmdEntries; + uint32_t cmdAddrLow; + uint32_t cmdAddrHigh; + + uint32_t rspEntries; + uint32_t rspAddrLow; + uint32_t rspAddrHigh; +} SLI2_RDSC; + +typedef struct _PCB { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t rsvd:12; + uint32_t maxRing:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t maxRing:4; + uint32_t rsvd:12; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; +#endif + + uint32_t mailBoxSize; + uint32_t mbAddrLow; + uint32_t mbAddrHigh; + + uint32_t hgpAddrLow; + uint32_t hgpAddrHigh; + + uint32_t pgpAddrLow; + uint32_t pgpAddrHigh; + SLI2_RDSC rdsc[MAX_RINGS]; +} PCB_t; + +/* NEW_FEATURE */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:27; + uint32_t discardFarp:1; + uint32_t IPEnable:1; + uint32_t nodeName:1; + uint32_t portName:1; + uint32_t filterEnable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t filterEnable:1; + uint32_t portName:1; + uint32_t nodeName:1; + uint32_t IPEnable:1; + uint32_t discardFarp:1; + uint32_t rsvd:27; +#endif + + uint8_t portname[8]; /* Used to be struct lpfc_name */ + uint8_t nodename[8]; + uint32_t rsvd1; + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t IPAddress; +} CONFIG_FARP_VAR; + +/* Union of all Mailbox Command types */ +#define MAILBOX_CMD_WSIZE 32 + +typedef union { + uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; + LOAD_SM_VAR varLdSM; /* cmd = 1 (LOAD_SM) */ + READ_NV_VAR varRDnvp; /* cmd = 2 (READ_NVPARMS) */ + WRITE_NV_VAR varWTnvp; /* cmd = 3 (WRITE_NVPARMS) */ + BIU_DIAG_VAR varBIUdiag; /* cmd = 4 (RUN_BIU_DIAG) */ + INIT_LINK_VAR varInitLnk; /* cmd = 5 (INIT_LINK) */ + DOWN_LINK_VAR varDwnLnk; /* cmd = 6 (DOWN_LINK) */ + CONFIG_LINK varCfgLnk; /* cmd = 7 (CONFIG_LINK) */ + PART_SLIM_VAR varSlim; /* cmd = 8 (PART_SLIM) */ + CONFIG_RING_VAR varCfgRing; /* cmd = 9 (CONFIG_RING) */ + RESET_RING_VAR varRstRing; /* cmd = 10 (RESET_RING) */ + READ_CONFIG_VAR varRdConfig; /* cmd = 11 (READ_CONFIG) */ + READ_RCONF_VAR varRdRConfig; /* cmd = 12 (READ_RCONFIG) */ + READ_SPARM_VAR varRdSparm; /* cmd = 13 (READ_SPARM(64)) */ + READ_STATUS_VAR varRdStatus; /* cmd = 14 (READ_STATUS) */ + READ_RPI_VAR varRdRPI; /* cmd = 15 (READ_RPI(64)) */ + READ_XRI_VAR varRdXRI; /* cmd = 16 (READ_XRI) */ + READ_REV_VAR varRdRev; /* cmd = 17 (READ_REV) */ + READ_LNK_VAR varRdLnk; /* cmd = 18 (READ_LNK_STAT) */ + REG_LOGIN_VAR varRegLogin; /* cmd = 19 (REG_LOGIN(64)) */ + UNREG_LOGIN_VAR varUnregLogin; /* cmd = 20 (UNREG_LOGIN) */ + READ_LA_VAR varReadLA; /* cmd = 21 (READ_LA(64)) */ + CLEAR_LA_VAR varClearLA; /* cmd = 22 (CLEAR_LA) */ + DUMP_VAR varDmp; /* Warm Start DUMP mbx cmd */ + UNREG_D_ID_VAR varUnregDID; /* cmd = 0x23 (UNREG_D_ID) */ + CONFIG_FARP_VAR varCfgFarp; /* cmd = 0x25 (CONFIG_FARP) NEW_FEATURE */ + CONFIG_PORT_VAR varCfgPort; /* cmd = 0x88 (CONFIG_PORT) */ +} MAILVARIANTS; + +/* + * SLI-2 specific structures + */ + +typedef struct { + uint32_t cmdPutInx; + uint32_t rspGetInx; +} HGP; + +typedef struct { + uint32_t cmdGetInx; + uint32_t rspPutInx; +} PGP; + +typedef struct _SLI2_DESC { + HGP host[MAX_RINGS]; + uint32_t unused1[16]; + PGP port[MAX_RINGS]; +} SLI2_DESC; + +typedef union { + SLI2_DESC s2; +} SLI_VAR; + +typedef volatile struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t mbxStatus; + uint8_t mbxCommand; + uint8_t mbxReserved:6; + uint8_t mbxHc:1; + uint8_t mbxOwner:1; /* Low order bit first word */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t mbxOwner:1; /* Low order bit first word */ + uint8_t mbxHc:1; + uint8_t mbxReserved:6; + uint8_t mbxCommand; + uint16_t mbxStatus; +#endif + + MAILVARIANTS un; + SLI_VAR us; +} MAILBOX_t; + +/* + * Begin Structure Definitions for IOCB Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t statAction; + uint8_t statRsn; + uint8_t statBaExp; + uint8_t statLocalError; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t statLocalError; + uint8_t statBaExp; + uint8_t statRsn; + uint8_t statAction; +#endif + /* statRsn P/F_RJT reason codes */ +#define RJT_BAD_D_ID 0x01 /* Invalid D_ID field */ +#define RJT_BAD_S_ID 0x02 /* Invalid S_ID field */ +#define RJT_UNAVAIL_TEMP 0x03 /* N_Port unavailable temp. */ +#define RJT_UNAVAIL_PERM 0x04 /* N_Port unavailable perm. */ +#define RJT_UNSUP_CLASS 0x05 /* Class not supported */ +#define RJT_DELIM_ERR 0x06 /* Delimiter usage error */ +#define RJT_UNSUP_TYPE 0x07 /* Type not supported */ +#define RJT_BAD_CONTROL 0x08 /* Invalid link conrtol */ +#define RJT_BAD_RCTL 0x09 /* R_CTL invalid */ +#define RJT_BAD_FCTL 0x0A /* F_CTL invalid */ +#define RJT_BAD_OXID 0x0B /* OX_ID invalid */ +#define RJT_BAD_RXID 0x0C /* RX_ID invalid */ +#define RJT_BAD_SEQID 0x0D /* SEQ_ID invalid */ +#define RJT_BAD_DFCTL 0x0E /* DF_CTL invalid */ +#define RJT_BAD_SEQCNT 0x0F /* SEQ_CNT invalid */ +#define RJT_BAD_PARM 0x10 /* Param. field invalid */ +#define RJT_XCHG_ERR 0x11 /* Exchange error */ +#define RJT_PROT_ERR 0x12 /* Protocol error */ +#define RJT_BAD_LENGTH 0x13 /* Invalid Length */ +#define RJT_UNEXPECTED_ACK 0x14 /* Unexpected ACK */ +#define RJT_LOGIN_REQUIRED 0x16 /* Login required */ +#define RJT_TOO_MANY_SEQ 0x17 /* Excessive sequences */ +#define RJT_XCHG_NOT_STRT 0x18 /* Exchange not started */ +#define RJT_UNSUP_SEC_HDR 0x19 /* Security hdr not supported */ +#define RJT_UNAVAIL_PATH 0x1A /* Fabric Path not available */ +#define RJT_VENDOR_UNIQUE 0xFF /* Vendor unique error */ + +#define IOERR_SUCCESS 0x00 /* statLocalError */ +#define IOERR_MISSING_CONTINUE 0x01 +#define IOERR_SEQUENCE_TIMEOUT 0x02 +#define IOERR_INTERNAL_ERROR 0x03 +#define IOERR_INVALID_RPI 0x04 +#define IOERR_NO_XRI 0x05 +#define IOERR_ILLEGAL_COMMAND 0x06 +#define IOERR_XCHG_DROPPED 0x07 +#define IOERR_ILLEGAL_FIELD 0x08 +#define IOERR_BAD_CONTINUE 0x09 +#define IOERR_TOO_MANY_BUFFERS 0x0A +#define IOERR_RCV_BUFFER_WAITING 0x0B +#define IOERR_NO_CONNECTION 0x0C +#define IOERR_TX_DMA_FAILED 0x0D +#define IOERR_RX_DMA_FAILED 0x0E +#define IOERR_ILLEGAL_FRAME 0x0F +#define IOERR_EXTRA_DATA 0x10 +#define IOERR_NO_RESOURCES 0x11 +#define IOERR_RESERVED 0x12 +#define IOERR_ILLEGAL_LENGTH 0x13 +#define IOERR_UNSUPPORTED_FEATURE 0x14 +#define IOERR_ABORT_IN_PROGRESS 0x15 +#define IOERR_ABORT_REQUESTED 0x16 +#define IOERR_RECEIVE_BUFFER_TIMEOUT 0x17 +#define IOERR_LOOP_OPEN_FAILURE 0x18 +#define IOERR_RING_RESET 0x19 +#define IOERR_LINK_DOWN 0x1A +#define IOERR_CORRUPTED_DATA 0x1B +#define IOERR_CORRUPTED_RPI 0x1C +#define IOERR_OUT_OF_ORDER_DATA 0x1D +#define IOERR_OUT_OF_ORDER_ACK 0x1E +#define IOERR_DUP_FRAME 0x1F +#define IOERR_LINK_CONTROL_FRAME 0x20 /* ACK_N received */ +#define IOERR_BAD_HOST_ADDRESS 0x21 +#define IOERR_RCV_HDRBUF_WAITING 0x22 +#define IOERR_MISSING_HDR_BUFFER 0x23 +#define IOERR_MSEQ_CHAIN_CORRUPTED 0x24 +#define IOERR_ABORTMULT_REQUESTED 0x25 +#define IOERR_BUFFER_SHORTAGE 0x28 +#define IOERR_DEFAULT 0x29 +#define IOERR_CNT 0x2A + +#define IOERR_DRVR_MASK 0x100 +#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */ +#define IOERR_SLI_BRESET 0x102 +#define IOERR_SLI_ABORTED 0x103 +} PARM_ERR; + +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t Rctl; /* R_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Rctl; /* R_CTL field */ +#endif + +#define BC 0x02 /* Broadcast Received - Fctl */ +#define SI 0x04 /* Sequence Initiative */ +#define LA 0x08 /* Ignore Link Attention state */ +#define LS 0x80 /* Last Sequence */ + } hcsw; + uint32_t reserved; +} WORD5; + +/* IOCB Command template for a generic response */ +typedef struct { + uint32_t reserved[4]; + PARM_ERR perr; +} GENERIC_RSP; + +/* IOCB Command template for XMIT / XMIT_BCAST / RCV_SEQUENCE / XMIT_ELS */ +typedef struct { + struct ulp_bde xrsqbde[2]; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XR_SEQ_FIELDS; + +/* IOCB Command template for ELS_REQUEST */ +typedef struct { + struct ulp_bde elsReq; + struct ulp_bde elsRsp; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST; + +/* IOCB Command template for RCV_ELS_REQ */ +typedef struct { + struct ulp_bde elsReq[2]; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ; + +/* IOCB Command template for ABORT / CLOSE_XRI */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; +#define ABORT_TYPE_ABTX 0x00000000 +#define ABORT_TYPE_ABTS 0x00000001 + uint32_t parm; +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t abortContextTag; /* ulpContext from command to abort/close */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ + uint16_t abortContextTag; /* ulpContext from command to abort/close */ +#endif +} AC_XRI; + +/* IOCB Command template for ABORT_MXRI64 */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; + uint32_t parm; + uint32_t iotag32; +} A_MXRI64; + +/* IOCB Command template for GET_RPI */ +typedef struct { + uint32_t rsvd[4]; + uint32_t parmRo; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} GET_RPI; + +/* IOCB Command template for all FCP Initiator commands */ +typedef struct { + struct ulp_bde fcpi_cmnd; /* FCP_CMND payload descriptor */ + struct ulp_bde fcpi_rsp; /* Rcv buffer */ + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS; + +/* IOCB Command template for all FCP Target commands */ +typedef struct { + struct ulp_bde fcpt_Buffer[2]; /* FCP_CMND payload descriptor */ + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS; + +/* SLI-2 IOCB structure definitions */ + +/* IOCB Command template for 64 bit XMIT / XMIT_BCAST / XMIT_ELS */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XMT_SEQ_FIELDS64; + +/* IOCB Command template for 64 bit RCV_SEQUENCE64 */ +typedef struct { + struct ulp_bde64 rcvBde; + uint32_t rsvd1; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} RCV_SEQ_FIELDS64; + +/* IOCB Command template for ELS_REQUEST64 */ +typedef struct { + ULP_BDL bdl; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST64; + +/* IOCB Command template for GEN_REQUEST64 */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} GEN_REQUEST64; + +/* IOCB Command template for RCV_ELS_REQ64 */ +typedef struct { + struct ulp_bde64 elsReq; + uint32_t rcvd1; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ64; + +/* IOCB Command template for all 64 bit FCP Initiator commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS64; + +/* IOCB Command template for all 64 bit FCP Target commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS64; + +typedef volatile struct _IOCB { /* IOCB structure */ + union { + GENERIC_RSP grsp; /* Generic response */ + XR_SEQ_FIELDS xrseq; /* XMIT / BCAST / RCV_SEQUENCE cmd */ + struct ulp_bde cont[3]; /* up to 3 continuation bdes */ + RCV_ELS_REQ rcvels; /* RCV_ELS_REQ template */ + AC_XRI acxri; /* ABORT / CLOSE_XRI template */ + A_MXRI64 amxri; /* abort multiple xri command overlay */ + GET_RPI getrpi; /* GET_RPI template */ + FCPI_FIELDS fcpi; /* FCP Initiator template */ + FCPT_FIELDS fcpt; /* FCP target template */ + + /* SLI-2 structures */ + + struct ulp_bde64 cont64[2]; /* up to 2 64 bit continuation + bde_64s */ + ELS_REQUEST64 elsreq64; /* ELS_REQUEST template */ + GEN_REQUEST64 genreq64; /* GEN_REQUEST template */ + RCV_ELS_REQ64 rcvels64; /* RCV_ELS_REQ template */ + XMT_SEQ_FIELDS64 xseq64; /* XMIT / BCAST cmd */ + FCPI_FIELDS64 fcpi64; /* FCP 64 bit Initiator template */ + FCPT_FIELDS64 fcpt64; /* FCP 64 bit target template */ + + uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */ + } un; + union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t1; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t2; + } un1; +#define ulpContext un1.t1.ulpContext +#define ulpIoTag un1.t1.ulpIoTag +#define ulpIoTag0 un1.t2.ulpIoTag0 + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t ulpTimeout:8; + uint32_t ulpXS:1; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpPU:2; + uint32_t ulpIr:1; + uint32_t ulpClass:3; + uint32_t ulpCommand:8; + uint32_t ulpStatus:4; + uint32_t ulpBdeCount:2; + uint32_t ulpLe:1; + uint32_t ulpOwner:1; /* Low order bit word 7 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ulpOwner:1; /* Low order bit word 7 */ + uint32_t ulpLe:1; + uint32_t ulpBdeCount:2; + uint32_t ulpStatus:4; + uint32_t ulpCommand:8; + uint32_t ulpClass:3; + uint32_t ulpIr:1; + uint32_t ulpPU:2; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpXS:1; + uint32_t ulpTimeout:8; +#endif + +#define PARM_UNUSED 0 /* PU field (Word 4) not used */ +#define PARM_REL_OFF 1 /* PU field (Word 4) = R. O. */ +#define PARM_READ_CHECK 2 /* PU field (Word 4) = Data Transfer Length */ +#define CLASS1 0 /* Class 1 */ +#define CLASS2 1 /* Class 2 */ +#define CLASS3 2 /* Class 3 */ +#define CLASS_FCP_INTERMIX 7 /* FCP Data->Cls 1, all else->Cls 2 */ + +#define IOSTAT_SUCCESS 0x0 /* ulpStatus - HBA defined */ +#define IOSTAT_FCP_RSP_ERROR 0x1 +#define IOSTAT_REMOTE_STOP 0x2 +#define IOSTAT_LOCAL_REJECT 0x3 +#define IOSTAT_NPORT_RJT 0x4 +#define IOSTAT_FABRIC_RJT 0x5 +#define IOSTAT_NPORT_BSY 0x6 +#define IOSTAT_FABRIC_BSY 0x7 +#define IOSTAT_INTERMED_RSP 0x8 +#define IOSTAT_LS_RJT 0x9 +#define IOSTAT_BA_RJT 0xA +#define IOSTAT_RSVD1 0xB +#define IOSTAT_RSVD2 0xC +#define IOSTAT_RSVD3 0xD +#define IOSTAT_RSVD4 0xE +#define IOSTAT_RSVD5 0xF +#define IOSTAT_DRIVER_REJECT 0x10 /* ulpStatus - Driver defined */ +#define IOSTAT_DEFAULT 0xF /* Same as rsvd5 for now */ +#define IOSTAT_CNT 0x11 + +} IOCB_t; + + +#define SLI1_SLIM_SIZE (4 * 1024) + +/* Up to 498 IOCBs will fit into 16k + * 256 (MAILBOX_t) + 140 (PCB_t) + ( 32 (IOCB_t) * 498 ) = < 16384 + */ +#define SLI2_SLIM_SIZE (16 * 1024) + +/* Maximum IOCBs that will fit in SLI2 slim */ +#define MAX_SLI2_IOCB 498 + +struct lpfc_sli2_slim { + MAILBOX_t mbx; + PCB_t pcb; + IOCB_t IOCBs[MAX_SLI2_IOCB]; +}; + +/******************************************************************* +This macro check PCI device to allow special handling for LC HBAs. + +Parameters: +device : struct pci_dev 's device field + +return 1 => TRUE + 0 => FALSE + *******************************************************************/ +static inline int +lpfc_is_LC_HBA(unsigned short device) +{ + if ((device == PCI_DEVICE_ID_TFLY) || + (device == PCI_DEVICE_ID_PFLY) || + (device == PCI_DEVICE_ID_LP101) || + (device == PCI_DEVICE_ID_BMID) || + (device == PCI_DEVICE_ID_BSMB) || + (device == PCI_DEVICE_ID_ZMID) || + (device == PCI_DEVICE_ID_ZSMB) || + (device == PCI_DEVICE_ID_RFLY)) + return 1; + else + return 0; +} + +#endif /* _H_LPFC_HW */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c new file mode 100644 index 000000000..e7f927892 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -0,0 +1,1345 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_init.c 1.168 2004/11/15 11:01:33EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" +#include "lpfc_compat.h" + +static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *); +static int lpfc_post_rcv_buf(struct lpfc_hba *); +static int lpfc_rdrev_wd30 = 0; + +/************************************************************************/ +/* */ +/* lpfc_config_port_prep */ +/* This routine will do LPFC initialization prior to the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success or -ERESTART if it wants */ +/* the SLI layer to reset the HBA and try again. Any */ +/* other return value indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_prep(struct lpfc_hba * phba) +{ + lpfc_vpd_t *vp = &phba->vpd; + int i = 0; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + mb = &pmb->mb; + phba->hba_state = LPFC_INIT_MBX_CMDS; + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + char licensed[56] = + "key unlock for use with gnu public licensed code only\0"; + uint32_t *ptext = (uint32_t *) licensed; + + for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) + *ptext = cpu_to_be32(*ptext); + + /* Setup and issue mailbox READ NVPARAMS command */ + lpfc_read_nv(phba, pmb); + memset((char*)mb->un.varRDnvp.rsvd3, 0, + sizeof (mb->un.varRDnvp.rsvd3)); + memcpy((char*)mb->un.varRDnvp.rsvd3, licensed, + sizeof (licensed)); + + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter initialization error, mbxCmd + READ_NVPARM, mbxStatus */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX, + "%d:0324 Config Port initialization " + "error, mbxCmd x%x READ_NVPARM, " + "mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + return -ERESTART; + } + memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename, + sizeof (mb->un.varRDnvp.nodename)); + } + + /* Setup and issue mailbox READ REV command */ + lpfc_read_rev(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd READ_REV, mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0439 Adapter failed to init, mbxCmd x%x " + "READ_REV, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + mempool_free( pmb, phba->mbox_mem_pool); + return -ERESTART; + } + + /* The HBA's current state is provided by the ProgType and rr fields. + * Read and check the value of these fields before continuing to config + * this port. + */ + if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) { + /* Old firmware */ + vp->rev.rBit = 0; + /* Adapter failed to init, mbxCmd READ_REV detected + outdated firmware */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0440 Adapter failed to init, mbxCmd x%x " + "READ_REV detected outdated firmware" + "Data: x%x\n", + phba->brd_no, + mb->mbxCommand, 0); + mempool_free(pmb, phba->mbox_mem_pool); + return -ERESTART; + } else { + vp->rev.rBit = 1; + vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev; + memcpy(vp->rev.sli1FwName, + (char*)mb->un.varRdRev.sli1FwName, 16); + vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev; + memcpy(vp->rev.sli2FwName, + (char *)mb->un.varRdRev.sli2FwName, 16); + } + + /* Save information as VPD data */ + vp->rev.biuRev = mb->un.varRdRev.biuRev; + vp->rev.smRev = mb->un.varRdRev.smRev; + vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev; + vp->rev.endecRev = mb->un.varRdRev.endecRev; + vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh; + vp->rev.fcphLow = mb->un.varRdRev.fcphLow; + vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh; + vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow; + vp->rev.postKernRev = mb->un.varRdRev.postKernRev; + vp->rev.opFwRev = mb->un.varRdRev.opFwRev; + lpfc_rdrev_wd30 = mb->un.varWords[30]; + + if (lpfc_is_LC_HBA(phba->pcidev->device)) + memcpy(phba->RandomData, (char *)&mb->un.varWords[24], + sizeof (phba->RandomData)); + + /* Get adapter VPD information */ + lpfc_dump_mem(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Let it go through even if failed. */ + /* Adapter failed to init, mbxCmd DUMP VPD, + mbxStatus */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0441 VPD not present on adapter, mbxCmd " + "x%x DUMP VPD, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + } else if (mb->un.varDmp.ra == 1) { + lpfc_parse_vpd(phba, (uint8_t *)&mb->un.varDmp.resp_offset); + } + mempool_free(pmb, phba->mbox_mem_pool); + return 0; +} + +/************************************************************************/ +/* */ +/* lpfc_config_port_post */ +/* This routine will do LPFC initialization after the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_post(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli = &phba->sli; + uint32_t status, timeout; + int i, j, flogi_sent; + unsigned long isr_cnt, clk_cnt; + + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + mb = &pmb->mb; + + /* Setup link timers */ + lpfc_config_link(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0447 Adapter failed init, mbxCmd x%x " + "CONFIG_LINK mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Get login parameters for NID. */ + lpfc_read_sparam(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0448 Adapter failed init, mbxCmd x%x " + "READ_SPARM mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + mp = (struct lpfc_dmabuf *) pmb->context1; + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + pmb->context1 = NULL; + + memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_portname, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* If no serial number in VPD data, use low 6 bytes of WWNN */ + /* This should be consolidated into parse_vpd ? - mr */ + if (phba->SerialNumber[0] == 0) { + uint8_t *outptr; + + outptr = (uint8_t *) & phba->fc_nodename.IEEE[0]; + for (i = 0; i < 12; i++) { + status = *outptr++; + j = ((status & 0xf0) >> 4); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + i++; + j = (status & 0xf); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + } + } + + /* This should turn on DELAYED ABTS for ELS timeouts */ + lpfc_set_slim(phba, pmb, 0x052198, 0x1); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + + lpfc_read_config(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0453 Adapter failed to init, mbxCmd x%x " + "READ_CONFIG, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Reset the DFT_HBA_Q_DEPTH to the max xri */ + if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1)) + phba->cfg_hba_queue_depth = + mb->un.varRdConfig.max_xri + 1; + + phba->lmt = mb->un.varRdConfig.lmt; + /* HBA is not 4GB capable, or HBA is not 2GB capable, + don't let link speed ask for it */ + if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) && + (phba->cfg_link_speed > LINK_SPEED_2G)) || + (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) && + (phba->cfg_link_speed > LINK_SPEED_1G))) { + /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1302 Invalid speed for this board: " + "Reset link speed to auto: x%x\n", + phba->brd_no, + phba->cfg_link_speed); + phba->cfg_link_speed = LINK_SPEED_AUTO; + } + + if (!phba->intr_inited) { + /* Add our interrupt routine to kernel's interrupt chain & + enable it */ + + if (request_irq(phba->pcidev->irq, + lpfc_intr_handler, + SA_SHIRQ, + LPFC_DRIVER_NAME, + phba) != 0) { + /* Enable interrupt handler failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0451 Enable interrupt handler " + "failed\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + phba->intr_inited = + (HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA); + } + + phba->hba_state = LPFC_LINK_DOWN; + + /* Only process IOCBs on ring 0 till hba_state is READY */ + if (psli->ring[psli->ip_ring].cmdringaddr) + psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->fcp_ring].cmdringaddr) + psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->next_ring].cmdringaddr) + psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT; + + /* Post receive buffers for desired rings */ + lpfc_post_rcv_buf(phba); + + /* Enable appropriate host interrupts */ + status = readl(phba->HCregaddr); + status |= phba->intr_inited; + if (psli->sliinit.num_rings > 0) + status |= HC_R0INT_ENA; + if (psli->sliinit.num_rings > 1) + status |= HC_R1INT_ENA; + if (psli->sliinit.num_rings > 2) + status |= HC_R2INT_ENA; + if (psli->sliinit.num_rings > 3) + status |= HC_R3INT_ENA; + + writel(status, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_init_link(phba, pmb, phba->cfg_topology, + phba->cfg_link_speed); + + isr_cnt = psli->slistat.sliIntr; + clk_cnt = jiffies; + + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0454 Adapter failed to init, mbxCmd x%x " + "INIT_LINK, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + /* Clear all pending interrupts */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + free_irq(phba->pcidev->irq, phba); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + /* MBOX buffer will be freed in mbox compl */ + + /* + * Setup the ring 0 (els) timeout handler + */ + timeout = phba->fc_ratov << 1; + + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + + phba->fc_prevDID = Mask_DID; + flogi_sent = 0; + i = 0; + while ((phba->hba_state != LPFC_HBA_READY) || + (phba->num_disc_nodes) || (phba->fc_prli_sent) || + ((phba->fc_map_cnt == 0) && (i<2)) || + (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE)) { + /* Check every second for 30 retries. */ + i++; + if (i > 30) { + break; + } + if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) { + /* The link is down. Set linkdown timeout */ + break; + } + + /* Delay for 1 second to give discovery time to complete. */ + for (j = 0; j < 20; j++) { + /* On some systems, the driver's attach/detect routines + * are uninterruptible. Since the driver cannot predict + * when this is true, just manually call the ISR every + * 50 ms to service any interrupts. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + isr_cnt = psli->slistat.sliIntr; + } + } + isr_cnt = psli->slistat.sliIntr; + + if (clk_cnt == jiffies) { + /* REMOVE: IF THIS HAPPENS, SYSTEM CLOCK IS NOT RUNNING. + * WE HAVE TO MANUALLY CALL OUR TIMEOUT ROUTINES. + */ + clk_cnt = jiffies; + } + } + + /* Since num_disc_nodes keys off of PLOGI, delay a bit to let + * any potential PRLIs to flush thru the SLI sub-system. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + } + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_down_prep */ +/* This routine will do LPFC uninitialization before the */ +/* HBA is reset when bringing down the SLI Layer. This will be */ +/* initialized as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_hba_down_prep(struct lpfc_hba * phba) +{ + /* Disable interrupts */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Cleanup potential discovery resources */ + lpfc_els_flush_rscn(phba); + lpfc_els_flush_cmd(phba); + lpfc_disc_flush_list(phba); + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_handle_eratt */ +/* This routine will handle processing a Host Attention */ +/* Error Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_eratt(struct lpfc_hba * phba, uint32_t status) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + volatile uint32_t status1, status2; + void *from_slim; + unsigned long iflag; + + psli = &phba->sli; + from_slim = ((uint8_t *)phba->MBslimaddr + 0xa8); + status1 = readl( from_slim); + from_slim = ((uint8_t *)phba->MBslimaddr + 0xac); + status2 = readl( from_slim); + + if (status & HS_FFER6) { + /* Re-establishing Link */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, + "%d:1301 Re-establishing Link " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + phba->fc_flag |= FC_ESTABLISH_LINK; + + /* + * Firmware stops when it triggled erratt with HS_FFER6. + * That could cause the I/Os dropped by the firmware. + * Error iocb (I/O) on txcmplq and let the SCSI layer + * retry it after re-establishing link. + */ + pring = &psli->ring[psli->fcp_ring]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *)(iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl)(phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* + * There was a firmware error. Take the hba offline and then + * attempt to restart it. + */ + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_offline(phba); + if (lpfc_online(phba) == 0) { /* Initialize the HBA */ + mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); + return; + } + } else { + /* The if clause above forces this code path when the status + * failure is a value other than FFER6. Do not call the offline + * twice. This is the adapter hardware error path. + */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0457 Adapter Hardware Error " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + + lpfc_offline(phba); + + /* + * Restart all traffic to this host. Since the fc_transport + * block functions (future) were not called in lpfc_offline, + * don't call them here. + */ + scsi_unblock_requests(phba->host); + } + return; +} + +/************************************************************************/ +/* */ +/* lpfc_handle_latt */ +/* This routine will handle processing a Host Attention */ +/* Link Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + volatile uint32_t control; + unsigned long iflag; + + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* called from host_interrupt, to process LATT */ + psli = &phba->sli; + psli->slistat.linkEvent++; + + /* Get a buffer which will be used for mailbox commands */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + if (lpfc_read_la(phba, pmb) == 0) { + pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la; + if (lpfc_sli_issue_mbox + (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + /* Turn off Link Attention interrupts until + CLEAR_LA done */ + psli->sliinit.sli_flag &= ~LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control &= ~HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return; + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/************************************************************************/ +/* */ +/* lpfc_parse_vpd */ +/* This routine will parse the VPD data */ +/* */ +/************************************************************************/ +static int +lpfc_parse_vpd(struct lpfc_hba * phba, uint8_t * vpd) +{ + uint8_t lenlo, lenhi; + uint8_t *Length; + int i, j; + int finished = 0; + int index = 0; + + /* Vital Product */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0455 Vital Product Data: x%x x%x x%x x%x\n", + phba->brd_no, + (uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2], + (uint32_t) vpd[3]); + do { + switch (vpd[index]) { + case 0x82: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + i = ((((unsigned short)lenhi) << 8) + lenlo); + index += i; + break; + case 0x90: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + i = ((((unsigned short)lenhi) << 8) + lenlo); + do { + /* Look for Serial Number */ + if ((vpd[index] == 'S') + && (vpd[index + 1] == 'N')) { + index += 2; + Length = &vpd[index]; + index += 1; + i = *Length; + j = 0; + while (i--) { + phba->SerialNumber[j++] = + vpd[index++]; + if (j == 31) + break; + } + phba->SerialNumber[j] = 0; + return (1); + } else { + index += 2; + Length = &vpd[index]; + index += 1; + j = (int)(*Length); + index += j; + i -= (3 + j); + } + } while (i > 0); + finished = 0; + break; + case 0x78: + finished = 1; + break; + default: + return (0); + } + } while (!finished); + return (1); +} + +/**************************************************/ +/* lpfc_post_buffer */ +/* */ +/* This routine will post count buffers to the */ +/* ring with the QUE_RING_BUF_CN command. This */ +/* allows 3 buffers / command to be posted. */ +/* Returns the number of buffers NOT posted. */ +/**************************************************/ +int +lpfc_post_buffer(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, int cnt, + int type) +{ + IOCB_t *icmd; + struct lpfc_iocbq *iocb; + struct lpfc_dmabuf *mp1, *mp2; + + cnt += pring->missbufcnt; + + /* While there are buffers to post */ + while (cnt > 0) { + /* Allocate buffer for command iocb */ + if ((iocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) + == 0) { + pring->missbufcnt = cnt; + return (cnt); + } + memset(iocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &iocb->iocb; + + /* 2 buffers can be posted per command */ + /* Allocate buffer to post */ + mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (mp1) + mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp1->phys); + if (mp1 == 0 || mp1->virt == 0) { + if (mp1) + kfree(mp1); + + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp1->list); + /* Allocate buffer to post */ + if (cnt > 1) { + mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL); + if (mp2) + mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp2->phys); + if (mp2 == 0 || mp2->virt == 0) { + if (mp2) + kfree(mp2); + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp2->list); + } else { + mp2 = NULL; + } + + icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys); + icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys); + icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE; + icmd->ulpBdeCount = 1; + cnt--; + if (mp2) { + icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys); + icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys); + icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE; + cnt--; + icmd->ulpBdeCount = 2; + } + + icmd->ulpCommand = CMD_QUE_RING_BUF64_CN; + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpLe = 1; + + if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) { + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + if (mp2) { + lpfc_mbuf_free(phba, mp2->virt, mp2->phys); + kfree(mp2); + } + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + lpfc_sli_ringpostbuf_put(phba, pring, mp1); + if (mp2) { + lpfc_sli_ringpostbuf_put(phba, pring, mp2); + } + } + pring->missbufcnt = 0; + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_post_rcv_buf */ +/* This routine post initial rcv buffers to the configured rings */ +/* */ +/************************************************************************/ +static int +lpfc_post_rcv_buf(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Ring 0, ELS / CT buffers */ + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1); + /* Ring 2 - FCP no buffers needed */ + + return 0; +} + +#define S(N,V) (((V)<<(N))|((V)>>(32-(N)))) + +/************************************************************************/ +/* */ +/* lpfc_sha_init */ +/* */ +/************************************************************************/ +static void +lpfc_sha_init(uint32_t * HashResultPointer) +{ + HashResultPointer[0] = 0x67452301; + HashResultPointer[1] = 0xEFCDAB89; + HashResultPointer[2] = 0x98BADCFE; + HashResultPointer[3] = 0x10325476; + HashResultPointer[4] = 0xC3D2E1F0; +} + +/************************************************************************/ +/* */ +/* lpfc_sha_iterate */ +/* */ +/************************************************************************/ +static void +lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer) +{ + int t; + uint32_t TEMP; + uint32_t A, B, C, D, E; + t = 16; + do { + HashWorkingPointer[t] = + S(1, + HashWorkingPointer[t - 3] ^ HashWorkingPointer[t - + 8] ^ + HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]); + } while (++t <= 79); + t = 0; + A = HashResultPointer[0]; + B = HashResultPointer[1]; + C = HashResultPointer[2]; + D = HashResultPointer[3]; + E = HashResultPointer[4]; + + do { + if (t < 20) { + TEMP = ((B & C) | ((~B) & D)) + 0x5A827999; + } else if (t < 40) { + TEMP = (B ^ C ^ D) + 0x6ED9EBA1; + } else if (t < 60) { + TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC; + } else { + TEMP = (B ^ C ^ D) + 0xCA62C1D6; + } + TEMP += S(5, A) + E + HashWorkingPointer[t]; + E = D; + D = C; + C = S(30, B); + B = A; + A = TEMP; + } while (++t <= 79); + + HashResultPointer[0] += A; + HashResultPointer[1] += B; + HashResultPointer[2] += C; + HashResultPointer[3] += D; + HashResultPointer[4] += E; + +} + +/************************************************************************/ +/* */ +/* lpfc_challenge_key */ +/* */ +/************************************************************************/ +static void +lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking) +{ + *HashWorking = (*RandomChallenge ^ *HashWorking); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_init */ +/* */ +/************************************************************************/ +void +lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit) +{ + int t; + uint32_t *HashWorking; + uint32_t *pwwnn = phba->wwnn; + + HashWorking = kmalloc(80 * sizeof(uint32_t), GFP_ATOMIC); + if (!HashWorking) + return; + + memset(HashWorking, 0, (80 * sizeof(uint32_t))); + HashWorking[0] = HashWorking[78] = *pwwnn++; + HashWorking[1] = HashWorking[79] = *pwwnn; + + for (t = 0; t < 7; t++) + lpfc_challenge_key(phba->RandomData + t, HashWorking + t); + + lpfc_sha_init(hbainit); + lpfc_sha_iterate(hbainit, HashWorking); + kfree(HashWorking); +} + +static void +lpfc_consistent_bind_cleanup(struct lpfc_hba * phba) +{ + struct lpfc_bindlist *bdlp, *next_bdlp; + + list_for_each_entry_safe(bdlp, next_bdlp, + &phba->fc_nlpbind_list, nlp_listp) { + list_del(&bdlp->nlp_listp); + mempool_free( bdlp, phba->bind_mem_pool); + } + phba->fc_bind_cnt = 0; +} + +void +lpfc_cleanup(struct lpfc_hba * phba, uint32_t save_bind) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + /* clean up phba - lpfc specific */ + lpfc_can_disctmo(phba); + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_reglogin_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + if (save_bind == 0) { + lpfc_consistent_bind_cleanup(phba); + } + + INIT_LIST_HEAD(&phba->fc_nlpmap_list); + INIT_LIST_HEAD(&phba->fc_nlpunmap_list); + INIT_LIST_HEAD(&phba->fc_unused_list); + INIT_LIST_HEAD(&phba->fc_plogi_list); + INIT_LIST_HEAD(&phba->fc_adisc_list); + INIT_LIST_HEAD(&phba->fc_reglogin_list); + INIT_LIST_HEAD(&phba->fc_prli_list); + INIT_LIST_HEAD(&phba->fc_npr_list); + + phba->fc_map_cnt = 0; + phba->fc_unmap_cnt = 0; + phba->fc_plogi_cnt = 0; + phba->fc_adisc_cnt = 0; + phba->fc_reglogin_cnt = 0; + phba->fc_prli_cnt = 0; + phba->fc_npr_cnt = 0; + phba->fc_unused_cnt= 0; + return; +} + +void +lpfc_establish_link_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* Re-establishing Link, timer expired */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1300 Re-establishing Link, timer expired " + "Data: x%x x%x\n", + phba->brd_no, phba->fc_flag, phba->hba_state); + phba->fc_flag &= ~FC_ESTABLISH_LINK; + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +int +lpfc_online(struct lpfc_hba * phba) +{ + if (!phba) + return 0; + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) + return 0; + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0458 Bring Adapter online\n", + phba->brd_no); + + if (!lpfc_sli_queue_setup(phba)) + return 1; + + if (lpfc_sli_hba_setup(phba)) /* Initialize the HBA */ + return 1; + + phba->fc_flag &= ~FC_OFFLINE_MODE; + + /* + * Restart all traffic to this host. Since the fc_transport block + * functions (future) were not called in lpfc_offline, don't call them + * here. + */ + scsi_unblock_requests(phba->host); + return 0; +} + +int +lpfc_offline(struct lpfc_hba * phba) +{ + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + unsigned long iflag; + int i = 0; + + if (!phba) + return 0; + + if (phba->fc_flag & FC_OFFLINE_MODE) + return 0; + + /* + * Don't call the fc_transport block api (future). The device is + * going offline and causing a timer to fire in the midlayer is + * unproductive. Just block all new requests until the driver + * comes back online. + */ + scsi_block_requests(phba->host); + psli = &phba->sli; + pring = &psli->ring[psli->fcp_ring]; + + lpfc_linkdown(phba); + + /* The linkdown event takes 30 seconds to timeout. */ + while (pring->txcmplq_cnt) { + mdelay(10); + if (i++ > 3000) + break; + } + + /* stop all timers associated with this hba */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_stop_timer(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0460 Bring Adapter offline\n", + phba->brd_no); + + /* Bring down the SLI Layer and cleanup. The HBA is offline + now. */ + lpfc_sli_hba_down(phba); + lpfc_cleanup(phba, 1); + phba->fc_flag |= FC_OFFLINE_MODE; + return 0; +} + +/****************************************************************************** +* Function name : lpfc_scsi_free +* +* Description : Called from fc_detach to free scsi tgt / lun resources +* +******************************************************************************/ +int +lpfc_scsi_free(struct lpfc_hba * phba) +{ + struct lpfc_target *targetp; + int i; + + for (i = 0; i < MAX_FCP_TARGET; i++) { + targetp = phba->device_queue_hash[i]; + if (targetp) { + kfree(targetp); + phba->device_queue_hash[i] = NULL; + } + } + return 0; +} + +static void +lpfc_wakeup_event(struct lpfc_hba * phba, fcEVTHDR_t * ep) +{ + ep->e_mode &= ~E_SLEEPING_MODE; + switch (ep->e_mask) { + case FC_REG_LINK_EVENT: + wake_up_interruptible(&phba->linkevtwq); + break; + case FC_REG_RSCN_EVENT: + wake_up_interruptible(&phba->rscnevtwq); + break; + case FC_REG_CT_EVENT: + wake_up_interruptible(&phba->ctevtwq); + break; + } + return; +} + +int +lpfc_put_event(struct lpfc_hba * phba, uint32_t evcode, uint32_t evdata0, + void * evdata1, uint32_t evdata2, uint32_t evdata3) +{ + fcEVT_t *ep; + fcEVTHDR_t *ehp = phba->fc_evt_head; + int found = 0; + void *fstype = NULL; + struct lpfc_dmabuf *mp; + struct lpfc_sli_ct_request *ctp; + struct lpfc_hba_event *rec; + uint32_t evtype; + + switch (evcode) { + case HBA_EVENT_RSCN: + evtype = FC_REG_RSCN_EVENT; + break; + case HBA_EVENT_LINK_DOWN: + case HBA_EVENT_LINK_UP: + evtype = FC_REG_LINK_EVENT; + break; + default: + evtype = FC_REG_CT_EVENT; + } + + if (evtype == FC_REG_RSCN_EVENT || evtype == FC_REG_LINK_EVENT) { + rec = &phba->hbaevt[phba->hba_event_put]; + rec->fc_eventcode = evcode; + rec->fc_evdata1 = evdata0; + rec->fc_evdata2 = (uint32_t)(unsigned long)evdata1; + rec->fc_evdata3 = evdata2; + rec->fc_evdata4 = evdata3; + + phba->hba_event_put++; + if (phba->hba_event_put >= MAX_HBAEVT) + phba->hba_event_put = 0; + + if (phba->hba_event_put == phba->hba_event_get) { + phba->hba_event_missed++; + phba->hba_event_get++; + if (phba->hba_event_get >= MAX_HBAEVT) + phba->hba_event_get = 0; + } + } + + if (evtype == FC_REG_CT_EVENT) { + mp = (struct lpfc_dmabuf *) evdata1; + ctp = (struct lpfc_sli_ct_request *) mp->virt; + fstype = (void *)(ulong) (ctp->FsType); + } + + while (ehp && ((ehp->e_mask != evtype) || (ehp->e_type != fstype))) + ehp = (fcEVTHDR_t *) ehp->e_next_header; + + if (!ehp) + return (0); + + ep = ehp->e_head; + + while (ep && !(found)) { + if (ep->evt_sleep) { + switch (evtype) { + case FC_REG_CT_EVENT: + if ((ep->evt_type == + (void *)(ulong) FC_FSTYPE_ALL) + || (ep->evt_type == fstype)) { + found++; + ep->evt_data0 = evdata0; /* tag */ + ep->evt_data1 = evdata1; /* buffer + ptr */ + ep->evt_data2 = evdata2; /* count */ + ep->evt_sleep = 0; + if (ehp->e_mode & E_SLEEPING_MODE) { + ehp->e_flag |= + E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For FC_REG_CT_EVENT just give it to + first one found */ + } + break; + default: + found++; + ep->evt_data0 = evdata0; + ep->evt_data1 = evdata1; + ep->evt_data2 = evdata2; + ep->evt_sleep = 0; + if ((ehp->e_mode & E_SLEEPING_MODE) + && !(ehp->e_flag & E_GET_EVENT_ACTIVE)) { + ehp->e_flag |= E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For all other events, give it to every one + waiting */ + break; + } + } + ep = ep->evt_next; + } + if (evtype == FC_REG_LINK_EVENT) + phba->nport_event_cnt++; + + return (found); +} + +int +lpfc_stop_timer(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Instead of a timer, this has been converted to a + * deferred procedding list. + */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *)(phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + + del_timer_sync(&phba->fc_estabtmo); + del_timer_sync(&phba->fc_disctmo); + del_timer_sync(&phba->fc_scantmo); + del_timer_sync(&phba->fc_fdmitmo); + del_timer_sync(&phba->els_tmofunc); + psli = &phba->sli; + del_timer_sync(&psli->mbox_tmo); + return(1); +} diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h new file mode 100644 index 000000000..5540e23f9 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_logmsg.h @@ -0,0 +1,46 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_logmsg.h 1.31 2004/08/26 19:17:52EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_LOGMSG +#define _H_LPFC_LOGMSG + +#define LOG_ELS 0x1 /* ELS events */ +#define LOG_DISCOVERY 0x2 /* Link discovery events */ +#define LOG_MBOX 0x4 /* Mailbox events */ +#define LOG_INIT 0x8 /* Initialization events */ +#define LOG_LINK_EVENT 0x10 /* Link events */ +#define LOG_IP 0x20 /* IP traffic history */ +#define LOG_FCP 0x40 /* FCP traffic history */ +#define LOG_NODE 0x80 /* Node table events */ +#define LOG_MISC 0x400 /* Miscellaneous events */ +#define LOG_SLI 0x800 /* SLI events */ +#define LOG_CHK_COND 0x1000 /* FCP Check condition flag */ +#define LOG_LIBDFC 0x2000 /* Libdfc events */ +#define LOG_ALL_MSG 0xffff /* LOG all messages */ + +#define lpfc_printf_log(phba, level, mask, fmt, arg...) \ + { if (((mask) &(phba)->cfg_log_verbose) || (level[1] <= '3')) \ + dev_printk(level, &((phba)->pcidev)->dev, fmt, ##arg); } +#endif + diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c new file mode 100644 index 000000000..436d1b7c5 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -0,0 +1,672 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mbox.c 1.70 2004/11/18 17:33:04EST sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" + +/**********************************************/ + +/* mailbox command */ +/**********************************************/ +void +lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + /* Setup to dump VPD region */ + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_DUMP_MEMORY; + mb->un.varDmp.cv = 1; + mb->un.varDmp.type = DMP_NV_PARAMS; + mb->un.varDmp.region_id = DMP_REGION_VPD; + mb->un.varDmp.word_cnt = (DMP_VPD_SIZE / sizeof (uint32_t)); + + mb->un.varDmp.co = 0; + mb->un.varDmp.resp_offset = 0; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_nv Issue a READ NVPARAM */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_READ_NV; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_la Issue a READ LA */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* Get a buffer to hold the loop map */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_LA64; + /* READ_LA: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0300 READ_LA: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_LA64; + mb->un.varReadLA.un.lilpBde64.tus.f.bdeSize = 128; + mb->un.varReadLA.un.lilpBde64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varReadLA.un.lilpBde64.addrLow = putPaddrLow(mp->phys); + + /* Sync the mailbox data with its PCI memory address now. */ + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* Save address for later completion and set the owner to host so that + * the FW knows this mailbox is available for processing. + */ + pmb->context1 = (uint8_t *) mp; + mb->mbxOwner = OWN_HOST; + return (0); +} + +/**********************************************/ +/* lpfc_clear_la Issue a CLEAR LA */ +/* mailbox command */ +/**********************************************/ +void +lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varClearLA.eventTag = phba->fc_eventTag; + mb->mbxCommand = MBX_CLEAR_LA; + mb->mbxOwner = OWN_HOST; + return; +} + +/**************************************************/ +/* lpfc_config_link Issue a CONFIG LINK */ +/* mailbox command */ +/**************************************************/ +void +lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* NEW_FEATURE + * SLI-2, Coalescing Response Feature. + */ + if (phba->cfg_cr_delay) { + mb->un.varCfgLnk.cr = 1; + mb->un.varCfgLnk.ci = 1; + mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay; + mb->un.varCfgLnk.cr_count = phba->cfg_cr_count; + } + + mb->un.varCfgLnk.myId = phba->fc_myDID; + mb->un.varCfgLnk.edtov = phba->fc_edtov; + mb->un.varCfgLnk.arbtov = phba->fc_arbtov; + mb->un.varCfgLnk.ratov = phba->fc_ratov; + mb->un.varCfgLnk.rttov = phba->fc_rttov; + mb->un.varCfgLnk.altov = phba->fc_altov; + mb->un.varCfgLnk.crtov = phba->fc_crtov; + mb->un.varCfgLnk.citov = phba->fc_citov; + + if (phba->cfg_ack0) + mb->un.varCfgLnk.ack0_enable = 1; + + mb->mbxCommand = MBX_CONFIG_LINK; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_init_link Issue an INIT LINK */ +/* mailbox command */ +/**********************************************/ +void +lpfc_init_link(struct lpfc_hba * phba, + LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed) +{ + lpfc_vpd_t *vpd; + struct lpfc_sli *psli; + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + psli = &phba->sli; + switch (topology) { + case FLAGS_TOPOLOGY_MODE_LOOP_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + case FLAGS_TOPOLOGY_MODE_PT_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + break; + case FLAGS_TOPOLOGY_MODE_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + break; + case FLAGS_TOPOLOGY_MODE_PT_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + } + + /* NEW_FEATURE + * Setting up the link speed + */ + vpd = &phba->vpd; + if (vpd->rev.feaLevelHigh >= 0x02){ + switch(linkspeed){ + case LINK_SPEED_1G: + case LINK_SPEED_2G: + case LINK_SPEED_4G: + mb->un.varInitLnk.link_flags |= + FLAGS_LINK_SPEED; + mb->un.varInitLnk.link_speed = linkspeed; + break; + case LINK_SPEED_AUTO: + default: + mb->un.varInitLnk.link_speed = + LINK_SPEED_AUTO; + break; + } + + } + else + mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO; + + mb->mbxCommand = (volatile uint8_t)MBX_INIT_LINK; + mb->mbxOwner = OWN_HOST; + mb->un.varInitLnk.fabric_AL_PA = phba->fc_pref_ALPA; + return; +} + +/**********************************************/ +/* lpfc_read_sparam Issue a READ SPARAM */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold the HBAs Service Parameters */ + + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_SPARM64; + /* READ_SPARAM: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0301 READ_SPARAM: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_SPARM64; + mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys); + + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* save address for completion */ + pmb->context1 = mp; + + return (0); +} + +/********************************************/ +/* lpfc_unreg_did Issue a UNREG_DID */ +/* mailbox command */ +/********************************************/ +void +lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregDID.did = did; + + mb->mbxCommand = MBX_UNREG_D_ID; + mb->mbxOwner = OWN_HOST; + return; +} + +/***********************************************/ + +/* command to write slim */ +/***********************************************/ +void +lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr, + uint32_t value) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* addr = 0x090597 is AUTO ABTS disable for ELS commands */ + /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */ + + /* + * Always turn on DELAYED ABTS for ELS timeouts + */ + if ((addr == 0x052198) && (value == 0)) + value = 1; + + mb->un.varWords[0] = addr; + mb->un.varWords[1] = value; + + mb->mbxCommand = MBX_SET_SLIM; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_nv Issue a READ CONFIG */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxCommand = MBX_READ_CONFIG; + mb->mbxOwner = OWN_HOST; + return; +} + +/********************************************/ +/* lpfc_reg_login Issue a REG_LOGIN */ +/* mailbox command */ +/********************************************/ +int +lpfc_reg_login(struct lpfc_hba * phba, + uint32_t did, uint8_t * param, LPFC_MBOXQ_t * pmb, uint32_t flag) +{ + uint8_t *sparam; + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varRegLogin.rpi = 0; + mb->un.varRegLogin.did = did; + mb->un.varWords[30] = flag; /* Set flag to issue action on cmpl */ + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold NPorts Service Parameters */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + + mb->mbxCommand = MBX_REG_LOGIN64; + /* REG_LOGIN: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0302 REG_LOGIN: no buffers Data x%x x%x\n", + phba->brd_no, + (uint32_t) did, (uint32_t) flag); + return (1); + } + INIT_LIST_HEAD(&mp->list); + sparam = mp->virt; + + /* Copy param's into a new buffer */ + memcpy(sparam, param, sizeof (struct serv_parm)); + + /* Sync the mailbox data with its PCI memory address now. */ + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* save address for completion */ + pmb->context1 = (uint8_t *) mp; + + mb->mbxCommand = MBX_REG_LOGIN64; + mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys); + + return (0); +} + +/**********************************************/ +/* lpfc_unreg_login Issue a UNREG_LOGIN */ +/* mailbox command */ +/**********************************************/ +void +lpfc_unreg_login(struct lpfc_hba * phba, uint32_t rpi, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregLogin.rpi = (uint16_t) rpi; + mb->un.varUnregLogin.rsvd1 = 0; + + mb->mbxCommand = MBX_UNREG_LOGIN; + mb->mbxOwner = OWN_HOST; + return; +} + +static void +lpfc_config_pcb_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + PCB_t *pcbp = &phba->slim2p->pcb; + LPFC_RING_INIT_t *pringinit; + dma_addr_t pdma_addr; + uint32_t offset; + uint32_t iocbCnt; + int i; + + psli->MBhostaddr = (uint32_t *)&phba->slim2p->mbx; + pcbp->maxRing = (psli->sliinit.num_rings - 1); + + iocbCnt = 0; + for (i = 0; i < psli->sliinit.num_rings; i++) { + pringinit = &psli->sliinit.ringinit[i]; + pring = &psli->ring[i]; + /* A ring MUST have both cmd and rsp entries defined to be + valid */ + if ((pringinit->numCiocb == 0) || (pringinit->numRiocb == 0)) { + pcbp->rdsc[i].cmdEntries = 0; + pcbp->rdsc[i].rspEntries = 0; + pcbp->rdsc[i].cmdAddrHigh = 0; + pcbp->rdsc[i].rspAddrHigh = 0; + pcbp->rdsc[i].cmdAddrLow = 0; + pcbp->rdsc[i].rspAddrLow = 0; + pring->cmdringaddr = NULL; + pring->rspringaddr = NULL; + continue; + } + /* Command ring setup for ring */ + pring->cmdringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + pcbp->rdsc[i].cmdEntries = pringinit->numCiocb; + + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numCiocb; + + /* Response ring setup for ring */ + pring->rspringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + + pcbp->rdsc[i].rspEntries = pringinit->numRiocb; + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numRiocb; + } +} + +void +lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->un.varRdRev.cv = 1; + mb->mbxCommand = MBX_READ_REV; + mb->mbxOwner = OWN_HOST; + return; +} + +void +lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb) +{ + int i; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_sli *psli; + LPFC_RING_INIT_t *pring; + + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varCfgRing.ring = ring; + mb->un.varCfgRing.maxOrigXchg = 0; + mb->un.varCfgRing.maxRespXchg = 0; + mb->un.varCfgRing.recvNotify = 1; + + psli = &phba->sli; + pring = &psli->sliinit.ringinit[ring]; + mb->un.varCfgRing.numMask = pring->num_mask; + mb->mbxCommand = MBX_CONFIG_RING; + mb->mbxOwner = OWN_HOST; + + /* Is this ring configured for a specific profile */ + if (pring->prt[0].profile) { + mb->un.varCfgRing.profile = pring->prt[0].profile; + return; + } + + /* Otherwise we setup specific rctl / type masks for this ring */ + for (i = 0; i < pring->num_mask; i++) { + mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl; + if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ) + mb->un.varCfgRing.rrRegs[i].rmask = 0xff; + else + mb->un.varCfgRing.rrRegs[i].rmask = 0xfe; + mb->un.varCfgRing.rrRegs[i].tval = pring->prt[i].type; + mb->un.varCfgRing.rrRegs[i].tmask = 0xff; + } + + return; +} + +void +lpfc_config_port(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + dma_addr_t pdma_addr; + uint32_t bar_low, bar_high; + size_t offset; + HGP hgp; + void *to_slim; + + memset(pmb, 0, sizeof(LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_CONFIG_PORT; + mb->mbxOwner = OWN_HOST; + + mb->un.varCfgPort.pcbLen = sizeof(PCB_t); + offset = (uint8_t *)&phba->slim2p->pcb - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr); + mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr); + + /* Now setup pcb */ + phba->slim2p->pcb.type = TYPE_NATIVE_SLI2; + phba->slim2p->pcb.feature = FEATURE_INITIAL_SLI2; + + /* Setup Mailbox pointers */ + phba->slim2p->pcb.mailBoxSize = sizeof(MAILBOX_t); + offset = (uint8_t *)&phba->slim2p->mbx - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.mbAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.mbAddrLow = putPaddrLow(pdma_addr); + + /* + * Setup Host Group ring pointer. + * + * For efficiency reasons, the ring get/put pointers can be + * placed in adapter memory (SLIM) rather than in host memory. + * This allows firmware to avoid PCI reads/writes when updating + * and checking pointers. + * + * The firmware recognizes the use of SLIM memory by comparing + * the address of the get/put pointers structure with that of + * the SLIM BAR (BAR0). + * + * Caution: be sure to use the PCI config space value of BAR0/BAR1 + * (the hardware's view of the base address), not the OS's + * value of pci_resource_start() as the OS value may be a cookie + * for ioremap/iomap. + */ + + + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_0, &bar_low); + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_1, &bar_high); + + + /* mask off BAR0's flag bits 0 - 3 */ + phba->slim2p->pcb.hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) + + (SLIMOFF*sizeof(uint32_t)); + if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64) + phba->slim2p->pcb.hgpAddrHigh = bar_high; + else + phba->slim2p->pcb.hgpAddrHigh = 0; + /* write HGP data to SLIM at the required longword offset */ + memset(&hgp, 0, sizeof(HGP)); + to_slim = (uint8_t *)phba->MBslimaddr + (SLIMOFF*sizeof (uint32_t)); + lpfc_memcpy_to_slim(to_slim, &hgp, sizeof (HGP)); + + /* Setup Port Group ring pointer */ + offset = (uint8_t *)&phba->slim2p->mbx.us.s2.port - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.pgpAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.pgpAddrLow = putPaddrLow(pdma_addr); + + /* Use callback routine to setp rings in the pcb */ + lpfc_config_pcb_setup(phba); + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + uint32_t hbainit[5]; + + lpfc_hba_init(phba, hbainit); + + memcpy(&mb->un.varCfgPort.hbainit, hbainit, 20); + } + + /* Swap PCB if needed */ + lpfc_sli_pcimem_bcopy((uint32_t *)&phba->slim2p->pcb, + (uint32_t *)&phba->slim2p->pcb, + sizeof (PCB_t)); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "%d:0405 Service Level Interface (SLI) 2 selected\n", + phba->brd_no); +} + +void +lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq) +{ + struct lpfc_sli *psli; + + psli = &phba->sli; + + list_add_tail(&mbq->list, &psli->mboxq); + + psli->mboxq_cnt++; + + return; +} + +LPFC_MBOXQ_t * +lpfc_mbox_get(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *mbq = NULL; + struct lpfc_sli *psli = &phba->sli; + + if (!list_empty(&psli->mboxq)) { + mbq = list_entry(psli->mboxq.next, LPFC_MBOXQ_t, list); + list_del_init(&mbq->list); + psli->mboxq_cnt--; + } + + return mbq; +} diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c new file mode 100644 index 000000000..16e35d040 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -0,0 +1,192 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mem.c 1.69 2004/09/28 07:54:24EDT sf_support Exp $ + */ + +#include +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_mem.h" + +static void * +lpfc_pool_kmalloc(int gfp_flags, void *data) +{ + return kmalloc((unsigned long)data, gfp_flags); +} + +static void +lpfc_pool_kfree(void *obj, void *data) +{ + kfree(obj); +} + +int +lpfc_mem_alloc(struct lpfc_hba * phba) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + int i; + + phba->lpfc_scsi_dma_ext_pool = pci_pool_create("lpfc_scsi_dma_ext_pool", + phba->pcidev, LPFC_SCSI_DMA_EXT_SIZE, 8, 0); + if (!phba->lpfc_scsi_dma_ext_pool) + goto fail; + + phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev, + LPFC_BPL_SIZE, 8,0); + if (!phba->lpfc_mbuf_pool) + goto fail_free_dma_ext_pool; + + pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) * + LPFC_MBUF_POOL_SIZE, GFP_KERNEL); + pool->max_count = 0; + pool->current_count = 0; + for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) { + pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool, + GFP_KERNEL, &pool->elements[i].phys); + if (!pool->elements[i].virt) + goto fail_free_mbuf_pool; + pool->max_count++; + pool->current_count++; + } + + phba->iocb_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_iocbq)); + if (!phba->iocb_mem_pool) + goto fail_free_mbuf_pool; + + phba->scsibuf_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_scsi_buf)); + if (!phba->scsibuf_mem_pool) + goto fail_free_iocb_pool; + + phba->mbox_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(LPFC_MBOXQ_t)); + if (!phba->mbox_mem_pool) + goto fail_free_scsibuf_pool; + + phba->nlp_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_nodelist)); + if (!phba->nlp_mem_pool) + goto fail_free_mbox_pool; + + phba->bind_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_bindlist)); + if (!phba->bind_mem_pool) + goto fail_free_nlp_pool; + + return 0; + + fail_free_nlp_pool: + mempool_destroy(phba->nlp_mem_pool); + fail_free_mbox_pool: + mempool_destroy(phba->mbox_mem_pool); + fail_free_scsibuf_pool: + mempool_destroy(phba->scsibuf_mem_pool); + fail_free_iocb_pool: + mempool_destroy(phba->iocb_mem_pool); + fail_free_mbuf_pool: + while (--i) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + pci_pool_destroy(phba->lpfc_mbuf_pool); + fail_free_dma_ext_pool: + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + fail: + return -ENOMEM; +} + +void +lpfc_mem_free(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + LPFC_MBOXQ_t *mbox, *next_mbox; + int i; + + list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) { + list_del(&mbox->list); + mempool_free(mbox, phba->mbox_mem_pool); + } + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (psli->mbox_active) { + mempool_free(psli->mbox_active, phba->mbox_mem_pool); + psli->mbox_active = NULL; + } + + for (i = 0; i < pool->current_count; i++) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + mempool_destroy(phba->bind_mem_pool); + mempool_destroy(phba->nlp_mem_pool); + mempool_destroy(phba->mbox_mem_pool); + mempool_destroy(phba->scsibuf_mem_pool); + mempool_destroy(phba->iocb_mem_pool); + + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + pci_pool_destroy(phba->lpfc_mbuf_pool); +} + +void * +lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + void *ret; + + ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_ATOMIC, handle); + + if (!ret && ( mem_flags & MEM_PRI) && pool->current_count) { + pool->current_count--; + ret = pool->elements[pool->current_count].virt; + *handle = pool->elements[pool->current_count].phys; + } + return ret; +} + +void +lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + + if (pool->current_count < pool->max_count) { + pool->elements[pool->current_count].virt = virt; + pool->elements[pool->current_count].phys = dma; + pool->current_count++; + } else { + pci_pool_free(phba->lpfc_mbuf_pool, virt, dma); + } + return; +} diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c new file mode 100644 index 000000000..c3f431bff --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -0,0 +1,2145 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_nportdisc.c 1.146 2004/11/18 14:53:54EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +extern uint8_t lpfcAlpaArray[]; + + +/* Called to verify a rcv'ed ADISC was intended for us. */ +static int +lpfc_check_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + struct lpfc_name * nn, struct lpfc_name * pn) +{ + /* Compare the ADISC rsp WWNN / WWPN matches our internal node + * table entry for that node. + */ + if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)) != 0) + return (0); + + if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0) + return (0); + + /* we match, return success */ + return (1); +} + + +int +lpfc_check_sparm(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct serv_parm * sp, + uint32_t class) +{ + volatile struct serv_parm *hsp = &phba->fc_sparam; + /* First check for supported version */ + + /* Next check for class validity */ + if (sp->cls1.classValid) { + + if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb) + sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb; + if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb) + sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb; + } else if (class == CLASS1) { + return (0); + } + + if (sp->cls2.classValid) { + + if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb) + sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb; + if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb) + sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb; + } else if (class == CLASS2) { + return (0); + } + + if (sp->cls3.classValid) { + + if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb) + sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb; + if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb) + sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb; + } else if (class == CLASS3) { + return (0); + } + + if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb) + sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb; + if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb) + sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb; + + /* If check is good, copy wwpn wwnn into ndlp */ + memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name)); + return (1); +} + +static void * +lpfc_check_elscmpl_iocb(struct lpfc_hba * phba, + struct lpfc_iocbq *cmdiocb, + struct lpfc_iocbq *rspiocb) +{ + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + void *ptr; + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* For lpfc_els_abort, context2 could be zero'ed to delay + * freeing associated memory till after ABTS completes. + */ + if (pcmd) { + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + ptr = (void *)((uint8_t *)lp + sizeof(uint32_t)); + } + else { + /* Force ulpStatus error since we are returning NULL ptr */ + if (!(irsp->ulpStatus)) { + irsp->ulpStatus = IOSTAT_LOCAL_REJECT; + irsp->un.ulpWord[4] = IOERR_SLI_ABORTED; + } + ptr = NULL; + } + return (ptr); +} + + +/* + * Free resources / clean up outstanding I/Os + * associated with a LPFC_NODELIST entry. This + * routine effectively results in a "software abort". + */ +static int +lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + int send_abts) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0201 Abort outstanding I/O on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* First check the txq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + + /* Everything on txcmplq will be returned by firmware + * with a no rpi / linkdown / abort error. For ring 0, + * ELS discovery, we want to get rid of it right here. + */ + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + icmd = &iocb->iocb; + /* If the driver is completing an ELS + * command early, flush it out of the firmware. + */ + if (send_abts && + (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) && + (icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32(phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + + /* If we are delaying issuing an ELS command, cancel it */ + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&ndlp->nlp_delayfunc); + } + return (0); +} + +static int +lpfc_rcv_plogi(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + + memset(&stat, 0, sizeof (struct ls_rjt)); + if (phba->hba_state <= LPFC_FLOGI) { + /* Before responding to PLOGI, check for pt2pt mode. + * If we are pt2pt, with an outstanding FLOGI, abort + * the FLOGI and resend it first. + */ + if (phba->fc_flag & FC_PT2PT) { + lpfc_els_abort_flogi(phba); + if(!(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* If the other side is supposed to initiate + * the PLOGI anyway, just ACC it now and + * move on with discovery. + */ + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + else { + lpfc_initial_flogi(phba); + } + } + else { + stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; + goto out; + } + } + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3) == 0)) { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + goto out; + } + icmd = &cmdiocb->iocb; + + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, + ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + /* no need to reg_login if we are already in one of these states */ + switch(ndlp->nlp_state) { + case NLP_STE_REG_LOGIN_ISSUE: + case NLP_STE_PRLI_ISSUE: + case NLP_STE_UNMAPPED_NODE: + case NLP_STE_MAPPED_NODE: + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0); + return (1); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + + if ((phba->fc_flag & FC_PT2PT) + && !(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* rcv'ed PLOGI decides what our NPortId will be */ + phba->fc_myDID = icmd->un.rcvels.parmRo; + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + lpfc_can_disctmo(phba); + } + + if(lpfc_reg_login(phba, icmd->un.rcvels.remoteID, + (uint8_t *) sp, mbox, 0)) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; +out: + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (0); + } + + /* ACC PLOGI rsp command needs to execute first, + * queue this mbox command to be processed later. + */ + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_flag |= NLP_ACC_REGLOGIN; + + /* If there is an outstanding PLOGI issued, abort it before + * sending ACC rsp to PLOGI recieved. + */ + if(ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) { + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + } + ndlp->nlp_flag |= NLP_RCV_PLOGI; + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0); + return (1); +} + +static int +lpfc_rcv_padisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + struct lpfc_name *pnn, *ppn; + struct ls_rjt stat; + ADISC *ap; + IOCB_t *icmd; + uint32_t *lp; + uint32_t cmd; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + if (cmd == ELS_CMD_ADISC) { + ap = (ADISC *) lp; + pnn = (struct lpfc_name *) & ap->nodeName; + ppn = (struct lpfc_name *) & ap->portName; + } else { + sp = (struct serv_parm *) lp; + pnn = (struct lpfc_name *) & sp->nodeName; + ppn = (struct lpfc_name *) & sp->portName; + } + + icmd = &cmdiocb->iocb; + if ((icmd->ulpStatus == 0) && + (lpfc_check_adisc(phba, ndlp, pnn, ppn))) { + if (cmd == ELS_CMD_ADISC) { + lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp); + } + else { + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, + NULL, 0); + } + return (1); + } + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + + ndlp->nlp_flag |= NLP_DELAY_TMO; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (0); +} + +static int +lpfc_rcv_logo(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */ + /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary + * PLOGIs during LOGO storms from a device. + */ + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if (!(ndlp->nlp_type & NLP_FABRIC)) { + /* Only try to re-login if this is NOT a Fabric Node */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + } + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + /* The driver has to wait until the ACC completes before it continues + * processing the LOGO. The action will resume in + * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an + * unreg_login, the driver waits so the ACC does not get aborted. + */ + return (0); +} + +static int +lpfc_binding_found(struct lpfc_bindlist * blp, struct lpfc_nodelist * ndlp) +{ + uint16_t bindtype = blp->nlp_bind_type; + + if ((bindtype & FCP_SEED_DID) && + (ndlp->nlp_DID == be32_to_cpu(blp->nlp_DID))) { + return (1); + } else if ((bindtype & FCP_SEED_WWPN) && + (memcmp(&ndlp->nlp_portname, &blp->nlp_portname, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } else if ((bindtype & FCP_SEED_WWNN) && + (memcmp(&ndlp->nlp_nodename, &blp->nlp_nodename, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } + return (0); +} + +static int +lpfc_binding_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_bindlist *blp; + + list_for_each_entry(blp, &phba->fc_nlpbind_list, nlp_listp) { + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static int +lpfc_mapping_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_nodelist *mapnode; + struct lpfc_bindlist *blp; + + list_for_each_entry(mapnode, &phba->fc_nlpmap_list, nlp_listp) { + blp = mapnode->nlp_listp_bind; + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static struct lpfc_bindlist * +lpfc_create_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint16_t index, + uint16_t bindtype) +{ + struct lpfc_bindlist *blp; + + if ((blp = mempool_alloc(phba->bind_mem_pool, GFP_ATOMIC))) { + memset(blp, 0, sizeof (struct lpfc_bindlist)); + switch (bindtype) { + case FCP_SEED_WWPN: + blp->nlp_bind_type = FCP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + blp->nlp_bind_type = FCP_SEED_WWNN; + break; + case FCP_SEED_DID: + blp->nlp_bind_type = FCP_SEED_DID; + break; + } + blp->nlp_sid = index; + blp->nlp_DID = ndlp->nlp_DID; + memcpy(&blp->nlp_nodename, &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + memcpy(&blp->nlp_portname, &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + + return (blp); + } + return NULL; +} + + +static struct lpfc_bindlist * +lpfc_consistent_bind_get(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_bindlist *blp, *next_blp; + uint16_t index; + + /* check binding list */ + list_for_each_entry_safe(blp, next_blp, &phba->fc_nlpbind_list, + nlp_listp) { + if (lpfc_binding_found(blp, ndlp)) { + + /* take it off the binding list */ + phba->fc_bind_cnt--; + list_del_init(&blp->nlp_listp); + + /* Reassign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0213 Reassign scsi id x%x to " + "NPort x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + blp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + return (blp); + } + } + + /* NOTE: if scan-down = 2 and we have private loop, then we use + * AlpaArray to determine sid. + */ + if ((phba->cfg_fcp_bind_method == 4) && + ((phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) || + (phba->fc_topology != TOPOLOGY_LOOP))) { + /* Log message: ALPA based binding used on a non loop + topology */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0245 ALPA based bind method used on an HBA " + "which is in a nonloop topology Data: x%x\n", + phba->brd_no, + phba->fc_topology); + } + + if ((phba->cfg_fcp_bind_method == 4) && + !(phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) && + (phba->fc_topology == TOPOLOGY_LOOP)) { + for (index = 0; index < FC_MAXLOOP; index++) { + if (ndlp->nlp_DID == (uint32_t) lpfcAlpaArray[index]) { + if ((blp = + lpfc_create_binding(phba, ndlp, index, + FCP_SEED_DID))) { + return (blp); + } + goto errid; + } + } + } + + if (phba->cfg_automap) { + while (1) { + if ((lpfc_binding_useid(phba, phba->sid_cnt)) + || (lpfc_mapping_useid (phba, phba->sid_cnt))) { + + phba->sid_cnt++; + } else { + if ((blp = + lpfc_create_binding(phba, ndlp, + phba->sid_cnt, + phba->fcp_mapping))) { + blp->nlp_bind_type |= FCP_SEED_AUTO; + + phba->sid_cnt++; + return (blp); + } + goto errid; + } + } + } + /* if automap on */ +errid: + /* Cannot assign scsi id on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0230 Cannot assign scsi ID on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + + return NULL; +} + +static uint32_t +lpfc_assign_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct lpfc_bindlist *blp) +{ + struct lpfc_target *targetp; + + targetp = lpfc_find_target(phba, blp->nlp_sid, ndlp); + if(!targetp) { + /* Cannot assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0229 Cannot assign scsi id x%x to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, blp->nlp_sid, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(0); + } + ndlp->nlp_sid = blp->nlp_sid; + ndlp->nlp_flag &= ~NLP_SEED_MASK; + switch ((blp->nlp_bind_type & FCP_SEED_MASK)) { + case FCP_SEED_WWPN: + ndlp->nlp_flag |= NLP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + ndlp->nlp_flag |= NLP_SEED_WWNN; + break; + case FCP_SEED_DID: + ndlp->nlp_flag |= NLP_SEED_DID; + break; + } + if (blp->nlp_bind_type & FCP_SEED_AUTO) { + ndlp->nlp_flag |= NLP_AUTOMAP; + } + /* Assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0216 Assign scsi " + "id x%x to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, + ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(1); +} + +static uint32_t +lpfc_disc_set_adisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp) +{ + /* Check config parameter use-adisc or FCP-2 */ + if ((phba->cfg_use_adisc == 0) && + !(phba->fc_flag & FC_RSCN_MODE)) { + return (0); + } + ndlp->nlp_flag |= NLP_NPR_ADISC; + return (1); +} + +static uint32_t +lpfc_disc_noop(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_disc_illegal(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0253 Illegal State Transition: node x%x event x%x, " + "state x%x Data: x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, + ndlp->nlp_flag); + return (ndlp->nlp_state); +} + +/* Start of Discovery State Machine routines */ + +static uint32_t +lpfc_rcv_plogi_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_state = NLP_STE_UNUSED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); + } + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_els_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_issue_els_logo(phba, ndlp, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb, uint32_t evt) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + uint32_t *lp; + struct ls_rjt stat; + int port_cmp; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + memset(&stat, 0, sizeof (struct ls_rjt)); + + /* For a PLOGI, we only accept if our portname is less + * than the remote portname. + */ + phba->fc_stat.elsLogiCol++; + port_cmp = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (port_cmp >= 0) { + /* Reject this request because the remote node will accept + ours */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + else { + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + } /* if our portname was less */ + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + if(evt == NLP_EVT_RCV_LOGO) { + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + } + else { + lpfc_issue_els_logo(phba, ndlp, 0); + } + + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + IOCB_t *irsp; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + return (ndlp->nlp_state); + } + + irsp = &rspiocb->iocb; + + if (irsp->ulpStatus == 0) { + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0121 PLOGI chkparm OK " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_flag, ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + lpfc_unreg_rpi(phba, ndlp); + if (lpfc_reg_login + (phba, irsp->un.elsreq64.remoteID, + (uint8_t *) sp, mbox, 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + switch(ndlp->nlp_DID) { + case NameServer_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_ns_reg_login; + break; + case FDMI_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fdmi_reg_login; + break; + default: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_reg_login; + } + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + ndlp->nlp_state = + NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_REGLOGIN_LIST); + return (ndlp->nlp_state); + } + mempool_free(mbox, phba->mbox_mem_pool); + } else { + mempool_free(mbox, phba->mbox_mem_pool); + } + } + } + } + + /* Free this node since the driver cannot login or has the wrong + sparm */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + return (ndlp->nlp_state); + } + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 0); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_adisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_bindlist *blp; + IOCB_t *irsp; + ADISC *ap; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + irsp = &rspiocb->iocb; + + if ((irsp->ulpStatus) || + (!lpfc_check_adisc(phba, ndlp, &ap->nodeName, &ap->portName))) { + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + memset(&ndlp->nlp_nodename, 0, sizeof (struct lpfc_name)); + memset(&ndlp->nlp_portname, 0, sizeof (struct lpfc_name)); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_unreg_rpi(phba, ndlp); + return (ndlp->nlp_state); + } + /* move to mapped / unmapped list accordingly */ + /* Can we assign a SCSI Id to this NPort */ + if ((blp = lpfc_consistent_bind_get(phba, ndlp))) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + void *arg, uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + uint32_t did; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + did = mb->un.varWords[1]; + if (mb->mbxStatus) { + /* RegLogin failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0246 RegLogin failed Data: x%x x%x x%x\n", + phba->brd_no, + did, mb->mbxStatus, phba->hba_state); + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + lpfc_issue_els_logo(phba, ndlp, 0); + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (ndlp->nlp_state); + } + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + /* Only if we are not a fabric nport do we issue PRLI */ + if (!(ndlp->nlp_type & NLP_FABRIC)) { + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Software abort outstanding PRLI before sending acc */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +/* This routine is envoked when we rcv a PRLO request from a nport + * we are logged into. We should send back a PRLO rsp setting the + * appropriate bits. + * NEXT STATE = PRLI_ISSUE + */ +static uint32_t +lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; + PRLI *npr; + struct lpfc_bindlist *blp; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + + /* Check out PRLI rsp */ + if ((npr->acceptRspCode != PRLI_REQ_EXECUTED) || + (npr->prliType != PRLI_FCP_TYPE) || (npr->targetFunc != 1)) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + if (npr->Retry == 1) { + ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + } + + /* Can we assign a SCSI Id to this NPort */ + if ((blp = lpfc_consistent_bind_get(phba, ndlp))) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); +} + +/*! lpfc_device_rm_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * This routine is envoked when we a request to remove a nport we are in the + * process of PRLIing. We should software abort outstanding prli, unreg + * login, send a logout. We will change node state to UNUSED_NODE, put it + * on plogi list so it can be freed when LOGO completes. + * + */ +static uint32_t +lpfc_device_rm_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + + +/*! lpfc_device_recov_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * The routine is envoked when the state of a device is unknown, like + * during a link down. We should remove the nodelist entry from the + * unmapped list, issue a UNREG_LOGIN, do a software abort of the + * outstanding PRLI command, then free the node entry. + */ +static uint32_t +lpfc_device_recov_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + ndlp->nlp_sid, LPFC_ABORT_ALLQ); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Ignore PLOGI if we have an outstanding LOGO */ + if (ndlp->nlp_flag & NLP_LOGO_SND) { + return (ndlp->nlp_state); + } + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC); + return (ndlp->nlp_state); + } + + /* send PLOGI immediately, move to PLOGI issue state */ + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + struct ls_rjt stat; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + memset(&stat, 0, sizeof (struct ls_rjt)); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + if (ndlp->nlp_last_elscmd == (unsigned long)ELS_CMD_PLOGI) { + return (ndlp->nlp_state); + } else { + del_timer_sync(&ndlp->nlp_delayfunc); + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + } + } + + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_unreg_rpi(phba, ndlp); + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + + /* save rpi */ + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + + +/* This next section defines the NPort Discovery State Machine */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The plogi list and adisc list are used when Link Up discovery or RSCN + * processing is needed. Each list holds the nodes that we will send PLOGI + * or ADISC on. These lists will keep track of what nodes will be effected + * by an RSCN, or a Link Up (Typically, all nodes are effected on Link Up). + * The unmapped_list will contain all nodes that we have successfully logged + * into at the Fibre Channel level. The mapped_list will contain all nodes + * that are mapped FCP targets. + */ +/* + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ +/* For UNUSED_NODE state, the node has just been allocated . + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to UNMAPPED_NODE. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added or removed (via DEVICE_RM) to / from + * the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * indentically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +static void *lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT] = { + /* Action routine Event Current State */ + (void *)lpfc_rcv_plogi_unused_node, /* RCV_PLOGI UNUSED_NODE */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unused_node, /* RCV_LOGO */ + (void *)lpfc_rcv_els_unused_node, /* RCV_ADISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PDISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_unused_node, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_unused_node, /* DEVICE_RM */ + (void *)lpfc_disc_illegal, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_plogi_issue, /* RCV_PLOGI PLOGI_ISSUE */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLO */ + (void *)lpfc_cmpl_plogi_plogi_issue, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_plogi_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_plogi_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_adisc_issue, /* RCV_PLOGI ADISC_ISSUE */ + (void *)lpfc_rcv_prli_adisc_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_adisc_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_adisc_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_cmpl_adisc_adisc_issue, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_adisc_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_adisc_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_reglogin_issue, /* RCV_PLOGI REG_LOGIN_ISSUE */ + (void *)lpfc_rcv_prli_reglogin_issue, /* RCV_PLOGI */ + (void *)lpfc_rcv_logo_reglogin_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_reglogin_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_reglogin_issue,/* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_reglogin_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_reglogin_issue,/* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_prli_issue, /* RCV_PLOGI PRLI_ISSUE */ + (void *)lpfc_rcv_prli_prli_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_prli_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_prli_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_cmpl_prli_prli_issue, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_prli_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_prli_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_unmap_node, /* RCV_PLOGI UNMAPPED_NODE */ + (void *)lpfc_rcv_prli_unmap_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unmap_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_unmap_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_unmap_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_mapped_node, /* RCV_PLOGI MAPPED_NODE */ + (void *)lpfc_rcv_prli_mapped_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_mapped_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_mapped_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_mapped_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_npr_node, /* RCV_PLOGI NPR_NODE */ + (void *)lpfc_rcv_prli_npr_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_npr_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_npr_node, /* RCV_PRLO */ + (void *)lpfc_disc_noop, /* CMPL_PLOGI */ + (void *)lpfc_disc_noop, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_npr_node, /* CMPL_LOGO */ + (void *)lpfc_disc_noop, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_npr_node, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_npr_node, /* DEVICE_RM */ + (void *)lpfc_device_recov_npr_node, /* DEVICE_RECOVERY */ +}; + +int +lpfc_disc_state_machine(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + uint32_t cur_state, rc; + uint32_t(*func) (struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + + ndlp->nlp_disc_refcnt++; + cur_state = ndlp->nlp_state; + + /* DSM in event on NPort in state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0211 DSM in event x%x on NPort x%x in state %d " + "Data: x%x\n", + phba->brd_no, + evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag); + + func = (uint32_t(*)(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t)) + lpfc_disc_action[(cur_state * NLP_EVT_MAX_EVENT) + evt]; + rc = (func) (phba, ndlp, arg, evt); + + /* DSM out state on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0212 DSM out state %d on NPort x%x Data: x%x\n", + phba->brd_no, + rc, ndlp->nlp_DID, ndlp->nlp_flag); + + ndlp->nlp_disc_refcnt--; + + /* Check to see if ndlp removal is deferred */ + if ((ndlp->nlp_disc_refcnt == 0) + && (ndlp->nlp_flag & NLP_DELAY_REMOVE)) { + + ndlp->nlp_flag &= ~NLP_DELAY_REMOVE; + lpfc_nlp_remove(phba, ndlp); + return (NLP_STE_FREED_NODE); + } + if (rc == NLP_STE_FREED_NODE) + return (NLP_STE_FREED_NODE); + ndlp->nlp_state = rc; + return (rc); +} diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h new file mode 100644 index 000000000..12412c283 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -0,0 +1,92 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_scsi.h 1.68 2004/11/10 11:40:43EST sf_support Exp $ + */ + +#ifndef _H_LPFC_SCSI +#define _H_LPFC_SCSI + +#include "lpfc_disc.h" +#include "lpfc_mem.h" +#include "lpfc_sli.h" + +struct lpfc_hba; + + +struct lpfc_target { + struct lpfc_nodelist *pnode; /* Pointer to the node structure. */ + uint16_t scsi_id; + uint32_t qcmdcnt; + uint32_t iodonecnt; + uint32_t errorcnt; + uint32_t slavecnt; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + uint16_t blocked; +#endif +#ifdef FC_TRANS_VER1 + struct scsi_target *starget; /* Pointer to midlayer target + structure. */ +#endif +#if defined(FC_TRANS_265_BLKPATCH) + struct timer_list dev_loss_timer; +#endif +}; + +struct lpfc_scsi_buf { + struct scsi_cmnd *pCmd; + struct lpfc_hba *scsi_hba; + struct lpfc_target *target; + + uint32_t timeout; + + uint16_t status; /* From IOCB Word 7- ulpStatus */ + uint32_t result; /* From IOCB Word 4. */ + + uint32_t seg_cnt; /* Number of scatter-gather segments returned by + * dma_map_sg. The driver needs this for calls + * to dma_unmap_sg. */ + dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ + + /* dma_ext has both virt, phys to dma-able buffer + * which contains fcp_cmd, fcp_rsp and scatter gather list fro upto + * 68 (LPFC_SCSI_BPL_SIZE) BDE entries, + * xfer length, cdb, data direction.... + */ + struct lpfc_dmabuf dma_ext; + struct fcp_cmnd *fcp_cmnd; + struct fcp_rsp *fcp_rsp; + struct ulp_bde64 *fcp_bpl; + + /* cur_iocbq has phys of the dma-able buffer. + * Iotag is in here + */ + struct lpfc_iocbq cur_iocbq; +}; + +#define LPFC_SCSI_INITIAL_BPL_SIZE 4 /* Number of scsi buf BDEs in fcp_bpl */ + +#define LPFC_SCSI_DMA_EXT_SIZE 264 +#define LPFC_BPL_SIZE 1024 + +#define MDAC_DIRECT_CMD 0x22 + +#endif /* _H_LPFC_SCSI */ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c new file mode 100644 index 000000000..e9068e721 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -0,0 +1,3349 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.c 1.178 2004/11/23 16:57:11EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" + +static int lpfc_sli_reset_on_init = 1; + +/* + * Define macro to log: Mailbox command x%x cannot issue Data + * This allows multiple uses of lpfc_msgBlk0311 + * w/o perturbing log msg utility. +*/ +#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \ + lpfc_printf_log(phba, \ + KERN_INFO, \ + LOG_MBOX | LOG_SLI, \ + "%d:0311 Mailbox command x%x cannot issue " \ + "Data: x%x x%x x%x\n", \ + phba->brd_no, \ + mb->mbxCommand, \ + phba->hba_state, \ + psli->sliinit.sli_flag, \ + flag); + + +/* This will save a huge switch to determine if the IOCB cmd + * is unsolicited or solicited. + */ +#define LPFC_UNKNOWN_IOCB 0 +#define LPFC_UNSOL_IOCB 1 +#define LPFC_SOL_IOCB 2 +#define LPFC_ABORT_IOCB 3 +static uint8_t lpfc_sli_iocb_cmd_type[CMD_MAX_IOCB_CMD] = { + LPFC_UNKNOWN_IOCB, /* 0x00 */ + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE_CX 0x01 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CR 0x02 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CX 0x03 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CN 0x04 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CX 0x05 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF_CN 0x06 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF_CX 0x07 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE_CN 0x08 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF_CX 0x09 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CR 0x0A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CX 0x0B */ + LPFC_UNKNOWN_IOCB, /* 0x0C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ_CX 0x0D */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CN 0x0E */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CX 0x0F */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CR 0x10 */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CX 0x11 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CR 0x12 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CX 0x13 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CN 0x14 */ + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP_CX 0x15 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CR 0x16 */ + LPFC_ABORT_IOCB, /* CMD_XRI_ABORTED_CX 0x17 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CR 0x18 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CX 0x19 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CR 0x1A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CX 0x1B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CR 0x1C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CX 0x1D */ + LPFC_UNKNOWN_IOCB, /* 0x1E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND_CX 0x1F */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_MSG 0x20 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE_CX 0x21 */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_DUMP 0x22 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP_CX 0x23 */ + /* 0x24 - 0x80 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0x30 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x40 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x50 */ + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x60 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x70 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x80 */ + LPFC_UNKNOWN_IOCB, + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE64_CX 0x81 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CR 0x82 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CX 0x83 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CN 0x84 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CX 0x85 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF64_CN 0x86 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF64_CX 0x87 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE64_CN 0x88 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF64_CX 0x89 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CR 0x8A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CX 0x8B */ + LPFC_ABORT_IOCB, /* CMD_ABORT_MXRI64_CN 0x8C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ64_CX 0x8D */ + /* 0x8E - 0x94 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP64_CX 0x95 */ + LPFC_UNKNOWN_IOCB, /* 0x96 */ + LPFC_UNKNOWN_IOCB, /* 0x97 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CR 0x98 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CX 0x99 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CR 0x9A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CX 0x9B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CR 0x9C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CX 0x9D */ + LPFC_UNKNOWN_IOCB, /* 0x9E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND64_CX 0x9F */ + LPFC_UNKNOWN_IOCB, /* 0xA0 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE64_CX 0xA1 */ + LPFC_UNKNOWN_IOCB, /* 0xA2 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP64_CX 0xA3 */ + /* 0xA4 - 0xC1 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CR 0xC2 */ + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CX 0xC3 */ + /* 0xC4 - 0xCF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CR 0xD1 */ + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CX 0xD2 */ + LPFC_SOL_IOCB, /* CMD_RCV_LOGIN 0xD3 */ + LPFC_SOL_IOCB, /* CMD_ACCEPT_LOGIN 0xD4 */ + LPFC_SOL_IOCB, /* CMD_REJECT_LOGIN 0xD5 */ + LPFC_UNSOL_IOCB, + /* 0xD7 - 0xDF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0xE0 */ + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB +}; + + +static int +lpfc_sli_ring_map(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *pmbox; + int i; + + psli = &phba->sli; + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + pmbox = &pmb->mb; + + /* Initialize the struct lpfc_sli_ring structure for each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* Issue a CONFIG_RING mailbox command for each ring */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_ring(phba, i, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CFG_RING, + mbxStatus , ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0446 Adapter failed to init, " + "mbxCmd x%x CFG_RING, mbxStatus x%x, " + "ring %d\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + i); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + +static int +lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb) +{ + uint16_t iotag; + + list_add_tail(&piocb->list, &pring->txcmplq); + pring->txcmplq_cnt++; + + if (pring->fast_lookup) { + /* Setup fast lookup based on iotag for completion */ + iotag = piocb->iocb.ulpIoTag; + if (iotag && (iotag + < phba->sli.sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = piocb; + else { + + /* Cmd ring put: iotag greater then + configured max wd0 */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0316 Cmd ring %d put: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, phba->sli.sliinit + .ringinit[pring->ringno].fast_iotag, + *(((uint32_t *)(&piocb->iocb)) + 7)); + } + } + return (0); +} + +static struct lpfc_iocbq * +lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct list_head *dlp; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *next_iocb; + + dlp = &pring->txq; + cmd_iocb = NULL; + next_iocb = (struct lpfc_iocbq *) pring->txq.next; + if (next_iocb != (struct lpfc_iocbq *) & pring->txq) { + /* If the first ptr is not equal to the list header, + * deque the IOCBQ_t and return it. + */ + cmd_iocb = next_iocb; + list_del(&cmd_iocb->list); + pring->txq_cnt--; + } + return (cmd_iocb); +} + +static IOCB_t * +lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring) +{ + MAILBOX_t *mbox = (MAILBOX_t *)phba->sli.MBhostaddr; + PGP *pgp = (PGP *)&mbox->us.s2.port[pring->ringno]; + uint32_t max_cmd_idx = + phba->sli.sliinit.ringinit[pring->ringno].numCiocb; + IOCB_t *iocb = NULL; + + if((pring->next_cmdidx == pring->cmdidx) && + (++pring->next_cmdidx >= max_cmd_idx)) + pring->next_cmdidx = 0; + + if (unlikely(pring->local_getidx == pring->next_cmdidx)) { + + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + + if (unlikely(pring->local_getidx >= max_cmd_idx)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "%d:0315 Ring %d issue: portCmdGet %d " + "is bigger then cmd ring %d\n", + phba->brd_no, pring->ringno, + pring->local_getidx, max_cmd_idx); + + phba->hba_state = LPFC_HBA_ERROR; + lpfc_handle_eratt(phba, HS_FFER3); + + return NULL; + } + + if (pring->local_getidx == pring->next_cmdidx) + return NULL; + } + + iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx); + + return iocb; +} + +static void +lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + IOCB_t *iocb, struct lpfc_iocbq *nextiocb) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + + /* + * Alloocate and set up an iotag + */ + nextiocb->iocb.ulpIoTag = + lpfc_sli_next_iotag(phba, &psli->ring[psli->fcp_ring]); + + /* + * Issue iocb command to adapter + */ + lpfc_sli_pcimem_bcopy((uint32_t *)&nextiocb->iocb, + (uint32_t *)(iocb), sizeof (IOCB_t)); + wmb(); + psli->slistat.iocbCmd[ringno]++; + + /* + * If there is no completion routine to call, we can release the + * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF, + * that have no rsp ring completion, iocb_cmpl MUST be NULL. + */ + if (nextiocb->iocb_cmpl) + lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb); + else + mempool_free(nextiocb, phba->iocb_mem_pool); + + /* + * Let the HBA know what IOCB slot will be the next one the + * driver will put a command into. + */ + pring->cmdidx = pring->next_cmdidx; + writeb(pring->cmdidx, + (u8 *)phba->MBslimaddr + (SLIMOFF + (ringno * 2)) * 4); + + return; +} + +static void +lpfc_sli_update_full_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + pring->flag |= LPFC_CALL_RING_AVAILABLE; + + wmb(); + + /* + * Set ring 'ringno' to SET R0CE_REQ in Chip Att register. + * The HBA will tell us when an IOCB entry is available. + */ + writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + phba->sli.slistat.iocbCmdFull[ringno]++; +} + +static void +lpfc_sli_update_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + /* + * Tell the HBA that there is work to do in this ring. + */ + wmb(); + writel(CA_R0ATT << (ringno * 4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ +} + +static void +lpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli = &phba->sli; + IOCB_t *iocb; + struct lpfc_iocbq *nextiocb; + + /* + * Check to see if: + * (a) there is anything on the txq to send + * (b) link is up + * (c) link attention events can be processed (fcp ring only) + * (d) IOCB processing is not blocked by the outstanding mbox command. + */ + if (pring->txq_cnt && + (phba->hba_state > LPFC_LINK_DOWN) && + (pring->ringno != psli->fcp_ring || + psli->sliinit.sli_flag & LPFC_PROCESS_LA) && + !(pring->flag & LPFC_STOP_IOCB_MBX)) { + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_ringtx_get(phba, pring))) + lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb); + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + } + + return; +} + +/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */ +static void +lpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + PGP *pgp; + + psli = &phba->sli; + pring = &psli->ring[ringno]; + pgp = (PGP *) & (((MAILBOX_t *)psli->MBhostaddr)->us.s2.port[ringno]); + + /* If the ring is active, flag it */ + if (psli->ring[ringno].cmdringaddr) { + if (psli->ring[ringno].flag & LPFC_STOP_IOCB_MBX) { + psli->ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + } + } +} + +static int +lpfc_sli_chk_mbx_command(uint8_t mbxCommand) +{ + uint8_t ret; + + switch (mbxCommand) { + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_WRITE_NV: + case MBX_RUN_BIU_DIAG: + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_SPARM: + case MBX_READ_STATUS: + case MBX_READ_RPI: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_REG_LOGIN: + case MBX_UNREG_LOGIN: + case MBX_READ_LA: + case MBX_CLEAR_LA: + case MBX_DUMP_MEMORY: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_UPDATE_CFG: + case MBX_DOWN_LOAD: + case MBX_DEL_LD_ENTRY: + case MBX_RUN_PROGRAM: + case MBX_SET_MASK: + case MBX_SET_SLIM: + case MBX_UNREG_D_ID: + case MBX_CONFIG_FARP: + case MBX_LOAD_AREA: + case MBX_RUN_BIU_DIAG64: + case MBX_CONFIG_PORT: + case MBX_READ_SPARM64: + case MBX_READ_RPI64: + case MBX_REG_LOGIN64: + case MBX_READ_LA64: + case MBX_FLASH_WR_ULA: + case MBX_SET_DEBUG: + case MBX_LOAD_EXP_ROM: + ret = mbxCommand; + break; + default: + ret = MBX_SHUTDOWN; + break; + } + return (ret); +} +static int +lpfc_sli_handle_mb_event(struct lpfc_hba * phba) +{ + MAILBOX_t *mbox; + MAILBOX_t *pmbox; + LPFC_MBOXQ_t *pmb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + int i; + unsigned long iflag; + uint32_t process_next; + + + psli = &phba->sli; + /* We should only get here if we are in SLI2 mode */ + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + return (1); + } + + spin_lock_irqsave(phba->host->host_lock, iflag); + + psli->slistat.mboxEvent++; + + /* Get a Mailbox buffer to setup mailbox commands for callback */ + if ((pmb = psli->mbox_active)) { + pmbox = &pmb->mb; + mbox = (MAILBOX_t *) psli->MBhostaddr; + + /* First check out the status word */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, (uint32_t *) pmbox, + sizeof (uint32_t)); + + /* Sanity check to ensure the host owns the mailbox */ + if (pmbox->mbxOwner != OWN_HOST) { + /* Lets try for a while */ + for (i = 0; i < 10240; i++) { + /* First copy command data */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + sizeof (uint32_t)); + if (pmbox->mbxOwner == OWN_HOST) + goto mbout; + } + /* Stray Mailbox Interrupt, mbxCommand mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0304 Stray Mailbox Interrupt " + "mbxCommand x%x mbxStatus x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus); + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (1); + } + + mbout: + del_timer_sync(&psli->mbox_tmo); + + /* + * It is a fatal error if unknown mbox command completion. + */ + if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) == + MBX_SHUTDOWN) { + + /* Unknow mailbox command compl */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0323 Unknown Mailbox command %x Cmpl\n", + phba->brd_no, + pmbox->mbxCommand); + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_handle_eratt(phba, HS_FFER3); + return (0); + } + + psli->mbox_active = NULL; + if (pmbox->mbxStatus) { + psli->slistat.mboxStatErr++; + if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) { + /* Mbox cmd cmpl error - RETRYing */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0305 Mbox cmd cmpl error - " + "RETRYing Data: x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + pmbox->un.varWords[0], + phba->hba_state); + pmbox->mbxStatus = 0; + pmbox->mbxOwner = OWN_HOST; + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) + == MBX_SUCCESS) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + return (0); + } + } + } + + /* Mailbox cmd Cmpl */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0307 Mailbox cmd x%x Cmpl x%p " + "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmb->mbox_cmpl, + *((uint32_t *) pmbox), + pmbox->un.varWords[0], + pmbox->un.varWords[1], + pmbox->un.varWords[2], + pmbox->un.varWords[3], + pmbox->un.varWords[4], + pmbox->un.varWords[5], + pmbox->un.varWords[6], + pmbox->un.varWords[7]); + + if (pmb->mbox_cmpl) { + /* Copy entire mbox completion over buffer */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + /* All mbox cmpls are posted to discovery tasklet */ + lpfc_discq_post_event(phba, pmb, NULL, + LPFC_EVT_MBOX); + } else { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + } + } + + + do { + process_next = 0; /* by default don't loop */ + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Process next mailbox command if there is one */ + if ((pmb = lpfc_mbox_get(phba))) { + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) == + MBX_NOT_FINISHED) { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, + mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + process_next = 1; + continue; /* loop back */ + } + } else { + /* Turn on IOCB processing */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + lpfc_sli_turn_on_ring(phba, i); + } + + /* Free any lpfc_dmabuf's waiting for mbox cmd cmpls */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *) + (phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, + mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + } + + } while (process_next); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (0); +} +static int +lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_sli * psli; + IOCB_t * irsp; + LPFC_RING_INIT_t * pringinit; + WORD5 * w5p; + uint32_t Rctl, Type; + uint32_t match, ringno, i; + + psli = &phba->sli; + match = 0; + ringno = pring->ringno; + irsp = &(saveq->iocb); + if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) + || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) { + Rctl = FC_ELS_REQ; + Type = FC_ELS_DATA; + } else { + w5p = + (WORD5 *) & (saveq->iocb.un. + ulpWord[5]); + Rctl = w5p->hcsw.Rctl; + Type = w5p->hcsw.Type; + } + /* unSolicited Responses */ + pringinit = &psli->sliinit.ringinit[ringno]; + if (pringinit->prt[0].profile) { + /* If this ring has a profile set, just + send it to prt[0] */ + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, (void *)&pringinit->prt[0], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + } + else { + (pringinit->prt[0]. + lpfc_sli_rcv_unsol_event) (phba, pring, saveq); + } + match = 1; + } else { + /* We must search, based on rctl / type + for the right routine */ + for (i = 0; i < pringinit->num_mask; + i++) { + if ((pringinit->prt[i].rctl == + Rctl) + && (pringinit->prt[i]. + type == Type)) { + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, + (void *)&pringinit->prt[i], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + } + else { + (pringinit->prt[i]. + lpfc_sli_rcv_unsol_event) + (phba, pring, saveq); + } + match = 1; + break; + } + } + } + if (match == 0) { + /* Unexpected Rctl / Type received */ + /* Ring handler: unexpected + Rctl Type received */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0313 Ring %d handler: unexpected Rctl x%x " + "Type x%x received \n", + phba->brd_no, + ringno, + Rctl, + Type); + } + return(1); +} +static struct lpfc_iocbq * +lpfc_search_txcmpl(struct lpfc_sli_ring * pring, struct lpfc_iocbq * prspiocb) +{ + IOCB_t *icmd = NULL; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *iocb, *next_iocb; + uint16_t iotag; + + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + cmd_iocb = NULL; + + /* Search through txcmpl from the begining */ + list_for_each_entry_safe(iocb, next_iocb, &(pring->txcmplq), list) { + icmd = &iocb->iocb; + if (iotag == icmd->ulpIoTag) { + /* Found a match. */ + cmd_iocb = iocb; + list_del(&iocb->list); + pring->txcmplq_cnt--; + break; + } + } + + return (cmd_iocb); +} +static struct lpfc_iocbq * +lpfc_sli_ringtxcmpl_get(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * prspiocb, uint32_t srch) +{ + struct list_head *dlp; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_sli *psli; + uint16_t iotag; + + + dlp = &pring->txcmplq; + + if (pring->fast_lookup && (srch == 0)) { + /* + * Use fast lookup based on iotag for completion + */ + psli = &phba->sli; + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + if (iotag < psli->sliinit.ringinit[pring->ringno].fast_iotag) { + cmd_iocb = *(pring->fast_lookup + iotag); + *(pring->fast_lookup + iotag) = NULL; + if (cmd_iocb) { + list_del(&cmd_iocb->list); + pring->txcmplq_cnt--; + return cmd_iocb; + } + } else { + /* + * Rsp ring get: iotag greater then + * configured max wd0 + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0317 Rsp ring %d get: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, + psli->sliinit.ringinit[pring->ringno] + .fast_iotag, + *(((uint32_t *) irsp) + 7)); + } + } + + cmd_iocb = lpfc_search_txcmpl(pring, prspiocb); + + return cmd_iocb; +} + +static int +lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_iocbq * cmdiocbp; + int ringno, rc; + unsigned long iflag; + + rc = 1; + ringno = pring->ringno; + /* Solicited Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + spin_lock_irqsave(phba->host->host_lock, iflag); + if ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, saveq, + 0))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + /* All iocb cmpls for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, (void *)cmdiocbp, + (void *)saveq, LPFC_EVT_SOL_IOCB); + } + else { + if (cmdiocbp->iocb_flag & LPFC_IO_POLL) { + rc = 0; + } + + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + } else { + mempool_free( cmdiocbp, phba->iocb_mem_pool); + } + } else { + /* Could not find the initiating command + * based of the response iotag. + * This is expected on ELS ring because of lpfc_els_abort(). + */ + if (ringno != LPFC_ELS_RING) { + /* Ring handler: unexpected + completion IoTag */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0322 Ring %d handler: unexpected " + "completion IoTag x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + saveq->iocb.ulpIoTag, + saveq->iocb.ulpStatus, + saveq->iocb.un.ulpWord[4], + saveq->iocb.ulpCommand, + saveq->iocb.ulpContext); + } + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return(rc); +} +static int +lpfc_sli_handle_ring_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, uint32_t mask) +{ + struct lpfc_sli * psli; + IOCB_t * entry; + IOCB_t * irsp; + struct lpfc_iocbq * rspiocbp, *next_iocb; + struct lpfc_iocbq * cmdiocbp; + struct lpfc_iocbq * saveq; + HGP * hgp; + PGP * pgp; + MAILBOX_t * mbox; + uint32_t status, free_saveq; + uint32_t portRspPut, portRspMax; + int ringno, loopcnt, rc; + uint8_t type; + unsigned long iflag; + void *to_slim; + + psli = &phba->sli; + ringno = pring->ringno; + irsp = NULL; + rc = 1; + + spin_lock_irqsave(phba->host->host_lock, iflag); + psli->slistat.iocbEvent[ringno]++; + + /* At this point we assume SLI-2 */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + pgp = (PGP *) & mbox->us.s2.port[ringno]; + hgp = (HGP *) & mbox->us.s2.host[ringno]; + + /* portRspMax is the number of rsp ring entries for this specific + ring. */ + portRspMax = psli->sliinit.ringinit[ringno].numRiocb; + + rspiocbp = NULL; + loopcnt = 0; + + /* Gather iocb entries off response ring. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + portRspPut = le32_to_cpu(pgp->rspPutInx); + + if (portRspPut >= portRspMax) { + + /* Ring handler: portRspPut is bigger then + rsp ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0312 Ring %d handler: portRspPut %d " + "is bigger then rsp ring %d\n", + phba->brd_no, + ringno, portRspPut, portRspMax); + /* + * Treat it as adapter hardware error. + */ + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_handle_eratt(phba, HS_FFER3); + return (1); + } + + rmb(); + + /* Get the next available response iocb. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + while (pring->rspidx != portRspPut) { + /* get an iocb buffer to copy entry into */ + if ((rspiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + break; + } + + lpfc_sli_pcimem_bcopy((uint32_t *) entry, + (uint32_t *) & rspiocbp->iocb, + sizeof (IOCB_t)); + irsp = &rspiocbp->iocb; + + /* bump iocb available response index */ + if (++pring->rspidx >= portRspMax) { + pring->rspidx = 0; + } + + /* Let the HBA know what IOCB slot will be the next one the + * driver will read a response from. + */ + to_slim = (uint8_t *) phba->MBslimaddr + + (SLIMOFF + (ringno * 2) + 1) * 4; + writeb( pring->rspidx, to_slim); + + /* chain all iocb entries until LE is set */ + if (list_empty(&(pring->iocb_continueq))) { + list_add(&rspiocbp->list, &(pring->iocb_continueq)); + } else { + list_add_tail(&rspiocbp->list, + &(pring->iocb_continueq)); + } + pring->iocb_continueq_cnt++; + + /* + * When the ulpLe field is set, the entire Command has been + * received. Start by getting a pointer to the first iocb entry + * in the chain. + */ + if (irsp->ulpLe) { + /* + * By default, the driver expects to free all resources + * associated with this iocb completion. + */ + free_saveq = 1; + saveq = list_entry(pring->iocb_continueq.next, + struct lpfc_iocbq, list); + irsp = &(saveq->iocb); + list_del_init(&pring->iocb_continueq); + pring->iocb_continueq_cnt = 0; + + psli->slistat.iocbRsp[ringno]++; + + if(irsp->ulpStatus) { + /* Rsp ring error: IOCB */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0324 Rsp Ring %d error: IOCB Data: " + "x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + irsp->un.ulpWord[0], + irsp->un.ulpWord[1], + irsp->un.ulpWord[2], + irsp->un.ulpWord[3], + irsp->un.ulpWord[4], + irsp->un.ulpWord[5], + *(((uint32_t *) irsp) + 6), + *(((uint32_t *) irsp) + 7)); + } + + /* Determine if IOCB command is a solicited or + unsolicited event */ + type = + lpfc_sli_iocb_cmd_type[(irsp-> + ulpCommand & + CMD_IOCB_MASK)]; + if (type == LPFC_SOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_sol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + /* + * If this solicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_UNSOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_unsol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* + * If this unsolicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_ABORT_IOCB) { + /* Solicited ABORT Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) && + ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, + saveq, 0)))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, + cmdiocbp, saveq); + spin_lock_irqsave( + phba->host->host_lock, + iflag); + } else { + mempool_free(cmdiocbp, + phba->iocb_mem_pool); + } + } + } else if (type == LPFC_UNKNOWN_IOCB) { + if (irsp->ulpCommand == CMD_ADAPTER_MSG) { + + char adaptermsg[LPFC_MAX_ADPTMSG]; + + memset(adaptermsg, 0, + LPFC_MAX_ADPTMSG); + memcpy(&adaptermsg[0], (uint8_t *) irsp, + MAX_MSG_DATA); + dev_warn(&((phba->pcidev)->dev), + "lpfc%d: %s", + phba->brd_no, adaptermsg); + } else { + /* Unknown IOCB command */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0321 Unknown IOCB command " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->ulpCommand, + irsp->ulpStatus, + irsp->ulpIoTag, + irsp->ulpContext); + } + } + + if (free_saveq) { + /* + * Free up iocb buffer chain for command just + * processed + */ + if (!list_empty(&pring->iocb_continueq)) { + list_for_each_entry_safe(rspiocbp, + next_iocb, + &pring->iocb_continueq, list) { + list_del_init(&rspiocbp->list); + mempool_free(rspiocbp, + phba->iocb_mem_pool); + } + } + mempool_free( saveq, phba->iocb_mem_pool); + } + } + + /* Entire Command has been received */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + + /* If the port response put pointer has not been updated, sync + * the pgp->rspPutInx in the MAILBOX_tand fetch the new port + * response put pointer. + */ + if (pring->rspidx == portRspPut) { + portRspPut = le32_to_cpu(pgp->rspPutInx); + } + } /* while (pring->rspidx != portRspPut) */ + + if ((rspiocbp != 0) && (mask & HA_R0RE_REQ)) { + /* At least one response entry has been freed */ + psli->slistat.iocbRspFull[ringno]++; + /* SET RxRE_RSP in Chip Att register */ + status = ((CA_R0ATT | CA_R0RE_RSP) << (ringno * 4)); + writel(status, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + } + if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) { + pring->flag &= ~LPFC_CALL_RING_AVAILABLE; + psli->slistat.iocbCmdEmpty[ringno]++; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + + if ((psli->sliinit.ringinit[ringno].lpfc_sli_cmd_available)) + (psli->sliinit.ringinit[ringno]. + lpfc_sli_cmd_available) (phba, pring); + + } + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (rc); +} + +static uint32_t +lpfc_intr_prep(struct lpfc_hba * phba) +{ + uint32_t ha_copy; + + /* Ignore all interrupts during initialization. */ + if (phba->hba_state < LPFC_LINK_DOWN) + return (0); + + /* Read host attention register to determine interrupt source */ + ha_copy = readl(phba->HAregaddr); + + /* Clear Attention Sources, except ERATT (to preserve status) & LATT + * (ha_copy & ~(HA_ERATT | HA_LATT)); + */ + writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + return (ha_copy); +} /* lpfc_intr_prep */ + +int +lpfc_sli_intr(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint32_t ha_copy, status; + int i; + + psli = &phba->sli; + psli->slistat.sliIntr++; + + /* + * Call the HBA to see if it is interrupting. If not, don't claim + * the interrupt + */ + ha_copy = lpfc_intr_prep(phba); + if (!ha_copy) { + return (1); + } + + if (ha_copy & HA_ERATT) { + /* + * There was a link/board error. Read the status register to + * retrieve the error event and process it. + */ + psli->slistat.errAttnEvent++; + status = readl(phba->HSregaddr); + + /* Clear Chip error bit */ + writel(HA_ERATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + lpfc_handle_eratt(phba, status); + return (0); + } + + if (ha_copy & HA_MBATT) { + /* There was a Mailbox event. */ + lpfc_sli_handle_mb_event(phba); + } + + if (ha_copy & HA_LATT) { + /* + * There was a link attention event. Provided the driver is in + * a state to handle link events, handle this event. + */ + if (psli->sliinit.sli_flag & LPFC_PROCESS_LA) { + lpfc_handle_latt(phba); + } + } + + /* Process all events on each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + if ((ha_copy & HA_RXATT) + || (pring->flag & LPFC_DEFERRED_RING_EVENT)) { + if (pring->flag & LPFC_STOP_IOCB_MASK) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + } else { + lpfc_sli_handle_ring_event(phba, pring, + (ha_copy & + HA_RXMASK)); + pring->flag &= ~LPFC_DEFERRED_RING_EVENT; + } + } + ha_copy = (ha_copy >> 4); + } + + return (0); +} + +static int +lpfc_sli_abort_iocb_ring(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + uint16_t iotag; + + psli = &phba->sli; + errcnt = 0; + + /* Error everything on txq and txcmplq + * First do the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + pring->txq_cnt = 0; + INIT_LIST_HEAD(&(pring->txq)); + + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + if (flag == LPFC_SLI_ABORT_IMED) { + /* + * Imediate abort of IOCB, clear fast_lookup entry, + * if any, deque and call compl + */ + iotag = cmd->ulpIoTag; + if (pring->fast_lookup && + iotag && + (iotag < + psli->sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = NULL; + + list_del_init(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + + } + + if (lpfc_sli_issue_iocb + (phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free(abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + + if (flag == LPFC_SLI_ABORT_IMED) { + INIT_LIST_HEAD(&(pring->txcmplq)); + pring->txcmplq_cnt = 0; + } + + return (errcnt); +} + +int +lpfc_sli_brdreset(struct lpfc_hba * phba) +{ + MAILBOX_t *swpmb; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint16_t cfg_value, skip_post; + volatile uint32_t word0; + int i; + void *to_slim; + struct lpfc_dmabuf *mp, *next_mp; + + psli = &phba->sli; + + /* A board reset must use REAL SLIM. */ + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + + word0 = 0; + swpmb = (MAILBOX_t *) & word0; + swpmb->mbxCommand = MBX_RESTART; + swpmb->mbxHc = 1; + + to_slim = phba->MBslimaddr; + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Only skip post after fc_ffinit is completed */ + if (phba->hba_state) { + skip_post = 1; + word0 = 1; /* This is really setting up word1 */ + } else { + skip_post = 0; + word0 = 0; /* This is really setting up word1 */ + } + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Reset HBA */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_SLI, + "%d:0325 Reset HBA Data: x%x x%x\n", + phba->brd_no, + phba->hba_state, + psli->sliinit.sli_flag); + + /* Turn off SERR, PERR in PCI cmd register */ + phba->hba_state = LPFC_INIT_START; + + /* perform board reset */ + phba->fc_eventTag = 0; + phba->fc_myDID = 0; + phba->fc_prevDID = 0; + + /* Turn off parity checking and serr during the physical reset */ + pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value); + pci_write_config_word(phba->pcidev, PCI_COMMAND, + (cfg_value & + ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); + + /* Now toggle INITFF bit in the Host Control Register */ + writel(HC_INITFF, phba->HCregaddr); + mdelay(1); + readl(phba->HCregaddr); /* flush */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Restore PCI cmd register */ + + pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value); + phba->hba_state = LPFC_INIT_START; + + /* Initialize relevant SLI info */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag = 0; + pring->rspidx = 0; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + pring->missbufcnt = 0; + } + + if (skip_post) { + mdelay(100); + } else { + mdelay(2000); + } + + /* Cleanup preposted buffers on the ELS ring */ + pring = &psli->ring[LPFC_ELS_RING]; + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + list_del(&mp->list); + pring->postbufq_cnt--; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + lpfc_sli_abort_iocb_ring(phba, pring, LPFC_SLI_ABORT_IMED); + } + + return (0); +} + +static void +lpfc_setup_slim_access(struct lpfc_hba *phba) +{ + phba->MBslimaddr = phba->slim_memmap_p; + phba->HAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HA_REG_OFFSET; + phba->HCregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HC_REG_OFFSET; + phba->CAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + CA_REG_OFFSET; + phba->HSregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HS_REG_OFFSET; + return; +} + +int +lpfc_sli_hba_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + int read_rev_reset, i, rc; + uint32_t status; + + psli = &phba->sli; + + /* Setep SLI interface for HBA register and HBA SLIM access */ + lpfc_setup_slim_access(phba); + + /* Set board state to initialization started */ + phba->hba_state = LPFC_INIT_START; + read_rev_reset = 0; + + /* On some platforms/OS's, the driver can't rely on the state the + * adapter may be in. For this reason, the driver is allowed to reset + * the HBA before initialization. + */ + if (lpfc_sli_reset_on_init) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + + /* Sleep for 2.5 sec */ + msleep(2500); + } + +top: + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + + /* Check status register to see what current state is */ + i = 0; + while ((status & (HS_FFRDY | HS_MBRDY)) != (HS_FFRDY | HS_MBRDY)) { + + /* Check every 100ms for 5 retries, then every 500ms for 5, then + * every 2.5 sec for 5, then reset board and every 2.5 sec for + * 4. + */ + if (i++ >= 20) { + /* Adapter failed to init, timeout, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0436 Adapter failed to init, " + "timeout, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -ETIMEDOUT; + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0437 Adapter failed to init, " + "chipset, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + if (i <= 5) { + msleep(10); + } else if (i <= 10) { + msleep(500); + } else { + msleep(2500); + } + + if (i == 15) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + } + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0438 Adapter failed to init, chipset, " + "status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* setup host attn register */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + /* Call pre CONFIG_PORT mailbox command initialization. A value of 0 + * means the call was successful. Any other nonzero value is a failure, + * but if ERESTART is returned, the driver may reset the HBA and try + * again. + */ + if ((rc = lpfc_config_port_prep(phba))) { + if ((rc == -ERESTART) && (read_rev_reset == 0)) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(500); + read_rev_reset = 1; + goto top; + } + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + /* Setup and issue mailbox CONFIG_PORT command */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_port(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CONFIG_PORT, + mbxStatus */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0442 Adapter failed to init, mbxCmd x%x " + "CONFIG_PORT, mbxStatus x%x Data: x%x\n", + phba->brd_no, pmb->mb.mbxCommand, + pmb->mb.mbxStatus, 0); + + /* This clause gives the config_port call is given multiple + chances to succeed. */ + if (read_rev_reset == 0) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(2500); + read_rev_reset = 1; + goto top; + } + + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + if ((rc = lpfc_sli_ring_map(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + + /* Call post CONFIG_PORT mailbox command initialization. */ + if ((rc = lpfc_config_port_post(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + + + + + + + +static void +lpfc_mbox_abort(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + + psli = &phba->sli; + + if (psli->mbox_active) { + del_timer_sync(&psli->mbox_tmo); + pmbox = psli->mbox_active; + mb = &pmbox->mb; + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + /* Abort all the non active mailbox commands. */ + pmbox = lpfc_mbox_get(phba); + while (pmbox) { + mb = &pmbox->mb; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + pmbox = lpfc_mbox_get(phba); + } + return; +} +/*! lpfc_mbox_timeout + * + * \pre + * \post + * \param hba Pointer to per struct lpfc_hba structure + * \param l1 Pointer to the driver's mailbox queue. + * \return + * void + * + * \b Description: + * + * This routine handles mailbox timeout events at timer interrupt context. + */ +void +lpfc_mbox_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + psli = &phba->sli; + spin_lock_irqsave(phba->host->host_lock, iflag); + + pmbox = psli->mbox_active; + mb = &pmbox->mb; + + /* Mbox cmd timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + psli->mbox_active); + + if (psli->mbox_active == pmbox) { + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + lpfc_mbox_abort(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + + +int +lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) +{ + MAILBOX_t *mbox; + MAILBOX_t *mb; + struct lpfc_sli *psli; + uint32_t status, evtctr; + uint32_t ha_copy; + int i; + unsigned long drvr_flag = 0; + volatile uint32_t word0, ldata; + void *to_slim; + + psli = &phba->sli; + if (flag & MBX_POLL) { + spin_lock_irqsave(phba->host->host_lock, drvr_flag); + } + + mb = &pmbox->mb; + status = MBX_SUCCESS; + + if (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE) { + /* Polling for a mbox command when another one is already active + * is not allowed in SLI. Also, the driver must have established + * SLI2 mode to queue and process multiple mbox commands. + */ + + if (flag & MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + + /* Another mailbox command is still being processed, queue this + * command to be processed later. + */ + lpfc_mbox_put(phba, pmbox); + + /* Mbox cmd issue - BUSY */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxBusy++; + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + } + return (MBX_BUSY); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + if (flag == MBX_NOWAIT) { + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + } + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + + /* If we are not polling, we MUST be in SLI2 mode */ + if (flag != MBX_POLL) { + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag); + return (MBX_NOT_FINISHED); + } + /* timeout active mbox command */ + mod_timer(&psli->mbox_tmo, jiffies + HZ * LPFC_MBOX_TMO); + } + + /* Mailbox cmd issue */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxCmd++; + evtctr = psli->slistat.mboxEvent; + + /* next set own bit for the adapter and copy over command word */ + mb->mbxOwner = OWN_CHIP; + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + + /* First copy command data to host SLIM area */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + + pci_dma_sync_single_for_device(phba->pcidev, + phba->slim2p_mapping, + sizeof (MAILBOX_t), + PCI_DMA_TODEVICE); + } else { + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* copy command data into host mbox for cmpl */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, + (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + } + + /* First copy mbox command data to HBA SLIM, skip past first + word */ + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + lpfc_memcpy_to_slim(to_slim, (void *)&mb->un.varWords[0], + (MAILBOX_CMD_WSIZE - 1) * sizeof (uint32_t)); + + /* Next copy over first word, with mbxOwner set */ + ldata = *((volatile uint32_t *)mb); + to_slim = phba->MBslimaddr; + writel(ldata, to_slim); + readl(to_slim); /* flush */ + + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* switch over to host mailbox */ + psli->sliinit.sli_flag |= LPFC_SLI2_ACTIVE; + } + } + + wmb(); + /* interrupt board to doit right away */ + writel(CA_MBATT, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + switch (flag) { + case MBX_NOWAIT: + /* Don't wait for it to finish, just return */ + psli->mbox_active = pmbox; + break; + + case MBX_POLL: + i = 0; + psli->mbox_active = NULL; + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First read mbox status word */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + } else { + /* First read mbox status word */ + word0 = readl(phba->MBslimaddr); + } + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + /* Wait for command to complete */ + while (((word0 & OWN_CHIP) == OWN_CHIP) + || !(ha_copy & HA_MBATT)) { + if (i++ >= 100) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + return (MBX_NOT_FINISHED); + } + + /* Check if we took a mbox interrupt while we were + polling */ + if (((word0 & OWN_CHIP) != OWN_CHIP) + && (evtctr != psli->slistat.mboxEvent)) + break; + + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + + /* Can be in interrupt context, do not sleep */ + /* (or might be called with interrupts disabled) */ + mdelay(i); + + spin_lock_irqsave(phba->host->host_lock, drvr_flag); + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + if (mb->mbxCommand == MBX_CONFIG_PORT) { + MAILBOX_t *slimmb; + volatile uint32_t slimword0; + /* Check real SLIM for any errors */ + slimword0 = readl(phba->MBslimaddr); + slimmb = (MAILBOX_t *) & slimword0; + if (((slimword0 & OWN_CHIP) != OWN_CHIP) + && slimmb->mbxStatus) { + psli->sliinit.sli_flag &= + ~LPFC_SLI2_ACTIVE; + word0 = slimword0; + } + } + } else { + /* First copy command data */ + word0 = readl(phba->MBslimaddr); + } + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + } + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + /* copy results back to user */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) mb, + (sizeof (uint32_t) * + MAILBOX_CMD_WSIZE)); + } else { + /* First copy command data */ + lpfc_memcpy_from_slim((void *)mb, + phba->MBslimaddr, + sizeof (uint32_t) * (MAILBOX_CMD_WSIZE)); + } + + writel(HA_MBATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + status = mb->mbxStatus; + } + + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, drvr_flag); + } + return (status); +} + +static int +lpfc_sli_ringtx_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb) +{ + /* Insert the caller's iocb in the txq tail for later processing. */ + list_add_tail(&piocb->list, &pring->txq); + pring->txq_cnt++; + return (0); +} + +static struct lpfc_iocbq * +lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq ** piocb) +{ + struct lpfc_iocbq * nextiocb; + + nextiocb = lpfc_sli_ringtx_get(phba, pring); + if (!nextiocb) { + nextiocb = *piocb; + *piocb = NULL; + } + + return nextiocb; +} + +int +lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *piocb, uint32_t flag) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + struct lpfc_iocbq *nextiocb; + IOCB_t *iocb; + + /* + * We should never get an IOCB if we are in a < LINK_DOWN state + */ + if (unlikely(phba->hba_state < LPFC_LINK_DOWN)) + return IOCB_ERROR; + + /* + * Check to see if we are blocking IOCB processing because of a + * outstanding mbox command. + */ + if (unlikely(pring->flag & LPFC_STOP_IOCB_MBX)) + goto iocb_busy; + + if (unlikely(phba->hba_state == LPFC_LINK_DOWN)) { + /* + * Only CREATE_XRI, CLOSE_XRI, ABORT_XRI, and QUE_RING_BUF + * can be issued if the link is not up. + */ + switch (piocb->iocb.ulpCommand) { + case CMD_QUE_RING_BUF_CN: + case CMD_QUE_RING_BUF64_CN: + case CMD_CLOSE_XRI_CN: + case CMD_ABORT_XRI_CN: + /* + * For IOCBs, like QUE_RING_BUF, that have no rsp ring + * completion, iocb_cmpl MUST be 0. + */ + if (piocb->iocb_cmpl) + piocb->iocb_cmpl = NULL; + /*FALLTHROUGH*/ + case CMD_CREATE_XRI_CR: + break; + default: + goto iocb_busy; + } + + /* + * For FCP commands, we must be in a state where we can process link + * attention events. + */ + } else if (unlikely(pring->ringno == psli->fcp_ring && + !(psli->sliinit.sli_flag & LPFC_PROCESS_LA))) + goto iocb_busy; + + /* + * Check to see if this is a high priority command. + * If so bypass tx queue processing. + */ + if (unlikely((flag & SLI_IOCB_HIGH_PRIORITY) && + (iocb = lpfc_sli_next_iocb_slot(phba, pring)))) { + lpfc_sli_submit_iocb(phba, pring, iocb, piocb); + piocb = NULL; + } + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_next_iocb(phba, pring, &piocb))) + lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb); + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + + if (!piocb) + return IOCB_SUCCESS; + + goto out_busy; + + iocb_busy: + psli->slistat.iocbCmdDelay[ringno]++; + + out_busy: + + if (!(flag & SLI_IOCB_RET_IOCB)) { + lpfc_sli_ringtx_put(phba, pring, piocb); + return IOCB_SUCCESS; + } + + return IOCB_BUSY; +} + +int +lpfc_sli_queue_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + int i, cnt; + + psli = &phba->sli; + INIT_LIST_HEAD(&psli->mboxq); + /* Initialize list headers for txq and txcmplq as double linked lists */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->ringno = i; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + INIT_LIST_HEAD(&pring->postbufq); + cnt = psli->sliinit.ringinit[i].fast_iotag; + if (cnt) { + pring->fast_lookup = + kmalloc(cnt * sizeof (struct lpfc_iocbq *), + GFP_KERNEL); + if (pring->fast_lookup == 0) { + return (0); + } + memset((char *)pring->fast_lookup, 0, + cnt * sizeof (struct lpfc_iocbq *)); + } + } + return (1); +} + +int +lpfc_sli_hba_down(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + LPFC_MBOXQ_t *pmb; + struct lpfc_dmabuf *mp; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL; + int i; + + psli = &phba->sli; + lpfc_hba_down_prep(phba); + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag |= LPFC_DEFERRED_RING_EVENT; + + /* + * Error everything on the txq since these iocbs have not been + * given to the FW yet. + */ + pring->txq_cnt = 0; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_DOWN; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + INIT_LIST_HEAD(&(pring->txq)); + + if (pring->fast_lookup) { + kfree(pring->fast_lookup); + pring->fast_lookup = NULL; + } + + } + + /* Return any active mbox cmds */ + del_timer_sync(&psli->mbox_tmo); + if ((psli->mbox_active)) { + pmb = psli->mbox_active; + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(psli->mbox_active, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + psli->mbox_active = NULL; + + /* Return any pending mbox cmds */ + while ((pmb = lpfc_mbox_get(phba)) != NULL) { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(pmb, phba->mbox_mem_pool); + } + + INIT_LIST_HEAD(&psli->mboxq); + + /* + * Provided the hba is not in an error state, reset it. It is not + * capable of IO anymore. + */ + if (phba->hba_state != LPFC_HBA_ERROR) { + phba->hba_state = LPFC_INIT_START; + lpfc_sli_brdreset(phba); + } + + return 1; +} + +void +lpfc_sli_pcimem_bcopy(uint32_t * src, uint32_t * dest, uint32_t cnt) +{ + uint32_t ldata; + int i; + + for (i = 0; i < (int)cnt; i += sizeof (uint32_t)) { + ldata = *src++; + ldata = le32_to_cpu(ldata); + *dest++ = ldata; + } +} + +int +lpfc_sli_ringpostbuf_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_dmabuf * mp) +{ + /* Stick struct lpfc_dmabuf at end of postbufq so driver can look it up + later */ + list_add_tail(&mp->list, &pring->postbufq); + + pring->postbufq_cnt++; + return 0; +} + + +struct lpfc_dmabuf * +lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + dma_addr_t phys) +{ + struct lpfc_dmabuf *mp, *next_mp; + struct list_head *slp = &pring->postbufq; + + /* Search postbufq, from the begining, looking for a match on phys */ + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + if (mp->phys == phys) { + list_del_init(&mp->list); + pring->postbufq_cnt--; + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + return mp; + } + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0410 Cannot find virtual addr for mapped buf on " + "ring %d Data x%llx x%p x%p x%x\n", + phba->brd_no, pring->ringno, (unsigned long long)phys, + slp->next, slp->prev, pring->postbufq_cnt); + return NULL; +} + +uint32_t +lpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + LPFC_RING_INIT_t *pringinit; + struct lpfc_sli *psli; + uint32_t search_start; + + psli = &phba->sli; + pringinit = &psli->sliinit.ringinit[pring->ringno]; + + if (pring->fast_lookup == NULL) { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->iotag_max) + pringinit->iotag_ctr = 1; + return pringinit->iotag_ctr; + } + + search_start = pringinit->iotag_ctr; + + do { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->fast_iotag) + pringinit->iotag_ctr = 1; + + if(*(pring->fast_lookup + pringinit->iotag_ctr) == NULL) + return pringinit->iotag_ctr; + + } while (pringinit->iotag_ctr != search_start); + + /* + * Outstanding I/O count for ring is at max + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0318 Outstanding I/O count for ring %d is at max x%x\n", + phba->brd_no, + pring->ringno, + psli->sliinit.ringinit[pring->ringno].fast_iotag); + return (0); +} + +static void +lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + /* Free the resources associated with the ELS_REQUEST64 IOCB the driver + * just aborted. + * In this case, context2 = cmd, context2->next = rsp, context3 = bpl + */ + if (cmdiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Free the response IOCB before completing the abort + command. */ + if (!list_empty(&buf_ptr1->list)) { + + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + + list_del(&buf_ptr->list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (cmdiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +int +lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * cmdiocb) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL; + IOCB_t *iabt = NULL; + uint32_t iotag32; + + psli = &phba->sli; + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) == 0) { + return (0); + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + iabt = &abtsiocbp->iocb; + + icmd = &cmdiocb->iocb; + switch (icmd->ulpCommand) { + case CMD_ELS_REQUEST64_CR: + iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32; + /* Even though we abort the ELS command, the firmware may access + * the BPL or other resources before it processes our + * ABORT_MXRI64. Thus we must delay reusing the cmdiocb + * resources till the actual abort request completes. + */ + abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand); + abtsiocbp->context2 = cmdiocb->context2; + abtsiocbp->context3 = cmdiocb->context3; + cmdiocb->context2 = NULL; + cmdiocb->context3 = NULL; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl; + break; + default: + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + iabt->un.amxri.abortType = ABORT_TYPE_ABTS; + iabt->un.amxri.iotag32 = iotag32; + + iabt->ulpLe = 1; + iabt->ulpClass = CLASS3; + iabt->ulpCommand = CMD_ABORT_MXRI64_CN; + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + return (1); +} + +int +lpfc_sli_abort_iocb_ctx(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t ctx) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_abort_iocb_tgt(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != scsi_target)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != scsi_target)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * txcmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + + + +void +lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2) +{ + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* The waiter is looking for LPFC_IO_HIPRI bit to be set + as a signal to wake up */ + queue1->iocb_flag |= LPFC_IO_HIPRI; + return; +} + +int +lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq, + uint32_t timeout) +{ + int j, delay_time, retval = IOCB_ERROR; + + /* The caller must left context1 empty. */ + if (piocb->context_un.hipri_wait_queue != 0) { + return IOCB_ERROR; + } + + /* + * If the caller has provided a response iocbq buffer, context2 must + * be NULL or its an error. + */ + if (prspiocbq && piocb->context2) { + return IOCB_ERROR; + } + + piocb->context2 = prspiocbq; + + /* Setup callback routine and issue the command. */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority; + retval = lpfc_sli_issue_iocb(phba, pring, piocb, + flag | SLI_IOCB_HIGH_PRIORITY); + if (retval != IOCB_SUCCESS) { + piocb->context2 = NULL; + return IOCB_ERROR; + } + + /* + * This high-priority iocb was sent out-of-band. Poll for its + * completion rather than wait for a signal. Note that the host_lock + * is held by the midlayer and must be released here to allow the + * interrupt handlers to complete the IO and signal this routine via + * the iocb_flag. + * Also, the delay_time is computed to be one second longer than + * the scsi command timeout to give the FW time to abort on + * timeout rather than the driver just giving up. Typically, + * the midlayer does not specify a time for this command so the + * driver is free to enforce its own timeout. + */ + + delay_time = ((timeout + 1) * 1000) >> 6; + retval = IOCB_ERROR; + spin_unlock_irq(phba->host->host_lock); + for (j = 0; j < 64; j++) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) + mdelay(delay_time); +#else + msleep(delay_time); +#endif + if (piocb->iocb_flag & LPFC_IO_HIPRI) { + piocb->iocb_flag &= ~LPFC_IO_HIPRI; + retval = IOCB_SUCCESS; + break; + } + } + + spin_lock_irq(phba->host->host_lock); + piocb->context2 = NULL; + return retval; +} +static void +lpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) +{ + wait_queue_head_t *pdone_q; + + /* + * If pdone_q is empty, the driver thread gave up waiting and + * continued running. + */ + pdone_q = (wait_queue_head_t *) pmboxq->context1; + if (pdone_q) + wake_up_interruptible(pdone_q); + return; +} + +int +lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty. */ + if (pmboxq->context1 != 0) { + return (MBX_NOT_FINISHED); + } + + /* setup wake call as IOCB callback */ + pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait; + /* setup context field to pass wait_queue pointer to wake function */ + pmboxq->context1 = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + spin_lock_irq(phba->host->host_lock); + retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + spin_unlock_irq(phba->host->host_lock); + + if (retval == MBX_BUSY || retval == MBX_SUCCESS) { + timeleft = schedule_timeout(timeout * HZ); + pmboxq->context1 = NULL; + /* if schedule_timeout returns 0, we timed out and were not + woken up */ + if (timeleft == 0) { + retval = MBX_TIMEOUT; + } else { + retval = MBX_SUCCESS; + } + } + + + set_current_state(TASK_RUNNING); + remove_wait_queue(&done_q, &wq_entry); + return retval; +} + +static void +lpfc_sli_wake_iocb_wait(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, struct lpfc_iocbq * queue2) +{ + wait_queue_head_t *pdone_q; + + queue1->iocb_flag |= LPFC_IO_WAIT; + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* + * If pdone_q is empty, the waiter gave up and returned and this + * call has nothing to do. + */ + pdone_q = queue1->context_un.hipri_wait_queue; + if (pdone_q) { + wake_up(pdone_q); + } + + return; +} + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty for the driver. */ + if (piocb->context_un.hipri_wait_queue != 0) + return (IOCB_ERROR); + + /* If the caller has provided a response iocbq buffer, then context2 + * is NULL or its an error. + */ + if (prspiocbq) { + if (piocb->context2) + return (IOCB_ERROR); + piocb->context2 = prspiocbq; + } + + /* setup wake call as IOCB callback */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait; + /* setup context field to pass wait_queue pointer to wake function */ + piocb->context_un.hipri_wait_queue = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0); + if (retval == IOCB_SUCCESS) { + /* Give up thread time and wait for the iocb to complete or for + * the alloted time to expire. + */ + timeleft = schedule_timeout(timeout * HZ); + + piocb->context_un.hipri_wait_queue = NULL; + piocb->iocb_cmpl = NULL; + if (piocb->context2 == prspiocbq) + piocb->context2 = NULL; + + /* + * Catch the error cases. A timeleft of zero is an error since + * the iocb should have completed. The iocb_flag not have value + * LPFC_IO_WAIT is also an error since the wakeup callback sets + * this flag when it runs. Handle each. + */ + if (timeleft == 0) { + printk(KERN_WARNING "lpfc driver detected iocb " + "Timeout!\n"); + retval = IOCB_TIMEDOUT; + } else if (!(piocb->iocb_flag & LPFC_IO_WAIT)) { + printk(KERN_WARNING "lpfc driver detected iocb " + "flag = 0x%X\n", piocb->iocb_flag); + retval = IOCB_TIMEDOUT; + } + } + + remove_wait_queue(&done_q, &wq_entry); + set_current_state(TASK_RUNNING); + piocb->context2 = NULL; + return retval; +} + +irqreturn_t +lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs) +{ + struct lpfc_hba *phba; + int intr_status; + + /* + * Get the driver's phba structure from the dev_id and + * assume the HBA is not interrupting. + */ + phba = (struct lpfc_hba *) dev_id; + + if (phba) { + /* Call SLI to handle the interrupt event. */ + intr_status = lpfc_sli_intr(phba); + if (intr_status == 0) + return IRQ_HANDLED; + } + + return IRQ_NONE; + +} /* lpfc_intr_handler */ diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h new file mode 100644 index 000000000..917a278d4 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -0,0 +1,218 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.h 1.36 2004/11/23 16:56:58EST sf_support Exp $ + */ + +#ifndef _H_LPFC_SLI +#define _H_LPFC_SLI + +#include "lpfc_hw.h" + +/* forward declaration for LPFC_IOCB_t's use */ +struct lpfc_hba; + +/* This structure is used to handle IOCB requests / responses */ +struct lpfc_iocbq { + /* lpfc_iocbqs are used in double linked lists */ + struct list_head list; + IOCB_t iocb; /* IOCB cmd */ + uint8_t retry; /* retry counter for IOCB cmd - if needed */ + uint8_t iocb_flag; +#define LPFC_IO_POLL 1 /* Polling mode iocb */ +#define LPFC_IO_LIBDFC 2 /* libdfc iocb */ +#define LPFC_IO_WAIT 4 +#define LPFC_IO_HIPRI 8 /* High Priority Queue signal flag */ + + uint8_t abort_count; + uint8_t rsvd2; + uint32_t drvrTimeout; /* driver timeout in seconds */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + void *context3; /* caller context information */ + union { + wait_queue_head_t *hipri_wait_queue; /* High Priority Queue wait + queue */ + struct lpfc_iocbq *rsp_iocb; + struct lpfcMboxq *mbox; + } context_un; + + void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + +}; + +#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */ +#define SLI_IOCB_HIGH_PRIORITY 2 /* High priority command */ + +#define IOCB_SUCCESS 0 +#define IOCB_BUSY 1 +#define IOCB_ERROR 2 +#define IOCB_TIMEDOUT 3 + +typedef struct lpfcMboxq { + /* MBOXQs are used in single linked lists */ + struct list_head list; /* ptr to next mailbox command */ + MAILBOX_t mb; /* Mailbox cmd */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + + void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *); + +} LPFC_MBOXQ_t; + +#define MBX_POLL 1 /* poll mailbox till command done, then + return */ +#define MBX_NOWAIT 2 /* issue command then return immediately */ +#define MBX_STOP_IOCB 4 /* Stop iocb processing till mbox cmds + complete */ + +#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per + ring */ +#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */ + +/* Structure used to hold SLI ring information */ +struct lpfc_sli_ring { + uint16_t flag; /* ring flags */ +#define LPFC_DEFERRED_RING_EVENT 0x001 /* Deferred processing a ring event */ +#define LPFC_CALL_RING_AVAILABLE 0x002 /* indicates cmd was full */ +#define LPFC_STOP_IOCB_MBX 0x010 /* Stop processing IOCB cmds mbox */ +#define LPFC_STOP_IOCB_EVENT 0x020 /* Stop processing IOCB cmds event */ +#define LPFC_STOP_IOCB_MASK 0x030 /* Stop processing IOCB cmds mask */ + uint16_t abtsiotag; /* tracks next iotag to use for ABTS */ + + uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */ + uint32_t next_cmdidx; /* next_cmd index */ + uint8_t rsvd; + uint8_t ringno; /* ring number */ + uint8_t rspidx; /* current index in response ring */ + uint8_t cmdidx; /* current index in command ring */ + struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by + iotag */ + struct list_head txq; + uint16_t txq_cnt; /* current length of queue */ + uint16_t txq_max; /* max length */ + struct list_head txcmplq; + uint16_t txcmplq_cnt; /* current length of queue */ + uint16_t txcmplq_max; /* max length */ + volatile uint32_t *cmdringaddr; /* virtual address for cmd rings */ + volatile uint32_t *rspringaddr; /* virtual address for rsp rings */ + uint32_t missbufcnt; /* keep track of buffers to post */ + struct list_head postbufq; + uint16_t postbufq_cnt; /* current length of queue */ + uint16_t postbufq_max; /* max length */ + struct list_head iocb_continueq; + uint16_t iocb_continueq_cnt; /* current length of queue */ + uint16_t iocb_continueq_max; /* max length */ +}; + +typedef struct { + uint8_t profile; /* profile associated with ring */ + uint8_t rctl; /* rctl / type pair configured for ring */ + uint8_t type; /* rctl / type pair configured for ring */ + uint8_t rsvd; + /* rcv'd unsol event */ + void (*lpfc_sli_rcv_unsol_event) (struct lpfc_hba *, + struct lpfc_sli_ring *, + struct lpfc_iocbq *); +} LPFC_RING_MASK_t; + +/* Structure used for configuring rings to a specific profile or rctl / type */ +typedef struct { + LPFC_RING_MASK_t prt[LPFC_MAX_RING_MASK]; + uint32_t num_mask; /* number of mask entries in prt array */ + uint32_t iotag_ctr; /* keeps track of the next iotag to use */ + uint32_t iotag_max; /* max iotag value to use */ + uint32_t fast_iotag; /* max fastlookup based iotag */ + uint16_t numCiocb; /* number of command iocb's per ring */ + uint16_t numRiocb; /* number of rsp iocb's per ring */ + /* cmd ring available */ + void (*lpfc_sli_cmd_available) (struct lpfc_hba *, + struct lpfc_sli_ring *); +} LPFC_RING_INIT_t; + +typedef struct { + LPFC_RING_INIT_t ringinit[LPFC_MAX_RING]; /* ring initialization info */ + uint32_t num_rings; + uint32_t sli_flag; +} LPFC_SLI_INIT_t; + +/* Structure used to hold SLI statistical counters and info */ +typedef struct { + uint64_t iocbEvent[LPFC_MAX_RING]; /* IOCB event counters */ + uint64_t iocbCmd[LPFC_MAX_RING]; /* IOCB cmd issued */ + uint64_t iocbRsp[LPFC_MAX_RING]; /* IOCB rsp received */ + uint64_t iocbCmdDelay[LPFC_MAX_RING]; /* IOCB cmd ring delay */ + uint64_t iocbCmdFull[LPFC_MAX_RING]; /* IOCB cmd ring full */ + uint64_t iocbCmdEmpty[LPFC_MAX_RING]; /* IOCB cmd ring is now empty */ + uint64_t iocbRspFull[LPFC_MAX_RING]; /* IOCB rsp ring full */ + uint64_t mboxStatErr; /* Mbox cmds completed status error */ + uint64_t mboxCmd; /* Mailbox commands issued */ + uint64_t sliIntr; /* Count of Host Attention interrupts */ + uint32_t errAttnEvent; /* Error Attn event counters */ + uint32_t linkEvent; /* Link event counters */ + uint32_t mboxEvent; /* Mailbox event counters */ + uint32_t mboxBusy; /* Mailbox cmd busy */ +} LPFC_SLI_STAT_t; + +/* Structure used to hold SLI information */ +struct lpfc_sli { + LPFC_SLI_INIT_t sliinit; /* initialization info */ + /* Additional sli_flags */ +#define LPFC_SLI_MBOX_ACTIVE 0x100 /* HBA mailbox is currently active */ +#define LPFC_SLI2_ACTIVE 0x200 /* SLI2 overlay in firmware is active */ +#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */ + + struct lpfc_sli_ring ring[LPFC_MAX_RING]; + int fcp_ring; /* ring used for FCP initiator commands */ + int next_ring; + + int ip_ring; /* ring used for IP network drv cmds */ + + LPFC_SLI_STAT_t slistat; /* SLI statistical info */ + struct list_head mboxq; + uint16_t mboxq_cnt; /* current length of queue */ + uint16_t mboxq_max; /* max length */ + LPFC_MBOXQ_t *mbox_active; /* active mboxq information */ + + struct timer_list mbox_tmo; /* Hold clk to timeout active mbox + cmd */ + + volatile uint32_t *MBhostaddr; /* virtual address for mbox cmds */ +}; + +/* Given a pointer to the start of the ring, and the slot number of + * the desired iocb entry, calc a pointer to that entry. + * (assume iocb entry size is 32 bytes, or 8 words) + */ +#define IOCB_ENTRY(ring,slot) ((IOCB_t *)(((char *)(ring)) + ((slot) * 32))) + +#define LPFC_SLI_ABORT_IMED 0 /* Immediate abort of IOCB, deque and + call compl routine immediately. */ +#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox + command */ + +/* Flags for aborting I/Os on tx and txcmpl queues */ +#define LPFC_ABORT_TXQ 1 /* Abort I/Os on txq */ +#define LPFC_ABORT_TXCMPLQ 2 /* Abort I/Os on txcmplq */ +#define LPFC_ABORT_ALLQ 3 /* Abort I/Os both txq and txcmplq */ + +#endif /* _H_LPFC_SLI */ diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h new file mode 100644 index 000000000..51c9dcee7 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -0,0 +1,37 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_version.h 1.36 2004/12/07 14:51:53EST sf_support Exp $ + */ + +#ifndef _H_LPFC_VERSION +#define _H_LPFC_VERSION + +#define LPFC_DRIVER_VERSION "8.0.16" + +#define LPFC_DRIVER_NAME "lpfc" + +#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ + LPFC_DRIVER_VERSION + +#define DFC_API_VERSION "0.0.0" + +#endif diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c new file mode 100644 index 000000000..1becb3691 --- /dev/null +++ b/drivers/scsi/sata_uli.c @@ -0,0 +1,283 @@ +/* + * sata_uli.c - ULi Electronics SATA + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_uli" +#define DRV_VERSION "0.11" + +enum { + uli_5289 = 0, + uli_5287 = 1, + + /* PCI configuration registers */ + ULI_SCR_BASE = 0x90, /* sata0 phy SCR registers */ + ULI_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + +}; + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); + +static struct pci_device_id uli_pci_tbl[] = { + { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, + { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, + { } /* terminate list */ +}; + + +static struct pci_driver uli_pci_driver = { + .name = DRV_NAME, + .id_table = uli_pci_tbl, + .probe = uli_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template uli_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations uli_ops = { + .port_disable = ata_port_disable, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = sata_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .scr_read = uli_scr_read, + .scr_write = uli_scr_write, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static struct ata_port_info uli_port_info = { + .sht = &uli_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | + ATA_FLAG_NO_LEGACY, + .pio_mask = 0x03, //support pio mode 4 (FIXME) + .udma_mask = 0x7f, //support udma mode 6 + .port_ops = &uli_ops, +}; + + +MODULE_AUTHOR("Peer Chen"); +MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, uli_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg) +{ + unsigned int addr = ULI_SCR_BASE + (4 * sc_reg); + + switch (port_no) { + case 0: + break; + case 1: + addr += ULI_SATA1_OFS; + break; + case 2: + addr += ULI_SATA1_OFS*4; + break; + case 3: + addr += ULI_SATA1_OFS*5; + break; + default: + BUG(); + break; + } + return addr; +} + +static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg); + u32 val; + + pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val); + return val; +} + +static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr); + + pci_write_config_dword(ap->host_set->pdev, cfg_addr, val); +} + +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + + return uli_scr_cfg_read(ap, sc_reg); +} + +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 + return; + + uli_scr_cfg_write(ap, sc_reg, val); +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ata_probe_ent *probe_ent; + struct ata_port_info *ppi; + int rc; + unsigned int board_idx = (unsigned int) ent->driver_data; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + ppi = &uli_port_info; + probe_ent = ata_pci_init_native_mode(pdev, &ppi); + if (!probe_ent) { + rc = -ENOMEM; + goto err_out_regions; + } + + switch (board_idx) { + case uli_5287: + probe_ent->n_ports = 4; + + probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8; + probe_ent->port[2].altstatus_addr = + probe_ent->port[2].ctl_addr = + (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16; + + probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8; + probe_ent->port[3].altstatus_addr = + probe_ent->port[3].ctl_addr = + (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24; + + ata_std_ports(&probe_ent->port[2]); + ata_std_ports(&probe_ent->port[3]); + break; + + case uli_5289: + /* do nothing; ata_pci_init_native_mode did it all */ + break; + + default: + BUG(); + break; + } + + pci_set_master(pdev); + pci_enable_intx(pdev); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + kfree(probe_ent); + + return 0; + +err_out_regions: + pci_release_regions(pdev); + +err_out: + pci_disable_device(pdev); + return rc; + +} + +static int __init uli_init(void) +{ + return pci_module_init(&uli_pci_driver); +} + +static void __exit uli_exit(void) +{ + pci_unregister_driver(&uli_pci_driver); +} + + +module_init(uli_init); +module_exit(uli_exit); diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile new file mode 100644 index 000000000..9213b8b97 --- /dev/null +++ b/drivers/usb/atm/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# + +obj-$(CONFIG_USB_ATM) += usb_atm.o +obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c new file mode 100644 index 000000000..c06a3c1b0 --- /dev/null +++ b/drivers/usb/atm/speedtch.c @@ -0,0 +1,863 @@ +/****************************************************************************** + * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +# define USE_FW_LOADER +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static const char speedtch_driver_name[] = "speedtch"; + +#define SPEEDTOUCH_VENDORID 0x06b9 +#define SPEEDTOUCH_PRODUCTID 0x4061 + +/* Timeout in jiffies */ +#define CTRL_TIMEOUT (2*HZ) +#define DATA_TIMEOUT (2*HZ) + +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ +#define TOTAL 15 + +#define SIZE_7 1 +#define SIZE_b 8 +#define SIZE_d 4 +#define SIZE_e 1 +#define SIZE_f 1 + +static int dl_512_first = 0; +static int sw_buffering = 0; + +module_param(dl_512_first, bool, 0444); +MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware"); + +module_param(sw_buffering, uint, 0444); +MODULE_PARM_DESC(sw_buffering, "Enable software buffering"); + +#define UDSL_IOCTL_LINE_UP 1 +#define UDSL_IOCTL_LINE_DOWN 2 + +#define SPEEDTCH_ENDPOINT_INT 0x81 +#define SPEEDTCH_ENDPOINT_DATA 0x07 +#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05 + +#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) + +static struct usb_device_id speedtch_usb_ids[] = { + {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); + +struct speedtch_instance_data { + struct udsl_instance_data u; + + /* Status */ + struct urb *int_urb; + unsigned char int_data[16]; + struct work_struct poll_work; + struct timer_list poll_timer; +}; +/* USB */ + +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void speedtch_usb_disconnect(struct usb_interface *intf); +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data); +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs); +static void speedtch_poll_status(struct speedtch_instance_data *instance); + +static struct usb_driver speedtch_usb_driver = { + .owner = THIS_MODULE, + .name = speedtch_driver_name, + .probe = speedtch_usb_probe, + .disconnect = speedtch_usb_disconnect, + .ioctl = speedtch_usb_ioctl, + .id_table = speedtch_usb_ids, +}; + +/*************** +** firmware ** +***************/ + +static void speedtch_got_firmware(struct speedtch_instance_data *instance, + int got_it) +{ + int err; + struct usb_interface *intf; + + down(&instance->u.serialize); /* vs self, speedtch_firmware_start */ + if (instance->u.status == UDSL_LOADED_FIRMWARE) + goto out; + if (!got_it) { + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { + dbg("speedtch_got_firmware: usb_set_interface returned %d!", err); + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + + /* Set up interrupt endpoint */ + intf = usb_ifnum_to_if(instance->u.usb_dev, 0); + if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) { + + instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (instance->int_urb) { + + usb_fill_int_urb(instance->int_urb, instance->u.usb_dev, + usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT), + instance->int_data, + sizeof(instance->int_data), + speedtch_handle_int, instance, 50); + err = usb_submit_urb(instance->int_urb, GFP_KERNEL); + if (err) { + /* Doesn't matter; we'll poll anyway */ + dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err); + usb_free_urb(instance->int_urb); + instance->int_urb = NULL; + usb_driver_release_interface(&speedtch_usb_driver, intf); + } + } + } + /* Start status polling */ + mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); + + instance->u.status = UDSL_LOADED_FIRMWARE; + tasklet_schedule(&instance->u.receive_tasklet); + out: + up(&instance->u.serialize); + wake_up_interruptible(&instance->u.firmware_waiters); +} + +static int speedtch_set_swbuff(struct speedtch_instance_data *instance, + int state) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x32, 0x40, state ? 0x01 : 0x00, + 0x00, NULL, 0, 100); + if (ret < 0) { + printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", + state ? "En" : "Dis", ret); + return ret; + } + + dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); + return 0; +} + +static void speedtch_test_sequence(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[10]; + int ret; + + /* URB 147 */ + buf[0] = 0x1c; + buf[1] = 0x50; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); + + /* URB 148 */ + buf[0] = 0x32; + buf[1] = 0x00; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x02, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); + + /* URB 149 */ + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x03, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); + + /* URB 150 */ + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x04, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); +} + +static int speedtch_start_synchro(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[2]; + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x04, 0x00, + buf, sizeof(buf), CTRL_TIMEOUT); + if (ret < 0) { + printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret); + return ret; + } + + dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]); + return 0; +} + +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) +{ + struct speedtch_instance_data *instance = urb->context; + unsigned int count = urb->actual_length; + int ret; + + /* The magic interrupt for "up state" */ + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; + /* The magic interrupt for "down state" */ + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated; clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + if (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } + + if (!memcmp(up_int, instance->int_data, 6)) { + del_timer(&instance->poll_timer); + printk(KERN_NOTICE "DSL line goes up\n"); + } else if (!memcmp(down_int, instance->int_data, 6)) { + printk(KERN_NOTICE "DSL line goes down\n"); + } else { + int i; + + printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); + for (i = 0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + } + schedule_work(&instance->poll_work); + + exit: + rmb(); + if (!instance->int_urb) + return; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + err("%s - usb_submit_urb failed with result %d", __func__, ret); +} + +static int speedtch_get_status(struct speedtch_instance_data *instance, + unsigned char *buf) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + memset(buf, 0, TOTAL); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG 7 failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG B failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG D failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG E failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG F failed"); + return ret; + } + + return 0; +} + +static void speedtch_poll_status(struct speedtch_instance_data *instance) +{ + unsigned char buf[TOTAL]; + int ret; + + ret = speedtch_get_status(instance, buf); + if (ret) { + printk(KERN_WARNING + "SpeedTouch: Error %d fetching device status\n", ret); + return; + } + + dbg("Line state %02x", buf[OFFSET_7]); + + switch (buf[OFFSET_7]) { + case 0: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is down\n"); + } + break; + + case 0x08: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "ADSL line is blocked?\n"); + } + break; + + case 0x10: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is synchronising\n"); + } + break; + + case 0x20: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { + int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) + | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); + int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) + | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); + + if (!(down_speed & 0x0000ffff) && + !(up_speed & 0x0000ffff)) { + down_speed >>= 16; + up_speed >>= 16; + } + instance->u.atm_dev->link_rate = down_speed * 1000 / 424; + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + + printk(KERN_NOTICE + "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", + down_speed, up_speed); + } + break; + + default: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); + } + break; + } +} + +static void speedtch_timer_poll(unsigned long data) +{ + struct speedtch_instance_data *instance = (void *)data; + + schedule_work(&instance->poll_work); + mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); +} + +#ifdef USE_FW_LOADER +static void speedtch_upload_firmware(struct speedtch_instance_data *instance, + const struct firmware *fw1, + const struct firmware *fw2) +{ + unsigned char *buffer; + struct usb_device *usb_dev = instance->u.usb_dev; + struct usb_interface *intf; + int actual_length, ret; + int offset; + + dbg("speedtch_upload_firmware"); + + if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { + dbg("speedtch_upload_firmware: interface not found!"); + goto fail; + } + + if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + dbg("speedtch_upload_firmware: no memory for buffer!"); + goto fail; + } + + /* A user-space firmware loader may already have claimed interface #2 */ + if ((ret = + usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) { + dbg("speedtch_upload_firmware: interface in use (%d)!", ret); + goto fail_free; + } + + /* URB 7 */ + if (dl_512_first) { /* some modems need a read before writing the firmware */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, 2 * HZ); + + if (ret < 0 && ret != -ETIMEDOUT) + dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret); + else + dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret); + } + + /* URB 8 : both leds are static green */ + for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); + memcpy(buffer, fw1->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size); + } + + /* USB led blinking green, ADSL led off */ + + /* URB 11 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length); + + /* URBs 12 to 139 - USB led blinking green, ADSL led off */ + for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); + memcpy(buffer, fw2->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); + goto fail_release; + } + } + dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size); + + /* USB led static green, ADSL led static red */ + + /* URB 142 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret); + goto fail_release; + } + + /* success */ + dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length); + + /* Delay to allow firmware to start up. We can do this here + because we're in our own kernel thread anyway. */ + msleep(1000); + + /* Enable software buffering, if requested */ + if (sw_buffering) + speedtch_set_swbuff(instance, 1); + + /* Magic spell; don't ask us what this does */ + speedtch_test_sequence(instance); + + /* Start modem synchronisation */ + if (speedtch_start_synchro(instance)) + dbg("speedtch_start_synchro: failed"); + + speedtch_got_firmware(instance, 1); + + free_page((unsigned long)buffer); + return; + + fail_release: + /* Only release interface #2 if uploading failed; we don't release it + we succeeded. This prevents the userspace tools from trying to load + the firmware themselves */ + usb_driver_release_interface(&speedtch_usb_driver, intf); + fail_free: + free_page((unsigned long)buffer); + fail: + speedtch_got_firmware(instance, 0); +} + +static int speedtch_find_firmware(struct speedtch_instance_data + *instance, int phase, + const struct firmware **fw_p) +{ + char buf[24]; + const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice; + const u8 major_revision = bcdDevice >> 8; + const u8 minor_revision = bcdDevice & 0xff; + + sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin", phase); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); + return -ENOENT; + } + } + } + + dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf); + + return 0; +} + +static int speedtch_load_firmware(void *arg) +{ + const struct firmware *fw1, *fw2; + struct speedtch_instance_data *instance = arg; + + BUG_ON(!instance); + + daemonize("firmware/speedtch"); + + if (!speedtch_find_firmware(instance, 1, &fw1)) { + if (!speedtch_find_firmware(instance, 2, &fw2)) { + speedtch_upload_firmware(instance, fw1, fw2); + release_firmware(fw2); + } + release_firmware(fw1); + } + + /* In case we failed, set state back to NO_FIRMWARE so that + another later attempt may work. Otherwise, we never actually + manage to recover if, for example, the firmware is on /usr and + we look for it too early. */ + speedtch_got_firmware(instance, 0); + + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); + return 0; +} +#endif /* USE_FW_LOADER */ + +static void speedtch_firmware_start(struct speedtch_instance_data *instance) +{ +#ifdef USE_FW_LOADER + int ret; +#endif + + dbg("speedtch_firmware_start"); + + down(&instance->u.serialize); /* vs self, speedtch_got_firmware */ + + if (instance->u.status >= UDSL_LOADING_FIRMWARE) { + up(&instance->u.serialize); + return; + } + + instance->u.status = UDSL_LOADING_FIRMWARE; + up(&instance->u.serialize); + +#ifdef USE_FW_LOADER + udsl_get_instance(&instance->u); + try_module_get(THIS_MODULE); + + ret = kernel_thread(speedtch_load_firmware, instance, + CLONE_FS | CLONE_FILES); + + if (ret >= 0) + return; /* OK */ + + dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); + + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); + /* Just pretend it never happened... hope modem_run happens */ +#endif /* USE_FW_LOADER */ + + speedtch_got_firmware(instance, 0); +} + +static int speedtch_firmware_wait(struct udsl_instance_data *instance) +{ + speedtch_firmware_start((void *)instance); + + if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) + return -ERESTARTSYS; + + return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; +} + +/********** +** USB ** +**********/ + +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data) +{ + struct speedtch_instance_data *instance = usb_get_intfdata(intf); + + dbg("speedtch_usb_ioctl entered"); + + if (!instance) { + dbg("speedtch_usb_ioctl: NULL instance!"); + return -ENODEV; + } + + switch (code) { + case UDSL_IOCTL_LINE_UP: + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + speedtch_got_firmware(instance, 1); + return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; + case UDSL_IOCTL_LINE_DOWN: + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + return 0; + default: + return -ENOTTY; + } +} + +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int ifnum = intf->altsetting->desc.bInterfaceNumber; + struct speedtch_instance_data *instance; + unsigned char mac_str[13]; + int ret, i; + char buf7[SIZE_7]; + + dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || + (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || + (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) + return -ENODEV; + + dbg("speedtch_usb_probe: device accepted"); + + /* instance init */ + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + dbg("speedtch_usb_probe: no memory for instance data!"); + return -ENOMEM; + } + + memset(instance, 0, sizeof(struct speedtch_instance_data)); + + if ((ret = usb_set_interface(dev, 0, 0)) < 0) + goto fail; + + if ((ret = usb_set_interface(dev, 2, 0)) < 0) + goto fail; + + instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; + instance->u.firmware_wait = speedtch_firmware_wait; + instance->u.driver_name = speedtch_driver_name; + + ret = udsl_instance_setup(dev, &instance->u); + if (ret) + goto fail; + + init_timer(&instance->poll_timer); + instance->poll_timer.function = speedtch_timer_poll; + instance->poll_timer.data = (unsigned long)instance; + + INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); + + /* set MAC address, it is stored in the serial number */ + memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); + if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { + for (i = 0; i < 6; i++) + instance->u.atm_dev->esi[i] = + (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); + } + + /* First check whether the modem already seems to be alive */ + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2); + + if (ret == SIZE_7) { + dbg("firmware appears to be already loaded"); + speedtch_got_firmware(instance, 1); + speedtch_poll_status(instance); + } else { + speedtch_firmware_start(instance); + } + + usb_set_intfdata(intf, instance); + + return 0; + + fail: + kfree(instance); + + return -ENOMEM; +} + +static void speedtch_usb_disconnect(struct usb_interface *intf) +{ + struct speedtch_instance_data *instance = usb_get_intfdata(intf); + + dbg("speedtch_usb_disconnect entered"); + + if (!instance) { + dbg("speedtch_usb_disconnect: NULL instance!"); + return; + } + + if (instance->int_urb) { + struct urb *int_urb = instance->int_urb; + instance->int_urb = NULL; + wmb(); + usb_unlink_urb(int_urb); + usb_free_urb(int_urb); + } + + instance->int_data[0] = 1; + del_timer_sync(&instance->poll_timer); + wmb(); + flush_scheduled_work(); + + udsl_instance_disconnect(&instance->u); + + /* clean up */ + usb_set_intfdata(intf, NULL); + udsl_put_instance(&instance->u); +} + +/*********** +** init ** +***********/ + +static int __init speedtch_usb_init(void) +{ + dbg("speedtch_usb_init: driver version " DRIVER_VERSION); + + return usb_register(&speedtch_usb_driver); +} + +static void __exit speedtch_usb_cleanup(void) +{ + dbg("speedtch_usb_cleanup entered"); + + usb_deregister(&speedtch_usb_driver); +} + +module_init(speedtch_usb_init); +module_exit(speedtch_usb_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c new file mode 100644 index 000000000..9180dda04 --- /dev/null +++ b/drivers/usb/atm/usb_atm.c @@ -0,0 +1,1201 @@ +/****************************************************************************** + * usb_atm.c - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + +/* + * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr) + * + * 1.7+: - See the check-in logs + * + * 1.6: - No longer opens a connection if the firmware is not loaded + * - Added support for the speedtouch 330 + * - Removed the limit on the number of devices + * - Module now autoloads on device plugin + * - Merged relevant parts of sarlib + * - Replaced the kernel thread with a tasklet + * - New packet transmission code + * - Changed proc file contents + * - Fixed all known SMP races + * - Many fixes and cleanups + * - Various fixes by Oliver Neukum (oliver@neukum.name) + * + * 1.5A: - Version for inclusion in 2.5 series kernel + * - Modifications by Richard Purdie (rpurdie@rpsys.net) + * - made compatible with kernel 2.5.6 onwards by changing + * udsl_usb_send_data_context->urb to a pointer and adding code + * to alloc and free it + * - remove_wait_queue() added to udsl_atm_processqueue_thread() + * + * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. + * (reported by stephen.robinson@zen.co.uk) + * + * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() + * - unlink all active send urbs of a vcc that is being closed. + * + * 1.3.1: - added the version number + * + * 1.3: - Added multiple send urb support + * - fixed memory leak and vcc->tx_inuse starvation bug + * when not enough memory left in vcc. + * + * 1.2: - Fixed race condition in udsl_usb_send_data() + * 1.1: - Turned off packet debugging + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#ifdef DEBUG +#define UDSL_ASSERT(x) BUG_ON(!(x)) +#else +#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0) +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; +static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; +static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; +static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; +static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; +static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; + +module_param(num_rcv_urbs, uint, 0444); +MODULE_PARM_DESC(num_rcv_urbs, + "Number of urbs used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")"); + +module_param(num_snd_urbs, uint, 0444); +MODULE_PARM_DESC(num_snd_urbs, + "Number of urbs used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")"); + +module_param(num_rcv_bufs, uint, 0444); +MODULE_PARM_DESC(num_rcv_bufs, + "Number of buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")"); + +module_param(num_snd_bufs, uint, 0444); +MODULE_PARM_DESC(num_snd_bufs, + "Number of buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")"); + +module_param(rcv_buf_size, uint, 0444); +MODULE_PARM_DESC(rcv_buf_size, + "Size of the buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")"); + +module_param(snd_buf_size, uint, 0444); +MODULE_PARM_DESC(snd_buf_size, + "Size of the buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")"); + +/* ATM */ + +static void udsl_atm_dev_close(struct atm_dev *dev); +static int udsl_atm_open(struct atm_vcc *vcc); +static void udsl_atm_close(struct atm_vcc *vcc); +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); + +static struct atmdev_ops udsl_atm_devops = { + .dev_close = udsl_atm_dev_close, + .open = udsl_atm_open, + .close = udsl_atm_close, + .ioctl = udsl_atm_ioctl, + .send = udsl_atm_send, + .proc_read = udsl_atm_proc_read, + .owner = THIS_MODULE, +}; + +/*********** +** misc ** +***********/ + +static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb) +{ + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); +} + +/************* +** decode ** +*************/ + +static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance, + short vpi, int vci) +{ + struct udsl_vcc_data *vcc; + + list_for_each_entry(vcc, &instance->vcc_list, list) + if ((vcc->vci == vci) && (vcc->vpi == vpi)) + return vcc; + return NULL; +} + +static void udsl_extract_cells(struct udsl_instance_data *instance, + unsigned char *source, unsigned int howmany) +{ + struct udsl_vcc_data *cached_vcc = NULL; + struct atm_vcc *vcc; + struct sk_buff *sarb; + struct udsl_vcc_data *vcc_data; + int cached_vci = 0; + unsigned int i; + int pti; + int vci; + short cached_vpi = 0; + short vpi; + + for (i = 0; i < howmany; + i++, source += ATM_CELL_SIZE + instance->rcv_padding) { + vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); + vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); + pti = (source[3] & 0x2) != 0; + + vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); + + if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) + vcc_data = cached_vcc; + else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) { + cached_vcc = vcc_data; + cached_vpi = vpi; + cached_vci = vci; + } else { + dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); + continue; + } + + vcc = vcc_data->vcc; + sarb = vcc_data->sarb; + + if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { + dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); + /* discard cells already received */ + skb_trim(sarb, 0); + } + + memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + __skb_put(sarb, ATM_CELL_PAYLOAD); + + if (pti) { + struct sk_buff *skb; + unsigned int length; + unsigned int pdu_length; + + length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5]; + + /* guard against overflow */ + if (length > ATM_MAX_AAL5_PDU) { + dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD; + + if (sarb->len < pdu_length) { + dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); + + if (!(skb = dev_alloc_skb(length))) { + dbg("udsl_extract_cells: no memory for skb (length: %u)!", length); + atomic_inc(&vcc->stats->rx_drop); + goto out; + } + + vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); + + if (!atm_charge(vcc, skb->truesize)) { + dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); + dev_kfree_skb(skb); + goto out; /* atm_charge increments rx_drop */ + } + + memcpy(skb->data, sarb->tail - pdu_length, length); + __skb_put(skb, length); + + vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); + + PACKETDEBUG(skb->data, skb->len); + + vcc->push(vcc, skb); + + atomic_inc(&vcc->stats->rx); + out: + skb_trim(sarb, 0); + } + } +} + +/************* +** encode ** +*************/ + +static const unsigned char zeros[ATM_CELL_PAYLOAD]; + +static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_control *ctrl = UDSL_SKB(skb); + unsigned int zero_padding; + u32 crc; + + ctrl->atm_data.vcc = vcc; + ctrl->cell_header[0] = vcc->vpi >> 4; + ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12); + ctrl->cell_header[2] = vcc->vci >> 4; + ctrl->cell_header[3] = vcc->vci << 4; + ctrl->cell_header[4] = 0xec; + + ctrl->num_cells = UDSL_NUM_CELLS(skb->len); + ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; + + zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; + + if (ctrl->num_entire + 1 < ctrl->num_cells) + ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + else + ctrl->pdu_padding = zero_padding; + + ctrl->aal5_trailer[0] = 0; /* UU = 0 */ + ctrl->aal5_trailer[1] = 0; /* CPI = 0 */ + ctrl->aal5_trailer[2] = skb->len >> 8; + ctrl->aal5_trailer[3] = skb->len; + + crc = crc32_be(~0, skb->data, skb->len); + crc = crc32_be(crc, zeros, zero_padding); + crc = crc32_be(crc, ctrl->aal5_trailer, 4); + crc = ~crc; + + ctrl->aal5_trailer[4] = crc >> 24; + ctrl->aal5_trailer[5] = crc >> 16; + ctrl->aal5_trailer[6] = crc >> 8; + ctrl->aal5_trailer[7] = crc; +} + +static unsigned int udsl_write_cells(struct udsl_instance_data *instance, + unsigned int howmany, struct sk_buff *skb, + unsigned char **target_p) +{ + struct udsl_control *ctrl = UDSL_SKB(skb); + unsigned char *target = *target_p; + unsigned int nc, ne, i; + + vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); + + nc = ctrl->num_cells; + ne = min(howmany, ctrl->num_entire); + + for (i = 0; i < ne; i++) { + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy(target, skb->data, ATM_CELL_PAYLOAD); + target += ATM_CELL_PAYLOAD; + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + __skb_pull(skb, ATM_CELL_PAYLOAD); + } + + ctrl->num_entire -= ne; + + if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) + goto out; + + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy(target, skb->data, skb->len); + target += skb->len; + __skb_pull(skb, skb->len); + memset(target, 0, ctrl->pdu_padding); + target += ctrl->pdu_padding; + + if (--ctrl->num_cells) { + if (!--howmany) { + ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + goto out; + } + + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + + --ctrl->num_cells; + UDSL_ASSERT(!ctrl->num_cells); + } + + memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + target += ATM_AAL5_TRAILER; + /* set pti bit in last cell */ + *(target + 3 - ATM_CELL_SIZE) |= 0x2; + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + out: + *target_p = target; + return nc - ctrl->num_cells; +} + +/************** +** receive ** +**************/ + +static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance; + struct udsl_receiver *rcv; + unsigned long flags; + + if (!urb || !(rcv = urb->context)) { + dbg("udsl_complete_receive: bad urb!"); + return; + } + + instance = rcv->instance; + buf = rcv->buffer; + + buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding); + + vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); + + UDSL_ASSERT(buf->filled_cells <= rcv_buf_size); + + /* may not be in_interrupt() */ + spin_lock_irqsave(&instance->receive_lock, flags); + list_add(&rcv->list, &instance->spare_receivers); + list_add_tail(&buf->list, &instance->filled_receive_buffers); + if (likely(!urb->status)) + tasklet_schedule(&instance->receive_tasklet); + spin_unlock_irqrestore(&instance->receive_lock, flags); +} + +static void udsl_process_receive(unsigned long data) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; + struct udsl_receiver *rcv; + int err; + + made_progress: + while (!list_empty(&instance->spare_receive_buffers)) { + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->spare_receivers)) { + spin_unlock_irq(&instance->receive_lock); + break; + } + rcv = list_entry(instance->spare_receivers.next, + struct udsl_receiver, list); + list_del(&rcv->list); + spin_unlock_irq(&instance->receive_lock); + + buf = list_entry(instance->spare_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); + + rcv->buffer = buf; + + usb_fill_bulk_urb(rcv->urb, instance->usb_dev, + usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + udsl_complete_receive, rcv); + + vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", + rcv->urb, rcv, buf); + + if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { + dbg("udsl_process_receive: urb submission failed (%d)!", err); + list_add(&buf->list, &instance->spare_receive_buffers); + spin_lock_irq(&instance->receive_lock); + list_add(&rcv->list, &instance->spare_receivers); + spin_unlock_irq(&instance->receive_lock); + break; + } + } + + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->filled_receive_buffers)) { + spin_unlock_irq(&instance->receive_lock); + return; /* done - no more buffers */ + } + buf = list_entry(instance->filled_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->receive_lock); + + vdbg("udsl_process_receive: processing buf 0x%p", buf); + udsl_extract_cells(instance, buf->base, buf->filled_cells); + list_add(&buf->list, &instance->spare_receive_buffers); + goto made_progress; +} + +/*********** +** send ** +***********/ + +static void udsl_complete_send(struct urb *urb, struct pt_regs *regs) +{ + struct udsl_instance_data *instance; + struct udsl_sender *snd; + unsigned long flags; + + if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { + dbg("udsl_complete_send: bad urb!"); + return; + } + + vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, + urb->status, snd, snd->buffer); + + /* may not be in_interrupt() */ + spin_lock_irqsave(&instance->send_lock, flags); + list_add(&snd->list, &instance->spare_senders); + list_add(&snd->buffer->list, &instance->spare_send_buffers); + tasklet_schedule(&instance->send_tasklet); + spin_unlock_irqrestore(&instance->send_lock, flags); +} + +static void udsl_process_send(unsigned long data) +{ + struct udsl_send_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; + struct sk_buff *skb; + struct udsl_sender *snd; + int err; + unsigned int num_written; + + made_progress: + spin_lock_irq(&instance->send_lock); + while (!list_empty(&instance->spare_senders)) { + if (!list_empty(&instance->filled_send_buffers)) { + buf = list_entry(instance->filled_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); + } else if ((buf = instance->current_buffer)) { + instance->current_buffer = NULL; + } else /* all buffers empty */ + break; + + snd = list_entry(instance->spare_senders.next, + struct udsl_sender, list); + list_del(&snd->list); + spin_unlock_irq(&instance->send_lock); + + snd->buffer = buf; + usb_fill_bulk_urb(snd->urb, instance->usb_dev, + usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), + udsl_complete_send, snd); + + vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", + snd->urb, snd_buf_size - buf->free_cells, snd, buf); + + if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { + dbg("udsl_process_send: urb submission failed (%d)!", err); + spin_lock_irq(&instance->send_lock); + list_add(&snd->list, &instance->spare_senders); + spin_unlock_irq(&instance->send_lock); + list_add(&buf->list, &instance->filled_send_buffers); + return; /* bail out */ + } + + spin_lock_irq(&instance->send_lock); + } /* while */ + spin_unlock_irq(&instance->send_lock); + + if (!instance->current_skb) + instance->current_skb = skb_dequeue(&instance->sndqueue); + if (!instance->current_skb) + return; /* done - no more skbs */ + + skb = instance->current_skb; + + if (!(buf = instance->current_buffer)) { + spin_lock_irq(&instance->send_lock); + if (list_empty(&instance->spare_send_buffers)) { + instance->current_buffer = NULL; + spin_unlock_irq(&instance->send_lock); + return; /* done - no more buffers */ + } + buf = list_entry(instance->spare_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->send_lock); + + buf->free_start = buf->base; + buf->free_cells = snd_buf_size; + + instance->current_buffer = buf; + } + + num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start); + + vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", + num_written, skb, buf); + + if (!(buf->free_cells -= num_written)) { + list_add_tail(&buf->list, &instance->filled_send_buffers); + instance->current_buffer = NULL; + } + + vdbg("udsl_process_send: buffer contains %d cells, %d left", + snd_buf_size - buf->free_cells, buf->free_cells); + + if (!UDSL_SKB(skb)->num_cells) { + struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc; + + udsl_pop(vcc, skb); + instance->current_skb = NULL; + + atomic_inc(&vcc->stats->tx); + } + + goto made_progress; +} + +static void udsl_cancel_send(struct udsl_instance_data *instance, + struct atm_vcc *vcc) +{ + struct sk_buff *skb, *n; + + dbg("udsl_cancel_send entered"); + spin_lock_irq(&instance->sndqueue.lock); + for (skb = instance->sndqueue.next, n = skb->next; + skb != (struct sk_buff *)&instance->sndqueue; + skb = n, n = skb->next) + if (UDSL_SKB(skb)->atm_data.vcc == vcc) { + dbg("udsl_cancel_send: popping skb 0x%p", skb); + __skb_unlink(skb, &instance->sndqueue); + udsl_pop(vcc, skb); + } + spin_unlock_irq(&instance->sndqueue.lock); + + tasklet_disable(&instance->send_tasklet); + if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) { + dbg("udsl_cancel_send: popping current skb (0x%p)", skb); + instance->current_skb = NULL; + udsl_pop(vcc, skb); + } + tasklet_enable(&instance->send_tasklet); + dbg("udsl_cancel_send done"); +} + +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + int err; + + vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); + + if (!instance) { + dbg("udsl_atm_send: NULL data!"); + err = -ENODEV; + goto fail; + } + + if (vcc->qos.aal != ATM_AAL5) { + dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); + err = -EINVAL; + goto fail; + } + + if (skb->len > ATM_MAX_AAL5_PDU) { + dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len, + ATM_MAX_AAL5_PDU); + err = -EINVAL; + goto fail; + } + + PACKETDEBUG(skb->data, skb->len); + + udsl_groom_skb(vcc, skb); + skb_queue_tail(&instance->sndqueue, skb); + tasklet_schedule(&instance->send_tasklet); + + return 0; + + fail: + udsl_pop(vcc, skb); + return err; +} + +/******************** +** bean counting ** +********************/ + +static void udsl_destroy_instance(struct kref *kref) +{ + struct udsl_instance_data *instance = + container_of(kref, struct udsl_instance_data, refcount); + + tasklet_kill(&instance->receive_tasklet); + tasklet_kill(&instance->send_tasklet); + usb_put_dev(instance->usb_dev); + kfree(instance); +} + +void udsl_get_instance(struct udsl_instance_data *instance) +{ + kref_get(&instance->refcount); +} + +void udsl_put_instance(struct udsl_instance_data *instance) +{ + kref_put(&instance->refcount, udsl_destroy_instance); +} + +/********** +** ATM ** +**********/ + +static void udsl_atm_dev_close(struct atm_dev *dev) +{ + struct udsl_instance_data *instance = dev->dev_data; + + dev->dev_data = NULL; + udsl_put_instance(instance); +} + +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) +{ + struct udsl_instance_data *instance = atm_dev->dev_data; + int left = *pos; + + if (!instance) { + dbg("udsl_atm_proc_read: NULL instance!"); + return -ENODEV; + } + + if (!left--) + return sprintf(page, "%s\n", instance->description); + + if (!left--) + return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi[0], atm_dev->esi[1], + atm_dev->esi[2], atm_dev->esi[3], + atm_dev->esi[4], atm_dev->esi[5]); + + if (!left--) + return sprintf(page, + "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", + atomic_read(&atm_dev->stats.aal5.tx), + atomic_read(&atm_dev->stats.aal5.tx_err), + atomic_read(&atm_dev->stats.aal5.rx), + atomic_read(&atm_dev->stats.aal5.rx_err), + atomic_read(&atm_dev->stats.aal5.rx_drop)); + + if (!left--) { + switch (atm_dev->signal) { + case ATM_PHY_SIG_FOUND: + sprintf(page, "Line up"); + break; + case ATM_PHY_SIG_LOST: + sprintf(page, "Line down"); + break; + default: + sprintf(page, "Line state unknown"); + break; + } + + if (instance->usb_dev->state == USB_STATE_NOTATTACHED) + strcat(page, ", disconnected\n"); + else { + if (instance->status == UDSL_LOADED_FIRMWARE) + strcat(page, ", firmware loaded\n"); + else if (instance->status == UDSL_LOADING_FIRMWARE) + strcat(page, ", firmware loading\n"); + else + strcat(page, ", no firmware\n"); + } + + return strlen(page); + } + + return 0; +} + +static int udsl_atm_open(struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *new; + unsigned int max_pdu; + int vci = vcc->vci; + short vpi = vcc->vpi; + int err; + + dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci); + + if (!instance) { + dbg("udsl_atm_open: NULL data!"); + return -ENODEV; + } + + /* only support AAL5 */ + if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) + || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { + dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); + return -EINVAL; + } + + if (instance->firmware_wait && + (err = instance->firmware_wait(instance)) < 0) { + dbg("udsl_atm_open: firmware not loaded (%d)!", err); + return err; + } + + down(&instance->serialize); /* vs self, udsl_atm_close */ + + if (udsl_find_vcc(instance, vpi, vci)) { + dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci); + up(&instance->serialize); + return -EADDRINUSE; + } + + if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for vcc_data!"); + up(&instance->serialize); + return -ENOMEM; + } + + memset(new, 0, sizeof(struct udsl_vcc_data)); + new->vcc = vcc; + new->vpi = vpi; + new->vci = vci; + + /* udsl_extract_cells requires at least one cell */ + max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; + if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for SAR buffer!"); + kfree(new); + up(&instance->serialize); + return -ENOMEM; + } + + vcc->dev_data = new; + + tasklet_disable(&instance->receive_tasklet); + list_add(&new->list, &instance->vcc_list); + tasklet_enable(&instance->receive_tasklet); + + set_bit(ATM_VF_ADDR, &vcc->flags); + set_bit(ATM_VF_PARTIAL, &vcc->flags); + set_bit(ATM_VF_READY, &vcc->flags); + + up(&instance->serialize); + + tasklet_schedule(&instance->receive_tasklet); + + dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); + + return 0; +} + +static void udsl_atm_close(struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *vcc_data = vcc->dev_data; + + dbg("udsl_atm_close called"); + + if (!instance || !vcc_data) { + dbg("udsl_atm_close: NULL data!"); + return; + } + + dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", + vcc_data, vcc_data->vpi, vcc_data->vci); + + udsl_cancel_send(instance, vcc); + + down(&instance->serialize); /* vs self, udsl_atm_open */ + + tasklet_disable(&instance->receive_tasklet); + list_del(&vcc_data->list); + tasklet_enable(&instance->receive_tasklet); + + kfree_skb(vcc_data->sarb); + vcc_data->sarb = NULL; + + kfree(vcc_data); + vcc->dev_data = NULL; + + vcc->vpi = ATM_VPI_UNSPEC; + vcc->vci = ATM_VCI_UNSPEC; + clear_bit(ATM_VF_READY, &vcc->flags); + clear_bit(ATM_VF_PARTIAL, &vcc->flags); + clear_bit(ATM_VF_ADDR, &vcc->flags); + + up(&instance->serialize); + + dbg("udsl_atm_close successful"); +} + +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, + void __user * arg) +{ + switch (cmd) { + case ATM_QUERYLOOP: + return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; + default: + return -ENOIOCTLCMD; + } +} + +/********** +** USB ** +**********/ + +int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance) +{ + char *buf; + int i, length; + + kref_init(&instance->refcount); /* one for USB */ + udsl_get_instance(instance); /* one for ATM */ + + init_MUTEX(&instance->serialize); + + instance->usb_dev = dev; + + INIT_LIST_HEAD(&instance->vcc_list); + + instance->status = UDSL_NO_FIRMWARE; + init_waitqueue_head(&instance->firmware_waiters); + + spin_lock_init(&instance->receive_lock); + INIT_LIST_HEAD(&instance->spare_receivers); + INIT_LIST_HEAD(&instance->filled_receive_buffers); + + tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance); + INIT_LIST_HEAD(&instance->spare_receive_buffers); + + skb_queue_head_init(&instance->sndqueue); + + spin_lock_init(&instance->send_lock); + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); + + tasklet_init(&instance->send_tasklet, udsl_process_send, + (unsigned long)instance); + INIT_LIST_HEAD(&instance->filled_send_buffers); + + /* receive init */ + for (i = 0; i < num_rcv_urbs; i++) { + struct udsl_receiver *rcv = &(instance->receivers[i]); + + if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for receive urb %d!", i); + goto fail; + } + + rcv->instance = instance; + + list_add(&rcv->list, &instance->spare_receivers); + } + + for (i = 0; i < num_rcv_bufs; i++) { + struct udsl_receive_buffer *buf = + &(instance->receive_buffers[i]); + + buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for receive buffer %d!", i); + goto fail; + } + + list_add(&buf->list, &instance->spare_receive_buffers); + } + + /* send init */ + for (i = 0; i < num_snd_urbs; i++) { + struct udsl_sender *snd = &(instance->senders[i]); + + if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for send urb %d!", i); + goto fail; + } + + snd->instance = instance; + + list_add(&snd->list, &instance->spare_senders); + } + + for (i = 0; i < num_snd_bufs; i++) { + struct udsl_send_buffer *buf = &(instance->send_buffers[i]); + + buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for send buffer %d!", i); + goto fail; + } + + list_add(&buf->list, &instance->spare_send_buffers); + } + + /* ATM init */ + instance->atm_dev = atm_dev_register(instance->driver_name, + &udsl_atm_devops, -1, NULL); + if (!instance->atm_dev) { + dbg("udsl_usb_probe: failed to register ATM device!"); + goto fail; + } + + instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX; + instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX; + instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + + /* temp init ATM device, set to 128kbit */ + instance->atm_dev->link_rate = 128 * 1000 / 424; + + /* device description */ + buf = instance->description; + length = sizeof(instance->description); + + if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + i = scnprintf(buf, length, " ("); + buf += i; + length -= i; + + if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + snprintf(buf, length, ")"); + + finish: + /* ready for ATM callbacks */ + wmb(); + instance->atm_dev->dev_data = instance; + + usb_get_dev(dev); + + return 0; + + fail: + for (i = 0; i < num_snd_bufs; i++) + kfree(instance->send_buffers[i].base); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb(instance->senders[i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree(instance->receive_buffers[i].base); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb(instance->receivers[i].urb); + + return -ENOMEM; +} + +void udsl_instance_disconnect(struct udsl_instance_data *instance) +{ + int i; + + dbg("udsl_instance_disconnect entered"); + + if (!instance) { + dbg("udsl_instance_disconnect: NULL instance!"); + return; + } + + /* receive finalize */ + tasklet_disable(&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + usb_kill_urb(instance->receivers[i].urb); + + /* no need to take the spinlock */ + INIT_LIST_HEAD(&instance->filled_receive_buffers); + INIT_LIST_HEAD(&instance->spare_receive_buffers); + + tasklet_enable(&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb(instance->receivers[i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree(instance->receive_buffers[i].base); + + /* send finalize */ + tasklet_disable(&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + usb_kill_urb(instance->senders[i].urb); + + /* no need to take the spinlock */ + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); + instance->current_buffer = NULL; + + tasklet_enable(&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb(instance->senders[i].urb); + + for (i = 0; i < num_snd_bufs; i++) + kfree(instance->send_buffers[i].base); + + /* ATM finalize */ + shutdown_atm_dev(instance->atm_dev); +} + +EXPORT_SYMBOL_GPL(udsl_get_instance); +EXPORT_SYMBOL_GPL(udsl_put_instance); +EXPORT_SYMBOL_GPL(udsl_instance_setup); +EXPORT_SYMBOL_GPL(udsl_instance_disconnect); + +/*********** +** init ** +***********/ + +static int __init udsl_usb_init(void) +{ + dbg("udsl_usb_init: driver version " DRIVER_VERSION); + + if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) { + printk(KERN_ERR __FILE__ ": unusable with this kernel!\n"); + return -EIO; + } + + if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) + || (num_snd_urbs > UDSL_MAX_SND_URBS) + || (num_rcv_bufs > UDSL_MAX_RCV_BUFS) + || (num_snd_bufs > UDSL_MAX_SND_BUFS) + || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) + || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) + return -EINVAL; + + return 0; +} + +static void __exit udsl_usb_exit(void) +{ +} + +module_init(udsl_usb_init); +module_exit(udsl_usb_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +/************ +** debug ** +************/ + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len) +{ + unsigned char buffer[256]; + int i = 0, j = 0; + + for (i = 0; i < len;) { + buffer[0] = '\0'; + sprintf(buffer, "%.3d :", i); + for (j = 0; (j < 16) && (i < len); j++, i++) { + sprintf(buffer, "%s %2.2x", buffer, data[i]); + } + dbg("%s", buffer); + } + return i; +} +#endif diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h new file mode 100644 index 000000000..188e91710 --- /dev/null +++ b/drivers/usb/atm/usb_atm.h @@ -0,0 +1,160 @@ +/****************************************************************************** + * usb_atm.h - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#define UDSL_MAX_RCV_URBS 4 +#define UDSL_MAX_SND_URBS 4 +#define UDSL_MAX_RCV_BUFS 8 +#define UDSL_MAX_SND_BUFS 8 +#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_DEFAULT_RCV_URBS 2 +#define UDSL_DEFAULT_SND_URBS 2 +#define UDSL_DEFAULT_RCV_BUFS 4 +#define UDSL_DEFAULT_SND_BUFS 4 +#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ + +#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) +#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) + +/* receive */ + +struct udsl_receive_buffer { + struct list_head list; + unsigned char *base; + unsigned int filled_cells; +}; + +struct udsl_receiver { + struct list_head list; + struct udsl_receive_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_vcc_data { + /* vpi/vci lookup */ + struct list_head list; + short vpi; + int vci; + struct atm_vcc *vcc; + + /* raw cell reassembly */ + struct sk_buff *sarb; +}; + +/* send */ + +struct udsl_send_buffer { + struct list_head list; + unsigned char *base; + unsigned char *free_start; + unsigned int free_cells; +}; + +struct udsl_sender { + struct list_head list; + struct udsl_send_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_control { + struct atm_skb_data atm_data; + unsigned int num_cells; + unsigned int num_entire; + unsigned int pdu_padding; + unsigned char cell_header[ATM_CELL_HEADER]; + unsigned char aal5_trailer[ATM_AAL5_TRAILER]; +}; + +#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) + +/* main driver data */ + +enum udsl_status { + UDSL_NO_FIRMWARE, + UDSL_LOADING_FIRMWARE, + UDSL_LOADED_FIRMWARE +}; + +struct udsl_instance_data { + struct kref refcount; + struct semaphore serialize; + + /* USB device part */ + struct usb_device *usb_dev; + char description[64]; + int data_endpoint; + int snd_padding; + int rcv_padding; + const char *driver_name; + + /* ATM device part */ + struct atm_dev *atm_dev; + struct list_head vcc_list; + + /* firmware */ + int (*firmware_wait) (struct udsl_instance_data *); + enum udsl_status status; + wait_queue_head_t firmware_waiters; + + /* receive */ + struct udsl_receiver receivers[UDSL_MAX_RCV_URBS]; + struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS]; + + spinlock_t receive_lock; + struct list_head spare_receivers; + struct list_head filled_receive_buffers; + + struct tasklet_struct receive_tasklet; + struct list_head spare_receive_buffers; + + /* send */ + struct udsl_sender senders[UDSL_MAX_SND_URBS]; + struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS]; + + struct sk_buff_head sndqueue; + + spinlock_t send_lock; + struct list_head spare_senders; + struct list_head spare_send_buffers; + + struct tasklet_struct send_tasklet; + struct sk_buff *current_skb; /* being emptied */ + struct udsl_send_buffer *current_buffer; /* being filled */ + struct list_head filled_send_buffers; +}; + +extern int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance); +extern void udsl_instance_disconnect(struct udsl_instance_data *instance); +extern void udsl_get_instance(struct udsl_instance_data *instance); +extern void udsl_put_instance(struct udsl_instance_data *instance); diff --git a/drivers/usb/media/pwc/Makefile b/drivers/usb/media/pwc/Makefile new file mode 100644 index 000000000..44bcc763a --- /dev/null +++ b/drivers/usb/media/pwc/Makefile @@ -0,0 +1,20 @@ +ifneq ($(KERNELRELEASE),) + +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o + +obj-m += pwc.o + +else + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +endif + +clean: + rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c + rm -rf .tmp_versions + diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c new file mode 100644 index 000000000..45c9323a1 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-ctrl.c @@ -0,0 +1,1630 @@ +/* Driver for Philips webcam + Functions that send various control messages to the webcam, including + video modes. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + Changes + 2001/08/03 Alvarado Added methods for changing white balance and + red/green gains + */ + +/* Control functions for the cam; brightness, contrast, video mode, etc. */ + +#ifdef __KERNEL__ +#include +#endif +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-uncompress.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 +#define SENSOR_TYPE_FORMATTER1 0x0C00 +#define READ_RAW_Y_MEAN_FORMATTER 0x3100 +#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 +#define MIRROR_IMAGE_FORMATTER 0x3300 +#define LED_FORMATTER 0x3400 +#define SENSOR_TYPE_FORMATTER2 0x3700 + +/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ +#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + +static char *size2name[PSZ_MAX] = +{ + "subQCIF", + "QSIF", + "QCIF", + "SIF", + "CIF", + "VGA", +}; + +/********/ + +/* Entries for the Nala (645/646) camera; the Nala doesn't have compression + preferences, so you either get compressed or non-compressed streams. + + An alternate value of 0 means this mode is not available at all. + */ + +struct Nala_table_entry { + char alternate; /* USB alternate setting */ + int compressed; /* Compressed yes/no */ + + unsigned char mode[3]; /* precomputed mode table */ +}; + +static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +{ +#include "pwc-nala.h" +}; + + +/****************************************************************************/ + + +#define SendControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, HZ / 2) + +#define RecvControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, HZ / 2) + + +#if PWC_DEBUG +void pwc_hexdump(void *p, int len) +{ + int i; + unsigned char *s; + char buf[100], *d; + + s = (unsigned char *)p; + d = buf; + *d = '\0'; + Debug("Doing hexdump @ %p, %d bytes.\n", p, len); + for (i = 0; i < len; i++) { + d += sprintf(d, "%02X ", *s++); + if ((i & 0xF) == 0xF) { + Debug("%s\n", buf); + d = buf; + *d = '\0'; + } + } + if ((i & 0xF) != 0) + Debug("%s\n", buf); +} +#endif + +static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, + index, + buf, buflen, HZ); +} + + + +static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +{ + unsigned char buf[3]; + int ret, fps; + struct Nala_table_entry *pEntry; + int frames2frames[31] = + { /* closest match of framerate */ + 0, 0, 0, 0, 4, /* 0-4 */ + 5, 5, 7, 7, 10, /* 5-9 */ + 10, 10, 12, 12, 15, /* 10-14 */ + 15, 15, 15, 20, 20, /* 15-19 */ + 20, 20, 20, 24, 24, /* 20-24 */ + 24, 24, 24, 24, 24, /* 25-29 */ + 24 /* 30 */ + }; + int frames2table[31] = + { 0, 0, 0, 0, 0, /* 0-4 */ + 1, 1, 1, 2, 2, /* 5-9 */ + 3, 3, 4, 4, 4, /* 10-14 */ + 5, 5, 5, 5, 5, /* 15-19 */ + 6, 6, 6, 6, 7, /* 20-24 */ + 7, 7, 7, 7, 7, /* 25-29 */ + 7 /* 30 */ + }; + + if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + return -EINVAL; + frames = frames2frames[frames]; + fps = frames2table[frames]; + pEntry = &Nala_table[size][fps]; + if (pEntry->alternate == 0) + return -EINVAL; + + if (pEntry->compressed) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pEntry->mode, 3); + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); + if (ret < 0) { + Debug("Failed to send video command... %d\n", ret); + return ret; + } + if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) + { + switch(pdev->type) { + case 645: + case 646: + pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); + break; + + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + break; + } + } + + pdev->cmd_len = 3; + memcpy(pdev->cmd_buf, buf, 3); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->valternate = pEntry->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; + if (pEntry->compressed) { + if (pdev->release < 5) { /* 4 fold compression */ + pdev->vbandlength = 528; + pdev->frame_size /= 4; + } + else { + pdev->vbandlength = 704; + pdev->frame_size /= 3; + } + } + else + pdev->vbandlength = 0; + return 0; +} + + +static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + unsigned char buf[13]; + const struct Timon_table_entry *pChoose; + int ret, fps; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + */ + pChoose = NULL; + while (compression <= 3) { + pChoose = &Timon_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pChoose->mode, 13); + if (snapshot) + buf[0] |= 0x80; + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + + pdev->cmd_len = 13; + memcpy(pdev->cmd_buf, buf, 13); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pChoose->bandlength > 0) + pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + +static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + const struct Kiara_table_entry *pChoose = 0; + int fps, ret; + unsigned char buf[12]; + struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ + if (size == PSZ_VGA && frames == 5 && snapshot) + { + /* Only available in case the raw palette is selected or + we have the decompressor available. This mode is + only available in compressed form + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); + pChoose = &RawEntry; + } + else + { + Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); + } + } + else + { + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + Skip this step when using RAW modes. + */ + while (compression <= 3) { + pChoose = &Kiara_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + Debug("Using alternate setting %d.\n", pChoose->alternate); + + /* usb_control_msg won't take staticly allocated arrays as argument?? */ + memcpy(buf, pChoose->mode, 12); + if (snapshot) + buf[0] |= 0x80; + + /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ + ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + + pdev->cmd_len = 12; + memcpy(pdev->cmd_buf, buf, 12); + /* All set and go */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pdev->vbandlength > 0) + pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + + +/** + @pdev: device structure + @width: viewport width + @height: viewport height + @frame: framerate, in fps + @compression: preferred compression ratio + @snapshot: snapshot mode or streaming + */ +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) +{ + int ret, size; + + Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + size = pwc_decode_size(pdev, width, height); + if (size < 0) { + Debug("Could not find suitable size.\n"); + return -ERANGE; + } + Debug("decode_size = %d.\n", size); + + ret = -EINVAL; + switch(pdev->type) { + case 645: + case 646: + ret = set_video_mode_Nala(pdev, size, frames); + break; + + case 675: + case 680: + case 690: + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); + break; + + case 720: + case 730: + case 740: + case 750: + ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); + break; + } + if (ret < 0) { + if (ret == -ENOENT) + Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); + else { + Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); + } + return ret; + } + pdev->view.x = width; + pdev->view.y = height; + pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; + pwc_set_image_buffer_size(pdev); + Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + return 0; +} + + +void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0, filler = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + filler = 128; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + filler = 0; + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with gray or black */ + for (i = 0; i < MAX_IMAGES; i++) { + if (pdev->image_ptr[i] != NULL) + memset(pdev->image_ptr[i], filler, pdev->view.size); + } +} + + + +/* BRIGHTNESS */ + +int pwc_get_brightness(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 9; +} + +int pwc_set_brightness(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 9) & 0x7f; + return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); +} + +/* CONTRAST */ + +int pwc_get_contrast(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 10; +} + +int pwc_set_contrast(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3f; + return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); +} + +/* GAMMA */ + +int pwc_get_gamma(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 11; +} + +int pwc_set_gamma(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 11) & 0x1f; + return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); +} + + +/* SATURATION */ + +int pwc_get_saturation(struct pwc_device *pdev) +{ + char buf; + int ret; + + if (pdev->type < 675) + return -1; + ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (ret < 0) + return ret; + return 32768 + buf * 327; +} + +int pwc_set_saturation(struct pwc_device *pdev, int value) +{ + char buf; + + if (pdev->type < 675) + return -EINVAL; + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* saturation ranges from -100 to +100 */ + buf = (value - 32768) / 327; + return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); +} + +/* AGC */ + +static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +{ + char buf; + int ret; + + if (mode) + buf = 0x0; /* auto */ + else + buf = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3F; + ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + } + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf != 0) { /* fixed */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + if (buf > 0x3F) + buf = 0x3F; + *value = (buf << 10); + } + else { /* auto */ + ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + /* Gah... this value ranges from 0x00 ... 0x9F */ + if (buf > 0x9F) + buf = 0x9F; + *value = -(48 + buf * 409); + } + + return 0; +} + +static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +{ + char buf[2]; + int speed, ret; + + + if (mode) + buf[0] = 0x0; /* auto */ + else + buf[0] = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + switch(pdev->type) { + case 675: + case 680: + case 690: + /* speed ranges from 0x0 to 0x290 (656) */ + speed = (value / 100); + buf[1] = speed >> 8; + buf[0] = speed & 0xff; + break; + case 720: + case 730: + case 740: + case 750: + /* speed seems to range from 0x0 to 0xff */ + buf[1] = 0; + buf[0] = value >> 8; + break; + } + + ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); + } + return ret; +} + + +/* POWER */ + +int pwc_camera_power(struct pwc_device *pdev, int power) +{ + char buf; + + if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) + return 0; /* Not supported by Nala or Timon < release 6 */ + + if (power) + buf = 0x00; /* active */ + else + buf = 0xFF; /* power save */ + return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); +} + + + +/* private calls */ + +static inline int pwc_restore_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_save_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_restore_factory(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); +} + + /* ************************************************* */ + /* Patch by Alvarado: (not in the original version */ + + /* + * the camera recognizes modes from 0 to 4: + * + * 00: indoor (incandescant lighting) + * 01: outdoor (sunlight) + * 02: fluorescent lighting + * 03: manual + * 04: auto + */ +static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +{ + char buf; + int ret; + + if (mode < 0) + mode = 0; + + if (mode > 4) + mode = 4; + + buf = mode & 0x07; /* just the lowest three bits */ + + ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_awb(struct pwc_device *pdev) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return buf; +} + +static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +/* The following two functions are different, since they only read the + internal red/blue gains, which may be different from the manual + gains set or read above. + */ +static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + +static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +{ + unsigned char buf; + + /* useful range is 0x01..0x20 */ + buf = speed / 0x7f0; + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); +} + +static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf * 0x7f0; + return 0; +} + + +static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +{ + unsigned char buf; + + /* useful range is 0x01..0x3F */ + buf = (delay >> 10); + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); +} + +static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 10; + return 0; +} + + +int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) +{ + unsigned char buf[2]; + + if (pdev->type < 730) + return 0; + on_value /= 100; + off_value /= 100; + if (on_value < 0) + on_value = 0; + if (on_value > 0xff) + on_value = 0xff; + if (off_value < 0) + off_value = 0; + if (off_value > 0xff) + off_value = 0xff; + + buf[0] = on_value; + buf[1] = off_value; + + return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); +} + +int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +{ + unsigned char buf[2]; + int ret; + + if (pdev->type < 730) { + *on_value = -1; + *off_value = -1; + return 0; + } + + ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); + if (ret < 0) + return ret; + *on_value = buf[0] * 100; + *off_value = buf[1] * 100; + return 0; +} + +static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +{ + unsigned char buf; + int ret; + + if (contour < 0) + buf = 0xff; /* auto contour on */ + else + buf = 0x0; /* auto contour off */ + ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (contour < 0) + return 0; + if (contour > 0xffff) + contour = 0xffff; + + buf = (contour >> 10); /* contour preset is [0..3f] */ + ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf == 0) { + /* auto mode off, query current preset value */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + *contour = buf << 10; + } + else + *contour = -1; + return 0; +} + + +static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +{ + unsigned char buf; + + if (backlight) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); +} + +static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + if (ret < 0) + return ret; + *backlight = buf; + return 0; +} + + +static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +{ + unsigned char buf; + + if (flicker) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); +} + +static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *flicker = buf; + return 0; +} + + +static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +{ + unsigned char buf; + + if (noise < 0) + noise = 0; + if (noise > 3) + noise = 3; + buf = noise; + return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); +} + +static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + if (ret < 0) + return ret; + *noise = buf; + return 0; +} + +int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + unsigned char buf; + + buf = flags & 0x03; // only lower two bits are currently used + return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); +} + +static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + unsigned char buf[4]; + + /* set new relative angle; angles are expressed in degrees * 100, + but cam as .5 degree resolution, hence devide by 200. Also + the angle must be multiplied by 64 before it's send to + the cam (??) + */ + pan = 64 * pan / 100; + tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ + buf[0] = pan & 0xFF; + buf[1] = (pan >> 8) & 0xFF; + buf[2] = tilt & 0xFF; + buf[3] = (tilt >> 8) & 0xFF; + return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); +} + +static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +{ + int ret; + unsigned char buf[5]; + + ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + if (ret < 0) + return ret; + status->status = buf[0] & 0x7; // 3 bits are used for reporting + status->time_pan = (buf[1] << 8) + buf[2]; + status->time_tilt = (buf[3] << 8) + buf[4]; + return 0; +} + + +int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) +{ + unsigned char buf; + int ret = -1, request; + + if (pdev->type < 675) + request = SENSOR_TYPE_FORMATTER1; + else if (pdev->type < 730) + return -1; /* The Vesta series doesn't have this call */ + else + request = SENSOR_TYPE_FORMATTER2; + + ret = RecvControlMsg(GET_STATUS_CTL, request, 1); + if (ret < 0) + return ret; + if (pdev->type < 675) + *sensor = buf | 0x100; + else + *sensor = buf; + return 0; +} + + + /* End of Add-Ons */ + /* ************************************************* */ + +/* Linux 2.5.something and 2.6 pass direct pointers to arguments of + ioctl() calls. With 2.4, you have to do tedious copy_from_user() + and copy_to_user() calls. With these macros we circumvent this, + and let me maintain only one source file. The functionality is + exactly the same otherwise. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + +/* define local variable for arg */ +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type *ARG_name = arg; +/* copy arg to local variable */ +#define ARG_IN(ARG_name) /* nothing */ +/* argument itself (referenced) */ +#define ARGR(ARG_name) (*ARG_name) +/* argument address */ +#define ARGA(ARG_name) ARG_name +/* copy local variable to arg */ +#define ARG_OUT(ARG_name) /* nothing */ + +#else + +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type ARG_name; +#define ARG_IN(ARG_name)\ + if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } +#define ARGR(ARG_name) ARG_name +#define ARGA(ARG_name) &ARG_name +#define ARG_OUT(ARG_name)\ + if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } + +#endif + +int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch(cmd) { + case VIDIOCPWCRUSER: + { + if (pwc_restore_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSUSER: + { + if (pwc_save_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCFACTORY: + { + if (pwc_restore_factory(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSCQUAL: + { + ARG_DEF(int, qual) + + ARG_IN(qual) + if (ARGR(qual) < 0 || ARGR(qual) > 3) + ret = -EINVAL; + else + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); + if (ret >= 0) + pdev->vcompression = ARGR(qual); + break; + } + + case VIDIOCPWCGCQUAL: + { + ARG_DEF(int, qual) + + ARGR(qual) = pdev->vcompression; + ARG_OUT(qual) + break; + } + + case VIDIOCPWCPROBE: + { + ARG_DEF(struct pwc_probe, probe) + + strcpy(ARGR(probe).name, pdev->vdev->name); + ARGR(probe).type = pdev->type; + ARG_OUT(probe) + break; + } + + case VIDIOCPWCGSERIAL: + { + ARG_DEF(struct pwc_serial, serial) + + strcpy(ARGR(serial).serial, pdev->serial); + ARG_OUT(serial) + break; + } + + case VIDIOCPWCSAGC: + { + ARG_DEF(int, agc) + + ARG_IN(agc) + if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) + ret = -EINVAL; + break; + } + + case VIDIOCPWCGAGC: + { + ARG_DEF(int, agc) + + if (pwc_get_agc(pdev, ARGA(agc))) + ret = -EINVAL; + ARG_OUT(agc) + break; + } + + case VIDIOCPWCSSHUTTER: + { + ARG_DEF(int, shutter_speed) + + ARG_IN(shutter_speed) + ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); + break; + } + + case VIDIOCPWCSAWB: + { + ARG_DEF(struct pwc_whitebalance, wb) + + ARG_IN(wb) + ret = pwc_set_awb(pdev, ARGR(wb).mode); + if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, ARGR(wb).manual_red); + pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); + } + break; + } + + case VIDIOCPWCGAWB: + { + ARG_DEF(struct pwc_whitebalance, wb) + + memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); + ARGR(wb).mode = pwc_get_awb(pdev); + if (ARGR(wb).mode < 0) + ret = -EINVAL; + else { + if (ARGR(wb).mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); + if (ret < 0) + break; + ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); + if (ret < 0) + break; + } + if (ARGR(wb).mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); + if (ret < 0) + break; + ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); + if (ret < 0) + break; + } + } + ARG_OUT(wb) + break; + } + + case VIDIOCPWCSAWBSPEED: + { + ARG_DEF(struct pwc_wb_speed, wbs) + + if (ARGR(wbs).control_speed > 0) { + ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); + } + if (ARGR(wbs).control_delay > 0) { + ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); + } + break; + } + + case VIDIOCPWCGAWBSPEED: + { + ARG_DEF(struct pwc_wb_speed, wbs) + + ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); + if (ret < 0) + break; + ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); + if (ret < 0) + break; + ARG_OUT(wbs) + break; + } + + case VIDIOCPWCSLED: + { + ARG_DEF(struct pwc_leds, leds) + + ARG_IN(leds) + ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); + break; + } + + + case VIDIOCPWCGLED: + { + ARG_DEF(struct pwc_leds, leds) + + ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); + ARG_OUT(leds) + break; + } + + case VIDIOCPWCSCONTOUR: + { + ARG_DEF(int, contour) + + ARG_IN(contour) + ret = pwc_set_contour(pdev, ARGR(contour)); + break; + } + + case VIDIOCPWCGCONTOUR: + { + ARG_DEF(int, contour) + + ret = pwc_get_contour(pdev, ARGA(contour)); + ARG_OUT(contour) + break; + } + + case VIDIOCPWCSBACKLIGHT: + { + ARG_DEF(int, backlight) + + ARG_IN(backlight) + ret = pwc_set_backlight(pdev, ARGR(backlight)); + break; + } + + case VIDIOCPWCGBACKLIGHT: + { + ARG_DEF(int, backlight) + + ret = pwc_get_backlight(pdev, ARGA(backlight)); + ARG_OUT(backlight) + break; + } + + case VIDIOCPWCSFLICKER: + { + ARG_DEF(int, flicker) + + ARG_IN(flicker) + ret = pwc_set_flicker(pdev, ARGR(flicker)); + break; + } + + case VIDIOCPWCGFLICKER: + { + ARG_DEF(int, flicker) + + ret = pwc_get_flicker(pdev, ARGA(flicker)); + ARG_OUT(flicker) + break; + } + + case VIDIOCPWCSDYNNOISE: + { + ARG_DEF(int, dynnoise) + + ARG_IN(dynnoise) + ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); + break; + } + + case VIDIOCPWCGDYNNOISE: + { + ARG_DEF(int, dynnoise) + + ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); + ARG_OUT(dynnoise); + break; + } + + case VIDIOCPWCGREALSIZE: + { + ARG_DEF(struct pwc_imagesize, size) + + ARGR(size).width = pdev->image.x; + ARGR(size).height = pdev->image.y; + ARG_OUT(size) + break; + } + + case VIDIOCPWCMPTRESET: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(int, flags) + + ARG_IN(flags) + ret = pwc_mpt_reset(pdev, ARGR(flags)); + if (ret >= 0) + { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGRANGE: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_range, range) + + ARGR(range) = pdev->angle_range; + ARG_OUT(range) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSANGLE: + { + int new_pan, new_tilt; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_angles, angles) + + ARG_IN(angles) + /* The camera can only set relative angles, so + do some calculations when getting an absolute angle . + */ + if (ARGR(angles).absolute) + { + new_pan = ARGR(angles).pan; + new_tilt = ARGR(angles).tilt; + } + else + { + new_pan = pdev->pan_angle + ARGR(angles).pan; + new_tilt = pdev->tilt_angle + ARGR(angles).tilt; + } + /* check absolute ranges */ + if (new_pan < pdev->angle_range.pan_min || + new_pan > pdev->angle_range.pan_max || + new_tilt < pdev->angle_range.tilt_min || + new_tilt > pdev->angle_range.tilt_max) + { + ret = -ERANGE; + } + else + { + /* go to relative range, check again */ + new_pan -= pdev->pan_angle; + new_tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) + ret = -ERANGE; + } + if (ret == 0) /* no errors so far */ + { + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); + if (ret >= 0) + { + pdev->pan_angle += new_pan; + pdev->tilt_angle += new_tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGANGLE: + { + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_angles, angles) + + ARGR(angles).absolute = 1; + ARGR(angles).pan = pdev->pan_angle; + ARGR(angles).tilt = pdev->tilt_angle; + ARG_OUT(angles) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSTATUS: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_status, status) + + ret = pwc_mpt_get_status(pdev, ARGA(status)); + ARG_OUT(status) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCGVIDCMD: + { + ARG_DEF(struct pwc_video_command, cmd); + + ARGR(cmd).type = pdev->type; + ARGR(cmd).release = pdev->release; + ARGR(cmd).command_len = pdev->cmd_len; + memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(cmd).bandlength = pdev->vbandlength; + ARGR(cmd).frame_size = pdev->frame_size; + ARG_OUT(cmd) + break; + } + /* + case VIDIOCPWCGVIDTABLE: + { + ARG_DEF(struct pwc_table_init_buffer, table); + ARGR(table).len = pdev->cmd_len; + memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_OUT(table) + break; + } + */ + + default: + ret = -ENOIOCTLCMD; + break; + } + + if (ret > 0) + return 0; + return ret; +} + + + diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c new file mode 100644 index 000000000..d5c4f24f8 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-if.c @@ -0,0 +1,2211 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + This code forms the interface between the USB layers and the Philips + specific stuff. Some adanved stuff of the driver falls under an + NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and + is thus not distributed in source form. The binary pwcx.o module + contains the code that falls under the NDA. + + In case you're wondering: 'pwc' stands for "Philips WebCam", but + I really didn't want to type 'philips_web_cam' every time (I'm lazy as + any Linux kernel hacker, but I don't like uncomprehensible abbreviations + without explanation). + + Oh yes, convention: to disctinguish between all the various pointers to + device-structures, I use these names for the pointer variables: + udev: struct usb_device * + vdev: struct video_device * + pdev: struct pwc_devive * +*/ + +/* Contributors: + - Alvarado: adding whitebalance code + - Alistar Moire: QuickCam 3000 Pro device/product ID + - Tony Hoyle: Creative Labs Webcam 5 device/product ID + - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged + - Jk Fang: Sotec Afina Eye ID + - Xavier Roche: QuickCam Pro 4000 ID + - Jens Knudsen: QuickCam Zoom ID + - J. Debert: QuickCam for Notebooks ID +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec23.h" +#include "pwc-dec1.h" +#include "pwc-uncompress.h" + +/* Function prototypes and driver templates */ + +/* hotplug device table support */ +static struct usb_device_id pwc_device_table [] = { + { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ + { USB_DEVICE(0x0471, 0x0303) }, + { USB_DEVICE(0x0471, 0x0304) }, + { USB_DEVICE(0x0471, 0x0307) }, + { USB_DEVICE(0x0471, 0x0308) }, + { USB_DEVICE(0x0471, 0x030C) }, + { USB_DEVICE(0x0471, 0x0310) }, + { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ + { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ + { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ + { USB_DEVICE(0x0d81, 0x1900) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pwc_device_table); + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void usb_pwc_disconnect(struct usb_interface *intf); + +static struct usb_driver pwc_driver = { + .owner = THIS_MODULE, + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ +}; + +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 + +static int default_size = PSZ_QCIF; +static int default_fps = 10; +static int default_fbufs = 3; /* Default number of frame buffers */ +static int default_mbufs = 2; /* Default number of mmap() buffers */ + int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; +static int power_save = 0; +static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ + int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ +static struct { + int type; + char serial_number[30]; + int device_node; + struct pwc_device *pdev; +} device_hint[MAX_DEV_HINTS]; + +/***/ + +static int pwc_video_open(struct inode *inode, struct file *file); +static int pwc_video_close(struct inode *inode, struct file *file); +static ssize_t pwc_video_read(struct file *file, char *buf, + size_t count, loff_t *ppos); +static unsigned int pwc_video_poll(struct file *file, poll_table *wait); +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, unsigned long arg); +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations pwc_fops = { + .owner = THIS_MODULE, + .open = pwc_video_open, + .release = pwc_video_close, + .read = pwc_video_read, + .poll = pwc_video_poll, + .mmap = pwc_video_mmap, + .ioctl = pwc_video_ioctl, + .llseek = no_llseek, +}; +static struct video_device pwc_template = { + .owner = THIS_MODULE, + .name = "Philips Webcam", /* Filled in later */ + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PWC, + .release = video_device_release, + .fops = &pwc_fops, + .minor = -1, +}; + +/***************************************************************************/ + +/* Okay, this is some magic that I worked out and the reasoning behind it... + + The biggest problem with any USB device is of course: "what to do + when the user unplugs the device while it is in use by an application?" + We have several options: + 1) Curse them with the 7 plagues when they do (requires divine intervention) + 2) Tell them not to (won't work: they'll do it anyway) + 3) Oops the kernel (this will have a negative effect on a user's uptime) + 4) Do something sensible. + + Of course, we go for option 4. + + It happens that this device will be linked to two times, once from + usb_device and once from the video_device in their respective 'private' + pointers. This is done when the device is probed() and all initialization + succeeded. The pwc_device struct links back to both structures. + + When a device is unplugged while in use it will be removed from the + list of known USB devices; I also de-register it as a V4L device, but + unfortunately I can't free the memory since the struct is still in use + by the file descriptor. This free-ing is then deferend until the first + opportunity. Crude, but it works. + + A small 'advantage' is that if a user unplugs the cam and plugs it back + in, it should get assigned the same video device minor, but unfortunately + it's non-trivial to re-link the cam back to the video device... (that + would surely be magic! :)) +*/ + +/***************************************************************************/ +/* Private functions */ + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr; + + size=PAGE_ALIGN(size); + mem=vmalloc_32(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) + { + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + + + + +static int pwc_allocate_buffers(struct pwc_device *pdev) +{ + int i; + void *kbuf; + + Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + + if (pdev == NULL) + return -ENXIO; + +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("allocate_buffers(): magic failed.\n"); + return -ENXIO; + } +#endif + /* Allocate Isochronuous pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->sbuf[i].data == NULL) { + kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate iso buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + pdev->sbuf[i].data = kbuf; + memset(kbuf, 0, ISO_BUFFER_SIZE); + } + } + + /* Allocate frame buffer structure */ + if (pdev->fbuf == NULL) { + kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate frame buffer structure.\n"); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + pdev->fbuf = kbuf; + memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); + } + /* create frame buffers, and make circular ring */ + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data == NULL) { + kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ + if (kbuf == NULL) { + Err("Failed to allocate frame buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + pdev->fbuf[i].data = kbuf; + memset(kbuf, 128, PWC_FRAME_SIZE); + } + } + + /* Allocate decompressor table space */ + kbuf = NULL; + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + Trace(TRACE_MEMORY,"private_data(%d)\n",sizeof(struct pwc_dec23_private)); + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ + break; + case 645: + case 646: + /* TODO & FIXME */ + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + break; + } + if (kbuf == NULL) { + Err("Failed to allocate decompress table.\n"); + return -ENOMEM; + } + pdev->decompress_data = kbuf; + + /* Allocate image buffer; double buffer for mmap() */ + kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + if (kbuf == NULL) { + Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + pdev->image_data = kbuf; + for (i = 0; i < default_mbufs; i++) + pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; + for (; i < MAX_IMAGES; i++) + pdev->image_ptr[i] = NULL; + + kbuf = NULL; + + Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + return 0; +} + +static void pwc_free_buffers(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + + if (pdev == NULL) + return; +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("free_buffers(): magic failed.\n"); + return; + } +#endif + + /* Release Iso-pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) + if (pdev->sbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + kfree(pdev->sbuf[i].data); + pdev->sbuf[i].data = NULL; + } + + /* The same for frame buffers */ + if (pdev->fbuf != NULL) { + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + vfree(pdev->fbuf[i].data); + pdev->fbuf[i].data = NULL; + } + } + kfree(pdev->fbuf); + pdev->fbuf = NULL; + } + + /* Intermediate decompression buffer & tables */ + if (pdev->decompress_data != NULL) { + Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + kfree(pdev->decompress_data); + pdev->decompress_data = NULL; + } + pdev->decompressor = NULL; + + /* Release image buffers */ + if (pdev->image_data != NULL) { + Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); + rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + } + pdev->image_data = NULL; + + Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); +} + +/* The frame & image buffer mess. + + Yes, this is a mess. Well, it used to be simple, but alas... In this + module, 3 buffers schemes are used to get the data from the USB bus to + the user program. The first scheme involves the ISO buffers (called thus + since they transport ISO data from the USB controller), and not really + interesting. Suffices to say the data from this buffer is quickly + gathered in an interrupt handler (pwc_isoc_handler) and placed into the + frame buffer. + + The frame buffer is the second scheme, and is the central element here. + It collects the data from a single frame from the camera (hence, the + name). Frames are delimited by the USB camera with a short USB packet, + so that's easy to detect. The frame buffers form a list that is filled + by the camera+USB controller and drained by the user process through + either read() or mmap(). + + The image buffer is the third scheme, in which frames are decompressed + and converted into planar format. For mmap() there is more than + one image buffer available. + + The frame buffers provide the image buffering. In case the user process + is a bit slow, this introduces lag and some undesired side-effects. + The problem arises when the frame buffer is full. I used to drop the last + frame, which makes the data in the queue stale very quickly. But dropping + the frame at the head of the queue proved to be a litte bit more difficult. + I tried a circular linked scheme, but this introduced more problems than + it solved. + + Because filling and draining are completely asynchronous processes, this + requires some fiddling with pointers and mutexes. + + Eventually, I came up with a system with 2 lists: an 'empty' frame list + and a 'full' frame list: + * Initially, all frame buffers but one are on the 'empty' list; the one + remaining buffer is our initial fill frame. + * If a frame is needed for filling, we try to take it from the 'empty' + list, unless that list is empty, in which case we take the buffer at + the head of the 'full' list. + * When our fill buffer has been filled, it is appended to the 'full' + list. + * If a frame is needed by read() or mmap(), it is taken from the head of + the 'full' list, handled, and then appended to the 'empty' list. If no + buffer is present on the 'full' list, we wait. + The advantage is that the buffer that is currently being decompressed/ + converted, is on neither list, and thus not in our way (any other scheme + I tried had the problem of old data lingering in the queue). + + Whatever strategy you choose, it always remains a tradeoff: with more + frame buffers the chances of a missed frame are reduced. On the other + hand, on slower machines it introduces lag because the queue will + always be full. + */ + +/** + \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. + */ +static inline int pwc_next_fill_frame(struct pwc_device *pdev) +{ + int ret; + unsigned long flags; + + ret = 0; + spin_lock_irqsave(&pdev->ptrlock, flags); + if (pdev->fill_frame != NULL) { + /* append to 'full' list */ + if (pdev->full_frames == NULL) { + pdev->full_frames = pdev->fill_frame; + pdev->full_frames_tail = pdev->full_frames; + } + else { + pdev->full_frames_tail->next = pdev->fill_frame; + pdev->full_frames_tail = pdev->fill_frame; + } + } + if (pdev->empty_frames != NULL) { + /* We have empty frames available. That's easy */ + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + } + else { + /* Hmm. Take it from the full list */ +#if PWC_DEBUG + /* sanity check */ + if (pdev->full_frames == NULL) { + Err("Neither empty or full frames available!\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return -EINVAL; + } +#endif + pdev->fill_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + ret = 1; + } + pdev->fill_frame->next = NULL; +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); + pdev->fill_frame->sequence = pdev->sequence++; +#endif + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + + +/** + \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. + + If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. + */ +static void pwc_reset_buffers(struct pwc_device *pdev) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + pdev->full_frames = NULL; + pdev->full_frames_tail = NULL; + for (i = 0; i < default_fbufs; i++) { + pdev->fbuf[i].filled = 0; + if (i > 0) + pdev->fbuf[i].next = &pdev->fbuf[i - 1]; + else + pdev->fbuf->next = NULL; + } + pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; + pdev->empty_frames_tail = pdev->fbuf; + pdev->read_frame = NULL; + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + + pdev->image_read_pos = 0; + pdev->fill_image = 0; + spin_unlock_irqrestore(&pdev->ptrlock, flags); +} + + +/** + \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. + */ +static int pwc_handle_frame(struct pwc_device *pdev) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + /* First grab our read_frame; this is removed from all lists, so + we can release the lock after this without problems */ + if (pdev->read_frame != NULL) { + /* This can't theoretically happen */ + Err("Huh? Read frame still in use?\n"); + } + else { + if (pdev->full_frames == NULL) { + Err("Woops. No frames ready.\n"); + } + else { + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); +#endif + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; + } + else { + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; + } + pdev->read_frame = NULL; + } + } + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + +/** + \brief Advance pointers of image buffer (after each user request) +*/ +static inline void pwc_next_image(struct pwc_device *pdev) +{ + pdev->image_used[pdev->fill_image] = 0; + pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; +} + + +/* This gets called for the Isochronous pipe (video). This is done in + * interrupt time, so it has to be fast, not crash, and not stall. Neat. + */ +static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) +{ + struct pwc_device *pdev; + int i, fst, flen; + int awake; + struct pwc_frame_buf *fbuf; + unsigned char *fillptr = 0, *iso_buf = 0; + + awake = 0; + pdev = (struct pwc_device *)urb->context; + if (pdev == NULL) { + Err("isoc_handler() called with NULL device?!\n"); + return; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("isoc_handler() called with bad magic!\n"); + return; + } +#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + return; + } + if (urb->status != -EINPROGRESS && urb->status != 0) { + const char *errmsg; + + errmsg = "Unknown"; + switch(urb->status) { + case -ENOSR: errmsg = "Buffer error (overrun)"; break; + case -EPIPE: errmsg = "Stalled (device not responding)"; break; + case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; + case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; + } + Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + /* Give up after a number of contiguous errors on the USB bus. + Appearantly something is wrong so we simulate an unplug event. + */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + Info("Too many ISOC errors, bailing out.\n"); + pdev->error_status = EIO; + awake = 1; + wake_up_interruptible(&pdev->frameq); + } + goto handler_end; // ugly, but practical + } + + fbuf = pdev->fill_frame; + if (fbuf == NULL) { + Err("pwc_isoc_handler without valid fill frame.\n"); + awake = 1; + goto handler_end; + } + else { + fillptr = fbuf->data + fbuf->filled; + } + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; + + /* vsync: 0 = don't copy data + 1 = sync-hunt + 2 = synched + */ + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + fst = urb->iso_frame_desc[i].status; + flen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (fst == 0) { + if (flen > 0) { /* if valid data... */ + if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ + pdev->vsync = 2; + + /* ...copy data to frame buffer, if possible */ + if (flen + fbuf->filled > pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ + pdev->vframes_error++; + } + else { + memmove(fillptr, iso_buf, flen); + fillptr += flen; + } + } + fbuf->filled += flen; + } /* ..flen > 0 */ + + if (flen < pdev->vlast_packet_size) { + /* Shorter packet... We probably have the end of an image-frame; + wake up read() process and let select()/poll() do something. + Decompression is done in user time over there. + */ + if (pdev->vsync == 2) { + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { +#if PWC_DEBUG + Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); +#endif + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) + Info("Snapshot button pressed.\n"); + else + Info("Snapshot button released.\n"); + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + Info("Image is mirrored.\n"); + else + Info("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) { + pdev->vframes_dumped++; + if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { + if (pdev->vframes_dumped < 20) + Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); + if (pdev->vframes_dumped == 20) + Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); + } + } + fbuf = pdev->fill_frame; + } + } /* !drop_frames */ + pdev->vframe_count++; + } + fbuf->filled = 0; + fillptr = fbuf->data; + pdev->vsync = 1; + } /* .. flen < last_packet_size */ + pdev->vlast_packet_size = flen; + } /* ..status == 0 */ +#if PWC_DEBUG + /* This is normally not interesting to the user, unless you are really debugging something */ + else { + static int iso_error = 0; + iso_error++; + if (iso_error < 20) + Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + } +#endif + } + +handler_end: + if (awake) + wake_up_interruptible(&pdev->frameq); + + urb->dev = pdev->udev; + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i != 0) + Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); +} + + +static int pwc_isoc_init(struct pwc_device *pdev) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; + + if (pdev == NULL) + return -EFAULT; + if (pdev->iso_init) + return 0; + pdev->vsync = 0; + udev = pdev->udev; + + /* Get the current alternate interface, adjust packet size */ + if (!udev->actconfig) + return -EFAULT; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) + idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; +#else + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); +#endif + + if (!idesc) + return -EFAULT; + + /* Search video endpoint */ + pdev->vmax_packet_size = -1; + for (i = 0; i < idesc->desc.bNumEndpoints; i++) + if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { + pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize; + break; + } + + if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { + Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + return -ENFILE; /* Odd error, that should be noticable */ + } + + /* Set alternate interface */ + ret = 0; + Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + Err("Failed to allocate urb %d\n", i); + ret = -ENOMEM; + break; + } + pdev->sbuf[i].urb = urb; + Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + } + if (ret) { + /* De-allocate in reverse order */ + while (i >= 0) { + if (pdev->sbuf[i].urb != NULL) + usb_free_urb(pdev->sbuf[i].urb); + pdev->sbuf[i].urb = NULL; + i--; + } + return ret; + } + + /* init URB structure */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = pdev->sbuf[i].urb; + + urb->interval = 1; // devik + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = pdev->sbuf[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = pwc_isoc_handler; + urb->context = pdev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = pdev->vmax_packet_size; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); + if (ret) + Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + else + Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + } + + /* All is done... */ + pdev->iso_init = 1; + Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + return 0; +} + +static void pwc_isoc_cleanup(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + if (pdev == NULL) + return; + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = pdev->sbuf[i].urb; + if (urb != 0) { + if (pdev->iso_init) { + Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + usb_unlink_urb(urb); + } + Trace(TRACE_MEMORY, "Freeing URB\n"); + usb_free_urb(urb); + pdev->sbuf[i].urb = NULL; + } + } + + /* Stop camera, but only if we are sure the camera is still there (unplug + is signalled by EPIPE) + */ + if (pdev->error_status && pdev->error_status != EPIPE) { + Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + usb_set_interface(pdev->udev, 0, 0); + } + + pdev->iso_init = 0; + Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); +} + +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) +{ + int ret, start; + + /* Stop isoc stuff */ + pwc_isoc_cleanup(pdev); + /* Reset parameters */ + pwc_reset_buffers(pdev); + /* Try to set video mode... */ + start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); + if (ret) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + /* That failed... restore old mode (we know that worked) */ + start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (start) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + } + } + if (start == 0) + { + if (pwc_isoc_init(pdev) < 0) + { + Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + ret = -EAGAIN; /* let's try again, who knows if it works a second time */ + } + } + pdev->drop_frames++; /* try to avoid garbage during switch */ + return ret; /* Return original error code */ +} + + +/***************************************************************************/ +/* Video4Linux functions */ + +static int pwc_video_open(struct inode *inode, struct file *file) +{ + int i; + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + + Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev == NULL) + BUG(); + if (pdev->vopen) + return -EBUSY; + + down(&pdev->modlock); + if (!pdev->usb_init) { + Trace(TRACE_OPEN, "Doing first time initialization.\n"); + pdev->usb_init = 1; + + if (pwc_trace & TRACE_OPEN) + { + /* Query sensor type */ + const char *sensor_type = NULL; + int ret; + + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) + { + switch(i) { + case 0x00: sensor_type = "Hyundai CMOS sensor"; break; + case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; + case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; + case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; + case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; + case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; + case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; + case 0x40: sensor_type = "UPA 1021 sensor"; break; + case 0x100: sensor_type = "VGA sensor"; break; + case 0x101: sensor_type = "PAL MR sensor"; break; + default: sensor_type = "unknown type of sensor"; break; + } + } + if (sensor_type != NULL) + Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + } + } + + /* Turn on camera */ + if (power_save) { + i = pwc_camera_power(pdev, 1); + if (i < 0) + Info("Failed to restore power to the camera! (%d)\n", i); + } + /* Set LED on/off time */ + if (pwc_set_leds(pdev, led_on, led_off) < 0) + Info("Failed to set LED on/off time.\n"); + + pwc_construct(pdev); /* set min/max sizes correct */ + + /* So far, so good. Allocate memory. */ + i = pwc_allocate_buffers(pdev); + if (i < 0) { + Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + up(&pdev->modlock); + return i; + } + + /* Reset buffers & parameters */ + pwc_reset_buffers(pdev); + for (i = 0; i < default_mbufs; i++) + pdev->image_used[i] = 0; + pdev->vframe_count = 0; + pdev->vframes_dumped = 0; + pdev->vframes_error = 0; + pdev->visoc_errors = 0; + pdev->error_status = 0; +#if PWC_DEBUG + pdev->sequence = 0; +#endif + pwc_construct(pdev); /* set min/max sizes correct */ + + /* Set some defaults */ + pdev->vsnapshot = 0; + + /* Start iso pipe for video; first try the last used video size + (or the default one); if that fails try QCIF/10 or QSIF/10; + it that fails too, give up. + */ + i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); + if (i) { + Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); + if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + else + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + } + if (i) { + Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + up(&pdev->modlock); + return i; + } + + i = pwc_isoc_init(pdev); + if (i) { + Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + up(&pdev->modlock); + return i; + } + + pdev->vopen++; + file->private_data = vdev; + up(&pdev->modlock); + Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + return 0; +} + +/* Note that all cleanup is done in the reverse order as in _open */ +static int pwc_video_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int i; + + Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev->vopen == 0) + Info("video_close() called on closed device?\n"); + + /* Dump statistics, but only if a reasonable amount of frames were + processed (to prevent endless log-entries in case of snap-shot + programs) + */ + if (pdev->vframe_count > 20) + Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_exit(); /* Timon & Kiara */ + break; + case 645: + case 646: + pwc_dec1_exit(); + break; + } + + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); + + /* Turn off LEDS and power down camera, but only when not unplugged */ + if (pdev->error_status != EPIPE) { + /* Turn LEDs off */ + if (pwc_set_leds(pdev, 0, 0) < 0) + Info("Failed to set LED on/off time.\n"); + if (power_save) { + i = pwc_camera_power(pdev, 0); + if (i < 0) + Err("Failed to power down camera (%d)\n", i); + } + } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + return 0; +} + +/* + * FIXME: what about two parallel reads ???? + * ANSWER: Not supported. You can't open the device more than once, + despite what the V4L1 interface says. First, I don't see + the need, second there's no mechanism of alerting the + 2nd/3rd/... process of events like changing image size. + And I don't see the point of blocking that for the + 2nd/3rd/... process. + In multi-threaded environments reading parallel from any + device is tricky anyhow. + */ + +static ssize_t pwc_video_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int noblock = file->f_flags & O_NONBLOCK; + DECLARE_WAITQUEUE(wait, current); + int bytes_to_read; + + Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count); + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + if (pdev->error_status) + return -pdev->error_status; /* Something happened, report what. */ + + /* In case we're doing partial reads, we don't have to wait for a frame */ + if (pdev->image_read_pos == 0) { + /* Do wait queueing according to the (doc)book */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status ; + } + if (noblock) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -EWOULDBLOCK; + } + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* Decompress and release frame */ + if (pwc_handle_frame(pdev)) + return -EFAULT; + } + + Trace(TRACE_READ, "Copying data to user space.\n"); + if (pdev->vpalette == VIDEO_PALETTE_RAW) + bytes_to_read = pdev->frame_size; + else + bytes_to_read = pdev->view.size; + + /* copy bytes to user space; we allow for partial reads */ + if (count + pdev->image_read_pos > bytes_to_read) + count = bytes_to_read - pdev->image_read_pos; + if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + return -EFAULT; + pdev->image_read_pos += count; + if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ + pdev->image_read_pos = 0; + pwc_next_image(pdev); + } + return count; +} + +static unsigned int pwc_video_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + poll_wait(file, &pdev->frameq, wait); + if (pdev->error_status) + return POLLERR; + if (pdev->full_frames != NULL) /* we have frames waiting */ + return (POLLIN | POLLRDNORM); + + return 0; +} + +static int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = val; + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = val; + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = val; + else + p->whiteness = 0xffff; + val = pwc_get_saturation(pdev); + if (val >= 0) + p->colour = val; + else + p->colour = 0xffff; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, p->colour); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = default_mbufs * pdev->len_per_image; + vm->frames = default_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < default_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= default_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= default_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); +} + + +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + pdev = vdev->priv; + + vma->vm_flags |= VM_IO; + + pos = (unsigned long)pdev->image_data; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/***************************************************************************/ +/* USB functions */ + +/* This function gets called when a new device is plugged in or the usb core + * is loaded. + */ + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pwc_device *pdev = NULL; + int vendor_id, product_id, type_id; + int i, hint; + int features = 0; + int video_nr = -1; /* default: use next available device */ + char serial_number[30], *name; + + /* Check if we can handle this device */ + Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", + udev->descriptor.idVendor, udev->descriptor.idProduct, + intf->altsetting->desc.bInterfaceNumber); + + /* the interfaces are probed one by one. We are only interested in the + video interface (0) now. + Interface 1 is the Audio Control, and interface 2 Audio itself. + */ + if (intf->altsetting->desc.bInterfaceNumber > 0) + return -ENODEV; + + vendor_id = udev->descriptor.idVendor; + product_id = udev->descriptor.idProduct; + + if (vendor_id == 0x0471) { + switch (product_id) { + case 0x0302: + Info("Philips PCA645VC USB webcam detected.\n"); + name = "Philips 645 webcam"; + type_id = 645; + break; + case 0x0303: + Info("Philips PCA646VC USB webcam detected.\n"); + name = "Philips 646 webcam"; + type_id = 646; + break; + case 0x0304: + Info("Askey VC010 type 2 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 646; + break; + case 0x0307: + Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + name = "Philips 675 webcam"; + type_id = 675; + break; + case 0x0308: + Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + name = "Philips 680 webcam"; + type_id = 680; + break; + case 0x030C: + Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + name = "Philips 690 webcam"; + type_id = 690; + break; + case 0x0310: + Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + name = "Philips 730 webcam"; + type_id = 730; + break; + case 0x0311: + Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + name = "Philips 740 webcam"; + type_id = 740; + break; + case 0x0312: + Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + name = "Philips 750 webcam"; + type_id = 750; + break; + case 0x0313: + Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720K/40 webcam"; + type_id = 720; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x069A) { + switch(product_id) { + case 0x0001: + Info("Askey VC010 type 1 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 645; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x046d) { + switch(product_id) { + case 0x08b0: + Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + name = "Logitech QuickCam Pro 3000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b1: + Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + name = "Logitech QuickCam Notebook Pro"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b2: + Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + name = "Logitech QuickCam Pro 4000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b3: + Info("Logitech QuickCam Zoom USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08B4: + Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b5: + Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + features |= FEATURE_MOTOR_PANTILT; + break; + case 0x08b6: + case 0x08b7: + case 0x08b8: + Info("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x055d) { + /* I don't know the difference between the C10 and the C30; + I suppose the difference is the sensor, but both cameras + work equally well with a type_id of 675 + */ + switch(product_id) { + case 0x9000: + Info("Samsung MPC-C10 USB webcam detected.\n"); + name = "Samsung MPC-C10"; + type_id = 675; + break; + case 0x9001: + Info("Samsung MPC-C30 USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 675; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x041e) { + switch(product_id) { + case 0x400c: + Info("Creative Labs Webcam 5 detected.\n"); + name = "Creative Labs Webcam 5"; + type_id = 730; + break; + case 0x4011: + Info("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x04cc) { + switch(product_id) { + case 0x8116: + Info("Sotec Afina Eye USB webcam detected.\n"); + name = "Sotec Afina Eye"; + type_id = 730; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x06be) { + switch(product_id) { + case 0x8116: + /* This is essentially the same cam as the Sotec Afina Eye */ + Info("AME Co. Afina Eye USB webcam detected.\n"); + name = "AME Co. Afina Eye"; + type_id = 750; + break; + default: + return -ENODEV; + break; + } + + } + else if (vendor_id == 0x0d81) { + switch(product_id) { + case 0x1900: + Info("Visionite VCS-UC300 USB webcam detected.\n"); + name = "Visionite VCS-UC300"; + type_id = 740; /* CCD sensor */ + break; + case 0x1910: + Info("Visionite VCS-UM100 USB webcam detected.\n"); + name = "Visionite VCS-UM100"; + type_id = 730; /* CMOS sensor */ + break; + default: + return -ENODEV; + break; + } + } + else + return -ENODEV; /* Not any of the know types; but the list keeps growing. */ + + memset(serial_number, 0, 30); + usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); + Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + + if (udev->descriptor.bNumConfigurations > 1) + Info("Warning: more than 1 configuration available.\n"); + + /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ + pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL); + if (pdev == NULL) { + Err("Oops, could not allocate memory for pwc_device.\n"); + return -ENOMEM; + } + memset(pdev, 0, sizeof(struct pwc_device)); + pdev->type = type_id; + pdev->vsize = default_size; + pdev->vframes = default_fps; + strcpy(pdev->serial, serial_number); + pdev->features = features; + if (vendor_id == 0x046D && product_id == 0x08B5) + { + /* Logitech QuickCam Orbit + The ranges have been determined experimentally; they may differ from cam to cam. + Also, the exact ranges left-right and up-down are different for my cam + */ + pdev->angle_range.pan_min = -7000; + pdev->angle_range.pan_max = 7000; + pdev->angle_range.tilt_min = -3000; + pdev->angle_range.tilt_max = 2500; + } + + init_MUTEX(&pdev->modlock); + pdev->ptrlock = SPIN_LOCK_UNLOCKED; + + pdev->udev = udev; + init_waitqueue_head(&pdev->frameq); + pdev->vcompression = pwc_preferred_compression; + + /* Allocate video_device structure */ + pdev->vdev = video_device_alloc(); + if (pdev->vdev == 0) + { + Err("Err, cannot allocate video_device struture. Failing probe."); + kfree(pdev); + return -ENOMEM; + } + memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev->name, name); + pdev->vdev->owner = THIS_MODULE; + video_set_drvdata(pdev->vdev, pdev); + + pdev->release = udev->descriptor.bcdDevice; + Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); + + /* Now search device_hint[] table for a match, so we can hint a node number. */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) { + if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && + (device_hint[hint].pdev == NULL)) { + /* so far, so good... try serial number */ + if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { + /* match! */ + video_nr = device_hint[hint].device_node; + Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + break; + } + } + } + + pdev->vdev->release = video_device_release; + i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (i < 0) { + Err("Failed to register as video device (%d).\n", i); + video_device_release(pdev->vdev); /* Drip... drip... drip... */ + kfree(pdev); /* Oops, no memory leaks please */ + return -EIO; + } + else { + Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + } + + /* occupy slot */ + if (hint < MAX_DEV_HINTS) + device_hint[hint].pdev = pdev; + + Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + usb_set_intfdata (intf, pdev); + return 0; +} + +/* The user janked out the cable... */ +static void usb_pwc_disconnect(struct usb_interface *intf) +{ + struct pwc_device *pdev; + int hint; + + lock_kernel(); + pdev = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + if (pdev == NULL) { + Err("pwc_disconnect() Called without private pointer.\n"); + goto disconnect_out; + } + if (pdev->udev == NULL) { + Err("pwc_disconnect() already called for %p\n", pdev); + goto disconnect_out; + } + if (pdev->udev != interface_to_usbdev(intf)) { + Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); + goto disconnect_out; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + goto disconnect_out; + } +#endif + + /* We got unplugged; this is signalled by an EPIPE error code */ + if (pdev->vopen) { + Info("Disconnected while webcam is in use!\n"); + pdev->error_status = EPIPE; + } + + /* Alert waiting processes */ + wake_up_interruptible(&pdev->frameq); + /* Wait until device is closed */ + while (pdev->vopen) + schedule(); + /* Device is now closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + video_unregister_device(pdev->vdev); + + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + +disconnect_out: + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; + + unlock_kernel(); +} + + +/* *grunt* We have to do atoi ourselves :-( */ +static int pwc_atoi(const char *s) +{ + int k = 0; + + k = 0; + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + return k; +} + + +/* + * Initialization code & module stuff + */ + +static char *size = NULL; +static int fps = 0; +static int fbufs = 0; +static int mbufs = 0; +static int trace = -1; +static int compression = -1; +static int leds[2] = { -1, -1 }; +static char *dev_hint[MAX_DEV_HINTS] = { }; + +MODULE_PARM(size, "s"); +MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); +MODULE_PARM(fps, "i"); +MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); +MODULE_PARM(fbufs, "i"); +MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); +MODULE_PARM(mbufs, "i"); +MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); +MODULE_PARM(trace, "i"); +MODULE_PARM_DESC(trace, "For debugging purposes"); +MODULE_PARM(power_save, "i"); +MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); +MODULE_PARM(compression, "i"); +MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); +MODULE_PARM(leds, "2i"); +MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); +MODULE_PARM(dev_hint, "0-20s"); +MODULE_PARM_DESC(dev_hint, "Device node hints"); + +MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); +MODULE_AUTHOR("Luc Saillard "); +MODULE_LICENSE("GPL"); + +static int __init usb_pwc_init(void) +{ + int i, sz; + char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; + + Info("Philips webcam module version " PWC_VERSION " loaded.\n"); + Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + + if (fps) { + if (fps < 4 || fps > 30) { + Err("Framerate out of bounds (4-30).\n"); + return -EINVAL; + } + default_fps = fps; + Info("Default framerate set to %d.\n", default_fps); + } + + if (size) { + /* string; try matching with array */ + for (sz = 0; sz < PSZ_MAX; sz++) { + if (!strcmp(sizenames[sz], size)) { /* Found! */ + default_size = sz; + break; + } + } + if (sz == PSZ_MAX) { + Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + return -EINVAL; + } + Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + } + if (mbufs) { + if (mbufs < 1 || mbufs > MAX_IMAGES) { + Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + return -EINVAL; + } + default_mbufs = mbufs; + Info("Number of image buffers set to %d.\n", default_mbufs); + } + if (fbufs) { + if (fbufs < 2 || fbufs > MAX_FRAMES) { + Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + return -EINVAL; + } + default_fbufs = fbufs; + Info("Number of frame buffers set to %d.\n", default_fbufs); + } + if (trace >= 0) { + Info("Trace options: 0x%04x\n", trace); + pwc_trace = trace; + } + if (compression >= 0) { + if (compression > 3) { + Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + return -EINVAL; + } + pwc_preferred_compression = compression; + Info("Preferred compression set to %d.\n", pwc_preferred_compression); + } + if (power_save) + Info("Enabling power save on open/close.\n"); + if (leds[0] >= 0) + led_on = leds[0]; + if (leds[1] >= 0) + led_off = leds[1]; + + /* Big device node whoopla. Basicly, it allows you to assign a + device node (/dev/videoX) to a camera, based on its type + & serial number. The format is [type[.serialnumber]:]node. + + Any camera that isn't matched by these rules gets the next + available free device node. + */ + for (i = 0; i < MAX_DEV_HINTS; i++) { + char *s, *colon, *dot; + + /* This loop also initializes the array */ + device_hint[i].pdev = NULL; + s = dev_hint[i]; + if (s != NULL && *s != '\0') { + device_hint[i].type = -1; /* wildcard */ + strcpy(device_hint[i].serial_number, "*"); + + /* parse string: chop at ':' & '/' */ + colon = dot = s; + while (*colon != '\0' && *colon != ':') + colon++; + while (*dot != '\0' && *dot != '.') + dot++; + /* Few sanity checks */ + if (*dot != '\0' && dot > colon) { + Err("Malformed camera hint: the colon must be after the dot.\n"); + return -EINVAL; + } + + if (*colon == '\0') { + /* No colon */ + if (*dot != '\0') { + Err("Malformed camera hint: no colon + device node given.\n"); + return -EINVAL; + } + else { + /* No type or serial number specified, just a number. */ + device_hint[i].device_node = pwc_atoi(s); + } + } + else { + /* There's a colon, so we have at least a type and a device node */ + device_hint[i].type = pwc_atoi(s); + device_hint[i].device_node = pwc_atoi(colon + 1); + if (*dot != '\0') { + /* There's a serial number as well */ + int k; + + dot++; + k = 0; + while (*dot != ':' && k < 29) { + device_hint[i].serial_number[k++] = *dot; + dot++; + } + device_hint[i].serial_number[k] = '\0'; + } + } +#if PWC_DEBUG + Debug("device_hint[%d]:\n", i); + Debug(" type : %d\n", device_hint[i].type); + Debug(" serial# : %s\n", device_hint[i].serial_number); + Debug(" node : %d\n", device_hint[i].device_node); +#endif + } + else + device_hint[i].type = 0; /* not filled */ + } /* ..for MAX_DEV_HINTS */ + + Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + return usb_register(&pwc_driver); +} + +static void __exit usb_pwc_exit(void) +{ + Trace(TRACE_MODULE, "Deregistering driver.\n"); + usb_deregister(&pwc_driver); + Info("Philips webcam module removed.\n"); +} + +module_init(usb_pwc_init); +module_exit(usb_pwc_exit); + diff --git a/drivers/usb/media/pwc/pwc-ioctl.h b/drivers/usb/media/pwc/pwc-ioctl.h new file mode 100644 index 000000000..65805eaa9 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-ioctl.h @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transfering x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/drivers/usb/media/pwc/pwc-kiara.c b/drivers/usb/media/pwc/pwc-kiara.c new file mode 100644 index 000000000..5485800ef --- /dev/null +++ b/drivers/usb/media/pwc/pwc-kiara.c @@ -0,0 +1,891 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 730/740/750 (Kiara) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + + +#include "pwc-kiara.h" +#include "pwc-uncompress.h" + +const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 20 fps */ + { + {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, + {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, + {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, + {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, + {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, + }, + /* 30 fps */ + { + {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, + {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, + {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, + {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, + {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, + {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, + {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, + {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, + {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, + {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, + {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, + {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, + {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, + {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, + {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, + {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + + +/* + * Rom table for kiara chips + * + * 32 roms tables (one for each resolution ?) + * 2 tables per roms (one for each passes) (Y, and U&V) + * 128 bytes per passes + */ + +const unsigned int KiaraRomTable [8][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009292,0x00009493,0x000124db}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009292, + 0x00009492,0x00009493,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; + diff --git a/drivers/usb/media/pwc/pwc-timon.c b/drivers/usb/media/pwc/pwc-timon.c new file mode 100644 index 000000000..f950a4e5e --- /dev/null +++ b/drivers/usb/media/pwc/pwc-timon.c @@ -0,0 +1,1446 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#include "pwc-timon.h" + +const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + }, + /* 15 fps */ + { + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + }, + /* 25 fps */ + { + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + }, + /* 30 fps */ + { + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, + {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, + {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, + {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, + {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, + {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, + {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, + {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, + {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, + {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, + {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, + {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, + {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, + {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, + {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, + {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, + {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, + {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, + {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, + {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, + {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, + {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, + {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, + {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, + {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, + {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, + {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, + {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, + {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, + {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, + {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, + {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, + {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, + }, + /* 25 fps */ + { + {0, }, + {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, + {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, + {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, + {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, + {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, + {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, + {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, + {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, + {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + +/* + * 16 versions: + * 2 tables (one for Y, and one for U&V) + * 16 levels of details per tables + * 8 blocs + */ + +const unsigned int TimonRomTable [16][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000001,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000001,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000049,0x00000249,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009292, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x000124db,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 8 */ + { /* version 8, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 8, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 9 */ + { /* version 9, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 9, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 10 */ + { /* version 10, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 10, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x00009493,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 11 */ + { /* version 11, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 11, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 12 */ + { /* version 12, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 12, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 13 */ + { /* version 13, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 13, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 14 */ + { /* version 14, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 14, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 15 */ + { /* version 15, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 15, passes 1 */ + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000124db,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; diff --git a/drivers/usb/media/pwc/pwc-uncompress.c b/drivers/usb/media/pwc/pwc-uncompress.c new file mode 100644 index 000000000..c062e43b3 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-uncompress.c @@ -0,0 +1,147 @@ +/* Linux driver for Philips webcam + Decompression frontend. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "pwc.h" +#include "pwc-uncompress.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +int pwc_decompress(struct pwc_device *pdev) +{ + struct pwc_frame_buf *fbuf; + int n, line, col, stride; + void *yuv, *image; + u16 *src; + u16 *dsty, *dstu, *dstv; + + if (pdev == NULL) + return -EFAULT; +#if defined(__KERNEL__) && defined(PWC_MAGIC) + if (pdev->magic != PWC_MAGIC) { + Err("pwc_decompress(): magic failed.\n"); + return -EFAULT; + } +#endif + + fbuf = pdev->read_frame; + if (fbuf == NULL) + return -EFAULT; + image = pdev->image_ptr[pdev->fill_image]; + if (!image) + return -EFAULT; + + yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ + + /* Raw format; that's easy... */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + memcpy(image, yuv, pdev->frame_size); + return 0; + } + + if (pdev->vbandlength == 0) { + /* Uncompressed mode. We copy the data into the output buffer, + using the viewport size (which may be larger than the image + size). Unfortunately we have to do a bit of byte stuffing + to get the desired output format/size. + */ + /* + * We do some byte shuffling here to go from the + * native format to YUV420P. + */ + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; + if (line & 1) + *dstv++ = *src++; + else + *dstu++ = *src++; + } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + } + else { + /* Compressed; the decompressor routines will write the data + in planar format immediately. + */ + int flags; + + flags = PWCX_FLAG_PLANAR; + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) + { + printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); + flags |= PWCX_FLAG_BAYER; + return -ENXIO; /* No such device or address: missing decompressor */ + } + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_decompress(&pdev->image, &pdev->view, &pdev->offset, + yuv, image, + flags, + pdev->decompress_data, pdev->vbandlength); + break; + case 645: + case 646: + /* TODO & FIXME */ + return -ENXIO; /* No such device or address: missing decompressor */ + break; + } + } + return 0; +} + + diff --git a/drivers/usb/media/pwc/pwc.h b/drivers/usb/media/pwc/pwc.h new file mode 100644 index 000000000..53b516d29 --- /dev/null +++ b/drivers/usb/media/pwc/pwc.h @@ -0,0 +1,278 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_H +#define PWC_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc-uncompress.h" +#include "pwc-ioctl.h" + +/* Defines and structures for the Philips webcam */ +/* Used for checking memory corruption/pointer validation */ +#define PWC_MAGIC 0x89DC10ABUL +#undef PWC_MAGIC + +/* Turn some debugging options on/off */ +#define PWC_DEBUG 0 + +/* Trace certain actions in the driver */ +#define TRACE_MODULE 0x0001 +#define TRACE_PROBE 0x0002 +#define TRACE_OPEN 0x0004 +#define TRACE_READ 0x0008 +#define TRACE_MEMORY 0x0010 +#define TRACE_FLOW 0x0020 +#define TRACE_SIZE 0x0040 +#define TRACE_PWCX 0x0080 +#define TRACE_SEQUENCE 0x1000 + +#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) +#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) +#define Info(A...) printk(KERN_INFO PWC_NAME " " A) +#define Err(A...) printk(KERN_ERR PWC_NAME " " A) + + +/* Defines for ToUCam cameras */ +#define TOUCAM_HEADER_SIZE 8 +#define TOUCAM_TRAILER_SIZE 4 + +#define FEATURE_MOTOR_PANTILT 0x0001 + +/* Version block */ +#define PWC_MAJOR 9 +#define PWC_MINOR 0 +#define PWC_VERSION "9.0.2-unofficial" +#define PWC_NAME "pwc" + +/* Turn certain features on/off */ +#define PWC_INT_PIPE 0 + +/* Ignore errors in the first N frames, to allow for startup delays */ +#define FRAME_LOWMARK 5 + +/* Size and number of buffers for the ISO pipe. */ +#define MAX_ISO_BUFS 2 +#define ISO_FRAMES_PER_DESC 10 +#define ISO_MAX_FRAME_SIZE 960 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +/* Frame buffers: contains compressed or uncompressed video data. */ +#define MAX_FRAMES 5 +/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ +#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) + +/* Absolute maximum number of buffers available for mmap() */ +#define MAX_IMAGES 10 + +/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ +struct pwc_iso_buf +{ + void *data; + int length; + int read; + struct urb *urb; +}; + +/* intermediate buffers with raw data from the USB cam */ +struct pwc_frame_buf +{ + void *data; + volatile int filled; /* number of bytes filled */ + struct pwc_frame_buf *next; /* list */ +#if PWC_DEBUG + int sequence; /* Sequence number */ +#endif +}; + +struct pwc_device +{ + struct video_device *vdev; +#ifdef PWC_MAGIC + int magic; +#endif + /* Pointer to our usb_device */ + struct usb_device *udev; + + int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ + int release; /* release number */ + int features; /* feature bits */ + char serial[30]; /* serial number (string) */ + int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ + int usb_init; /* set when the cam has been initialized over USB */ + + /*** Video data ***/ + int vopen; /* flag */ + int vendpoint; /* video isoc endpoint */ + int vcinterface; /* video control interface */ + int valternate; /* alternate interface needed */ + int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ + int vpalette; /* palette: 420P, RAW or RGBBAYER */ + int vframe_count; /* received frames */ + int vframes_dumped; /* counter for dumped frames */ + int vframes_error; /* frames received in error */ + int vmax_packet_size; /* USB maxpacket size */ + int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ + int vcompression; /* desired compression factor */ + int vbandlength; /* compressed band length; 0 is uncompressed */ + char vsnapshot; /* snapshot mode */ + char vsync; /* used by isoc handler */ + char vmirror; /* for ToUCaM series */ + + int cmd_len; + unsigned char cmd_buf[13]; + + /* The image acquisition requires 3 to 4 steps: + 1. data is gathered in short packets from the USB controller + 2. data is synchronized and packed into a frame buffer + 3a. in case data is compressed, decompress it directly into image buffer + 3b. in case data is uncompressed, copy into image buffer with viewport + 4. data is transferred to the user process + + Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... + We have in effect a back-to-back-double-buffer system. + */ + /* 1: isoc */ + struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; + char iso_init; + + /* 2: frame */ + struct pwc_frame_buf *fbuf; /* all frames */ + struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ + struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ + struct pwc_frame_buf *fill_frame; /* frame currently being filled */ + struct pwc_frame_buf *read_frame; /* frame currently read by user process */ + int frame_header_size, frame_trailer_size; + int frame_size; + int frame_total_size; /* including header & trailer */ + int drop_frames; +#if PWC_DEBUG + int sequence; /* Debugging aid */ +#endif + + /* 3: decompression */ + struct pwc_decompressor *decompressor; /* function block with decompression routines */ + void *decompress_data; /* private data for decompression engine */ + + /* 4: image */ + /* We have an 'image' and a 'view', where 'image' is the fixed-size image + as delivered by the camera, and 'view' is the size requested by the + program. The camera image is centered in this viewport, laced with + a gray or black border. view_min <= image <= view <= view_max; + */ + int image_mask; /* bitmask of supported sizes */ + struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ + struct pwc_coord abs_max; /* maximum supported size with compression */ + struct pwc_coord image, view; /* image and viewport size */ + struct pwc_coord offset; /* offset within the viewport */ + + void *image_data; /* total buffer, which is subdivided into ... */ + void *image_ptr[MAX_IMAGES]; /* ...several images... */ + int fill_image; /* ...which are rotated. */ + int len_per_image; /* length per image */ + int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ + int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ + + struct semaphore modlock; /* to prevent races in video_open(), etc */ + spinlock_t ptrlock; /* for manipulating the buffer pointers */ + + /*** motorized pan/tilt feature */ + struct pwc_mpt_range angle_range; + int pan_angle; /* in degrees * 100 */ + int tilt_angle; /* absolute angle; 0,0 is home position */ + + /*** Misc. data ***/ + wait_queue_head_t frameq; /* When waiting for a frame to finish... */ +#if PWC_INT_PIPE + void *usb_int_handler; /* for the interrupt endpoint */ +#endif +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global variables */ +extern int pwc_trace; +extern int pwc_preferred_compression; + +/** functions in pwc-if.c */ +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); + +/** Functions in pwc-misc.c */ +/* sizes in pixels */ +extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; + +int pwc_decode_size(struct pwc_device *pdev, int width, int height); +void pwc_construct(struct pwc_device *pdev); + +/** Functions in pwc-ctrl.c */ +/* Request a certain video mode. Returns < 0 if not possible */ +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +/* Calculate the number of bytes per image (not frame) */ +extern void pwc_set_image_buffer_size(struct pwc_device *pdev); + +/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ +extern int pwc_get_brightness(struct pwc_device *pdev); +extern int pwc_set_brightness(struct pwc_device *pdev, int value); +extern int pwc_get_contrast(struct pwc_device *pdev); +extern int pwc_set_contrast(struct pwc_device *pdev, int value); +extern int pwc_get_gamma(struct pwc_device *pdev); +extern int pwc_set_gamma(struct pwc_device *pdev, int value); +extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_set_saturation(struct pwc_device *pdev, int value); +extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); +extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); + +/* Power down or up the camera; not supported by all models */ +extern int pwc_camera_power(struct pwc_device *pdev, int power); + +/* Private ioctl()s; see pwc-ioctl.h */ +extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); + + +/** pwc-uncompress.c */ +/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ +extern int pwc_decompress(struct pwc_device *pdev); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/asm-ia64/sn/shub_mmr.h b/include/asm-ia64/sn/shub_mmr.h new file mode 100644 index 000000000..430c50fd1 --- /dev/null +++ b/include/asm-ia64/sn/shub_mmr.h @@ -0,0 +1,199 @@ +/* + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#ifndef _ASM_IA64_SN_SHUB_MMR_H +#define _ASM_IA64_SN_SHUB_MMR_H + +/* ==================================================================== */ +/* Register "SH_IPI_INT" */ +/* SHub Inter-Processor Interrupt Registers */ +/* ==================================================================== */ +#define SH_IPI_INT 0x0000000110000380UL +#define SH_IPI_INT_MASK 0x8ff3ffffffefffffUL +#define SH_IPI_INT_INIT 0x0000000000000000UL + +/* SH_IPI_INT_TYPE */ +/* Description: Type of Interrupt: 0=INT, 2=PMI, 4=NMI, 5=INIT */ +#define SH_IPI_INT_TYPE_SHFT 0 +#define SH_IPI_INT_TYPE_MASK 0x0000000000000007UL + +/* SH_IPI_INT_AGT */ +/* Description: Agent, must be 0 for SHub */ +#define SH_IPI_INT_AGT_SHFT 3 +#define SH_IPI_INT_AGT_MASK 0x0000000000000008UL + +/* SH_IPI_INT_PID */ +/* Description: Processor ID, same setting as on targeted McKinley */ +#define SH_IPI_INT_PID_SHFT 4 +#define SH_IPI_INT_PID_MASK 0x00000000000ffff0UL + +/* SH_IPI_INT_BASE */ +/* Description: Optional interrupt vector area, 2MB aligned */ +#define SH_IPI_INT_BASE_SHFT 21 +#define SH_IPI_INT_BASE_MASK 0x0003ffffffe00000UL + +/* SH_IPI_INT_IDX */ +/* Description: Targeted McKinley interrupt vector */ +#define SH_IPI_INT_IDX_SHFT 52 +#define SH_IPI_INT_IDX_MASK 0x0ff0000000000000UL + +/* SH_IPI_INT_SEND */ +/* Description: Send Interrupt Message to PI, This generates a puls */ +#define SH_IPI_INT_SEND_SHFT 63 +#define SH_IPI_INT_SEND_MASK 0x8000000000000000UL + +/* ==================================================================== */ +/* Register "SH_EVENT_OCCURRED" */ +/* SHub Interrupt Event Occurred */ +/* ==================================================================== */ +#define SH_EVENT_OCCURRED 0x0000000110010000UL +#define SH_EVENT_OCCURRED_ALIAS 0x0000000110010008UL + +/* ==================================================================== */ +/* Register "SH_PI_CAM_CONTROL" */ +/* CRB CAM MMR Access Control */ +/* ==================================================================== */ +#ifndef __ASSEMBLY__ +#define SH_PI_CAM_CONTROL 0x0000000120050300UL +#else +#define SH_PI_CAM_CONTROL 0x0000000120050300 +#endif + +/* ==================================================================== */ +/* Register "SH_SHUB_ID" */ +/* SHub ID Number */ +/* ==================================================================== */ +#define SH_SHUB_ID 0x0000000110060580UL +#define SH_SHUB_ID_REVISION_SHFT 28 +#define SH_SHUB_ID_REVISION_MASK 0x00000000f0000000 + +/* ==================================================================== */ +/* Register "SH_PTC_0" */ +/* Puge Translation Cache Message Configuration Information */ +/* ==================================================================== */ +#define SH_PTC_0 0x00000001101a0000UL +#define SH_PTC_1 0x00000001101a0080UL + +/* ==================================================================== */ +/* Register "SH_RTC" */ +/* Real-time Clock */ +/* ==================================================================== */ +#define SH_RTC 0x00000001101c0000UL +#define SH_RTC_MASK 0x007fffffffffffffUL + +/* ==================================================================== */ +/* Register "SH_MEMORY_WRITE_STATUS_0|1" */ +/* Memory Write Status for CPU 0 & 1 */ +/* ==================================================================== */ +#define SH_MEMORY_WRITE_STATUS_0 0x0000000120070000UL +#define SH_MEMORY_WRITE_STATUS_1 0x0000000120070080UL + +/* ==================================================================== */ +/* Register "SH_PIO_WRITE_STATUS_0|1" */ +/* PIO Write Status for CPU 0 & 1 */ +/* ==================================================================== */ +#ifndef __ASSEMBLY__ +#define SH_PIO_WRITE_STATUS_0 0x0000000120070200UL +#define SH_PIO_WRITE_STATUS_1 0x0000000120070280UL + +/* SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK */ +/* Description: Deadlock response detected */ +#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1 +#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_MASK 0x0000000000000002 + +/* SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT */ +/* Description: Count of currently pending PIO writes */ +#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56 +#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK 0x3f00000000000000UL +#else +#define SH_PIO_WRITE_STATUS_0 0x0000000120070200 +#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56 +#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1 +#endif + +/* ==================================================================== */ +/* Register "SH_PIO_WRITE_STATUS_0_ALIAS" */ +/* ==================================================================== */ +#ifndef __ASSEMBLY__ +#define SH_PIO_WRITE_STATUS_0_ALIAS 0x0000000120070208UL +#else +#define SH_PIO_WRITE_STATUS_0_ALIAS 0x0000000120070208 +#endif + +/* ==================================================================== */ +/* Register "SH_EVENT_OCCURRED" */ +/* SHub Interrupt Event Occurred */ +/* ==================================================================== */ +/* SH_EVENT_OCCURRED_UART_INT */ +/* Description: Pending Junk Bus UART Interrupt */ +#define SH_EVENT_OCCURRED_UART_INT_SHFT 20 +#define SH_EVENT_OCCURRED_UART_INT_MASK 0x0000000000100000 + +/* SH_EVENT_OCCURRED_IPI_INT */ +/* Description: Pending IPI Interrupt */ +#define SH_EVENT_OCCURRED_IPI_INT_SHFT 28 +#define SH_EVENT_OCCURRED_IPI_INT_MASK 0x0000000010000000 + +/* SH_EVENT_OCCURRED_II_INT0 */ +/* Description: Pending II 0 Interrupt */ +#define SH_EVENT_OCCURRED_II_INT0_SHFT 29 +#define SH_EVENT_OCCURRED_II_INT0_MASK 0x0000000020000000 + +/* SH_EVENT_OCCURRED_II_INT1 */ +/* Description: Pending II 1 Interrupt */ +#define SH_EVENT_OCCURRED_II_INT1_SHFT 30 +#define SH_EVENT_OCCURRED_II_INT1_MASK 0x0000000040000000 + +/* ==================================================================== */ +/* Register "SH_PTC_0" */ +/* Puge Translation Cache Message Configuration Information */ +/* ==================================================================== */ +#define SH_PTC_0 0x00000001101a0000UL +#define SH_PTC_0_MASK 0x80000000fffffffd +#define SH_PTC_0_INIT 0x0000000000000000 + +/* SH_PTC_0_A */ +/* Description: Type */ +#define SH_PTC_0_A_SHFT 0 +#define SH_PTC_0_A_MASK 0x0000000000000001 + +/* SH_PTC_0_PS */ +/* Description: Page Size */ +#define SH_PTC_0_PS_SHFT 2 +#define SH_PTC_0_PS_MASK 0x00000000000000fc + +/* SH_PTC_0_RID */ +/* Description: Region ID */ +#define SH_PTC_0_RID_SHFT 8 +#define SH_PTC_0_RID_MASK 0x00000000ffffff00 + +/* SH_PTC_0_START */ +/* Description: Start */ +#define SH_PTC_0_START_SHFT 63 +#define SH_PTC_0_START_MASK 0x8000000000000000 + +/* ==================================================================== */ +/* Register "SH_PTC_1" */ +/* Puge Translation Cache Message Configuration Information */ +/* ==================================================================== */ +#define SH_PTC_1 0x00000001101a0080UL +#define SH_PTC_1_MASK 0x9ffffffffffff000 +#define SH_PTC_1_INIT 0x0000000000000000 + +/* SH_PTC_1_VPN */ +/* Description: Virtual page number */ +#define SH_PTC_1_VPN_SHFT 12 +#define SH_PTC_1_VPN_MASK 0x1ffffffffffff000 + +/* SH_PTC_1_START */ +/* Description: PTC_1 Start */ +#define SH_PTC_1_START_SHFT 63 +#define SH_PTC_1_START_MASK 0x8000000000000000 + +#endif /* _ASM_IA64_SN_SHUB_MMR_H */ -- 2.47.0