#include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include "../mpt3sas_base.h" #include "../mpt3sas_ctl.h" #include "inc/diag_mpt.h" #include "inc/sasdiag.h" #include "inc/diag_ioctl.h" #include "inc/diag_debug.h" /* * Extern Declaration */ extern int sasIocPhyErrLogClear(int ioc_num, int mapi); extern int sasIocSinglePhyErrLogClear(int ioc_num, int phy_num); // Added by Sean P. extern int sasdiag_getIocPhyLinkSpeed(ioc_phy_speed* phy_speed); // Added by Sean P. extern int sasdiag_iocGetAttachedDevInfo(ioc_phy_att_dev_info* att_dev_info); //Added by Sean P. extern int sasdiag_getIocTemperature(ioc_temp_info* tempInfo); extern int sasdiag_getIocPhyErrInfo(U8 ioc_num, U8 phy_num, ioc_phy_err *err); extern int sasdiag_sendBlockCmd(U8 ioc_num, U8 cmdCode, U16 devHandle, U16 ioSizeInBlocks, U32 lba, void *inBuf, U32 inSize, void *outBuf, U32 outSize, U8 pat, U32 loopCount); extern int sasdiag_sendScsiCmd(U8 ioc_num, U8 cmdCode, U16 devHandle, U8 pat, U32 loopCount, void *data); extern int sasdiag_gpioReadWrite(U8 action, U8 ioc_num, U8 gpio_num, U16 *val); extern int sasdiag_iocRegReadWrite(U8 ioc_num, U8 regCtrl, U32 regOffset, U32 *val); extern bool isIocNumValid(U8 ioc_num); extern int sasdiag_getIocWWID(U8 iocNum, U32* wwid); extern int sasdiag_getIocNvdataVersionDefault(U8 iocNum, U32* nvdataVersion); extern int sasdiag_getIocVPD(U8 iocNum, U8* VPDdata); extern int sasdiag_doPhyCfg(U8 ioc_num, U8 action, phy_ctrl* phyctrl); static long sasdiag_gpioCtrl(gpio_led_ctrl *karg, void __user *arg) { int ret = 0; switch (karg->action) { case GPIO_READ: case GPIO_WRITE: ret = sasdiag_gpioReadWrite(karg->action, karg->g_ctrl.ioc_num, karg->g_ctrl.gpio.gpio_num, &karg->g_ctrl.gpio.gpio_val); break; default: DBG_PRINT(DBG_ERR, "gpioCtrl: Unsupported gpio ctrl action x%02x\n", karg->action); return -EINVAL; } // switch if (karg->action == GPIO_READ) { if (copy_to_user(arg + offsetof(gpio_led_ctrl, g_ctrl), &karg->g_ctrl, sizeof(gpio_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } } return ret; } static long sasdiag_iocRegCtrl(reg_ctrl *karg, void __user *arg) { switch (karg->action) { case REG_CTRL_READ: case REG_CTRL_WRITE: sasdiag_iocRegReadWrite(karg->ioc_num, karg->action, karg->regOffset, &karg->val); break; default: DBG_PRINT(DBG_ERR, "iocRegCtrl: Unsupported iocRegControl action 0x%02x\n", karg->action); return -EINVAL; } // switch if (karg->action == REG_CTRL_READ) { if (copy_to_user(arg + offsetof(reg_ctrl, val), &karg->val, sizeof(U32))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } } return 0; } static long sasdiag_portPhyCtrl(port_phy_ctrl *karg, void __user *arg) { long ret = 0; switch (karg->action) { case PHY_CTRL_GET_PHY_STATUS: case PHY_CTRL_SET_LINK_SPEED: case PHY_CTRL_SET_ENABLE: case PHY_CTRL_SET_DISABLE: ret = sasdiag_doPhyCfg(karg->ioc_num, karg->action, &(karg->phyctrl)); if (copy_to_user(arg, karg, sizeof(port_phy_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } break; default: DBG_PRINT(DBG_ERR, "portPhyCtrl: Unsupported IOCTL ctrl action x%02x\n", karg->action); return -EINVAL; } // switch return ret; } static long sasdiag_execCtrl(exec_ctrl *karg, void __user *arg) { long ret = 0; if (!isIocNumValid(karg->test_exc.ioc_num)) { DBG_PRINT(DBG_ERR, "execCtrl: Unsupported IOC num %d\n", karg->test_exc.ioc_num); return IOC_NUM_INVALID; } switch (karg->exec_action) { // Exec IO case EXEC_SEND_BLOCK_CMD: { void *inBuf = NULL, *outBuf = NULL; U16 ioSize, blkSize = 512; U32 inSize = 0, outSize = 0; ioSize = karg->ioCmd_exc.ioSize; if (ioSize > 64) // for now ioSize = 64; DBG_PRINT(DBG_IOCTL, "execCtrl: EXEC_SEND_BLOCK_CMD - " "ioc %d devHandle x%04x cmd 0x%02x ioSizeInBlocks 0x%x pat %d count %d\n", karg->ioCmd_exc.ioc_num, karg->ioCmd_exc.devHandle, karg->ioCmd_exc.cmdCode, ioSize, karg->ioCmd_exc.dataPattern, karg->ioCmd_exc.loopCount); // if Read 10 if (karg->ioCmd_exc.cmdCode == SCSI_COMMAND_READ_10) { inSize = ioSize * blkSize; inBuf = (char *)kmalloc(inSize, GFP_KERNEL); if (!inBuf) { DBG_PRINT(DBG_ERR, "execCtrl: mem alloc for block Read FAILED\n"); return -1; } // if Write 10 } else if (karg->ioCmd_exc.cmdCode == SCSI_COMMAND_WRITE_10) { outSize = ioSize * blkSize; outBuf = (char *)kmalloc(outSize, GFP_KERNEL); if (!outBuf) { DBG_PRINT(DBG_ERR, "execCtrl: mem alloc for block Write FAILED\n"); return -1; } // to do: add more cmd later ... } else { DBG_PRINT(DBG_ERR, "execCtrl: BLOCK CMD not supported yet\n"); } ret = sasdiag_sendBlockCmd(karg->ioCmd_exc.ioc_num, karg->ioCmd_exc.cmdCode, karg->ioCmd_exc.devHandle, ioSize, karg->ioCmd_exc.lba, inBuf, inSize, outBuf, outSize, karg->ioCmd_exc.dataPattern, karg->ioCmd_exc.loopCount); if (inBuf) kfree(inBuf); if (outBuf) kfree(outBuf); break; } case EXEC_SEND_IO_CMD: DBG_PRINT(DBG_IOCTL, "execCtrl: EXEC_SEND_IO_CMD - " "ioc %d devHandle x%04x cmd 0x%02x pat %d count %d\n", karg->ioCmd_exc.ioc_num, karg->ioCmd_exc.devHandle, karg->ioCmd_exc.cmdCode, karg->ioCmd_exc.dataPattern, karg->ioCmd_exc.loopCount); ret = sasdiag_sendScsiCmd(karg->ioCmd_exc.ioc_num, karg->ioCmd_exc.cmdCode, karg->ioCmd_exc.devHandle, karg->ioCmd_exc.dataPattern, karg->ioCmd_exc.loopCount, NULL); break; // Exec Task case EXEC_IOC_CLEAR_PHY_ERR_LOG: if (karg->task_exc.task_flag & EXEC_TASK_FROM_MAPI) ret = sasIocPhyErrLogClear(karg->task_exc.ioc_num, TRUE); else ret = sasIocPhyErrLogClear(karg->task_exc.ioc_num, FALSE); break; case EXEC_IOC_CLEAR_SINGLE_PHY_ERR_LOG: //Added by Sean P. for PHY reset Statistics ret = sasIocSinglePhyErrLogClear(karg->task_exc.ioc_num, karg->task_exc.phy_num); break; default: DBG_PRINT(DBG_ERR, "execCtrl: Unsupported IOCTL exec action x%02x\n", karg->exec_action); return -EINVAL; } // switch return ret; } static long sasdiag_getInfo(get_info *karg, void __user *arg) { U8 size, offset = 0; switch (karg->get_action) { case GET_IOC_INFO: karg->i_info.num_base_iocs = NUM_IOC_ON_BASE_CTRL; karg->i_info.num_hic_iocs = NUM_IOC_ON_HIC; karg->i_info.num_phys_on_ioc = MAX_IOC_PHYS; karg->i_info.num_ports_on_base_ioc = MAX_BASE_IOC_WIDE_PORT; karg->i_info.num_ports_on_hic_ioc = MAX_HIC_IOC_WIDE_PORT; size = sizeof(ioc_info); break; case GET_IOC_PHY_ERR: { sasdiag_getIocPhyErrInfo(karg->phy_err.ioc_num, karg->phy_err.phy_num, &(karg->phy_err)); // force to copy the whole get_info instead of the member of it offset = 0; size = sizeof(get_info); /* printk( "[diag_ioctl]:ioc numb: %d, phy numb: %d, invalid Dword: %#x, disparity: %#x, loss: %#x, reset: %#x\n", karg->phy_err.ioc_num, karg->phy_err.phy_num, karg->phy_err.InvalidDwordCount, karg->phy_err.RunningDisparityErrorCount, karg->phy_err.LossDwordSynchCount, karg->phy_err.PhyResetProblemCount); */ break; } case GET_IOC_PHY_SPEED:// Added by Sean P. { sasdiag_getIocPhyLinkSpeed(&(karg->phy_speed)); offset = 0; size = sizeof(get_info); break; } case GET_IOC_PHY_ATT_DEV_INFO:// Added by Sean P. { sasdiag_iocGetAttachedDevInfo(&(karg->phy_att_dev_info)); offset = 0; size = sizeof(get_info); break; } case GET_IOC_TEMP: // Added by Jerry Lee. { sasdiag_getIocTemperature(&(karg->temp_info)); offset = 0; size = sizeof(get_info); break; } case GET_IOC_WWID: { sasdiag_getIocWWID(karg->ioc_num, karg->ioc_wwid); offset = 0; size = sizeof(get_info); break; } case GET_IOC_DEFAULT_NVDATA_VERSION: { sasdiag_getIocNvdataVersionDefault(karg->ioc_num, &(karg->nvdata_defautl_version)); offset = 0; size = sizeof(get_info); break; } case GET_IOC_VPD: { sasdiag_getIocVPD(karg->ioc_num, karg->vpd); offset = 0; size = sizeof(get_info); break; } case GET_IOC_BASIC_INFO: { sasdiag_getIocTemperature(&(karg->temp_info)); sasdiag_getIocWWID(karg->ioc_num, karg->ioc_wwid); sasdiag_getIocNvdataVersionDefault(karg->ioc_num, &(karg->nvdata_defautl_version)); sasdiag_getIocVPD(karg->ioc_num, karg->vpd); offset = 0; size = sizeof(get_info); break; } default: DBG_PRINT(DBG_ERR, "getInfo: Unsupported IOCTL get info action x%02x\n", karg->get_action); return -EINVAL; } // switch if (copy_to_user((arg + offset), (karg + offset), size)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } return 0; } /** * sasdiag_check_ioctl - main ioctl entry point for SAS Diag * @file - (struct file) * @cmd - ioctl opcode * @arg - */ long sasdiag_checkIoctl(struct file *file, unsigned int cmd, void __user *arg) { long ret = -EINVAL; switch (cmd) { case SASDIAG_GPIO_LED_CONTROL: { gpio_led_ctrl karg; if (copy_from_user(&karg, arg, sizeof(gpio_led_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } ret = sasdiag_gpioCtrl(&karg, arg); break; } case SASDIAG_PORT_PHY_CONTROL: { port_phy_ctrl karg; if (copy_from_user(&karg, arg, sizeof(port_phy_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } ret = sasdiag_portPhyCtrl(&karg, arg); break; } case SASDIAG_REGISTER_CONTROL: { reg_ctrl karg; if (copy_from_user(&karg, arg, sizeof(reg_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } ret = sasdiag_iocRegCtrl(&karg, arg); break; } case SASDIAG_EXEC: { exec_ctrl karg; if (copy_from_user(&karg, arg, sizeof(exec_ctrl))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } ret = sasdiag_execCtrl(&karg, arg); break; } case SASDIAG_GET_INFO: { get_info karg; if (copy_from_user(&karg, arg, sizeof(get_info))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } ret = sasdiag_getInfo(&karg, arg); break; } default: DBG_PRINT(DBG_ERR, "getIoctl: unsupported SASDIAG IOCTL opcode(0x%08x)\n", cmd); ret = -EINVAL; } // switch return ret; }