Files
OpenRGB/scsiapi/scsiapi_linux.c

238 lines
7.7 KiB
C

/*---------------------------------------------------------*\
| scsiapi_linux.c |
| |
| Cross-platform SCSI access library |
| Linux implementation |
| |
| Adam Honse (calcprogrammer1@gmail.com) 28 Jul 2023 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
/*---------------------------------------------------------*\
| Includes |
\*---------------------------------------------------------*/
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "scsiapi.h"
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------------------------------------*\
| Functions |
\*---------------------------------------------------------*/
void scsi_close(struct scsi_device * dev)
{
close(dev->fd);
}
struct scsi_device_info * scsi_enumerate(const char * vendor, const char * product)
{
struct scsi_device_info * ret_ptr = NULL;
struct scsi_device_info * last_ptr = NULL;
/*-----------------------------------------------------*\
| Search for /dev/sgX nodes matching vendor and product |
\*-----------------------------------------------------*/
unsigned int sg_idx = 0;
while(1)
{
/*-------------------------------------------------*\
| Create the scsi_generic class model path |
\*-------------------------------------------------*/
char sg_dev_buf[1024];
char sg_model_buf[512];
char sg_vendor_buf[512];
char sg_path_buf[512];
/*-------------------------------------------------*\
| Open the input event path to get the model and |
| vendor strings |
\*-------------------------------------------------*/
snprintf(sg_dev_buf, 1024, "/sys/class/scsi_generic/sg%d/device/model", sg_idx);
int sg_model_fd = open(sg_dev_buf, O_RDONLY | O_NONBLOCK);
snprintf(sg_dev_buf, 1024, "/sys/class/scsi_generic/sg%d/device/vendor", sg_idx);
int sg_vendor_fd = open(sg_dev_buf, O_RDONLY | O_NONBLOCK);
snprintf(sg_path_buf, 512, "/dev/sg%d", sg_idx);
/*-------------------------------------------------*\
| Fail if either path failed to open |
\*-------------------------------------------------*/
if(sg_model_fd < 0 || sg_vendor_fd < 0)
{
close(sg_model_fd);
close(sg_vendor_fd);
break;
}
memset(sg_model_buf, 0, 512);
memset(sg_vendor_buf, 0, 512);
/*-------------------------------------------------*\
| Read the model string and close the model file |
\*-------------------------------------------------*/
if(read(sg_model_fd, sg_model_buf, 512) < 0)
{
close(sg_model_fd);
close(sg_vendor_fd);
break;
}
close(sg_model_fd);
for(unsigned int i = 0; i < strlen(sg_model_buf); i++)
{
if(sg_model_buf[i] == '\r' || sg_model_buf[i] == '\n')
{
sg_model_buf[i] = '\0';
break;
}
}
/*-------------------------------------------------*\
| Read the vendor string and close the vendor file |
\*-------------------------------------------------*/
if(read(sg_vendor_fd, sg_vendor_buf, 512) < 0)
{
close(sg_vendor_fd);
break;
}
close(sg_vendor_fd);
for(unsigned int i = 0; i < strlen(sg_vendor_buf); i++)
{
if(sg_vendor_buf[i] == '\r' || sg_vendor_buf[i] == '\n')
{
sg_vendor_buf[i] = '\0';
break;
}
}
/*-------------------------------------------------*\
| Check if this SCSI device should be added to the |
| list |
\*-------------------------------------------------*/
int add_to_list = 0;
if(vendor == NULL || product == NULL)
{
add_to_list = 1;
}
else if(strncmp(sg_model_buf, product, strlen(product)) == 0)
{
if(strncmp(sg_vendor_buf, vendor, strlen(vendor)) == 0)
{
add_to_list = 1;
}
}
/*-------------------------------------------------*\
| Create new scsi_device_info if adding to list |
\*-------------------------------------------------*/
if(add_to_list == 1)
{
struct scsi_device_info * info = malloc(sizeof(struct scsi_device_info));
info->path = malloc(strlen(sg_path_buf) + 1);
strcpy(info->path, sg_path_buf);
info->vendor = malloc(strlen(sg_vendor_buf) + 1);
strcpy(info->vendor, sg_vendor_buf);
info->product = malloc(strlen(sg_model_buf) + 1);
strcpy(info->product, sg_model_buf);
info->next = NULL;
if(ret_ptr == NULL)
{
ret_ptr = info;
}
else
{
last_ptr->next = info;
}
last_ptr = info;
}
sg_idx++;
}
return(ret_ptr);
}
void scsi_free_enumeration(struct scsi_device_info * devs)
{
struct scsi_device_info * dev = devs;
while(dev)
{
struct scsi_device_info * next = dev->next;
free(dev->path);
free(dev->vendor);
free(dev->product);
free(dev);
dev = next;
}
}
struct scsi_device * scsi_open_path(const char *path)
{
int device_fd = open(path, O_RDWR);
struct scsi_device * device = NULL;
if(device_fd > 0)
{
device = malloc(sizeof(struct scsi_device));
device->fd = device_fd;
}
return(device);
}
int scsi_write(struct scsi_device * dev, const unsigned char * data, size_t data_length, const unsigned char * cdb, size_t cdb_length, unsigned char * sense, size_t sense_length)
{
/*-----------------------------------------------------*\
| Create buffer to hold header |
\*-----------------------------------------------------*/
struct sg_io_hdr header;
/*-----------------------------------------------------*\
| Set up pass through command |
\*-----------------------------------------------------*/
header.interface_id = 'S';
header.dxfer_direction = SG_DXFER_TO_DEV;
header.cmd_len = cdb_length;
header.mx_sb_len = sense_length;
header.iovec_count = 0;
header.dxfer_len = data_length;
header.dxferp = (void *)data;
header.cmdp = (unsigned char *)cdb;
header.sbp = sense;
header.timeout = 20000;
header.flags = 0;
/*-----------------------------------------------------*\
| Send pass through command |
\*-----------------------------------------------------*/
ioctl(dev->fd, SG_IO, &header);
return 0;
}
#ifdef __cplusplus
}
#endif