patch-2.4.7 linux/drivers/message/fusion/mptctl.c
Next file: linux/drivers/message/fusion/mptlan.c
Previous file: linux/drivers/message/fusion/mptbase.h
Back to the patch index
Back to the overall index
- Lines: 1297
- Date:
Fri Jul 6 17:03:11 2001
- Orig file:
v2.4.6/linux/drivers/message/fusion/mptctl.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.6/linux/drivers/message/fusion/mptctl.c linux/drivers/message/fusion/mptctl.c
@@ -0,0 +1,1296 @@
+/*
+ * linux/drivers/message/fusion/mptctl.c
+ * Fusion MPT misc device (ioctl) driver.
+ * For use with PCI chip/adapter(s):
+ * LSIFC9xx/LSI409xx Fibre Channel
+ * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
+ *
+ * Credits:
+ * This driver would not exist if not for Alan Cox's development
+ * of the linux i2o driver.
+ *
+ * A huge debt of gratitude is owed to David S. Miller (DaveM)
+ * for fixing much of the stupid and broken stuff in the early
+ * driver while porting to sparc64 platform. THANK YOU!
+ *
+ * A big THANKS to Eddie C. Dost for fixing the ioctl path
+ * and most importantly f/w download on sparc64 platform!
+ * (plus Eddie's other helpful hints and insights)
+ *
+ * Thanks to Arnaldo Carvalho de Melo for finding and patching
+ * a potential memory leak in mpt_ioctl_do_fw_download(),
+ * and for some kmalloc insight:-)
+ *
+ * (see also mptbase.c)
+ *
+ * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Originally By: Steven J. Ralston, Noah Romer
+ * (mailto:Steve.Ralston@lsil.com)
+ *
+ * $Id: mptctl.c,v 1.23 2001/03/21 19:42:31 sralston Exp $
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ 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; version 2 of the License.
+
+ 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.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/miscdevice.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/proc_fs.h>
+
+#define COPYRIGHT "Copyright (c) 1999-2001 LSI Logic Corporation"
+#define MODULEAUTHOR "Steven J. Ralston, Noah Romer"
+#include "mptbase.h"
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#define my_NAME "Fusion MPT misc device (ioctl) driver"
+#define my_VERSION MPT_LINUX_VERSION_COMMON
+#define MYNAM "mptctl"
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR(MODULEAUTHOR);
+MODULE_DESCRIPTION(my_NAME);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+static int mptctl_id = -1;
+static int rwperf_reset = 0;
+static struct semaphore mptctl_syscall_sem_ioc[MPT_MAX_ADAPTERS];
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+static int mpt_ioctl_rwperf(unsigned long arg);
+static int mpt_ioctl_rwperf_status(unsigned long arg);
+static int mpt_ioctl_rwperf_reset(unsigned long arg);
+static int mpt_ioctl_fw_download(unsigned long arg);
+static int mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen);
+static int mpt_ioctl_scsi_cmd(unsigned long arg);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Scatter gather list (SGL) sizes and limits...
+ */
+//#define MAX_SCSI_FRAGS 9
+#define MAX_FRAGS_SPILL1 9
+#define MAX_FRAGS_SPILL2 15
+#define FRAGS_PER_BUCKET (MAX_FRAGS_SPILL2 + 1)
+
+//#define MAX_CHAIN_FRAGS 64
+//#define MAX_CHAIN_FRAGS (15+15+15+16)
+#define MAX_CHAIN_FRAGS (4 * MAX_FRAGS_SPILL2 + 1)
+
+// Define max sg LIST bytes ( == (#frags + #chains) * 8 bytes each)
+// Works out to: 592d bytes! (9+1)*8 + 4*(15+1)*8
+// ^----------------- 80 + 512
+#define MAX_SGL_BYTES ((MAX_FRAGS_SPILL1 + 1 + (4 * FRAGS_PER_BUCKET)) * 8)
+
+/* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */
+#define MAX_KMALLOC_SZ (128*1024)
+
+struct buflist {
+ u8 *kptr;
+ int len;
+};
+
+#define myMAX_TARGETS (1<<4)
+#define myMAX_LUNS (1<<3)
+#define myMAX_T_MASK (myMAX_TARGETS-1)
+#define myMAX_L_MASK (myMAX_LUNS-1)
+static u8 DevInUse[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
+static u32 DevIosCount[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
+
+static u32 fwReplyBuffer[16];
+static pMPIDefaultReply_t ReplyMsg = NULL;
+
+/* some private forw protos */
+static SGESimple32_t *kbuf_alloc_2_sgl( int bytes, u32 dir, int *frags,
+ struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
+static void kfree_sgl( SGESimple32_t *sgl, dma_addr_t sgl_dma,
+ struct buflist *buflist, MPT_ADAPTER *ioc);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptctl_syscall_down - Down the MPT adapter syscall semaphore.
+ * @ioc: Pointer to MPT adapter
+ * @nonblock: boolean, non-zero if O_NONBLOCK is set
+ *
+ * All of the mptctl commands can potentially sleep, which is illegal
+ * with a spinlock held, thus we perform mutual exclusion here.
+ *
+ * Returns negative errno on error, or zero for success.
+ */
+static inline int
+mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
+{
+ dprintk((KERN_INFO MYNAM "::mpt_syscall_down(%p,%d) called\n", ioc, nonblock));
+
+ if (nonblock) {
+ if (down_trylock(&mptctl_syscall_sem_ioc[ioc->id]))
+ return -EAGAIN;
+ } else {
+ if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id]))
+ return -ERESTARTSYS;
+ }
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This is the callback for any message we have posted. The message itself
+ * will be returned to the message pool when we return from the IRQ
+ *
+ * This runs in irq context so be short and sweet.
+ */
+static int
+mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
+{
+ u8 targ;
+
+ //dprintk((KERN_DEBUG MYNAM ": Got mptctl_reply()!\n"));
+
+ if (req && req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
+ targ = req->u.scsireq.TargetID & myMAX_T_MASK;
+ DevIosCount[targ][0]--;
+ } else if (reply && req && req->u.hdr.Function == MPI_FUNCTION_FW_DOWNLOAD) {
+ // NOTE: Expects/requires non-Turbo reply!
+ dprintk((KERN_INFO MYNAM ": Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n"));
+ memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength));
+ ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer;
+ }
+
+ return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static loff_t
+mptctl_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static ssize_t
+mptctl_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ printk(KERN_ERR MYNAM ": ioctl WRITE not yet supported\n");
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static ssize_t
+mptctl_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * MPT ioctl handler
+ */
+static int
+mpt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct mpt_ioctl_sanity *usanity = (struct mpt_ioctl_sanity *) arg;
+ struct mpt_ioctl_sanity ksanity;
+ int iocnum;
+ unsigned iocnumX;
+ int nonblock = (file->f_flags & O_NONBLOCK);
+ int ret;
+ MPT_ADAPTER *iocp = NULL;
+
+ dprintk((KERN_INFO MYNAM "::mpt_ioctl() called\n"));
+
+ if (copy_from_user(&ksanity, usanity, sizeof(ksanity))) {
+ printk(KERN_ERR "%s::mpt_ioctl() @%d - "
+ "Unable to copy mpt_ioctl_sanity data @ %p\n",
+ __FILE__, __LINE__, (void*)usanity);
+ return -EFAULT;
+ }
+ ret = -ENXIO; /* (-6) No such device or address */
+
+ /* Verify intended MPT adapter */
+ iocnumX = ksanity.iocnum & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
+ (iocp == NULL)) {
+ printk(KERN_ERR "%s::mpt_ioctl() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
+ return ret;
+
+ dprintk((KERN_INFO MYNAM "::mpt_ioctl() - Using %s\n", iocp->name));
+
+ switch(cmd) {
+ case MPTRWPERF:
+ ret = mpt_ioctl_rwperf(arg);
+ break;
+ case MPTRWPERF_CHK:
+ ret = mpt_ioctl_rwperf_status(arg);
+ break;
+ case MPTRWPERF_RESET:
+ ret = mpt_ioctl_rwperf_reset(arg);
+ break;
+ case MPTFWDOWNLOAD:
+ ret = mpt_ioctl_fw_download(arg);
+ break;
+ case MPTSCSICMD:
+ ret = mpt_ioctl_scsi_cmd(arg);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ up(&mptctl_syscall_sem_ioc[iocp->id]);
+
+ return ret;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int mptctl_open(struct inode *inode, struct file *file)
+{
+ /*
+ * Should support multiple management users
+ */
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int mptctl_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_fw_download(unsigned long arg)
+{
+ struct mpt_fw_xfer *ufwdl = (struct mpt_fw_xfer *) arg;
+ struct mpt_fw_xfer kfwdl;
+
+ dprintk((KERN_INFO "mpt_ioctl_fwdl called. mptctl_id = %xh\n", mptctl_id)); //tc
+ if (copy_from_user(&kfwdl, ufwdl, sizeof(struct mpt_fw_xfer))) {
+ printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
+ "Unable to copy mpt_fw_xfer struct @ %p\n",
+ __FILE__, __LINE__, (void*)ufwdl);
+ return -EFAULT;
+ }
+
+ return mpt_ioctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * MPT FW Download
+ */
+static int
+mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen)
+{
+ FWDownload_t *dlmsg;
+ MPT_FRAME_HDR *mf;
+ MPT_ADAPTER *iocp;
+// char *fwbuf;
+// dma_addr_t fwbuf_dma;
+ FWDownloadTCSGE_t *fwVoodoo;
+// SGEAllUnion_t *fwSgl;
+ int ret;
+
+ SGESimple32_t *sgl;
+ SGESimple32_t *sgOut, *sgIn;
+ dma_addr_t sgl_dma;
+ struct buflist *buflist;
+ struct buflist *bl;
+ int numfrags = 0;
+ int maxfrags;
+ int n = 0;
+ u32 sgdir;
+ u32 nib;
+ int fw_bytes_copied = 0;
+ u16 iocstat;
+ int i;
+
+ dprintk((KERN_INFO "mpt_ioctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id));
+
+ dprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf));
+ dprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen));
+ dprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc));
+
+ if ((ioc = mpt_verify_adapter(ioc, &iocp)) < 0) {
+ printk("%s@%d::_ioctl_fwdl - ioc%d not found!\n",
+ __FILE__, __LINE__, ioc);
+ return -ENXIO; /* (-6) No such device or address */
+ }
+
+ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL)
+ return -EAGAIN;
+ dlmsg = (FWDownload_t*) mf;
+ fwVoodoo = (FWDownloadTCSGE_t *) &dlmsg->SGL;
+ sgOut = (SGESimple32_t *) (fwVoodoo + 1);
+
+ /*
+ * Construct f/w download request
+ */
+ dlmsg->ImageType = MPI_FW_DOWNLOAD_ITYPE_FW;
+ dlmsg->Reserved = 0;
+ dlmsg->ChainOffset = 0;
+ dlmsg->Function = MPI_FUNCTION_FW_DOWNLOAD;
+ dlmsg->Reserved1[0] = dlmsg->Reserved1[1] = dlmsg->Reserved1[2] = 0;
+ dlmsg->MsgFlags = 0;
+
+ fwVoodoo->Reserved = 0;
+ fwVoodoo->ContextSize = 0;
+ fwVoodoo->DetailsLength = 12;
+ fwVoodoo->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT;
+ fwVoodoo->Reserved1 = 0;
+ fwVoodoo->ImageOffset = 0;
+ fwVoodoo->ImageSize = cpu_to_le32(fwlen);
+
+ /*
+ * Need to kmalloc area(s) for holding firmware image bytes.
+ * But we need to do it piece meal, using a proper
+ * scatter gather list (with 128kB MAX hunks).
+ *
+ * A practical limit here might be # of sg hunks that fit into
+ * a single IOC request frame; 12 or 8 (see below), so:
+ * For FC9xx: 12 x 128kB == 1.5 mB (max)
+ * For C1030: 8 x 128kB == 1 mB (max)
+ * We could support chaining, but things get ugly(ier:)
+ */
+ sgdir = 0x04000000; /* IOC will READ from sys mem */
+ if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
+ return -ENOMEM;
+
+ /*
+ * We should only need SGL with 2 simple_32bit entries (up to 256 kB)
+ * for FC9xx f/w image, but calculate max number of sge hunks
+ * we can fit into a request frame, and limit ourselves to that.
+ * (currently no chain support)
+ * For FC9xx: (128-12-16)/8 = 12.5 = 12
+ * For C1030: (96-12-16)/8 = 8.5 = 8
+ */
+ maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) / sizeof(SGESimple32_t);
+ if (numfrags > maxfrags) {
+ ret = -EMLINK;
+ goto fwdl_out;
+ }
+
+ dprintk((KERN_INFO "DbG: sgl buffer = %p, sgfrags = %d\n", sgl, numfrags));
+
+ /*
+ * Parse SG list, copying sgl itself,
+ * plus f/w image hunks from user space as we go...
+ */
+ ret = -EFAULT;
+ sgIn = sgl;
+ bl = buflist;
+ for (i=0; i < numfrags; i++) {
+ nib = (sgIn->FlagsLength & 0xF0000000) >> 28;
+ /* skip ignore/chain. */
+ if (nib == 0 || nib == 3) {
+ ;
+ } else if (sgIn->Address) {
+ *sgOut = *sgIn;
+ n++;
+ if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) {
+ printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
+ "Unable to copy f/w buffer hunk#%d @ %p\n",
+ __FILE__, __LINE__, n, (void*)ufwbuf);
+ goto fwdl_out;
+ }
+ fw_bytes_copied += bl->len;
+ }
+ sgIn++;
+ bl++;
+ sgOut++;
+ }
+
+#ifdef MPT_DEBUG
+ {
+ u32 *m = (u32 *)mf;
+ printk(KERN_INFO MYNAM ": F/W download request:\n" KERN_INFO " ");
+ for (i=0; i < 7+numfrags*2; i++)
+ printk(" %08x", le32_to_cpu(m[i]));
+ printk("\n");
+ }
+#endif
+
+ /*
+ * Finally, perform firmware download.
+ */
+ ReplyMsg = NULL;
+ mpt_put_msg_frame(mptctl_id, ioc, mf);
+
+ /*
+ * Wait until the reply has been received
+ */
+ {
+ int foo = 0;
+
+ while (ReplyMsg == NULL) {
+ if (!(foo%1000000)) {
+ dprintk((KERN_INFO "DbG::_do_fwdl: "
+ "In ReplyMsg loop - iteration %d\n",
+ foo)); //tc
+ }
+ ret = -ETIME;
+ if (++foo > 60000000)
+ goto fwdl_out;
+ mb();
+ schedule();
+ barrier();
+ }
+ }
+
+ if (sgl)
+ kfree_sgl(sgl, sgl_dma, buflist, iocp);
+
+ iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK;
+ if (iocstat == MPI_IOCSTATUS_SUCCESS) {
+ printk(KERN_INFO MYNAM ": F/W update successfully sent to %s!\n", iocp->name);
+ return 0;
+ } else if (iocstat == MPI_IOCSTATUS_INVALID_FUNCTION) {
+ printk(KERN_WARNING MYNAM ": ?Hmmm... %s says it doesn't support F/W download!?!\n",
+ iocp->name);
+ printk(KERN_WARNING MYNAM ": (time to go bang on somebodies door)\n");
+ return -EBADRQC;
+ } else if (iocstat == MPI_IOCSTATUS_BUSY) {
+ printk(KERN_WARNING MYNAM ": Warning! %s says: IOC_BUSY!\n", iocp->name);
+ printk(KERN_WARNING MYNAM ": (try again later?)\n");
+ return -EBUSY;
+ } else {
+ printk(KERN_WARNING MYNAM "::ioctl_fwdl() ERROR! %s returned [bad] status = %04xh\n",
+ iocp->name, iocstat);
+ printk(KERN_WARNING MYNAM ": (bad VooDoo)\n");
+ return -ENOMSG;
+ }
+ return 0;
+
+fwdl_out:
+ kfree_sgl(sgl, sgl_dma, buflist, iocp);
+ return ret;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * NEW rwperf (read/write performance) stuff starts here...
+ */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static SGESimple32_t *
+kbuf_alloc_2_sgl(int bytes, u32 sgdir, int *frags,
+ struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc)
+{
+ SGESimple32_t *sglbuf = NULL;
+ struct buflist *buflist = NULL;
+ int numfrags = 0;
+ int fragcnt = 0;
+ int alloc_sz = MIN(bytes,MAX_KMALLOC_SZ); // avoid kernel warning msg!
+ int bytes_allocd = 0;
+ int this_alloc;
+ SGESimple32_t *sgl;
+ u32 pa; // phys addr
+ SGEChain32_t *last_chain = NULL;
+ SGEChain32_t *old_chain = NULL;
+ int chaincnt = 0;
+ int i, buflist_ent;
+ int sg_spill = MAX_FRAGS_SPILL1;
+ int dir;
+
+ *frags = 0;
+ *blp = NULL;
+ i = MAX_SGL_BYTES / 8;
+ buflist = kmalloc(i, GFP_USER);
+ if (buflist == NULL)
+ return NULL;
+ memset(buflist, 0, i);
+ buflist_ent = 0;
+
+ sglbuf = pci_alloc_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf_dma);
+ if (sglbuf == NULL)
+ goto free_and_fail;
+
+ if (sgdir & 0x04000000)
+ dir = PCI_DMA_TODEVICE;
+ else
+ dir = PCI_DMA_FROMDEVICE;
+
+ sgl = sglbuf;
+ while (bytes_allocd < bytes) {
+ this_alloc = MIN(alloc_sz, bytes-bytes_allocd);
+ buflist[buflist_ent].len = this_alloc;
+ buflist[buflist_ent].kptr = pci_alloc_consistent(ioc->pcidev,
+ this_alloc,
+ &pa);
+ if (buflist[buflist_ent].kptr == NULL) {
+ alloc_sz = alloc_sz / 2;
+ if (alloc_sz == 0) {
+ printk(KERN_WARNING MYNAM "-SG: No can do - "
+ "not enough memory! :-(\n");
+ printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n",
+ numfrags);
+ goto free_and_fail;
+ }
+ continue;
+ } else {
+ dma_addr_t dma_addr;
+
+ bytes_allocd += this_alloc;
+
+ /* Write one SIMPLE sge */
+ sgl->FlagsLength = cpu_to_le32(0x10000000|sgdir|this_alloc);
+ dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir);
+ sgl->Address = cpu_to_le32(dma_addr);
+
+ fragcnt++;
+ numfrags++;
+ sgl++;
+ buflist_ent++;
+ }
+
+ if (bytes_allocd >= bytes)
+ break;
+
+ /* Need to chain? */
+ if (fragcnt == sg_spill) {
+ dma_addr_t chain_link;
+
+ if (last_chain != NULL)
+ last_chain->NextChainOffset = 0x1E;
+
+ fragcnt = 0;
+ sg_spill = MAX_FRAGS_SPILL2;
+
+ /* fixup previous SIMPLE sge */
+ sgl[-1].FlagsLength |= cpu_to_le32(0x80000000);
+
+ chain_link = (*sglbuf_dma) +
+ ((u8 *)(sgl+1) - (u8 *)sglbuf);
+
+ /* Write one CHAIN sge */
+ sgl->FlagsLength = cpu_to_le32(0x30000080);
+ sgl->Address = cpu_to_le32(chain_link);
+
+ old_chain = last_chain;
+ last_chain = (SGEChain32_t*)sgl;
+ chaincnt++;
+ numfrags++;
+ sgl++;
+ }
+
+ /* overflow check... */
+ if (numfrags*8 > MAX_SGL_BYTES) {
+ /* GRRRRR... */
+ printk(KERN_WARNING MYNAM "-SG: No can do - "
+ "too many SG frags! :-(\n");
+ printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n",
+ numfrags);
+ goto free_and_fail;
+ }
+ }
+
+ /* Last sge fixup: set LE+eol+eob bits */
+ sgl[-1].FlagsLength |= cpu_to_le32(0xC1000000);
+
+ /* Chain fixup needed? */
+ if (last_chain != NULL && fragcnt < 16)
+ last_chain->Length = cpu_to_le16(fragcnt * 8);
+
+ *frags = numfrags;
+ *blp = buflist;
+
+ dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
+ "%d SG frags generated! (%d CHAIN%s)\n",
+ numfrags, chaincnt, chaincnt>1?"s":""));
+
+ dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
+ "last (big) alloc_sz=%d\n",
+ alloc_sz));
+
+ return sglbuf;
+
+free_and_fail:
+ if (sglbuf != NULL) {
+ int i;
+
+ for (i = 0; i < numfrags; i++) {
+ dma_addr_t dma_addr;
+ u8 *kptr;
+ int len;
+
+ if ((sglbuf[i].FlagsLength >> 24) == 0x30)
+ continue;
+
+ dma_addr = le32_to_cpu(sglbuf[i].Address);
+ kptr = buflist[i].kptr;
+ len = buflist[i].len;
+
+ pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
+ }
+ pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf, *sglbuf_dma);
+ }
+ kfree(buflist);
+ return NULL;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static void
+kfree_sgl(SGESimple32_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc)
+{
+ SGESimple32_t *sg = sgl;
+ struct buflist *bl = buflist;
+ u32 nib;
+ int dir;
+ int n = 0;
+
+ if (sg->FlagsLength & 0x04000000)
+ dir = PCI_DMA_TODEVICE;
+ else
+ dir = PCI_DMA_FROMDEVICE;
+
+ nib = (sg->FlagsLength & 0xF0000000) >> 28;
+ while (! (nib & 0x4)) { /* eob */
+ /* skip ignore/chain. */
+ if (nib == 0 || nib == 3) {
+ ;
+ } else if (sg->Address) {
+ dma_addr_t dma_addr;
+ void *kptr;
+ int len;
+
+ dma_addr = le32_to_cpu(sg->Address);
+ kptr = bl->kptr;
+ len = bl->len;
+ pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
+ pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
+ n++;
+ }
+ sg++;
+ bl++;
+ nib = (sg->FlagsLength & 0xF0000000) >> 28;
+ }
+
+ /* we're at eob! */
+ if (sg->Address) {
+ dma_addr_t dma_addr;
+ void *kptr;
+ int len;
+
+ dma_addr = le32_to_cpu(sg->Address);
+ kptr = bl->kptr;
+ len = bl->len;
+ pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
+ pci_free_consistent(ioc->pcidev, len, kptr, dma_addr);
+ n++;
+ }
+
+ pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sgl, sgl_dma);
+ kfree(buflist);
+ dprintk((KERN_INFO MYNAM "-SG: Free'd 1 SGL buf + %d kbufs!\n", n));
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_rwperf_init(struct mpt_raw_r_w *dest, unsigned long src,
+ char *caller, MPT_ADAPTER **iocpp)
+{
+ char *myname = "_rwperf_init()";
+ int ioc;
+
+ /* get copy of structure passed from user space */
+ if (copy_from_user(dest, (void*)src, sizeof(*dest))) {
+ printk(KERN_ERR MYNAM "::%s() @%d - Can't copy mpt_raw_r_w data @ %p\n",
+ myname, __LINE__, (void*)src);
+ return -EFAULT; /* (-14) Bad address */
+ } else {
+ dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{ioc,targ,qd,iters,nblks}"
+ ": %d %d %d %d %d\n",
+ dest->iocnum, dest->target,
+ (int)dest->qdepth, dest->iters, dest->nblks ));
+ dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{cache,skip,range,rdwr,seqran}"
+ ": %d %d %d %d %d\n",
+ dest->cache_sz, dest->skip, dest->range,
+ dest->rdwr, dest->seqran ));
+
+ /* Get the MPT adapter id. */
+ if ((ioc = mpt_verify_adapter(dest->iocnum, iocpp)) < 0) {
+ printk(KERN_ERR MYNAM "::%s() @%d - ioc%d not found!\n",
+ myname, __LINE__, dest->iocnum);
+ return -ENXIO; /* (-6) No such device or address */
+ } else {
+ dprintk((MYNAM "-perf: %s using mpt/ioc%x, target %02xh\n",
+ caller, dest->iocnum, dest->target));
+ }
+ }
+
+ return ioc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/* Treat first N blocks of disk as sacred! */
+#define SACRED_BLOCKS 100
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_rwperf(unsigned long arg)
+{
+ struct mpt_raw_r_w kPerfInfo;
+ /* NOTE: local copy, on stack==KERNEL_SPACE! */
+ u8 target, targetM;
+ u8 lun, lunM;
+ u8 scsiop;
+ int qdepth;
+ int iters;
+ int cache_sz;
+ u32 xferbytes;
+ u32 scsidir;
+ u32 qtag;
+ u32 scsictl;
+ u32 sgdir;
+ u32 blkno;
+ u32 sbphys;
+ SGESimple32_t *sgl;
+ dma_addr_t sgl_dma;
+ struct buflist *buflist;
+ SGESimple32_t *sgOut, *sgIn;
+ int numfrags;
+ u32 *msg;
+ int i;
+ int ioc;
+ MPT_FRAME_HDR *mf;
+ MPT_ADAPTER *iocp;
+ int sgfragcpycnt;
+ int blklo, blkhi;
+ u8 nextchainoffset;
+ u8 *SenseBuf;
+ dma_addr_t SenseBufDMA;
+ char *myname = "_rwperf()";
+
+ dprintk((KERN_INFO "%s - starting...\n", myname));
+
+ /* Validate target device */
+ if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
+ return ioc;
+
+ /* Allocate DMA'able memory for the sense buffer. */
+ SenseBuf = pci_alloc_consistent(iocp->pcidev, 256, &SenseBufDMA);
+
+ /* set perf parameters from input */
+ target = kPerfInfo.target & 0x0FF;
+ targetM = target & myMAX_T_MASK;
+ lun = kPerfInfo.lun & 0x1F; // LUN=31 max
+ lunM = lun & myMAX_L_MASK;
+ qdepth = kPerfInfo.qdepth;
+ iters = kPerfInfo.iters;
+ xferbytes = ((u32)kPerfInfo.nblks)<<9;
+
+ DevInUse[targetM][lunM] = 1;
+ DevIosCount[targetM][lunM] = 0;
+
+ cache_sz = kPerfInfo.cache_sz * 1024; // CacheSz in kB!
+
+ /* ToDo: */
+ /* get capacity (?) */
+
+
+ // pre-build, one time, everything we can for speed in the loops below...
+
+ scsiop = 0x28; // default to SCSI READ!
+ scsidir = MPI_SCSIIO_CONTROL_READ; // DATA IN (host<--ioc<--dev)
+ // 02000000
+ qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; // 00000000
+
+ if (xferbytes == 0) {
+ // Do 0-byte READ!!!
+ // IMPORTANT! Need to set no SCSI DIR for this!
+ scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
+ }
+
+ scsictl = scsidir | qtag;
+
+ /*
+ * Set sgdir for DMA transfer.
+ */
+// sgdir = 0x04000000; // SCSI WRITE
+ sgdir = 0x00000000; // SCSI READ
+
+ if ((sgl = kbuf_alloc_2_sgl(MAX(512,xferbytes), sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
+ return -ENOMEM;
+
+ sgfragcpycnt = MIN(10,numfrags);
+ nextchainoffset = 0;
+ if (numfrags > 10)
+ nextchainoffset = 0x1E;
+
+ sbphys = SenseBufDMA;
+
+ rwperf_reset = 0;
+
+// do { // target-loop
+
+ blkno = SACRED_BLOCKS; // Treat first N blocks as sacred!
+ // FIXME! Skip option
+ blklo = blkno;
+ blkhi = blkno;
+
+ do { // inner-loop
+
+ while ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
+ mb();
+ schedule();
+ barrier();
+ }
+ msg = (u32*)mf;
+
+ /* Start piecing the SCSIIORequest together */
+ msg[0] = 0x00000000 | nextchainoffset<<16 | target;
+ msg[1] = 0x0000FF0A; // 255 sense bytes, 10-byte CDB!
+ msg[3] = lun << 8;
+ msg[4] = 0;
+ msg[5] = scsictl;
+
+ // 16 bytes of CDB @ msg[6,7,8,9] are below...
+
+ msg[6] = ( ((blkno & 0xFF000000) >> 8)
+ | ((blkno & 0x00FF0000) << 8)
+ | scsiop );
+ msg[7] = ( (((u32)kPerfInfo.nblks & 0x0000FF00) << 16)
+ | ((blkno & 0x000000FF) << 8)
+ | ((blkno & 0x0000FF00) >> 8) );
+ msg[8] = (kPerfInfo.nblks & 0x00FF);
+ msg[9] = 0;
+
+ msg[10] = xferbytes;
+
+// msg[11] = 0xD0000100;
+// msg[12] = sbphys;
+// msg[13] = 0;
+ msg[11] = sbphys;
+
+ // Copy the SGL...
+ if (xferbytes) {
+ sgOut = (SGESimple32_t*)&msg[12];
+ sgIn = sgl;
+ for (i=0; i < sgfragcpycnt; i++)
+ *sgOut++ = *sgIn++;
+ }
+
+ // fubar! QueueDepth issue!!!
+ while ( !rwperf_reset
+ && (DevIosCount[targetM][lunM] >= MIN(qdepth,64)) )
+ {
+ mb();
+ schedule();
+ barrier();
+ }
+
+// blkno += kPerfInfo.nblks;
+// EXP Stuff!
+// Try optimizing to certain cache size for the target!
+// by keeping blkno within cache range if at all possible
+#if 0
+ if ( cache_sz
+ && ((2 * kPerfInfo.nblks) <= (cache_sz>>9))
+ && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
+ blkno = SACRED_BLOCKS;
+ else
+ blkno += kPerfInfo.nblks;
+#endif
+// Ok, cheat!
+ if (cache_sz && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
+ blkno = SACRED_BLOCKS;
+ else
+ blkno += kPerfInfo.nblks;
+
+ if (blkno > blkhi)
+ blkhi = blkno;
+
+ DevIosCount[targetM][lunM]++;
+
+ /*
+ * Finally, post the request
+ */
+ mpt_put_msg_frame(mptctl_id, ioc, mf);
+
+
+ /* let linux breath! */
+ mb();
+ schedule();
+ barrier();
+
+ //dprintk((KERN_DEBUG MYNAM "-perf: inner-loop, cnt=%d\n", iters));
+
+ } while ((--iters > 0) && !rwperf_reset);
+
+ dprintk((KERN_INFO MYNAM "-perf: DbG: blklo=%d, blkhi=%d\n", blklo, blkhi));
+ dprintk((KERN_INFO MYNAM "-perf: target-loop, thisTarget=%d\n", target));
+
+// // TEMPORARY!
+// target = 0;
+
+// } while (target);
+
+
+ if (DevIosCount[targetM][lunM]) {
+ dprintk((KERN_INFO " DbG: DevIosCount[%d][%d]=%d\n",
+ targetM, lunM, DevIosCount[targetM][lunM]));
+ }
+
+ while (DevIosCount[targetM][lunM]) {
+ //dprintk((KERN_DEBUG " DbG: Waiting... DevIosCount[%d][%d]=%d\n",
+ // targetM, lunM, DevIosCount[targetM][lunM]));
+ mb();
+ schedule();
+ barrier();
+ }
+ DevInUse[targetM][lunM] = 0;
+
+ pci_free_consistent(iocp->pcidev, 256, SenseBuf, SenseBufDMA);
+
+ if (sgl)
+ kfree_sgl(sgl, sgl_dma, buflist, iocp);
+
+ dprintk((KERN_INFO " *** done ***\n"));
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_rwperf_status(unsigned long arg)
+{
+ struct mpt_raw_r_w kPerfInfo;
+ /* NOTE: local copy, on stack==KERNEL_SPACE! */
+ MPT_ADAPTER *iocp;
+ int ioc;
+// u8 targ;
+// u8 lun;
+ int T, L;
+ char *myname = "_rwperf_status()";
+
+
+ dprintk((KERN_INFO "%s - starting...\n", myname));
+
+ /* Get a pointer to the MPT adapter. */
+ if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
+ return ioc;
+
+ /* set perf parameters from input */
+// targ = kPerfInfo.target & 0xFF;
+// lun = kPerfInfo.lun & 0x1F;
+
+ for (T=0; T < myMAX_TARGETS; T++)
+ for (L=0; L < myMAX_LUNS; L++)
+ if (DevIosCount[T][L]) {
+ printk(KERN_INFO "%s: ioc%d->00:%02x:%02x"
+ ", IosCnt=%d\n",
+ myname, ioc, T, L, DevIosCount[T][L] );
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_rwperf_reset(unsigned long arg)
+{
+ struct mpt_raw_r_w kPerfInfo;
+ /* NOTE: local copy, on stack==KERNEL_SPACE! */
+ MPT_ADAPTER *iocp;
+ int ioc;
+// u8 targ;
+// u8 lun;
+ int T, L;
+ int i;
+ char *myname = "_rwperf_reset()";
+
+ dprintk((KERN_INFO "%s - starting...\n", myname));
+
+ /* Get MPT adapter id. */
+ if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
+ return ioc;
+
+ /* set perf parameters from input */
+// targ = kPerfInfo.target & 0xFF;
+// lun = kPerfInfo.lun & 0x1F;
+
+ rwperf_reset = 1;
+ for (i=0; i < 1000000; i++) {
+ mb();
+ schedule();
+ barrier();
+ }
+ rwperf_reset = 0;
+
+ for (T=0; T < myMAX_TARGETS; T++)
+ for (L=0; L < myMAX_LUNS; L++)
+ if (DevIosCount[T][L]) {
+ printk(KERN_INFO "%s: ioc%d->00:%02x:%02x, "
+ "IosCnt RESET! (from %d to 0)\n",
+ myname, ioc, T, L, DevIosCount[T][L] );
+ DevIosCount[T][L] = 0;
+ DevInUse[T][L] = 0;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mpt_ioctl_scsi_cmd(unsigned long arg)
+{
+ return -ENOSYS;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+static struct file_operations mptctl_fops = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51)
+ owner: THIS_MODULE,
+#endif
+ llseek: mptctl_llseek,
+ read: mptctl_read,
+ write: mptctl_write,
+ ioctl: mpt_ioctl,
+ open: mptctl_open,
+ release: mptctl_release,
+};
+
+static struct miscdevice mptctl_miscdev = {
+ MPT_MINOR,
+ MYNAM,
+ &mptctl_fops
+};
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
+
+/* The dynamic ioctl32 compat. registry only exists in >2.3.x sparc64 kernels */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
+extern int register_ioctl32_conversion(unsigned int cmd,
+ int (*handler)(unsigned int,
+ unsigned int,
+ unsigned long,
+ struct file *));
+int unregister_ioctl32_conversion(unsigned int cmd);
+
+struct mpt_fw_xfer32 {
+ unsigned int iocnum;
+ unsigned int fwlen;
+ u32 bufp;
+};
+
+#define MPTFWDOWNLOAD32 _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer32)
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd,
+ unsigned long arg, struct file *filp)
+{
+ struct mpt_fw_xfer32 kfw32;
+ struct mpt_fw_xfer kfw;
+ MPT_ADAPTER *iocp = NULL;
+ int iocnum, iocnumX;
+ int nonblock = (filp->f_flags & O_NONBLOCK);
+ int ret;
+
+ dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n"));
+
+ if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32)))
+ return -EFAULT;
+
+ /* Verify intended MPT adapter */
+ iocnumX = kfw32.iocnum & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
+ (iocp == NULL)) {
+ printk(KERN_ERR MYNAM "::sparc32_mptfwxfer_ioctl @%d - ioc%d not found!\n",
+ __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
+ return ret;
+
+ kfw.iocnum = iocnum;
+ kfw.fwlen = kfw32.fwlen;
+ kfw.bufp = (void *)(unsigned long)kfw32.bufp;
+
+ ret = mpt_ioctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
+
+ up(&mptctl_syscall_sem_ioc[iocp->id]);
+
+ return ret;
+}
+
+#if 0 /* { */
+static int
+sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd,
+ unsigned long arg, struct file *filp)
+{
+ struct mpt_fw_xfer32 kfw32;
+ struct mpt_fw_xfer kfw;
+ mm_segment_t old_fs;
+ int ret;
+
+ dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n"));
+
+ if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32)))
+ return -EFAULT;
+
+ /* Verify intended MPT adapter */
+ iocnumX = kfw32.iocnum & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
+ (iocp == NULL)) {
+ printk(KERN_ERR MYNAM "::sparc32_mptfwxfer_ioctl @%d - ioc%d not found!\n",
+ __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
+ return ret;
+
+ kfw.iocnum = iocnum;
+ kfw.fwlen = kfw32.fwlen;
+ kfw.bufp = (void *)(unsigned long)kfw32.bufp;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_ioctl(fd, MPTFWDOWNLOAD, (unsigned long)&kfw);
+ set_fs(old_fs);
+
+ up(&mptctl_syscall_sem_ioc[iocp->id]);
+
+ return ret;
+}
+#endif /* #if 0 } */
+
+#endif /*} linux >= 2.3.x */
+#endif /*} sparc */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+int __init mptctl_init(void)
+{
+ int err;
+ int i;
+ int where = 1;
+
+ show_mptmod_ver(my_NAME, my_VERSION);
+
+ for (i=0; i<MPT_MAX_ADAPTERS; i++) {
+ sema_init(&mptctl_syscall_sem_ioc[i], 1);
+ }
+
+#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
+ err = register_ioctl32_conversion(MPTRWPERF, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTRWPERF_CHK, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTRWPERF_RESET, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTFWDOWNLOAD32,
+ sparc32_mptfwxfer_ioctl);
+ if (++where && err) goto out_fail;
+#endif /*} linux >= 2.3.x */
+#endif /*} sparc */
+
+ if (misc_register(&mptctl_miscdev) == -1) {
+ printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR);
+ err = -EBUSY;
+ goto out_fail;
+ }
+ printk(KERN_INFO MYNAM ": Registered with Fusion MPT base driver\n");
+ printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n",
+ mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor);
+
+ /*
+ * Install our handler
+ */
+ ++where;
+ if ((mptctl_id = mpt_register(mptctl_reply, MPTCTL_DRIVER)) < 0) {
+ printk(KERN_ERR MYNAM ": ERROR: Failed to register with Fusion MPT base driver\n");
+ misc_deregister(&mptctl_miscdev);
+ err = -EBUSY;
+ goto out_fail;
+ }
+
+ return 0;
+
+out_fail:
+
+#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
+ printk(KERN_ERR MYNAM ": ERROR: Failed to register ioctl32_conversion!"
+ " (%d:err=%d)\n", where, err);
+ unregister_ioctl32_conversion(MPTRWPERF);
+ unregister_ioctl32_conversion(MPTRWPERF_CHK);
+ unregister_ioctl32_conversion(MPTRWPERF_RESET);
+ unregister_ioctl32_conversion(MPTFWDOWNLOAD32);
+#endif /*} linux >= 2.3.x */
+#endif /*} sparc */
+
+ return err;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+void mptctl_exit(void)
+{
+ misc_deregister(&mptctl_miscdev);
+ printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n",
+ mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor);
+ printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n");
+
+ mpt_deregister(mptctl_id);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+module_init(mptctl_init);
+module_exit(mptctl_exit);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)