/* * Low-level interface to adapter information. * * Copyright (c) 2007 by Jefferson Ogata */ /* * 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, 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; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "megaioctl.h" /* Don't include */ #include #include #include #include #include int megaErrno = 0; static int doIoctl (struct mega_adapter_path *adapter, void *u) { switch (adapter->type) { case MEGA_ADAPTER_V2: case MEGA_ADAPTER_V34: return ioctl (adapter->fd, _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t), u); case MEGA_ADAPTER_V5: return ioctl (adapter->fd, MEGASAS_IOC_FIRMWARE, u); } return -1; } static int driverQuery (int fd, uint16_t adap, void *data, uint32_t len, uint8_t subop) { struct uioctl_t u; struct mega_adapter_path adapter; memset (&u, 0, sizeof u); u.outlen = len; u.ui.fcs.opcode = M_RD_DRIVER_IOCTL_INTERFACE; u.ui.fcs.subopcode = subop; u.ui.fcs.length = len; u.data = data; if (data) memset (data, 0, len); adapter.fd = fd; adapter.type = MEGA_ADAPTER_V34; if (doIoctl (&adapter, &u) < 0) { megaErrno = errno; return -1; } return 0; } static int oldCommand (struct mega_adapter_path *adapter, void *data, uint32_t len, uint8_t cmd, uint8_t opcode, uint8_t subopcode) { struct uioctl_t u; int_mbox_t *m = (int_mbox_t *) &u.mbox; memset (&u, 0, sizeof u); u.outlen = len; u.ui.fcs.opcode = M_RD_IOCTL_CMD; u.ui.fcs.adapno = MKADAP(adapter->adapno); u.data = data; m->cmd = cmd; m->opcode = opcode; m->subopcode = subopcode; m->xferaddr = (uint32_t) data; if (data) memset (data, 0, len); if (doIoctl (adapter, &u) < 0) { megaErrno = errno; return -1; } return 0; } static int newCommand (struct mega_adapter_path *adapter, void *data, uint32_t len, uint8_t cmd, uint8_t opcode, uint8_t subopcode) { struct uioctl_t u; int_mbox_t *m = (int_mbox_t *) &u.mbox; memset (&u, 0, sizeof u); u.outlen = len; u.ui.fcs.opcode = M_RD_IOCTL_CMD_NEW; u.ui.fcs.adapno = MKADAP(adapter->adapno); u.ui.fcs.buffer = data; u.ui.fcs.length = len; u.data = data; m->cmd = cmd; m->opcode = opcode; m->subopcode = subopcode; m->xferaddr = (uint32_t) data; if (data) memset (data, 0, len); if (doIoctl (adapter, &u) < 0) { megaErrno = errno; return -1; } return 0; } static int sasCommand (struct mega_adapter_path *adapter, void *data, uint32_t len, uint32_t opcode, uint16_t flags, void *mbox, uint32_t mboxlen) { struct megasas_iocpacket u; struct megasas_dcmd_frame *f = (struct megasas_dcmd_frame *) &u.frame; memset (&u, 0, sizeof u); u.host_no = (u16) adapter->adapno; f->cmd = MFI_CMD_DCMD; f->flags = (u16) flags; f->opcode = (u32) opcode; if ((data != NULL) && (len > 0)) { u.sgl_off = ((void *) &f->sgl) - ((void *) f); u.sge_count = 1; u.sgl[0].iov_base = data; u.sgl[0].iov_len = len; f->sge_count = 1; f->data_xfer_len = (u32) len; f->sgl.sge32[0].phys_addr = (u32) data; f->sgl.sge32[0].length = (u32) len; } if (mbox != NULL) memcpy (&f->mbox, mbox, mboxlen); if (doIoctl (adapter, &u) < 0) { megaErrno = errno; return -1; } return f->cmd_status; } static int passthruCommand (struct mega_adapter_path *adapter, void *data, uint32_t len, uint8_t target, uint8_t *cdb, uint8_t cdblen) { if ((adapter->type == MEGA_ADAPTER_V2) || (adapter->type == MEGA_ADAPTER_V34)) { struct uioctl_t u; int_mbox_t *m = (int_mbox_t *) &u.mbox; mraid_passthru_t *p = &u.pthru; memset (&u, 0, sizeof u); u.outlen = len; u.ui.fcs.opcode = M_RD_IOCTL_CMD; u.ui.fcs.adapno = MKADAP(adapter->adapno); u.data = data; m->cmd = MBOXCMD_PASSTHRU; m->xferaddr = (uint32_t) p; p->timeout = 3; p->ars = 1; p->target = target; p->dataxferaddr = (uint32_t) data; p->dataxferlen = len; p->scsistatus = 239; /* HMMM */ memcpy (p->cdb, cdb, cdblen); p->cdblen = cdblen; if (data) memset (data, 0, len); if (doIoctl (adapter, &u) < 0) { megaErrno = errno; return -1; } if (m->status) { megaErrno = - (m->status); return -1; } if (p->scsistatus & CHECK_CONDITION) { megaErrno = - CHECK_CONDITION; return -1; } if ((p->scsistatus & STATUS_MASK) != GOOD) { megaErrno = - (p->scsistatus & STATUS_MASK); return -1; } } else { struct megasas_iocpacket u; struct megasas_pthru_frame *f = (struct megasas_pthru_frame *) &u.frame; memset (&u, 0, sizeof u); u.host_no = (u16) adapter->adapno; f->cmd = MFI_CMD_PD_SCSI_IO; f->target_id = target; f->cdb_len = cdblen; f->flags = MFI_FRAME_DIR_READ; memcpy (f->cdb, cdb, cdblen); if ((data != NULL) && (len > 0)) { u.sgl_off = ((void *) &f->sgl) - ((void *) f); u.sge_count = 1; u.sgl[0].iov_base = data; u.sgl[0].iov_len = len; f->sge_count = 1; f->data_xfer_len = (u32) len; f->sgl.sge32[0].phys_addr = (u32) data; f->sgl.sge32[0].length = (u32) len; } if (doIoctl (adapter, &u) < 0) { megaErrno = errno; return -1; } if (f->cmd_status) { megaErrno = - (f->cmd_status); return -1; } if ((f->scsi_status & STATUS_MASK) != GOOD) { megaErrno = - (f->scsi_status & STATUS_MASK); return -1; } } return 0; } int megaScsiDriveInquiry (struct mega_adapter_path *adapter, uint8_t target, void *data, uint32_t len, uint8_t pageCode, uint8_t evpd) { uint8_t cdb[6]; cdb[0] = INQUIRY; cdb[1] = (evpd != 0); cdb[2] = pageCode; cdb[3] = (len >> 8) & 0xff; cdb[4] = len & 0xff; cdb[5] = 0; return passthruCommand (adapter, data, len, target, cdb, sizeof cdb); } int megaScsiModeSense (struct mega_adapter_path *adapter, uint8_t target, void *data, uint32_t len, uint8_t pageControl, uint8_t page, uint8_t subpage) { #ifdef USE_MODE_SENSE_6 uint8_t cdb[6]; cdb[0] = MODE_SENSE; cdb[1] = 0; /* dbd in bit 3 */ cdb[2] = ((pageControl & 0x3) << 6) | (page & 0x3f); cdb[3] = subpage; cdb[4] = len & 0xff; cdb[5] = 0; #else uint8_t cdb[10]; cdb[0] = MODE_SENSE_10; cdb[1] = 0; /* llbaa in bit 4, dbd in bit 3 */ cdb[2] = ((pageControl & 0x3) << 6) | (page & 0x3f); cdb[3] = subpage; cdb[4] = 0; cdb[5] = 0; cdb[6] = 0; cdb[7] = (len >> 8) & 0xff; cdb[8] = len & 0xff; cdb[9] = 0; #endif return passthruCommand (adapter, data, len, target, cdb, sizeof cdb); } int megaScsiLogSense (struct mega_adapter_path *adapter, uint8_t target, void *data, uint32_t len, uint8_t pageControl, uint8_t page, uint16_t parameterPointer) { uint8_t cdb[10]; cdb[0] = LOG_SENSE; cdb[1] = 0; /* ppc in bit 1, sp in bit 1 */ cdb[2] = ((pageControl & 0x3) << 6) | (page & 0x3f); cdb[3] = 0; cdb[4] = 0; cdb[5] = (parameterPointer >> 8) & 0xff; cdb[6] = parameterPointer & 0xff; cdb[7] = (len >> 8) & 0xff; cdb[8] = len & 0xff; cdb[9] = 0; return passthruCommand (adapter, data, len, target, cdb, sizeof cdb); } int megaScsiSendDiagnostic (struct mega_adapter_path *adapter, uint8_t target, void *data, uint32_t len, uint8_t testCode, uint8_t unitOffline, uint8_t deviceOffline) { uint8_t cdb[6]; cdb[0] = SEND_DIAGNOSTIC; cdb[1] = ((testCode & 0x7) << 5) | ((deviceOffline != 0) << 1) | (unitOffline != 0); cdb[2] = 0; cdb[3] = 0; cdb[4] = 0; cdb[5] = 0; return passthruCommand (adapter, data, len, target, cdb, sizeof cdb); } int megaGetAdapterConfig8 (struct mega_adapter_path *adapter, disk_array_8ld_span8_t *config) { return oldCommand (adapter, config, sizeof (*config), NEW_READ_CONFIG_8LD, 0, 0); } int megaGetAdapterConfig40 (struct mega_adapter_path *adapter, disk_array_40ld_t *config) { return newCommand (adapter, config, sizeof (*config), FC_NEW_CONFIG, OP_DCMD_READ_CONFIG, 0); } int megaGetAdapterInquiry (struct mega_adapter_path *adapter, mraid_inquiry1_t *data) { return oldCommand (adapter, data, sizeof (*data), MBOXCMD_ADAPTERINQ, 0, 0); } int megaGetAdapterExtendedInquiry (struct mega_adapter_path *adapter, mraid_extinq1_t *data) { return oldCommand (adapter, data, sizeof (*data), MBOXCMD_ADPEXTINQ, 0, 0); } int megaGetAdapterEnquiry3 (struct mega_adapter_path *adapter, mraid_inquiry3_t *data) { return newCommand (adapter, data, sizeof (*data), FC_NEW_CONFIG, NC_SUBOP_ENQUIRY3, 0); } int megaGetPredictiveMap (struct mega_adapter_path *adapter, struct mega_predictive_map *data) { return oldCommand (adapter, data, sizeof (*data), MAIN_MISC_OPCODE, 0x0f, 0); } int megaGetDriveErrorCount (struct mega_adapter_path *adapter, uint8_t target, struct mega_physical_drive_error_info *data) { return oldCommand (adapter, data, sizeof (*data), 0x77, 0, target); } int megaSasGetDeviceList (struct mega_adapter_path *adapter, struct mega_device_list_sas **data) { unsigned char buf[0x20]; uint32_t len; if (sasCommand (adapter, buf, sizeof buf, 0x02010000, MFI_FRAME_DIR_READ, NULL, 0) < 0) return -1; len = ((struct mega_device_list_sas *) buf)->length; if ((*data = (struct mega_device_list_sas *) malloc (len)) == NULL) { megaErrno = errno; return -1; } return sasCommand (adapter, *data, len, 0x02010000, MFI_FRAME_DIR_READ, NULL, 0); } int megaSasGetDiskInfo (struct mega_adapter_path *adapter, uint8_t target, struct mega_physical_disk_info_sas *data) { uint8_t mbox[0xc]; memset (&mbox, 0, sizeof mbox); mbox[0] = target; return sasCommand (adapter, data, sizeof (*data), 0x02020000, MFI_FRAME_DIR_READ, mbox, sizeof mbox); } int megaSasGetArrayConfig (struct mega_adapter_path *adapter, struct mega_array_config_sas *data) { unsigned char buf[0x20]; uint32_t len; if (sasCommand (adapter, buf, sizeof buf, 0x04010000, MFI_FRAME_DIR_READ, NULL, 0) < 0) return -1; len = ((struct mega_array_header_sas *) buf)->length; if ((data->header = (struct mega_array_header_sas *) malloc (len)) == NULL) { megaErrno = errno; return -1; } if (sasCommand (adapter, data->header, len, 0x04010000, MFI_FRAME_DIR_READ, NULL, 0) < 0) { megaErrno = errno; return -1; } data->span = (struct mega_array_span_def_sas *) (data->header + 1); data->disk = (struct mega_array_disk_def_sas *) (data->span + data->header->num_span_defs); data->hotspare = (struct mega_array_hotspare_def_sas *) (data->disk + data->header->num_disk_defs); return 0; } int megaSasGetBatteryInfo (struct mega_adapter_path *adapter, struct mega_battery_info_sas *data) { if (sasCommand (adapter, &(data->state), sizeof (data->state), 0x05010000, MFI_FRAME_DIR_READ, NULL, 0) < 0) return -1; if (sasCommand (adapter, &(data->capacity), sizeof (data->capacity), 0x05020000, MFI_FRAME_DIR_READ, NULL, 0) < 0) return -1; if (sasCommand (adapter, &(data->design), sizeof (data->design), 0x05030000, MFI_FRAME_DIR_READ, NULL, 0) < 0) return -1; return sasCommand (adapter, &(data->properties), sizeof (data->properties), 0x05050100, MFI_FRAME_DIR_READ, NULL, 0); } int megaGetDriverVersion (int fd, uint32_t *version) { return driverQuery (fd, 0, version, sizeof (*version), 'e'); } int megaGetNumAdapters (int fd, uint32_t *numAdapters, int sas) { if (sas) { uint8_t k; for (k = 0; k < 16; ++k) if (megaSasAdapterPing (fd, k) < 0) break; *numAdapters = k; return 0; } else return driverQuery (fd, 0, numAdapters, sizeof (*numAdapters), 'm'); } int megaGetAdapterProductInfo (int fd, uint8_t adapno, mraid_pinfo_t *data) { struct mega_adapter_path adapter; adapter.fd = fd; adapter.adapno = adapno; adapter.type = MEGA_ADAPTER_V34; return newCommand (&adapter, data, sizeof (*data), FC_NEW_CONFIG, NC_SUBOP_PRODUCT_INFO, 0); } int megaSasGetAdapterProductInfo (int fd, uint8_t adapno, struct megasas_ctrl_info *data) { struct mega_adapter_path adapter; adapter.fd = fd; adapter.adapno = adapno; adapter.type = MEGA_ADAPTER_V5; return sasCommand (&adapter, data, sizeof (*data), MR_DCMD_CTRL_GET_INFO, MFI_FRAME_DIR_READ, NULL, 0); } int megaSasAdapterPing (int fd, uint8_t adapno) { struct mega_adapter_path adapter; unsigned char data[0xc4]; adapter.fd = fd; adapter.adapno = adapno; adapter.type = MEGA_ADAPTER_V5; return sasCommand (&adapter, data, sizeof data, 0x04060100, MFI_FRAME_DIR_READ, NULL, 0); } char *megaErrorString (void) { if (megaErrno >= 0) return strerror (megaErrno); switch (-megaErrno) { case CHECK_CONDITION: return "scsi command status CHECK_CONDITION"; break; case CONDITION_GOOD: return "scsi command status CONDITION_GOOD"; break; case BUSY: return "scsi command status BUSY"; break; case INTERMEDIATE_GOOD: return "scsi command status INTERMEDIATE_GOOD"; break; case INTERMEDIATE_C_GOOD: return "scsi command status INTERMEDIATE_C_GOOD"; break; case RESERVATION_CONFLICT: return "scsi command status RESERVATION_CONFLICT"; break; case COMMAND_TERMINATED: return "scsi command status COMMAND_TERMINATED"; break; case QUEUE_FULL: return "scsi command status QUEUE_FULL"; break; default: return "scsi command status unknown"; break; } }