📄 mptctl.c
字号:
ioctl->status &= ~MPT_IOCTL_STATUS_DID_IOCRESET; break; case MPT_IOC_PRE_RESET: default: break; } return 1;}/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * MPT ioctl handler * cmd - specify the particular IOCTL command to be issued * arg - data specific to the command. Must not be null. */static long__mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ mpt_ioctl_header __user *uhdr = (void __user *) arg; mpt_ioctl_header khdr; int iocnum; unsigned iocnumX; int nonblock = (file->f_flags & O_NONBLOCK); int ret; MPT_ADAPTER *iocp = NULL; dctlprintk(("mptctl_ioctl() called\n")); if (copy_from_user(&khdr, uhdr, sizeof(khdr))) { printk(KERN_ERR "%s::mptctl_ioctl() @%d - " "Unable to copy mpt_ioctl_header data @ %p\n", __FILE__, __LINE__, uhdr); return -EFAULT; } ret = -ENXIO; /* (-6) No such device or address */ /* Verify intended MPT adapter - set iocnum and the adapter * pointer (iocp) */ iocnumX = khdr.iocnum & 0xFF; if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || (iocp == NULL)) { dctlprintk((KERN_ERR "%s::mptctl_ioctl() @%d - ioc%d not found!\n", __FILE__, __LINE__, iocnumX)); return -ENODEV; } if (!iocp->active) { printk(KERN_ERR "%s::mptctl_ioctl() @%d - Controller disabled.\n", __FILE__, __LINE__); return -EFAULT; } /* Handle those commands that are just returning * information stored in the driver. * These commands should never time out and are unaffected * by TM and FW reloads. */ if ((cmd & ~IOCSIZE_MASK) == (MPTIOCINFO & ~IOCSIZE_MASK)) { return mptctl_getiocinfo(arg, _IOC_SIZE(cmd)); } else if (cmd == MPTTARGETINFO) { return mptctl_gettargetinfo(arg); } else if (cmd == MPTTEST) { return mptctl_readtest(arg); } else if (cmd == MPTEVENTQUERY) { return mptctl_eventquery(arg); } else if (cmd == MPTEVENTENABLE) { return mptctl_eventenable(arg); } else if (cmd == MPTEVENTREPORT) { return mptctl_eventreport(arg); } else if (cmd == MPTFWREPLACE) { return mptctl_replace_fw(arg); } /* All of these commands require an interrupt or * are unknown/illegal. */ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) return ret; dctlprintk((MYIOC_s_INFO_FMT ": mptctl_ioctl()\n", iocp->name)); if (cmd == MPTFWDOWNLOAD) ret = mptctl_fw_download(arg); else if (cmd == MPTCOMMAND) ret = mptctl_mpt_command(arg); else if (cmd == MPTHARDRESET) ret = mptctl_do_reset(arg); else if ((cmd & ~IOCSIZE_MASK) == (HP_GETHOSTINFO & ~IOCSIZE_MASK)) ret = mptctl_hp_hostinfo(arg, _IOC_SIZE(cmd)); else if (cmd == HP_GETTARGETINFO) ret = mptctl_hp_targetinfo(arg); else ret = -EINVAL; up(&iocp->ioctl->sem_ioc); return ret;}static longmptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ long ret; lock_kernel(); ret = __mptctl_ioctl(file, cmd, arg); unlock_kernel(); return ret;}static int mptctl_do_reset(unsigned long arg){ struct mpt_ioctl_diag_reset __user *urinfo = (void __user *) arg; struct mpt_ioctl_diag_reset krinfo; MPT_ADAPTER *iocp; dctlprintk((KERN_INFO "mptctl_do_reset called.\n")); if (copy_from_user(&krinfo, urinfo, sizeof(struct mpt_ioctl_diag_reset))) { printk(KERN_ERR "%s@%d::mptctl_do_reset - " "Unable to copy mpt_ioctl_diag_reset struct @ %p\n", __FILE__, __LINE__, urinfo); return -EFAULT; } if (mpt_verify_adapter(krinfo.hdr.iocnum, &iocp) < 0) { dctlprintk((KERN_ERR "%s@%d::mptctl_do_reset - ioc%d not found!\n", __FILE__, __LINE__, krinfo.hdr.iocnum)); return -ENODEV; /* (-6) No such device or address */ } if (mpt_HardResetHandler(iocp, CAN_SLEEP) != 0) { printk (KERN_ERR "%s@%d::mptctl_do_reset - reset failed.\n", __FILE__, __LINE__); return -1; } return 0;}/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * MPT FW download function. Cast the arg into the mpt_fw_xfer structure. * This structure contains: iocnum, firmware length (bytes), * pointer to user space memory where the fw image is stored. * * Outputs: None. * Return: 0 if successful * -EFAULT if data unavailable * -ENXIO if no such device * -EAGAIN if resource problem * -ENOMEM if no memory for SGE * -EMLINK if too many chain buffers required * -EBADRQC if adapter does not support FW download * -EBUSY if adapter is busy * -ENOMSG if FW upload returned bad status */static intmptctl_fw_download(unsigned long arg){ struct mpt_fw_xfer __user *ufwdl = (void __user *) arg; struct mpt_fw_xfer kfwdl; dctlprintk((KERN_INFO "mptctl_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__, ufwdl); return -EFAULT; } return mptctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen);}/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * FW Download engine. * Outputs: None. * Return: 0 if successful * -EFAULT if data unavailable * -ENXIO if no such device * -EAGAIN if resource problem * -ENOMEM if no memory for SGE * -EMLINK if too many chain buffers required * -EBADRQC if adapter does not support FW download * -EBUSY if adapter is busy * -ENOMSG if FW upload returned bad status */static intmptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen){ FWDownload_t *dlmsg; MPT_FRAME_HDR *mf; MPT_ADAPTER *iocp; FWDownloadTCSGE_t *ptsge; MptSge_t *sgl, *sgIn; char *sgOut; struct buflist *buflist; struct buflist *bl; dma_addr_t sgl_dma; int ret; int numfrags = 0; int maxfrags; int n = 0; u32 sgdir; u32 nib; int fw_bytes_copied = 0; int i; int sge_offset = 0; u16 iocstat; pFWDownloadReply_t ReplyMsg = NULL; dctlprintk((KERN_INFO "mptctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id)); dctlprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf)); dctlprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen)); dctlprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc)); if ((ioc = mpt_verify_adapter(ioc, &iocp)) < 0) { dctlprintk(("%s@%d::_ioctl_fwdl - ioc%d not found!\n", __FILE__, __LINE__, ioc)); return -ENODEV; /* (-6) No such device or address */ } /* Valid device. Get a message frame and construct the FW download message. */ if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) return -EAGAIN; dlmsg = (FWDownload_t*) mf; ptsge = (FWDownloadTCSGE_t *) &dlmsg->SGL; sgOut = (char *) (ptsge + 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; /* Set up the Transaction SGE. */ ptsge->Reserved = 0; ptsge->ContextSize = 0; ptsge->DetailsLength = 12; ptsge->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT; ptsge->Reserved_0100_Checksum = 0; ptsge->ImageOffset = 0; ptsge->ImageSize = cpu_to_le32(fwlen); /* Add the SGL */ /* * 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:) * * Set the sge_offset to the start of the sgl (bytes). */ sgdir = 0x04000000; /* IOC will READ from sys mem */ sge_offset = sizeof(MPIHeader_t) + sizeof(FWDownloadTCSGE_t); if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, sge_offset, &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) * maxfrags = (Request Size - FWdownload Size ) / Size of 32 bit SGE * Request maxfrags * 128 12 * 96 8 * 64 4 */ maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) / (sizeof(dma_addr_t) + sizeof(u32)); if (numfrags > maxfrags) { ret = -EMLINK; goto fwdl_out; } dctlprintk((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++) { /* Get the SGE type: 0 - TCSGE, 3 - Chain, 1 - Simple SGE * Skip everything but Simple. If simple, copy from * user space into kernel space. * Note: we should not have anything but Simple as * Chain SGE are illegal. */ nib = (sgIn->FlagsLength & 0x30000000) >> 28; if (nib == 0 || nib == 3) { ; } else if (sgIn->Address) { mpt_add_sge(sgOut, sgIn->FlagsLength, sgIn->Address); 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, ufwbuf); goto fwdl_out; } fw_bytes_copied += bl->len; } sgIn++; bl++; sgOut += (sizeof(dma_addr_t) + sizeof(u32)); }#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. */ iocp->ioctl->wait_done = 0; mpt_put_msg_frame(mptctl_id, iocp, mf); /* Now wait for the command to complete */ ret = wait_event_interruptible_timeout(mptctl_wait, iocp->ioctl->wait_done == 1, HZ*60); if(ret <=0 && (iocp->ioctl->wait_done != 1 )) { /* Now we need to reset the board */ mptctl_timeout_expired(iocp->ioctl); ret = -ENODATA; goto fwdl_out; } if (sgl) kfree_sgl(sgl, sgl_dma, buflist, iocp); ReplyMsg = (pFWDownloadReply_t)iocp->ioctl->ReplyFrame; 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;}/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * SGE Allocation routine * * Inputs: bytes - number of bytes to be transferred * sgdir - data direction * sge_offset - offset (in bytes) from the start of the request * frame to the first SGE * ioc - pointer to the mptadapter * Outputs: frags - number of scatter gather elements * blp - point to the buflist pointer * sglbuf_dma - pointer to the (dma) sgl * Returns: Null if failes * pointer to the (virtual) sgl if successful. */static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags, struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc){ MptSge_t *sglbuf = NULL; /* pointer to array of SGE */ /* and chain buffers */ struct buflist *buflist = NULL; /* kernel routine */ MptSge_t *sgl; 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; dma_addr_t pa; // phys addr int i, buflist_ent; int sg_spill = MAX_FRAGS_SPILL1; int dir; /* initialization */ *frags = 0; *blp = NULL; /* Allocate and initialize an array of kernel * structures for the SG elements. */ i = MAX_SGL_BYTES / 8; buflist = kmalloc(i, GFP_USER); if (buflist == NULL) return NULL; memset(buflist, 0, i); buflist_ent = 0; /* Allocate a single block of memory to store the sg elements and * the chain buffers. The calling routine is responsible for * copying the data in this array into the correct place in the * request and chain buffers. */ 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; /* At start: * sgl = sglbuf = point to beginning of sg buffer * buflist_ent = 0 = first kernel structure * sg_spill = number of SGE that can be written before the first * chain element. * */ sgl = sglbuf; sg_spill = ((ioc->req_sz - sge_offset)/(sizeof(dma_addr_t) + sizeof(u32))) - 1; 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -