You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
5.5 KiB
159 lines
5.5 KiB
/*
|
|
* cc -O2 -Wall camtest.c -lcam -o camtest
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/ata.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
/* Official FreeBSD CAM Library Interface Headers */
|
|
#include <camlib.h>
|
|
#include <cam/scsi/scsi_all.h>
|
|
|
|
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 <device_name> (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;
|
|
}
|
|
|
|
|