| /* |
| * Copyright (C) 1999-2018 D. Gilbert |
| * 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. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| * |
| * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") |
| * device driver. |
| * This program does a SCSI inquiry command on the given device and |
| * outputs some of the result. This program highlights the use of the |
| * SCSI_IOCTL_SEND_COMMAND ioctl. This should be able to be applied to |
| * any SCSI device file descriptor (not just one related to sg). [Whether |
| * this is a good idea on a disk while it is mounted is debatable. |
| * No detrimental effects when this was tested ...] |
| * |
| * Version 0.16 20181207 |
| */ |
| |
| #include <unistd.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <scsi/scsi.h> |
| /* #include <scsi/scsi_ioctl.h> */ /* glibc hides this file sometimes */ |
| |
| typedef struct my_scsi_ioctl_command { |
| unsigned int inlen; /* _excluding_ scsi command length */ |
| unsigned int outlen; |
| unsigned char data[1]; /* was 0 but that's not ISO C!! */ |
| /* on input, scsi command starts here then opt. data */ |
| } My_Scsi_Ioctl_Command; |
| |
| #define OFF (2 * sizeof(unsigned int)) |
| |
| #ifndef SCSI_IOCTL_SEND_COMMAND |
| #define SCSI_IOCTL_SEND_COMMAND 1 |
| #endif |
| |
| #define INQUIRY_CMD 0x12 |
| #define INQUIRY_CMDLEN 6 |
| #define INQUIRY_REPLY_LEN 96 |
| |
| |
| int main(int argc, char * argv[]) |
| { |
| int s_fd, res, k, to; |
| unsigned char inq_cdb [INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, |
| INQUIRY_REPLY_LEN, 0}; |
| unsigned char * inqBuff = (unsigned char *) |
| malloc(OFF + sizeof(inq_cdb) + 512); |
| unsigned char * buffp = inqBuff + OFF; |
| My_Scsi_Ioctl_Command * ishp = (My_Scsi_Ioctl_Command *)inqBuff; |
| char * file_name = 0; |
| int do_nonblock = 0; |
| int oflags = 0; |
| |
| for (k = 1; k < argc; ++k) { |
| if (0 == strcmp(argv[k], "-n")) |
| do_nonblock = 1; |
| else if (*argv[k] != '-') |
| file_name = argv[k]; |
| else { |
| printf("Unrecognized argument '%s'\n", argv[k]); |
| file_name = 0; |
| break; |
| } |
| } |
| if (0 == file_name) { |
| printf("Usage: 'scsi_inquiry [-n] <scsi_device>'\n"); |
| printf(" where: -n open device in non-blocking mode\n"); |
| printf(" Examples: scsi_inquiry /dev/sda\n"); |
| printf(" scsi_inquiry /dev/sg0\n"); |
| printf(" scsi_inquiry -n /dev/scd0\n"); |
| return 1; |
| } |
| |
| if (do_nonblock) |
| oflags = O_NONBLOCK; |
| s_fd = open(file_name, oflags | O_RDWR); |
| if (s_fd < 0) { |
| if ((EROFS == errno) || (EACCES == errno)) { |
| s_fd = open(file_name, oflags | O_RDONLY); |
| if (s_fd < 0) { |
| perror("scsi_inquiry: open error"); |
| return 1; |
| } |
| } |
| else { |
| perror("scsi_inquiry: open error"); |
| return 1; |
| } |
| } |
| /* Don't worry, being very careful not to write to a none-scsi file ... */ |
| res = ioctl(s_fd, SCSI_IOCTL_GET_BUS_NUMBER, &to); |
| if (res < 0) { |
| /* perror("ioctl on scsi device, error"); */ |
| printf("scsi_inquiry: not a scsi device\n"); |
| return 1; |
| } |
| |
| ishp->inlen = 0; |
| ishp->outlen = INQUIRY_REPLY_LEN; |
| memcpy(buffp, inq_cdb, INQUIRY_CMDLEN); |
| res = ioctl(s_fd, SCSI_IOCTL_SEND_COMMAND, inqBuff); |
| if (0 == res) { |
| to = (int)*(buffp + 7); |
| printf(" %.8s %.16s %.4s, byte_7=0x%x\n", buffp + 8, |
| buffp + 16, buffp + 32, to); |
| } |
| else if (res < 0) |
| perror("scsi_inquiry: SCSI_IOCTL_SEND_COMMAND err"); |
| else |
| printf("scsi_inquiry: SCSI_IOCTL_SEND_COMMAND status=0x%x\n", res); |
| |
| res = close(s_fd); |
| if (res < 0) { |
| perror("scsi_inquiry: close error"); |
| return 1; |
| } |
| return 0; |
| } |