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

/*
* 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;
}