mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-28 09:47:50 -05:00
238 lines
7.7 KiB
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
|