r3343 - in trunk/kernel/source/kernel-source-2.6.12-2.6.12: . debian/patches
Jurij Smakov
jurij-guest@costa.debian.org
Sat, 18 Jun 2005 16:17:45 +0000
Author: jurij-guest
Date: 2005-06-18 16:17:44 +0000 (Sat, 18 Jun 2005)
New Revision: 3343
Added:
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-calibrate-tau.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-fix-power3-ftbfs.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-g3-750cxe.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-pmac-cache-power34-fix.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-ppc64-ibmvscsi.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-serial.patch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/sparc64-hme-lockup.dpatch
trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/tty-locking-fixes9.patch
Modified:
trunk/kernel/source/kernel-source-2.6.12-2.6.12/TODO.patches
Log:
Add more patches, rediffed/tested against 2.6.12
and update TODO.patches accordingly.
Modified: trunk/kernel/source/kernel-source-2.6.12-2.6.12/TODO.patches
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/TODO.patches 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/TODO.patches 2005-06-18 16:17:44 UTC (rev 3343)
@@ -9,6 +9,22 @@
ia64-generic-nosmp.patch
NOT ACCEPTED.
One hunk (include/asm-ia64/smp.h) fails to apply.
+powerpc-g4-l2-flush-errata.patch
+ NOT ACCEPTED. Needs to be reviewed. Last hunk fails to apply
+ to include/asm-ppc/cputable.h, as patch introduces a #define
+ conflicting with the existing one.
+powerpc-ppc64-biarch-override.patch
+ NOT ACCEPTED. Needs to be reviewed, as fails to apply completely.
+qla2xxx-removed.patch
+ NOT ACCEPTED. Fails to apply, needs work.
+remove-references-to-removed-drivers.patch
+ NOT ACCEPTED. Fails to apply, needs work
+sparc32-hypersparc-srmmu.patch
+ Presumably not required, but as sparc32 is currently
+ broken, there is no way to tell for sure.
+x86-i486_emu.patch
+ NOT ACCEPTED.
+ Makx wrote: not a backport but defintely dropped
These are already processed:
----------------------------
@@ -91,18 +107,13 @@
drivers-media-video-mt352-update.patch
docbook-move-kernel-doc-comment-next-to-function.patch
arch-ppc64-hugepage-aio-panic.patch
- backports
-
-These are not processed yet:
-----------------------------
+ Backports
powerpc-calibrate-tau.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | REDIFFED | INCLUDED.
powerpc-fix-power3-ftbfs.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | INCLUDED.
powerpc-g3-750cxe.patch
- NOT ACCEPTED.
-powerpc-g4-l2-flush-errata.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | INCLUDED.
powerpc-mv643xx-enet.patch
ACCEPTED.
powerpc-mv643xx-eth-pegasos.patch
@@ -110,30 +121,21 @@
powerpc-pmac-agp-sleep.patch
ACCEPTED.
powerpc-pmac-cache-power34-fix.patch
- NOT ACCEPTED.
-powerpc-ppc64-biarch-override.patch
- NOT ACCEPTED (may need rediffing).
+ NOT ACCEPTED | INCLUDED.
powerpc-ppc64-ibmvscsi.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | REDIFFED | INCLUDED.
powerpc-prep-motorola-irq-fix.patch
ACCEPTED.
powerpc-serial.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | REDIFFED | INCLUDED.
powerpc-therm-adt746x-new-i2c-fix.patch
ACCEPTED.
-qla2xxx-removed.patch
- NOT ACCEPTED.
-remove-references-to-removed-drivers.patch
- NOT ACCEPTED (may need rediffing).
sparc-sunsab-serial-lockup.patch
SUPERSEDED by sunsab-uart-update-timeout.patch
-sparc32-hypersparc-srmmu.patch
- Presumably not required, but as sparc32 is currently
- broken, there is no way to tell for sure.
sparc64-compat-nanoseconds.patch
ACCEPTED.
sparc64-hme-lockup.patch
- NOT ACCEPTED.
+ NOT ACCEPTED | REDIFFED | INCLUDED.
sparc64-rtc-mostek.patch
ACCEPTED.
sparc64-sb1500-clock-2.6.patch
@@ -145,7 +147,4 @@
sunsab-uart-update-timeout.patch
ACCEPTED.
tty-locking-fixes9.patch
- NOT ACCEPTED.
-x86-i486_emu.patch
- NOT ACCEPTED.
- Makx wrote: not a backport but defintely dropped
+ NOT ACCEPTED | INCLUDED.
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-calibrate-tau.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-calibrate-tau.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-calibrate-tau.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,69 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Adds support TAU calibration on G3 processors
+## DP: Patch author: Nicolas DET <nd@bplan-gmbh.de>
+## DP: Upstream status: submitted
+
+diff -aurN a/arch/ppc/Kconfig b/arch/ppc/Kconfig
+--- a/arch/ppc/Kconfig 2005-06-06 11:22:29.000000000 -0400
++++ b/arch/ppc/Kconfig 2005-06-17 20:48:38.000000000 -0400
+@@ -198,6 +198,24 @@
+
+ If in doubt, say N here.
+
++config TAU_CALIBRATED
++ bool "The CPU sensor has been calibrated."
++ depends on TAU
++ help
++ Enable it you got the real temperature with an external sensor
++ and you know the offset between the one advertised by the CPU
++ and the real one
++
++config TAU_CALIBRATED_VALUE
++ int "Offset of the themal sensor"
++ depends on TAU_CALIBRATED
++ default "0"
++ help
++ This is the offset of the thermal sensor compare to the real value
++ For example, if you get 27°C in /proc/cpuinfo (uncalibrated) and
++ you know real one is 53°C, then you should set 26 as offset.
++ value = Real val - CPU val;
++
+ config MATH_EMULATION
+ bool "Math emulation"
+ depends on 4xx || 8xx || E500
+diff -aurN a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c
+--- a/arch/ppc/kernel/setup.c 2005-06-06 11:22:29.000000000 -0400
++++ b/arch/ppc/kernel/setup.c 2005-06-17 20:50:14.000000000 -0400
+@@ -201,17 +201,25 @@
+
+ #ifdef CONFIG_TAU
+ if (cur_cpu_spec[i]->cpu_features & CPU_FTR_TAU) {
++#ifdef CONFIG_TAU_CALIBRATED
++ int is_calibrated = 1;
++ int temp_offset = CONFIG_TAU_CALIBRATED_VALUE;
++#else
++ int is_calibrated = 0;
++ int temp_offset = 0;
++#endif
+ #ifdef CONFIG_TAU_AVERAGE
+ /* more straightforward, but potentially misleading */
+- seq_printf(m, "temperature \t: %u C (uncalibrated)\n",
+- cpu_temp(i));
++ seq_printf(m, "temperature \t: %u C %s- average\n",
++ cpu_temp(i) + temp_offset, is_calibrated ? "" : "(uncalibrated) " );
+ #else
+ /* show the actual temp sensor range */
+ u32 temp;
+ temp = cpu_temp_both(i);
+- seq_printf(m, "temperature \t: %u-%u C (uncalibrated)\n",
+- temp & 0xff, temp >> 16);
+-#endif
++ seq_printf(m, "temperature \t: %u-%u C %s\n",
++ (temp & 0xff) + temp_offset, (temp >> 16) + temp_offset, is_calibrated ? "" : "(uncalibrated)" );
++
++#endif /* CONFIG_TAU_AVERAGE */
+ }
+ #endif /* CONFIG_TAU */
+
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-fix-power3-ftbfs.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-fix-power3-ftbfs.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-fix-power3-ftbfs.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,27 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Works around a broken build system, namely the
+## DP: Description: simple bootloader on power 3/4.
+## DP: Misc: Pulled from the ubuntu tree.
+## DP: Patch author: fabbione@ubuntu.com
+## DP: Upstream status: FTBFS fix, guess it will be fixed upstream too
+
+. $(dirname $0)/DPATCH
+
+@DPATCH@
+diff -urNad linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c /usr/src/dpatchtemp/dpep.cQRwcC/linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c
+--- linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c 2004-12-24 22:33:51.000000000 +0100
++++ /usr/src/dpatchtemp/dpep.cQRwcC/linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c 2004-12-28 10:43:29.838010536 +0100
+@@ -152,9 +152,11 @@
+ hold_residual->VitalProductData.Reserved5 = 0xdeadbeef;
+ }
+
++#if defined(CONFIG_6xx)
+ /* Now go and clear out the BATs and ensure that our MSR is
+ * correct .*/
+ disable_6xx_mmu();
++#endif
+
+ /* Make r3 be a pointer to the residual data. */
+ return (unsigned long)hold_residual;
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-g3-750cxe.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-g3-750cxe.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-g3-750cxe.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,68 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Adds support for 750CXe rev 3.1, prv 0008 3311 (previously recognized as 745/755).
+## DP: Patch author: Nicolas DET <nd@bplan-gmbh.de>
+## DP: Upstream status: submitted
+
+. $(dirname $0)/DPATCH
+
+@DPATCH@
+--- linux-2.6.11.orig/arch/ppc/kernel/cputable.c 2005-03-02 08:38:09.000000000 +0100
++++ linux-2.6.11_nico/arch/ppc/kernel/cputable.c 2005-03-04 15:39:11.032975088 +0100
+@@ -198,20 +198,6 @@
+ .num_pmcs = 4,
+ .cpu_setup = __setup_cpu_750
+ },
+- { /* 745/755 */
+- .pvr_mask = 0xfffff000,
+- .pvr_value = 0x00083000,
+- .cpu_name = "745/755",
+- .cpu_features = CPU_FTR_COMMON |
+- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE |
+- CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU |
+- CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP,
+- .cpu_user_features = COMMON_PPC,
+- .icache_bsize = 32,
+- .dcache_bsize = 32,
+- .num_pmcs = 4,
+- .cpu_setup = __setup_cpu_750
+- },
+ { /* 750CX (80100 and 8010x?) */
+ .pvr_mask = 0xfffffff0,
+ .pvr_value = 0x00080100,
+@@ -254,6 +240,34 @@
+ .num_pmcs = 4,
+ .cpu_setup = __setup_cpu_750cx
+ },
++ { /* 750CXe (00082311 or 00083311) revision 3.1 / 3.1 pre_GA */
++ .pvr_mask = 0xffff0fff,
++ .pvr_value = 0x00080311,
++ .cpu_name = "750CXe rev 3.1",
++ .cpu_features = CPU_FTR_COMMON |
++ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE |
++ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU |
++ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP,
++ .cpu_user_features = COMMON_PPC,
++ .icache_bsize = 32,
++ .dcache_bsize = 32,
++ .num_pmcs = 4,
++ .cpu_setup = __setup_cpu_750cx
++ },
++ { /* 745/755 */
++ .pvr_mask = 0xfffff000,
++ .pvr_value = 0x00083000,
++ .cpu_name = "745/755",
++ .cpu_features = CPU_FTR_COMMON |
++ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE |
++ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU |
++ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP,
++ .cpu_user_features = COMMON_PPC,
++ .icache_bsize = 32,
++ .dcache_bsize = 32,
++ .num_pmcs = 4,
++ .cpu_setup = __setup_cpu_750
++ },
+ { /* 750FX rev 1.x */
+ .pvr_mask = 0xffffff00,
+ .pvr_value = 0x70000100,
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-pmac-cache-power34-fix.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-pmac-cache-power34-fix.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-pmac-cache-power34-fix.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,30 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: [PATCH] ppc32: fixes FTBFS on power3/4.
+## DP: This patch doesn't build pmac_cache.S on power3/power4, since it is
+## DP: broken there and not needed.
+## DP: Patch author: Sven Luther <luther@debian.org>
+## DP: Upstream status: FTBFS, submitted to linuxppc-dev and benh.
+
+. $(dirname $0)/DPATCH
+
+@DPATCH@
+--- kernel-source-2.6.11/arch/ppc/platforms/Makefile.orig 2005-03-27 11:38:25.000000000 +0200
++++ kernel-source-2.6.11/arch/ppc/platforms/Makefile 2005-03-27 11:39:23.000000000 +0200
+@@ -9,9 +9,15 @@
+ ifeq ($(CONFIG_APUS),y)
+ obj-$(CONFIG_PCI) += apus_pci.o
+ endif
++ifeq ($(CONFIG_6xx),y)
+ obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \
+ pmac_feature.o pmac_pci.o pmac_sleep.o \
+ pmac_low_i2c.o pmac_cache.o
++else
++obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \
++ pmac_feature.o pmac_pci.o pmac_sleep.o \
++ pmac_low_i2c.o
++endif
+ obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o \
+ chrp_pegasos_eth.o
+ obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_setup.o
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-ppc64-ibmvscsi.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-ppc64-ibmvscsi.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-ppc64-ibmvscsi.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,2868 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Enables IBM eServer i/pSeries Virtual SCSI Target Driver
+## DP: Description: Needed for i/pSeries with logical partitions (LPAR).
+## DP: Patch author: Dave Boutcher (boutcher@us.ibm.com)
+## DP: Upstream status: unknown, sent to me by Cajus Pollmeier.
+
+diff -aurN a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
+--- a/drivers/scsi/Kconfig 2005-06-17 15:48:29.000000000 -0400
++++ b/drivers/scsi/Kconfig 2005-06-18 12:02:58.000000000 -0400
+@@ -813,6 +813,14 @@
+ To compile this driver as a module, choose M here: the
+ module will be called ibmvscsic.
+
++config SCSI_IBMVSCSIS
++ tristate "IBM Virtual SCSI Server support"
++ depends on PPC_PSERIES
++ help
++ This is the IBM Virtual SCSI Server
++ To compile this driver as a module, choose M here: the
++ module will be called ibmvscsis.
++
+ config SCSI_INITIO
+ tristate "Initio 9100U(W) support"
+ depends on PCI && SCSI
+diff -aurN a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
+--- a/drivers/scsi/ibmvscsi/Makefile 2005-06-17 15:48:29.000000000 -0400
++++ b/drivers/scsi/ibmvscsi/Makefile 2005-06-18 12:02:58.000000000 -0400
+@@ -3,3 +3,5 @@
+ ibmvscsic-y += ibmvscsi.o
+ ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o
+ ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o
++
++obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsis.o
+diff -aurN a/drivers/scsi/ibmvscsi/ibmvscsis.c b/drivers/scsi/ibmvscsi/ibmvscsis.c
+--- a/drivers/scsi/ibmvscsi/ibmvscsis.c 1969-12-31 19:00:00.000000000 -0500
++++ b/drivers/scsi/ibmvscsi/ibmvscsis.c 2005-06-18 12:02:58.000000000 -0400
+@@ -0,0 +1,2818 @@
++/**************************************************************************/
++/* -*- -linux- -*- */
++/* IBM eServer i/pSeries Virtual SCSI Target Driver */
++/* Copyright (C) 2003 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. */
++/* */
++/* 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 module contains the eServer virtual SCSI target code. The driver */
++/* takes SRP requests from the virtual SCSI client (the linux version is */
++/* int ibmvscsi.c, but there can be other clients, like AIX or OF) and */
++/* passes them on to real devices in this system. */
++/* */
++/* The basic hierarchy (and somewhat the organization of this file) is */
++/* that SCSI CDBs are in SRPs are in CRQs. */
++/* */
++/**************************************************************************/
++/*
++ TODO:
++ - Support redirecting SRP SCSI requests to a real SCSI driver
++*/
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/pagemap.h>
++#include <linux/dma-mapping.h>
++#include <linux/sched.h>
++#include <linux/blkdev.h>
++#include <linux/fs.h>
++#include <linux/bio.h>
++
++#include <asm/hvcall.h>
++#include <asm/vio.h>
++#include <asm/iommu.h>
++
++#include "../scsi.h"
++#include "viosrp.h"
++
++#define IBMVSCSIS_VERSION "1.2"
++
++MODULE_DESCRIPTION("IBM Virtual SCSI Target");
++MODULE_AUTHOR("Dave Boutcher");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(IBMVSCSIS_VERSION);
++
++static int ibmvscsis_debug = 0;
++
++/* These are fixed and come from the device tree...we
++ * just store them here to save getting them every time.
++ */
++static char system_id[64] = "";
++static char partition_name[97] = "UNKNOWN";
++static unsigned int partition_number = -1;
++
++/*
++ * Quick macro to enable/disable interrupts
++ * TODO: move to vio.h to be common with ibmvscsi.c
++ */
++#define h_vio_signal(ua, mode) \
++ plpar_hcall_norets(H_VIO_SIGNAL, ua, mode)
++
++/*
++ * These are indexes into the following table, and have to match!!!
++ */
++#define SENSE_SUCCESS 0
++#define SENSE_ABORT 1
++#define SENSE_INVALID_ID 2
++#define SENSE_DEVICE_FAULT 3
++#define SENSE_DEVICE_BUSY 4
++#define SENSE_UNIT_OFFLINE 5
++#define SENSE_INVALID_CMD 6
++#define SENSE_INTERMEDIATE 7
++#define SENSE_WRITE_PROT 8
++#define SENSE_INVALID_FIELD 9
++
++#define TARGET_MAX_NAME_LEN 128
++
++static unsigned char ibmvscsis_sense_data[][3] = {
++/*
++ * Sense key lookup table
++ * Format: SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier
++ * Adapted from 3w-xxxx.h
++ */
++ {0x00, 0x00, 0x00}, /* Success */
++ {0x0b, 0x00, 0x00}, /* Aborted command */
++ {0x0b, 0x14, 0x00}, /* ID not found */
++ {0x04, 0x00, 0x00}, /* Device fault */
++ {0x0b, 0x00, 0x00}, /* Device busy */
++ {0x02, 0x04, 0x00}, /* Unit offline */
++ {0x05, 0x20, 0x00}, /* Invalid Command */
++ {0x10, 0x00, 0x00}, /* Intermediate */
++ {0x07, 0x27, 0x00}, /* Write Protected */
++ {0x05, 0x24, 0x00}, /* Invalid field */
++};
++
++/*
++ * SCSI defined structure for inquiry data
++ * TODO: Seral number is currently messed up if you do
++ * scsiinfo. I'm not sure why and I think it comes out of
++ * here
++ */
++struct inquiry_data {
++ u8 qual_type;
++ u8 rmb_reserve;
++ u8 version;
++ u8 aerc_naca_hisup_format;
++ u8 addl_len;
++ u8 sccs_reserved;
++ u8 bque_encserv_vs_multip_mchngr_reserved;
++ u8 reladr_reserved_linked_cmdqueue_vs;
++ char vendor[8];
++ char product[16];
++ char revision[4];
++ char vendor_specific[20];
++ char reserved1[2];
++ char version_descriptor[16];
++ char reserved2[22];
++ char unique[158];
++};
++
++extern int vio_num_address_cells;
++
++/*
++ * an RPA command/response transport queue. This is our structure
++ * that points to the actual queue. feel free to modify this structure
++ * as needed
++ */
++struct crq_queue {
++ struct viosrp_crq *msgs;
++ int size, cur;
++ dma_addr_t msg_token;
++ spinlock_t lock;
++};
++
++/*
++ * This structure tracks our fundamental unit of work. Whenever
++ * an SRP Information Unit (IU) arrives, we track all the good stuff
++ * here
++ */
++struct iu_entry {
++ union viosrp_iu *iu;
++ struct server_adapter *adapter;
++ struct list_head next;
++ dma_addr_t iu_token;
++ int aborted;
++ struct {
++ dma_addr_t remote_token;
++ char *data_buffer;
++ dma_addr_t data_token;
++ long data_len;
++ struct vdev *vd;
++ char in_use:1;
++ char diunder:1;
++ char diover:1;
++ char dounder:1;
++ char doover:1;
++ char write:1;
++ char linked:1;
++ int data_out_residual_count;
++ int data_in_residual_count;
++ int ioerr;
++ } req;
++};
++
++/*
++ * a pool of ius for use
++ */
++struct iu_pool {
++ spinlock_t lock;
++ struct list_head iu_entries;
++ struct iu_entry *list;
++ union viosrp_iu *iu_storage;
++ dma_addr_t iu_token;
++ u32 size;
++};
++
++/*
++ * Represents a single device that someone told us about
++ * that we treat as a LUN
++ */
++struct vdev {
++ struct list_head list;
++ char type; /* 'B' for block, 'S' for SCSI */
++ atomic_t refcount;
++ int disabled;
++ u64 lun;
++ struct kobject kobj;
++ struct {
++ char device_name[TARGET_MAX_NAME_LEN];
++ struct block_device *bdev;
++ long blksize;
++ long lastlba;
++ int ro;
++ } b;
++};
++
++/*
++ * Represents a bus. target #'s in SCSI are 6 bits long,
++ * so you can have 64 targets per bus
++ */
++#define TARGETS_PER_BUS (64)
++#define BUS_PER_ADAPTER (8)
++struct vbus {
++ struct vdev *vdev[TARGETS_PER_BUS];
++ atomic_t num_targets;
++ struct kobject kobj;
++ int bus_num;
++};
++
++/*
++ * Buffer cache
++ */
++struct dma_buffer {
++ dma_addr_t token;
++ char *addr;
++ size_t len;
++};
++#define DMA_BUFFER_CACHE_SIZE (16)
++#define DMA_BUFFER_INIT_COUNT (4)
++#define DMA_BUFFER_INIT_LEN (PAGE_SIZE*16)
++
++/* all driver data associated with a host adapter */
++struct server_adapter {
++ struct device *dev;
++ struct vio_dev *dma_dev;
++ struct crq_queue queue;
++ struct work_struct crq_task;
++ struct tasklet_struct endio_tasklet;
++ struct iu_pool pool;
++ spinlock_t lock;
++ struct bio *bio_done;
++ struct bio *bio_donetail;
++ struct list_head inflight;
++ struct vbus *vbus[8];
++ int nvdevs;
++ char name[32];
++ unsigned long liobn;
++ unsigned long riobn;
++
++ atomic_t num_buses;
++ struct kobject stats_kobj;
++
++ /* This ugly expression allocates a bit array of
++ * in-use flags large enough for the number of buffers
++ */
++ unsigned long dma_buffer_use[(DMA_BUFFER_CACHE_SIZE +
++ sizeof(unsigned long) - 1)
++ / sizeof(unsigned long)];
++ struct dma_buffer dma_buffer[DMA_BUFFER_CACHE_SIZE];
++
++ /* Statistics only */
++ atomic_t iu_count; /* number allocated */
++ atomic_t bio_count; /* number allocated */
++ atomic_t crq_processed;
++ atomic_t interrupts;
++ atomic_t read_processed;
++ atomic_t write_processed;
++ atomic_t buffers_allocated;
++ atomic_t errors;
++};
++
++/*
++ * Forward declarations
++ */
++static long send_rsp(struct iu_entry *iue, int status);
++
++/*
++ * The following are lifted from usb.h
++ */
++#define DEBUG 1
++#ifdef DEBUG
++#define dbg(format, arg...) if (ibmvscsis_debug) printk(KERN_WARNING __FILE__ ": " format , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) printk(KERN_ERR "ibmvscsis: " format , ## arg)
++#define info(format, arg...) printk(KERN_INFO "ibmvscsis: " format , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING "ibmvscsis: " format , ## arg)
++
++/* ==============================================================
++ * Utility Routines
++ * ==============================================================
++ */
++/*
++ * return an 8 byte lun given a bus, target, lun.
++ * Today this only supports single level luns. Should we add a level or a
++ * 64 bit LUN as input to support multi-level luns?
++ */
++u64 make_lun(unsigned int bus, unsigned int target, unsigned int lun)
++{
++ u16 result = (0x8000 |
++ ((target & 0x003f) << 8) |
++ ((bus & 0x0007) << 5) | (lun & 0x001f));
++ return ((u64) result) << 48;
++}
++
++/*
++ * Given an 8 byte LUN, return the first level bus/target/lun.
++ * Today this doesn't support multi-level LUNs
++ */
++#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007))
++#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f))
++#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f))
++
++static u8 getcontrolbyte(u8 * cdb)
++{
++ return cdb[COMMAND_SIZE(cdb[0]) - 1];
++}
++
++static u8 getlink(struct iu_entry *iue)
++{
++ return (getcontrolbyte(iue->iu->srp.cmd.cdb) & 0x01);
++}
++
++/*
++ * Given an SRP, figure out the data in length
++ */
++static int did_len(struct srp_cmd *cmd)
++{
++ struct memory_descriptor *md;
++ struct indirect_descriptor *id;
++ int offset = cmd->additional_cdb_len * 4;
++
++ switch (cmd->data_out_format) {
++ case SRP_NO_BUFFER:
++ offset += 0;
++ break;
++ case SRP_DIRECT_BUFFER:
++ offset += sizeof(struct memory_descriptor);
++ break;
++ case SRP_INDIRECT_BUFFER:
++ offset += sizeof(struct indirect_descriptor)
++ +
++ ((cmd->data_out_count -
++ 1) * sizeof(struct memory_descriptor));
++ break;
++ default:
++ err("client error. Invalid data_out_format %d\n",
++ cmd->data_out_format);
++ return 0;
++ }
++
++ switch (cmd->data_in_format) {
++ case SRP_NO_BUFFER:
++ return 0;
++ case SRP_DIRECT_BUFFER:
++ md = (struct memory_descriptor *)(cmd->additional_data +
++ offset);
++ return md->length;
++ case SRP_INDIRECT_BUFFER:
++ id = (struct indirect_descriptor *)(cmd->additional_data +
++ offset);
++ return id->total_length;
++ default:
++ err("client error. Invalid data_in_format %d\n",
++ cmd->data_in_format);
++ return 0;
++ }
++}
++
++/*
++ * We keep a pool of IUs, this routine builds the pool. The pool is
++ * per-adapter. The size of the pool is negotiated as part of the SRP
++ * login, where we negotiate the number of requests (IUs) the client
++ * can send us. This routine is not synchronized.
++ */
++static int initialize_iu_pool(struct server_adapter *adapter, int size)
++{
++ struct iu_pool *pool = &adapter->pool;
++ int i;
++
++ pool->size = size;
++ pool->lock = SPIN_LOCK_UNLOCKED;
++ INIT_LIST_HEAD(&pool->iu_entries);
++
++ pool->list = kmalloc(pool->size * sizeof(*pool->list), GFP_KERNEL);
++ if (!pool->list) {
++ err("Error: Cannot allocate memory for IU list\n");
++ return -ENOMEM;
++ }
++ memset(pool->list, 0x00, pool->size * sizeof(*pool->list));
++
++ pool->iu_storage =
++ dma_alloc_coherent(adapter->dev,
++ pool->size * sizeof(*pool->iu_storage),
++ &pool->iu_token, 0);
++ if (!pool->iu_storage) {
++ err("Error: Cannot allocate memory for IU pool\n");
++ kfree(pool->list);
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < pool->size; ++i) {
++ pool->list[i].iu = pool->iu_storage + i;
++ pool->list[i].iu_token =
++ pool->iu_token + sizeof(*pool->iu_storage) * i;
++ pool->list[i].adapter = adapter;
++ list_add_tail(&pool->list[i].next, &pool->iu_entries);
++ }
++
++ return 0;
++}
++
++/*
++ * Free the pool we allocated in initialize_iu_pool
++ */
++static void release_iu_pool(struct server_adapter *adapter)
++{
++ struct iu_pool *pool = &adapter->pool;
++ int i, in_use = 0;
++ for (i = 0; i < pool->size; ++i)
++ if (pool->list[i].req.in_use)
++ ++in_use;
++ if (in_use)
++ err("Releasing event pool with %d events still in use?\n",
++ in_use);
++ kfree(pool->list);
++ dma_free_coherent(adapter->dev, pool->size * sizeof(*pool->iu_storage),
++ pool->iu_storage, pool->iu_token);
++}
++
++/*
++ * Get an IU from the pool. Return NULL of the pool is empty. This
++ * routine is syncronized by a lock. The routine sets all the important
++ * fields to 0
++ */
++static struct iu_entry *get_iu(struct server_adapter *adapter)
++{
++ struct iu_entry *e;
++ unsigned long flags;
++
++ spin_lock_irqsave(&adapter->pool.lock, flags);
++ if (!list_empty(&adapter->pool.iu_entries)) {
++ e = list_entry(adapter->pool.iu_entries.next, struct iu_entry,
++ next);
++ list_del(adapter->pool.iu_entries.next);
++
++ if (e->req.in_use) {
++ err("Found in-use iue in free pool!");
++ }
++
++ memset(&e->req, 0x00, sizeof(e->req));
++
++ e->req.in_use = 1;
++ } else {
++ e = NULL;
++ }
++
++ spin_unlock_irqrestore(&adapter->pool.lock, flags);
++ atomic_inc(&adapter->iu_count);
++ return e;
++}
++
++/*
++ * Return an IU to the pool. This routine is synchronized
++ */
++static void free_iu(struct iu_entry *iue)
++{
++ unsigned long flags;
++ if (iue->req.vd) {
++ atomic_dec(&iue->req.vd->refcount);
++ }
++
++ spin_lock_irqsave(&iue->adapter->pool.lock, flags);
++ if (iue->req.in_use == 0) {
++ warn("Internal error, freeing iue twice!\n");
++ } else {
++ iue->req.in_use = 0;
++ list_add_tail(&iue->next, &iue->adapter->pool.iu_entries);
++ }
++ spin_unlock_irqrestore(&iue->adapter->pool.lock, flags);
++ atomic_dec(&iue->adapter->iu_count);
++}
++
++/*
++ * Get a CRQ from the inter-partition queue.
++ */
++static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue)
++{
++ struct viosrp_crq *crq;
++ unsigned long flags;
++
++ spin_lock_irqsave(&queue->lock, flags);
++ crq = &queue->msgs[queue->cur];
++ if (crq->valid & 0x80) {
++ if (++queue->cur == queue->size)
++ queue->cur = 0;
++ } else
++ crq = NULL;
++ spin_unlock_irqrestore(&queue->lock, flags);
++
++ return crq;
++}
++
++/*
++ * Make the RDMA hypervisor call. There should be a better way to do this
++ * than inline assembler.
++ * TODO: Fix the inline assembler
++ */
++static long h_copy_rdma(long length,
++ unsigned long sliobn, unsigned long slioba,
++ unsigned long dliobn, unsigned long dlioba)
++{
++ long lpar_rc = 0;
++ __asm__ __volatile__(" li 3,0x110 \n\t"
++ " mr 4, %1 \n\t"
++ " mr 5, %2 \n\t"
++ " mr 6, %3 \n\t"
++ " mr 7, %4 \n\t"
++ " mr 8, %5 \n\t"
++ " .long 0x44000022 \n\t"
++ " mr %0, 3 \n\t":"=&r"(lpar_rc)
++ :"r"(length), "r"(sliobn), "r"(slioba),
++ "r"(dliobn), "r"(dlioba)
++ :"r0", "r3", "r4", "r5", "r6", "r7", "r8", "cr0",
++ "cr1", "ctr", "xer", "memory");
++ return lpar_rc;
++}
++
++/*
++ * Send an SRP to another partition using the CRQ.
++ */
++static int send_srp(struct iu_entry *iue, u64 length)
++{
++ long rc, rc1;
++ union {
++ struct viosrp_crq cooked;
++ u64 raw[2];
++ } crq;
++
++ /* First copy the SRP */
++ rc = h_copy_rdma(length,
++ iue->adapter->liobn,
++ iue->iu_token,
++ iue->adapter->riobn, iue->req.remote_token);
++
++ if (rc) {
++ err("Error %ld transferring data to client\n", rc);
++ }
++
++ crq.cooked.valid = 0x80;
++ crq.cooked.format = VIOSRP_SRP_FORMAT;
++ crq.cooked.reserved = 0x00;
++ crq.cooked.timeout = 0x00;
++ crq.cooked.IU_length = length;
++ crq.cooked.IU_data_ptr = iue->iu->srp.generic.tag;
++
++ if (rc == 0) {
++ crq.cooked.status = 0x99; /* TODO: is this right? */
++ } else {
++ crq.cooked.status = 0x00;
++ }
++
++ rc1 =
++ plpar_hcall_norets(H_SEND_CRQ, iue->adapter->dma_dev->unit_address,
++ crq.raw[0], crq.raw[1]);
++
++ if (rc1) {
++ err("Error %ld sending response to client\n", rc1);
++ return rc1;
++ }
++
++ return rc;
++}
++
++/*
++ * Send data to a single SRP memory descriptor
++ * Returns amount of data sent, or negative value on error
++ */
++static long send_md_data(dma_addr_t stoken, int len,
++ struct memory_descriptor *md,
++ struct server_adapter *adapter)
++{
++ int tosend;
++ long rc;
++
++ if (len < md->length)
++ tosend = len;
++ else
++ tosend = md->length;
++
++ rc = h_copy_rdma(tosend,
++ adapter->liobn,
++ stoken, adapter->riobn, md->virtual_address);
++
++ if (rc != H_Success) {
++ err(" Error %ld transferring data to client\n", rc);
++ return -1;
++ }
++
++ return tosend;
++}
++
++/*
++ * Send data to the SRP data_in buffers
++ * Returns amount of data sent, or negative value on error
++ */
++static long send_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue)
++{
++ struct srp_cmd *cmd = &iue->iu->srp.cmd;
++ struct memory_descriptor *md;
++ struct indirect_descriptor *id;
++ int offset = 0;
++ int total_length = 0;
++ int i;
++ int thislen;
++ int bytes;
++ int sentlen = 0;
++
++ offset = cmd->additional_cdb_len * 4;
++
++ switch (cmd->data_out_format) {
++ case SRP_NO_BUFFER:
++ offset += 0;
++ break;
++ case SRP_DIRECT_BUFFER:
++ offset += sizeof(struct memory_descriptor);
++ break;
++ case SRP_INDIRECT_BUFFER:
++ offset += sizeof(struct indirect_descriptor)
++ +
++ ((cmd->data_out_count -
++ 1) * sizeof(struct memory_descriptor));
++ break;
++ default:
++ err("client error: Invalid data_out_format %d\n",
++ cmd->data_out_format);
++ return 0;
++ }
++
++ switch (cmd->data_in_format) {
++ case SRP_NO_BUFFER:
++ return 0;
++ case SRP_DIRECT_BUFFER:
++ md = (struct memory_descriptor *)(cmd->additional_data +
++ offset);
++ sentlen = send_md_data(stoken, len, md, iue->adapter);
++ len -= sentlen;
++ if (len) {
++ iue->req.diover = 1;
++ iue->req.data_in_residual_count = len;
++ }
++ return sentlen;
++ }
++
++ if (cmd->data_in_format != SRP_INDIRECT_BUFFER) {
++ err("client error Invalid data_in_format %d\n",
++ cmd->data_in_format);
++ return 0;
++ }
++
++ id = (struct indirect_descriptor *)(cmd->additional_data + offset);
++
++ total_length = id->total_length;
++
++ /* Work through the partial memory descriptor list */
++ for (i = 0; ((i < cmd->data_in_count) && (len)); i++) {
++ if (len > id->list[i].length) {
++ thislen = id->list[i].length;
++ } else {
++ thislen = len;
++ }
++
++ bytes =
++ send_md_data(stoken + sentlen, thislen, id->list + i,
++ iue->adapter);
++ if (bytes < 0)
++ return bytes;
++
++ if (bytes != thislen) {
++ warn("Error: Tried to send %d, sent %d\n", thislen,
++ bytes);
++ }
++
++ sentlen += bytes;
++ total_length -= bytes;
++ len -= bytes;
++ }
++
++ if (len) {
++ iue->req.diover = 1;
++ iue->req.data_in_residual_count = len;
++ }
++
++ return sentlen;
++}
++
++/*
++ * Get data from the other partition from a single SRP memory descriptor
++ * Returns amount of data sent, or negative value on error
++ */
++static long get_md_data(dma_addr_t ttoken, int len,
++ struct memory_descriptor *md,
++ struct server_adapter *adapter)
++{
++ int toget;
++ long rc;
++
++ if (len < md->length)
++ toget = len;
++ else
++ toget = md->length;
++
++ rc = h_copy_rdma(toget,
++ adapter->riobn,
++ md->virtual_address, adapter->liobn, ttoken);
++
++ if (rc != H_Success) {
++ err("Error %ld transferring data to client\n", rc);
++ return -1;
++ }
++
++ return toget;
++}
++
++/*
++ * Get data from an SRP data in area.
++ * Returns amount of data sent, or negative value on error
++ */
++static long get_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue)
++{
++ struct srp_cmd *cmd = &iue->iu->srp.cmd;
++ struct memory_descriptor *md;
++ struct indirect_descriptor *id;
++ int offset = 0;
++ int total_length = 0;
++ int i;
++ int thislen;
++ int bytes;
++ int sentlen = 0;
++
++ offset = cmd->additional_cdb_len * 4;
++
++ switch (cmd->data_out_format) {
++ case SRP_NO_BUFFER:
++ return 0;
++ break;
++ case SRP_DIRECT_BUFFER:
++ md = (struct memory_descriptor *)(cmd->additional_data +
++ offset);
++ return get_md_data(stoken, len, md, iue->adapter);
++ break;
++ }
++
++ if (cmd->data_out_format != SRP_INDIRECT_BUFFER) {
++ err("client error: Invalid data_out_format %d\n",
++ cmd->data_out_format);
++ return 0;
++ }
++
++ id = (struct indirect_descriptor *)(cmd->additional_data + offset);
++
++ total_length = id->total_length;
++
++ /* Work through the partial memory descriptor list */
++ for (i = 0; ((i < cmd->data_out_count) && (len)); i++) {
++ if (len > id->list[i].length) {
++ thislen = id->list[i].length;
++ } else {
++ thislen = len;
++ }
++
++ bytes =
++ get_md_data(stoken + sentlen, thislen, id->list + i,
++ iue->adapter);
++ if (bytes < 0)
++ return bytes;
++
++ if (bytes != thislen) {
++ err("Partial data sent to client (%d/%d)\n", bytes, thislen);
++ }
++
++ sentlen += bytes;
++ total_length -= bytes;
++ len -= bytes;
++ }
++
++ return sentlen;
++}
++
++/*
++ * Get some data buffers to start. This doesn't lock the adapter structure!
++ */
++static void init_data_buffer(struct server_adapter *adapter)
++{
++ int i;
++
++ for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) {
++ if (adapter->dma_buffer[i].addr == NULL) {
++ adapter->dma_buffer[i].addr = (char *)
++ dma_alloc_coherent(adapter->dev,
++ DMA_BUFFER_INIT_LEN,
++ &adapter->dma_buffer[i].token,
++ 0);
++ adapter->dma_buffer[i].len = DMA_BUFFER_INIT_LEN;
++ dbg("data buf %p token %8.8x, len %ld\n",
++ adapter->dma_buffer[i].addr,
++ adapter->dma_buffer[i].token,
++ adapter->dma_buffer[i].len);
++ atomic_inc(&adapter->buffers_allocated);
++ }
++ }
++
++ return;
++}
++
++/*
++ * Get a memory buffer that includes a mapped TCE.
++ */
++static void get_data_buffer(char **buffer, dma_addr_t * data_token, size_t len,
++ struct server_adapter *adapter)
++{
++ int i;
++
++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++ if ((adapter->dma_buffer[i].addr) &&
++ (adapter->dma_buffer[i].len >= len) &&
++ (!test_and_set_bit(i, adapter->dma_buffer_use))) {
++ *buffer = adapter->dma_buffer[i].addr;
++ *data_token = adapter->dma_buffer[i].token;
++ return;
++ }
++ }
++
++ /* Couldn't get a buffer! Try and get a new one */
++ *buffer = (char *)dma_alloc_coherent(adapter->dev, len, data_token, 0);
++ atomic_inc(&adapter->buffers_allocated);
++ dbg("get: %p, %8.8x, %ld\n", *buffer, *data_token, len);
++ return;
++}
++
++/*
++ * Free a memory buffer that includes a mapped TCE.
++ */
++static void free_data_buffer(char *buffer, dma_addr_t data_token, size_t len,
++ struct server_adapter *adapter)
++{
++ int i;
++
++ /* First see if this buffer is already in the cache */
++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++ if (adapter->dma_buffer[i].addr == buffer) {
++ if (adapter->dma_buffer[i].token != data_token) {
++ err("Inconsistent data buffer pool info!\n");
++ }
++ if (!test_and_clear_bit(i, adapter->dma_buffer_use)) {
++ err("Freeing data buffer twice!\n");
++ }
++ return;
++ }
++ }
++
++ /* See if there is an empty slot in our list */
++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++ if (!test_and_set_bit(i, adapter->dma_buffer_use)) {
++ if (adapter->dma_buffer[i].addr == NULL) {
++ adapter->dma_buffer[i].addr = buffer;
++ adapter->dma_buffer[i].token = data_token;
++ adapter->dma_buffer[i].len = len;
++ clear_bit(i, adapter->dma_buffer_use);
++ return;
++ } else {
++ clear_bit(i, adapter->dma_buffer_use);
++ }
++ }
++ }
++
++ /* Now see if there is a smaller buffer we should throw out */
++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++ if (!test_and_set_bit(i, adapter->dma_buffer_use)) {
++ if (adapter->dma_buffer[i].len < len) {
++ dbg("fre1: %p, %8.8x, %ld\n",
++ adapter->dma_buffer[i].addr,
++ adapter->dma_buffer[i].token,
++ adapter->dma_buffer[i].len);
++
++ dma_free_coherent(adapter->dev,
++ adapter->dma_buffer[i].len,
++ adapter->dma_buffer[i].addr,
++ adapter->dma_buffer[i].token);
++
++ atomic_dec(&adapter->buffers_allocated);
++
++ adapter->dma_buffer[i].addr = buffer;
++ adapter->dma_buffer[i].token = data_token;
++ adapter->dma_buffer[i].len = len;
++ clear_bit(i, adapter->dma_buffer_use);
++ return;
++ } else {
++ clear_bit(i, adapter->dma_buffer_use);
++ }
++ }
++ }
++
++ /* No space to cache this. Give it back to the kernel */
++ dbg("fre2: %p, %8.8x, %ld\n", buffer, data_token, len);
++ dma_free_coherent(adapter->dev, len, buffer, data_token);
++ atomic_dec(&adapter->buffers_allocated);
++}
++
++/*
++ * Release all the data buffers
++ */
++static void release_data_buffer(struct server_adapter *adapter)
++{
++ int i;
++ int free_in_use = 0;
++
++ for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) {
++ if (adapter->dma_buffer[i].addr != NULL) {
++ if (test_bit(i, adapter->dma_buffer_use)) {
++ free_in_use++;
++ }
++ dma_free_coherent(adapter->dev,
++ adapter->dma_buffer[i].len,
++ adapter->dma_buffer[i].addr,
++ adapter->dma_buffer[i].token);
++
++ atomic_dec(&adapter->buffers_allocated);
++ }
++ }
++
++ if (free_in_use) {
++ err("Freeing %d in-use data buffers\n", free_in_use);
++ }
++ return;
++}
++
++/*
++ * the routine that gets called on end_io of our bios. We basically
++ * schedule the processing to be done in our task, since we don't want
++ * do things like RDMA in someone else's interrupt handler
++ *
++ * Each iu request may result in multiple bio requests. only proceed
++ * when all the bio requests have done.
++ */
++static int ibmvscsis_end_io(struct bio *bio, unsigned int nbytes, int error)
++{
++ struct iu_entry *iue = (struct iu_entry *)bio->bi_private;
++ struct server_adapter *adapter = iue->adapter;
++ unsigned long flags;
++
++ if (bio->bi_size)
++ return 1;
++
++ if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
++ iue->req.ioerr = 1;
++ };
++
++ /* Add the bio to the done queue */
++ spin_lock_irqsave(&adapter->lock, flags);
++ if (adapter->bio_donetail) {
++ adapter->bio_donetail->bi_next = bio;
++ adapter->bio_donetail = bio;
++ } else
++ adapter->bio_done = adapter->bio_donetail = bio;
++ bio->bi_next = NULL;
++ spin_unlock_irqrestore(&adapter->lock, flags);
++
++ /* Schedule the task */
++ tasklet_schedule(&adapter->endio_tasklet);
++
++ return 0;
++}
++
++/*
++ * Find the vdev structure from the LUN field in an SRP IUE
++ * Note that this routine bumps a refcount field in the vdev.
++ * Normally this is done when free_iu is called.
++ */
++static struct vdev *find_device(struct iu_entry *iue)
++{
++ u16 *lun = (u16 *) & iue->iu->srp.cmd.lun;
++ u32 bus = (lun[0] & 0x00E0) >> 5;
++ u32 target = (lun[0] & 0x3F00) >> 8;
++ u32 slun = (lun[0] & 0x001F);
++ struct vdev *vd;
++ unsigned long flags;
++
++ /* If asking for a lun other than 0, return nope */
++ if (slun) {
++ return NULL;
++ }
++
++ /* Only from SRP CMD */
++ if (iue->iu->srp.generic.type != SRP_CMD_TYPE)
++ return NULL;
++
++ /* if not a recognized LUN format, return NULL */
++ if ((lun[0] & 0xC000) != 0x8000)
++ return NULL;
++
++ spin_lock_irqsave(&iue->adapter->lock, flags);
++ if (iue->adapter->vbus[bus] == NULL) {
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++ return NULL;
++ }
++
++ vd = iue->adapter->vbus[bus]->vdev[target];
++
++ if ((vd == NULL) || (vd->disabled)) {
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++ return NULL;
++ }
++
++ if (vd) {
++ atomic_inc(&vd->refcount);
++ }
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++ return vd;
++}
++
++/*
++ * Process BH buffer completions. When the end_io routine gets called
++ * we queue the bio on an internal queue and start a task to process them
++ */
++static void endio_task(unsigned long data)
++{
++ struct server_adapter *adapter = (struct server_adapter *)data;
++ struct iu_entry *iue= NULL;
++ struct bio *bio;
++ int bytes;
++ unsigned long flags;
++
++ do {
++ spin_lock_irqsave(&adapter->lock, flags);
++ if ((bio = adapter->bio_done)) {
++ if (bio == adapter->bio_donetail)
++ adapter->bio_donetail = NULL;
++ adapter->bio_done = bio->bi_next;
++ bio->bi_next = NULL;
++ }
++ if (bio) {
++ /* Remove this iue from the in-flight list */
++ iue = (struct iu_entry *)bio->bi_private;
++ if (!iue->req.in_use) {
++ err("Internal error! freed iue in bio!!!\n");
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ return;
++ }
++
++ list_del(&iue->next);
++ }
++
++ spin_unlock_irqrestore(&adapter->lock, flags);
++
++ if (bio) {
++ /* Send back the SRP and data if this request was NOT
++ * aborted
++ */
++ if (!iue->aborted) {
++
++ if (!iue->req.ioerr) {
++ /* return data if this was a read */
++ if (!iue->req.write) {
++ bytes =
++ send_cmd_data(iue->req.
++ data_token,
++ iue->req.
++ data_len,
++ iue);
++ if (bytes != iue->req.data_len) {
++ err("Error sending data "
++ "on response "
++ "(tried %d, sent %d\n",
++ bio->bi_size, bytes);
++ send_rsp(iue,
++ SENSE_ABORT);
++ } else {
++ send_rsp(iue,
++ SENSE_SUCCESS);
++ }
++ } else {
++ send_rsp(iue, SENSE_SUCCESS);
++ }
++ } else {
++ err("Block operation failed\n");
++ print_command(iue->iu->srp.cmd.cdb);
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++ }
++ }
++
++ spin_lock_irqsave(&adapter->lock, flags);
++ free_data_buffer(iue->req.data_buffer,
++ iue->req.data_token, iue->req.data_len,
++ adapter);
++ spin_unlock_irqrestore(&adapter->lock, flags);
++
++ free_iu(iue);
++
++ bio_put(bio);
++ atomic_dec(&adapter->bio_count);
++ }
++ } while (bio);
++}
++
++/* ==============================================================
++ * SCSI Command Emulation Routines
++ * ==============================================================
++ */
++
++/*
++ * Process an inquiry SCSI Command
++ */
++static void process_inquiry(struct iu_entry *iue)
++{
++ struct inquiry_data *id;
++ dma_addr_t data_token;
++ u8 *raw_id;
++ int bytes;
++
++ id = (struct inquiry_data *)dma_alloc_coherent(iue->adapter->dev,
++ sizeof(*id),
++ &data_token, 0);
++ raw_id = (u8 *)id;
++ memset(id, 0x00, sizeof(*id));
++
++ /* If we have a valid device */
++ if (iue->req.vd) {
++ /* Standard inquiry page */
++ if ((iue->iu->srp.cmd.cdb[1] == 0x00) &&
++ (iue->iu->srp.cmd.cdb[2] == 0x00)) {
++ dbg(" inquiry returning device\n");
++ id->qual_type = 0x00; /* Direct Access */
++ id->rmb_reserve = 0x00; /* TODO: CD is removable */
++ id->version = 0x84; /* ISO/IE */
++ id->aerc_naca_hisup_format = 0x22;/* naca & fmt 0x02 */
++ id->addl_len = sizeof(*id) - 4;
++ id->bque_encserv_vs_multip_mchngr_reserved = 0x00;
++ id->reladr_reserved_linked_cmdqueue_vs = 0x02;/*CMDQ*/
++ memcpy(id->vendor, "IBM ", 8);
++ memcpy(id->product, "VSCSI blkdev ", 16);
++ memcpy(id->revision, "0001", 4);
++ snprintf(id->unique,sizeof(id->unique),
++ "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n",
++ system_id,
++ partition_number,
++ iue->adapter->dma_dev->unit_address,
++ GETBUS(iue->req.vd->lun),
++ GETTARGET(iue->req.vd->lun),
++ GETLUN(iue->req.vd->lun));
++ } else if ((iue->iu->srp.cmd.cdb[1] == 0x01) &&
++ (iue->iu->srp.cmd.cdb[2] == 0x00)) {
++ /* Supported VPD pages */
++ raw_id[0] = 0x00; /* qualifier & type */
++ raw_id[1] = 0x80; /* page */
++ raw_id[2] = 0x00; /* reserved */
++ raw_id[3] = 0x03; /* length */
++ raw_id[4] = 0x00; /* page 0 */
++ raw_id[5] = 0x80; /* serial number page */
++ } else if ((iue->iu->srp.cmd.cdb[1] == 0x01) &&
++ (iue->iu->srp.cmd.cdb[2] == 0x80)) {
++ /* serial number page */
++ raw_id[0] = 0x00; /* qualifier & type */
++ raw_id[1] = 0x80; /* page */
++ raw_id[2] = 0x00; /* reserved */
++ snprintf((char *)(raw_id+4),
++ sizeof(*id)-4,
++ "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n",
++ system_id,
++ partition_number,
++ iue->adapter->dma_dev->unit_address,
++ GETBUS(iue->req.vd->lun),
++ GETTARGET(iue->req.vd->lun),
++ GETLUN(iue->req.vd->lun));
++ raw_id[3] = strlen((char *)raw_id+4);
++ } else {
++ /* Some unsupported data */
++ send_rsp(iue, SENSE_INVALID_FIELD);
++ free_iu(iue);
++ return;
++ }
++ } else {
++ dbg(" inquiry returning no device\n");
++ id->qual_type = 0x7F; /* Not supported, no device */
++ }
++
++ bytes = send_cmd_data(data_token, sizeof(*id), iue);
++
++ dma_free_coherent(iue->adapter->dev, sizeof(*id), id, data_token);
++
++ if (bytes < 0) {
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++ } else {
++ send_rsp(iue, SENSE_SUCCESS);
++ }
++
++ free_iu(iue);
++}
++
++/*
++ * Handle an I/O. Called by WRITE6, WRITE10, etc
++ */
++static void process_rw(char *cmd, int rw, struct iu_entry *iue, long lba,
++ long len)
++{
++ char *buffer;
++ struct bio *bio;
++ int bytes;
++ int num_biovec;
++ int cur_biovec;
++ long flags;
++
++ dbg("%s %16.16lx[%d:%d:%d][%s] lba %ld len %ld reladr %d link %d\n",
++ cmd,
++ iue->iu->srp.cmd.lun,
++ GETBUS(iue->iu->srp.cmd.lun),
++ GETTARGET(iue->iu->srp.cmd.lun),
++ GETLUN(iue->iu->srp.cmd.lun),
++ iue->req.vd->b.device_name,
++ lba,
++ len / iue->req.vd->b.blksize,
++ iue->iu->srp.cmd.cdb[1] & 0x01, iue->req.linked);
++
++ if (rw == WRITE) {
++ atomic_inc(&iue->adapter->write_processed);
++ } else if (rw == READ) {
++ atomic_inc(&iue->adapter->read_processed);
++ } else {
++ err("Major internal error...rw not read or write\n");
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++
++ free_iu(iue);
++ return;
++ }
++
++ if (len == 0) {
++ warn("Zero length I/O\n");
++ send_rsp(iue, SENSE_INVALID_CMD);
++
++ free_iu(iue);
++ return;
++ }
++
++ /* Writing to a read-only device */
++ if ((rw == WRITE) && (iue->req.vd->b.ro)) {
++ warn("WRITE to read-only device\n");
++ send_rsp(iue, SENSE_WRITE_PROT);
++
++ free_iu(iue);
++ return;
++ }
++
++ get_data_buffer(&buffer, &iue->req.data_token, len, iue->adapter);
++ iue->req.data_buffer = buffer;
++ iue->req.data_len = len;
++ if (buffer == NULL) {
++ err("Not able to get a data buffer (%lu pages)\n",
++ len / PAGE_SIZE);
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++
++ free_iu(iue);
++ return;
++ }
++
++ /* if reladr */
++ if (iue->iu->srp.cmd.cdb[1] & 0x01) {
++ lba = lba + iue->req.vd->b.lastlba;
++ }
++
++ /* If this command is linked, Keep this lba */
++ if (iue->req.linked) {
++ iue->req.vd->b.lastlba = lba;
++ } else {
++ iue->req.vd->b.lastlba = 0;
++ }
++
++ if (rw == WRITE) {
++ iue->req.write = 1;
++ /* Get the data */
++ bytes = get_cmd_data(iue->req.data_token, len, iue);
++ if (bytes != len) {
++ err("Error transferring data\n");
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++
++ free_iu(iue);
++ return;
++ }
++ }
++
++ num_biovec = (len - 1) / PAGE_CACHE_SIZE + 1;
++
++ bio = bio_alloc(GFP_ATOMIC, num_biovec);
++ if (!bio) {
++ /* Ouch. couldn't get a bio. Mark this I/O as
++ * in error, then decrement the outstanding bio.
++ * If there are still outstanding bio, they will send
++ * the error and free the IU. If there are none, we
++ * should do it here
++ */
++ iue->req.ioerr = 1;
++ err("Not able to allocate a bio\n");
++ send_rsp(iue, SENSE_DEVICE_FAULT);
++ free_iu(iue);
++ return;
++ }
++
++ iue->aborted = 0;
++ spin_lock_irqsave(&iue->adapter->lock, flags);
++ list_add_tail(&iue->next, &iue->adapter->inflight);
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++ atomic_inc(&iue->adapter->bio_count);
++ bio->bi_size = len;
++ bio->bi_bdev = iue->req.vd->b.bdev;
++ bio->bi_sector = lba;
++ bio->bi_end_io = &ibmvscsis_end_io;
++ bio->bi_private = iue;
++ bio->bi_rw = (rw == WRITE) ? 1 : 0;
++ bio->bi_phys_segments = 1;
++ bio->bi_hw_segments = 1;
++
++ /* This all assumes that the buffers we get are page-aligned */
++ for (cur_biovec = 0; cur_biovec < num_biovec; cur_biovec++) {
++ long thislen;
++
++ if (len > PAGE_CACHE_SIZE) {
++ thislen = PAGE_CACHE_SIZE;
++ } else {
++ thislen = len;
++ }
++
++ bio->bi_io_vec[cur_biovec].bv_page = virt_to_page(buffer);
++ bio->bi_io_vec[cur_biovec].bv_len = thislen;
++ bio->bi_io_vec[cur_biovec].bv_offset =
++ (unsigned long)buffer & PAGE_OFFSET_MASK;
++ bio->bi_vcnt++;
++
++ len -= thislen;
++ buffer += thislen;
++ }
++ generic_make_request(bio);
++}
++
++/*
++ * Process a READ6
++ */
++static void processRead6(struct iu_entry *iue)
++{
++ long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF;
++ long len = iue->iu->srp.cmd.cdb[4];
++
++ /* Length of 0 indicates 256 */
++ if (len == 0) {
++ len = 256;
++ }
++
++ len = len * iue->req.vd->b.blksize;
++
++ process_rw("Read6", READ, iue, lba, len);
++}
++
++/*
++ * Process a READ10
++ */
++static void processRead10(struct iu_entry *iue)
++{
++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++ long len =
++ *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize;
++
++ process_rw("Read10", READ, iue, lba, len);
++}
++
++/*
++ * Process a READ10
++ */
++static void processRead12(struct iu_entry *iue)
++{
++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++ long len =
++ *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize;
++
++ process_rw("Read12", READ, iue, lba, len);
++}
++
++static void processWrite6(struct iu_entry *iue)
++{
++ long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF;
++ long len = iue->iu->srp.cmd.cdb[4];
++
++ /* Length of 0 indicates 256 */
++ if (len == 0) {
++ len = 256;
++ }
++
++ len = len * iue->req.vd->b.blksize;
++
++ process_rw("Write6", WRITE, iue, lba, len);
++}
++
++static void processWrite10(struct iu_entry *iue)
++{
++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++ long len =
++ *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize;
++
++ process_rw("Write10", WRITE, iue, lba, len);
++}
++
++static void processWrite12(struct iu_entry *iue)
++{
++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++ long len =
++ *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize;
++
++ process_rw("Write12", WRITE, iue, lba, len);
++}
++
++/*
++ * Handle Read Capacity
++ */
++static void processReadCapacity(struct iu_entry *iue)
++{
++ struct ReadCapacityData {
++ u32 blocks;
++ u32 blocksize;
++ } *cap;
++ dma_addr_t data_token;
++ int bytes;
++
++ cap = (struct ReadCapacityData *)dma_alloc_coherent(iue->adapter->dev,
++ sizeof(*cap),
++ &data_token, 0);
++
++ /* return block size and last valid block */
++ cap->blocksize = iue->req.vd->b.blksize;
++ cap->blocks = iue->req.vd->b.bdev->bd_inode->i_size
++ / iue->req.vd->b.blksize
++ - 1;
++
++ info("Reporting capacity as %u block of size %u\n", cap->blocks,
++ cap->blocksize);
++
++ bytes = send_cmd_data(data_token, sizeof(*cap), iue);
++
++ dma_free_coherent(iue->adapter->dev, sizeof(*cap), cap, data_token);
++
++ if (bytes != sizeof(*cap)) {
++ err("Error sending read capacity data. bytes %d, wanted %ld\n",
++ bytes, sizeof(*cap));
++ }
++
++ send_rsp(iue, SENSE_SUCCESS);
++
++ free_iu(iue);
++}
++
++/*
++ * Process Mode Sense
++ * TODO: I know scsiinfo asks for a bunch of mode pages not implemented here.
++ * Also, we need to act differently for virtual disk and virtual CD
++ */
++#define MODE_SENSE_BUFFER_SIZE (512)
++static void processModeSense(struct iu_entry *iue)
++{
++ dma_addr_t data_token;
++ int bytes;
++
++ u8 *mode = (u8 *) dma_alloc_coherent(iue->adapter->dev,
++ MODE_SENSE_BUFFER_SIZE,
++ &data_token, 0);
++ /* which page */
++ switch (iue->iu->srp.cmd.cdb[2]) {
++ case 0:
++ case 0x3f:
++ mode[1] = 0x00; /* Default medium */
++ if (iue->req.vd->b.ro) {
++ mode[2] = 0x80; /* device specific */
++ } else {
++ mode[2] = 0x00; /* device specific */
++ }
++ /* note the DPOFUA bit is set to zero! */
++ mode[3] = 0x08; /* block descriptor length */
++ *((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size /
++ iue->req.vd->b.blksize;
++ *((u32 *) & mode[8]) = iue->req.vd->b.blksize;
++ bytes = mode[0] = 12; /* length */
++ break;
++
++ case 0x08: /* Cache page */
++ /* length should be 4 */
++ if (iue->iu->srp.cmd.cdb[4] != 4
++ && iue->iu->srp.cmd.cdb[4] != 0x20) {
++ send_rsp(iue, SENSE_INVALID_CMD);
++ dma_free_coherent(iue->adapter->dev,
++ MODE_SENSE_BUFFER_SIZE,
++ mode, data_token);
++ free_iu(iue);
++ return;
++ }
++
++ mode[1] = 0x00; /* Default medium */
++ if (iue->req.vd->b.ro) {
++ mode[2] = 0x80; /* device specific */
++ } else {
++ mode[2] = 0x00; /* device specific */
++ }
++ /* note the DPOFUA bit is set to zero! */
++ mode[3] = 0x08; /* block descriptor length */
++ *((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size /
++ iue->req.vd->b.blksize;
++ *((u32 *) & mode[8]) = iue->req.vd->b.blksize;
++
++ /* Cache page */
++ mode[12] = 0x08; /* page */
++ mode[13] = 0x12; /* page length */
++ mode[14] = 0x01; /* no cache (0x04 for read/write cache) */
++
++ bytes = mode[0] = 12 + mode[13]; /* length */
++ break;
++ default:
++ warn("Request for unknown mode page %d\n",
++ iue->iu->srp.cmd.cdb[2]);
++ send_rsp(iue, SENSE_INVALID_CMD);
++ dma_free_coherent(iue->adapter->dev,
++ MODE_SENSE_BUFFER_SIZE, mode, data_token);
++ free_iu(iue);
++ return;
++ }
++
++ bytes = send_cmd_data(data_token, bytes, iue);
++
++ dma_free_coherent(iue->adapter->dev,
++ MODE_SENSE_BUFFER_SIZE, mode, data_token);
++
++ send_rsp(iue, SENSE_SUCCESS);
++
++ free_iu(iue);
++ return;
++}
++
++/*
++ * Report LUNS command.
++ */
++static void processReportLUNs(struct iu_entry *iue)
++{
++ int listsize = did_len(&iue->iu->srp.cmd);
++ dma_addr_t data_token;
++ int index = 2; /* Start after the two entries (length and LUN0) */
++ int bus;
++ int target;
++ int bytes;
++ unsigned long flags;
++
++ u64 *lunlist = (u64 *) dma_alloc_coherent(iue->adapter->dev,
++ listsize,
++ &data_token, 0);
++
++ memset(lunlist, 0x00, listsize);
++
++ /* work out list size in units of u64 */
++ listsize = listsize / 8;
++
++ if (listsize < 1) {
++ send_rsp(iue, SENSE_INVALID_CMD);
++ free_iu(iue);
++ }
++
++ spin_lock_irqsave(&iue->adapter->lock, flags);
++
++ /* send lunlist of size 1 when requesting lun is not all zeros */
++ if (iue->iu->srp.cmd.lun != 0x0LL) {
++ *lunlist = ((u64) 1 * 8) << 32;
++ goto send_lunlist;
++ }
++
++ /* return the total number of luns plus LUN0 in bytes */
++ *lunlist = (((u64) ((iue->adapter->nvdevs + 1) * 8)) << 32);
++
++ dbg("reporting %d luns\n", iue->adapter->nvdevs + 1);
++ /* loop through the bus */
++ for (bus = 0; bus < BUS_PER_ADAPTER; bus++) {
++ /* If this bus exists */
++ if (iue->adapter->vbus[bus]) {
++ /* loop through the targets */
++ for (target = 0; target < TARGETS_PER_BUS; target++) {
++ /* If the target exists */
++ if (iue->adapter->vbus[bus]->vdev[target]) {
++ if ((index < listsize) &&
++ (!iue->adapter->vbus[bus]->
++ vdev[target]->disabled)) {
++ lunlist[index++] =
++ iue->adapter->vbus[bus]->
++ vdev[target]->lun;
++ dbg(" lun %16.16lx\n",
++ iue->adapter->vbus[bus]->
++ vdev[target]->lun);
++ }
++ }
++ }
++ }
++ }
++
++ send_lunlist:
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++ bytes = send_cmd_data(data_token, (index * 8), iue);
++
++ dma_free_coherent(iue->adapter->dev, listsize * 8, lunlist, data_token);
++
++ if (bytes != (index * 8)) {
++ err("Error sending report luns data. bytes %d, wanted %d\n",
++ bytes, index * 4);
++ send_rsp(iue, SENSE_ABORT);
++ } else {
++ send_rsp(iue, SENSE_SUCCESS);
++ }
++
++ free_iu(iue);
++ return;
++}
++
++/*
++ * Process an IU.
++ *
++ * Note that THIS routine is responsible for returning the IU from the pool
++ * The current assumption is that all the process routines called from here
++ * are, in turn, responsible for freeing the IU
++ */
++static void process_cmd(struct iu_entry *iue)
++{
++ union viosrp_iu *iu = iue->iu;
++
++ iue->req.vd = find_device(iue);
++
++ if ((iue->req.vd == NULL) &&
++ (iu->srp.cmd.cdb[0] != REPORT_LUNS) &&
++ (iu->srp.cmd.cdb[0] != INQUIRY)) {
++ dbg("Cmd %2.2x for unknown LUN %16.16lx\n",
++ iu->srp.cmd.cdb[0], iue->iu->srp.cmd.lun);
++ send_rsp(iue, SENSE_INVALID_ID);
++ free_iu(iue);
++ return;
++ }
++
++ iue->req.linked = getlink(iue);
++
++ switch (iu->srp.cmd.cdb[0]) {
++ case READ_6:
++ processRead6(iue);
++ break;
++ case READ_10:
++ processRead10(iue);
++ break;
++ case READ_12:
++ processRead12(iue);
++ break;
++ case WRITE_6:
++ processWrite6(iue);
++ break;
++ case WRITE_10:
++ processWrite10(iue);
++ break;
++ case WRITE_12:
++ processWrite12(iue);
++ break;
++ case REPORT_LUNS:
++ dbg("REPORT LUNS lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ processReportLUNs(iue);
++ break;
++ case INQUIRY:
++ dbg("INQUIRY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ process_inquiry(iue);
++ break;
++ case READ_CAPACITY:
++ dbg("READ CAPACITY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ processReadCapacity(iue);
++ break;
++ case MODE_SENSE:
++ dbg("MODE SENSE lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ processModeSense(iue);
++ break;
++ case TEST_UNIT_READY:
++ /* we already know the device exists */
++ dbg("TEST UNIT READY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ send_rsp(iue, SENSE_SUCCESS);
++ free_iu(iue);
++ break;
++ case START_STOP:
++ /* just respond OK */
++ dbg("START_STOP lun %16.16lx\n", iue->iu->srp.cmd.lun);
++ send_rsp(iue, SENSE_SUCCESS);
++ free_iu(iue);
++ break;
++ default:
++ warn("Unsupported SCSI Command 0x%2.2x\n", iu->srp.cmd.cdb[0]);
++ send_rsp(iue, SENSE_INVALID_CMD);
++ free_iu(iue);
++ }
++}
++
++u16 send_adapter_info(struct iu_entry *iue,
++ dma_addr_t remote_buffer, u16 length)
++{
++ dma_addr_t data_token;
++ struct mad_adapter_info_data *info =
++ (struct mad_adapter_info_data *)dma_alloc_coherent(iue->adapter->
++ dev,
++ sizeof(*info),
++ &data_token, 0);
++
++ dbg("in send_adapter_info\n ");
++ if ((info) && (!dma_mapping_error(data_token))) {
++ int rc;
++ memset(info, 0x00, sizeof(*info));
++
++ dbg("building adapter_info\n ");
++ strcpy(info->srp_version, "1.6a");
++ strncpy(info->partition_name, partition_name,
++ sizeof(info->partition_name));
++ info->partition_number = partition_number;
++ info->mad_version = 1;
++ info->os_type = 3;
++
++ rc = h_copy_rdma(sizeof(*info),
++ iue->adapter->liobn,
++ data_token,
++ iue->adapter->riobn,
++ remote_buffer);
++
++ dma_free_coherent(iue->adapter->dev,
++ sizeof(*info), info, data_token);
++
++ if (rc != H_Success) {
++ err("Error sending adapter info rc %d\n",rc);
++ return 1;
++ }
++ } else {
++ dbg("bad dma_alloc_cohereint in adapter_info\n ");
++ return 1;
++ }
++ return 0;
++
++}
++
++/* ==============================================================
++ * SRP Processing Routines
++ * ==============================================================
++ */
++/*
++ * Process an incoming SRP Login request
++ */
++static void process_login(struct iu_entry *iue)
++{
++ union viosrp_iu *iu = iue->iu;
++ u64 tag = iu->srp.generic.tag;
++
++ /* TODO handle case that requested size is wrong and buffer format is wrong */
++ memset(iu, 0x00, sizeof(struct srp_login_rsp));
++ iu->srp.login_rsp.type = SRP_LOGIN_RSP_TYPE;
++ iu->srp.login_rsp.request_limit_delta = iue->adapter->pool.size;
++ iu->srp.login_rsp.tag = tag;
++ iu->srp.login_rsp.max_initiator_to_target_iulen = sizeof(union srp_iu);
++ iu->srp.login_rsp.max_target_to_initiator_iulen = sizeof(union srp_iu);
++ iu->srp.login_rsp.supported_buffer_formats = 0x0006; /* direct and indirect */
++ iu->srp.login_rsp.multi_channel_result = 0x00; /* TODO fix if we were already logged in */
++
++ send_srp(iue, sizeof(iu->srp.login_rsp));
++}
++
++/*
++ * Send an SRP response that includes sense data
++ */
++static long send_rsp(struct iu_entry *iue, int status)
++{
++ u8 *sense = iue->iu->srp.rsp.sense_and_response_data;
++ u64 tag = iue->iu->srp.generic.tag;
++ union viosrp_iu *iu = iue->iu;
++
++ if (status != SENSE_SUCCESS) {
++ atomic_inc(&iue->adapter->errors);
++ }
++
++ /* If the linked bit is on and status is good */
++ if ((iue->req.linked) && (status == SENSE_SUCCESS)) {
++ status = SENSE_INTERMEDIATE;
++ }
++
++ memset(iu, 0x00, sizeof(struct srp_rsp));
++ iu->srp.rsp.type = SRP_RSP_TYPE;
++ iu->srp.rsp.request_limit_delta = 1;
++ iu->srp.rsp.tag = tag;
++
++ iu->srp.rsp.diunder = iue->req.diunder;
++ iu->srp.rsp.diover = iue->req.diover;
++ iu->srp.rsp.dounder = iue->req.dounder;
++ iu->srp.rsp.doover = iue->req.doover;
++
++ iu->srp.rsp.data_in_residual_count = iue->req.data_in_residual_count;
++ iu->srp.rsp.data_out_residual_count = iue->req.data_out_residual_count;
++
++ iu->srp.rsp.rspvalid = 0;
++
++ iu->srp.rsp.response_data_list_length = 0;
++
++ if (status) {
++ iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION;
++ iu->srp.rsp.snsvalid = 1;
++ iu->srp.rsp.sense_data_list_length = 18; /* TODO be smarter about this */
++
++ /* Valid bit and 'current errors' */
++ sense[0] = (0x1 << 7 | 0x70);
++
++ /* Sense key */
++ sense[2] = ibmvscsis_sense_data[status][0];
++
++ /* Additional sense length */
++ sense[7] = 0xa; /* 10 bytes */
++
++ /* Additional sense code */
++ sense[12] = ibmvscsis_sense_data[status][1];
++
++ /* Additional sense code qualifier */
++ sense[13] = ibmvscsis_sense_data[status][2];
++ } else {
++ iu->srp.rsp.status = 0;
++ }
++
++ send_srp(iue, sizeof(iu->srp.rsp));
++
++ return 0;
++}
++
++static void process_device_reset(struct iu_entry *iue)
++{
++ struct iu_entry *tmp_iue;
++ unsigned long flags;
++ union viosrp_iu *iu = iue->iu;
++
++ info("device reset for lun %16.16lx\n", iu->srp.tsk_mgmt.lun);
++
++ spin_lock_irqsave(&iue->adapter->lock, flags);
++
++ list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) {
++ if (iu->srp.tsk_mgmt.lun == tmp_iue->iu->srp.cmd.lun) {
++ {
++ tmp_iue->aborted = 1;
++ }
++ }
++
++ }
++
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++ send_rsp(iue, SENSE_SUCCESS);
++}
++
++static void process_abort(struct iu_entry *iue)
++{
++ struct iu_entry *tmp_iue;
++ unsigned long flags;
++ union viosrp_iu *iu = iue->iu;
++
++ info("aborting task with tag %16.16lx, lun %16.16lx\n",
++ iu->srp.tsk_mgmt.managed_task_tag, iu->srp.tsk_mgmt.lun);
++
++ spin_lock_irqsave(&iue->adapter->lock, flags);
++
++ list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) {
++ if (tmp_iue->iu->srp.cmd.tag ==
++ iu->srp.tsk_mgmt.managed_task_tag) {
++ {
++ tmp_iue->aborted = 1;
++ info("abort successful\n");
++ spin_unlock_irqrestore(&iue->adapter->lock,
++ flags);
++ send_rsp(iue, SENSE_SUCCESS);
++ return;
++ }
++ }
++ }
++ info("unable to abort cmd\n");
++
++ spin_unlock_irqrestore(&iue->adapter->lock, flags);
++ send_rsp(iue, SENSE_INVALID_ID);
++}
++
++static void process_tsk_mgmt(struct iu_entry *iue)
++{
++ union viosrp_iu *iu = iue->iu;
++
++ if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x01) {
++ process_abort(iue);
++ } else if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x08) {
++ process_device_reset(iue);
++ } else {
++ send_rsp(iue, SENSE_INVALID_CMD);
++ }
++}
++
++static void process_iu(struct viosrp_crq *crq, struct server_adapter *adapter)
++{
++ struct iu_entry *iue = get_iu(adapter);
++ union viosrp_iu *iu;
++ int queued = 0;
++ long rc;
++
++ if (iue == NULL) {
++ /* TODO Yikes! */
++ warn("Error getting IU from pool, other side exceeded limit\n");
++ return;
++ }
++
++ iue->req.remote_token = crq->IU_data_ptr;
++
++ rc = h_copy_rdma(crq->IU_length,
++ iue->adapter->riobn,
++ iue->req.remote_token, adapter->liobn, iue->iu_token);
++
++ iu = iue->iu;
++
++ if (rc) {
++ err("Error %ld transferring data to client\n", rc);
++ }
++
++ if (crq->format == VIOSRP_MAD_FORMAT) {
++ switch (iu->mad.empty_iu.common.type) {
++ case VIOSRP_EMPTY_IU_TYPE:
++ warn("Unsupported EMPTY MAD IU\n");
++ break;
++ case VIOSRP_ERROR_LOG_TYPE:
++ warn("Unsupported ERROR LOG MAD IU\n");
++ iu->mad.error_log.common.status = 1;
++ send_srp(iue, sizeof(iu->mad.error_log));
++ break;
++ case VIOSRP_ADAPTER_INFO_TYPE:
++ iu->mad.adapter_info.common.status =
++ send_adapter_info(iue,
++ iu->mad.adapter_info.buffer,
++ iu->mad.adapter_info.common.
++ length);
++
++ send_srp(iue, sizeof(iu->mad.adapter_info));
++ break;
++ case VIOSRP_HOST_CONFIG_TYPE:
++ iu->mad.host_config.common.status = 1;
++ send_srp(iue, sizeof(iu->mad.host_config));
++ break;
++ default:
++ warn("Unsupported MAD type %d\n", iu->srp.generic.type);
++ }
++ } else {
++ switch (iu->srp.generic.type) {
++ case SRP_LOGIN_REQ_TYPE:
++ dbg("SRP LOGIN\n");
++ process_login(iue);
++ break;
++ case SRP_LOGIN_RSP_TYPE:
++ warn("Unsupported LOGIN_RSP SRP IU\n");
++ break;
++ case SRP_I_LOGOUT_TYPE:
++ warn("Unsupported I_LOGOUT SRP IU\n");
++ break;
++ case SRP_T_LOGOUT_TYPE:
++ warn("Unsupported T_LOGOUT SRP IU\n");
++ break;
++ case SRP_TSK_MGMT_TYPE:
++ process_tsk_mgmt(iue);
++ break;
++ case SRP_CMD_TYPE:
++ process_cmd(iue);
++ queued = 1;
++ break;
++ case SRP_RSP_TYPE:
++ warn("Unsupported RSP SRP IU\n");
++ break;
++ case SRP_CRED_REQ_TYPE:
++ warn("Unsupported CRED_REQ SRP IU\n");
++ break;
++ case SRP_CRED_RSP_TYPE:
++ warn("Unsupported CRED_RSP SRP IU\n");
++ break;
++ case SRP_AER_REQ_TYPE:
++ warn("Unsupported AER_REQ SRP IU\n");
++ break;
++ case SRP_AER_RSP_TYPE:
++ warn("Unsupported AER_RSP SRP IU\n");
++ break;
++ default:
++ warn("Unsupported SRP type %d\n", iu->srp.generic.type);
++ }
++ }
++
++ /*
++ * If no one has queued the IU for further work, free it
++ * Note that this is kind of an ugly design based on setting
++ * this variable up above in cases where the routine we call
++ * is responsible for freeing the IU
++ */
++ if (!queued)
++ free_iu(iue);
++}
++
++/* ==============================================================
++ * CRQ Processing Routines
++ * ==============================================================
++ */
++
++/*
++ * Handle a CRQ event
++ */
++static void handle_crq(struct viosrp_crq *crq, struct server_adapter *adapter)
++{
++ switch (crq->valid) {
++ case 0xC0: /* initialization */
++ switch (crq->format) {
++ case 0x01:
++ info("Client just initialized\n");
++ plpar_hcall_norets(H_SEND_CRQ,
++ adapter->dma_dev->unit_address,
++ 0xC002000000000000, 0);
++ break;
++ case 0x02:
++ info("Client initialization complete\n");
++ break;
++ default:
++ err("Client error: Unknwn msg format %d\n",
++ crq->format);
++ }
++ return;
++ case 0xFF: /* transport event */
++ info("Client closed\n");
++ return;
++ case 0x80: /* real payload */
++ {
++ switch (crq->format) {
++ case VIOSRP_SRP_FORMAT:
++ case VIOSRP_MAD_FORMAT:
++ process_iu(crq, adapter);
++ break;
++ case VIOSRP_OS400_FORMAT:
++ warn("Unsupported OS400 format CRQ\n");
++ break;
++
++ case VIOSRP_AIX_FORMAT:
++ warn("Unsupported AIX format CRQ\n");
++ break;
++
++ case VIOSRP_LINUX_FORMAT:
++ warn("Unsupported LINUX format CRQ\n");
++ break;
++
++ case VIOSRP_INLINE_FORMAT:
++ warn("Unsupported _INLINE_ format CRQ\n");
++ break;
++
++ default:
++ err("Client error: Unsupported msg format %d\n",
++ crq->format);
++ }
++ }
++ break;
++ default:
++ err("Client error: unknown message type 0x%02x!?\n",
++ crq->valid);
++ return;
++ }
++
++}
++
++/*
++ * Task to handle CRQs and completions
++ */
++static void crq_task(void *data)
++{
++ struct server_adapter *adapter = (struct server_adapter *)data;
++ struct viosrp_crq *crq;
++ long rc;
++ int done = 0;
++
++ while (!done) {
++
++ /* Loop through and process CRQs */
++ while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) {
++ atomic_inc(&adapter->crq_processed);
++ handle_crq(crq, adapter);
++ crq->valid = 0x00;
++ }
++
++ rc = h_vio_signal(adapter->dma_dev->unit_address, 1);
++ if (rc != 0) {
++ err("Error %ld enabling interrupts!!!\n", rc);
++ }
++ if ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) {
++ rc = h_vio_signal(adapter->dma_dev->unit_address, 0);
++ if (rc != 0) {
++ err("Error %ld enabling interrupts!!!\n", rc);
++ }
++ handle_crq(crq, adapter);
++ crq->valid = 0x00;
++ } else {
++ done = 1;
++ }
++ }
++}
++
++/*
++ * Handle the interrupt that occurs when something is placed on our CRQ
++ */
++static irqreturn_t handle_interrupt(int irq, void *dev_instance,
++ struct pt_regs *regs)
++{
++ struct server_adapter *adapter = (struct server_adapter *)dev_instance;
++ long rc;
++
++ rc = h_vio_signal(adapter->dma_dev->unit_address, 0);
++ if (rc != 0) {
++ err(" Error %ld disabling interrupts!!!\n", rc);
++ }
++
++ atomic_inc(&adapter->interrupts);
++
++ kblockd_schedule_work(&adapter->crq_task);
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * Initialize our CRQ
++ * return zero on success, non-zero on failure
++ */
++static int initialize_crq_queue(struct crq_queue *queue,
++ struct server_adapter *adapter)
++{
++ int rc;
++
++ queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
++ if (!queue->msgs)
++ goto malloc_failed;
++ queue->size = PAGE_SIZE / sizeof(*queue->msgs);
++
++ queue->msg_token = dma_map_single(adapter->dev, queue->msgs,
++ queue->size * sizeof(*queue->msgs),
++ DMA_BIDIRECTIONAL);
++
++ if (dma_mapping_error(queue->msg_token))
++ goto map_failed;
++
++ rc = plpar_hcall_norets(H_REG_CRQ, adapter->dma_dev->unit_address,
++ queue->msg_token, PAGE_SIZE);
++
++ if ((rc != 0) && (rc != 2)) {
++ err("Error 0x%x opening virtual adapter\n", rc);
++ goto reg_crq_failed;
++ }
++
++ if (request_irq
++ (adapter->dma_dev->irq, &handle_interrupt, SA_INTERRUPT,
++ "ibmvscsis", adapter) != 0)
++ goto req_irq_failed;
++
++ rc = h_vio_signal(adapter->dma_dev->unit_address, 1);
++ if (rc != 0) {
++ err("Error %d enabling interrupts!!!\n", rc);
++ goto req_irq_failed;
++ }
++
++ plpar_hcall_norets(H_SEND_CRQ, adapter->dma_dev->unit_address,
++ 0xC001000000000000, 0);
++
++ queue->cur = 0;
++ queue->lock = SPIN_LOCK_UNLOCKED;
++
++ return 0;
++
++ req_irq_failed:
++ do {
++ rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address);
++ } while ((rc == H_Busy) || (H_isLongBusy(rc)));
++
++ reg_crq_failed:
++ dma_unmap_single(adapter->dev, queue->msg_token,
++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++ map_failed:
++ free_page((unsigned long)queue->msgs);
++ malloc_failed:
++ return -1;
++}
++
++/*
++ * Release the CRQ
++ */
++static void release_crq_queue(struct crq_queue *queue,
++ struct server_adapter *adapter)
++{
++ int rc;
++
++ info("releasing adapter\n");
++ free_irq(adapter->dma_dev->irq, adapter);
++ do {
++ rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address);
++ } while ((rc == H_Busy) || (H_isLongBusy(rc)));
++ dma_unmap_single(adapter->dev, queue->msg_token,
++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++ free_page((unsigned long)queue->msgs);
++}
++
++/* ==============================================================
++ * Module Management
++ * ==============================================================
++ */
++/*
++ * Add a block device as a SCSI LUN
++ */
++static int activate_block_device(struct vdev *vdev)
++{
++ struct block_device *bdev;
++ char *name = vdev->b.device_name;
++ int ro = vdev->b.ro;
++
++ bdev = open_bdev_excl(name, ro, activate_block_device);
++ if (IS_ERR(bdev))
++ return PTR_ERR(bdev);;
++
++ vdev->b.bdev = bdev;
++ vdev->disabled = 0;
++
++ info("Activating block device %s as %sLUN 0x%lx\n",
++ name, ro ? "read only " : "", vdev->lun);
++
++ return 0;
++}
++
++static void deactivate_block_device(struct vdev *vdev)
++{
++ info("Deactivating block device, LUN 0x%lx\n", vdev->lun);
++
++ /* Wait while any users of this device finish. Note there should
++ * be no new users, since we have marked this disabled
++ *
++ * We just poll here, since we are blocking write
++ */
++ while (atomic_read(&vdev->refcount)) {
++ schedule_timeout(HZ / 4); /* 1/4 second */
++ }
++
++ vdev->disabled = 1;
++ close_bdev_excl(vdev->b.bdev);
++}
++
++
++#define ATTR(_type, _name, _mode) \
++struct attribute vscsi_##_type##_##_name##_attr = { \
++.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE \
++};
++
++static struct kobj_type ktype_vscsi_target;
++static struct kobj_type ktype_vscsi_bus;
++static struct kobj_type ktype_vscsi_stats;
++
++static void set_num_targets(struct vbus* vbus, long value)
++{
++ struct device *dev =
++ container_of(vbus->kobj.parent, struct device , kobj);
++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++ int cur_num_targets = atomic_read(&vbus->num_targets);
++ unsigned long flags;
++
++ spin_lock_irqsave(&adapter->lock, flags);
++
++ if (cur_num_targets < value) { //growing
++ int i;
++ for (i = cur_num_targets; i < value; i++) {
++ vbus->vdev[i] = (struct vdev *)
++ kmalloc(sizeof(struct vdev), GFP_KERNEL);
++ if (!vbus->vdev[i]) {
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ err("Couldn't allocate target memory %d\n", i);
++ return;
++ }
++ memset(vbus->vdev[i], 0x00, sizeof(struct vdev));
++
++ vbus->vdev[i]->lun = make_lun(vbus->bus_num, i, 0);
++ vbus->vdev[i]->b.blksize = 512;
++ vbus->vdev[i]->disabled = 1;
++
++ vbus->vdev[i]->kobj.parent = &vbus->kobj;
++ sprintf(vbus->vdev[i]->kobj.name, "target%d", i);
++ vbus->vdev[i]->kobj.ktype = &ktype_vscsi_target;
++ kobject_register(&vbus->vdev[i]->kobj);
++ adapter->nvdevs++;
++ atomic_inc(&vbus->num_targets);
++ }
++ } else { //shrinking
++ int i;
++ for (i = cur_num_targets - 1; i >= value; i--)
++ {
++ if (!vbus->vdev[i]->disabled) {
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ err("Can't remove active target %d\n", i);
++ return;
++ }
++
++ kobject_unregister(&vbus->vdev[i]->kobj);
++
++ kfree(vbus->vdev[i]);
++
++ adapter->nvdevs--;
++ atomic_dec(&vbus->num_targets);
++ }
++ }
++ spin_unlock_irqrestore(&adapter->lock, flags);
++}
++
++static void set_num_buses(struct device *dev, long value)
++{
++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++ int cur_num_buses = atomic_read(&adapter->num_buses);
++ unsigned long flags= 0L;
++
++
++ if (cur_num_buses < value) { // growing
++ int i;
++ for (i = cur_num_buses; i < value; i++) {
++ adapter->vbus[i] = (struct vbus *)
++ kmalloc(sizeof(struct vbus), GFP_KERNEL);
++ if (!adapter->vbus[i]) {
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ err("Couldn't allocate bus %d memory\n", i);
++ return;
++ }
++ memset(adapter->vbus[i], 0x00, sizeof(struct vbus));
++
++ spin_lock_irqsave(&adapter->lock, flags);
++
++ adapter->vbus[i]->bus_num = i;
++
++ adapter->vbus[i]->kobj.parent = &dev->kobj;
++ sprintf(adapter->vbus[i]->kobj.name, "bus%d", i);
++ adapter->vbus[i]->kobj.ktype = &ktype_vscsi_bus;
++ kobject_register(&adapter->vbus[i]->kobj);
++
++ atomic_inc(&adapter->num_buses);
++ spin_unlock_irqrestore(&adapter->lock, flags);
++
++ set_num_targets(adapter->vbus[i], 1);
++ }
++
++ } else if (cur_num_buses > value) { //shrinking
++ int i, j, active_target;
++ for (i = cur_num_buses - 1; i >= value; i--) {
++ active_target = -1;
++ for (j = 0; j < TARGETS_PER_BUS; j++) {
++ if (adapter->vbus[i]->vdev[j] &&
++ !adapter->vbus[i]->vdev[j]->disabled) {
++ active_target = j;
++ break;
++ }
++ }
++ if (active_target != -1) {
++ err("Can't remove bus%d, target%d active\n",
++ i, active_target);
++ return ;
++ }
++
++ set_num_targets(adapter->vbus[i], 0);
++
++ spin_lock_irqsave(&adapter->lock, flags);
++ atomic_dec(&adapter->num_buses);
++ kobject_unregister(&adapter->vbus[i]->kobj);
++ kfree(adapter->vbus[i]);
++ adapter->vbus[i] = NULL;
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ }
++ }
++}
++
++
++/* Target sysfs stuff */
++static ATTR(target, type, 0644);
++static ATTR(target, device, 0644);
++static ATTR(target, active, 0644);
++static ATTR(target, ro, 0644);
++
++static ssize_t vscsi_target_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++ struct vdev *vdev = container_of(kobj, struct vdev, kobj);
++ struct device *dev = container_of(kobj->parent->parent, struct device, kobj);
++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++ unsigned long flags;
++ ssize_t returned= (ssize_t)0;
++
++ spin_lock_irqsave(&adapter->lock, flags);
++
++ if (attr == &vscsi_target_type_attr)
++ returned = sprintf(buf, "%c\n", vdev->type);
++ else if (attr == &vscsi_target_device_attr)
++ returned = sprintf(buf, "%s\n", vdev->b.device_name);
++ else if (attr == &vscsi_target_active_attr)
++ returned = sprintf(buf, "%d\n", !vdev->disabled);
++ else if (attr == &vscsi_target_ro_attr)
++ returned = sprintf(buf, "%d\n", vdev->b.ro);
++ else {
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ BUG();
++ }
++
++ spin_unlock_irqrestore(&adapter->lock, flags);
++
++ return returned;
++}
++
++static ssize_t vscsi_target_store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count)
++{
++ struct vdev *vdev = container_of(kobj, struct vdev, kobj);
++ struct device *dev = container_of(kobj->parent->parent, struct device, kobj);
++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++ long flags;
++ long value = simple_strtol(buf, NULL, 10);
++
++ if (attr != &vscsi_target_active_attr && !vdev->disabled) {
++ err("Error: Can't modify properties while target is active.\n");
++ return -EPERM;
++ }
++
++ if (attr == &vscsi_target_type_attr) {
++ if (buf[0] == 'B' || buf[0] == 'b')
++ vdev->type = 'B';
++ else if (buf[0] == 'S' || buf[0] == 's') {
++ // TODO
++ err ("SCSI mode not supported yet\n");
++ return -EINVAL;
++ } else
++ return -EINVAL;
++ } else if (attr == &vscsi_target_device_attr) {
++ int i;
++ spin_lock_irqsave(&adapter->lock, flags);
++ i = strlcpy(vdev->b.device_name, buf, TARGET_MAX_NAME_LEN);
++ for (; i >= 0; i--)
++ if (vdev->b.device_name[i] == '\n')
++ vdev->b.device_name[i] = '\0';
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ } else if (attr == &vscsi_target_active_attr) {
++ if (value) {
++ int rc;
++ if (!vdev->disabled) {
++ warn("Warning: Target was already active\n");
++ return -EINVAL;
++ }
++ if (vdev->type == '\0') {
++ err("Error: Type not specified\n");
++ return -EPERM;
++ }
++ rc = activate_block_device(vdev);
++ if (rc) {
++ err("Error opening block device=%d\n", rc);
++ return rc;
++ }
++ } else {
++ if (!vdev->disabled)
++ deactivate_block_device(vdev);
++ }
++ } else if (attr == &vscsi_target_ro_attr)
++ vdev->b.ro = value > 0 ? 1 : 0;
++ else
++ BUG();
++
++ return count;
++}
++
++static struct attribute * vscsi_target_attrs[] = {
++ &vscsi_target_type_attr,
++ &vscsi_target_device_attr,
++ &vscsi_target_active_attr,
++ &vscsi_target_ro_attr,
++ NULL,
++};
++
++static struct sysfs_ops vscsi_target_ops = {
++ .show = vscsi_target_show,
++ .store = vscsi_target_store,
++};
++
++static struct kobj_type ktype_vscsi_target = {
++ .release = NULL,
++ .sysfs_ops = &vscsi_target_ops,
++ .default_attrs = vscsi_target_attrs,
++};
++
++
++
++/* Bus sysfs stuff */
++static ssize_t vscsi_bus_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++ struct vbus *vbus = container_of(kobj, struct vbus, kobj);
++ return sprintf(buf, "%d\n", atomic_read(&vbus->num_targets));
++}
++
++static ssize_t vscsi_bus_store(struct kobject * kobj, struct attribute * attr,
++const char * buf, size_t count)
++{
++ struct vbus *vbus = container_of(kobj, struct vbus, kobj);
++ long value = simple_strtol(buf, NULL, 10);
++
++ if (value < 0 || value > TARGETS_PER_BUS)
++ return -EINVAL;
++
++ set_num_targets(vbus, value);
++
++ return count;
++}
++
++
++static ATTR(bus, num_targets, 0644);
++
++static struct attribute * vscsi_bus_attrs[] = {
++ &vscsi_bus_num_targets_attr,
++ NULL,
++};
++
++static struct sysfs_ops vscsi_bus_ops = {
++ .show = vscsi_bus_show,
++ .store = vscsi_bus_store,
++};
++
++static struct kobj_type ktype_vscsi_bus = {
++ .release = NULL,
++ .sysfs_ops = &vscsi_bus_ops,
++ .default_attrs = vscsi_bus_attrs,
++};
++
++
++/* Device attributes */
++static ssize_t vscsi_dev_bus_show(struct device * dev, char * buf)
++{
++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++
++ return sprintf(buf, "%d\n", atomic_read(&adapter->num_buses));
++}
++
++static ssize_t vscsi_dev_bus_store(struct device * dev, const char * buf, size_t count)
++{
++ long value = simple_strtol(buf, NULL, 10);
++
++ if (value < 0 || value > BUS_PER_ADAPTER)
++ return -EINVAL;
++
++ set_num_buses(dev, value);
++ return count;
++}
++
++static DEVICE_ATTR(num_buses, 0644, vscsi_dev_bus_show, vscsi_dev_bus_store);
++
++
++/* Stats kobj stuff */
++
++static ATTR(stats, interrupts, 0444);
++static ATTR(stats, read_ops, 0444);
++static ATTR(stats, write_ops, 0444);
++static ATTR(stats, crq_msgs, 0444);
++static ATTR(stats, iu_allocs, 0444);
++static ATTR(stats, bio_allocs, 0444);
++static ATTR(stats, buf_allocs, 0444);
++static ATTR(stats, errors, 0444);
++
++static struct attribute * vscsi_stats_attrs[] = {
++ &vscsi_stats_interrupts_attr,
++ &vscsi_stats_read_ops_attr,
++ &vscsi_stats_write_ops_attr,
++ &vscsi_stats_crq_msgs_attr,
++ &vscsi_stats_iu_allocs_attr,
++ &vscsi_stats_bio_allocs_attr,
++ &vscsi_stats_buf_allocs_attr,
++ &vscsi_stats_errors_attr,
++ NULL,
++};
++
++
++static ssize_t vscsi_stats_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++ struct server_adapter *adapter= container_of(kobj, struct server_adapter, stats_kobj);
++ if (attr == &vscsi_stats_interrupts_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->interrupts));
++ if (attr == &vscsi_stats_read_ops_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->read_processed));
++ if (attr == &vscsi_stats_write_ops_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->write_processed));
++ if (attr == &vscsi_stats_crq_msgs_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->crq_processed));
++ if (attr == &vscsi_stats_iu_allocs_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->iu_count));
++ if (attr == &vscsi_stats_bio_allocs_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->bio_count));
++ if (attr == &vscsi_stats_buf_allocs_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->buffers_allocated));
++ if (attr == &vscsi_stats_errors_attr)
++ return sprintf(buf, "%d\n",
++ atomic_read(&adapter->errors));
++
++ BUG();
++ return 0;
++}
++
++static struct sysfs_ops vscsi_stats_ops = {
++ .show = vscsi_stats_show,
++ .store = NULL,
++};
++
++static struct kobj_type ktype_vscsi_stats = {
++ .release = NULL,
++ .sysfs_ops = &vscsi_stats_ops,
++ .default_attrs = vscsi_stats_attrs,
++};
++
++
++static int ibmvscsis_probe(struct vio_dev *dev, const struct vio_device_id *id)
++{
++ struct server_adapter *adapter;
++ int rc;
++ unsigned int *dma_window;
++ unsigned int dma_window_property_size;
++
++ adapter = kmalloc(sizeof(*adapter), GFP_KERNEL);
++ if (!adapter) {
++ err("couldn't allocate adapter memory\n");
++ return -1;
++ }
++ memset(adapter, 0x00, sizeof(*adapter));
++ adapter->dma_dev = dev;
++ adapter->dev = &dev->dev;
++ dev->driver_data = adapter;
++ sprintf(adapter->name, "%x", dev->unit_address);
++ adapter->lock = SPIN_LOCK_UNLOCKED;
++
++ dma_window =
++ (unsigned int *)vio_get_attribute(dev, "ibm,my-dma-window",
++ &dma_window_property_size);
++ if (!dma_window) {
++ warn("Couldn't find ibm,my-dma-window property\n");
++ }
++
++ adapter->liobn = dma_window[0];
++ /* RPA docs say that #address-cells is always 1 for virtual
++ devices, but some older boxes' OF returns 2. This should
++ be removed by GA, unless there is legacy OFs that still
++ have 2 or 3 for #address-cells */
++ /*adapter->riobn = dma_window[2+vio_num_address_cells]; */
++
++ /* This is just an ugly kludge. Remove as soon as the OF for all
++ machines actually follow the spec and encodes the offset field
++ as phys-encode (that is, #address-cells wide) */
++ if (dma_window_property_size == 24) {
++ adapter->riobn = dma_window[3];
++ } else if (dma_window_property_size == 40) {
++ adapter->riobn = dma_window[5];
++ } else {
++ warn("Invalid size of ibm,my-dma-window=%i\n",
++ dma_window_property_size);
++ }
++
++ INIT_WORK(&adapter->crq_task, crq_task, adapter);
++
++ tasklet_init(&adapter->endio_tasklet,
++ endio_task, (unsigned long)adapter);
++
++ INIT_LIST_HEAD(&adapter->inflight);
++
++ /* Initialize the buffer cache */
++ init_data_buffer(adapter);
++
++ /* Arbitrarily support 16 IUs right now */
++ rc = initialize_iu_pool(adapter, 16);
++ if (rc) {
++ kfree(adapter);
++ return rc;
++ }
++
++ rc = initialize_crq_queue(&adapter->queue, adapter);
++ if (rc != 0) {
++ kfree(adapter);
++ return rc;
++ }
++
++ set_num_buses(&dev->dev, 1);
++ device_create_file(&dev->dev, &dev_attr_num_buses);
++
++ adapter->stats_kobj.parent = &dev->dev.kobj;
++ strcpy(adapter->stats_kobj.name, "stats");
++ adapter->stats_kobj.ktype = & ktype_vscsi_stats;
++ kobject_register(&adapter->stats_kobj);
++
++ return 0;
++}
++
++static int ibmvscsis_remove(struct vio_dev *dev)
++{
++ int bus;
++ int target;
++ unsigned long flags;
++ struct server_adapter *adapter =
++ (struct server_adapter *)dev->driver_data;
++
++ spin_lock_irqsave(&adapter->lock, flags);
++
++ /*
++ * Loop through the bus
++ */
++ for (bus = 0; bus < BUS_PER_ADAPTER; bus++) {
++ /* If this bus exists */
++ if (adapter->vbus[bus]) {
++ /* loop through the targets */
++ for (target = 0; target < TARGETS_PER_BUS; target++) {
++ /* If the target exists */
++ if (adapter->vbus[bus]->vdev[target] &&
++ !adapter->vbus[bus]->vdev[target]
++ ->disabled) {
++ deactivate_block_device(adapter->
++ vbus[bus]->vdev[target]);
++ }
++ }
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ set_num_targets(adapter->vbus[bus], 0);
++ spin_lock_irqsave(&adapter->lock, flags);
++ }
++ }
++
++ spin_unlock_irqrestore(&adapter->lock, flags);
++ set_num_buses(adapter->dev, 0);
++ release_crq_queue(&adapter->queue, adapter);
++
++ release_iu_pool(adapter);
++
++ release_data_buffer(adapter);
++
++ kobject_unregister(&adapter->stats_kobj);
++ device_remove_file(&dev->dev, &dev_attr_num_buses);
++
++ kfree(adapter);
++
++ return 0;
++}
++
++static struct vio_device_id ibmvscsis_device_table[] __devinitdata = {
++ {"v-scsi-host", "IBM,v-scsi-host"},
++ {0,}
++};
++
++MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
++
++static struct vio_driver ibmvscsis_driver = {
++ .name = "ibmvscsis",
++ .id_table = ibmvscsis_device_table,
++ .probe = ibmvscsis_probe,
++ .remove = ibmvscsis_remove,
++};
++
++static int mod_init(void)
++{
++ struct device_node *rootdn;
++ char *ppartition_name;
++ char *psystem_id;
++ char *pmodel;
++ unsigned int *p_number_ptr;
++ int rc;
++
++ /* Retrieve information about this partition */
++ rootdn = find_path_device("/");
++ if (rootdn) {
++ pmodel = get_property(rootdn, "model", NULL);
++ psystem_id = get_property(rootdn, "system-id", NULL);
++ if (pmodel && psystem_id)
++ snprintf(system_id,sizeof(system_id),
++ "%s-%s",
++ pmodel, psystem_id);
++ ppartition_name =
++ get_property(rootdn, "ibm,partition-name", NULL);
++ if (ppartition_name)
++ strncpy(partition_name, ppartition_name,
++ sizeof(partition_name));
++ p_number_ptr =
++ (unsigned int *)get_property(rootdn, "ibm,partition-no",
++ NULL);
++ if (p_number_ptr)
++ partition_number = *p_number_ptr;
++ }
++
++ info("initialized version "IBMVSCSIS_VERSION"\n");
++
++ rc = vio_register_driver(&ibmvscsis_driver);
++
++ if (rc) {
++ warn("rc %d from vio_register_driver\n", rc);
++ }
++
++ return rc;
++}
++
++static void mod_exit(void)
++{
++ info("terminated\n");
++
++ vio_unregister_driver(&ibmvscsis_driver);
++}
++
++module_init(mod_init);
++module_exit(mod_exit);
+diff -aurN a/include/asm-ppc64/vio.h b/include/asm-ppc64/vio.h
+--- a/include/asm-ppc64/vio.h 2005-06-17 15:48:29.000000000 -0400
++++ b/include/asm-ppc64/vio.h 2005-06-18 12:02:58.000000000 -0400
+@@ -91,6 +91,7 @@
+ char *type;
+ uint32_t unit_address;
+ unsigned int irq;
++ void *driver_data;
+
+ struct device dev;
+ };
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-serial.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-serial.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/powerpc-serial.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,48 @@
+#! /bin/sh -e
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Disables legacy serial driver on powermacs.
+## DP: Patch author: Sven Luther <luther@debian.org>
+## DP: Patch author: adapted from the SuSE kernel tree.
+## DP: Upstream status: workaround hack waiting for a clean legacy device solution.
+
+diff -aurN a/drivers/serial/8250.c b/drivers/serial/8250.c
+--- a/drivers/serial/8250.c 2005-06-17 15:48:29.000000000 -0400
++++ b/drivers/serial/8250.c 2005-06-18 12:05:39.000000000 -0400
+@@ -46,6 +46,10 @@
+
+ #include "8250.h"
+
++#ifdef CONFIG_PPC_MULTIPLATFORM
++#include <asm/processor.h>
++#endif
++
+ /*
+ * Configuration:
+ * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option
+@@ -2188,6 +2192,12 @@
+
+ static int __init serial8250_console_init(void)
+ {
++#ifdef CONFIG_PPC_MULTIPLATFORM
++ if(_machine == _MACH_Pmac) {
++ printk("%s: nothing to do on PowerMac\n",__FUNCTION__);
++ return -ENODEV;
++ }
++#endif
+ serial8250_isa_init_ports();
+ register_console(&serial8250_console);
+ return 0;
+@@ -2491,6 +2501,12 @@
+ {
+ int ret, i;
+
++#ifdef CONFIG_PPC_MULTIPLATFORM
++ if(_machine == _MACH_Pmac) {
++ printk("%s: nothing to do on PowerMac\n",__FUNCTION__);
++ return -ENODEV;
++ }
++#endif
+ printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
+ "%d ports, IRQ sharing %sabled\n", (int) UART_NR,
+ share_irqs ? "en" : "dis");
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/sparc64-hme-lockup.dpatch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/sparc64-hme-lockup.dpatch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/sparc64-hme-lockup.dpatch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,16 @@
+# origin: Debian (bcollins)
+# cset: n/a
+# inclusion: not suitable for upstream
+# revision date: 2004-10-08
+
+diff -aurN a/drivers/net/sunhme.c b/drivers/net/sunhme.c
+--- a/drivers/net/sunhme.c 2005-06-17 15:48:29.000000000 -0400
++++ b/drivers/net/sunhme.c 2005-06-18 12:12:18.000000000 -0400
+@@ -1996,6 +1996,7 @@
+ }
+ hp->tx_old = elem;
+ TXD((">"));
++ udelay(1);
+
+ if (netif_queue_stopped(dev) &&
+ TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1))
Added: trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/tty-locking-fixes9.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/tty-locking-fixes9.patch 2005-06-18 11:56:32 UTC (rev 3342)
+++ trunk/kernel/source/kernel-source-2.6.12-2.6.12/debian/patches/tty-locking-fixes9.patch 2005-06-18 16:17:44 UTC (rev 3343)
@@ -0,0 +1,12 @@
+--- kernel-source-2.6.11-2.6.11-orig/drivers/serial/serial_core.c 2005-03-02 08:37:50.000000000 +0100
++++ kernel-source-2.6.11-2.6.11/drivers/serial/serial_core.c 2005-03-05 10:46:43.417109240 +0100
+@@ -108,7 +108,8 @@
+ static void uart_tasklet_action(unsigned long data)
+ {
+ struct uart_state *state = (struct uart_state *)data;
+- tty_wakeup(state->info->tty);
++ if (state->info->tty)
++ tty_wakeup(state->info->tty);
+ }
+
+ static inline void