/* * cc -O2 -Wall camtest.c -lcam -o camtest */ #include #include #include #include #include #include #include #include #include #include /* Official FreeBSD CAM Library Interface Headers */ #include #include static void trim_str(char *str) { int i = strlen(str) - 1; while (i >= 0 && (isspace((unsigned char)str[i]) || str[i] == '\0')) { str[i] = '\0'; i--; } } static void ata_str_fix(char *str, size_t len) { for (size_t i = 0; i < len - 1; i += 2) { char tmp = str[i]; str[i] = str[i + 1]; str[i + 1] = tmp; } trim_str(str); } int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "Usage: %s (e.g. ada0 or da0)\n", argv[0]); return 1; } const char *disk = argv[1]; printf("=== Testing device via libcam (O_RDWR): %s ===\n\n", disk); /* ------------------------------------------------------------- * TEST 1: Open device via libcam using O_RDWR * ------------------------------------------------------------- */ struct cam_device *cam_dev = cam_open_device(disk, O_RDWR); if (cam_dev == NULL) { fprintf(stderr, "Error: cam_open_device failed for '%s'.\n", disk); fprintf(stderr, "Check permissions on /dev/xpt0 and /dev/pass* (group operator/devfs.rules).\n"); return 1; } union ccb *ccb = cam_getccb(cam_dev); if (ccb == NULL) { fprintf(stderr, "Error: Failed to allocate CCB block\n"); cam_close_device(cam_dev); return 1; } /* Allocate 512 bytes alignment buffer for responses */ uint8_t *data_buf = calloc(1, 512); if (data_buf == NULL) { fprintf(stderr, "Error: Memory allocation failed\n"); cam_freeccb(ccb); cam_close_device(cam_dev); return 1; } /* ------------------------------------------------------------- * TEST 2: Pure SCSI INQUIRY * ------------------------------------------------------------- */ memset(&ccb->csio, 0, sizeof(struct ccb_scsiio)); struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)data_buf; cam_fill_csio(&ccb->csio, /*retries*/ 2, /*cbfcnp*/ NULL, /*flags*/ CAM_DIR_IN, /*tag_action*/ CAM_TAG_ACTION_NONE, /*data_ptr*/ (uint8_t *)inq, /*dxfer_len*/ SHORT_INQUIRY_LENGTH, /*sense_len*/ SSD_FULL_SIZE, /*cdb_len*/ 6, /*timeout*/ 5000); /* Safely fill raw SCSI INQUIRY command bytes */ ccb->csio.cdb_io.cdb_bytes[0] = INQUIRY; ccb->csio.cdb_io.cdb_bytes[4] = SHORT_INQUIRY_LENGTH; if (cam_send_ccb(cam_dev, ccb) == 0 && (ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { char vendor[9], product[17]; snprintf(vendor, sizeof(vendor), "%.8s", inq->vendor); snprintf(product, sizeof(product), "%.16s", inq->product); trim_str(vendor); trim_str(product); printf("[SCSI INQUIRY]:\n"); printf(" Vendor : %s\n", vendor); printf(" Product : %s\n", product); printf(" Removable : %s (RMB Bit)\n", (inq->device & SID_RMB) ? "Yes (Flash/SD)" : "No (Fixed/HDD/SSD)"); } else { printf("[SCSI INQUIRY]: Failed (CAM Status: 0x%X)\n", ccb->ccb_h.status); } /* ------------------------------------------------------------- * TEST 3: SCSI SAT ATA PASS-THROUGH (16) -> ATA IDENTIFY * ------------------------------------------------------------- */ cam_freeccb(ccb); ccb = cam_getccb(cam_dev); if (ccb == NULL) { free(data_buf); cam_close_device(cam_dev); return 1; } memset(&ccb->csio, 0, sizeof(struct ccb_scsiio)); memset(data_buf, 0, 512); struct ata_params *ident_buf = (struct ata_params *)data_buf; cam_fill_csio(&ccb->csio, /*retries*/ 2, /*cbfcnp*/ NULL, /*flags*/ CAM_DIR_IN, /*tag_action*/ CAM_TAG_ACTION_NONE, /*data_ptr*/ (uint8_t *)ident_buf, /*dxfer_len*/ sizeof(struct ata_params), /*sense_len*/ SSD_FULL_SIZE, /*cdb_len*/ 16, /*timeout*/ 5000); /* Construct manual SAT ATA PASS-THROUGH (16) command block array */ uint8_t *cdb = ccb->csio.cdb_io.cdb_bytes; cdb[0] = 0x85; /* ATA PASS-THROUGH (16) Opcode */ cdb[1] = 4 << 1; /* Protocol: PIO Data-In */ cdb[2] = 0x08 | 0x02; /* T_DIR = 1 (Data In), ByteBlock = 1 (Block mode) */ cdb[4] = 1; /* Sector Count = 1 sector (512 bytes) */ cdb[14] = 0xEC; /* ATA Command: IDENTIFY DEVICE */ if (cam_send_ccb(cam_dev, ccb) == 0 && (ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { char ata_model[41]; memcpy(ata_model, ident_buf->model, sizeof(ident_buf->model)); ata_model[40] = '\0'; ata_str_fix(ata_model, sizeof(ata_model)); printf("\n[SCSI ATA PASS-THROUGH (16)]:\n"); printf(" ATA Model : %s\n", ata_model); printf(" Media RPM : %d (0=SSD, 1=Unknown)\n", ident_buf->media_rotation_rate); } else { printf("\n[SCSI ATA PASS-THROUGH (16)]: Failed / rejected by bridge (CAM Status: 0x%X)\n", ccb->ccb_h.status); } free(data_buf); cam_freeccb(ccb); cam_close_device(cam_dev); return 0; }